# Luhn Algorithm In Ruby

# The Problem

I was given a take-home programming problem for an interview process. I was told to use the Luhn Algorithm to validate credit cards in this problem. The algorithm is trivial and yet I wasted more time Googling and Stack Overflowing, than it would have taken me to write the dang thing myself.

# The Solution

Below is a Ruby, out of the box solution, with testing

```
require "minitest"
require "minitest/autorun"
# Public: Validates number against Luhn 10 scheme
#
# Luhn Algo ripped from: http://en.wikipedia.org/wiki/Luhn_algorithm
# 1. From the rightmost digit, which is the check digit, moving left, double the value of every second digit; if product of this doubling operation is greater than 9 (e.g., 7 * 2 = 14).
# 2. Sum the digits of the products (e.g., 10: 1 + 0 = 1, 14: 1 + 4 = 5) together with the undoubled digits from the original number.
# 3. If the total modulo 10 is equal to 0 (if the total ends in zero) then the number is valid according to the Luhn formula; else it is not valid.
#
# Returns true or false
def luhn_valid?(cc_number)
number = cc_number.
gsub(/\D/, ''). # remove non-digits
reverse # read from right to left
sum, i = 0, 0
number.each_char do |ch|
n = ch.to_i
# Step 1
n *= 2 if i.odd?
# Step 2
n = 1 + (n - 10) if n >= 10
sum += n
i += 1
end
# Step 3
(sum % 10).zero?
end
describe "Luhn Algorithm" do
# Cards ripped from paypal: http://www.paypalobjects.com/en_US/vhelp/paypalmanager_help/credit_card_numbers.htm
CC_NUMBERS = {
"American Express" => "378282246310005",
"American Express 2" => "371449635398431",
"American Express Corporate" => "378734493671000",
"Australian BankCard" => "5610591081018250",
"Diners Club" => "30569309025904",
"Diners Club 2" => "38520000023237",
"Discover" => "6011111111111117",
"Discover 2" => "6011000990139424",
"JCB" => "3530111333300000",
"JCB 2" => "3566002020360505",
"MasterCard" => "5555555555554444",
"MasterCard 2" => "5105105105105100",
"Visa" => "4111111111111111",
"Visa 2" => "4012888888881881",
"Visa 3" => "4222222222222",
}
it "returns true for valid numbers" do
assert CC_NUMBERS.values.all? { |number| luhn_valid?(number) }
end
it "returns false for invalid numbers" do
CC_NUMBERS.values.each do |number|
me_turn_bad = (number.to_i + 1).to_s
refute luhn_valid?(me_turn_bad)
end
end
end
```

# Results

It may be an ugly method, but it does the trick.

blog comments powered by Disqus

#### Published

04 June 2013