## ~mendelmaleh/checksum

e71e2f6f91dd70e3fd9fce07672614204fce5348 — Mendel E 1 year, 1 month ago
```Initial commit
```
```7 files changed, 388 insertions(+), 0 deletions(-)

A damm.go
A damm_test.go
A luhn.go
A luhn_test.go
A utils.go
A verhoeff.go
A verhoeff_test.go
```
`A  => damm.go +28 -0`
```@@ 1,28 @@
+package checksum
+
+var quasi = [10][10]int{
+	{0, 3, 1, 7, 5, 9, 8, 6, 4, 2},
+	{7, 0, 9, 2, 1, 5, 4, 8, 6, 3},
+	{4, 2, 0, 6, 8, 7, 1, 3, 5, 9},
+	{1, 7, 5, 0, 9, 8, 3, 4, 2, 6},
+	{6, 1, 2, 3, 0, 4, 5, 9, 7, 8},
+	{3, 6, 7, 4, 2, 0, 9, 5, 8, 1},
+	{5, 8, 6, 9, 7, 2, 0, 1, 3, 4},
+	{8, 9, 4, 5, 3, 6, 2, 0, 1, 7},
+	{9, 4, 3, 8, 6, 1, 7, 2, 0, 5},
+	{2, 5, 8, 1, 4, 3, 6, 7, 9, 0},
+}
+
+func damm(i, n, c int) int {
+	return quasi[c][n]
+}
+
+// Damm checksum validation
+func Damm(num int) bool {
+	return IterOppositeInt(num, damm) == 0
+}
+
+// GetDamm calculates the Damm checksum digit for num.
+func GetDamm(num int) int {
+	return IterOppositeInt(num, damm)
+}

```
`A  => damm_test.go +50 -0`
```@@ 1,50 @@
+package checksum
+
+import (
+	"math/rand"
+	"testing"
+)
+
+func TestDamm(t *testing.T) {
+	cases := []struct {
+		num int
+		ok  bool
+	}{
+		{5724, true},
+	}
+
+	for i, test := range cases {
+		if Damm(test.num) != test.ok {
+			t.Errorf("TestDamm [%d] (%d): expected test to return %t", i, test.num, test.ok)
+		}
+	}
+}
+
+func TestGetDamm(t *testing.T) {
+	cases := []struct {
+		num, check int
+	}{
+		{5, 9},
+		{57, 7},
+		{572, 4},
+		{5720, 6},
+	}
+
+	for i, test := range cases {
+		if res := GetDamm(test.num); res != test.check {
+			t.Errorf("TestGetDamm [%d] (%d): expected check %d, got %d", i, test.num, test.check, res)
+		}
+	}
+}
+
+func BenchmarkDamm(b *testing.B) {
+	for n := 0; n < b.N; n++ {
+		Damm(rand.Int())
+	}
+}
+
+func BenchmarkGetDamm(b *testing.B) {
+	for n := 0; n < b.N; n++ {
+		GetDamm(rand.Int())
+	}
+}

```
`A  => luhn.go +39 -0`
```@@ 1,39 @@
+package checksum
+
+func luhn(i, n, c int) int {
+	if i%2 == 0 {
+		return c + n
+	}
+
+	return c + n*2/10 + n*2%10
+}
+
+func getluhn(i, n, c int) int {
+	if i%2 == 1 {
+		return c + n
+	}
+
+	return c + n*2/10 + n*2%10
+}
+
+// Luhn checksum validation
+func Luhn(num int) bool {
+	return IterInt(num, luhn)%10 == 0
+}
+
+// GetLuhn calculates the Luhn check digit for num.
+func GetLuhn(num int) int {
+	return IterInt(num, getluhn) * 9 % 10
+}
+
+// LuhnString checksum validation
+func LuhnString(num string) (bool, error) {
+	sum, err := IterString(num, luhn)
+	return sum%10 == 0, err
+}
+
+// GetLuhnString calculates the Luhn check digit for num.
+func GetLuhnString(num string) (int, error) {
+	sum, err := IterString(num, getluhn)
+	return sum * 9 % 10, err
+}

```
`A  => luhn_test.go +127 -0`
```@@ 1,127 @@
+package checksum
+
+import (
+	"math/rand"
+	"strconv"
+	"testing"
+)
+
+func TestLuhn(t *testing.T) {
+	cases := []int{18, 273, 79927398713}
+
+	for i, test := range cases {
+		if !Luhn(test) {
+			t.Errorf("Luhn [%d] (%d) failed", i, test)
+		}
+	}
+}
+
+func TestGetLuhn(t *testing.T) {
+	cases := []struct {
+		num, check int
+	}{
+		{1, 8},
+		{27, 3},
+		{7992739871, 3},
+	}
+
+	for i, test := range cases {
+		if res := GetLuhn(test.num); res != test.check {
+			t.Errorf("GetLuhn [%d] (%d): expected check %d, got %d", i, test.num, test.check, res)
+		}
+	}
+}
+
+func TestLuhnString(t *testing.T) {
+	cases := []string{"18", "273", "79927398713", "79 92   7398   713"}
+
+	for i, test := range cases {
+		res, err := LuhnString(test)
+		if err != nil {
+			t.Error(err)
+		}
+
+		if !res {
+			t.Errorf("LuhnString [%d] (%s) failed", i, test)
+		}
+	}
+}
+
+func TestGetLuhnString(t *testing.T) {
+	cases := []struct {
+		num   string
+		check int
+	}{
+		{"1", 8},
+		{"27", 3},
+		{"7992739871", 3},
+		{"79 92   7398   71", 3},
+	}
+
+	for i, test := range cases {
+		res, err := GetLuhnString(test.num)
+		if err != nil {
+			t.Error(err)
+		}
+
+		if res != test.check {
+			t.Errorf("GetLuhnString [%d] (%s): expected check %d, got %d", i, test.num, test.check, res)
+		}
+	}
+}
+
+func BenchmarkLuhn(b *testing.B) {
+	for n := 0; n < b.N; n++ {
+		Luhn(rand.Int())
+	}
+}
+
+func BenchmarkGetLuhn(b *testing.B) {
+	for n := 0; n < b.N; n++ {
+		GetLuhn(rand.Int())
+	}
+}
+
+func BenchmarkLuhnString(b *testing.B) {
+	for n := 0; n < b.N; n++ {
+		LuhnString(strconv.Itoa(rand.Int()))
+	}
+}
+
+func BenchmarkGetLuhnString(b *testing.B) {
+	for n := 0; n < b.N; n++ {
+		GetLuhnString(strconv.Itoa(rand.Int()))
+	}
+}
+
+func BenchmarkLuhn_Atoi(b *testing.B) {
+	for n := 0; n < b.N; n++ {
+		s, _ := strconv.Atoi(strconv.Itoa(rand.Int()))
+		Luhn(s)
+	}
+}
+
+func BenchmarkGetLuhn_Atoi(b *testing.B) {
+	for n := 0; n < b.N; n++ {
+		s, _ := strconv.Atoi(strconv.Itoa(rand.Int()))
+		GetLuhn(s)
+	}
+}
+
+func Benchmark_randInt(b *testing.B) {
+	for n := 0; n < b.N; n++ {
+		rand.Int()
+	}
+}
+
+func Benchmark_Itoa(b *testing.B) {
+	for n := 0; n < b.N; n++ {
+		strconv.Itoa(rand.Int())
+	}
+}
+
+func Benchmark_Atoi(b *testing.B) {
+	for n := 0; n < b.N; n++ {
+		strconv.Atoi(strconv.Itoa(rand.Int()))
+	}
+}

```
`A  => utils.go +58 -0`
```@@ 1,58 @@
+package checksum
+
+import (
+	"fmt"
+	"math"
+)
+
+// IterInt iterates smallest to largest, over each decimal digit in num,
+// and sets fn(index, digit, checksum) as the new sum, and returns it.
+func IterInt(num int, fn func(i, n, c int) int) (check int) {
+	for i, n := 0, num; n > 0; i, n = i+1, n/10 {
+		check = fn(i, n%10, check)
+	}
+
+	return check
+}
+
+// IterOppositeInt iterates largest to smallest, over each decimal digit in num,
+// and sets fn(index, digit, checksum) as the new sum, and returns it.
+func IterOppositeInt(num int, fn func(i, n, c int) int) (check int) {
+	digits := int(math.Log10(float64(num))) + 1
+
+	for i, n := 0, num; i < digits; i++ {
+		p := int(math.Pow10(digits - i - 1))
+		check, n = fn(i, n/p, check), n%p
+	}
+
+	return check
+}
+
+// IterString iterates over each decimal digit in num,
+// and sets fn(index, digit, checksum) as the new sum, and returns it.
+func IterString(num string, fn func(i, n, c int) int) (check int, err error) {
+	for _, r := range num {
+		if r == ' ' || r == '_' {
+			continue
+		}
+
+		if r < '0' || '9' < r {
+			return 0, fmt.Errorf("can't validate %q, invalid rune %c, should be a number", num, r)
+		}
+	}
+
+	bnum := []byte(num)
+	var index int
+	for i := 0; i < len(bnum); i++ {
+		r := bnum[len(bnum)-i-1]
+
+		if r == ' ' || r == '_' {
+			continue
+		}
+
+		check = fn(index, int(r-'0'), check)
+		index++
+	}
+
+	return
+}

```
`A  => verhoeff.go +44 -0`
```@@ 1,44 @@
+package checksum
+
+// https://en.m.wikibooks.org/wiki/Algorithm_Implementation/Checksums/Verhoeff_Algorithm#C
+var (
+	mult = [10][10]int{
+		{0, 1, 2, 3, 4, 5, 6, 7, 8, 9},
+		{1, 2, 3, 4, 0, 6, 7, 8, 9, 5},
+		{2, 3, 4, 0, 1, 7, 8, 9, 5, 6},
+		{3, 4, 0, 1, 2, 8, 9, 5, 6, 7},
+		{4, 0, 1, 2, 3, 9, 5, 6, 7, 8},
+		{5, 9, 8, 7, 6, 0, 4, 3, 2, 1},
+		{6, 5, 9, 8, 7, 1, 0, 4, 3, 2},
+		{7, 6, 5, 9, 8, 2, 1, 0, 4, 3},
+		{8, 7, 6, 5, 9, 3, 2, 1, 0, 4},
+		{9, 8, 7, 6, 5, 4, 3, 2, 1, 0},
+	}
+
+	perm = [8][10]int{
+		{0, 1, 2, 3, 4, 5, 6, 7, 8, 9},
+		{1, 5, 7, 6, 2, 8, 3, 0, 9, 4},
+		{5, 8, 0, 3, 7, 9, 6, 1, 4, 2},
+		{8, 9, 1, 6, 0, 4, 3, 5, 2, 7},
+		{9, 4, 5, 3, 1, 2, 6, 8, 7, 0},
+		{4, 2, 8, 6, 5, 7, 3, 9, 0, 1},
+		{2, 7, 9, 3, 8, 0, 6, 4, 1, 5},
+		{7, 0, 4, 6, 9, 1, 3, 2, 5, 8},
+	}
+
+	inv = [10]int{0, 4, 3, 2, 1, 5, 6, 7, 8, 9}
+)
+
+func verhoeff(i, n, c int) int {
+	return mult[c][perm[i%8][n]]
+}
+
+// Verhoeff checksum validation
+func Verhoeff(num int) bool {
+	return IterInt(num, verhoeff) == 0
+}
+
+// GetVerhoeff calculates the Verhoeff checksum digit for num.
+func GetVerhoeff(num int) int {
+	return inv[IterInt(num*10, verhoeff)]
+}

```
`A  => verhoeff_test.go +42 -0`
```@@ 1,42 @@
+package checksum
+
+import (
+	"math/rand"
+	"testing"
+)
+
+func TestVerhoeff(t *testing.T) {
+	cases := []int{2363}
+
+	for i, test := range cases {
+		if !Verhoeff(test) {
+			t.Errorf("Verhoeff [%d] (%d) failed", i, test)
+		}
+	}
+}
+
+func TestGetVerhoeff(t *testing.T) {
+	cases := []struct {
+		num, check int
+	}{
+		{236, 3},
+	}
+
+	for i, test := range cases {
+		if res := GetVerhoeff(test.num); res != test.check {
+			t.Errorf("GetVerhoeff [%d] (%d): expected check %d, got %d", i, test.num, test.check, res)
+		}
+	}
+}
+
+func BenchmarkVerhoeff(b *testing.B) {
+	for n := 0; n < b.N; n++ {
+		Verhoeff(rand.Int())
+	}
+}
+
+func BenchmarkGetVerhoeff(b *testing.B) {
+	for n := 0; n < b.N; n++ {
+		GetVerhoeff(rand.Int())
+	}
+}

```