{-# LANGUAGE AllowAmbiguousTypes #-}

-- | Provides typeclasses for convert between byte sizes.
--
-- @since 0.1
module Data.Bytes.Class.Conversion
  ( Conversion (..),
    convert',
    convertWitness,
  )
where

import Data.Bytes.Size
  ( SSize (SB, SE, SG, SK, SM, SP, ST, SY, SZ),
    SingSize (singSize),
    Size (B, E, G, K, M, P, T, Y, Z),
  )
import Data.Proxy (Proxy)
import Numeric.Algebra (MGroup ((.%.)), MSemigroup ((.*.)))
import Numeric.Literal.Integer (FromInteger (afromInteger))

-- $setup
-- >>> import Data.Bytes.Internal (Bytes (..))
-- >>> import Data.Bytes.Size (Size (..), Sized (..))
-- >>> import Data.Proxy (Proxy (Proxy))

-- | This class allows one to transform a bytes type to any 'Size'. For types
-- with existentially quantified 'Size' (e.g. 'Data.Bytes.SomeSize',
-- 'Data.Bytes.Network.NetBytes.SomeNetSize'), this will "undo" the existential quantification.
--
-- @since 0.1
class Conversion a where
  -- | @since 0.1
  type Converted (t :: Size) a = r | r -> t

  -- | @convert (Proxy :: Proxy t) x@ converts @x@ to size @t@.
  --
  -- ==== __Examples__
  --
  -- >>> let bytes = MkBytes 50_000 :: Bytes 'M Int
  -- >>> let gBytes = convert @_ @G Proxy bytes
  -- >>> :type gBytes
  -- gBytes :: Bytes G Int
  -- >>> gBytes
  -- MkBytes 50
  --
  -- >>> let bytes = hideSize (MkBytes 0.2 :: Bytes 'T Float)
  -- >>> let mBytes = convert @_ @M Proxy bytes
  -- >>> :type mBytes
  -- mBytes :: Bytes M Float
  -- >>> mBytes
  -- MkBytes 200000.0
  --
  -- @since 0.1
  convert :: (SingSize t) => Proxy t -> a -> Converted t a

-- | Low level function for converting a numeric literal /from/ the inferred
-- 'SingSize' /to/ the parameter 'Size'. For instance,
--
-- >>> convertWitness @K M 5_000
-- 5
--
-- This is slightly more principled than 'convert', but the higher level
-- byte types and functions should still be preferred
-- (e.g. 'Data.Bytes', 'Data.Bytes.Class.Normalize').
--
-- @since 0.1
convertWitness ::
  forall s n.
  ( FromInteger n,
    MGroup n,
    SingSize s
  ) =>
  Size ->
  n ->
  n
convertWitness :: forall (s :: Size) n.
(FromInteger n, MGroup n, SingSize s) =>
Size -> n -> n
convertWitness Size
toUnits n
n = case forall (s :: Size). SingSize s => SSize s
singSize @s of
  SSize s
SB -> Size -> Size -> n -> n
forall n. (FromInteger n, MGroup n) => Size -> Size -> n -> n
convert' Size
B Size
toUnits n
n
  SSize s
SK -> Size -> Size -> n -> n
forall n. (FromInteger n, MGroup n) => Size -> Size -> n -> n
convert' Size
K Size
toUnits n
n
  SSize s
SM -> Size -> Size -> n -> n
forall n. (FromInteger n, MGroup n) => Size -> Size -> n -> n
convert' Size
M Size
toUnits n
n
  SSize s
SG -> Size -> Size -> n -> n
forall n. (FromInteger n, MGroup n) => Size -> Size -> n -> n
convert' Size
G Size
toUnits n
n
  SSize s
ST -> Size -> Size -> n -> n
forall n. (FromInteger n, MGroup n) => Size -> Size -> n -> n
convert' Size
T Size
toUnits n
n
  SSize s
SP -> Size -> Size -> n -> n
forall n. (FromInteger n, MGroup n) => Size -> Size -> n -> n
convert' Size
P Size
toUnits n
n
  SSize s
SE -> Size -> Size -> n -> n
forall n. (FromInteger n, MGroup n) => Size -> Size -> n -> n
convert' Size
E Size
toUnits n
n
  SSize s
SZ -> Size -> Size -> n -> n
forall n. (FromInteger n, MGroup n) => Size -> Size -> n -> n
convert' Size
Z Size
toUnits n
n
  SSize s
SY -> Size -> Size -> n -> n
forall n. (FromInteger n, MGroup n) => Size -> Size -> n -> n
convert' Size
Y Size
toUnits n
n
{-# INLINEABLE convertWitness #-}

-- | Low level function for converting a numeric literal between
-- byte sizes. @convert b1 b2@ converts /from/ @b1@ /to/ @b2@,
-- e.g. @convert G K = \\n -> n * 1_000_000@. The higher level
-- byte types and functions should be preferred
-- (e.g. 'Conversion', "Data.Bytes.Class.Normalize"), but this is
-- here when it is needed.
--
-- @since 0.1
convert' ::
  forall n.
  ( FromInteger n,
    MGroup n
  ) =>
  Size ->
  Size ->
  n ->
  n
convert' :: forall n. (FromInteger n, MGroup n) => Size -> Size -> n -> n
convert' Size
B Size
B n
n = n
n
convert' Size
B Size
K n
n = n
n n -> n -> n
forall g. MGroup g => g -> g -> g
.%. Integer -> n
forall a. (FromInteger a, HasCallStack) => Integer -> a
afromInteger Integer
1_000
convert' Size
B Size
M n
n = n
n n -> n -> n
forall g. MGroup g => g -> g -> g
.%. Integer -> n
forall a. (FromInteger a, HasCallStack) => Integer -> a
afromInteger Integer
1_000_000
convert' Size
B Size
G n
n = n
n n -> n -> n
forall g. MGroup g => g -> g -> g
.%. Integer -> n
forall a. (FromInteger a, HasCallStack) => Integer -> a
afromInteger Integer
1_000_000_000
convert' Size
B Size
T n
n = n
n n -> n -> n
forall g. MGroup g => g -> g -> g
.%. Integer -> n
forall a. (FromInteger a, HasCallStack) => Integer -> a
afromInteger Integer
1_000_000_000_000
convert' Size
B Size
P n
n = n
n n -> n -> n
forall g. MGroup g => g -> g -> g
.%. Integer -> n
forall a. (FromInteger a, HasCallStack) => Integer -> a
afromInteger Integer
1_000_000_000_000_000
convert' Size
B Size
E n
n = n
n n -> n -> n
forall g. MGroup g => g -> g -> g
.%. Integer -> n
forall a. (FromInteger a, HasCallStack) => Integer -> a
afromInteger Integer
1_000_000_000_000_000_000
convert' Size
B Size
Z n
n = n
n n -> n -> n
forall g. MGroup g => g -> g -> g
.%. Integer -> n
forall a. (FromInteger a, HasCallStack) => Integer -> a
afromInteger Integer
1_000_000_000_000_000_000_000
convert' Size
B Size
Y n
n = n
n n -> n -> n
forall g. MGroup g => g -> g -> g
.%. Integer -> n
forall a. (FromInteger a, HasCallStack) => Integer -> a
afromInteger Integer
1_000_000_000_000_000_000_000_000
convert' Size
K Size
B n
n = n
n n -> n -> n
forall s. MSemigroup s => s -> s -> s
.*. Integer -> n
forall a. (FromInteger a, HasCallStack) => Integer -> a
afromInteger Integer
1_000
convert' Size
K Size
K n
n = n
n
convert' Size
K Size
M n
n = n
n n -> n -> n
forall g. MGroup g => g -> g -> g
.%. Integer -> n
forall a. (FromInteger a, HasCallStack) => Integer -> a
afromInteger Integer
1_000
convert' Size
K Size
G n
n = n
n n -> n -> n
forall g. MGroup g => g -> g -> g
.%. Integer -> n
forall a. (FromInteger a, HasCallStack) => Integer -> a
afromInteger Integer
1_000_000
convert' Size
K Size
T n
n = n
n n -> n -> n
forall g. MGroup g => g -> g -> g
.%. Integer -> n
forall a. (FromInteger a, HasCallStack) => Integer -> a
afromInteger Integer
1_000_000_000
convert' Size
K Size
P n
n = n
n n -> n -> n
forall g. MGroup g => g -> g -> g
.%. Integer -> n
forall a. (FromInteger a, HasCallStack) => Integer -> a
afromInteger Integer
1_000_000_000_000
convert' Size
K Size
E n
n = n
n n -> n -> n
forall g. MGroup g => g -> g -> g
.%. Integer -> n
forall a. (FromInteger a, HasCallStack) => Integer -> a
afromInteger Integer
1_000_000_000_000_000
convert' Size
K Size
Z n
n = n
n n -> n -> n
forall g. MGroup g => g -> g -> g
.%. Integer -> n
forall a. (FromInteger a, HasCallStack) => Integer -> a
afromInteger Integer
1_000_000_000_000_000_000
convert' Size
K Size
Y n
n = n
n n -> n -> n
forall g. MGroup g => g -> g -> g
.%. Integer -> n
forall a. (FromInteger a, HasCallStack) => Integer -> a
afromInteger Integer
1_000_000_000_000_000_000_000
convert' Size
M Size
B n
n = n
n n -> n -> n
forall s. MSemigroup s => s -> s -> s
.*. Integer -> n
forall a. (FromInteger a, HasCallStack) => Integer -> a
afromInteger Integer
1_000_000
convert' Size
M Size
K n
n = n
n n -> n -> n
forall s. MSemigroup s => s -> s -> s
.*. Integer -> n
forall a. (FromInteger a, HasCallStack) => Integer -> a
afromInteger Integer
1_000
convert' Size
M Size
M n
n = n
n
convert' Size
M Size
G n
n = n
n n -> n -> n
forall g. MGroup g => g -> g -> g
.%. Integer -> n
forall a. (FromInteger a, HasCallStack) => Integer -> a
afromInteger Integer
1_000
convert' Size
M Size
T n
n = n
n n -> n -> n
forall g. MGroup g => g -> g -> g
.%. Integer -> n
forall a. (FromInteger a, HasCallStack) => Integer -> a
afromInteger Integer
1_000_000
convert' Size
M Size
P n
n = n
n n -> n -> n
forall g. MGroup g => g -> g -> g
.%. Integer -> n
forall a. (FromInteger a, HasCallStack) => Integer -> a
afromInteger Integer
1_000_000_000
convert' Size
M Size
E n
n = n
n n -> n -> n
forall g. MGroup g => g -> g -> g
.%. Integer -> n
forall a. (FromInteger a, HasCallStack) => Integer -> a
afromInteger Integer
1_000_000_000_000
convert' Size
M Size
Z n
n = n
n n -> n -> n
forall g. MGroup g => g -> g -> g
.%. Integer -> n
forall a. (FromInteger a, HasCallStack) => Integer -> a
afromInteger Integer
1_000_000_000_000_000
convert' Size
M Size
Y n
n = n
n n -> n -> n
forall g. MGroup g => g -> g -> g
.%. Integer -> n
forall a. (FromInteger a, HasCallStack) => Integer -> a
afromInteger Integer
1_000_000_000_000_000_000
convert' Size
G Size
B n
n = n
n n -> n -> n
forall s. MSemigroup s => s -> s -> s
.*. Integer -> n
forall a. (FromInteger a, HasCallStack) => Integer -> a
afromInteger Integer
1_000_000_000
convert' Size
G Size
K n
n = n
n n -> n -> n
forall s. MSemigroup s => s -> s -> s
.*. Integer -> n
forall a. (FromInteger a, HasCallStack) => Integer -> a
afromInteger Integer
1_000_000
convert' Size
G Size
M n
n = n
n n -> n -> n
forall s. MSemigroup s => s -> s -> s
.*. Integer -> n
forall a. (FromInteger a, HasCallStack) => Integer -> a
afromInteger Integer
1_000
convert' Size
G Size
G n
n = n
n
convert' Size
G Size
T n
n = n
n n -> n -> n
forall g. MGroup g => g -> g -> g
.%. Integer -> n
forall a. (FromInteger a, HasCallStack) => Integer -> a
afromInteger Integer
1_000
convert' Size
G Size
P n
n = n
n n -> n -> n
forall g. MGroup g => g -> g -> g
.%. Integer -> n
forall a. (FromInteger a, HasCallStack) => Integer -> a
afromInteger Integer
1_000_000
convert' Size
G Size
E n
n = n
n n -> n -> n
forall g. MGroup g => g -> g -> g
.%. Integer -> n
forall a. (FromInteger a, HasCallStack) => Integer -> a
afromInteger Integer
1_000_000_000
convert' Size
G Size
Z n
n = n
n n -> n -> n
forall g. MGroup g => g -> g -> g
.%. Integer -> n
forall a. (FromInteger a, HasCallStack) => Integer -> a
afromInteger Integer
1_000_000_000_000
convert' Size
G Size
Y n
n = n
n n -> n -> n
forall g. MGroup g => g -> g -> g
.%. Integer -> n
forall a. (FromInteger a, HasCallStack) => Integer -> a
afromInteger Integer
1_000_000_000_000_000
convert' Size
T Size
B n
n = n
n n -> n -> n
forall s. MSemigroup s => s -> s -> s
.*. Integer -> n
forall a. (FromInteger a, HasCallStack) => Integer -> a
afromInteger Integer
1_000_000_000_000
convert' Size
T Size
K n
n = n
n n -> n -> n
forall s. MSemigroup s => s -> s -> s
.*. Integer -> n
forall a. (FromInteger a, HasCallStack) => Integer -> a
afromInteger Integer
1_000_000_000
convert' Size
T Size
M n
n = n
n n -> n -> n
forall s. MSemigroup s => s -> s -> s
.*. Integer -> n
forall a. (FromInteger a, HasCallStack) => Integer -> a
afromInteger Integer
1_000_000
convert' Size
T Size
G n
n = n
n n -> n -> n
forall s. MSemigroup s => s -> s -> s
.*. Integer -> n
forall a. (FromInteger a, HasCallStack) => Integer -> a
afromInteger Integer
1_000
convert' Size
T Size
T n
n = n
n
convert' Size
T Size
P n
n = n
n n -> n -> n
forall g. MGroup g => g -> g -> g
.%. Integer -> n
forall a. (FromInteger a, HasCallStack) => Integer -> a
afromInteger Integer
1_000
convert' Size
T Size
E n
n = n
n n -> n -> n
forall g. MGroup g => g -> g -> g
.%. Integer -> n
forall a. (FromInteger a, HasCallStack) => Integer -> a
afromInteger Integer
1_000_000
convert' Size
T Size
Z n
n = n
n n -> n -> n
forall g. MGroup g => g -> g -> g
.%. Integer -> n
forall a. (FromInteger a, HasCallStack) => Integer -> a
afromInteger Integer
1_000_000_000
convert' Size
T Size
Y n
n = n
n n -> n -> n
forall g. MGroup g => g -> g -> g
.%. Integer -> n
forall a. (FromInteger a, HasCallStack) => Integer -> a
afromInteger Integer
1_000_000_000_000
convert' Size
P Size
B n
n = n
n n -> n -> n
forall s. MSemigroup s => s -> s -> s
.*. Integer -> n
forall a. (FromInteger a, HasCallStack) => Integer -> a
afromInteger Integer
1_000_000_000_000_000
convert' Size
P Size
K n
n = n
n n -> n -> n
forall s. MSemigroup s => s -> s -> s
.*. Integer -> n
forall a. (FromInteger a, HasCallStack) => Integer -> a
afromInteger Integer
1_000_000_000_000
convert' Size
P Size
M n
n = n
n n -> n -> n
forall s. MSemigroup s => s -> s -> s
.*. Integer -> n
forall a. (FromInteger a, HasCallStack) => Integer -> a
afromInteger Integer
1_000_000_000
convert' Size
P Size
G n
n = n
n n -> n -> n
forall s. MSemigroup s => s -> s -> s
.*. Integer -> n
forall a. (FromInteger a, HasCallStack) => Integer -> a
afromInteger Integer
1_000_000
convert' Size
P Size
T n
n = n
n n -> n -> n
forall s. MSemigroup s => s -> s -> s
.*. Integer -> n
forall a. (FromInteger a, HasCallStack) => Integer -> a
afromInteger Integer
1_000
convert' Size
P Size
P n
n = n
n
convert' Size
P Size
E n
n = n
n n -> n -> n
forall g. MGroup g => g -> g -> g
.%. Integer -> n
forall a. (FromInteger a, HasCallStack) => Integer -> a
afromInteger Integer
1_000
convert' Size
P Size
Z n
n = n
n n -> n -> n
forall g. MGroup g => g -> g -> g
.%. Integer -> n
forall a. (FromInteger a, HasCallStack) => Integer -> a
afromInteger Integer
1_000_000
convert' Size
P Size
Y n
n = n
n n -> n -> n
forall g. MGroup g => g -> g -> g
.%. Integer -> n
forall a. (FromInteger a, HasCallStack) => Integer -> a
afromInteger Integer
1_000_000_000
convert' Size
E Size
B n
n = n
n n -> n -> n
forall s. MSemigroup s => s -> s -> s
.*. Integer -> n
forall a. (FromInteger a, HasCallStack) => Integer -> a
afromInteger Integer
1_000_000_000_000_000_000
convert' Size
E Size
K n
n = n
n n -> n -> n
forall s. MSemigroup s => s -> s -> s
.*. Integer -> n
forall a. (FromInteger a, HasCallStack) => Integer -> a
afromInteger Integer
1_000_000_000_000_000
convert' Size
E Size
M n
n = n
n n -> n -> n
forall s. MSemigroup s => s -> s -> s
.*. Integer -> n
forall a. (FromInteger a, HasCallStack) => Integer -> a
afromInteger Integer
1_000_000_000_000
convert' Size
E Size
G n
n = n
n n -> n -> n
forall s. MSemigroup s => s -> s -> s
.*. Integer -> n
forall a. (FromInteger a, HasCallStack) => Integer -> a
afromInteger Integer
1_000_000_000
convert' Size
E Size
T n
n = n
n n -> n -> n
forall s. MSemigroup s => s -> s -> s
.*. Integer -> n
forall a. (FromInteger a, HasCallStack) => Integer -> a
afromInteger Integer
1_000_000
convert' Size
E Size
P n
n = n
n n -> n -> n
forall s. MSemigroup s => s -> s -> s
.*. Integer -> n
forall a. (FromInteger a, HasCallStack) => Integer -> a
afromInteger Integer
1_000
convert' Size
E Size
E n
n = n
n
convert' Size
E Size
Z n
n = n
n n -> n -> n
forall g. MGroup g => g -> g -> g
.%. Integer -> n
forall a. (FromInteger a, HasCallStack) => Integer -> a
afromInteger Integer
1_000
convert' Size
E Size
Y n
n = n
n n -> n -> n
forall g. MGroup g => g -> g -> g
.%. Integer -> n
forall a. (FromInteger a, HasCallStack) => Integer -> a
afromInteger Integer
1_000_000
convert' Size
Z Size
B n
n = n
n n -> n -> n
forall s. MSemigroup s => s -> s -> s
.*. Integer -> n
forall a. (FromInteger a, HasCallStack) => Integer -> a
afromInteger Integer
1_000_000_000_000_000_000_000
convert' Size
Z Size
K n
n = n
n n -> n -> n
forall s. MSemigroup s => s -> s -> s
.*. Integer -> n
forall a. (FromInteger a, HasCallStack) => Integer -> a
afromInteger Integer
1_000_000_000_000_000_000
convert' Size
Z Size
M n
n = n
n n -> n -> n
forall s. MSemigroup s => s -> s -> s
.*. Integer -> n
forall a. (FromInteger a, HasCallStack) => Integer -> a
afromInteger Integer
1_000_000_000_000_000
convert' Size
Z Size
G n
n = n
n n -> n -> n
forall s. MSemigroup s => s -> s -> s
.*. Integer -> n
forall a. (FromInteger a, HasCallStack) => Integer -> a
afromInteger Integer
1_000_000_000_000
convert' Size
Z Size
T n
n = n
n n -> n -> n
forall s. MSemigroup s => s -> s -> s
.*. Integer -> n
forall a. (FromInteger a, HasCallStack) => Integer -> a
afromInteger Integer
1_000_000_000
convert' Size
Z Size
P n
n = n
n n -> n -> n
forall s. MSemigroup s => s -> s -> s
.*. Integer -> n
forall a. (FromInteger a, HasCallStack) => Integer -> a
afromInteger Integer
1_000_000
convert' Size
Z Size
E n
n = n
n n -> n -> n
forall s. MSemigroup s => s -> s -> s
.*. Integer -> n
forall a. (FromInteger a, HasCallStack) => Integer -> a
afromInteger Integer
1_000
convert' Size
Z Size
Z n
n = n
n
convert' Size
Z Size
Y n
n = n
n n -> n -> n
forall g. MGroup g => g -> g -> g
.%. Integer -> n
forall a. (FromInteger a, HasCallStack) => Integer -> a
afromInteger Integer
1_000
convert' Size
Y Size
B n
n = n
n n -> n -> n
forall s. MSemigroup s => s -> s -> s
.*. Integer -> n
forall a. (FromInteger a, HasCallStack) => Integer -> a
afromInteger Integer
1_000_000_000_000_000_000_000_000
convert' Size
Y Size
K n
n = n
n n -> n -> n
forall s. MSemigroup s => s -> s -> s
.*. Integer -> n
forall a. (FromInteger a, HasCallStack) => Integer -> a
afromInteger Integer
1_000_000_000_000_000_000_000
convert' Size
Y Size
M n
n = n
n n -> n -> n
forall s. MSemigroup s => s -> s -> s
.*. Integer -> n
forall a. (FromInteger a, HasCallStack) => Integer -> a
afromInteger Integer
1_000_000_000_000_000_000
convert' Size
Y Size
G n
n = n
n n -> n -> n
forall s. MSemigroup s => s -> s -> s
.*. Integer -> n
forall a. (FromInteger a, HasCallStack) => Integer -> a
afromInteger Integer
1_000_000_000_000_000
convert' Size
Y Size
T n
n = n
n n -> n -> n
forall s. MSemigroup s => s -> s -> s
.*. Integer -> n
forall a. (FromInteger a, HasCallStack) => Integer -> a
afromInteger Integer
1_000_000_000_000
convert' Size
Y Size
P n
n = n
n n -> n -> n
forall s. MSemigroup s => s -> s -> s
.*. Integer -> n
forall a. (FromInteger a, HasCallStack) => Integer -> a
afromInteger Integer
1_000_000_000
convert' Size
Y Size
E n
n = n
n n -> n -> n
forall s. MSemigroup s => s -> s -> s
.*. Integer -> n
forall a. (FromInteger a, HasCallStack) => Integer -> a
afromInteger Integer
1_000_000
convert' Size
Y Size
Z n
n = n
n n -> n -> n
forall s. MSemigroup s => s -> s -> s
.*. Integer -> n
forall a. (FromInteger a, HasCallStack) => Integer -> a
afromInteger Integer
1_000
convert' Size
Y Size
Y n
n = n
n
{-# INLINEABLE convert' #-}