{-
-- EPITECH PROJECT, 2025
-- Glados
-- File description:
-- Integer
-}

module Common.Type.Integer
    ( IntValue(..)
    , intValueToInt
    , fitInteger
    , toInt64
    , fromInt64
    ) where

import Data.Int (Int8, Int16, Int32, Int64)
import Data.Word (Word8, Word16, Word32, Word64)

data IntValue
    = I8 Int8
    | UI8 Word8
    | I16 Int16
    | UI16 Word16
    | I32 Int32
    | UI32 Word32
    | I64 Int64
    | UI64 Word64
    | IChar Int8
    | UIChar Word8
    deriving (Int -> IntValue -> ShowS
[IntValue] -> ShowS
IntValue -> String
(Int -> IntValue -> ShowS)
-> (IntValue -> String) -> ([IntValue] -> ShowS) -> Show IntValue
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: Int -> IntValue -> ShowS
showsPrec :: Int -> IntValue -> ShowS
$cshow :: IntValue -> String
show :: IntValue -> String
$cshowList :: [IntValue] -> ShowS
showList :: [IntValue] -> ShowS
Show, IntValue -> IntValue -> Bool
(IntValue -> IntValue -> Bool)
-> (IntValue -> IntValue -> Bool) -> Eq IntValue
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
$c== :: IntValue -> IntValue -> Bool
== :: IntValue -> IntValue -> Bool
$c/= :: IntValue -> IntValue -> Bool
/= :: IntValue -> IntValue -> Bool
Eq, Eq IntValue
Eq IntValue =>
(IntValue -> IntValue -> Ordering)
-> (IntValue -> IntValue -> Bool)
-> (IntValue -> IntValue -> Bool)
-> (IntValue -> IntValue -> Bool)
-> (IntValue -> IntValue -> Bool)
-> (IntValue -> IntValue -> IntValue)
-> (IntValue -> IntValue -> IntValue)
-> Ord IntValue
IntValue -> IntValue -> Bool
IntValue -> IntValue -> Ordering
IntValue -> IntValue -> IntValue
forall a.
Eq a =>
(a -> a -> Ordering)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> a)
-> (a -> a -> a)
-> Ord a
$ccompare :: IntValue -> IntValue -> Ordering
compare :: IntValue -> IntValue -> Ordering
$c< :: IntValue -> IntValue -> Bool
< :: IntValue -> IntValue -> Bool
$c<= :: IntValue -> IntValue -> Bool
<= :: IntValue -> IntValue -> Bool
$c> :: IntValue -> IntValue -> Bool
> :: IntValue -> IntValue -> Bool
$c>= :: IntValue -> IntValue -> Bool
>= :: IntValue -> IntValue -> Bool
$cmax :: IntValue -> IntValue -> IntValue
max :: IntValue -> IntValue -> IntValue
$cmin :: IntValue -> IntValue -> IntValue
min :: IntValue -> IntValue -> IntValue
Ord)

-- | Converts an IntValue to a standard Haskell Int.
--
-- @args
--   - v: The IntValue to convert.
--
-- @details
--   This function handles all IntValue constructors, converting them
--   to the system's native 'Int' type. Note that converting large
--   64-bit values on a 32-bit system might cause overflow, though
--   this is rare in modern environments.
--
-- @return
--   The integer value as an 'Int'.
intValueToInt :: IntValue -> Int
intValueToInt :: IntValue -> Int
intValueToInt (I8 Int8
i) = Int8 -> Int
forall a b. (Integral a, Num b) => a -> b
fromIntegral Int8
i
intValueToInt (UI8 Word8
i) = Word8 -> Int
forall a b. (Integral a, Num b) => a -> b
fromIntegral Word8
i
intValueToInt (I16 Int16
i) = Int16 -> Int
forall a b. (Integral a, Num b) => a -> b
fromIntegral Int16
i
intValueToInt (UI16 Word16
i) = Word16 -> Int
forall a b. (Integral a, Num b) => a -> b
fromIntegral Word16
i
intValueToInt (I32 Int32
i) = Int32 -> Int
forall a b. (Integral a, Num b) => a -> b
fromIntegral Int32
i
intValueToInt (UI32 Word32
i) = Word32 -> Int
forall a b. (Integral a, Num b) => a -> b
fromIntegral Word32
i
intValueToInt (I64 Int64
i) = Int64 -> Int
forall a b. (Integral a, Num b) => a -> b
fromIntegral Int64
i
intValueToInt (UI64 Word64
i) = Word64 -> Int
forall a b. (Integral a, Num b) => a -> b
fromIntegral Word64
i
intValueToInt (IChar Int8
i) = Int8 -> Int
forall a b. (Integral a, Num b) => a -> b
fromIntegral Int8
i
intValueToInt (UIChar Word8
i) = Word8 -> Int
forall a b. (Integral a, Num b) => a -> b
fromIntegral Word8
i

