Last active
January 2, 2016 16:39
-
-
Save paracycle/8331913 to your computer and use it in GitHub Desktop.
TCKN - VKN validation
This file contains hidden or 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
def self.validate_tckn(tckn) | |
return false if invalid_value?(tckn, 11) | |
digits = tckn[0..-3].each_char.map(&:to_i).each_with_index | |
# Accumulate the check for both the last digit and the one-from-last | |
# digit in the same loop. So reduce takes a two element array as memo | |
# and returns the updated two element array at each iteration. | |
first, last = | |
digits.reduce([0, 0]) do |memo, (digit, idx)| | |
add = digit * (idx.even? ? 7 : -1) | |
[ | |
# Multiply all even digits with 7 and odd digits with -1 and sum | |
(memo.first + add) % 10, | |
# Sum all the digits and add the first sum. We add the first sum | |
# by accumulating it on to this number as well. | |
(memo.last + digit + add) % 10 | |
] | |
end | |
# Check that the first result matches with the one-from-last digit, and | |
# the last result matches with the last digit | |
tckn[-2] == first.to_s && tckn[-1] == last.to_s | |
end | |
def self.validate_vkn(vkn) | |
return false if invalid_value?(vkn, 10) | |
digits = vkn[0..-2].each_char.map(&:to_i).each_with_index | |
checksum = | |
digits.reduce(0) do |memo, (digit, idx)| | |
# We need to work with indices from the right | |
rev_idx = 9 - idx | |
# The numbers below are 2's powers mod 9 | |
# Instead of calculating 2's powers and then doing a mod 9 | |
# operation, we instead use a simple lookup table that is | |
# just 6 elements long. | |
coeff = [1, 2, 4, 8, 7, 5][rev_idx % 6] | |
# Step 1. Add the reverse index and the digit mod 10 | |
result = (digit + rev_idx) % 10 | |
# Step 2.1. If this result is 0, there is nothing more we need to do. | |
# Not doing any more calculation here helps make the weird mod operation | |
# below simpler. | |
if result.nonzero? | |
# Step 2.2. If it is non-zero, multiply with 2's power mod 9 | |
result = (coeff * result) % 9 | |
# Step 2.3. If the result of the previous operation is 0 | |
# then it should be converted to 9 (weird way of doing modular | |
# arithmetic, where 0 = 9 mod 9, but so it is) | |
result = 9 if result.zero? | |
end | |
# Step 3. Add the result to the accumulator | |
memo += result | |
end | |
# The final result added to the last digit should = 0 mod 10 | |
(checksum + vkn[-1].to_i) % 10 == 0 | |
end | |
private | |
def self.numeric?(str) | |
!!(str =~ /\A[[:digit:]]+\Z/) | |
end | |
def self.invalid_value?(str, length) | |
str.nil? || str.length != length || !numeric?(str) | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment