Y
Yossef Mendelssohn
This quiz reminded me of my days in credit card processing. The
weighted checksum for routing numbers is more interesting, but the
sort-of-two-pass aspect of the Luhn algorithm is a great stumbling
block. My solution follows. You'll notice I liked your ARGV.join
trick for the input.
class Array
def cycle!
push(shift)
end
end
class CCNum < String
PATTERNS = {
'AMEX' => { :start => ['34', '37'], :length => 15 },
'Discover' => { :start => ['6011', '65'], :length => 16 },
'MasterCard' => { :start => (51..55).to_a.collect { |n|
n.to_s }, :length => 16 },
'Visa' => { :start => '4', :length => [13, 16] },
}.freeze
def initialize(*args)
super
gsub!(/\D/, '')
@factors = [1,2]
@factors.cycle! if (length % 2) == 1
end
def type
return @type if @type
PATTERNS.each do |name, pat|
@type = name if [pat[:start]].flatten.any? { |s| match /
^#{s}/ } and [pat[:length]].flatten.any? { |l| length == l }
end
@type ||= 'Unknown'
end
def luhn_sum
@luhn_sum ||= split('').inject(0) do |sum, digit|
@factors.cycle!
sum += (digit.to_i * @factors.first).to_s.split('').inject(0) { |
s,d| s += d.to_i }
end
end
def luhn_valid?
(luhn_sum % 10) == 0
end
end
card_num = CCNum.new(ARGV.join)
puts "#{card_num} is a(n) #{card_num.luhn_valid? ? 'V' : 'Inv' }alid
#{card_num.type}"
weighted checksum for routing numbers is more interesting, but the
sort-of-two-pass aspect of the Luhn algorithm is a great stumbling
block. My solution follows. You'll notice I liked your ARGV.join
trick for the input.
class Array
def cycle!
push(shift)
end
end
class CCNum < String
PATTERNS = {
'AMEX' => { :start => ['34', '37'], :length => 15 },
'Discover' => { :start => ['6011', '65'], :length => 16 },
'MasterCard' => { :start => (51..55).to_a.collect { |n|
n.to_s }, :length => 16 },
'Visa' => { :start => '4', :length => [13, 16] },
}.freeze
def initialize(*args)
super
gsub!(/\D/, '')
@factors = [1,2]
@factors.cycle! if (length % 2) == 1
end
def type
return @type if @type
PATTERNS.each do |name, pat|
@type = name if [pat[:start]].flatten.any? { |s| match /
^#{s}/ } and [pat[:length]].flatten.any? { |l| length == l }
end
@type ||= 'Unknown'
end
def luhn_sum
@luhn_sum ||= split('').inject(0) do |sum, digit|
@factors.cycle!
sum += (digit.to_i * @factors.first).to_s.split('').inject(0) { |
s,d| s += d.to_i }
end
end
def luhn_valid?
(luhn_sum % 10) == 0
end
end
card_num = CCNum.new(ARGV.join)
puts "#{card_num} is a(n) #{card_num.luhn_valid? ? 'V' : 'Inv' }alid
#{card_num.type}"