1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140
|
# # ## ### ##### ######## ############# ######################
## Luhn test of numbers - 10+5 variant
## From Rosetta Code
## http://rosettacode.org/wiki/Luhn_test#Tcl
## Author Donal K. Fellows
## See also
## http://en.wikipedia.org/wiki/Luhn_algorithm
## ISO/IEC 7812-1
## http://www.iso.org/iso/iso_catalogue/catalogue_tc/catalogue_detail.htm?csnumber=39698
## US Patent 2,950,048 (Aug 23, 1960): expired.
## http://www.google.com/patents?q=2950048
## Public Domain.
#
# The 10+5 variant of the Luhn test is used by some credit card
# companies to distinguish valid credit card numbers from what could
# be a random selection of digits.
#
# Those companies using credit card numbers that can be validated by
# the Luhn test have numbers that pass the following test:
##
# 1. Reverse the order of the digits in the number.
#
# 2. Take the first, third, ... and every other odd digit in the
# reversed digits and sum them to form the partial sum s1
#
# 3. Taking the second, fourth ... and every other even digit in the
# reversed digits:
#
# a. Multiply each digit by two and sum the digits if the answer
# is greater than nine to form partial sums for the even digits
# b. Sum the partial sums of the even digits to form s2
#
# Note that the steps above induce a simple permutation on digits
# 0-9 which can be handled through a lookup table instead of doing
# the doubling and summing explicitly.
#
# 4. If s1 + s2 ends in zero or five then the original number is in
# the form of a valid credit card number as verified by the Luhn test.
# 3.a/3.b lookup table
# i|0 1 2 3 4 5 6 7 8 9
# *2|0 2 4 6 8 10 12 14 16 18
# sum|0 2 4 6 8 1 3 5 7 9 (butterfly)
# # ## ### ##### ######## ############# ######################
# The code below implements the interface of a snit validation type,
# making it directly usable with snit's -type option in option
# specifications.
# # ## ### ##### ######## ############# ######################
## Requisites
package require Tcl 8.5
package require snit
package require valtype::common
# # ## ### ##### ######## ############# ######################
## Implementation
namespace eval ::valtype::luhn5 {
namespace import ::valtype::common::*
}
snit::type ::valtype::luhn5 {
#-------------------------------------------------------------------
# Type Methods
typemethod validate {value {code LUHN5}} {
if {[regexp {[^0-9]} $value]} {
badchar $code "$code number, expected only digits"
}
# Luhn test.
set sum [Sum $value 1]
if {($sum % 10) ni {0 5}} {
badcheck $code "$code number"
}
return $value
}
typemethod checkdigit0 {value {code LUHN5}} {
if {[regexp {[^0-9]} $value]} {
badchar LUHN "$code number, expected only digits"
}
# Compute the luhn5 checkdigit 0 % 10.
set c [expr {10 - ([Sum $value 0] % 10)}]
if {$c == 10} { set c 0 }
return $c
}
typemethod checkdigit5 {value {code LUHN5}} {
if {[regexp {[^0-9]} $value]} {
badchar LUHN "$code number, expected only digits"
}
# Compute the luhn5 checkdigit 5 % 10 (the alternate).
set c [expr {10 - (([Sum $value 0] + 5) % 10)}]
if {$c == 10} { set c 0 }
return $c
}
proc Sum {value flip} {
# Check digit computation starts with flip == 0!
#
# In the validation (see above) the check-digit is the last
# digit, and flip initialized to 1. The next-to-last digit is
# our last here and processed with the bit flipped. Hence our
# different, pre-flipped, starting point.
set sum 0
foreach ch [lreverse [split $value {}]] {
incr sum [lindex {
{0 1 2 3 4 5 6 7 8 9}
{0 2 4 6 8 1 3 5 7 9}
} [expr {[incr flip] & 1}] $ch]
}
return $sum
}
#-------------------------------------------------------------------
# Constructor
# None needed; no options
#-------------------------------------------------------------------
# Public Methods
method validate {value} {
$type validate $value
}
}
# # ## ### ##### ######## ############# ######################
## Ready
package provide valtype::luhn5 1
|