{-# OPTIONS_GHC -Wall -fwarn-tabs -fno-warn-name-shadowing #-}
{-# OPTIONS_HADDOCK hide #-}
{-# LANGUAGE CPP #-}
#ifdef __GLASGOW_HASKELL__
{-# LANGUAGE MagicHash #-}
#endif
#if __GLASGOW_HASKELL__ >= 701
-- Naturally, the MagicHash stuff from "GHC.Exts" isn't considered safe.
{-# LANGUAGE Trustworthy #-}
#endif
----------------------------------------------------------------
--                                                  ~ 2021.12.14
-- |
-- Module      :  Data.Trie.Internal.BitTwiddle
-- Copyright   :  2012 Clark Gaebel, 2012 Johan Tibel, 2002 Daan Leijen
-- License     :  BSD-3-Clause
-- Maintainer  :  libraries@haskell.org, wren@cpan.org
-- Stability   :  stable
-- Portability :  portable (with CPP)
--
-- Functions to treat 'Word' as a bit-vector for big-endian patricia
-- trees. This code is duplicated from "Data.IntMap" (or
-- "Utils.Containers.Internal.BitUtil" these days). The only
-- differences are that some of the conversion functions are
-- specialized to 'Data.Word.Word8' for bytestrings, instead of
-- being specialized to 'Int'.
----------------------------------------------------------------

module Data.Trie.Internal.BitTwiddle
    (
    -- * Type aliases
      KeyElem, Prefix, Mask
    -- * Predicates
    , zero, nomatch, shorter
    -- * Constructors
    , applyMask, getMask
    ) where

import Data.Trie.Internal.ByteString (ByteStringElem)

-- It's too much noise to fully restrict this import, so just note
-- the requirements:
--      base 4.8.0 / GHC 7.10.1 -- 'countLeadingZeros', 'countTrailingZeros'
--      base 4.7.0 / GHC 7.8.2  -- 'FiniteBits', 'finiteBitSize'
--      base 4.5.0 / GHC 7.4.1  -- 'popCount'
import Data.Bits

-- To make it clearer what we're really testing for.
-- TODO: make this into a Cabal flag; for easier testing if nothing else.
#define USE_CLZ_IMPLEMENTATION MIN_VERSION_base(4,8,0)

#if __GLASGOW_HASKELL__ >= 503
-- Before GHC 5.3 these were in "GlaExts" instead.
import GHC.Exts
    ( Word(W#)
    , Int(I#)
#   if USE_CLZ_IMPLEMENTATION
    , shiftL#
#   else
    , shiftRL#
#   endif
    )
#else
import Data.Word (Word)
#endif

----------------------------------------------------------------

-- | 'KeyElem' is what we actually use for 'Prefix' and 'Mask'.
-- For now we're using 'ByteStringElem' ('Data.Word.Word8') for
-- simplicity, but in the future we might switch to a larger word
-- size.
type KeyElem = ByteStringElem

-- | Some prefix of the 'KeyElem', as constructed by 'applyMask'.
type Prefix  = KeyElem

-- | A single bit, signifying a mask (of all the bits preceding the
-- masking bit).
type Mask    = KeyElem

elemToNat :: KeyElem -> Word
{-# INLINE elemToNat #-}
elemToNat :: KeyElem -> Word
elemToNat = KeyElem -> Word
forall a b. (Integral a, Num b) => a -> b
fromIntegral

natToElem :: Word -> KeyElem
{-# INLINE natToElem #-}
natToElem :: Word -> KeyElem
natToElem = Word -> KeyElem
forall a b. (Integral a, Num b) => a -> b
fromIntegral

-- TODO: newer versions of the containers library just use
-- 'unsafeShift{R,L}' unilaterally. So, what is the difference
-- (i.e., these days) between using the 'uncheckedShift{L,RL}#' of
-- 'unsafeShift{L,R}' vs using the 'shift{L,RL}#' of 'shift{L,R}'?
-- Also, do we no longer need to trick GHC into actually unboxing
-- and inlining these?
#if USE_CLZ_IMPLEMENTATION
shiftLL :: Word -> Int -> Word
{-# INLINE shiftLL #-}
#   if __GLASGOW_HASKELL__
-- Use unboxing to get @shiftLL@ inlined.
shiftLL :: Word -> Int -> Word
shiftLL (W# Word#
x) (I# Int#
i) = Word# -> Word
W# (Word# -> Int# -> Word#
shiftL# Word#
x Int#
i)
#   else
shiftLL x i = unsafeShiftL x i
#   endif
#else
shiftRL :: Word -> Int -> Word
{-# INLINE shiftRL #-}
#   if __GLASGOW_HASKELL__
-- Use unboxing to get @shiftRL@ inlined.
shiftRL (W# x) (I# i) = W# (shiftRL# x i)
#   else
shiftRL x i = unsafeShiftR x i
#   endif
#endif


{---------------------------------------------------------------
-- Endian independent bit twiddling (Trie endianness, not architecture)
---------------------------------------------------------------}

-- TODO: should we use the (Bits Word8) instance instead of 'elemToNat'
-- and (Bits Nat)? We need to compare Core, C--, or ASM in order
-- to decide this. The choice will apply to 'zero', 'applyMask', 'maskW',...
-- If we shouldn't, then we should probably send a patch upstream
-- to fix the (Bits Word8) instance.

-- | Is the key zero under the masking bit?  If true then whatever
-- is associated with that key should go to the left, otherwise it
-- should go to the right.
zero :: KeyElem -> Mask -> Bool
{-# INLINE zero #-}
zero :: KeyElem -> KeyElem -> Bool
zero KeyElem
i KeyElem
m = (KeyElem -> Word
elemToNat KeyElem
i) Word -> Word -> Word
forall a. Bits a => a -> a -> a
.&. (KeyElem -> Word
elemToNat KeyElem
m) Word -> Word -> Bool
forall a. Eq a => a -> a -> Bool
== Word
0

-- | Does the masked key /not/ match the prefix?  (Hence a subtree
-- matching the value doesn't exist.)
nomatch :: KeyElem -> Prefix -> Mask -> Bool
{-# INLINE nomatch #-}
nomatch :: KeyElem -> KeyElem -> KeyElem -> Bool
nomatch KeyElem
i KeyElem
p KeyElem
m = KeyElem -> KeyElem -> KeyElem
applyMask KeyElem
i KeyElem
m KeyElem -> KeyElem -> Bool
forall a. Eq a => a -> a -> Bool
/= KeyElem
p

-- | Convert a masking bit to the full mask it represents, and then
-- return the prefix of the key under that mask (i.e., all the bits
-- preceding the masking bit).
applyMask :: KeyElem -> Mask -> Prefix
{-# INLINE applyMask #-}
applyMask :: KeyElem -> KeyElem -> KeyElem
applyMask KeyElem
i KeyElem
m = Word -> Word -> KeyElem
maskW (KeyElem -> Word
elemToNat KeyElem
i) (KeyElem -> Word
elemToNat KeyElem
m)


{---------------------------------------------------------------
-- Big endian operations (Trie endianness, not architecture)
---------------------------------------------------------------}

-- | Get mask by setting all bits higher than the smallest bit in
-- @m@. Then apply that mask to @i@.
maskW :: Word -> Word -> Prefix
{-# INLINE maskW #-}
maskW :: Word -> Word -> KeyElem
maskW Word
i Word
m = Word -> KeyElem
natToElem (Word
i Word -> Word -> Word
forall a. Bits a => a -> a -> a
.&. (Word -> Word
forall a. Bits a => a -> a
complement (Word
mWord -> Word -> Word
forall a. Num a => a -> a -> a
-Word
1) Word -> Word -> Word
forall a. Bits a => a -> a -> a
`xor` Word
m))
-- TODO: try the alternatives mentioned in the Containers paper:
-- \i m -> natToElem (i .&. (negate m - m))
-- \i m -> natToElem (i .&. (m * complement 1))
-- N.B. these return /all/ the low bits, and therefore they are not
-- equal functions for all m. They are, however, equal when only
-- one bit of m is set.

-- | Determine whether the first mask denotes a shorter prefix than
-- the second.
shorter :: Mask -> Mask -> Bool
{-# INLINE shorter #-}
shorter :: KeyElem -> KeyElem -> Bool
shorter KeyElem
m1 KeyElem
m2 = KeyElem -> Word
elemToNat KeyElem
m1 Word -> Word -> Bool
forall a. Ord a => a -> a -> Bool
> KeyElem -> Word
elemToNat KeyElem
m2

-- | Determine first differing bit of two prefixes.
getMask :: Prefix -> Prefix -> Mask
{-# INLINE getMask #-}
getMask :: KeyElem -> KeyElem -> KeyElem
getMask KeyElem
p1 KeyElem
p2 = Word -> KeyElem
natToElem (Word -> Word
highestBitMask (KeyElem -> Word
elemToNat KeyElem
p1 Word -> Word -> Word
forall a. Bits a => a -> a -> a
`xor` KeyElem -> Word
elemToNat KeyElem
p2))

{---------------------------------------------------------------
  Finding the highest bit (mask) in a word [x] can be done efficiently
  in three ways:
  * convert to a floating point value and the mantissa tells us the
    [log2(x)] that corresponds with the highest bit position. The
    mantissa is retrieved either via the standard C function [frexp]
    or by some bit twiddling on IEEE compatible numbers (float).
    Note that one needs to use at least [double] precision for an
    accurate mantissa of 32 bit numbers.
  * use bit twiddling, a logarithmic sequence of bitwise or's and
    shifts (bit).
  * use processor specific assembler instruction (asm).

  The most portable way would be [bit], but is it efficient enough?
  I have measured the cycle counts of the different methods on an
  AMD Athlon-XP 1800 (~ Pentium III 1.8Ghz) using the RDTSC
  instruction:

  highestBitMask: method  cycles
                  --------------
                   frexp   200
                   float    33
                   bit      11
                   asm      12

  highestBit:     method  cycles
                  --------------
                   frexp   195
                   float    33
                   bit      11
                   asm      11

  Wow, the bit twiddling is on today's RISC like machines even
  faster than a single CISC instruction (BSR)!
---------------------------------------------------------------}

{---------------------------------------------------------------
  [highestBitMask] returns a word where only the highest bit is
  set. It is found by first setting all bits in lower positions
  than the highest bit and than taking an exclusive or with the
  original value. Allthough the function may look expensive, GHC
  compiles this into excellent C code that subsequently compiled
  into highly efficient machine code. The algorithm is derived from
  Jorg Arndt's FXT library.
---------------------------------------------------------------}

highestBitMask :: Word -> Word
{-# INLINE highestBitMask #-}
#if USE_CLZ_IMPLEMENTATION
-- This is the implementation used in newer versions of the containers library.
-- Added this implementation here in version 0.2.7.
highestBitMask :: Word -> Word
highestBitMask Word
w = Word -> Int -> Word
shiftLL Word
1 (Int
wordSize Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
1 Int -> Int -> Int
forall a. Num a => a -> a -> a
- Word -> Int
forall b. FiniteBits b => b -> Int
countLeadingZeros Word
w)
#else
-- This is the classic one we used up to bytestring-trie-0.2.6.1
-- And it's still what containers falls back to for older versions of base.
--
-- N.B., because this is not exported and is only used by 'branchMask'
-- which operates on 'Word8' inputs, we can safely restrict the
-- algorithm to only doing the first few steps, rather than doing
-- all the steps needed for 'Word64'.
highestBitMask x
    = case (x .|. shiftRL x 1) of
       x -> case (x .|. shiftRL x 2) of
        x -> case (x .|. shiftRL x 4) of        -- for 8-bit input range.
        {-
         x -> case (x .|. shiftRL x 8) of       -- for 16-bit
          x -> case (x .|. shiftRL x 16) of     -- for 32-bit
           x -> case (x .|. shiftRL x 32) of    -- for 64-bit platforms
        -}
            x -> (x `xor` shiftRL x 1)
#endif

#if USE_CLZ_IMPLEMENTATION
wordSize :: Int
{-# INLINE wordSize #-}
#   if MIN_VERSION_base(4,7,0)
wordSize :: Int
wordSize = Word -> Int
forall b. FiniteBits b => b -> Int
finiteBitSize (Word
0 :: Word)
#   else
wordSize = bitSize (0 :: Word)
#   endif
#endif

----------------------------------------------------------------
----------------------------------------------------------- fin.