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

import qualified Synthesizer.LLVM.Filter.SecondOrderCascade as Cascade

import qualified Synthesizer.Plain.Filter.Recursive.Butterworth as Butterworth
import qualified Synthesizer.Plain.Filter.Recursive.SecondOrder as Filt2
import Synthesizer.Plain.Filter.Recursive (Passband, )

import qualified LLVM.Extra.Control as U
import qualified LLVM.Extra.Representation as Rep
import qualified Synthesizer.LLVM.Simple.Value as Value

import qualified LLVM.Extra.Arithmetic as A

import qualified LLVM.Core as LLVM
import LLVM.Core
   (Value, valueOf, constOf,
    IsConst, IsFloating, IsSized,
    CodeGenFunction, )
import Data.Word (Word32, )

import qualified Data.TypeLevel.Num      as TypeNum
import qualified Data.TypeLevel.Num.Sets as TypeSet

import qualified Algebra.Transcendental as Trans
-- import qualified Algebra.Field as Field
-- import qualified Algebra.Module as Module
-- import qualified Algebra.Ring as Ring

import NumericPrelude.Numeric
import NumericPrelude.Base


parameter, parameterMalloc, _parameterAlloca ::
   (Trans.C a, IsConst a, IsFloating a, IsSized a as,
    TypeSet.Nat n,
    TypeNum.Mul n as sineSize,
    TypeSet.Pos sineSize,
    IsSized (Cascade.Parameter n a) paramSize) =>
   n -> Passband -> Value a -> Value a ->
   CodeGenFunction r (Cascade.ParameterValue n a)
parameter = parameterMalloc

parameterMalloc n kind ratio freq = do
   let order = 2 * TypeNum.toInt n
   partialRatio <-
      Value.decons (Butterworth.partialRatio order (Value.constantValue ratio))
   let sines =
          (flip const :: n -> LLVM.Value (LLVM.Array n a)
                           -> LLVM.Value (LLVM.Array n a)) n $
          LLVM.value $
          LLVM.constArray $
          map constOf $ Butterworth.makeSines order
   psine <- LLVM.malloc
   LLVM.store sines psine
   s <- LLVM.getElementPtr0 psine (valueOf (0::Word32), ())
   ps <- LLVM.malloc
   p <- LLVM.getElementPtr0 ps (valueOf (0::Word32), ())
   let len = valueOf $ (fromIntegral $ TypeNum.toInt n :: Word32)
   U.arrayLoop len p s $ \ptri si -> do
      sinw <- LLVM.load si
      flip Rep.store ptri =<<
         Value.flatten
            (Filt2.adjustPassband kind
               (flip
                  (Butterworth.partialParameter
                      (Value.constantValue partialRatio))
                  (Value.constantValue sinw))
               (Value.constantValue freq))
      A.advanceArrayElementPtr si
   pv <- LLVM.load ps
   LLVM.free psine
   LLVM.free ps
   return (Cascade.ParameterValue pv)

_parameterAlloca n kind ratio freq = do
   let order = 2 * TypeNum.toInt n
   partialRatio <-
      Value.decons (Butterworth.partialRatio order (Value.constantValue ratio))
   let sines =
          (flip const :: n -> LLVM.Value (LLVM.Array n a)
                           -> LLVM.Value (LLVM.Array n a)) n $
          LLVM.value $
          LLVM.constArray $
          map constOf $ Butterworth.makeSines order
   psine <- LLVM.alloca
   LLVM.store sines psine
   s <- LLVM.getElementPtr0 psine (valueOf (0::Word32), ())
   ps <- LLVM.alloca
   p <- LLVM.getElementPtr0 ps (valueOf (0::Word32), ())
   let len = valueOf $ (fromIntegral $ TypeNum.toInt n :: Word32)
   U.arrayLoop len p s $ \ptri si -> do
      sinw <- LLVM.load si
      flip Rep.store ptri =<<
         Value.flatten
            (Filt2.adjustPassband kind
               (flip
                  (Butterworth.partialParameter
                      (Value.constantValue partialRatio))
                  (Value.constantValue sinw))
               (Value.constantValue freq))
      A.advanceArrayElementPtr si
   fmap Cascade.ParameterValue $ LLVM.load ps