-- | Converts an IntValue to a standard Haskell Int64.
--
-- @args
--   - v: The IntValue to convert.
--
-- @details
--   Provides a safe conversion to 'Int64' for all underlying types.
--   Unsigned 64-bit values are treated as signed (wrapping) if they
--   exceed 'maxBound :: Int64'.
--
-- @return
--   The integer value as an 'Int64'.
toInt64 :: IntValue -> Int64
toInt64 :: IntValue -> Int64
toInt64 (I8 Int8
i) = Int8 -> Int64
forall a b. (Integral a, Num b) => a -> b
fromIntegral Int8
i
toInt64 (UI8 Word8
i) = Word8 -> Int64
forall a b. (Integral a, Num b) => a -> b
fromIntegral Word8
i
toInt64 (I16 Int16
i) = Int16 -> Int64
forall a b. (Integral a, Num b) => a -> b
fromIntegral Int16
i
toInt64 (UI16 Word16
i) = Word16 -> Int64
forall a b. (Integral a, Num b) => a -> b
fromIntegral Word16
i
toInt64 (I32 Int32
i) = Int32 -> Int64
forall a b. (Integral a, Num b) => a -> b
fromIntegral Int32
i
toInt64 (UI32 Word32
i) = Word32 -> Int64
forall a b. (Integral a, Num b) => a -> b
fromIntegral Word32
i
toInt64 (I64 Int64
i) = Int64
i
toInt64 (UI64 Word64
i) = Word64 -> Int64
forall a b. (Integral a, Num b) => a -> b
fromIntegral Word64
i
toInt64 (IChar Int8
i) = Int8 -> Int64
forall a b. (Integral a, Num b) => a -> b
fromIntegral Int8
i
toInt64 (UIChar Word8
i) = Word8 -> Int64
forall a b. (Integral a, Num b) => a -> b
fromIntegral Word8
i

-- | Converts an Int64 to the smallest suitable IntValue representation.
--
-- @args
--   - n: The Int64 value to compress.
--
-- @details
--   This function checks the bounds of the integer to determine the
--   most compact storage format (I8, UI8, I16, etc.). It prioritizes
--   signed representation for small positive numbers but will use
--   unsigned types if it saves space (e.g., 200 fits in UI8 but requires I16).
--
-- @return
--   An 'IntValue' wrapping the value in the smallest possible container.
fromInt64 :: Int64 -> IntValue
fromInt64 :: Int64 -> IntValue
fromInt64 Int64
n | Int64
n Int64 -> Int64 -> Bool
forall a. Ord a => a -> a -> Bool
>= Int8 -> Int64
forall a b. (Integral a, Num b) => a -> b
fromIntegral (Int8
forall a. Bounded a => a
minBound :: Int8) Bool -> Bool -> Bool
&&
        Int64
n Int64 -> Int64 -> Bool
forall a. Ord a => a -> a -> Bool
<= Int8 -> Int64
forall a b. (Integral a, Num b) => a -> b
fromIntegral (Int8
forall a. Bounded a => a
maxBound :: Int8) = Int8 -> IntValue
I8 (Int64 -> Int8
forall a b. (Integral a, Num b) => a -> b
fromIntegral Int64
n)
    | Int64
