# \[QUIZ\] Checking Credit Cards (#122)

**URL:** <https://www.ruby-forum.com/t/quiz-checking-credit-cards-122/93221>\
**Category:** Ruby\
**Created:** [April 29, 2007, 3:05pm UTC](https://www.ruby-forum.com/t/quiz-checking-credit-cards-122/93221 "2007-04-29T15:05:56Z")\
**Posts on this page:** 2\
**Page:** 1

<div class="post-metadata">

**Author:** ![toadkicker](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/t/5fc32e/32.png) [@toadkicker](https://www.ruby-forum.com/u/toadkicker)\
**Post date:** [April 29, 2007, 3:05pm UTC](https://www.ruby-forum.com/t/quiz-checking-credit-cards-122/93221/1 "2007-04-29T15:05:56Z")

</div>

Spartan solution (along with its obscure sister below)  
🙂

# 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.4_10\*\*14…3.5_10**14-1 =\> “AMEX”,  
3.7\*10**14…3.8_10\*\*14-1 =\> “AMEX”,  
6.011_10 **15…6.012\*10** 15-1 =\> “Discover”,  
5.1_10\*\*15…5.6_10**15-1 =\> “MasterCard”,  
4\*10**12…5_10\*\*12-1 =\> “Visa”,  
4_10 **15…5\*10** 15-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 🙂

# preliminary data

sum=known=false;type=’’;t=0  
db = {  
3.4_10\*\*14…3.5_10**14-1 =\> “AMEX”,  
3.7\*10**14…3.8_10\*\*14-1 =\> “AMEX”,  
6.011_10 **15…6.012\*10** 15-1 =\> “Discover”,  
5.1_10\*\*15…5.6_10**15-1 =\> “MasterCard”,  
4\*10**12…5_10\*\*12-1 =\> “Visa”,  
4_10 **15…5\*10** 15-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}”

---

<div class="post-metadata">

**Author:** ![toadkicker](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/t/5fc32e/32.png) [@toadkicker](https://www.ruby-forum.com/u/toadkicker)\
**Post date:** [April 30, 2007, 5:53am UTC](https://www.ruby-forum.com/t/quiz-checking-credit-cards-122/93221/2 "2007-04-30T05:53:04Z")

</div>

Hi all,  
Here’s my solution…

(pastie: [Parked at Loopia](http://pastie.caboo.se/57591))

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](http://en.wikipedia.org/wiki/Credit_card_number)

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
