luhn.go 1.5 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768
  1. // Package luhn generates and validates Luhn mod N check digits.
  2. package luhn
  3. import (
  4. "fmt"
  5. "strings"
  6. )
  7. // An alphabet is a string of N characters, representing the digits of a given
  8. // base N.
  9. type Alphabet string
  10. var (
  11. Base32 Alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567"
  12. )
  13. // Generate returns a check digit for the string s, which should be composed
  14. // of characters from the Alphabet a.
  15. func (a Alphabet) Generate(s string) (rune, error) {
  16. if err:=a.check();err!=nil{
  17. return 0,err
  18. }
  19. factor := 1
  20. sum := 0
  21. n := len(a)
  22. for i := range s {
  23. codepoint := strings.IndexByte(string(a), s[i])
  24. if codepoint == -1 {
  25. return 0, fmt.Errorf("Digit %q not valid in alphabet %q", s[i], a)
  26. }
  27. addend := factor * codepoint
  28. if factor == 2 {
  29. factor = 1
  30. } else {
  31. factor = 2
  32. }
  33. addend = (addend / n) + (addend % n)
  34. sum += addend
  35. }
  36. remainder := sum % n
  37. checkCodepoint := (n - remainder) % n
  38. return rune(a[checkCodepoint]), nil
  39. }
  40. // Validate returns true if the last character of the string s is correct, for
  41. // a string s composed of characters in the alphabet a.
  42. func (a Alphabet) Validate(s string) bool {
  43. t := s[:len(s)-1]
  44. c, err := a.Generate(t)
  45. if err != nil {
  46. return false
  47. }
  48. return rune(s[len(s)-1]) == c
  49. }
  50. // check returns an error if the given alphabet does not consist of unique characters
  51. func (a Alphabet) check() error {
  52. cm := make(map[byte]bool, len(a))
  53. for i := range a {
  54. if cm[a[i]] {
  55. return fmt.Errorf("Digit %q non-unique in alphabet %q", a[i], a)
  56. }
  57. cm[a[i]] = true
  58. }
  59. return nil
  60. }