RosettaCodeData/Task/Password-generator/Ceylon/password-generator-2.ceylon

133 lines
4.9 KiB
Ceylon

import ceylon.random {
DefaultRandom,
Random
}
Character[] lowerCaseChars = 'a'..'z';
Character[] upperCaseChars = 'A'..'Z';
Character[] numsAsChars = '0'..'9';
Character[] specialChars = "!\"#$%&'()*+,-./:;<=>?@[]^_{|}~".sequence();
Character[] allChars = lowerCaseChars.append(upperCaseChars)
.append(numsAsChars)
.append(specialChars);
shared void run() {
print("2 passwords of size 8");
print("------");
generatePasswords(8,2).each(print);
print("------");
print("5 passwords of size 12");
print("------");
generatePasswords(12,5).each(print);
print("------");
print("7 passwords of size 16");
print("------");
generatePasswords(16,7).each(print);
print("------");
print("2 passwords of size 16 exclude AiX24!~");
print("------");
generatePasswords(16,7,"AiX24!~".sequence()).each(print);
}
String[] generatePasswords(Integer numChars, Integer numPasswords, Character[] excludedChars=[])
=> [for (count in 1..numPasswords) generatePassword(numChars,excludedChars) ];
//The generated password(s) must include at least one lower-case letter, one upper-case letter and one character from digits and 'other'.
String generatePassword(Integer numChars, Character[] excludedChars) {
"Must provide a password length of at least 4"
assert(numChars >= 4);
value random = DefaultRandom();
value fixedPartOfPassword = passwordFragmentForRequirements(random,excludedChars);
value randomPart =
randomPartOfPassword{ random = random;
index = fixedPartOfPassword.size + 1;
randomPart = "";
numChars = numChars;
candidateChars = filterCharsToExclude(allChars, excludedChars); };
value unshuffledPassword = fixedPartOfPassword + randomPart;
assert(unshuffledPassword.string.size == numChars);
"excluded chars contained"
assert(! unshuffledPassword.any((char) => excludedChars.contains(char)));
"Sizes not equal"
assert(unshuffledPassword.size == numChars);
value shuffledPassword =
shuffleCharsRecursive(unshuffledPassword.string,"",unshuffledPassword.indexes().sequence());
"Sizes not equal"
assert(shuffledPassword.string.size == numChars);
"set of characters not equal"
assert(set(unshuffledPassword) == set(shuffledPassword));
return shuffledPassword;
}
String passwordFragmentForRequirements(Random random, Character[] excludedChars)
=> String({filterAndUse(random,lowerCaseChars, excludedChars),
filterAndUse(random,upperCaseChars,excludedChars),
filterAndUse(random,numsAsChars,excludedChars),
filterAndUse(random,specialChars,excludedChars)});
String randomPartOfPassword(Random random,
Integer index,
String randomPart,
Integer numChars,
Character[] candidateChars) {
if(index <= numChars) {
value candidateCharsIndex = random.nextInteger(candidateChars.size);
assert(exists allChar = candidateChars[candidateCharsIndex]);
return randomPartOfPassword{ random=random;
index=index+1;
randomPart = randomPart + allChar.string;
numChars =numChars;
candidateChars = candidateChars; };
}
return randomPart;
}
String shuffleCharsRecursive(String orig,String shuffledString,Integer[] remainingOrigIndexes) {
value random = DefaultRandom();
if (nonempty constRemainingIndexes = remainingOrigIndexes) {
value randomIndex = random.nextInteger(orig.size);
assert(exists nextChar = orig[randomIndex]);
String newShuffledString;
Integer[] newRemainingIndexes;
if (constRemainingIndexes.contains(randomIndex)) {
newShuffledString = shuffledString + nextChar.string;
newRemainingIndexes = constRemainingIndexes.filter((index) => randomIndex != index).sequence();
} else {
newShuffledString = shuffledString;
newRemainingIndexes = constRemainingIndexes;
}
return shuffleCharsRecursive{ orig=orig;
shuffledString=newShuffledString;
remainingOrigIndexes=newRemainingIndexes; };
}
return shuffledString;
}
Character filterAndUse(Random random, Character[] chars, Character[] excludedChars) {
value charsToUse = filterCharsToExclude(chars, excludedChars);
value charToUseIndex = random.nextInteger(charsToUse.size);
assert( exists charToUse = charsToUse[charToUseIndex]);
return charToUse;
}
Character[] filterCharsToExclude(Character[] chars, Character[] charsToExclude)
=> chars.filter((char) => ! charsToExclude.contains(char)).sequence();