aboutsummaryrefslogtreecommitdiff
path: root/rosetta/luhn.neb
blob: ac06e17cacd8369a59b98c19bac6401b5d9afa1f (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
(func odd-elems (lst)
    (odd-elems lst (list)))

(func odd-elems (lst acc)
    (if (eq? 0 (length lst))
        acc
        (odd-elems (rest (rest lst)) (append acc (first lst)))))

(func even-elems (lst)
    (even-elems lst (list)))

(func even-elems (lst acc)
    (if (>= 1 (length lst))
        acc
        (even-elems (rest (rest lst)) (append acc (first (rest lst))))))

(func luhn? (num)
    ;; algo:
    ;; with digits reversed, sum odd digits
    ;; for all even digits:
    ;;   - double them
    ;;   - sum the digits (i.e. 16 -> 1 + 6 -> 7)
    ;;   - sum everything
    ;; sum both values, and if the result ends in 0, it's valid 
    (def reverse-digits (reverse (->string num)))
    ; sum the odd digits
    (def s1
        (reduce 
            (lambda (x y) (+ x (string->int y)))
            (odd-elems reverse-digits) 0))
    (def s2
        ; sum everything
        (reduce (lambda (x y) (+ x y))
            ; sum each of the digits
            (map (lambda (x) (apply + x))
                ; split the resulting numbers into digits
                (map (lambda (x) (map string->int (split (->string x))))
                    ; double the even digits
                    (map (lambda (x) (* 2 (string->int x))) 
                        (even-elems reverse-digits)))) 0))
    (int? (/ (+ s1 s2) 10)))

(func base36-to-base10 (value :string)
    (base36-to-base10 (ord value)))

(func base36-to-base10 (value :int)
    (if (< value 32)
        (+ 9 value)
        (base36-to-base10 (- value 32))))

(func isin? (value)
    ;; algo:
    ;;  - must be 12 digits
    ;;  - first 2 must be letters
    ;;  - last must be a number
    ;;  - interpret all values as base 32
    ;;  - replace them as if replacing characters in a string
    ;;  - run luhn
    (def digits (split "0123456789"))
    (and
        (eq? 12 (length value))
        (not (in? (first value) digits))
        (not (in? (first (rest value)) digits))
        (in? (last value) digits)
        (luhn? (string->int
            (apply concat
                (reduce
                    (lambda (acc val)
                        (append acc
                            (->string (try
                                (string->int val)
                                (base36-to-base10 val)))))
                    (split value) (list)))))))