{-# LANGUAGE NoImplicitPrelude #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE TypeOperators #-}
module Synthesizer.LLVM.Generator.Core where

import qualified Synthesizer.LLVM.Causal.Private as Causal
import qualified Synthesizer.LLVM.Generator.Private as Sig
import qualified Synthesizer.LLVM.Random as Rnd

import Synthesizer.Causal.Class (($*))

import qualified LLVM.DSL.Expression as Expr
import LLVM.DSL.Expression (Exp)

import qualified LLVM.Extra.Multi.Value.Marshal as Marshal
import qualified LLVM.Extra.Multi.Value as MultiValue
import qualified LLVM.Extra.Arithmetic as A

import Control.Applicative ((<$>))

import Data.Word (Word32)

import NumericPrelude.Numeric
import NumericPrelude.Base hiding (map, iterate, takeWhile, tail)



type MV a = Sig.T (MultiValue.T a)

iterate :: (Marshal.C a) => (Exp a -> Exp a) -> Exp a -> MV a
iterate f a = Sig.iterate (Expr.unliftM1 f) (Expr.unExp a)

-- ToDo: replace by constantSharing and scanl
iterateParam ::
   (Marshal.C a, Marshal.C b) =>
   (Exp b -> Exp a -> Exp a) -> Exp b -> Exp a -> MV a
iterateParam f b a =
   MultiValue.snd <$>
   iterate (Expr.uncurry $ \bi ai -> Expr.zip bi $ f bi ai) (Expr.zip b a)


ramp ::
   (Marshal.C a, MultiValue.Additive a) =>
   Exp a -> Exp a -> MV a
ramp = iterateParam Expr.add

parabola ::
   (Marshal.C a, MultiValue.Additive a) =>
   Exp a -> Exp a -> Exp a -> MV a
parabola d2 d1 start = integrate start $* ramp d2 d1

integrate ::
   (Marshal.C a, MultiValue.Additive a, MultiValue.T a ~ al) =>
   Exp a -> Causal.T al al
integrate start =
   Causal.mapAccum (\a s -> (,) s <$> A.add s a) (Expr.unExp start)


osci ::
   (MultiValue.Fraction t, Marshal.C t) =>
   Exp t -> Exp t -> MV t
osci phase freq  =  iterate (Expr.liftM2 A.incPhase freq) phase

exponential ::
   (Marshal.C a, MultiValue.PseudoRing a) =>
   Exp a -> Exp a -> MV a
exponential  =  iterateParam Expr.mul

exponentialBounded ::
   (Marshal.C a, MultiValue.PseudoRing a,
    MultiValue.Real a, MultiValue.IntegerConstant a) =>
   Exp a -> Exp a -> Exp a -> MV a
exponentialBounded bound decay =
   iterateParam
      (\bk y -> case Expr.unzip bk of (b,k) -> Expr.max b $ k*y)
      (Expr.zip bound decay)


noise, noiseAlt :: Exp Word32 -> MV Word32
noise seed =
   iterate (Expr.liftReprM Rnd.nextCG)
      (Expr.irem seed (Expr.cons Rnd.modulus-1) + 1)

noiseAlt seed =
   iterate (Expr.liftReprM Rnd.nextCG32)
      (Expr.irem seed (Expr.cons Rnd.modulus-1) + 1)