n Int64 -> Int64 -> Bool
forall a. Ord a => a -> a -> Bool
>= Int64
0 Bool -> Bool -> Bool
&& Int64
n Int64 -> Int64 -> Bool
forall a. Ord a => a -> a -> Bool
<= Word8 -> Int64
forall a b. (Integral a, Num b) => a -> b
fromIntegral (Word8
forall a. Bounded a => a
maxBound :: Word8) = Word8 -> IntValue
UI8 (Int64 -> Word8
forall a b. (Integral a, Num b) => a -> b
fromIntegral Int64
n)
    | Int64
n Int64 -> Int64 -> Bool
forall a. Ord a => a -> a -> Bool
>= Int16 -> Int64
forall a b. (Integral a, Num b) => a -> b
fromIntegral (Int16
forall a. Bounded a => a
minBound :: Int16) Bool -> Bool -> Bool
&&
        Int64
n Int64 -> Int64 -> Bool
forall a. Ord a => a -> a -> Bool
<= Int16 -> Int64
forall a b. (Integral a, Num b) => a -> b
fromIntegral (Int16
forall a. Bounded a => a
maxBound :: Int16) = Int16 -> IntValue
I16 (Int64 -> Int16
forall a b. (Integral a, Num b) => a -> b
fromIntegral Int64
n)
    | Int64
n Int64 -> Int64 -> Bool
forall a. Ord a => a -> a -> Bool
>= Int64
0 Bool -> Bool -> Bool
&& Int64
n Int64 -> Int64 -> Bool
forall a. Ord a => a -> a -> Bool
<= Word16 -> Int64
forall a b. (Integral a, Num b) => a -> b
fromIntegral (Word16
forall a. Bounded a => a
maxBound :: Word16) = Word16 -> IntValue
UI16 (Int64 -> Word16
forall a b. (Integral a, Num b) => a -> b
fromIntegral Int64
n)
    | Int64
n Int64 -> Int64 -> Bool
forall a. Ord a => a -> a -> Bool
>= Int32 -> Int64
forall a b. (Integral a, Num b) => a -> b
fromIntegral (Int32
forall a. Bounded a => a
minBound :: Int32) Bool -> Bool -> Bool
&&
        Int64
n Int64 -> Int64 -> Bool
forall a. Ord a => a -> a -> Bool
<= Int32 -> Int64
forall a b. (Integral a, Num b) => a -> b
fromIntegral (Int32
forall a. Bounded a => a
maxBound :: Int32) = Int32 -> IntValue
I32 (Int64 -> Int32
forall a b. (Integral a, Num b) => a -> b
fromIntegral Int64
n)
    | Int64
n Int64 -> Int64 -> Bool
forall a. Ord a => a -> a -> Bool
>= Int64
0 Bool -> Bool -> Bool
&& Int64
n Int64 -> Int64 -> Bool
forall a. Ord a => a -> a -> Bool
<= Word32 -> Int64
forall a b. (Integral a, Num b) => a -> b
fromIntegral (Word32
forall a. Bounded a => a
maxBound :: Word32) = Word32 -> IntValue
UI32 (Int64 -> Word32
forall a b. (Integral a, Num b) => a -> b
fromIntegral Int64
n)
    | Bool
otherwise = Int64 -> IntValue
I64 Int64
n

-- | Converts an arbitrary Integer to the smallest suitable IntValue.
--
-- @args
--   - n: The Integer to compress.
--
-- @details
--   Similar to 'fromInt64' but works on arbitrary precision 'Integer's.
--   It attempts to fit the value into I8/UI8, then I16/UI16, etc.
--   If the value exceeds the signed 64-bit range but fits in unsigned 64-bit,
--   it uses UI64.
--
-- @return
--   The optimized 'IntValue'.
fitInteger :: Integer -> IntValue
fitInteger :: Integer -> IntValue
fitInteger Integer
n | Integer
n Integer -> Integer -> Bool
forall a. Ord a => a -> a -> Bool
>= Int8 -> Integer
forall a b. (Integral a, Num b) => a -> b
fromIntegral (Int8
forall a. Bounded a => a
minBound :: Int8) Bool -> Bool -> Bool
&&
        Integer
