{-# LANGUAGE LambdaCase #-}

-- | Functions operating on `BuiltinList`.
module PlutusTx.BuiltinList (
  BuiltinList,
  B.caseList,
  B.caseList',
  map,
  elem,
  find,
  any,
  all,
  (!!),
)
where

import Prelude (Bool (..), Integer, Maybe (..), otherwise, (.))

import PlutusTx.Builtins qualified as B
import PlutusTx.Builtins.HasOpaque
import PlutusTx.Builtins.Internal (BuiltinList)
import PlutusTx.Builtins.Internal qualified as BI
import PlutusTx.Eq
import PlutusTx.ErrorCodes
import PlutusTx.Trace (traceError)

infixl 9 !!

map :: forall a b. (MkNil b) => (a -> b) -> BuiltinList a -> BuiltinList b
map :: forall a b. MkNil b => (a -> b) -> BuiltinList a -> BuiltinList b
map a -> b
f = BuiltinList a -> BuiltinList b
go
 where
  go :: BuiltinList a -> BuiltinList b
  go :: BuiltinList a -> BuiltinList b
go =
    BuiltinList b
-> (a -> BuiltinList a -> BuiltinList b)
-> BuiltinList a
-> BuiltinList b
forall a r. r -> (a -> BuiltinList a -> r) -> BuiltinList a -> r
B.caseList'
      BuiltinList b
forall arep. MkNil arep => BuiltinList arep
B.mkNil
      (\a
x -> b -> BuiltinList b -> BuiltinList b
forall a. a -> BuiltinList a -> BuiltinList a
BI.mkCons (a -> b
f a
x) (BuiltinList b -> BuiltinList b)
-> (BuiltinList a -> BuiltinList b)
-> BuiltinList a
-> BuiltinList b
forall b c a. (b -> c) -> (a -> b) -> a -> c
. BuiltinList a -> BuiltinList b
go)
{-# INLINEABLE map #-}

elem :: forall a. (Eq a) => a -> BuiltinList a -> Bool
elem :: forall a. Eq a => a -> BuiltinList a -> Bool
elem a
a = BuiltinList a -> Bool
go
 where
  go :: BuiltinList a -> Bool
  go :: BuiltinList a -> Bool
go = Bool -> (a -> BuiltinList a -> Bool) -> BuiltinList a -> Bool
forall a r. r -> (a -> BuiltinList a -> r) -> BuiltinList a -> r
B.caseList' Bool
False (\a
x BuiltinList a
xs -> if a
a a -> a -> Bool
forall a. Eq a => a -> a -> Bool
== a
x then Bool
True else BuiltinList a -> Bool
go BuiltinList a
xs)
{-# INLINEABLE elem #-}

find :: forall a. (a -> Bool) -> BuiltinList a -> Maybe a
find :: forall a. (a -> Bool) -> BuiltinList a -> Maybe a
find a -> Bool
p = BuiltinList a -> Maybe a
go
 where
  go :: BuiltinList a -> Maybe a
  go :: BuiltinList a -> Maybe a
go =
    Maybe a
-> (a -> BuiltinList a -> Maybe a) -> BuiltinList a -> Maybe a
forall a r. r -> (a -> BuiltinList a -> r) -> BuiltinList a -> r
B.caseList'
      Maybe a
forall a. Maybe a
Nothing
      (\a
x BuiltinList a
xs -> if a -> Bool
p a
x then a -> Maybe a
forall a. a -> Maybe a
Just a
x else BuiltinList a -> Maybe a
go BuiltinList a
xs)
{-# INLINEABLE find #-}

any :: forall a. (a -> Bool) -> BuiltinList a -> Bool
any :: forall a. (a -> Bool) -> BuiltinList a -> Bool
any a -> Bool
p = BuiltinList a -> Bool
go
 where
  go :: BuiltinList a -> Bool
  go :: BuiltinList a -> Bool
go = Bool -> (a -> BuiltinList a -> Bool) -> BuiltinList a -> Bool
forall a r. r -> (a -> BuiltinList a -> r) -> BuiltinList a -> r
B.caseList' Bool
False (\a
x BuiltinList a
xs -> if a -> Bool
p a
x then Bool
True else BuiltinList a -> Bool
go BuiltinList a
xs)
{-# INLINEABLE any #-}

all :: forall a. (a -> Bool) -> BuiltinList a -> Bool
all :: forall a. (a -> Bool) -> BuiltinList a -> Bool
all a -> Bool
p = BuiltinList a -> Bool
go
 where
  go :: BuiltinList a -> Bool
  go :: BuiltinList a -> Bool
go = Bool -> (a -> BuiltinList a -> Bool) -> BuiltinList a -> Bool
forall a r. r -> (a -> BuiltinList a -> r) -> BuiltinList a -> r
B.caseList' Bool
True (\a
x BuiltinList a
xs -> if a -> Bool
p a
x then BuiltinList a -> Bool
go BuiltinList a
xs else Bool
False)
{-# INLINEABLE all #-}

{-| Get the element at a given index.

This function is partial and takes linear time.
-}
(!!) :: forall a. BuiltinList a -> Integer -> a
!! :: forall a. BuiltinList a -> Integer -> a
(!!) BuiltinList a
xs0 Integer
i0
  | Integer
i0 Integer -> Integer -> Bool
`B.lessThanInteger` Integer
0 = BuiltinString -> a
forall a. BuiltinString -> a
traceError BuiltinString
builtinListNegativeIndexError
  | Bool
otherwise = BuiltinList a -> Integer -> a
go BuiltinList a
xs0 Integer
i0
 where
  go :: BuiltinList a -> Integer -> a
  go :: BuiltinList a -> Integer -> a
go BuiltinList a
xs Integer
i =
    (() -> () -> a)
-> (a -> BuiltinList a -> () -> a) -> BuiltinList a -> () -> a
forall a r.
(() -> r) -> (a -> BuiltinList a -> r) -> BuiltinList a -> r
B.caseList
      (\()
_ -> BuiltinString -> () -> a
forall a. BuiltinString -> a
traceError BuiltinString
builtinListIndexTooLargeError)
      ( \a
y BuiltinList a
ys ()
_ ->
          if Integer
i Integer -> Integer -> Bool
`B.equalsInteger` Integer
0
            then a
y
            else BuiltinList a -> Integer -> a
go BuiltinList a
ys (Integer -> Integer -> Integer
B.subtractInteger Integer
i Integer
1)
      )
      BuiltinList a
xs
      ()