38 lines
1.0 KiB
Racket
38 lines
1.0 KiB
Racket
#lang racket
|
||
(require srfi/14)
|
||
|
||
(define 0-char (char->integer #\0))
|
||
(define A-char (char->integer #\A))
|
||
|
||
(define (cusip-value c)
|
||
(cond
|
||
[(char-set-contains? char-set:digit c)
|
||
(- (char->integer c) 0-char)]
|
||
[(char-set-contains? char-set:upper-case c)
|
||
(+ 10 (- (char->integer c) A-char))]
|
||
[(char=? c #\*) 36]
|
||
[(char=? c #\@) 37]
|
||
[(char=? c #\#) 38]))
|
||
|
||
(define (cusip-check-digit cusip)
|
||
(modulo
|
||
(- 10
|
||
(modulo
|
||
(for/sum
|
||
((i (sequence-map add1 (in-range 8))) (c (in-string cusip)))
|
||
(let* ((v (cusip-value c)) (v′ (if (even? i) (* v 2) v)))
|
||
(+ (quotient v′ 10) (modulo v′ 10)))) 10)) 10))
|
||
|
||
(define (CUSIP? s)
|
||
(char=? (string-ref s (sub1 (string-length s)))
|
||
(integer->char (+ 0-char (cusip-check-digit s)))))
|
||
|
||
(module+ test
|
||
(require rackunit)
|
||
(check-true (CUSIP? "037833100"))
|
||
(check-true (CUSIP? "17275R102"))
|
||
(check-true (CUSIP? "38259P508"))
|
||
(check-true (CUSIP? "594918104"))
|
||
(check-false (CUSIP? "68389X106"))
|
||
(check-true (CUSIP? "68389X105")))
|