module Data.Word64 where

import Data.Bits
import Data.Word
import Unsafe.Coerce

{- |
 @encode64@ / @decode64@

 +-------------------+-------------+-----------+
 | @Int@             | - 4 * 10^18 | 4 * 10^18 |
 +-------------------+-------------+-----------+
 | @(Int, Int)@      |      -10^9  |     10^9  |
 +-------------------+-------------+-----------+
 | @(Int, Int, Int)@ |      -10^6  |     10^6  |
 +-------------------+-------------+-----------+

 @encodeNonNegative64@ / @decodeNonNegative64@

 +-------------------+---+-----------+
 | @Int@             | 0 | 9 * 10^18 |
 +-------------------+---+-----------+
 | @(Int, Int)@      | 0 | 2 * 10^9  |
 +-------------------+---+-----------+
 | @(Int, Int, Int)@ | 0 | 2 * 10^6  |
 +-------------------+---+-----------+
-}
class Word64Encode a where
  encode64 :: a -> Word64
  decode64 :: Word64 -> a

  -- | for non-negative
  encodeNonNegative64 :: a -> Word64
  encodeNonNegative64 = a -> Word64
forall a. Word64Encode a => a -> Word64
encode64

  -- | for non-negative
  decodeNonNegative64 :: Word64 -> a
  decodeNonNegative64 = Word64 -> a
forall a. Word64Encode a => Word64 -> a
decode64

instance Word64Encode Int where
  encode64 :: Int -> Word64
encode64 Int
x = Int -> Word64
forall a b. a -> b
unsafeCoerce (Int -> Word64) -> Int -> Word64
forall a b. (a -> b) -> a -> b
$ Int
x Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
0x3fffffffffffffff
  decode64 :: Word64 -> Int
decode64 Word64
x = Word64 -> Int
forall a b. a -> b
unsafeCoerce Word64
x Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
0x3fffffffffffffff
  encodeNonNegative64 :: Int -> Word64
encodeNonNegative64 = Int -> Word64
forall a b. a -> b
unsafeCoerce
  decodeNonNegative64 :: Word64 -> Int
decodeNonNegative64 = Word64 -> Int
forall a b. a -> b
unsafeCoerce

instance Word64Encode (Int, Int) where
  encode64 :: (Int, Int) -> Word64
encode64 (Int
x, Int
y) =
    Int -> Word64
forall a b. a -> b
unsafeCoerce (Int -> Word64) -> Int -> Word64
forall a b. (a -> b) -> a -> b
$
      Int -> Int -> Int
forall a. Bits a => a -> Int -> a
unsafeShiftL (Int
x Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
0x3fffffff) Int
31 Int -> Int -> Int
forall a. Bits a => a -> a -> a
.|. (Int
y Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
0x3fffffff)
  decode64 :: Word64 -> (Int, Int)
decode64 Word64
xy = (Word64, Word64) -> (Int, Int)
forall a b. a -> b
unsafeCoerce (Word64
x, Word64
y)
    where
      !x :: Word64
x = Word64 -> Int -> Word64
forall a. Bits a => a -> Int -> a
unsafeShiftR Word64
xy Int
31 Word64 -> Word64 -> Word64
forall a. Num a => a -> a -> a
- Word64
0x3fffffff
      !y :: Word64
y = (Word64
xy Word64 -> Word64 -> Word64
forall a. Bits a => a -> a -> a
.&. Word64
0x7fffffff) Word64 -> Word64 -> Word64
forall a. Num a => a -> a -> a
- Word64
0x3fffffff
  encodeNonNegative64 :: (Int, Int) -> Word64
encodeNonNegative64 (Int
x, Int
y) = Int -> Word64
forall a b. a -> b
unsafeCoerce (Int -> Word64) -> Int -> Word64
forall a b. (a -> b) -> a -> b
$ Int -> Int -> Int
forall a. Bits a => a -> Int -> a
unsafeShiftL Int
x Int
31 Int -> Int -> Int
forall a. Bits a => a -> a -> a
.|. Int
y
  decodeNonNegative64 :: Word64 -> (Int, Int)
