{-# LANGUAGE BangPatterns #-}
{-# LANGUAGE MagicHash #-}
{-# LANGUAGE UnliftedNewtypes #-}
{-# LANGUAGE UnboxedTuples #-}
{-# LANGUAGE ViewPatterns #-}

-- |
-- Module: Data.Choice
-- Copyright: (c) 2025 Jared Tobin
-- License: MIT
-- Maintainer: Jared Tobin <jared@ppad.tech>
--
-- Constant-time choice.

module Data.Choice (
  -- * Choice
    Choice
  , true#
  , false#
  , decide
  , to_word#

  -- * MaybeWord#
  , MaybeWord#(..)
  , some_word#
  , none_word#

  -- * MaybeWide#
  , MaybeWide#(..)
  , some_wide#
  , just_wide#
  , none_wide#
  , expect_wide#
  , expect_wide_or#

  -- * Construction
  , from_word_mask#
  , from_word#
  , from_word_nonzero#
  , from_word_eq#
  , from_word_le#
  , from_word_lt#
  , from_word_gt#

  , from_wide#
  , from_wide_le#

  -- * Manipulation
  , or#
  , and#
  , xor#
  , not#
  , ne#
  , eq#

  -- * Constant-time Selection
  , select_word#
  , select_wide#
  , select_wider#

  -- * Constant-time Equality
  , eq_word#
  , eq_wide#
  , eq_wider#
  ) where

import qualified Data.Bits as B
import GHC.Exts (Word#, Int(..), Word(..))
import qualified GHC.Exts as Exts

-- utilities ------------------------------------------------------------------

-- wrapping negation
neg_w# :: Word# -> Word#
neg_w# :: Word# -> Word#
neg_w# Word#
w = Word# -> Word# -> Word#
Exts.plusWord# (Word# -> Word#
Exts.not# Word#
w) Word#
1##
{-# INLINE neg_w# #-}

hi# :: Word# -> (# Word#, Word# #)
hi# :: Word# -> (# Word#, Word# #)
hi# Word#
w = (# Word#
0##, Word#
w #)
{-# INLINE hi# #-}

lo# :: Word# -> (# Word#, Word# #)
lo# :: Word# -> (# Word#, Word# #)
lo# Word#
w = (# Word#
w, Word#
0## #)
{-# INLINE lo# #-}

not_w# :: (# Word#, Word# #) -> (# Word#, Word# #)
not_w# :: (# Word#, Word# #) -> (# Word#, Word# #)
not_w# (# Word#
a0, Word#
a1 #) = (# Word# -> Word#
Exts.not# Word#
a0, Word# -> Word#
Exts.not# Word#
a1 #)
{-# INLINE not_w# #-}

or_w# :: (# Word#, Word# #) -> (# Word#, Word# #) -> (# Word#, Word# #)
or_w# :: (# Word#, Word# #) -> (# Word#, Word# #) -> (# Word#, Word# #)
or_w# (# Word#
a0, Word#
a1 #) (# Word#
b0, Word#
b1 #) = (# Word# -> Word# -> Word#
Exts.or# Word#
a0 Word#
b0, Word# -> Word# -> Word#
Exts.or# Word#
a1 Word#
b1 #)
{-# INLINE or_w# #-}

and_w# :: (# Word#, Word# #) -> (# Word#, Word# #) -> (# Word#, Word# #)
and_w# :: (# Word#, Word# #) -> (# Word#, Word# #) -> (# Word#, Word# #)
and_w# (# Word#
a0, Word#
a1 #) (# Word#
b0, Word#
b1 #) = (# Word# -> Word# -> Word#
Exts.and# Word#
a0 Word#
b0, Word# -> Word# -> Word#
Exts.and# Word#
a1 Word#
b1 #)
{-# INLINE and_w# #-}

xor_w# :: (# Word#, Word# #) -> (# Word#, Word# #) -> (# Word#, Word# #)
xor_w# :: (# Word#, Word# #) -> (# Word#, Word# #) -> (# Word#, Word# #)
xor_w# (# Word#
a0, Word#
a1 #) (# Word#
b0, Word#
b1 #) = (# Word# -> Word# -> Word#
Exts.xor# Word#
a0 Word#
b0, Word# -> Word# -> Word#
Exts.xor# Word#
a1 Word#
b1 #)
{-# INLINE xor_w# #-}

-- subtract-with-borrow
sub_b# :: Word# -> Word# -> Word# -> (# Word#, Word# #)
sub_b# :: Word# -> Word# -> Word# -> (# Word#, Word# #)
sub_b# Word#
m Word#
n Word#
b =
  let !(# Word#
d0, Int#
b0 #) = Word# -> Word# -> (# Word#, Int# #)
Exts.subWordC# Word#
m Word#
n
      !(#  Word#
d, Int#
b1 #) = Word# -> Word# -> (# Word#, Int# #)
Exts.subWordC# Word#
d0 Word#
b
      !c :: Word#
c = Int# -> Word#
Exts.int2Word# (Int# -> Int# -> Int#
Exts.orI# Int#
b0 Int#
b1)
  in  (# Word#
d, Word#
c #)
{-# INLINE sub_b# #-}

-- wide subtract-with-borrow
sub_wb#
  :: (# Word#, Word# #)
  -> (# Word#, Word# #)
  -> (# Word#, Word#, Word# #)
sub_wb# :: (# Word#, Word# #)
-> (# Word#, Word# #) -> (# Word#, Word#, Word# #)
sub_wb# (# Word#
a0, Word#
a1 #) (# Word#
b0, Word#
b1 #) =
  let !(# Word#
s0, Word#
c0 #) = Word# -> Word# -> Word# -> (# Word#, Word# #)
sub_b# Word#
a0 Word#
b0 Word#
0##
      !(# Word#
s1, Word#
c1 #) = Word# -> Word# -> Word# -> (# Word#, Word# #)
sub_b# Word#
a1 Word#
b1 Word#
c0
  in  (# Word#
s0, Word#
s1, Word#
c1 #)
{-# INLINE sub_wb# #-}

-- wide subtraction (wrapping)
sub_w#
  :: (# Word#, Word# #)
  -> (# Word#, Word# #)
  -> (# Word#, Word# #)
sub_w# :: (# Word#, Word# #) -> (# Word#, Word# #) -> (# Word#, Word# #)
sub_w# (# Word#, Word# #)
a (# Word#, Word# #)
b =
  let !(# Word#
c0, Word#
c1, Word#
_ #) = (# Word#, Word# #)
-> (# Word#, Word# #) -> (# Word#, Word#, Word# #)
sub_wb# (# Word#, Word# #)
a (# Word#, Word# #)
b
  in  (# Word#
c0, Word#
c1 #)
{-# INLINE sub_w# #-}

-- choice ---------------------------------------------------------------------

-- | Constant-time choice, encoded as a mask.
--
--   Note that 'Choice' is defined as an unboxed newtype, and so a
--   'Choice' value cannot be bound at the top level. You should work
--   with it locally in the context of a computation.
--
--   It's safe to 'decide' a choice, reducing it to a 'Bool', at any
--   time, but the general encouraged pattern is to do that only at the
--   end of a computation.
--
--   >>> decide (or# (false# ()) (true# ()))
--   True
newtype Choice = Choice Word#

-- | Construct the falsy value.
--
--   >>> decide (false# ())
--   False
false# :: () -> Choice
false# :: () -> Choice
false# ()
_ = Word# -> Choice
Choice Word#
0##
{-# INLINE false# #-}

-- | Construct the truthy value.
--
--   >>> decide (true# ())
--   True
true# :: () -> Choice
true# :: () -> Choice
true# ()
_ = case Word
forall a. Bounded a => a
maxBound :: Word of
  W# Word#
w -> Word# -> Choice
Choice Word#
w
{-# INLINE true# #-}

-- | Decide a 'Choice' by reducing it to a 'Bool'.
--
--   >>> decide (true# ())
--   True
decide :: Choice -> Bool
decide :: Choice -> Bool
decide (Choice Word#
c) = Int# -> Bool
Exts.isTrue# (Word# -> Word# -> Int#
Exts.neWord# Word#
c Word#
0##)
{-# INLINE decide #-}

-- | Convert a 'Choice' to an unboxed 'Word#'.
to_word# :: Choice -> Word#
to_word# :: Choice -> Word#
to_word# (Choice Word#
c) = Word# -> Word# -> Word#
Exts.and# Word#
c Word#
1##
{-# INLINE to_word# #-}

-- constant time 'Maybe Word#'
newtype MaybeWord# = MaybeWord# (# Word#, Choice #)

some_word# :: Word# -> MaybeWord#
some_word# :: Word# -> MaybeWord#
some_word# Word#
w = (# Word#, Choice #) -> MaybeWord#
MaybeWord# (# Word#
w, () -> Choice
true# () #)
{-# INLINE some_word# #-}

none_word# :: Word# -> MaybeWord#
none_word# :: Word# -> MaybeWord#
none_word# Word#
w = (# Word#, Choice #) -> MaybeWord#
MaybeWord# (# Word#
w, () -> Choice
false# () #)
{-# INLINE none_word# #-}

-- constant time 'Maybe (# Word#, Word# #)'
newtype MaybeWide# = MaybeWide# (# (# Word#, Word# #), Choice #)

just_wide# :: (# Word#, Word# #) -> Choice -> MaybeWide#
just_wide# :: (# Word#, Word# #) -> Choice -> MaybeWide#
just_wide# (# Word#, Word# #)
w Choice
c = (# (# Word#, Word# #), Choice #) -> MaybeWide#
MaybeWide# (# (# Word#, Word# #)
w, Choice
c #)
{-# INLINE just_wide# #-}

some_wide# :: (# Word#, Word# #) -> MaybeWide#
some_wide# :: (# Word#, Word# #) -> MaybeWide#
some_wide# (# Word#, Word# #)
w = (# (# Word#, Word# #), Choice #) -> MaybeWide#
MaybeWide# (# (# Word#, Word# #)
w, () -> Choice
true# () #)
{-# INLINE some_wide# #-}

none_wide# :: (# Word#, Word# #) -> MaybeWide#
none_wide# :: (# Word#, Word# #) -> MaybeWide#
none_wide# (# Word#, Word# #)
w = (# (# Word#, Word# #), Choice #) -> MaybeWide#
MaybeWide# (# (# Word#, Word# #)
w, () -> Choice
false# () #)
{-# INLINE none_wide# #-}

expect_wide# :: MaybeWide# -> String -> (# Word#, Word# #)
expect_wide# :: MaybeWide# -> [Char] -> (# Word#, Word# #)
expect_wide# (MaybeWide# (# (# Word#, Word# #)
w, Choice Word#
c #)) [Char]
msg
    | Int# -> Bool
Exts.isTrue# (Word# -> Word# -> Int#
Exts.eqWord# Word#
c Word#
t#) = (# Word#, Word# #)
w
    | Bool
otherwise = [Char] -> (# Word#, Word# #)
forall a. HasCallStack => [Char] -> a
error ([Char] -> (# Word#, Word# #)) -> [Char] -> (# Word#, Word# #)
forall a b. (a -> b) -> a -> b
$ [Char]
"ppad-fixed (expect_wide#): " [Char] -> [Char] -> [Char]
forall a. Semigroup a => a -> a -> a
<> [Char]
msg
  where
    !(Choice Word#
t#) = () -> Choice
true# ()
{-# INLINE expect_wide# #-}

expect_wide_or# :: MaybeWide# -> (# Word#, Word# #) -> (# Word#, Word# #)
expect_wide_or# :: MaybeWide# -> (# Word#, Word# #) -> (# Word#, Word# #)
expect_wide_or# (MaybeWide# (# (# Word#, Word# #)
w, Choice Word#
c #)) (# Word#, Word# #)
alt
    | Int# -> Bool
Exts.isTrue# (Word# -> Word# -> Int#
Exts.eqWord# Word#
c Word#
t#) = (# Word#, Word# #)
w
    | Bool
otherwise = (# Word#, Word# #)
alt
  where
    !(Choice Word#
t#) = () -> Choice
true# ()
{-# INLINE expect_wide_or# #-}

-- construction ---------------------------------------------------------------

-- | Construct a 'Choice' from an unboxed mask.
--
--   The input is /not/ checked.
--
--   >>> decide (from_word_mask# 0##)
--   False
--   >>> decide (from_word_mask# 0xFFFFFFFFF_FFFFFFFF##)
--   True
from_word_mask# :: Word# -> Choice
from_word_mask# :: Word# -> Choice
from_word_mask# Word#
w = Word# -> Choice
Choice Word#
w
{-# INLINE from_word_mask# #-}

-- | Construct a 'Choice' from an unboxed word, which should be either
--   0## or 1##.
--
--   The input is /not/ checked.
--
--   >>> decide (from_word# 1##)
--   True
from_word# :: Word# -> Choice
from_word# :: Word# -> Choice
from_word# Word#
w = Word# -> Choice
Choice (Word# -> Word#
neg_w# Word#
w)
{-# INLINE from_word# #-}

-- | Construct a 'Choice' from a two-limb word, constructing a mask from
--   the lower limb, which should be 0## or 1##.
--
--   The input is /not/ checked.
--
--   >>> decide (from_wide# (# 0##, 1## #))
--   False
from_wide# :: (# Word#, Word# #) -> Choice
from_wide# :: (# Word#, Word# #) -> Choice
from_wide# (# Word#
l, Word#
_ #) = Word# -> Choice
from_word# Word#
l
{-# INLINE from_wide# #-}

-- | Construct a 'Choice' from a /nonzero/ unboxed word.
--
--   The input is /not/ checked.
--
--   >>> decide (from_word_nonzero# 2##)
--   True
from_word_nonzero# :: Word# -> Choice
from_word_nonzero# :: Word# -> Choice
from_word_nonzero# Word#
w =
  let !n :: Word#
n = Word# -> Word#
neg_w# Word#
w
      !s :: Int#
s = case Word -> Int
forall b. FiniteBits b => b -> Int
B.finiteBitSize (Word
0 :: Word) of I# Int#
m -> Int#
m Int# -> Int# -> Int#
Exts.-# Int#
1#
      !v :: Word#
v = Word# -> Int# -> Word#
Exts.uncheckedShiftRL# (Word# -> Word# -> Word#
Exts.or# Word#
w Word#
n) Int#
s
  in  Word# -> Choice
from_word# Word#
v
{-# INLINE from_word_nonzero# #-}

-- | Construct a 'Choice' from an equality comparison.
--
--   >>> decide (from_word_eq# 0## 1##)
--   False
--   decide (from_word_eq# 1## 1##)
--   True
from_word_eq# :: Word# -> Word# -> Choice
from_word_eq# :: Word# -> Word# -> Choice
from_word_eq# Word#
x Word#
y = case Word# -> Choice
from_word_nonzero# (Word# -> Word# -> Word#
Exts.xor# Word#
x Word#
y) of
  Choice Word#
w -> Word# -> Choice
Choice (Word# -> Word#
Exts.not# Word#
w)
{-# INLINE from_word_eq# #-}

-- | Construct a 'Choice from an at most comparison.
--
--   >>> decide (from_word_le# 0## 1##)
--   True
--   >>> decide (from_word_le# 1## 1##)
--   True
from_word_le# :: Word# -> Word# -> Choice
from_word_le# :: Word# -> Word# -> Choice
from_word_le# Word#
x Word#
y =
  let !s :: Int#
s = case Word -> Int
forall b. FiniteBits b => b -> Int
B.finiteBitSize (Word
0 :: Word) of I# Int#
m -> Int#
m Int# -> Int# -> Int#
Exts.-# Int#
1#
      !bit :: Word#
bit =
        Word# -> Int# -> Word#
Exts.uncheckedShiftRL#
          (Word# -> Word# -> Word#
Exts.and#
            (Word# -> Word# -> Word#
Exts.or# (Word# -> Word#
Exts.not# Word#
x) Word#
y)
            (Word# -> Word# -> Word#
Exts.or# (Word# -> Word# -> Word#
Exts.xor# Word#
x Word#
y) (Word# -> Word#
Exts.not# (Word# -> Word# -> Word#
Exts.minusWord# Word#
y Word#
x))))
          Int#
s
  in  Word# -> Choice
from_word# Word#
bit
{-# INLINE from_word_le# #-}

-- | Construct a 'Choice' from an at most comparison on a two-limb
--   unboxed word.
--
--   >>> decide (from_wide_le# (# 0##, 0## #) (# 1##, 0## #))
--   True
--   >>> decide (from_wide_le# (# 1##, 0## #) (# 1##, 0## #))
--   True
from_wide_le# :: (# Word#, Word# #) -> (# Word#, Word# #) -> Choice
from_wide_le# :: (# Word#, Word# #) -> (# Word#, Word# #) -> Choice
from_wide_le# (# Word#, Word# #)
x (# Word#, Word# #)
y =
  let !s :: Int#
s = case Word -> Int
forall b. FiniteBits b => b -> Int
B.finiteBitSize (Word
0 :: Word) of I# Int#
m -> Int#
m Int# -> Int# -> Int#
Exts.-# Int#
1#
      !mask :: (# Word#, Word# #)
mask =
        ((# Word#, Word# #) -> (# Word#, Word# #) -> (# Word#, Word# #)
and_w#
          ((# Word#, Word# #) -> (# Word#, Word# #) -> (# Word#, Word# #)
or_w# ((# Word#, Word# #) -> (# Word#, Word# #)
not_w# (# Word#, Word# #)
x) (# Word#, Word# #)
y)
          ((# Word#, Word# #) -> (# Word#, Word# #) -> (# Word#, Word# #)
or_w# ((# Word#, Word# #) -> (# Word#, Word# #) -> (# Word#, Word# #)
xor_w# (# Word#, Word# #)
x (# Word#, Word# #)
y) ((# Word#, Word# #) -> (# Word#, Word# #)
not_w# ((# Word#, Word# #) -> (# Word#, Word# #) -> (# Word#, Word# #)
sub_w# (# Word#, Word# #)
y (# Word#, Word# #)
x))))
      !bit :: Word#
bit = case (# Word#, Word# #)
mask of
        (# Word#
l, Word#
_ #) -> Word# -> Int# -> Word#
Exts.uncheckedShiftRL# Word#
l Int#
s
  in  Word# -> Choice
from_word# Word#
bit
{-# INLINE from_wide_le# #-}

-- | Construct a 'Choice' from a less-than comparison.
--
--   >>> decide (from_word_lt# 0## 1##)
--   True
--   >>> decide (from_word_lt# 1## 1##)
--   False
from_word_lt# :: Word# -> Word# -> Choice
from_word_lt# :: Word# -> Word# -> Choice
from_word_lt# Word#
x Word#
y =
  let !s :: Int#
s = case Word -> Int
forall b. FiniteBits b => b -> Int
B.finiteBitSize (Word
0 :: Word) of I# Int#
m -> Int#
m Int# -> Int# -> Int#
Exts.-# Int#
1#
      !bit :: Word#
bit =
        Word# -> Int# -> Word#
Exts.uncheckedShiftRL#
          (Word# -> Word# -> Word#
Exts.or#
            (Word# -> Word# -> Word#
Exts.and# (Word# -> Word#
Exts.not# Word#
x) Word#
y)
            (Word# -> Word# -> Word#
Exts.and# (Word# -> Word# -> Word#
Exts.or# (Word# -> Word#
Exts.not# Word#
x) Word#
y) (Word# -> Word# -> Word#
Exts.minusWord# Word#
x Word#
y)))
          Int#
s
  in  Word# -> Choice
from_word# Word#
bit
{-# INLINE from_word_lt# #-}

-- | Construct a 'Choice' from a greater-than comparison.
--
--   >>> decide (from_word_gt# 0## 1##)
--   False
--   >>> decide (from_word_gt# 1## 1##)
--   False
from_word_gt# :: Word# -> Word# -> Choice
from_word_gt# :: Word# -> Word# -> Choice
from_word_gt# Word#
x Word#
y = Word# -> Word# -> Choice
from_word_lt# Word#
y Word#
x
{-# INLINE from_word_gt# #-}

-- manipulation ---------------------------------------------------------------

-- | Logically negate a 'Choice'.
not# :: Choice -> Choice
not# :: Choice -> Choice
not# (Choice Word#
w) = Word# -> Choice
Choice (Word# -> Word#
Exts.not# Word#
w)
{-# INLINE not# #-}

-- | Logical disjunction on 'Choice' values.
or# :: Choice -> Choice -> Choice
or# :: Choice -> Choice -> Choice
or# (Choice Word#
w0) (Choice Word#
w1) = Word# -> Choice
Choice (Word# -> Word# -> Word#
Exts.or# Word#
w0 Word#
w1)
{-# INLINE or# #-}

-- | Logical conjunction on 'Choice' values.
and# :: Choice -> Choice -> Choice
and# :: Choice -> Choice -> Choice
and# (Choice Word#
w0) (Choice Word#
w1) = Word# -> Choice
Choice (Word# -> Word# -> Word#
Exts.and# Word#
w0 Word#
w1)
{-# INLINE and# #-}

-- | Logical inequality on 'Choice' values.
xor# :: Choice -> Choice -> Choice
xor# :: Choice -> Choice -> Choice
xor# (Choice Word#
w0) (Choice Word#
w1) = Word# -> Choice
Choice (Word# -> Word# -> Word#
Exts.xor# Word#
w0 Word#
w1)
{-# INLINE xor# #-}

-- | Logical inequality on 'Choice' values.
ne# :: Choice -> Choice -> Choice
ne# :: Choice -> Choice -> Choice
ne# Choice
c0 Choice
c1 = Choice -> Choice -> Choice
xor# Choice
c0 Choice
c1
{-# INLINE ne# #-}

-- | Logical equality on 'Choice' values.
eq# :: Choice -> Choice -> Choice
eq# :: Choice -> Choice -> Choice
eq# Choice
c0 Choice
c1 = Choice -> Choice
not# (Choice -> Choice -> Choice
ne# Choice
c0 Choice
c1)
{-# INLINE eq# #-}

-- constant-time selection ----------------------------------------------------

-- | Select an unboxed word, given a 'Choice'.
select_word# :: Word# -> Word# -> Choice -> Word#
select_word# :: Word# -> Word# -> Choice -> Word#
select_word# Word#
a Word#
b (Choice Word#
c) = Word# -> Word# -> Word#
Exts.xor# Word#
a (Word# -> Word# -> Word#
Exts.and# Word#
c (Word# -> Word# -> Word#
Exts.xor# Word#
a Word#
b))
{-# INLINE select_word# #-}

-- | Select an unboxed two-limb word, given a 'Choice'.
select_wide#
  :: (# Word#, Word# #)
  -> (# Word#, Word# #)
  -> Choice
  -> (# Word#, Word# #)
select_wide# :: (# Word#, Word# #)
-> (# Word#, Word# #) -> Choice -> (# Word#, Word# #)
select_wide# (# Word#, Word# #)
a (# Word#, Word# #)
b (Choice Word#
w) =
  let !mask :: (# Word#, Word# #)
mask = (# Word#, Word# #) -> (# Word#, Word# #) -> (# Word#, Word# #)
or_w# (Word# -> (# Word#, Word# #)
hi# Word#
w) (Word# -> (# Word#, Word# #)
lo# Word#
w)
  in  (# Word#, Word# #) -> (# Word#, Word# #) -> (# Word#, Word# #)
xor_w# (# Word#, Word# #)
a ((# Word#, Word# #) -> (# Word#, Word# #) -> (# Word#, Word# #)
and_w# (# Word#, Word# #)
mask ((# Word#, Word# #) -> (# Word#, Word# #) -> (# Word#, Word# #)
xor_w# (# Word#, Word# #)
a (# Word#, Word# #)
b))
{-# INLINE select_wide# #-}

-- | Select an unboxed four-limb word, given a 'Choice'.
select_wider#
  :: (# Word#, Word#, Word#, Word# #)
  -> (# Word#, Word#, Word#, Word# #)
  -> Choice
  -> (# Word#, Word#, Word#, Word# #)
select_wider# :: (# Word#, Word#, Word#, Word# #)
-> (# Word#, Word#, Word#, Word# #)
-> Choice
-> (# Word#, Word#, Word#, Word# #)
select_wider# (# Word#
a0, Word#
a1, Word#
a2, Word#
a3 #) (# Word#
b0, Word#
b1, Word#
b2, Word#
b3 #) (Choice Word#
w) =
  let !w0 :: Word#
w0 = Word# -> Word# -> Word#
Exts.xor# Word#
a0 (Word# -> Word# -> Word#
Exts.and# Word#
w (Word# -> Word# -> Word#
Exts.xor# Word#
a0 Word#
b0))
      !w1 :: Word#
w1 = Word# -> Word# -> Word#
Exts.xor# Word#
a1 (Word# -> Word# -> Word#
Exts.and# Word#
w (Word# -> Word# -> Word#
Exts.xor# Word#
a1 Word#
b1))
      !w2 :: Word#
w2 = Word# -> Word# -> Word#
Exts.xor# Word#
a2 (Word# -> Word# -> Word#
Exts.and# Word#
w (Word# -> Word# -> Word#
Exts.xor# Word#
a2 Word#
b2))
      !w3 :: Word#
w3 = Word# -> Word# -> Word#
Exts.xor# Word#
a3 (Word# -> Word# -> Word#
Exts.and# Word#
w (Word# -> Word# -> Word#
Exts.xor# Word#
a3 Word#
b3))
  in  (# Word#
w0, Word#
w1, Word#
w2, Word#
w3 #)
{-# INLINE select_wider# #-}

-- constant-time equality -----------------------------------------------------

-- | Compare unboxed words for equality in constant time.
--
--   >>> decide (eq_word# 0## 1##)
--   False
eq_word# :: Word# -> Word# -> Choice
eq_word# :: Word# -> Word# -> Choice
eq_word# Word#
a Word#
b =
  let !s :: Int#
s = case Word -> Int
forall b. FiniteBits b => b -> Int
B.finiteBitSize (Word
0 :: Word) of I# Int#
m -> Int#
m Int# -> Int# -> Int#
Exts.-# Int#
1#
      !x :: Word#
x = Word# -> Word# -> Word#
Exts.xor# Word#
a Word#
b
      !y :: Word#
y = Word# -> Int# -> Word#
Exts.uncheckedShiftRL# (Word# -> Word# -> Word#
Exts.or# Word#
x (Word# -> Word#
neg_w# Word#
x)) Int#
s
  in  Word# -> Choice
Choice (Word# -> Word# -> Word#
Exts.xor# Word#
y Word#
1##)
{-# INLINE eq_word# #-}

-- | Compare unboxed two-limb words for equality in constant time.
--
--   >>> decide (eq_wide (# 0##, 0## #) (# 0##, 0## #))
--   True
eq_wide#
  :: (# Word#, Word# #)
  -> (# Word#, Word# #)
  -> Choice
eq_wide# :: (# Word#, Word# #) -> (# Word#, Word# #) -> Choice
eq_wide# (# Word#
a0, Word#
a1 #) (# Word#
b0, Word#
b1 #) =
  let !s :: Int#
s = case Word -> Int
forall b. FiniteBits b => b -> Int
B.finiteBitSize (Word
0 :: Word) of I# Int#
m -> Int#
m Int# -> Int# -> Int#
Exts.-# Int#
1#
      !x :: Word#
x = Word# -> Word# -> Word#
Exts.or# (Word# -> Word# -> Word#
Exts.xor# Word#
a0 Word#
b0) (Word# -> Word# -> Word#
Exts.xor# Word#
a1 Word#
b1)
      !y :: Word#
y = Word# -> Int# -> Word#
Exts.uncheckedShiftRL# (Word# -> Word# -> Word#
Exts.or# Word#
x (Word# -> Word#
neg_w# Word#
x)) Int#
s
  in  Word# -> Choice
Choice (Word# -> Word# -> Word#
Exts.xor# Word#
y Word#
1##)
{-# INLINE eq_wide# #-}

-- | Compare unboxed four-limb words for equality in constant time.
--
--   >>> let zero = (# 0##, 0##, 0##, 0## #) in decide (eq_wider# zero zero)
--   True
eq_wider#
  :: (# Word#, Word#, Word#, Word# #)
  -> (# Word#, Word#, Word#, Word# #)
  -> Choice
eq_wider# :: (# Word#, Word#, Word#, Word# #)
-> (# Word#, Word#, Word#, Word# #) -> Choice
eq_wider# (# Word#
a0, Word#
a1, Word#
a2, Word#
a3 #) (# Word#
b0, Word#
b1, Word#
b2, Word#
b3 #) =
  let !s :: Int#
s = case Word -> Int
forall b. FiniteBits b => b -> Int
B.finiteBitSize (Word
0 :: Word) of I# Int#
m -> Int#
m Int# -> Int# -> Int#
Exts.-# Int#
1#
      !x :: Word#
x = Word# -> Word# -> Word#
Exts.or# (Word# -> Word# -> Word#
Exts.or# (Word# -> Word# -> Word#
Exts.xor# Word#
a0 Word#
b0) (Word# -> Word# -> Word#
Exts.xor# Word#
a1 Word#
b1))
                    (Word# -> Word# -> Word#
Exts.or# (Word# -> Word# -> Word#
Exts.xor# Word#
a2 Word#
b2) (Word# -> Word# -> Word#
Exts.xor# Word#
a3 Word#
b3))
      !y :: Word#
y = Word# -> Int# -> Word#
Exts.uncheckedShiftRL# (Word# -> Word# -> Word#
Exts.or# Word#
x (Word# -> Word#
neg_w# Word#
x)) Int#
s
  in  Word# -> Choice
Choice (Word# -> Word# -> Word#
Exts.xor# Word#
y Word#
1##)
{-# INLINE eq_wider# #-}