fixed

Pure Haskell large fixed-width integers.
git clone git://git.ppad.tech/fixed.git
Log | Files | Refs | README | LICENSE

commit 680a0698c60fe87b21c0d65c20751c2f4b044aed
parent 8e0da908a4b40fc350164b26de2f7ce3c8707868
Author: Jared Tobin <jared@jtobin.io>
Date:   Fri, 24 Jan 2025 11:21:07 +0400

lib: pure quotrem_knuth

Diffstat:
Mlib/Data/Word/Extended.hs | 44++++++++++++++++++++++++++++++++++++++++++++
Mtest/Main.hs | 29++++++++++++++++++++++++++++-
2 files changed, 72 insertions(+), 1 deletion(-)

diff --git a/lib/Data/Word/Extended.hs b/lib/Data/Word/Extended.hs @@ -97,6 +97,11 @@ data Word640 = Word640 {-# UNPACK #-} !Word64 deriving (Eq, Show, Generic) +data Word1152 = Word1152 -- yikes + {-# UNPACK #-} !Word576 + {-# UNPACK #-} !Word576 + deriving (Eq, Show, Generic) + -- conversion ----------------------------------------------------------------- to_integer :: Word256 -> Integer @@ -503,6 +508,44 @@ quotrem_by1_gen u ulen d = !nacc = set576 acc j q_j in loop (pred j) nacc r +quotrem_knuth_gen + :: Word576 + -> Int + -> Word256 + -> Int + -> Word1152 +quotrem_knuth_gen u ulen d dlen = loop (ulen - dlen - 1) zero576 u where + !d_hi = sel256 d (dlen - 1) + !d_lo = sel256 d (dlen - 2) + !rec = recip_2by1 d_hi + loop j !qacc !uacc + | j < 0 = Word1152 qacc uacc + | otherwise = + let !u_2 = sel576 uacc (j + dlen) + !u_1 = sel576 uacc (j + dlen - 1) + !u_0 = sel576 uacc (j + dlen - 2) + !qhat + | u_2 >= d_hi = 0xffff_ffff_ffff_ffff + | otherwise = + let !(P qh rh) = quotrem_2by1 u_2 u_1 d_hi rec + !(P ph pl) = mul_c qh d_lo + in if ph > rh || (ph == rh && pl > u_0) + then qh - 1 + else qh + + !(Word640 u0 borrow) = sub_mul u j d dlen qhat + !u1 = set576 u0 (j + dlen) (u_2 - borrow) + in if u_2 < borrow + then + let !qh = qhat - 1 + !(Word640 u2 r) = add_big u1 j d dlen + !u3 = set576 u2 (j + dlen) r + !q = set576 qacc j qh + in loop (pred j) q u3 + else + let !q = set576 qacc j qhat + in loop (pred j) q u1 + -- XX primarray quotrem_knuth :: PrimMonad m @@ -543,6 +586,7 @@ quotrem_knuth quo u d = do loop (pred j) loop (lu - ld - 1) + -- XX needs work -- quotrem_256 -- :: Word256 diff --git a/test/Main.hs b/test/Main.hs @@ -232,6 +232,32 @@ quotrem_by1_gen_case1 = do H.assertEqual "remainder matches" pec_rem r H.assertEqual "quotient matches" pec_quo q +quotrem_knuth_gen_case0 :: H.Assertion +quotrem_knuth_gen_case0 = do + let !u = Word576 + 2162362899639802732 + 8848548347662387477 + 13702897166684377657 + 16799544643779908154 + 1 + 0 0 0 0 + !d = Word256 + 16950798510782491100 + 2612788699139816405 + 5146719872810836952 + 14966148379609982000 + !(Word1152 q nu) = quotrem_knuth_gen u 5 d 4 + !pec_q = Word576 2 0 0 0 0 0 0 0 0 + !pec_u = Word576 + 5154254025493923764 + 3622970949382754665 + 3409457421062703753 + 5313991958269495770 + 0 + 0 0 0 0 + H.assertEqual "divisor matches" pec_u nu + H.assertEqual "quotient matches" pec_q q + quotrem_knuth_case0 :: H.Assertion quotrem_knuth_case0 = do let (q, u) = runST $ do @@ -258,7 +284,7 @@ quotrem_knuth_case0 = do 5154254025493923764 , 3622970949382754665 , 3409457421062703753 - , 5313991958269495770 -- was off by 1 :| + , 5313991958269495770 , 0 ] H.assertEqual "divisor matches" pec_u u @@ -388,6 +414,7 @@ main = defaultMain $ , H.testCase "quotrem_by1 matches case1" quotrem_by1_case1 , H.testCase "quotrem_by1_gen matches case0" quotrem_by1_gen_case0 , H.testCase "quotrem_by1_gen matches case1" quotrem_by1_gen_case1 + , H.testCase "quotrem_knuth_gen matches case0" quotrem_knuth_gen_case0 , H.testCase "quotrem_knuth matches case0" quotrem_knuth_case0 , H.testCase "quotrem matches case0" quotrem_case0 , H.testCase "quotrem matches case1" quotrem_case1