decodeNonNegative64 Word64
xy = (Word64, Word64) -> (Int, Int)
forall a b. a -> b
unsafeCoerce (Word64
x, Word64
y)
    where
      !x :: Word64
x = Word64 -> Int -> Word64
forall a. Bits a => a -> Int -> a
unsafeShiftR Word64
xy Int
31
      !y :: Word64
y = Word64
xy Word64 -> Word64 -> Word64
forall a. Bits a => a -> a -> a
.&. Word64
0x7fffffff

instance Word64Encode (Int, Int, Int) where
  encode64 :: (Int, Int, Int) -> Word64
encode64 (Int
x, Int
y, Int
z) =
    Int -> Word64
forall a b. a -> b
unsafeCoerce (Int -> Word64) -> Int -> Word64
forall a b. (a -> b) -> a -> b
$
      Int -> Int -> Int
forall a. Bits a => a -> Int -> a
unsafeShiftL (Int -> Int -> Int
forall a. Bits a => a -> Int -> a
unsafeShiftL (Int
x Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
0xfffff) Int
21 Int -> Int -> Int
forall a. Bits a => a -> a -> a
.|. (Int
y Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
0xfffff)) Int
21 Int -> Int -> Int
forall a. Bits a => a -> a -> a
.|. (Int
z Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
0xfffff)
  decode64 :: Word64 -> (Int, Int, Int)
decode64 Word64
xyz = (Word64, Word64, Word64) -> (Int, Int, Int)
forall a b. a -> b
unsafeCoerce (Word64
x, Word64
y, Word64
z)
    where
      !x :: Word64
x = Word64 -> Int -> Word64
forall a. Bits a => a -> Int -> a
unsafeShiftR Word64
xyz Int
42 Word64 -> Word64 -> Word64
forall a. Num a => a -> a -> a
- Word64
0xfffff
      !y :: Word64
y = (Word64 -> Int -> Word64
forall a. Bits a => a -> Int -> a
unsafeShiftR Word64
xyz Int
21 Word64 -> Word64 -> Word64
forall a. Bits a => a -> a -> a
.&. Word64
0x1fffff) Word64 -> Word64 -> Word64
forall a. Num a => a -> a -> a
- Word64
0xfffff
      !z :: Word64
z = Word64
xyz Word64 -> Word64 -> Word64
forall a. Bits a => a -> a -> a
.&. Word64
0x1fffff Word64 -> Word64 -> Word64
forall a. Num a => a -> a -> a
- Word64
0xfffff
  encodeNonNegative64 :: (Int, Int, Int) -> Word64
encodeNonNegative64 (Int
x, Int
y, Int
z) =
    Int -> Word64
forall a b. a -> b
unsafeCoerce (Int -> Word64) -> Int -> Word64
forall a b. (a -> b) -> a -> b
$
      Int -> Int -> Int
forall a. Bits a => a -> Int -> a
unsafeShiftL (Int -> Int -> Int
forall a. Bits a => a -> Int -> a
unsafeShiftL Int
x Int
21 Int -> Int -> Int
forall a. Bits a => a -> a -> a
.|. Int
y) Int
21 Int -> Int -> Int
forall a. Bits a => a -> a -> a
.|. Int
z
  decodeNonNegative64 :: Word64 -> (Int, Int, Int)
decodeNonNegative64 Word64
xyz = (Word64, Word64, Word64) -> (Int, Int, Int)
forall a b. a -> b
unsafeCoerce (Word64
x, Word64
y, Word64
z)
    where
      !x :: Word64
x = Word64 -> Int -> Word64
forall a. Bits a => a -> Int -> a
unsafeShiftR Word64
xyz Int
42
      !y :: Word64
y = Word64 -> Int -> Word64
forall a. Bits a => a -> Int -> a
unsafeShiftR Word64
xyz Int
21 Word64 -> Word64 -> Word64
forall a. Bits a => a -> a -> a
.&. Word64
0x1fffff
      !z :: Word64
z = Word64
xyz Word64 -> Word64 -> Word64
forall a. Bits a => a -> a -> a
.&. Word64
0x1fffff