[QUIZ] Checking Credit Cards (#122)

Spartan solution (along with its obscure sister below)
:slight_smile:

usage: ruby quiz122.rb <credit card number with no

dashes or spaces>

example: ruby quiz122.rb 341275084937123

some variables

user_input = $*[0]
sum_is_valid = card_is_known = false
sum = 0

the limited database

db = {
3.410**14…3.51014-1 => “AMEX”,
3.7*10
14…3.810**14-1 => “AMEX”,
6.011
1015…6.012*1015-1 => “Discover”,
5.110**15…5.61015-1 => “MasterCard”,
4*10
12…510**12-1 => “Visa”,
4
1015…5*1015-1 => “Visa”,
}

check the database

number = user_input.to_i
type = ‘unknown’
db.each { |key,value| type = value if key === number }
card_is_known ||= type != ‘unknown’

ugly way to code the Luhn algorithm

1. initialize sum

2. reverse the supplied string

3. turn the string into an array with one digit per

index

4. turn the individual digits into integers

5. sum everything up …

5a. add the digit to the sum if the array index is

odd

5b. add the digits making up twice the value of the

digit if the array index is even

6. test 2 is valid if the sum is divisible by 10

(user_input.reverse.scan(/\d/).map! { |digit|
digit.to_i }).each_with_index { |digit,index| sum += (
index % 2 == 0 ? digit : digit.divmod(5)[1] * 2 +
digit.divmod(5)[0] ) }
puts sum
sum_is_valid ||= sum % 10 == 0

print results

card_is_known = (card_is_known ? “is” : “is not”)
sum_is_valid = (sum_is_valid ? “is” : “is not”)
puts “The card #{card_is_known} known.”
puts “The card type is #{type}.”
puts “The card number sum #{sum_is_valid} valid.”


Solution, obscure version :slight_smile:

preliminary data

sum=known=false;type=’’;t=0
db = {
3.410**14…3.51014-1 => “AMEX”,
3.7*10
14…3.810**14-1 => “AMEX”,
6.011
1015…6.012*1015-1 => “Discover”,
5.110**15…5.61015-1 => “MasterCard”,
4*10
12…510**12-1 => “Visa”,
4
1015…5*1015-1 => “Visa”,
}

check stuff

db.each { |k,v| type=v if
k===$[0].to_i};known||=type!=’’
($
[0].reverse.scan(/\d/).map!{|d|d.to_i}).each_with_index{|d,i|t+=(i%2==0
? d : d.divmod(5)[1]*2+d.divmod(5)[0])};sum||=t%10==0

cryptically print results

puts “Known: #{known}\nType: #{type}\nValid sum: #{sum}”

Hi all,
Here’s my solution…

(pastie: Parked at Loopia)

regards,
rolando.-

#!/usr/bin/env ruby

RubyQuiz #122

Solution by Rolando A. M.

rabarca (at) scio.cl

small hack to allow intervals as a cc length

class Fixnum
def include?(n)
self == n
end
end

module CChecker

prefixes taken from wikipedia

Payment card number - Wikipedia

PREFIXES = [
# regexp, length, name, checking algorithm
# length can be a fixnum, array or range.
# the algorithm must be in the CChecker module
[/^(34|37)\d+$/, 15, “AMEX”, :luhn],
[/^30[0-5]\d+$/, 14, “Diners Club Carte Blanche”, :luhn],
[/^36\d+$/, 14, “Diners Club International”, :luhn],
[/^55\d+$/, 16, “Diners Club US & Canada”, :luhn],
[/^(6011|65)\d+$/, 16, “Discover”, :luhn],
[/^35\d+$/, 16, “JCB”, :luhn],
[/^(1800|2131)\d+$/, 15, “JCB”, :luhn],
[/^(5020|5038|6759)\d+$/, 16, “Maestro”, :luhn],
[/^(51|54|55)\d+$/, 16, “Mastercard”, :luhn],
[/^(6334|6767)\d+$/, [16,19], “Solo”, :luhn],
[/^4\d+$/, [13,16], “Visa”, :luhn],
[/^(417500|4917|4913)\d+$/, 16, “Visa Electron”, :luhn]
]
UNKNOWN_PREFIX = [nil, 0, “Unknown”, :luhn]

def CChecker.usage(doexit = false)
puts “usage: cchecker.rb ”
exit if doexit
end

try to identify the card

def CChecker.check_prefix(ccnumber)
pr = PREFIXES.detect {|p| p[0].match(ccnumber) &&
p[1].include?(ccnumber.length)}
(pr.nil?) ? UNKNOWN_PREFIX : pr
end

do the complete check of the cc:

1.- try to identify

2.- apply algorithm (should return true/false)

returns an array: [isvalid, card_identifier]

def CChecker.check(ccnumber)
ccnumber = ccnumber.to_s.delete(" ")
pr = check_prefix(ccnumber)
[send(pr[3], ccnumber), pr[2]]
end

classic Luhn’s algorithm

def CChecker.luhn(ccnumber)
sum = 0
ccnumber.reverse.split(//).each_with_index do |c, i|
cx = c[0]-48; # this should be faster than c.to_i, right?
next if cx > 9 || cx < 0 # only numbers, please
if (i+1) & 1 == 0
cx *= 2
cx = (cx/10 + cx%10) if cx > 9
end
sum += cx
end
sum % 10 == 0
end
end

CChecker::usage(true) if ARGV.size != 1
valid, card = CChecker::check(ARGV[0])
if valid
puts “#{card} Valid”
else
puts “#{card} Invalid”
end