luhn.go 1.7 KB

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