Skip to content

Instantly share code, notes, and snippets.

@paracycle
Last active January 2, 2016 16:39
Show Gist options
  • Save paracycle/8331913 to your computer and use it in GitHub Desktop.
Save paracycle/8331913 to your computer and use it in GitHub Desktop.
TCKN - VKN validation
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