{- | Conversions between various types.

@since WIP
-}
module Plutarch.Convert (
  -- * Types
  PEndianness,

  -- * Functions
  pmostSignificantFirst,
  pmostSignificantLast,
  pbyteStringToInteger,
  pintegerToByteString,
  pintegerToByteStringSized,
) where

import GHC.Generics (Generic)
import Plutarch.Builtin.Bool (PBool (PFalse, PTrue))
import Plutarch.ByteString (PByteString)
import Plutarch.Integer (PInteger)
import Plutarch.Internal.Eq (PEq)
import Plutarch.Internal.Newtype (PlutusTypeNewtype)
import Plutarch.Internal.Ord (POrd, PPartialOrd)
import Plutarch.Internal.Other (pto)
import Plutarch.Internal.PLam (plam)
import Plutarch.Internal.PlutusType (
  DPTStrat,
  DerivePlutusType,
  PlutusType,
  pcon,
 )
import Plutarch.Internal.Term (S, Term, (#), (:-->))
import Plutarch.Positive (PPositive)
import Plutarch.Unsafe (punsafeBuiltin)
import PlutusCore qualified as PLC

{- | Type designating whether a conversion should be most-significant-first or
most-significant-last. See
[CIP-121](https://github.com/cardano-foundation/CIPs/tree/master/CIP-0121#representation)
for more details on this.

@since WIP
-}
newtype PEndianness (s :: S) = PEndianness (Term s PBool)
  deriving stock
    ( -- | @since WIP
      (forall x. PEndianness s -> Rep (PEndianness s) x)
-> (forall x. Rep (PEndianness s) x -> PEndianness s)
-> Generic (PEndianness s)
forall x. Rep (PEndianness s) x -> PEndianness s
forall x. PEndianness s -> Rep (PEndianness s) x
forall a.
(forall x. a -> Rep a x) -> (forall x. Rep a x -> a) -> Generic a
forall (s :: S) x. Rep (PEndianness s) x -> PEndianness s
forall (s :: S) x. PEndianness s -> Rep (PEndianness s) x
$cfrom :: forall (s :: S) x. PEndianness s -> Rep (PEndianness s) x
from :: forall x. PEndianness s -> Rep (PEndianness s) x
$cto :: forall (s :: S) x. Rep (PEndianness s) x -> PEndianness s
to :: forall x. Rep (PEndianness s) x -> PEndianness s
Generic
    )
  deriving anyclass
    ( -- | @since WIP
      (forall (s :: S). PEndianness s -> Term s (PInner PEndianness))
-> (forall (s :: S) (b :: PType).
    Term s (PInner PEndianness)
    -> (PEndianness s -> Term s b) -> Term s b)
-> PlutusType PEndianness
forall (s :: S). PEndianness s -> Term s (PInner PEndianness)
forall (s :: S) (b :: PType).
Term s (PInner PEndianness)
-> (PEndianness s -> Term s b) -> Term s b
forall (a :: PType).
(forall (s :: S). a s -> Term s (PInner a))
-> (forall (s :: S) (b :: PType).
    Term s (PInner a) -> (a s -> Term s b) -> Term s b)
-> PlutusType a
$cpcon' :: forall (s :: S). PEndianness s -> Term s (PInner PEndianness)
pcon' :: forall (s :: S). PEndianness s -> Term s (PInner PEndianness)
$cpmatch' :: forall (s :: S) (b :: PType).
Term s (PInner PEndianness)
-> (PEndianness s -> Term s b) -> Term s b
pmatch' :: forall (s :: S) (b :: PType).
Term s (PInner PEndianness)
-> (PEndianness s -> Term s b) -> Term s b
PlutusType
    , -- | @since WIP
      (forall (s :: S).
 Term s PEndianness -> Term s PEndianness -> Term s PBool)
-> PEq PEndianness
forall (s :: S).
Term s PEndianness -> Term s PEndianness -> Term s PBool
forall (t :: PType).
(forall (s :: S). Term s t -> Term s t -> Term s PBool) -> PEq t
$c#== :: forall (s :: S).
Term s PEndianness -> Term s PEndianness -> Term s PBool
#== :: forall (s :: S).
Term s PEndianness -> Term s PEndianness -> Term s PBool
PEq
    , -- | @since WIP
      PEq PEndianness
PEq PEndianness =>
(forall (s :: S).
 Term s PEndianness -> Term s PEndianness -> Term s PBool)
-> (forall (s :: S).
    Term s PEndianness -> Term s PEndianness -> Term s PBool)
-> (forall (s :: S).
    Term s PEndianness -> Term s PEndianness -> Term s PBool)
-> (forall (s :: S).
    Term s PEndianness -> Term s PEndianness -> Term s PBool)
-> PPartialOrd PEndianness
forall (s :: S).
Term s PEndianness -> Term s PEndianness -> Term s PBool
forall (t :: PType).
PEq t =>
(forall (s :: S). Term s t -> Term s t -> Term s PBool)
-> (forall (s :: S). Term s t -> Term s t -> Term s PBool)
-> (forall (s :: S). Term s t -> Term s t -> Term s PBool)
-> (forall (s :: S). Term s t -> Term s t -> Term s PBool)
-> PPartialOrd t
$c#<= :: forall (s :: S).
Term s PEndianness -> Term s PEndianness -> Term s PBool
#<= :: forall (s :: S).
Term s PEndianness -> Term s PEndianness -> Term s PBool
$c#< :: forall (s :: S).
Term s PEndianness -> Term s PEndianness -> Term s PBool
#< :: forall (s :: S).
Term s PEndianness -> Term s PEndianness -> Term s PBool
$c#>= :: forall (s :: S).
Term s PEndianness -> Term s PEndianness -> Term s PBool
#>= :: forall (s :: S).
Term s PEndianness -> Term s PEndianness -> Term s PBool
$c#> :: forall (s :: S).
Term s PEndianness -> Term s PEndianness -> Term s PBool
#> :: forall (s :: S).
Term s PEndianness -> Term s PEndianness -> Term s PBool
PPartialOrd
    , -- | @since WIP
      PPartialOrd PEndianness
PPartialOrd PEndianness =>
(forall (s :: S).
 Term s PEndianness -> Term s PEndianness -> Term s PEndianness)
-> (forall (s :: S).
    Term s PEndianness -> Term s PEndianness -> Term s PEndianness)
-> POrd PEndianness
forall (s :: S).
Term s PEndianness -> Term s PEndianness -> Term s PEndianness
forall (t :: PType).
PPartialOrd t =>
(forall (s :: S). Term s t -> Term s t -> Term s t)
-> (forall (s :: S). Term s t -> Term s t -> Term s t) -> POrd t
$cpmax :: forall (s :: S).
Term s PEndianness -> Term s PEndianness -> Term s PEndianness
pmax :: forall (s :: S).
Term s PEndianness -> Term s PEndianness -> Term s PEndianness
$cpmin :: forall (s :: S).
Term s PEndianness -> Term s PEndianness -> Term s PEndianness
pmin :: forall (s :: S).
Term s PEndianness -> Term s PEndianness -> Term s PEndianness
POrd
    )

-- | @since WIP
instance DerivePlutusType PEndianness where
  type DPTStrat _ = PlutusTypeNewtype

{- | Indicates the conversion should be most-significant-first.

@since WIP
-}
pmostSignificantFirst :: forall (s :: S). Term s PEndianness
pmostSignificantFirst :: forall (s :: S). Term s PEndianness
pmostSignificantFirst = PEndianness s -> Term s PEndianness
forall (a :: PType) (s :: S). PlutusType a => a s -> Term s a
pcon (PEndianness s -> Term s PEndianness)
-> (PBool s -> PEndianness s) -> PBool s -> Term s PEndianness
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Term s PBool -> PEndianness s
forall (s :: S). Term s PBool -> PEndianness s
PEndianness (Term s PBool -> PEndianness s)
-> (PBool s -> Term s PBool) -> PBool s -> PEndianness s
forall b c a. (b -> c) -> (a -> b) -> a -> c
. PBool s -> Term s PBool
forall (a :: PType) (s :: S). PlutusType a => a s -> Term s a
pcon (PBool s -> Term s PEndianness) -> PBool s -> Term s PEndianness
forall a b. (a -> b) -> a -> b
$ PBool s
forall (s :: S). PBool s
PTrue

{- | Indicates the conversion should be most-significant-last.

@since WIP
-}
pmostSignificantLast :: forall (s :: S). Term s PEndianness
pmostSignificantLast :: forall (s :: S). Term s PEndianness
pmostSignificantLast = PEndianness s -> Term s PEndianness
forall (a :: PType) (s :: S). PlutusType a => a s -> Term s a
pcon (PEndianness s -> Term s PEndianness)
-> (PBool s -> PEndianness s) -> PBool s -> Term s PEndianness
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Term s PBool -> PEndianness s
forall (s :: S). Term s PBool -> PEndianness s
PEndianness (Term s PBool -> PEndianness s)
-> (PBool s -> Term s PBool) -> PBool s -> PEndianness s
forall b c a. (b -> c) -> (a -> b) -> a -> c
. PBool s -> Term s PBool
forall (a :: PType) (s :: S). PlutusType a => a s -> Term s a
pcon (PBool s -> Term s PEndianness) -> PBool s -> Term s PEndianness
forall a b. (a -> b) -> a -> b
$ PBool s
forall (s :: S). PBool s
PFalse

{- | Convert a 'PByteString' into a 'PInteger', as per
[CIP-121](https://github.com/cardano-foundation/CIPs/tree/master/CIP-0121#builtinbytestringtointeger).

@since WIP
-}
pbyteStringToInteger ::
  forall (s :: S).
  Term s PEndianness ->
  Term s (PByteString :--> PInteger)
pbyteStringToInteger :: forall (s :: S).
Term s PEndianness -> Term s (PByteString :--> PInteger)
pbyteStringToInteger Term s PEndianness
e = (Term s PByteString -> Term s PInteger)
-> Term s (PByteString :--> PInteger)
forall a (b :: PType) (s :: S) (c :: PType).
(PLamN a b s, HasCallStack) =>
(Term s c -> a) -> Term s (c :--> b)
forall (c :: PType).
HasCallStack =>
(Term s c -> Term s PInteger) -> Term s (c :--> PInteger)
plam ((Term s PByteString -> Term s PInteger)
 -> Term s (PByteString :--> PInteger))
-> (Term s PByteString -> Term s PInteger)
-> Term s (PByteString :--> PInteger)
forall a b. (a -> b) -> a -> b
$ \Term s PByteString
bs ->
  DefaultFun
-> Term s (PInner PEndianness :--> (PByteString :--> PInteger))
forall (s :: S) (a :: PType). DefaultFun -> Term s a
punsafeBuiltin DefaultFun
PLC.ByteStringToInteger Term s (PInner PEndianness :--> (PByteString :--> PInteger))
-> Term s (PInner PEndianness)
-> Term s (PByteString :--> PInteger)
forall (s :: S) (a :: PType) (b :: PType).
Term s (a :--> b) -> Term s a -> Term s b
# Term s PEndianness -> Term s (PInner PEndianness)
forall (s :: S) (a :: PType). Term s a -> Term s (PInner a)
pto Term s PEndianness
e Term s (PByteString :--> PInteger)
-> Term s PByteString -> Term s PInteger
forall (s :: S) (a :: PType) (b :: PType).
Term s (a :--> b) -> Term s a -> Term s b
# Term s PByteString
bs

{- | Convert a (non-negative) 'PInteger' into a 'PByteString'. This will produce
a result of the minimal size required: if you want to specify a size, use
'pintegerToByteStringSized'. For details, see
[CIP-121](https://github.com/cardano-foundation/CIPs/tree/master/CIP-0121#builtinintegertobytestring).

= Note

This conversion is unsafe, as it will error when given a non-negative
integer.
-}
pintegerToByteString ::
  forall (s :: S).
  Term s PEndianness ->
  Term s (PInteger :--> PByteString)
pintegerToByteString :: forall (s :: S).
Term s PEndianness -> Term s (PInteger :--> PByteString)
pintegerToByteString Term s PEndianness
e = (Term s PInteger -> Term s PByteString)
-> Term s (PInteger :--> PByteString)
forall a (b :: PType) (s :: S) (c :: PType).
(PLamN a b s, HasCallStack) =>
(Term s c -> a) -> Term s (c :--> b)
forall (c :: PType).
HasCallStack =>
(Term s c -> Term s PByteString) -> Term s (c :--> PByteString)
plam ((Term s PInteger -> Term s PByteString)
 -> Term s (PInteger :--> PByteString))
-> (Term s PInteger -> Term s PByteString)
-> Term s (PInteger :--> PByteString)
forall a b. (a -> b) -> a -> b
$ \Term s PInteger
i ->
  DefaultFun
-> Term
     s
     (PInner PEndianness
      :--> (PInteger :--> (PInteger :--> PByteString)))
forall (s :: S) (a :: PType). DefaultFun -> Term s a
punsafeBuiltin DefaultFun
PLC.IntegerToByteString Term
  s
  (PInner PEndianness
   :--> (PInteger :--> (PInteger :--> PByteString)))
-> Term s (PInner PEndianness)
-> Term s (PInteger :--> (PInteger :--> PByteString))
forall (s :: S) (a :: PType) (b :: PType).
Term s (a :--> b) -> Term s a -> Term s b
# Term s PEndianness -> Term s (PInner PEndianness)
forall (s :: S) (a :: PType). Term s a -> Term s (PInner a)
pto Term s PEndianness
e Term s (PInteger :--> (PInteger :--> PByteString))
-> Term s PInteger -> Term s (PInteger :--> PByteString)
forall (s :: S) (a :: PType) (b :: PType).
Term s (a :--> b) -> Term s a -> Term s b
# (Term s PInteger
0 :: Term s PInteger) Term s (PInteger :--> PByteString)
-> Term s PInteger -> Term s PByteString
forall (s :: S) (a :: PType) (b :: PType).
Term s (a :--> b) -> Term s a -> Term s b
# Term s PInteger
i

{- | As 'pintegerToByteString', but allows specifying a required size. If
a size larger than the minimum is specified, the result will be padded with zero
bytes, positioned according to the endianness argument.

For more details, see [CIP-121](https://github.com/cardano-foundation/CIPs/tree/master/CIP-0121#builtinintegertobytestring).

= Note

This conversion is unsafe. In addition to the reasons for
'punsafeIntegerToByteString' being unsafe, this will also error if the
requested size is too large (currently 8192 is the limit) or too small to fit
the specified 'PInteger'.
-}
pintegerToByteStringSized ::
  forall (s :: S).
  Term s PEndianness ->
  Term s (PPositive :--> PInteger :--> PByteString)
pintegerToByteStringSized :: forall (s :: S).
Term s PEndianness
-> Term s (PPositive :--> (PInteger :--> PByteString))
pintegerToByteStringSized Term s PEndianness
e = (Term s PPositive -> Term s PInteger -> Term s PByteString)
-> Term s (PPositive :--> (PInteger :--> PByteString))
forall a (b :: PType) (s :: S) (c :: PType).
(PLamN a b s, HasCallStack) =>
(Term s c -> a) -> Term s (c :--> b)
forall (c :: PType).
HasCallStack =>
(Term s c -> Term s PInteger -> Term s PByteString)
-> Term s (c :--> (PInteger :--> PByteString))
plam \Term s PPositive
len Term s PInteger
i ->
  DefaultFun
-> Term
     s
     (PInner PEndianness
      :--> (PInner PPositive :--> (PInteger :--> PByteString)))
forall (s :: S) (a :: PType). DefaultFun -> Term s a
punsafeBuiltin DefaultFun
PLC.IntegerToByteString Term
  s
  (PInner PEndianness
   :--> (PInner PPositive :--> (PInteger :--> PByteString)))
-> Term s (PInner PEndianness)
-> Term s (PInner PPositive :--> (PInteger :--> PByteString))
forall (s :: S) (a :: PType) (b :: PType).
Term s (a :--> b) -> Term s a -> Term s b
# Term s PEndianness -> Term s (PInner PEndianness)
forall (s :: S) (a :: PType). Term s a -> Term s (PInner a)
pto Term s PEndianness
e Term s (PInner PPositive :--> (PInteger :--> PByteString))
-> Term s (PInner PPositive) -> Term s (PInteger :--> PByteString)
forall (s :: S) (a :: PType) (b :: PType).
Term s (a :--> b) -> Term s a -> Term s b
# Term s PPositive -> Term s (PInner PPositive)
forall (s :: S) (a :: PType). Term s a -> Term s (PInner a)
pto Term s PPositive
len Term s (PInteger :--> PByteString)
-> Term s PInteger -> Term s PByteString
forall (s :: S) (a :: PType) (b :: PType).
Term s (a :--> b) -> Term s a -> Term s b
# Term s PInteger
i