fixed

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

commit c9a6ece74d2d0ee7dd7f4b1f842e941124aee25c
parent e01e1dada86812f81da0a0e5c526bc3d78d4846f
Author: Jared Tobin <jared@jtobin.io>
Date:   Wed,  9 Jul 2025 11:22:21 -0230

lib: add Data.Choice module

Diffstat:
M.gitignore | 2++
Alib/Data/Choice.hs | 211+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mppad-fixed.cabal | 3++-
3 files changed, 215 insertions(+), 1 deletion(-)

diff --git a/.gitignore b/.gitignore @@ -2,3 +2,5 @@ dist-newstyle/ *.prof *.hp core +.claude/ +etc/ diff --git a/lib/Data/Choice.hs b/lib/Data/Choice.hs @@ -0,0 +1,211 @@ +{-# LANGUAGE BangPatterns #-} +{-# LANGUAGE MagicHash #-} +{-# LANGUAGE UnliftedNewtypes #-} +{-# LANGUAGE UnboxedTuples #-} +{-# LANGUAGE ViewPatterns #-} + +module Data.Choice ( + -- * Choice + Choice + + -- * Construction + , from_word_lsb# + , from_word_nonzero# + , from_word_eq# + , from_word_le# + , from_word_lt# + , from_word_gt# + + , from_wide_lsb# + , from_wide_le# + + -- * Manipulation + , or_c# + , and_c# + , xor_c# + , not_c# + , ne_c# + , eq_c# + + -- * Constant-time Selection + , ct_select# + , ct_select_wide# + + ) where + +import qualified Data.Bits as B +import GHC.Exts + +-- utilities ------------------------------------------------------------------ + +-- make a mask from a bit (0 -> 0, 1 -> maxBound) +wrapping_neg# :: Word# -> Word# +wrapping_neg# w = plusWord# (not# w) 1## +{-# INLINE wrapping_neg# #-} + +hi# :: Word# -> (# Word#, Word# #) +hi# w = (# 0##, w #) +{-# INLINE hi# #-} + +lo# :: Word# -> (# Word#, Word# #) +lo# w = (# w, 0## #) +{-# INLINE lo# #-} + +not_w# :: (# Word#, Word# #) -> (# Word#, Word# #) +not_w# (# a0, a1 #) = (# not# a0, not# a1 #) +{-# INLINE not_w# #-} + +or_w# :: (# Word#, Word# #) -> (# Word#, Word# #) -> (# Word#, Word# #) +or_w# (# a0, a1 #) (# b0, b1 #) = (# or# a0 b0, or# a1 b1 #) +{-# INLINE or_w# #-} + +and_w# :: (# Word#, Word# #) -> (# Word#, Word# #) -> (# Word#, Word# #) +and_w# (# a0, a1 #) (# b0, b1 #) = (# and# a0 b0, and# a1 b1 #) +{-# INLINE and_w# #-} + +xor_w# :: (# Word#, Word# #) -> (# Word#, Word# #) -> (# Word#, Word# #) +xor_w# (# a0, a1 #) (# b0, b1 #) = (# xor# a0 b0, xor# a1 b1 #) +{-# INLINE xor_w# #-} + +-- subtract-with-borrow +sub_b# :: Word# -> Word# -> Word# -> (# Word#, Word# #) +sub_b# m n b = + let !(# d0, b0 #) = subWordC# m n + !(# d, b1 #) = subWordC# d0 b + !c = int2Word# (orI# b0 b1) + in (# d, c #) +{-# INLINE sub_b# #-} + +-- wide subtract-with-borrow +sub_wb# + :: (# Word#, Word# #) + -> (# Word#, Word# #) + -> (# Word#, Word#, Word# #) +sub_wb# (# a0, a1 #) (# b0, b1 #) = + let !(# s0, c0 #) = sub_b# a0 b0 0## + !(# s1, c1 #) = sub_b# a1 b1 c0 + in (# s0, s1, c1 #) +{-# INLINE sub_wb# #-} + +-- wide subtraction (wrapping) +sub_w# :: (# Word#, Word# #) -> (# Word#, Word# #) -> (# Word#, Word# #) +sub_w# a b = + let !(# c0, c1, _ #) = sub_wb# a b + in (# c0, c1 #) +{-# INLINE sub_w# #-} + +-- choice --------------------------------------------------------------------- + +-- choice encoded as a mask +newtype Choice = Choice Word# + +-- construction --------------------------------------------------------------- + +-- XX remove "debug" conditional before releases +from_word_lsb# :: Word# -> Choice +from_word_lsb# w + | isTrue# (gtWord# w 1##) = + error "ppad-fixed (from_word_lsb#): internal error (non-bit input)" + | otherwise = + Choice (wrapping_neg# w) +{-# INLINE from_word_lsb# #-} + +from_wide_lsb# :: (# Word#, Word# #) -> Choice +from_wide_lsb# (# l, _ #) = from_word_lsb# l +{-# INLINE from_wide_lsb# #-} + +from_word_nonzero# :: Word# -> Choice +from_word_nonzero# w = + let !n = wrapping_neg# w + !s = case B.finiteBitSize (0 :: Word) of I# m -> m -# 1# + !v = uncheckedShiftRL# (or# w n) s + in from_word_lsb# v +{-# INLINE from_word_nonzero# #-} + +from_word_eq# :: Word# -> Word# -> Choice +from_word_eq# x y = case from_word_nonzero# (xor# x y) of + Choice w -> Choice (not# w) +{-# INLINE from_word_eq# #-} + +from_word_le# :: Word# -> Word# -> Choice +from_word_le# x y = + let !s = case B.finiteBitSize (0 :: Word) of I# m -> m -# 1# + !bit = + uncheckedShiftRL# + (and# + (or# (not# x) y) + (or# (xor# x y) (not# (minusWord# y x)))) + s + in from_word_lsb# bit +{-# INLINE from_word_le# #-} + +from_wide_le# :: (# Word#, Word# #) -> (# Word#, Word# #) -> Choice +from_wide_le# x y = + let !s = case B.finiteBitSize (0 :: Word) of I# m -> m -# 1# + !mask = + (and_w# + (or_w# (not_w# x) y) + (or_w# (xor_w# x y) (not_w# (sub_w# y x)))) + !bit = case mask of + (# l, _ #) -> uncheckedShiftRL# l s + in from_word_lsb# bit +{-# INLINE from_wide_le# #-} + +from_word_lt# :: Word# -> Word# -> Choice +from_word_lt# x y = + let !s = case B.finiteBitSize (0 :: Word) of I# m -> m -# 1# + !bit = + uncheckedShiftRL# + (or# + (and# (not# x) y) + (and# (or# (not# x) y) (minusWord# x y))) + s + in from_word_lsb# bit +{-# INLINE from_word_lt# #-} + +from_word_gt# :: Word# -> Word# -> Choice +from_word_gt# x y = from_word_lt# y x +{-# INLINE from_word_gt# #-} + +-- manipulation --------------------------------------------------------------- + +not_c# :: Choice -> Choice +not_c# (Choice w) = Choice (not# w) +{-# INLINE not_c# #-} + +or_c# :: Choice -> Choice -> Choice +or_c# (Choice w0) (Choice w1) = Choice (or# w0 w1) +{-# INLINE or_c# #-} + +and_c# :: Choice -> Choice -> Choice +and_c# (Choice w0) (Choice w1) = Choice (and# w0 w1) +{-# INLINE and_c# #-} + +xor_c# :: Choice -> Choice -> Choice +xor_c# (Choice w0) (Choice w1) = Choice (xor# w0 w1) +{-# INLINE xor_c# #-} + +ne_c# :: Choice -> Choice -> Choice +ne_c# c0 c1 = xor_c# c0 c1 +{-# INLINE ne_c# #-} + +eq_c# :: Choice -> Choice -> Choice +eq_c# c0 c1 = not_c# (ne_c# c0 c1) +{-# INLINE eq_c# #-} + +-- constant-time selection ---------------------------------------------------- + +ct_select# :: Word# -> Word# -> Choice -> Word# +ct_select# a b (Choice c) = xor# a (and# c (xor# a b)) +{-# INLINE ct_select# #-} + +ct_select_wide# + :: (# Word#, Word# #) + -> (# Word#, Word# #) + -> Choice + -> (# Word#, Word# #) +ct_select_wide# a b (Choice w) = + let !mask = or_w# (hi# w) (lo# w) + in xor_w# a (and_w# mask (xor_w# a b)) +{-# INLINE ct_select_wide# #-} + diff --git a/ppad-fixed.cabal b/ppad-fixed.cabal @@ -23,7 +23,8 @@ library ghc-options: -Wall exposed-modules: - Data.Word.Extended + Data.Choice + , Data.Word.Extended , Data.Word.Word256 build-depends: base >= 4.9 && < 5