module Data.Urn.Internal.AlmostPerfect (almostPerfect, reverseBits#) where
import Data.List.NonEmpty (NonEmpty(..))
import GHC.Integer.Logarithms
import GHC.Exts
almostPerfect :: (b -> b -> b) -> (a -> b) -> Word -> NonEmpty a -> b
almostPerfect node leaf (W# size) (e0:|elements0) =
case go perfectDepth 0## (e0:elements0) of (# tree, _, _ #) -> tree
where
perfectDepthInt = wordLog2# size
perfectDepth = int2Word# perfectDepthInt
remainder = size -.# (1## <<.# perfectDepthInt)
go 0## index elements
| reverseBits# perfectDepth index <.# remainder
, l:r:elements' <- elements
= (# leaf l `node` leaf r, elements', succ# index #)
| x:elements' <- elements
= (# leaf x, elements', succ# index #)
| otherwise
= error $ "almostPerfect: size mismatch: got input of length " ++
show (length (e0:|elements0)) ++
", but expected size " ++ show (W# size)
go depth index elements =
let (# l, elements', index' #) = go (pred# depth) index elements
(# r, elements'', index'' #) = go (pred# depth) index' elements'
in (# l `node` r, elements'', index'' #)
reverseBits# :: Word# -> Word# -> Word#
reverseBits# = go 0##
where go r 0## _ = r
go r n x =
go ((r <<.# 1#) `or#` (x `and#` 1##))
(pred# n)
(x >>.# 1#)
succ# :: Word# -> Word#
succ# x = x `plusWord#` 1##
pred# :: Word# -> Word#
pred# x = x -.# 1##
(-.#) :: Word# -> Word# -> Word#
(-.#) = minusWord#
(<<.#) :: Word# -> Int# -> Word#
(<<.#) = uncheckedShiftL#
(>>.#) :: Word# -> Int# -> Word#
(>>.#) = uncheckedShiftRL#
(<.#) :: Word# -> Word# -> Bool
m <.# n = case m `ltWord#` n of
0# -> False
_ -> True