{-# LANGUAGE FlexibleInstances     #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE ScopedTypeVariables   #-}
{-# LANGUAGE TypeFamilies          #-}

module HaskellWorks.Data.Bits.Broadword.Word32
  ( h
  , l
  , kBitDiff
  , kBitDiffPos
  , kBitDiffUnsafe
  ) where

import Data.Word
import HaskellWorks.Data.Bits.BitWise

-- | Initialise all sub-words of size k where 'k' ∈ { 2, 4, 8, 16, 32 } such that the lowest bit is set to 1 and all other bits are cleared.
--
-- >>> import Numeric(showHex)
-- >>> showHex (l 2) ""
-- "55555555"
-- >>> showHex (l 4) ""
-- "11111111"
-- >>> showHex (l 8) ""
-- "1010101"
-- >>> showHex (l 16) ""
-- "10001"
-- >>> showHex (l 32) ""
-- "1"
l :: Int -> Word32
l :: Int -> Word32
l Int
2  = Word32
0x55555555
l Int
4  = Word32
0x11111111
l Int
8  = Word32
0x01010101
l Int
16 = Word32
0x00010001
l Int
32 = Word32
0x00000001
l Int
k  = [Char] -> Word32
forall a. HasCallStack => [Char] -> a
error ([Char]
"Invalid h k where k = " [Char] -> [Char] -> [Char]
forall a. [a] -> [a] -> [a]
++ Int -> [Char]
forall a. Show a => a -> [Char]
show Int
k)
{-# INLINE l #-}

-- | Initialise all sub-words of size k where 'k' ∈ { 2, 4, 8, 16, 32 } such that the highest bit is set to 1 and all other bits are cleared.
--
-- >>> import Numeric(showHex)
-- >>> showHex (h 2) ""
-- "aaaaaaaa"
-- >>> showHex (h 4) ""
-- "88888888"
-- >>> showHex (h 8) ""
-- "80808080"
-- >>> showHex (h 16) ""
-- "80008000"
-- >>> showHex (h 32) ""
-- "80000000"
h :: Int -> Word32
h :: Int -> Word32
h Int
2  = Word32
0xaaaaaaaa
h Int
4  = Word32
0x88888888
h Int
8  = Word32
0x80808080
h Int
16 = Word32
0x80008000
h Int
32 = Word32
0x80000000
h Int
k  = [Char] -> Word32
forall a. HasCallStack => [Char] -> a
error ([Char]
"Invalid h k where k = " [Char] -> [Char] -> [Char]
forall a. [a] -> [a] -> [a]
++ Int -> [Char]
forall a. Show a => a -> [Char]
show Int
k)
{-# INLINE h #-}

-- | Broadword subtraction of sub-words of size 'k' where 'k' ∈ { 2, 4, 8, 16, 32 }.
--
-- The subtraction respects 2's complement so sub-words may be regarded as signed or unsigned words.
--
-- >>> import Numeric(showHex)
-- >>> showHex (kBitDiff 8 0x04030201 0x02020101) ""
-- "2010100"
-- >>> showHex (kBitDiff 8 0x04030201 0x01020304) ""
-- "301fffd"
-- >>> showHex (kBitDiff 8 0x200000ff 0x100000ff) ""
-- "10000000"
kBitDiff :: Int -> Word32 -> Word32 -> Word32
kBitDiff :: Int -> Word32 -> Word32 -> Word32
kBitDiff Int
k Word32
x Word32
y = ((Word32
x Word32 -> Word32 -> Word32
forall a. BitWise a => a -> a -> a
.|. Int -> Word32
h Int
k) Word32 -> Word32 -> Word32
forall a. Num a => a -> a -> a
- (Word32
y Word32 -> Word32 -> Word32
forall a. BitWise a => a -> a -> a
.&. Word32 -> Word32
forall a. BitWise a => a -> a
comp (Int -> Word32
h Int
k))) Word32 -> Word32 -> Word32
forall a. BitWise a => a -> a -> a
.^. ((Word32
x Word32 -> Word32 -> Word32
forall a. BitWise a => a -> a -> a
.^. Word32 -> Word32
forall a. BitWise a => a -> a
comp Word32
y) Word32 -> Word32 -> Word32
forall a. BitWise a => a -> a -> a
.&. Int -> Word32
h Int
k)
{-# INLINE kBitDiff #-}

-- | Broadword subtraction of sub-words of size 'k' where 'k' ∈ { 2, 4, 8, 16, 32 } where results are bounded from below by 0.
--
-- >>> import Numeric(showHex)
-- >>> showHex (kBitDiffPos 8 0x04030201 0x02020101) ""
-- "2010100"
-- >>> showHex (kBitDiffPos 8 0x04030201 0x01020304) ""
-- "3010000"
-- >>> showHex (kBitDiffPos 8 0x200000ff 0x100000ff) "" -- produces nonsense in the last sub-word
-- "10000000"
kBitDiffPos :: Int -> Word32 -> Word32 -> Word32
kBitDiffPos :: Int -> Word32 -> Word32 -> Word32
kBitDiffPos Int
k Word32
x Word32
y =
                                                                        -- let !_ = trace (">> x = " <> bitShow x) x in
                                                                        -- let !_ = trace (">> y = " <> bitShow y) y in
  let d :: Word32
d = Int -> Word32 -> Word32 -> Word32
kBitDiff Int
k Word32
x Word32
y                                            in  -- let !_ = trace (">> d = " <> bitShow d) d in
  let s :: Word32
s = Int -> Word32 -> Word32 -> Word32
kBitDiff Int
k Word32
0 ((Word32 -> Word32
forall a. BitWise a => a -> a
comp Word32
d Word32 -> Word32 -> Word32
forall a. BitWise a => a -> a -> a
.&. Int -> Word32
h Int
k) Word32 -> Count -> Word32
forall a. Shift a => a -> Count -> a
.>. Int -> Count
forall a b. (Integral a, Num b) => a -> b
fromIntegral (Int
k Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
1))  in  -- let !_ = trace (">> s = " <> bitShow s) s in
  let r :: Word32
r = Word32
d Word32 -> Word32 -> Word32
forall a. BitWise a => a -> a -> a
.&. Word32
s                                                   in  -- let !_ = trace (">> r = " <> bitShow r) r in
  Word32
r
{-# INLINE kBitDiffPos #-}

-- | Broadword subtraction of sub-words of size 'k' where 'k' ∈ { 2, 4, 8, 16, 32 } where all the sub-words of 'x' and 'y' must
-- not have the signed bit set for the result to be meaningful.
--
-- >>> import Numeric(showHex)
-- >>> showHex (kBitDiffUnsafe 8 0x04030201 0x02020101) ""
-- "2010100"
-- >>> showHex (kBitDiffUnsafe 8 0x04030201 0x05060708) ""
-- "fffdfbf9"
-- >>> showHex (kBitDiffUnsafe 8 0x200000ff 0x100000ff) ""
-- "10000080"
kBitDiffUnsafe :: Int -> Word32 -> Word32 -> Word32
kBitDiffUnsafe :: Int -> Word32 -> Word32 -> Word32
kBitDiffUnsafe Int
k Word32
x Word32
y = ((Word32
x Word32 -> Word32 -> Word32
forall a. BitWise a => a -> a -> a
.|. Int -> Word32
h Int
k) Word32 -> Word32 -> Word32
forall a. Num a => a -> a -> a
- Word32
y) Word32 -> Word32 -> Word32
forall a. BitWise a => a -> a -> a
.^. Int -> Word32
h Int
k
{-# INLINE kBitDiffUnsafe #-}