n Integer -> Integer -> Bool
forall a. Ord a => a -> a -> Bool
<= Int8 -> Integer
forall a b. (Integral a, Num b) => a -> b
fromIntegral (Int8
forall a. Bounded a => a
maxBound :: Int8) = Int8 -> IntValue
I8 (Integer -> Int8
forall a. Num a => Integer -> a
fromInteger Integer
n)
    | Integer
n Integer -> Integer -> Bool
forall a. Ord a => a -> a -> Bool
>= Integer
0 Bool -> Bool -> Bool
&& Integer
n Integer -> Integer -> Bool
forall a. Ord a => a -> a -> Bool
<= Word8 -> Integer
forall a b. (Integral a, Num b) => a -> b
fromIntegral (Word8
forall a. Bounded a => a
maxBound :: Word8) = Word8 -> IntValue
UI8 (Integer -> Word8
forall a. Num a => Integer -> a
fromInteger Integer
n)
    | Integer
n Integer -> Integer -> Bool
forall a. Ord a => a -> a -> Bool
>= Int16 -> Integer
forall a b. (Integral a, Num b) => a -> b
fromIntegral (Int16
forall a. Bounded a => a
minBound :: Int16) Bool -> Bool -> Bool
&&
        Integer
n Integer -> Integer -> Bool
forall a. Ord a => a -> a -> Bool
<= Int16 -> Integer
forall a b. (Integral a, Num b) => a -> b
fromIntegral (Int16
forall a. Bounded a => a
maxBound :: Int16) = Int16 -> IntValue
I16 (Integer -> Int16
forall a. Num a => Integer -> a
fromInteger Integer
n)
    | Integer
n Integer -> Integer -> Bool
forall a. Ord a => a -> a -> Bool
>= Integer
0 Bool -> Bool -> Bool
&& Integer
n Integer -> Integer -> Bool
forall a. Ord a => a -> a -> Bool
<= Word16 -> Integer
forall a b. (Integral a, Num b) => a -> b
fromIntegral (Word16
forall a. Bounded a => a
maxBound :: Word16) = Word16 -> IntValue
UI16 (Integer -> Word16
forall a. Num a => Integer -> a
fromInteger Integer
n)
    | Integer
n Integer -> Integer -> Bool
forall a. Ord a => a -> a -> Bool
>= Int32 -> Integer
forall a b. (Integral a, Num b) => a -> b
fromIntegral (Int32
forall a. Bounded a => a
minBound :: Int32) Bool -> Bool -> Bool
&&
        Integer
n Integer -> Integer -> Bool
forall a. Ord a => a -> a -> Bool
<= Int32 -> Integer
forall a b. (Integral a, Num b) => a -> b
fromIntegral (Int32
forall a. Bounded a => a
maxBound :: Int32) = Int32 -> IntValue
I32 (Integer -> Int32
forall a. Num a => Integer -> a
fromInteger Integer
n)
    | Integer
n Integer -> Integer -> Bool
forall a. Ord a => a -> a -> Bool
>= Integer
0 Bool -> Bool -> Bool
&& Integer
n Integer -> Integer -> Bool
forall a. Ord a => a -> a -> Bool
<= Word32 -> Integer
forall a b. (Integral a, Num b) => a -> b
fromIntegral (Word32
forall a. Bounded a => a
maxBound :: Word32) = Word32 -> IntValue
UI32 (Integer -> Word32
forall a. Num a => Integer -> a
fromInteger Integer
n)
    | Bool
otherwise = Int64 -> IntValue
I64 (Integer -> Int64
forall a. Num a => Integer -> a
fromInteger Integer
n)