{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE Rank2Types #-}
{- |
Apply operations on symbolic arrays to physical ones.

This is an approach with no pre-defined direction of type dependencies.
-}
module Data.Array.Knead.Symbolic.Render.Basic (
   run,
   (*->),

   storable,
   marshal,
   array,
   scatter,
   ) where

import qualified Data.Array.Knead.Symbolic.Render.Argument as Arg
import qualified Data.Array.Knead.Symbolic.PhysicalParametric as PhysP
import qualified Data.Array.Knead.Symbolic.Physical as Phys
import qualified Data.Array.Knead.Symbolic.Private as Core
import qualified Data.Array.Knead.Shape as Shape

import qualified Data.Array.Comfort.Storable.Unchecked as Array

import qualified LLVM.DSL.Render.Run as Run
import LLVM.DSL.Render.Run (run, (*->))
import LLVM.DSL.Expression (Exp)

import qualified LLVM.Extra.Multi.Value.Storable as Storable
import qualified LLVM.Extra.Multi.Value.Marshal as Marshal

import Prelude2010
import Prelude ()



_example1raw ::
   (Marshal.C sh, Shape.C sh, Marshal.C z, Marshal.C a, Storable.C b) =>
   Run.T IO z (Exp a -> Core.Array sh b) (a -> IO (Phys.Array sh b))
_example1raw = Arg.primitive *-> array

_example2raw ::
   (Marshal.C sh, Shape.C sh,
    Marshal.C z, Marshal.C a, Marshal.C b, Storable.C c) =>
   Run.T IO z
      (Exp a -> Exp b -> Core.Array sh c)
      (a -> b -> IO (Phys.Array sh c))
_example2raw = Arg.primitive *-> Arg.primitive *-> array


_example2 ::
   (Marshal.C sh, Shape.C sh,
    Marshal.C a, Marshal.C b, Storable.C c) =>
   (Exp a -> Exp b -> Core.Array sh c) ->
   IO (a -> b -> IO (Phys.Array sh c))
_example2 = run (Arg.primitive *-> Arg.primitive *-> array)

_example2exp ::
   (Marshal.C a, Marshal.C b, Storable.C c) =>
   (Exp a -> Exp b -> Exp c) ->
   IO (a -> b -> IO c)
_example2exp = run (Arg.primitive *-> Arg.primitive *-> storable)

_example2marshal ::
   (Marshal.C a, Marshal.C b, Marshal.C c) =>
   (Exp a -> Exp b -> Exp c) ->
   IO (a -> b -> IO c)
_example2marshal = run (Arg.primitive *-> Arg.primitive *-> marshal)

_example2scatter ::
   (Shape.C sh0, Shape.C sh1, Marshal.C sh1,
    Marshal.C a, Marshal.C b, Storable.C c) =>
   (Exp a -> Exp b -> PhysP.Scatter sh0 sh1 c) ->
   IO (a -> b -> IO (Array.Array sh1 c))
_example2scatter = run (Arg.primitive *-> Arg.primitive *-> scatter)




singleton :: Exp a -> Core.Array () a
singleton = Core.fromScalar

storable :: (Marshal.C p, Storable.C a) => Run.T IO p (Exp a) (IO a)
storable = Run.Cons $ PhysP.the . fmap singleton

marshal :: (Marshal.C p, Marshal.C a) => Run.T IO p (Exp a) (IO a)
marshal = Run.Cons $ PhysP.theMarshal . fmap singleton

array ::
   (Shape.C sh, Shape.Index sh ~ ix, Marshal.C sh,
    Marshal.C p, Storable.C a) =>
   Run.T IO p (Core.Array sh a) (IO (Phys.Array sh a))
array = Run.Cons PhysP.render


scatter ::
   (Shape.C sh0, Shape.C sh1, Marshal.C sh1, Marshal.C p, Storable.C a) =>
   Run.T IO p (PhysP.Scatter sh0 sh1 a) (IO (Array.Array sh1 a))
scatter = Run.Cons PhysP.scatter