commit 1810140a360ea7428a157175003ba734e054e70e
parent 868e347c86692437c59eefeec5055dce709528ba
Author: Jared Tobin <jared@jtobin.io>
Date: Sun, 7 Dec 2025 18:22:29 +0400
lib: docs, list for wider
Diffstat:
1 file changed, 231 insertions(+), 28 deletions(-)
diff --git a/lib/Data/Word/Wider.hs b/lib/Data/Word/Wider.hs
@@ -14,7 +14,60 @@
--
-- Wider words, consisting of four 'Limb's.
-module Data.Word.Wider where
+module Data.Word.Wider (
+ -- * Four-limb words
+ Wider(..)
+ , wider
+ , to
+ , from
+
+ -- * Comparison
+ , eq_vartime
+ , cmp
+ , eq#
+ , lt#
+ , gt#
+ , cmp#
+
+ -- * Constant-time selection
+ , select
+ , select#
+
+ -- * Bit manipulation
+ , shl1_c
+ , shr1_c
+ , shr_limb
+ , shl_limb
+ , shl1_c#
+ , shr1_c#
+ , shr_limb#
+ , shl_limb#
+ , and
+ , and_w#
+ , or
+ , or_w#
+ , not
+ , not#
+
+ -- * Arithmetic
+ , add_o
+ , add_o#
+ , add
+ , add_w#
+ , add_mod
+ , add_mod#
+ , sub
+ , sub_b
+ , sub_b#
+ , sub_mod
+ , sub_mod#
+ , sub_mod_c#
+ , mul
+ , mul_c
+ , mul_c#
+ , sqr
+ , sqr#
+ ) where
import Control.DeepSeq
import Data.Bits ((.|.), (.&.), (.<<.), (.>>.))
@@ -34,7 +87,10 @@ fi = fromIntegral
-- wider words ----------------------------------------------------------------
--- | Little-endian wider words.
+-- | Little-endian wider words, consisting of four 'Limbs'.
+--
+-- >>> 1 :: Wider
+-- 1
data Wider = Wider !(# Limb, Limb, Limb, Limb #)
instance Show Wider where
@@ -68,6 +124,11 @@ eq# a b =
{-# INLINE eq# #-}
-- | Compare 'Wider' words for equality in variable time.
+--
+-- >>> eq_vartime 1 0
+-- False
+-- >>> eq_vartime 1 1
+-- True
eq_vartime :: Wider -> Wider -> Bool
eq_vartime a b =
let !(Wider (# a0, a1, a2, a3 #)) = a
@@ -114,6 +175,13 @@ cmp# (# l0, l1, l2, l3 #) (# r0, r1, r2, r3 #) =
{-# INLINE cmp# #-}
-- | Constant-time comparison between 'Wider' words.
+--
+-- >>> cmp 1 2
+-- LT
+-- >>> cmp 2 1
+-- GT
+-- >>> cmp 2 2
+-- EQ
cmp :: Wider -> Wider -> Ordering
cmp (Wider a) (Wider b) = case cmp# a b of
1# -> GT
@@ -124,11 +192,17 @@ cmp (Wider a) (Wider b) = case cmp# a b of
-- | Construct a 'Wider' word from four 'Words', provided in
-- little-endian order.
+--
+-- >>> wider 1 0 0 0
+-- 1
wider :: Word -> Word -> Word -> Word -> Wider
wider (W# w0) (W# w1) (W# w2) (W# w3) = Wider
(# Limb w0, Limb w1, Limb w2, Limb w3 #)
-- | Convert an 'Integer' to a 'Wider' word.
+--
+-- >>> to 1
+-- 1
to :: Integer -> Wider
to n =
let !size = B.finiteBitSize (0 :: Word)
@@ -140,6 +214,9 @@ to n =
in Wider (# Limb w0, Limb w1, Limb w2, Limb w3 #)
-- | Convert a 'Wider' word to an 'Integer'.
+--
+-- >>> from 1
+-- 1
from :: Wider -> Integer
from (Wider (# Limb w0, Limb w1, Limb w2, Limb w3 #)) =
fi (W# w3) .<<. (3 * size)
@@ -151,7 +228,6 @@ from (Wider (# Limb w0, Limb w1, Limb w2, Limb w3 #)) =
-- constant-time selection-----------------------------------------------------
--- | Return a if c is truthy, otherwise return b.
select#
:: (# Limb, Limb, Limb, Limb #) -- ^ a
-> (# Limb, Limb, Limb, Limb #) -- ^ b
@@ -166,6 +242,10 @@ select# a b c =
{-# INLINE select# #-}
-- | Return a if c is truthy, otherwise return b.
+--
+-- >>> import qualified Data.Choice as C
+-- >>> select 0 1 (C.true# ())
+-- 1
select
:: Wider -- ^ a
-> Wider -- ^ b
@@ -175,8 +255,6 @@ select (Wider a) (Wider b) c = Wider (select# a b c)
-- bit manipulation -----------------------------------------------------------
--- | Constant-time 1-bit shift-right with carry, indicating whether the
--- lowest bit was set.
shr1_c#
:: (# Limb, Limb, Limb, Limb #) -- ^ argument
-> (# (# Limb, Limb, Limb, Limb #), C.Choice #) -- ^ result, carry
@@ -194,13 +272,18 @@ shr1_c# (# w0, w1, w2, w3 #) =
in (# (# r0, r1, r2, r3 #), C.from_word_lsb# w #)
{-# INLINE shr1_c# #-}
+-- | Constant-time 1-bit shift-right with carry, indicating whether the
+-- lowest bit was set.
+--
+-- >>> shr1_c 2
+-- (1,False)
+-- >>> shr1_c 1
+-- (0,True)
shr1_c :: Wider -> (Wider, Bool)
shr1_c (Wider w) =
let !(# r, c #) = shr1_c# w
in (Wider r, C.decide c)
--- | Constant-time 1-bit shift-left with carry, indicating whether the
--- highest bit was set.
shl1_c#
:: (# Limb, Limb, Limb, Limb #) -- ^ argument
-> (# (# Limb, Limb, Limb, Limb #), C.Choice #) -- ^ result, carry
@@ -214,16 +297,22 @@ shl1_c# (# w0, w1, w2, w3 #) =
!r2 = L.or# s2 c1
!(# s3, c3 #) = (# L.shl# w3 1#, L.shr# w3 s #)
!r3 = L.or# s3 c2
- !(Limb w) = L.shr# c3 s
+ !(Limb w) = L.shl# c3 s
in (# (# r0, r1, r2, r3 #), C.from_word_lsb# w #)
{-# INLINE shl1_c# #-}
+-- | Constant-time 1-bit shift-left with carry, indicating whether the
+-- highest bit was set.
+--
+-- >>> shl1_c 1
+-- (2,False)
+-- >>> shl1_c (2 ^ (255 :: Word))
+-- (0,True)
shl1_c :: Wider -> (Wider, Bool)
shl1_c (Wider w) =
let !(# r, c #) = shl1_c# w
in (Wider r, C.decide c)
--- shift-right by 0 < s < WORD_SIZE (unchecked)
shr_limb#
:: (# Limb, Limb, Limb, Limb #)
-> Int#
@@ -238,6 +327,12 @@ shr_limb# (# a0, a1, a2, a3 #) rs =
in (# (# l0, l1, l2, l3 #), c0 #)
{-# INLINE shr_limb# #-}
+-- | Shift right by less than the number of bits in a 'Limb' (e.g., by
+-- a maximum of 63 bits on 64-bit architectures). The shift amount is
+-- unchecked.
+--
+-- >>> shr_limb 2 1
+-- 1
shr_limb
:: Wider -- ^ value
-> Int -- ^ right-shift amount (0 < s < WORD_SIZE)
@@ -246,7 +341,6 @@ shr_limb (Wider w) (I# s) =
let !(# r, _ #) = shr_limb# w s
in Wider r
--- shift-left by 0 < s < WORD_SIZE (unchecked)
shl_limb#
:: (# Limb, Limb, Limb, Limb #)
-> Int#
@@ -261,6 +355,14 @@ shl_limb# (# a0, a1, a2, a3 #) ls =
in (# (# l0, l1, l2, l3 #), c3 #)
{-# INLINE shl_limb# #-}
+-- | Shift left by less than the number of bits in a 'Limb' (e.g., by
+-- a maximum of 63 bits on 64-bit architectures). The shift amount is
+-- unchecked.
+--
+-- >>> shl_limb 2 1
+-- 1
+-- >>> shl_limb 1 63
+-- 9223372036854775808
shl_limb
:: Wider -- ^ value
-> Int -- ^ left-shift amount (0 < s < WORD_SIZE)
@@ -277,7 +379,16 @@ and_w# (# a0, a1, a2, a3 #) (# b0, b1, b2, b3 #) =
(# L.and# a0 b0, L.and# a1 b1, L.and# a2 b2, L.and# a3 b3 #)
{-# INLINE and_w# #-}
-and :: Wider -> Wider -> Wider
+-- | Binary /and/.
+--
+-- >>> and 1 1
+-- 1
+-- >>> and 1 0
+-- 0
+and
+ :: Wider -- ^ a
+ -> Wider -- ^ b
+ -> Wider -- ^ a & b
and (Wider a) (Wider b) = Wider (and_w# a b)
or_w#
@@ -288,7 +399,16 @@ or_w# (# a0, a1, a2, a3 #) (# b0, b1, b2, b3 #) =
(# L.or# a0 b0, L.or# a1 b1, L.or# a2 b2, L.or# a3 b3 #)
{-# INLINE or_w# #-}
-or :: Wider -> Wider -> Wider
+-- | Binary /or/.
+--
+-- >>> or 1 1
+-- 1
+-- >>> or 1 0
+-- 1
+or
+ :: Wider -- ^ a
+ -> Wider -- ^ b
+ -> Wider -- ^ a | b
or (Wider a) (Wider b) = Wider (or_w# a b)
not#
@@ -297,15 +417,19 @@ not#
not# (# l0, l1, l2, l3 #) = (# L.not# l0, L.not# l1, L.not# l2, L.not# l3 #)
{-# INLINE not# #-}
+-- | Binary /not/.
+--
+-- >>> not 0
+-- 115792089237316195423570985008687907853269984665640564039457584007913129639935
+-- >>> not (not 0)
+-- 0
not
- :: Wider
- -> Wider
+ :: Wider -- ^ value
+ -> Wider -- ^ not value
not (Wider w) = Wider (not# w)
-- addition, subtraction ------------------------------------------------------
--- | Overflowing addition, computing 'a + b', returning the sum and a
--- carry bit.
add_o#
:: (# Limb, Limb, Limb, Limb #) -- ^ augend
-> (# Limb, Limb, Limb, Limb #) -- ^ addend
@@ -320,6 +444,11 @@ add_o# (# a0, a1, a2, a3 #) (# b0, b1, b2, b3 #) =
-- | Overflowing addition, computing 'a + b', returning the sum and a
-- carry bit.
+--
+-- >>> add_o 1 1
+-- (2,0)
+-- >>> add_o 1 (2 ^ (256 :: Word) - 1)
+-- (0,1)
add_o
:: Wider
-> Wider
@@ -328,7 +457,6 @@ add_o (Wider a) (Wider b) =
let !(# s, Limb c #) = add_o# a b
in (Wider s, W# c)
--- | Wrapping addition, computing 'a + b'.
add_w#
:: (# Limb, Limb, Limb, Limb #) -- ^ augend
-> (# Limb, Limb, Limb, Limb #) -- ^ addend
@@ -339,6 +467,12 @@ add_w# a b =
{-# INLINE add_w# #-}
-- | Wrapping addition, computing 'a + b'.
+--
+-- Note that as 'Wider' is an instance of 'Num', you can use '+' to apply
+-- this function.
+--
+-- >>> add 1 (2 ^ (256 :: Word) - 1)
+-- 0
add
:: Wider
-> Wider
@@ -346,7 +480,6 @@ add
add (Wider a) (Wider b) = Wider (add_w# a b)
{-# INLINE add #-}
--- | Modular addition.
add_mod#
:: (# Limb, Limb, Limb, Limb #) -- ^ augend
-> (# Limb, Limb, Limb, Limb #) -- ^ addend
@@ -357,8 +490,22 @@ add_mod# a b m =
in sub_mod_c# w c m m
{-# INLINE add_mod# #-}
--- | Borrowing subtraction, computing 'a - b' and returning the
--- difference with a borrow mask.
+-- | Modular addition.
+--
+-- Assumes that the sum is less than twice the modulus; this is not
+-- checked.
+--
+-- >>> add_mod 1 1 3
+-- 2
+-- >>> add_mod 1 2 3
+-- 0
+add_mod
+ :: Wider -- ^ augend
+ -> Wider -- ^ addend
+ -> Wider -- ^ modulus
+ -> Wider -- ^ sum
+add_mod (Wider a) (Wider b) (Wider m) = Wider (add_mod# a b m)
+
sub_b#
:: (# Limb, Limb, Limb, Limb #) -- ^ minuend
-> (# Limb, Limb, Limb, Limb #) -- ^ subtrahend
@@ -371,23 +518,39 @@ sub_b# (# a0, a1, a2, a3 #) (# b0, b1, b2, b3 #) =
in (# (# s0, s1, s2, s3 #), c3 #)
{-# INLINE sub_b# #-}
+-- | Borrowing subtraction, computing 'a - b' and returning the
+-- difference with a borrow mask.
+--
+-- >>> sub_b 1 1
+-- (0,0)
+-- >>> sub_b 0 (2 ^ (256 :: Word) - 1)
+-- (1,18446744073709551615)
sub_b
- :: Wider
- -> Wider
- -> (Wider, Word)
+ :: Wider -- ^ minuend
+ -> Wider -- ^ subtrahend
+ -> (Wider, Word) -- ^ (difference, borrow mask)
sub_b (Wider l) (Wider r) =
let !(# d, Limb b #) = sub_b# l r
in (Wider d, W# b)
+-- | Wrapping subtraction, computing 'a - b' and returning the
+-- difference.
+--
+-- Note that as 'Wider' is an instance of 'Num', you can use '-' to apply
+-- this function.
+--
+-- >>> sub 1 1
+-- 0
+-- >>> sub 0 (2 ^ (256 :: Word) - 1)
+-- 1
sub
- :: Wider
- -> Wider
- -> Wider
+ :: Wider -- ^ minuend
+ -> Wider -- ^ subtrahend
+ -> Wider -- ^ difference
sub (Wider a) (Wider b) =
let !(# d, _ #) = sub_b# a b
in Wider d
--- | Modular subtraction. Computes a - b mod m.
sub_mod#
:: (# Limb, Limb, Limb, Limb #) -- ^ minuend
-> (# Limb, Limb, Limb, Limb #) -- ^ subtrahend
@@ -399,6 +562,15 @@ sub_mod# a b (# p0, p1, p2, p3 #) =
in add_w# o ba
{-# INLINE sub_mod# #-}
+-- | Modular subtraction. Computes a - b mod m.
+--
+-- Assumes that the magnitude of the difference is less than the
+-- modulus (this is unchecked).
+--
+-- >>> sub_mod 1 1 4
+-- 0
+-- >>> sub_mod 2 3 4
+-- 3
sub_mod
:: Wider
-> Wider
@@ -422,7 +594,6 @@ sub_mod_c# a c b (# p0, p1, p2, p3 #) =
-- multiplication -------------------------------------------------------------
--- widening multiplication
mul_c#
:: (# Limb, Limb, Limb, Limb #)
-> (# Limb, Limb, Limb, Limb #)
@@ -456,6 +627,32 @@ mul_c# (# x0, x1, x2, x3 #) (# y0, y1, y2, y3 #) =
in (# (# z0, z1, z2, z3 #), (# w4, w5f, w6ff, w7ff #) #)
{-# INLINE mul_c# #-}
+-- | Widening multiplication.
+--
+-- Returns the low and high 'Wider' words of the product, in that
+-- order.
+--
+-- >>> mul_c 2 3
+-- (6,0)
+-- >>> mul_c (2 ^ (256 :: Word) - 1) 2
+-- (115792089237316195423570985008687907853269984665640564039457584007913129639934,1)
+mul_c
+ :: Wider
+ -> Wider
+ -> (Wider, Wider)
+mul_c (Wider a) (Wider b) =
+ let !(# l, h #) = mul_c# a b
+ in (Wider l, Wider h)
+
+-- | Wrapping multiplication.
+--
+-- Note that as 'Wider' is an instance of 'Num', you can use '*' to apply
+-- this function.
+--
+-- >>> mul 1 1
+-- 1
+-- >>> mul 1 2
+-- 2
mul
:: Wider
-> Wider
@@ -496,6 +693,12 @@ sqr# (# x0, x1, x2, x3 #) =
in (# (# pf, qf, rf, sf #), (# tf, uf, vf, wf #) #)
{-# INLINE sqr# #-}
+-- | Widening square.
+--
+-- >>> sqr 2
+-- (4,0)
+-- >>> sqr (2 ^ (256 :: Word) - 1)
+-- (1,115792089237316195423570985008687907853269984665640564039457584007913129639934)
sqr :: Wider -> (Wider, Wider)
sqr (Wider w) =
let !(# l, h #) = sqr# w