{-# LANGUAGE NoImplicitPrelude #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE FlexibleContexts #-}
module Synthesizer.LLVM.Filter.Butterworth (
   parameter, parameterCausal, Cascade.ParameterValue,
   Cascade.causal,  Cascade.causalPacked,
   Cascade.causalP, Cascade.causalPackedP,
   Cascade.fixSize,
   ) where

import qualified Synthesizer.LLVM.Filter.SecondOrderCascade as Cascade
import qualified Synthesizer.LLVM.Filter.SecondOrder as Filt2
import qualified Synthesizer.LLVM.Causal.ProcessPrivate as Causal
import qualified Synthesizer.LLVM.Simple.SignalPrivate as Sig
import qualified Synthesizer.LLVM.Simple.Value as Value

import qualified Synthesizer.Plain.Filter.Recursive.Butterworth as Butterworth
import Synthesizer.Plain.Filter.Recursive (Passband, )
import Synthesizer.Causal.Class (($<), )

import qualified LLVM.Extra.ScalarOrVector as SoV
import qualified LLVM.Extra.Control as U
import qualified LLVM.Extra.Arithmetic as A

import qualified LLVM.Core as LLVM
import LLVM.Core
   (Value, valueOf, constOf,
    IsFloating, IsSized, SizeOf,
    CodeGenFunction, )

import Data.Word (Word32, )

import Foreign.Ptr (Ptr, )

import qualified Type.Data.Num.Decimal as TypeNum
import Type.Data.Num.Decimal.Number ((:*:), )
import Type.Base.Proxy (Proxy, )

import qualified Algebra.Transcendental as Trans

import NumericPrelude.Numeric
import NumericPrelude.Base



parameterCausal ::
   (Causal.C process,
    Trans.C a, SoV.TranscendentalConstant a, IsFloating a, IsSized a,
    TypeNum.Natural n,
    TypeNum.Positive (n :*: SizeOf a),
    IsSized (Cascade.ParameterStruct n a)) =>
   Proxy n -> Passband ->
   process (Value a, Value a) (Cascade.ParameterValue n a)
parameterCausal n kind =
   Causal.map
      (\((psine, ps), (ratio, freq)) ->
         parameterCore n kind psine ps ratio freq)
   $<
   Sig.zipWith (curry return) Sig.alloca Sig.alloca

parameter ::
   (Trans.C a, SoV.TranscendentalConstant a, IsFloating a, IsSized a,
    TypeNum.Natural n,
    TypeNum.Positive (n :*: SizeOf a),
    IsSized (Cascade.ParameterStruct n a)) =>
   Proxy n -> Passband -> Value a -> Value a ->
   CodeGenFunction r (Cascade.ParameterValue n a)
parameter n kind ratio freq = do
   psine <- LLVM.malloc
   ps <- LLVM.malloc
   pv <- parameterCore n kind psine ps ratio freq
   LLVM.free ps
   LLVM.free psine
   return pv

parameterCore ::
   (Trans.C a, SoV.TranscendentalConstant a, IsFloating a, IsSized a,
    TypeNum.Natural n,
    TypeNum.Positive (n :*: SizeOf a),
    IsSized (Cascade.ParameterStruct n a)) =>
   Proxy n -> Passband ->
   Value (Ptr (LLVM.Array n a)) ->
   Value (Ptr (Cascade.ParameterStruct n a)) ->
   Value a -> Value a ->
   CodeGenFunction r (Cascade.ParameterValue n a)
parameterCore n kind psine ps ratio freq = do
   let order = 2 * TypeNum.integralFromProxy n
   partialRatio <- Value.unlift1 (Butterworth.partialRatio order) ratio
   let sines =
          Cascade.constArray n $
          map constOf $ Butterworth.makeSines order
   LLVM.store sines psine
   s <- LLVM.getElementPtr0 psine (valueOf (0::Word32), ())
   p <- LLVM.getElementPtr0 ps (valueOf (0::Word32), ())
   let len = valueOf $ (TypeNum.integralFromProxy n :: Word32)
   _ <- U.arrayLoop len p s $ \ptri si -> do
      sinw <- LLVM.load si
      flip LLVM.store ptri =<<
         Filt2.composeParameter =<<
         Value.unlift3 (Butterworth.partialParameter kind) partialRatio sinw freq
      A.advanceArrayElementPtr si
   fmap Cascade.ParameterValue $ LLVM.load ps