(.module: {#.doc "Implementations of common structures for Lux's primitive number types."} [lux #* [control number [monoid (#+ Monoid)] [equivalence (#+ Equivalence)] hash ["." order (#+ Order)] enum interval [codec (#+ Codec)]] [data ["." error (#+ Error)] ["." maybe] ["." text] [collection ["." array (#+ Array)]]] ["." math]] [/ ["." i64]]) ## [Structures] (do-template [ ] [(structure: #export _ (Equivalence ) (def: = ))] [ Nat n/=] [ Int i/=] [ Rev r/=] [Frac f/=] ) (do-template [ ] [(structure: #export _ (Order ) (def: eq ) (def: < ) (def: <= ) (def: > ) (def: >= ))] [ Nat Equivalence n/< n/<= n/> n/>=] [ Int Equivalence i/< i/<= i/> i/>=] [Rev Equivalence r/< r/<= r/> r/>=] [Frac Equivalence f/< f/<= f/> f/>=] ) (structure: #export _ (Number Nat) (def: + n/+) (def: - n/-) (def: * n/*) (def: / n//) (def: % n/%) (def: negate id) (def: abs id) (def: (signum x) (case x 0 0 _ 1)) ) (do-template [ <+> <-> <*> <%> <=> <<> <0> <1> <-1>] [(structure: #export _ (Number ) (def: + <+>) (def: - <->) (def: * <*>) (def: / ) (def: % <%>) (def: negate (<*> <-1>)) (def: (abs x) (if (<<> <0> x) (<*> <-1> x) x)) (def: (signum x) (cond (<=> <0> x) <0> (<<> <0> x) <-1> ## else <1>)) )] [ Int Order i/+ i/- i/* i// i/% i/= i/< +0 +1 -1] [Frac Order f/+ f/- f/* f// f/% f/= f/< +0.0 +1.0 -1.0] ) (structure: #export _ (Number Rev) (def: + r/+) (def: - r/-) (def: * r/*) (def: / r//) (def: % r/%) (def: (negate x) (r/- x (:coerce Rev -1))) (def: abs id) (def: (signum x) (:coerce Rev -1))) (do-template [ ] [(structure: #export _ (Enum ) (def: order ) (def: succ ) (def: pred ))] [Nat Order inc dec] [Int Order inc dec] [Frac Order (f/+ ("lux frac smallest")) (f/- ("lux frac smallest"))] [Rev Order inc dec] ) (do-template [ ] [(structure: #export _ (Interval ) (def: enum ) (def: top ) (def: bottom ))] [ Nat Enum (:coerce Nat -1) 0] [ Int Enum +9_223_372_036_854_775_807 -9_223_372_036_854_775_808] [Frac Enum ("lux frac max") ("lux frac min")] [ Rev Enum (:coerce Rev -1) (:coerce Rev 0)] ) (do-template [ ] [(structure: #export (Monoid ) (def: identity ) (def: compose ))] [ Add@Monoid Nat 0 n/+] [ Mul@Monoid Nat 1 n/*] [ Max@Monoid Nat (:: Interval bottom) n/max] [ Min@Monoid Nat (:: Interval top) n/min] [ Add@Monoid Int +0 i/+] [ Mul@Monoid Int +1 i/*] [ Max@Monoid Int (:: Interval bottom) i/max] [ Min@Monoid Int (:: Interval top) i/min] [Add@Monoid Frac +0.0 f/+] [Mul@Monoid Frac +1.0 f/*] [Max@Monoid Frac (:: Interval bottom) f/max] [Min@Monoid Frac (:: Interval top) f/min] [ Add@Monoid Rev (:: Interval bottom) r/+] [ Mul@Monoid Rev (:: Interval top) r/*] [ Max@Monoid Rev (:: Interval bottom) r/max] [ Min@Monoid Rev (:: Interval top) r/min] ) (do-template [ ] [(def: #export {#.doc } Frac (f// +0.0 ))] [not-a-number +0.0 "Not a number."] [positive-infinity +1.0 "Positive infinity."] [negative-infinity -1.0 "Negative infinity."] ) (def: #export (not-a-number? number) {#.doc "Tests whether a frac is actually not-a-number."} (-> Frac Bit) (not (f/= number number))) (def: #export (frac? value) (-> Frac Bit) (not (or (not-a-number? value) (f/= positive-infinity value) (f/= negative-infinity value)))) (do-template [ ] [(structure: #export _ (Codec Text ) (def: (encode x) ( [x])) (def: (decode input) (case ( [input]) (#.Some value) (#error.Success value) #.None (#error.Error ))))] [Frac "lux frac encode" "lux frac decode" "Could not decode Frac"] ) ## [Values & Syntax] (def: (get-char full idx) (-> Text Nat (Maybe Text)) ("lux text clip" full idx (inc idx))) (def: (binary-character value) (-> Nat (Maybe Text)) (case value 0 (#.Some "0") 1 (#.Some "1") _ #.None)) (def: (binary-value digit) (-> Text (Maybe Nat)) (case digit "0" (#.Some 0) "1" (#.Some 1) _ #.None)) (def: (octal-character value) (-> Nat (Maybe Text)) (case value 0 (#.Some "0") 1 (#.Some "1") 2 (#.Some "2") 3 (#.Some "3") 4 (#.Some "4") 5 (#.Some "5") 6 (#.Some "6") 7 (#.Some "7") _ #.None)) (def: (octal-value digit) (-> Text (Maybe Nat)) (case digit "0" (#.Some 0) "1" (#.Some 1) "2" (#.Some 2) "3" (#.Some 3) "4" (#.Some 4) "5" (#.Some 5) "6" (#.Some 6) "7" (#.Some 7) _ #.None)) (def: (decimal-character value) (-> Nat (Maybe Text)) (case value 0 (#.Some "0") 1 (#.Some "1") 2 (#.Some "2") 3 (#.Some "3") 4 (#.Some "4") 5 (#.Some "5") 6 (#.Some "6") 7 (#.Some "7") 8 (#.Some "8") 9 (#.Some "9") _ #.None)) (def: (decimal-value digit) (-> Text (Maybe Nat)) (case digit "0" (#.Some 0) "1" (#.Some 1) "2" (#.Some 2) "3" (#.Some 3) "4" (#.Some 4) "5" (#.Some 5) "6" (#.Some 6) "7" (#.Some 7) "8" (#.Some 8) "9" (#.Some 9) _ #.None)) (def: (hexadecimal-character value) (-> Nat (Maybe Text)) (case value 0 (#.Some "0") 1 (#.Some "1") 2 (#.Some "2") 3 (#.Some "3") 4 (#.Some "4") 5 (#.Some "5") 6 (#.Some "6") 7 (#.Some "7") 8 (#.Some "8") 9 (#.Some "9") 10 (#.Some "A") 11 (#.Some "B") 12 (#.Some "C") 13 (#.Some "D") 14 (#.Some "E") 15 (#.Some "F") _ #.None)) (def: (hexadecimal-value digit) (-> Text (Maybe Nat)) (case digit "0" (#.Some 0) "1" (#.Some 1) "2" (#.Some 2) "3" (#.Some 3) "4" (#.Some 4) "5" (#.Some 5) "6" (#.Some 6) "7" (#.Some 7) "8" (#.Some 8) "9" (#.Some 9) (^or "a" "A") (#.Some 10) (^or "b" "B") (#.Some 11) (^or "c" "C") (#.Some 12) (^or "d" "D") (#.Some 13) (^or "e" "E") (#.Some 14) (^or "f" "F") (#.Some 15) _ #.None)) (do-template [ ] [(structure: #export (Codec Text Nat) (def: (encode value) (loop [input value output ""] (let [digit (maybe.assume ( (n/% input))) output' ("lux text concat" digit output) input' (n// input)] (if (n/= 0 input') output' (recur input' output'))))) (def: (decode repr) (let [input-size ("lux text size" repr)] (if (n/> 0 input-size) (loop [idx 0 output 0] (if (n/< input-size idx) (let [digit (maybe.assume (get-char repr idx))] (case ( digit) #.None (#error.Error ("lux text concat" repr)) (#.Some digit-value) (recur (inc idx) (|> output (n/* ) (n/+ digit-value))))) (#error.Success output))) (#error.Error ("lux text concat" repr))))))] [Binary@Codec 2 binary-character binary-value "Invalid binary syntax for Nat: "] [Octal@Codec 8 octal-character octal-value "Invalid octal syntax for Nat: "] [_ 10 decimal-character decimal-value "Invalid syntax for Nat: "] [Hex@Codec 16 hexadecimal-character hexadecimal-value "Invalid hexadecimal syntax for Nat: "] ) (def: (int/sign!! value) (-> Int Text) (if (i/< +0 value) "-" "+")) (def: (int/sign?? representation) (-> Text (Maybe Int)) (case (get-char representation 0) (^ (#.Some "-")) (#.Some -1) (^ (#.Some "+")) (#.Some +1) _ #.None)) (def: (int-decode-loop input-size repr sign ) (-> Nat Text Int Int (-> Text (Maybe Nat)) Text (Error Int)) (loop [idx 1 output +0] (if (n/< input-size idx) (let [digit (maybe.assume (get-char repr idx))] (case ( digit) #.None (#error.Error ) (#.Some digit-value) (recur (inc idx) (|> output (i/* ) (i/+ (.int digit-value)))))) (#error.Success (i/* sign output))))) (do-template [ ] [(structure: #export (Codec Text Int) (def: (encode value) (if (i/= +0 value) "+0" (loop [input (|> value (i// ) (:: Number abs)) output (|> value (i/% ) (:: Number abs) .nat maybe.assume)] (if (i/= +0 input) ("lux text concat" (int/sign!! value) output) (let [digit (maybe.assume ( (.nat (i/% input))))] (recur (i// input) ("lux text concat" digit output))))))) (def: (decode repr) (let [input-size ("lux text size" repr)] (if (n/> 1 input-size) (case (int/sign?? repr) (#.Some sign) (int-decode-loop input-size repr sign ) #.None (#error.Error )) (#error.Error )))))] [Binary@Codec +2 binary-character binary-value "Invalid binary syntax for Int: "] [Octal@Codec +8 octal-character octal-value "Invalid octal syntax for Int: "] [_ +10 decimal-character decimal-value "Invalid syntax for Int: "] [Hex@Codec +16 hexadecimal-character hexadecimal-value "Invalid hexadecimal syntax for Int: "] ) (def: (de-prefix input) (-> Text Text) (maybe.assume ("lux text clip" input 1 ("lux text size" input)))) (do-template [ ] [(structure: #export (Codec Text Rev) (def: (encode value) (let [raw-output (de-prefix (:: encode (:coerce Nat value))) max-num-chars (n// 64) raw-size ("lux text size" raw-output) zero-padding (loop [zeroes-left (n/- raw-size max-num-chars) output ""] (if (n/= 0 zeroes-left) output (recur (dec zeroes-left) ("lux text concat" "0" output)))) padded-output ("lux text concat" zero-padding raw-output)] ("lux text concat" "." padded-output))) (def: (decode repr) (let [repr-size ("lux text size" repr)] (if (n/>= 2 repr-size) (case ("lux text char" repr 0) (^multi (^ (#.Some (char "."))) [(:: decode (de-prefix repr)) (#error.Success output)]) (#error.Success (:coerce Rev output)) _ (#error.Error ("lux text concat" repr))) (#error.Error ("lux text concat" repr))))))] [Binary@Codec Binary@Codec 1 "Invalid binary syntax: "] [Octal@Codec Octal@Codec 3 "Invalid octal syntax: "] [Hex@Codec Hex@Codec 4 "Invalid hexadecimal syntax: "] ) (do-template [ ] [(structure: #export (Codec Text Frac) (def: (encode value) (let [whole (frac-to-int value) whole-part (:: encode whole) decimal (:: Number abs (f/% +1.0 value)) decimal-part (if (f/= +0.0 decimal) ".0" (loop [dec-left decimal output ""] (if (f/= +0.0 dec-left) ("lux text concat" "." output) (let [shifted (f/* dec-left) digit (|> shifted (f/% ) frac-to-int .nat (get-char ) maybe.assume)] (recur (f/% +1.0 shifted) ("lux text concat" output digit))))))] ("lux text concat" whole-part decimal-part))) (def: (decode repr) (case ("lux text index" repr "." 0) (#.Some split-index) (let [whole-part (maybe.assume ("lux text clip" repr 0 split-index)) decimal-part (maybe.assume ("lux text clip" repr (inc split-index) ("lux text size" repr)))] (case [(:: decode whole-part) (:: decode decimal-part)] (^multi [(#error.Success whole) (#error.Success decimal)] (i/>= +0 decimal)) (let [sign (if (i/< +0 whole) -1.0 +1.0) div-power (loop [muls-left ("lux text size" decimal-part) output +1.0] (if (n/= 0 muls-left) output (recur (dec muls-left) (f/* output)))) adjusted-decimal (|> decimal int-to-frac (f// div-power)) dec-rev (case (:: Hex@Codec decode ("lux text concat" "." decimal-part)) (#error.Success dec-rev) dec-rev (#error.Error error) (error! error))] (#error.Success (f/+ (int-to-frac whole) (f/* sign adjusted-decimal)))) _ (#error.Error ("lux text concat" repr)))) _ (#error.Error ("lux text concat" repr)))))] [Binary@Codec Binary@Codec +2.0 "01" "Invalid binary syntax: "] ) (def: (segment-digits chunk-size digits) (-> Nat Text (List Text)) (case digits "" (list) _ (let [num-digits ("lux text size" digits)] (if (n/<= chunk-size num-digits) (list digits) (let [boundary (n/- chunk-size num-digits) chunk (maybe.assume ("lux text clip" digits boundary num-digits)) remaining (maybe.assume ("lux text clip" digits 0 boundary))] (list& chunk (segment-digits chunk-size remaining))))))) (def: (bin-segment-to-hex input) (-> Text Text) (case input "0000" "0" "0001" "1" "0010" "2" "0011" "3" "0100" "4" "0101" "5" "0110" "6" "0111" "7" "1000" "8" "1001" "9" "1010" "A" "1011" "B" "1100" "C" "1101" "D" "1110" "E" "1111" "F" _ (undefined))) (def: (hex-segment-to-bin input) (-> Text Text) (case input "0" "0000" "1" "0001" "2" "0010" "3" "0011" "4" "0100" "5" "0101" "6" "0110" "7" "0111" "8" "1000" "9" "1001" (^or "a" "A") "1010" (^or "b" "B") "1011" (^or "c" "C") "1100" (^or "d" "D") "1101" (^or "e" "E") "1110" (^or "f" "F") "1111" _ (undefined))) (def: (bin-segment-to-octal input) (-> Text Text) (case input "000" "0" "001" "1" "010" "2" "011" "3" "100" "4" "101" "5" "110" "6" "111" "7" _ (undefined))) (def: (octal-segment-to-bin input) (-> Text Text) (case input "0" "000" "1" "001" "2" "010" "3" "011" "4" "100" "5" "101" "6" "110" "7" "111" _ (undefined))) (def: (map f xs) (All [a b] (-> (-> a b) (List a) (List b))) (case xs #.Nil #.Nil (#.Cons x xs') (#.Cons (f x) (map f xs')))) (def: (re-join-chunks xs) (-> (List Text) Text) (case xs #.Nil "" (#.Cons x xs') ("lux text concat" x (re-join-chunks xs')))) (do-template [ ] [(def: ( on-left? input) (-> Bit Text Text) (let [max-num-chars (n// 64) input-size ("lux text size" input) zero-padding (let [num-digits-that-need-padding (n/% input-size)] (if (n/= 0 num-digits-that-need-padding) "" (loop [zeroes-left (n/- num-digits-that-need-padding ) output ""] (if (n/= 0 zeroes-left) output (recur (dec zeroes-left) ("lux text concat" "0" output)))))) padded-input (if on-left? ("lux text concat" zero-padding input) ("lux text concat" input zero-padding))] (|> padded-input (segment-digits ) (map ) re-join-chunks))) (def: (-> Text Text) (|>> (segment-digits 1) (map ) re-join-chunks))] [binary-to-hex bin-segment-to-hex hex-to-binary hex-segment-to-bin 4] [binary-to-octal bin-segment-to-octal octal-to-binary octal-segment-to-bin 3] ) (do-template [ ] [(structure: #export (Codec Text Frac) (def: (encode value) (let [sign (:: Number signum value) raw-bin (:: Binary@Codec encode value) dot-idx (maybe.assume ("lux text index" raw-bin "." 0)) whole-part (maybe.assume ("lux text clip" raw-bin (if (f/= -1.0 sign) 1 0) dot-idx)) decimal-part (maybe.assume ("lux text clip" raw-bin (inc dot-idx) ("lux text size" raw-bin))) hex-output (|> ( #0 decimal-part) ("lux text concat" ".") ("lux text concat" ( #1 whole-part)) ("lux text concat" (if (f/= -1.0 sign) "-" "")))] hex-output)) (def: (decode repr) (let [sign (case ("lux text index" repr "-" 0) (#.Some 0) -1.0 _ +1.0)] (case ("lux text index" repr "." 0) (#.Some split-index) (let [whole-part (maybe.assume ("lux text clip" repr 1 split-index)) decimal-part (maybe.assume ("lux text clip" repr (inc split-index) ("lux text size" repr))) as-binary (|> ( decimal-part) ("lux text concat" ".") ("lux text concat" ( whole-part)) ("lux text concat" (if (f/= -1.0 sign) "-" "+")))] (case (:: Binary@Codec decode as-binary) (#error.Error _) (#error.Error ("lux text concat" repr)) output output)) _ (#error.Error ("lux text concat" repr))))))] [Octal@Codec "Invalid octaladecimal syntax: " binary-to-octal octal-to-binary] [Hex@Codec "Invalid hexadecimal syntax: " binary-to-hex hex-to-binary] ) (macro: (encoding-doc tokens state) (case tokens (^ (list [cursor (#.Text encoding)] example-1 example-2)) (let [encoding ($_ "lux text concat" "Given syntax for a " encoding " number, generates a Nat, an Int, a Rev or a Frac.") underscore "Allows for the presence of underscore in the numbers." description [cursor (#.Text ($_ "lux text concat" encoding " " underscore))]] (#error.Success [state (list (` (doc (~ description) (~ example-1) (~ example-2))))])) _ (#error.Error "Wrong syntax for 'encoding-doc'."))) (def: (underscore-prefixed? number) (-> Text Bit) (case ("lux text index" number "_" 0) (#.Some 0) #1 _ #0)) (def: clean-underscores (-> Text Text) (text.replace-all "_" "")) (do-template [ ] [(macro: #export ( tokens state) {#.doc } (case tokens (#.Cons [meta (#.Text repr')] #.Nil) (if (underscore-prefixed? repr') (#error.Error ) (let [repr (clean-underscores repr')] (case (:: decode repr) (#error.Success value) (#error.Success [state (list [meta (#.Nat value)])]) (^multi (#error.Error _) [(:: decode repr) (#error.Success value)]) (#error.Success [state (list [meta (#.Int value)])]) (^multi (#error.Error _) [(:: decode repr) (#error.Success value)]) (#error.Success [state (list [meta (#.Rev value)])]) (^multi (#error.Error _) [(:: decode repr) (#error.Success value)]) (#error.Success [state (list [meta (#.Frac value)])]) _ (#error.Error )))) _ (#error.Error )))] [bin Binary@Codec Binary@Codec Binary@Codec Binary@Codec "Invalid binary syntax." (encoding-doc "binary" (bin "+11001001") (bin "+11_00_10_01"))] [oct Octal@Codec Octal@Codec Octal@Codec Octal@Codec "Invalid octal syntax." (encoding-doc "octal" (oct "+615243") (oct "+615_243"))] [hex Hex@Codec Hex@Codec Hex@Codec Hex@Codec "Invalid hexadecimal syntax." (encoding-doc "hexadecimal" (hex "deadBEEF") (hex "dead_BEEF"))] ) ## The following code allows one to encode/decode Rev numbers as text. ## This is not a simple algorithm, and it requires subverting the Rev ## abstraction a bit. ## It takes into account the fact that Rev numbers are represented by ## Lux as 64-bit integers. ## A valid way to model them is as Lux's Nat type. ## This is a somewhat hackish way to do things, but it allows one to ## write the encoding/decoding algorithm once, in pure Lux, rather ## than having to implement it on the compiler for every platform ## targeted by Lux. (type: Digits (Array Nat)) (def: (make-digits _) (-> Any Digits) (array.new i64.width)) (def: (digits-get idx digits) (-> Nat Digits Nat) (|> digits (array.read idx) (maybe.default 0))) (def: digits-put (-> Nat Nat Digits Digits) array.write) (def: (prepend left right) (-> Text Text Text) ("lux text concat" left right)) (def: (digits-times-5! idx output) (-> Nat Digits Digits) (loop [idx idx carry 0 output output] (if (i/>= +0 (:coerce Int idx)) (let [raw (|> (digits-get idx output) (n/* 5) (n/+ carry))] (recur (dec idx) (n// 10 raw) (digits-put idx (n/% 10 raw) output))) output))) (def: (digits-power power) (-> Nat Digits) (loop [times power output (|> (make-digits []) (digits-put power 1))] (if (i/>= +0 (:coerce Int times)) (recur (dec times) (digits-times-5! power output)) output))) (def: (digits-to-text digits) (-> Digits Text) (loop [idx (dec i64.width) all-zeroes? #1 output ""] (if (i/>= +0 (:coerce Int idx)) (let [digit (digits-get idx digits)] (if (and (n/= 0 digit) all-zeroes?) (recur (dec idx) #1 output) (recur (dec idx) #0 ("lux text concat" (:: Codec encode (:coerce Int digit)) output)))) (if all-zeroes? "0" output)))) (def: (digits-add param subject) (-> Digits Digits Digits) (loop [idx (dec i64.width) carry 0 output (make-digits [])] (if (i/>= +0 (:coerce Int idx)) (let [raw ($_ n/+ carry (digits-get idx param) (digits-get idx subject))] (recur (dec idx) (n// 10 raw) (digits-put idx (n/% 10 raw) output))) output))) (def: (text-to-digits input) (-> Text (Maybe Digits)) (let [length ("lux text size" input)] (if (n/<= i64.width length) (loop [idx 0 output (make-digits [])] (if (n/< length idx) (let [char (maybe.assume (get-char input idx))] (case ("lux text index" "+0123456789" char 0) #.None #.None (#.Some digit) (recur (inc idx) (digits-put idx digit output)))) (#.Some output))) #.None))) (def: (digits-lt param subject) (-> Digits Digits Bit) (loop [idx 0] (and (n/< i64.width idx) (let [pd (digits-get idx param) sd (digits-get idx subject)] (if (n/= pd sd) (recur (inc idx)) (n/< pd sd)))))) (def: (digits-sub-once! idx param subject) (-> Nat Nat Digits Digits) (let [sd (digits-get idx subject)] (if (n/>= param sd) (digits-put idx (n/- param sd) subject) (let [diff (|> sd (n/+ 10) (n/- param))] (|> subject (digits-put idx diff) (digits-sub-once! (dec idx) 1)))))) (def: (digits-sub! param subject) (-> Digits Digits Digits) (loop [idx (dec i64.width) output subject] (if (i/>= +0 (.int idx)) (recur (dec idx) (digits-sub-once! idx (digits-get idx param) output)) output))) (structure: #export _ (Codec Text Rev) (def: (encode input) (let [input (:coerce Nat input) last-idx (dec i64.width)] (if (n/= 0 input) ".0" (loop [idx last-idx digits (make-digits [])] (if (i/>= +0 (:coerce Int idx)) (if (i64.set? idx input) (let [digits' (digits-add (digits-power (n/- idx last-idx)) digits)] (recur (dec idx) digits')) (recur (dec idx) digits)) ("lux text concat" "." (digits-to-text digits)) ))))) (def: (decode input) (let [length ("lux text size" input) dotted? (case ("lux text index" input "." 0) (#.Some 0) #1 _ #0)] (if (and dotted? (n/<= (inc i64.width) length)) (case (|> ("lux text clip" input 1 length) maybe.assume text-to-digits) (#.Some digits) (loop [digits digits idx 0 output 0] (if (n/< i64.width idx) (let [power (digits-power idx)] (if (digits-lt power digits) ## Skip power (recur digits (inc idx) output) (recur (digits-sub! power digits) (inc idx) (i64.set (n/- idx (dec i64.width)) output)))) (#error.Success (:coerce Rev output)))) #.None (#error.Error ("lux text concat" "Wrong syntax for Rev: " input))) (#error.Error ("lux text concat" "Wrong syntax for Rev: " input)))) )) (def: (log2 input) (-> Frac Frac) (f// (math.log +2.0) (math.log input))) (def: double-bias Nat 1023) (def: mantissa-size Nat 52) (def: exponent-size Nat 11) (def: #export (frac-to-bits input) (-> Frac I64) (i64 (cond (not-a-number? input) (hex "7FF7FFFFFFFFFFFF") (f/= positive-infinity input) (hex "7FF0000000000000") (f/= negative-infinity input) (hex "FFF0000000000000") (f/= +0.0 input) (let [reciprocal (f// input +1.0)] (if (f/= positive-infinity reciprocal) ## Positive zero (hex "0000000000000000") ## Negative zero (hex "8000000000000000"))) ## else (let [sign (:: Number signum input) input (:: Number abs input) exponent (math.floor (log2 input)) exponent-mask (|> 1 (i64.left-shift exponent-size) dec) mantissa (|> input ## Normalize (f// (math.pow exponent +2.0)) ## Make it int-equivalent (f/* (math.pow +52.0 +2.0))) sign-bit (if (f/= -1.0 sign) 1 0) exponent-bits (|> exponent frac-to-int .nat (n/+ double-bias) (i64.and exponent-mask)) mantissa-bits (|> mantissa frac-to-int .nat)] ($_ i64.or (i64.left-shift 63 sign-bit) (i64.left-shift mantissa-size exponent-bits) (i64.clear mantissa-size mantissa-bits))) ))) (do-template [ ] [(def: (|> 1 (i64.left-shift ) dec (i64.left-shift ))) (def: ( input) (-> (I64 Any) I64) (|> input (i64.and ) (i64.logical-right-shift ) i64))] [mantissa mantissa-mask mantissa-size 0] [exponent exponent-mask exponent-size mantissa-size] [sign sign-mask 1 (n/+ exponent-size mantissa-size)] ) (def: #export (bits-to-frac input) (-> (I64 Any) Frac) (let [S (sign input) E (exponent input) M (mantissa input)] (cond (n/= (hex "7FF") E) (if (n/= 0 M) (if (n/= 0 S) positive-infinity negative-infinity) not-a-number) (and (n/= 0 E) (n/= 0 M)) (if (n/= 0 S) +0.0 (f/* -1.0 +0.0)) ## else (let [normalized (|> M (i64.set mantissa-size) .int int-to-frac (f// (math.pow +52.0 +2.0))) power (math.pow (|> E (n/- double-bias) .int int-to-frac) +2.0) shifted (f/* power normalized)] (if (n/= 0 S) shifted (f/* -1.0 shifted)))))) ## [Hash] (structure: #export _ (Hash Nat) (def: eq Equivalence) (def: hash id)) (structure: #export _ (Hash Int) (def: eq Equivalence) (def: hash .nat)) (structure: #export _ (Hash Frac) (def: eq Equivalence) (def: hash frac-to-bits)) (structure: #export _ (Hash Rev) (def: eq Equivalence) (def: hash (|>> (:coerce Nat))))