Last active
December 18, 2015 08:32
-
-
Save ebluehands/1e36e41422e729949ff8 to your computer and use it in GitHub Desktop.
This is an implementation of the Luhn mod N algorithm. A repo is also available : https://github.com/ebluehands/LuhnModN
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import Darwin | |
enum LuhnError : ErrorType { | |
case InvalidCharacters | |
case ModuloOutOfRange | |
} | |
/// This is an implementation of the [Luhn mod N algorithm](https://en.wikipedia.org/wiki/Luhn_mod_N_algorithm) | |
public class LuhnModN { | |
// MARK: Public | |
/** | |
Check if a string is valid | |
- Parameter s: The string to validate | |
- Parameter modulo: The modulo in which to compute. It corresponds to the number of valid characters | |
- Returns: true if the string is valid, false otherwise | |
*/ | |
public static func isValid(s : String, modulo : Int = 10) -> Bool { | |
guard validateModulo(modulo) && !containsInvalidCharacters(s, modulo: Int32(modulo)) else { return false } | |
let sum = calculateLuhnSum(s, modulo: modulo, shouldDouble: { ($0 % 2 != 0) }) | |
let remainder = sum % modulo | |
return remainder == 0 | |
} | |
/** | |
Add the check character | |
- Parameter s: The string to add the check character | |
- Parameter modulo: The modulo in which to compute. It corresponds to the number of valid characters | |
- Throws: LuhnError | |
- Returns: The string with the check character | |
*/ | |
public static func addCheckCharacter(s : String, modulo : Int = 10) throws -> String { | |
guard validateModulo(modulo) else { throw LuhnError.ModuloOutOfRange } | |
guard !containsInvalidCharacters(s, modulo: Int32(modulo)) else { throw LuhnError.InvalidCharacters } | |
return s + generateCheckCharacter(s, modulo : modulo) | |
} | |
// MARK: Private | |
/** | |
Check if a string contains invalid characters | |
- Parameter s : The string to check | |
- Parameter modulo: The modulo in which to compute. It corresponds to the number of valid characters | |
- Returns: true if one or more invalid characters were found, true otherwise | |
*/ | |
private static func containsInvalidCharacters(s : String, modulo : Int32) -> Bool { | |
return [Character](s.characters) | |
.reduce(false) { $0 || (strtoul(String($1), nil, modulo) == 0 && $1 != "0") } | |
} | |
/** | |
Validate the modulo. The modulo should be between 2 and 36 inclusive | |
- Parameter modulo: The modulo | |
- Returns: true if the modulo is valid, false otherwise | |
*/ | |
private static func validateModulo(modulo : Int) -> Bool { | |
return modulo > 1 && modulo < 37 | |
} | |
/** | |
Get the int value of a character | |
- Parameter character: The character | |
- Parameter modulo: The modulo in which to compute. It corresponds to the number of valid characters | |
- Returns: The int value of the character | |
*/ | |
private static func codePointFromCharacter(character : Character, modulo : Int32) -> Int { | |
return Int(strtoul(String(character), nil, modulo)) | |
} | |
/** | |
Get the string value of an int | |
- Parameter codePoint: The int to convert | |
- Parameter modulo: The modulo in which to compute. It corresponds to the number of valid characters | |
- Returns: The string value of the integer | |
*/ | |
private static func characterFromCodePoint(codePoint : Int, modulo : Int) -> String { | |
return String(codePoint, radix: modulo) | |
} | |
/** | |
Sum the digits of a number | |
- Parameter addend: The number | |
- Parameter modulo: The modulo in which to compute. It corresponds to the number of valid characters | |
- Returns : The sum of its digit | |
*/ | |
private static func sumDigits(addend : Int, modulo : Int) -> Int { | |
return (addend / modulo) + (addend % modulo) | |
} | |
/** | |
Calculate the luhn sum | |
- Parameter s: The string | |
- Parameter sohuldDouble: A closure to know if the value should be doubled or not | |
- Returns: The Luhn sum | |
*/ | |
private static func calculateLuhnSum(s : String, modulo : Int, shouldDouble : (index : Int) -> Bool) -> Int { | |
return [Character](s.characters) | |
.reverse() | |
.map { codePointFromCharacter($0, modulo: Int32(modulo)) } | |
.enumerate() | |
.map { sumDigits($1 * ((shouldDouble(index: $0)) ? 2 : 1), modulo : modulo) } | |
.reduce(0, combine: +) | |
} | |
/** | |
Calculate the check character | |
- Parameter input: The input to string from which the check character should be calculated | |
- Returns: The check character | |
*/ | |
private static func generateCheckCharacter(input : String, modulo : Int) -> String { | |
let sum = calculateLuhnSum(input, modulo : modulo, shouldDouble: { ($0 % 2 == 0) }) | |
let remainder = sum % modulo | |
let checkCodePoint = (modulo - remainder) % modulo | |
return characterFromCodePoint(checkCodePoint, modulo : modulo) | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment