Initial Commit
This commit is contained in:
71
go/luhn/README.md
Normal file
71
go/luhn/README.md
Normal file
@@ -0,0 +1,71 @@
|
||||
# Luhn
|
||||
|
||||
Write a program that can take a number and determine whether or not it is valid per the Luhn formula.
|
||||
|
||||
The Luhn formula is a simple checksum formula used to validate a variety
|
||||
of identification numbers, such as credit card numbers and Canadian
|
||||
Social Insurance Numbers.
|
||||
|
||||
The formula verifies a number against its included check digit, which is
|
||||
usually appended to a partial number to generate the full number. This
|
||||
number must pass the following test:
|
||||
|
||||
- Counting from rightmost digit (which is the check digit) and moving
|
||||
left, double the value of every second digit.
|
||||
- For any digits that thus become 10 or more, subtract 9 from the
|
||||
result.
|
||||
- 1111 becomes 2121.
|
||||
- 8763 becomes 7733 (from 2×6=12 → 12-9=3 and 2×8=16 → 16-9=7).
|
||||
- Add all these digits together.
|
||||
- 1111 becomes 2121 sums as 2+1+2+1 to give a check digit of 6.
|
||||
- 8763 becomes 7733, and 7+7+3+3 is 20.
|
||||
|
||||
If the total ends in 0 (put another way, if the total modulus 10 is
|
||||
congruent to 0), then the number is valid according to the Luhn formula;
|
||||
else it is not valid. So, 1111 is not valid (as shown above, it comes
|
||||
out to 6), while 8763 is valid (as shown above, it comes out to 20).
|
||||
|
||||
Write a program that, given a number
|
||||
|
||||
- Can check if it is valid per the Luhn formula. This should treat, for
|
||||
example, "2323 2005 7766 3554" as valid.
|
||||
- Can return the checksum, or the remainder from using the Luhn method.
|
||||
- Can add a check digit to make the number valid per the Luhn formula and
|
||||
return the original number plus that digit. This should give "2323 2005 7766
|
||||
3554" in response to "2323 2005 7766 355".
|
||||
|
||||
## About Checksums
|
||||
|
||||
A checksum has to do with error-detection. There are a number of different
|
||||
ways in which a checksum could be calculated.
|
||||
|
||||
When transmitting data, you might also send along a checksum that says how
|
||||
many bytes of data are being sent. That means that when the data arrives on
|
||||
the other side, you can count the bytes and compare it to the checksum. If
|
||||
these are different, then the data has been garbled in transmission.
|
||||
|
||||
In the Luhn problem the final digit acts as a sanity check for all the prior
|
||||
digits. Running those prior digits through a particular algorithm should give
|
||||
you that final digit.
|
||||
|
||||
It doesn't actually tell you if it's a real credit card number, only that it's
|
||||
a plausible one. It's the same thing with the bytes that get transmitted --
|
||||
you could have the right number of bytes and still have a garbled message. So
|
||||
checksums are a simple sanity-check, not a real in-depth verification of the
|
||||
authenticity of some data. It's often a cheap first pass, and can be used to
|
||||
quickly discard obviously invalid things.
|
||||
|
||||
|
||||
To run the tests simply run the command `go test` in the exercise directory.
|
||||
|
||||
If the test suite contains benchmarks, you can run these with the `-bench`
|
||||
flag:
|
||||
|
||||
go test -bench .
|
||||
|
||||
For more detailed info about the Go track see the [help
|
||||
page](http://help.exercism.io/getting-started-with-go.html).
|
||||
|
||||
## Source
|
||||
|
||||
The Luhn Algorithm on Wikipedia [view source](http://en.wikipedia.org/wiki/Luhn_algorithm)
|
58
go/luhn/luhn.go
Normal file
58
go/luhn/luhn.go
Normal file
@@ -0,0 +1,58 @@
|
||||
package luhn
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"regexp"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
// Valid takes a numeric string and returns whether
|
||||
// it's Luhn valid.
|
||||
func Valid(toCheck string) bool {
|
||||
return GetRemainder(toCheck) == 0
|
||||
}
|
||||
|
||||
// GetRemainder returns the leftover from
|
||||
// running a number through the Luhn checker
|
||||
func GetRemainder(toCheck string) int {
|
||||
// Remove any non-numeric characters
|
||||
r := regexp.MustCompile("[^0-9]+")
|
||||
toCheck = r.ReplaceAllString(toCheck, "")
|
||||
|
||||
if len(toCheck) == 0 {
|
||||
return -1
|
||||
}
|
||||
var total int
|
||||
var double bool
|
||||
for i := range toCheck {
|
||||
var add int
|
||||
var err error
|
||||
idx := (len(toCheck) - 1) - i
|
||||
if add, err = strconv.Atoi(string(toCheck[idx])); err != nil {
|
||||
return -1
|
||||
}
|
||||
if double {
|
||||
add = add * 2
|
||||
if add >= 10 {
|
||||
add = add - 9
|
||||
}
|
||||
}
|
||||
double = !double
|
||||
total += add
|
||||
}
|
||||
return total % 10
|
||||
}
|
||||
|
||||
// AddCheck takes a number and returns the appropriate
|
||||
// number with an added digit that makes it valid
|
||||
func AddCheck(toCheck string) string {
|
||||
if GetRemainder(toCheck) != 0 {
|
||||
for i := 0; i <= 9; i++ {
|
||||
tst := fmt.Sprintf("%s%d", toCheck, i)
|
||||
if GetRemainder(tst) == 0 {
|
||||
return tst
|
||||
}
|
||||
}
|
||||
}
|
||||
return toCheck
|
||||
}
|
54
go/luhn/luhn_test.go
Normal file
54
go/luhn/luhn_test.go
Normal file
@@ -0,0 +1,54 @@
|
||||
package luhn
|
||||
|
||||
import "testing"
|
||||
|
||||
var validTests = []struct {
|
||||
n string
|
||||
ok bool
|
||||
}{
|
||||
{"738", false},
|
||||
{"8739567", true},
|
||||
{"1111", false},
|
||||
{"8763", true},
|
||||
{" ", false},
|
||||
{"", false},
|
||||
{"2323 2005 7766 3554", true},
|
||||
}
|
||||
|
||||
var addTests = []struct{ raw, luhn string }{
|
||||
{"123", "1230"},
|
||||
{"873956", "8739567"},
|
||||
{"837263756", "8372637564"},
|
||||
{"2323 2005 7766 355", "2323 2005 7766 3554"},
|
||||
// bonus Unicode cases
|
||||
// {"2323·2005·7766·355", "2323·2005·7766·3554"},
|
||||
// {"123", "1230"},
|
||||
}
|
||||
|
||||
func TestValid(t *testing.T) {
|
||||
for _, test := range validTests {
|
||||
if ok := Valid(test.n); ok != test.ok {
|
||||
t.Fatalf("Valid(%s) = %t, want %t.", test.n, ok, test.ok)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestAddCheck(t *testing.T) {
|
||||
for _, test := range addTests {
|
||||
if luhn := AddCheck(test.raw); luhn != test.luhn {
|
||||
t.Fatalf("AddCheck(%s) = %s, want %s.", test.raw, luhn, test.luhn)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkValid(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
Valid("2323 2005 7766 3554")
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkAddCheck(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
AddCheck("2323 2005 7766 355")
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user