{-# OPTIONS_GHC -Wno-orphans #-}

module Plutarch.Num (PNum (..)) where

import Plutarch.Internal (
  PType,
  Term,
  punsafeCoerce,
  (#),
  (:-->),
 )
import Plutarch.Internal.Other (pto)
import Plutarch.Internal.PlutusType (PInner)
import Plutarch.Unsafe (punsafeDowncast)

class PNum (a :: PType) where
  (#+) :: Term s a -> Term s a -> Term s a
  default (#+) :: PNum (PInner a) => Term s a -> Term s a -> Term s a
  Term s a
x #+ Term s a
y = forall (s :: S) (a :: PType). Term s (PInner a) -> Term s a
punsafeDowncast forall a b. (a -> b) -> a -> b
$ forall (s :: S) (a :: PType). Term s a -> Term s (PInner a)
pto Term s a
x forall (a :: PType) (s :: S).
PNum a =>
Term s a -> Term s a -> Term s a
#+ forall (s :: S) (a :: PType). Term s a -> Term s (PInner a)
pto Term s a
y

  (#-) :: Term s a -> Term s a -> Term s a
  default (#-) :: PNum (PInner a) => Term s a -> Term s a -> Term s a
  Term s a
x #- Term s a
y = forall (s :: S) (a :: PType). Term s (PInner a) -> Term s a
punsafeDowncast forall a b. (a -> b) -> a -> b
$ forall (s :: S) (a :: PType). Term s a -> Term s (PInner a)
pto Term s a
x forall (a :: PType) (s :: S).
PNum a =>
Term s a -> Term s a -> Term s a
#- forall (s :: S) (a :: PType). Term s a -> Term s (PInner a)
pto Term s a
y

  (#*) :: Term s a -> Term s a -> Term s a
  default (#*) :: PNum (PInner a) => Term s a -> Term s a -> Term s a
  Term s a
x #* Term s a
y = forall (s :: S) (a :: PType). Term s (PInner a) -> Term s a
punsafeDowncast forall a b. (a -> b) -> a -> b
$ forall (s :: S) (a :: PType). Term s a -> Term s (PInner a)
pto Term s a
x forall (a :: PType) (s :: S).
PNum a =>
Term s a -> Term s a -> Term s a
#* forall (s :: S) (a :: PType). Term s a -> Term s (PInner a)
pto Term s a
y

  pnegate :: Term s (a :--> a)
  default pnegate :: PNum (PInner a) => Term s (a :--> a)
  pnegate = forall (s :: S) (a :: PType) (b :: PType). Term s a -> Term s b
punsafeCoerce (forall (a :: PType) (s :: S). PNum a => Term s (a :--> a)
pnegate :: Term s (PInner a :--> PInner a))

  pabs :: Term s (a :--> a)
  default pabs :: PNum (PInner a) => Term s (a :--> a)
  pabs = forall (s :: S) (a :: PType) (b :: PType). Term s a -> Term s b
punsafeCoerce (forall (a :: PType) (s :: S). PNum a => Term s (a :--> a)
pabs :: Term s (PInner a :--> PInner a))

  psignum :: Term s (a :--> a)
  default psignum :: PNum (PInner a) => Term s (a :--> a)
  psignum = forall (s :: S) (a :: PType) (b :: PType). Term s a -> Term s b
punsafeCoerce (forall (a :: PType) (s :: S). PNum a => Term s (a :--> a)
psignum :: Term s (PInner a :--> PInner a))

  pfromInteger :: Integer -> Term s a
  default pfromInteger :: PNum (PInner a) => Integer -> Term s a
  pfromInteger Integer
x = forall (s :: S) (a :: PType). Term s (PInner a) -> Term s a
punsafeDowncast forall a b. (a -> b) -> a -> b
$ forall (a :: PType) (s :: S). PNum a => Integer -> Term s a
pfromInteger Integer
x

-- orphan instance, but only visibly orphan when importing internal modules
instance PNum a => Num (Term s a) where
  + :: Term s a -> Term s a -> Term s a
(+) = forall (a :: PType) (s :: S).
PNum a =>
Term s a -> Term s a -> Term s a
(#+)
  - :: Term s a -> Term s a -> Term s a
(-) = forall (a :: PType) (s :: S).
PNum a =>
Term s a -> Term s a -> Term s a
(#-)
  * :: Term s a -> Term s a -> Term s a
(*) = forall (a :: PType) (s :: S).
PNum a =>
Term s a -> Term s a -> Term s a
(#*)
  abs :: Term s a -> Term s a
abs Term s a
x = forall (a :: PType) (s :: S). PNum a => Term s (a :--> a)
pabs forall (s :: S) (a :: PType) (b :: PType).
HasCallStack =>
Term s (a :--> b) -> Term s a -> Term s b
# Term s a
x
  negate :: Term s a -> Term s a
negate Term s a
x = forall (a :: PType) (s :: S). PNum a => Term s (a :--> a)
pnegate forall (s :: S) (a :: PType) (b :: PType).
HasCallStack =>
Term s (a :--> b) -> Term s a -> Term s b
# Term s a
x
  signum :: Term s a -> Term s a
signum Term s a
x = forall (a :: PType) (s :: S). PNum a => Term s (a :--> a)
psignum forall (s :: S) (a :: PType) (b :: PType).
HasCallStack =>
Term s (a :--> b) -> Term s a -> Term s b
# Term s a
x
  fromInteger :: Integer -> Term s a
fromInteger = forall (a :: PType) (s :: S). PNum a => Integer -> Term s a
pfromInteger