From 1ef5669a325159c6d902c34e04e20ab24c8e77da Mon Sep 17 00:00:00 2001 From: Ferdy Pruis Date: Mon, 28 Dec 2020 12:51:11 +0100 Subject: [PATCH] Performance optimizations --- go.mod | 2 +- luhn.go | 78 ++++++++++++++++++++++++++++++++++----------------------- 2 files changed, 48 insertions(+), 32 deletions(-) diff --git a/go.mod b/go.mod index fabf3c9..3e38fc2 100644 --- a/go.mod +++ b/go.mod @@ -1,3 +1,3 @@ module github.com/ferdypruis/go-luhn -go 1.12 +go 1.15 diff --git a/luhn.go b/luhn.go index c0c00b5..a6418b3 100644 --- a/luhn.go +++ b/luhn.go @@ -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" @@ -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) }