From dd42100aa2f062a4e53399a3cabce732eaf0f6e5 Mon Sep 17 00:00:00 2001 From: Alexander Kiryukhin Date: Fri, 30 Aug 2019 02:53:06 +0300 Subject: [PATCH] Barcode and ISIN --- README.md | 48 ++++++++++++++++++++++++++++++++++++++++ barcode/barcode.go | 35 +++++++++++++++++++++++++++++ barcode/barcode_test.go | 22 ++++++++++++++++++ damm/damm_test.go | 2 +- errors.go | 1 + isin/isin.go | 49 +++++++++++++++++++++++++++++++++++++++++ isin/isin_test.go | 22 ++++++++++++++++++ luhn/luhn.go | 4 ++++ 8 files changed, 182 insertions(+), 1 deletion(-) create mode 100644 barcode/barcode.go create mode 100644 barcode/barcode_test.go create mode 100644 isin/isin.go create mode 100644 isin/isin_test.go diff --git a/README.md b/README.md index 989ebbf..e74c510 100644 --- a/README.md +++ b/README.md @@ -2,6 +2,54 @@ Pure Go implementations. +## ISIN + +> An International Securities Identification Number (ISIN) uniquely identifies a security. Its structure is defined in ISO 6166. The ISIN code is a 12-character alphanumeric code that serves for uniform identification of a security through normalization of the assigned National Number, where one exists, at trading and settlement. + +[Wikipedia](https://en.wikipedia.org/wiki/International_Securities_Identification_Number) + +### Usage + +```golang +import ( + "github.com/neonxp/checksum" + "github.com/neonxp/checksum/isin" +) +... +err := isin.Check("4000000000006") // +switch err { + case checksum.ErrInvalidNumber: + // Not a number + case checksum.ErrInvalidChecksum: + // Invalid checksum + case nil: + // Valid number +} +``` + +## Barcode EAN-8, UPC-12, EAN-13 + +Validate barcode's checksum + +### Usage + +```golang +import ( + "github.com/neonxp/checksum" + "github.com/neonxp/checksum/barcode" +) +... +err := barcode.Check("041689300494") // UPC-12 barcode +switch err { + case checksum.ErrInvalidNumber: + // Not a number + case checksum.ErrInvalidChecksum: + // Invalid checksum + case nil: + // Valid number +} +``` + ## Luhn algorithm > The Luhn algorithm or Luhn formula, also known as the "modulus 10" or "mod 10" algorithm, named after its creator, IBM scientist Hans Peter Luhn, is a simple checksum formula used to validate a variety of identification numbers, such as credit card numbers, IMEI numbers, National Provider Identifier numbers in the United States, Canadian Social Insurance Numbers, Israel ID Numbers, Greek Social Security Numbers (ΑΜΚΑ), and survey codes appearing on McDonald's, Taco Bell, and Tractor Supply Co. receipts. diff --git a/barcode/barcode.go b/barcode/barcode.go new file mode 100644 index 0000000..f400002 --- /dev/null +++ b/barcode/barcode.go @@ -0,0 +1,35 @@ +package barcode + +import ( + "strconv" + "strings" + + "github.com/neonxp/checksum" +) + +// Check barcode +func Check(number string) error { + sum := 0 + numbers := strings.Split(number, "") + l := len(numbers) + for i := l; i > 0; i-- { + ch := numbers[i-1] + num, err := strconv.Atoi(string(ch)) + if err != nil { + return checksum.ErrInvalidNumber + } + if (l-i)%2 != 0 { + sum += num * 3 + } else { + sum += num + } + } + if sum%10 != 0 { + return checksum.ErrInvalidChecksum + } + return nil +} + +func Generate(number string) (string, error) { + return "", checksum.ErrNotImplemented +} diff --git a/barcode/barcode_test.go b/barcode/barcode_test.go new file mode 100644 index 0000000..573691b --- /dev/null +++ b/barcode/barcode_test.go @@ -0,0 +1,22 @@ +package barcode + +import ( + "testing" + + "github.com/neonxp/checksum" +) + +func TestBarcode(t *testing.T) { + samples := map[string]error{ + "4600051000058": checksum.ErrInvalidChecksum, + "A600051000057": checksum.ErrInvalidNumber, + "4600051000057": nil, + "46009333": nil, + "041689300494": nil, + } + for num, result := range samples { + if err := Check(num); err != result { + t.Errorf("Expected %+v actual %+v for %s", result, err, num) + } + } +} diff --git a/damm/damm_test.go b/damm/damm_test.go index 1f338b8..94b3712 100644 --- a/damm/damm_test.go +++ b/damm/damm_test.go @@ -7,7 +7,7 @@ import ( "github.com/neonxp/checksum" ) -func TestLuhn(t *testing.T) { +func TestDamm(t *testing.T) { samples := map[string]error{ "456126121234546": checksum.ErrInvalidChecksum, "A56126121234546": checksum.ErrInvalidNumber, diff --git a/errors.go b/errors.go index fd57ccc..21973fa 100644 --- a/errors.go +++ b/errors.go @@ -5,4 +5,5 @@ import "errors" var ( ErrInvalidNumber = errors.New("invalid number") // number contains non numeric symbols ErrInvalidChecksum = errors.New("invalid checksum") // number not correct by luhn algorithm + ErrNotImplemented = errors.New("not implemented") // not implemented currently ) diff --git a/isin/isin.go b/isin/isin.go new file mode 100644 index 0000000..0117506 --- /dev/null +++ b/isin/isin.go @@ -0,0 +1,49 @@ +package isin + +import ( + "strconv" + "strings" + + "github.com/neonxp/checksum" +) + +// Check ISIN code +func Check(number string) error { + sum := 0 + numbersString := "" + for _, ch := range strings.ToLower(number) { + if int(ch) >= 'a' && int(ch) <= 'z' { + numbersString = numbersString + strconv.Itoa(int(ch-'a'+10)) + continue + } + numbersString = numbersString + string(ch) + } + numbers := strings.Split(numbersString, "") + l := len(numbers) + for i := l; i > 0; i-- { + ch := numbers[i-1] + num, err := strconv.Atoi(string(ch)) + if err != nil { + return checksum.ErrInvalidNumber + } + if (l-i)%2 != 0 { + n := num * 2 + if n > 9 { + sum += n - 9 + } else { + sum += n + } + } else { + sum += num + } + } + if sum%10 != 0 { + return checksum.ErrInvalidChecksum + } + return nil +} + +func Generate(number string) (string, error) { + // TODO implement + return "", checksum.ErrNotImplemented +} diff --git a/isin/isin_test.go b/isin/isin_test.go new file mode 100644 index 0000000..b7d084d --- /dev/null +++ b/isin/isin_test.go @@ -0,0 +1,22 @@ +package isin + +import ( + "testing" + + "github.com/neonxp/checksum" +) + +func TestISIN(t *testing.T) { + samples := map[string]error{ + "4600051000058": checksum.ErrInvalidChecksum, + "?600051000057": checksum.ErrInvalidNumber, + "A600051000057": checksum.ErrInvalidChecksum, + "4000000000006": nil, + "US0378331005": nil, + } + for num, result := range samples { + if err := Check(num); err != result { + t.Errorf("Expected %+v actual %+v for %s", result, err, num) + } + } +} diff --git a/luhn/luhn.go b/luhn/luhn.go index 0724320..36291e6 100644 --- a/luhn/luhn.go +++ b/luhn/luhn.go @@ -30,3 +30,7 @@ func Check(number string) error { } return nil } + +func Generate(number string) (string, error) { + return "", checksum.ErrNotImplemented +}