module PlutusTx.Enum.Class (Enum (..)) where

import PlutusTx.Bool
import PlutusTx.Builtins
import PlutusTx.List
import PlutusTx.Numeric
import PlutusTx.Ord

-- | Class 'Enum' defines operations on sequentially ordered types.
class Enum a where
  {-# MINIMAL toEnum, fromEnum #-}

  {-| The successor of a value.  For numeric types, 'succ' adds 1.

  For types that implement 'Ord', @succ x@ should be the least element
  that is greater than @x@, and 'error' if there is none. -}
  succ :: a -> a

  {-| The predecessor of a value.  For numeric types, 'pred' subtracts 1.

  For types that implement 'Ord', @pred x@ should be the greatest element
  that is less than @x@, and 'error' if there is none. -}
  pred :: a -> a

  -- | Convert from an 'Integer'.
  toEnum :: Integer -> a

  -- | Convert to an 'Integer'.
  fromEnum :: a -> Integer

  -- | Construct a list from the given range (corresponds to [a..b]).
  enumFromTo :: a -> a -> [a]

  {-| Construct a list from the given range (corresponds to [a,b..c]).  This
  has the same semantics as the Haskell version,so if a==b and c>=b then you
  get an infinite list, which you probably don't want in Plutus Core. -}
  enumFromThenTo :: a -> a -> a -> [a]

  {-# INLINEABLE succ #-}
  succ a
x = Integer -> a
forall a. Enum a => Integer -> a
toEnum (a -> Integer
forall a. Enum a => a -> Integer
fromEnum a
x Integer -> Integer -> Integer
forall a. AdditiveSemigroup a => a -> a -> a
+ Integer
1)
  {-# INLINEABLE pred #-}
  pred a
x = Integer -> a
forall a. Enum a => Integer -> a
toEnum (a -> Integer
forall a. Enum a => a -> Integer
fromEnum a
x Integer -> Integer -> Integer
forall a. AdditiveGroup a => a -> a -> a
- Integer
1)

  {-# INLINEABLE enumFromTo #-}
  enumFromTo a
x a
lim = (Integer -> a) -> [Integer] -> [a]
forall a b. (a -> b) -> [a] -> [b]
map Integer -> a
forall a. Enum a => Integer -> a
toEnum (Integer -> Integer -> [Integer]
forall a. Enum a => a -> a -> [a]
enumFromTo (a -> Integer
forall a. Enum a => a -> Integer
fromEnum a
x) (a -> Integer
forall a. Enum a => a -> Integer
fromEnum a
lim))

  {-# INLINEABLE enumFromThenTo #-}
  enumFromThenTo a
x a
y a
lim = (Integer -> a) -> [Integer] -> [a]
forall a b. (a -> b) -> [a] -> [b]
map Integer -> a
forall a. Enum a => Integer -> a
toEnum (Integer -> Integer -> Integer -> [Integer]
forall a. Enum a => a -> a -> a -> [a]
enumFromThenTo (a -> Integer
forall a. Enum a => a -> Integer
fromEnum a
x) (a -> Integer
forall a. Enum a => a -> Integer
fromEnum a
y) (a -> Integer
forall a. Enum a => a -> Integer
fromEnum a
lim))

instance Enum Integer where
  {-# INLINEABLE succ #-}
  succ :: Integer -> Integer
succ Integer
x = Integer -> Integer -> Integer
addInteger Integer
x Integer
1

  {-# INLINEABLE pred #-}
  pred :: Integer -> Integer
pred Integer
x = Integer -> Integer -> Integer
subtractInteger Integer
x Integer
1

  {-# INLINEABLE toEnum #-}
  toEnum :: Integer -> Integer
toEnum Integer
x = Integer
x

  {-# INLINEABLE fromEnum #-}
  fromEnum :: Integer -> Integer
fromEnum Integer
x = Integer
x

  {-# INLINEABLE enumFromTo #-}
  enumFromTo :: Integer -> Integer -> [Integer]
enumFromTo Integer
x Integer
lim
    | Integer
x Integer -> Integer -> Bool
forall a. Ord a => a -> a -> Bool
> Integer
lim = []
    | Bool
otherwise = Integer
x Integer -> [Integer] -> [Integer]
forall a. a -> [a] -> [a]
: Integer -> Integer -> [Integer]
forall a. Enum a => a -> a -> [a]
enumFromTo (Integer -> Integer
forall a. Enum a => a -> a
succ Integer
x) Integer
lim

  {-# INLINEABLE enumFromThenTo #-}
  enumFromThenTo :: Integer -> Integer -> Integer -> [Integer]
enumFromThenTo Integer
x Integer
y Integer
lim =
    if Integer
delta Integer -> Integer -> Bool
forall a. Ord a => a -> a -> Bool
>= Integer
0
      then Integer -> [Integer]
up_list Integer
x
      else Integer -> [Integer]
dn_list Integer
x
    where
      delta :: Integer
delta = Integer -> Integer -> Integer
subtractInteger Integer
y Integer
x
      up_list :: Integer -> [Integer]
up_list Integer
x1 =
        if Integer
x1 Integer -> Integer -> Bool
forall a. Ord a => a -> a -> Bool
> Integer
lim
          then []
          else Integer
x1 Integer -> [Integer] -> [Integer]
forall a. a -> [a] -> [a]
: Integer -> [Integer]
up_list (Integer -> Integer -> Integer
addInteger Integer
x1 Integer
delta)
      dn_list :: Integer -> [Integer]
dn_list Integer
x1 =
        if Integer
x1 Integer -> Integer -> Bool
forall a. Ord a => a -> a -> Bool
< Integer
lim
          then []
          else Integer
x1 Integer -> [Integer] -> [Integer]
forall a. a -> [a] -> [a]
: Integer -> [Integer]
dn_list (Integer -> Integer -> Integer
addInteger Integer
x1 Integer
delta)