Skip to content

Commit

Permalink
Performance optimizations
Browse files Browse the repository at this point in the history
  • Loading branch information
ferdypruis committed Jan 4, 2021
1 parent 19211ce commit 1ef5669
Show file tree
Hide file tree
Showing 2 changed files with 48 additions and 32 deletions.
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
module github.com/ferdypruis/go-luhn

go 1.12
go 1.15
78 changes: 47 additions & 31 deletions luhn.go
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
// Package luhn implements the Luhn algorithm, or Luhn formula, also known as the "modulus 10" or "mod 10" algorithm.
package luhn

type Error string

func (e Error) Error() string { return "luhn: " + string(e) }
import (
"fmt"
)

// mod10 is the pre-calculated value of 10 minus the modulos-10 of every digit
const mod10 = "0987654321"
Expand All @@ -12,60 +12,76 @@ const mod10 = "0987654321"
// When the result of doubling is greater than 9 the digits are added together; 16 => 1 + 6 => 7
var doubles = [10]int{0, 2, 4, 6, 8, 1, 3, 5, 7, 9}

// Checksum returns the Lühn check digit for number.
// Checksum returns the check digit for the number.
func Checksum(number string) (string, error) {
if number == "" {
l := len(number)
if l == 0 {
return "", Error("input is too short")
}

// determine even or odd length, so we know when to double
double := 1&len(number) == 1

var sum int
for _, c := range []byte(number) {
// assert character is numeric
if c < '0' || c > '9' {
return "", Error("input contains a non-number; " + string(c))

for i := l - 1; i >= 0; i -= 2 {
n := int(number[i] - '0')
if n < 0 || n > 9 {
return "", Error(fmt.Sprintf("character %q at position %d is not a digit", number[i], i))
}

// numeric value from character
n := int(c - '0')
sum += doubles[n]
}

// double every other digit
if double {
n = doubles[n]
for i := l - 2; i >= 0; i -= 2 {
n := int(number[i] - '0')
if n < 0 || n > 9 {
return "", Error(fmt.Sprintf("character %q at position %d is not a digit", number[i], i))
}
double = !double

sum += n
}

unit := sum % 10
return mod10[unit : unit+1], nil
return mod10[unit:unit+1], nil
}

// Valid returns whether number verifies against its appended check digit
// Valid returns whether number verifies against its appended check digit.
func Valid(number string) bool {
l := len(number) - 1
if l < 1 {
l := len(number)
if l < 2 {
return false
}

checkdigit, err := Checksum(number[:l])
if err != nil {
return false
var sum = int(number[l-1] - '0')

for i := l - 2; i >= 0; i -= 2 {
n := int(number[i] - '0')
if n < 0 || n > 9 {
return false
}

sum += doubles[n]
}

// compare checksum with checkdigit in input
return checkdigit == number[l:]
for i := l - 3; i >= 0; i -= 2 {
n := int(number[i] - '0')
if n < 0 || n > 9 {
return false
}

sum += n
}

return sum % 10 == 0
}

// Sign returns number with its Lühn check digit appended
func Sign(number string) (string, error) {
digit, err := Checksum(number)
// Sign returns the partial number with its Lühn check digit appended, creating a full number.
func Sign(partial string) (string, error) {
checkdigit, err := Checksum(partial)
if err != nil {
return "", err
}

return number + digit, nil
return partial + checkdigit, nil
}

type Error string
func (e Error) Error() string { return "luhn: " + string(e) }

0 comments on commit 1ef5669

Please sign in to comment.