tricu

A lazy functional language based on Tree Calculus
Log | Files | Refs | README | LICENSE

float-finite-product-quotient.tri (4359B)


      1 -- Benchmark-only finite signed multiplication and nonzero-divisor division.
      2 -- Checked interface matches finite.tri; error 3 rejects a zero divisor.
      3 !import native_math as N
      4 !import kernels as K
      5 !import fast as F
      6 !import finite as B
      7 !import adders as A
      8 !export *
      9 check operation a b = K.lazy (_ : t t 2)
     10   (_ : K.lazy (_ : t t 1) (_ : t (t t) (operation a b))
     11     (K.either (N.equal (B.exponent a) 2047) (N.equal (B.exponent b) 2047)))
     12   (K.either (N.gte a 18446744073709551616) (N.gte b 18446744073709551616))
     13 product kernel a b = let da = B.decode a in let db = B.decode b in
     14   let negative = K.xor (B.sign da) (B.sign db) in K.lazy
     15     (_ : B.zero negative)
     16     (_ : B.signed negative (F.selective (N.subtract (N.add (B.scale da) (B.scale db)) 2304)
     17       (kernel (B.mant da) (B.mant db))))
     18     (K.either (N.zero (B.mant da)) (N.zero (B.mant db)))
     19 nativeProduct = product N.multiply
     20 orderedProduct = product A.orderedMultiply
     21 -- Exact control: divide a 109-bit numerator by the normalized divisor.
     22 nativeQuotient a b = N.divMod (F.shiftPositive 56 a) b
     23 -- Produce the exact quotient/remainder by consuming 56 fractional bits.
     24 -- Initial quotient is 0 or 1 because 1/2 < a/b < 2.
     25 walk count divisor q r = K.lazy (_ : t q r)
     26   (_ : K.lazy (_ : t (F.shiftPositive count q) 0)
     27     (_ : let shifted = F.zeros1 r in K.lazy
     28       (_ : walk (N.predecessor count) divisor (K.node t q) shifted)
     29       (_ : walk (N.predecessor count) divisor (N.successor (K.node t q)) (N.subtract shifted divisor))
     30       (N.lt shifted divisor)) (N.zero r)) (N.zero count)
     31 boundedQuotient a b = K.lazy (_ : walk 56 b 0 a)
     32   (_ : walk 56 b 1 (N.subtract a b)) (N.lt a b)
     33 -- Round-to-odd retains the discarded rational remainder. At least three
     34 -- working bits survive normalization, so this only feeds immediate final RNE.
     35 jam split = let q = K.head split in K.lazy (_ : q)
     36   (_ : K.choose q (N.successor q) (N.zero (K.tail split))) (N.odd q)
     37 quotient kernel a b = let da = B.decode a in let db = B.decode b in
     38   let negative = K.xor (B.sign da) (B.sign db) in K.lazy
     39     (_ : B.zero negative)
     40     (_ : B.signed negative (F.selective (N.subtract (N.add 2248 (B.scale da)) (B.scale db))
     41       (jam (kernel (B.mant da) (B.mant db))))) (N.zero (B.mant da))
     42 -- Range/nonfinite checks precede division-by-zero rejection; error codes are
     43 -- domain errors in this finite-input test interface.
     44 checkedQuotient kernel a b = let result = check (quotient kernel) a b in K.lazy
     45   (_ : K.lazy (_ : t t 3) (_ : result) (N.zero (B.mant (B.decode b))))
     46   (_ : result) (K.head result)
     47 nativeMultiplyOne x = check nativeProduct (K.head x) (K.tail x)
     48 orderedMultiplyOne x = check orderedProduct (K.head x) (K.tail x)
     49 nativeDivideOne x = checkedQuotient nativeQuotient (K.head x) (K.tail x)
     50 boundedDivideOne x = checkedQuotient boundedQuotient (K.head x) (K.tail x)
     51 nativeMultiplies xs = K.map nativeMultiplyOne xs
     52 orderedMultiplies xs = K.map orderedMultiplyOne xs
     53 nativeDivides xs = K.map nativeDivideOne xs
     54 boundedDivides xs = K.map boundedDivideOne xs
     55 nativeRawOne x = nativeQuotient (K.head x) (K.tail x)
     56 boundedRawOne x = boundedQuotient (K.head x) (K.tail x)
     57 nativeRaw xs = K.map nativeRawOne xs
     58 boundedRaw xs = K.map boundedRawOne xs
     59 spin x = spin x
     60 productZeroDemand x = product (spin x) 0 1
     61 quotientZeroDemand x = quotient (spin x) 0 1
     62 jamOddDemand x = jam (t 7 (spin x))
     63 rangeDemand x = check nativeProduct 18446744073709551616 (spin x)
     64 walkExactDemand x = walk 56 (spin x) 1 0
     65 -- Negative rounding controls: separate precision/quantum cuts and dropped remainder.
     66 truncateKernel a b = t (K.head (boundedQuotient a b)) 0
     67 truncateDivideOne x = checkedQuotient truncateKernel (K.head x) (K.tail x)
     68 truncateDivides xs = K.map truncateDivideOne xs
     69 doubleProduct a b = let da = B.decode a in let db = B.decode b in
     70   let n = N.multiply (B.mant da) (B.mant db) in
     71   let cut = N.subtract (K.width n) 53 in let first = K.roundL cut n in
     72   let result = F.selective (N.add cut (N.subtract (N.add (B.scale da) (B.scale db)) 2304)) (K.head first) in
     73   let ix = K.either (K.tail first) (K.head (K.tail result)) in
     74     B.signed (K.xor (B.sign da) (B.sign db))
     75       (t (K.head result) (t ix (K.both ix (N.lt (K.head result) 4503599627370496))))
     76 doubleProductOne x = check doubleProduct (K.head x) (K.tail x)
     77 doubleProducts xs = K.map doubleProductOne xs
     78 divisionRejectDemand x = checkedQuotient (spin x) 1 0