module Data.ZigZag(zzEncode,zzEncodeInteger,zzDecode8,zzDecode16,zzDecode32,zzDecode64,zzDecodeInteger,zzDecode) where

import Data.Word
import Data.Int
import Data.Bits

{-# SPECIALIZE INLINE zzEncode :: Int8 -> Word8 #-}
{-# SPECIALIZE INLINE zzEncode :: Int16 -> Word16 #-}
{-# SPECIALIZE INLINE zzEncode :: Int32 -> Word32 #-}
{-# SPECIALIZE INLINE zzEncode :: Int64 -> Word64 #-}
zzEncode :: (Num b, Integral a, FiniteBits a) => a -> b
zzEncode w = fromIntegral ((w `shiftL` 1) `xor` (w `shiftR` (finiteBitSize w -1)))

--{-# INLINE zzEncode8 #-}
--zzEncode8 :: Int8 -> Word8
-- zzEncode8 x = fromIntegral ((x `shiftL` 1) `xor` (x `shiftR` 7))

-- {-# INLINE zzEncode16 #-}
-- zzEncode16 :: Int16 -> Word16
-- zzEncode16 x = fromIntegral ((x `shiftL` 1) `xor` (x `shiftR` 15))

-- {-# INLINE zzEncode32 #-}
-- zzEncode32 :: Int32 -> Word32
-- zzEncode32 x = fromIntegral ((x `shiftL` 1) `xor` (x `shiftR` 31))

-- {-# INLINE zzEncode64 #-}
-- zzEncode64 :: Int64 -> Word64
-- zzEncode64 x = fromIntegral ((x `shiftL` 1) `xor` (x `shiftR` 63))

{-# INLINE zzEncodeInteger #-}
zzEncodeInteger :: Integer -> Integer
zzEncodeInteger x | x>=0      = x `shiftL` 1
                  | otherwise = negate (x `shiftL` 1) - 1

-- {-# SPECIALIZE INLINE zzDecode :: Word8 -> Int8 #-}
-- {-# SPECIALIZE INLINE zzDecode :: Word16 -> Int16 #-}
-- {-# SPECIALIZE INLINE zzDecode :: Word32 -> Int32 #-}
-- {-# SPECIALIZE INLINE zzDecode :: Word64 -> Int64 #-}
-- {-# SPECIALIZE INLINE zzDecode :: Integer -> Integer #-}

{-# INLINE zzDecode #-}
zzDecode :: (Num a, Integral a1, Bits a1) => a1 -> a
zzDecode w = fromIntegral ((w `shiftR` 1) `xor` (negate (w .&. 1)))
-- zzDecode w = (fromIntegral (w `shiftR` 1)) `xor` (negate (fromIntegral (w .&. 1)))

{-# INLINE zzDecode8 #-}
zzDecode8 :: Word8 -> Int8
zzDecode8 = zzDecode

{-# INLINE zzDecode16 #-}
zzDecode16 :: Word16 -> Int16
zzDecode16 = zzDecode

{-# INLINE zzDecode32 #-}
zzDecode32 :: Word32 -> Int32
zzDecode32 = zzDecode

{-# INLINE zzDecode64 #-}
zzDecode64 :: Word64 -> Int64
zzDecode64 = zzDecode

{-# INLINE zzDecodeInteger #-}
zzDecodeInteger :: Integer -> Integer
zzDecodeInteger = zzDecode