(module units mzscheme
(provide
unit-convert
unit-convert->string
unit-convert->rounded->string
)
(define (find-all-conversion-paths L)
(let ((H (make-hash-table))
(paths (list)))
(define (all-units L)
(if (null? L)
(list)
(let ((from (hash-table-get H (caar L) (lambda () (caar L))))
(to (hash-table-get H (cadar L) (lambda () (cadar L)))))
(hash-table-put! H (caar L) #t)
(hash-table-put! H (cadar L) #t)
(cond
((and (symbol? from) (symbol? to))
(cons from (cons to (all-units (cdr L)))))
((symbol? from)
(cons from (all-units (cdr L))))
((symbol? to)
(cons to (all-units (cdr L))))
(else (all-units (cdr L)))))))
(define (construct-paths streng units L)
(define (can-and-should-reach? from to type L)
(if (null? L)
#f
(let ((f (caar L))
(t (cadar L))
(tt (caddar L)))
(if (and (eq? f from) (eq? t to) (eq? tt type))
#t
(can-and-should-reach? from to type (cdr L))))))
(define (type-of-streng streng)
(car streng))
(let ((last (car (reverse streng))))
(let ((R
(apply append
(map (lambda (unit)
(if (can-and-should-reach? last unit (type-of-streng streng) L)
(let ((new-streng (append streng (list unit))))
(cons new-streng (construct-paths new-streng units L)))
(list)))
units))))
R)))
(define (identity-strengs units)
(map (lambda (unit)
(list unit unit))
units))
(let ((strengs (map (lambda (E)
(apply (lambda (from to type proc)
(list type from))
E))
L))
(units (all-units L)))
(append (identity-strengs units)
(map (lambda (streng) (cdr streng))
(apply append
(map (lambda (streng)
(construct-paths streng units L))
strengs)))))))
(define (create-conversion streng L)
(define (find-transition from to L)
(if (eq? from to)
(lambda (x) x)
(if (null? L)
(error (format "Cannot find transition from ~a to ~a" from to))
(if (and (eq? (caar L) from) (eq? (cadar L) to))
(cadddr (car L))
(find-transition from to (cdr L))))))
(define (make-calculation streng)
(if (null? (cddr streng))
(find-transition (car streng) (cadr streng) L)
(let ((F (find-transition (car streng) (cadr streng) L))
(G (make-calculation (cdr streng))))
(lambda (x) (F (G x))))))
(make-calculation streng))
(define (create-conversions strengs L)
(let ((H (make-hash-table 'equal)))
(for-each (lambda (streng)
(let ((from (car streng))
(to (car (reverse streng))))
(hash-table-put! H (list from to) (create-conversion streng L))))
strengs)
H))
(define MINIMAL-CONVERSIONS
(list
`(kb b ,(lambda (x) (* x 1024)) ,(lambda (x) (/ x 1024)))
`(mb kb ,(lambda (x) (* x 1024)) ,(lambda (x) (/ x 1024)))
`(gb mb ,(lambda (x) (* x 1024)) ,(lambda (x) (/ x 1024)))
`(tb gb ,(lambda (x) (* x 1024)) ,(lambda (x) (/ x 1024)))
`(w d ,(lambda (x) (* x 7)) ,(lambda (x) (/ x 7)))
`(d h ,(lambda (x) (* x 24)) ,(lambda (x) (/ x 24)))
`(h m ,(lambda (x) (* x 60)) ,(lambda (x) (/ x 60)))
`(m s ,(lambda (x) (* x 60)) ,(lambda (x) (/ x 60)))
`(s ms ,(lambda (x) (* x 1000)) ,(lambda (x) (/ x 1000)))
`(s mus ,(lambda (x) (* x 1000)) ,(lambda (x) (/ x 1000)))
`(mus ns ,(lambda (x) (* x 1000)) ,(lambda (x) (/ x 1000)))
`(kg g ,(lambda (x) (* x 1000)) ,(lambda (x) (/ x 1000)))
`(l dl ,(lambda (x) (* x 10)) ,(lambda (x) (/ x 10)))
`(dl cl ,(lambda (x) (* x 10)) ,(lambda (x) (/ x 10)))
`(cl ml ,(lambda (x) (* x 10)) ,(lambda (x) (/ x 10)))
`(km m ,(lambda (x) (* x 1000)) ,(lambda (x) (/ x 1000)))
`(m dm ,(lambda (x) (* x 10)) ,(lambda (x) (/ x 10)))
`(dm cm ,(lambda (x) (* x 10)) ,(lambda (x) (/ x 10)))
`(cm mm ,(lambda (x) (* x 10)) ,(lambda (x) (/ x 10)))
))
(define conversion-hash
(let ((MINCONVS
(apply append
(map (lambda (E)
(apply (lambda (from to smaller bigger)
(list
(list from to 'smaller smaller)
(list to from 'bigger bigger)))
E))
MINIMAL-CONVERSIONS))))
(create-conversions (find-all-conversion-paths MINCONVS)
MINCONVS)))
(define q
(let ((MINCONVS
(apply append
(map (lambda (E)
(apply (lambda (from to smaller bigger)
(list
(list from to 'smaller smaller)
(list to from 'bigger bigger)))
E))
MINIMAL-CONVERSIONS))))
(find-all-conversion-paths MINCONVS)))
(define (internal-convert A from to)
(let ((convert (hash-table-get conversion-hash
(list from to)
(lambda () (lambda (x) #f)))))
(convert A)))
(define (unit-convert A from to . threshold)
(if (list? to)
(let ((T (if (null? threshold) 1 (car threshold)))
(B #f)
(U (car to)))
(for-each (lambda (t)
(let ((R (internal-convert A from t)))
(if (not (eq? R #f))
(if (eq? B #f)
(begin
(set! B R)
(set! U t))
(if (or (and (< R B) (>= R T))
(and (> T B) (>= R T)))
(begin
(set! B R)
(set! U t)))))))
to)
(values U B))
(internal-convert A from to)))
(define (unit-convert->string A from to . threshold)
(call-with-values
(lambda ()
(if (list? to)
(if (null? threshold)
(unit-convert A from to)
(unit-convert A from to (car threshold)))
(if (null? threshold)
(values to (unit-convert A from to))
(values to (unit-convert A from to (car threshold))))))
(lambda (unit value)
(let ((u (symbol->string unit)))
(if (eq? #f value)
(string-append "?" u)
(string-append (number->string value) u))))))
(define (unit-convert->rounded->string A from to digits . threshold)
(call-with-values
(lambda ()
(if (list? to)
(if (null? threshold)
(unit-convert A from to)
(unit-convert A from to (car threshold)))
(if (null? threshold)
(values to (unit-convert A from to))
(values to (unit-convert A from to (car threshold))))))
(lambda (unit value)
(let ((u (symbol->string unit)))
(if (eq? #f value)
(string-append "?" u)
(let ((fold (exact->inexact (expt 10 digits))))
(string-append (number->string (/ (round (* value fold)) fold)) u)))))))
)