#lang scheme/base
(require "base.ss")
(require (only-in srfi/13 string-pad string-drop string-take))
(define (number->symbol num)
(string->symbol (number->string num)))
(define (symbol->number sym)
(string->number (symbol->string sym)))
(define (number+false->string+false num)
(and num (number->string num)))
(define (string+false->number+false str)
(and str (string->number str)))
(define (symbol+false->string+false num)
(and num (symbol->string num)))
(define (string+false->symbol+false sym)
(and sym (string->symbol sym)))
(define (number+false->symbol+false num)
(and num (number->symbol num)))
(define (symbol+false->number+false sym)
(and sym (symbol->number sym)))
(define (natural->hex-string num #:uppercase? [uppercase? #f] #:digits [digits 1] #:prefix? [prefix? #f])
(define (make-digit num)
(case num
[( 0) #\0] [( 1) #\1] [( 2) #\2] [( 3) #\3]
[( 4) #\4] [( 5) #\5] [( 6) #\6] [( 7) #\7]
[( 8) #\8] [( 9) #\9] [(10) #\a] [(11) #\b]
[(12) #\c] [(13) #\d] [(14) #\e] [(15) #\f]))
(define (make-digits num)
(if (zero? num)
null
(cons (make-digit (remainder num 16))
(make-digits (quotient num 16)))))
(let* ([ans0 (apply string (reverse (make-digits num)))]
[ans1 (if (and digits (< (string-length ans0) digits))
(string-pad ans0 digits #\0)
ans0)]
[ans2 (if uppercase?
(string-upcase ans1)
ans1)]
[ans3 (if prefix?
(string-append "0x" ans2)
ans2)])
ans3))
(define (hex-string->natural str #:prefix? [prefix? #f])
(define (type-error)
(if prefix?
(raise-type-error 'hex-string->natural "hex string with \"0x\" prefix" str)
(raise-type-error 'hex-string->natural "hex string" str)))
(define (parse-digit chr)
(case chr
[(#\0) 0] [(#\1) 1] [(#\2) 2] [(#\3) 3]
[(#\4) 4] [(#\5) 5] [(#\6) 6] [(#\7) 7]
[(#\8) 8] [(#\9) 9] [(#\a) 10] [(#\b) 11]
[(#\c) 12] [(#\d) 13] [(#\e) 14] [(#\f) 15]
[else (raise-type-error 'hex-string->natural "hex string" str)]))
(let ([str (if prefix?
(if (regexp-match #rx"^0x" str)
(string-drop str 2)
(type-error))
(if (zero? (string-length str))
(type-error)
str))])
(let loop ([digits (reverse (map parse-digit (string->list (string-downcase str))))] [exponent 1] [accum 0])
(if (null? digits)
accum
(loop (cdr digits)
(* exponent 16)
(+ accum (* (car digits) exponent)))))))
(provide/contract
[number->symbol (-> number? symbol?)]
[symbol->number (-> symbol? (or/c number? #f))]
[number+false->string+false (-> (or/c number? #f) (or/c string? #f))]
[string+false->number+false (-> (or/c string? #f) (or/c number? #f))]
[symbol+false->string+false (-> (or/c symbol? #f) (or/c string? #f))]
[string+false->symbol+false (-> (or/c string? #f) (or/c symbol? #f))]
[number+false->symbol+false (-> (or/c number? #f) (or/c symbol? #f))]
[symbol+false->number+false (-> (or/c symbol? #f) (or/c number? #f))]
[natural->hex-string (->* (natural-number/c) (#:uppercase? boolean? #:digits (or/c natural-number/c #f) #:prefix? boolean?) string?)]
[hex-string->natural (->* (string?) (#:prefix? boolean?) natural-number/c)])