{-# LANGUAGE NoImplicitPrelude #-}
module Synthesizer.LLVM.Wave where

import qualified Synthesizer.LLVM.Simple.Value as Value
import qualified LLVM.Extra.ScalarOrVector as SoV

import qualified LLVM.Extra.Arithmetic as A
import qualified LLVM.Extra.Monad as M

import qualified LLVM.Core as LLVM
import LLVM.Core
          (Value, CodeGenFunction,
           IsFloating, IsArithmetic, IsConst, )

import Control.Monad.HT ((<=<), )

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

import NumericPrelude.Numeric
import NumericPrelude.Base hiding (replicate, )



saw ::
   (Ring.C a, IsConst a, SoV.Replicate a v, IsArithmetic v) =>
   Value v -> CodeGenFunction r (Value v)
saw =
   A.sub (SoV.replicateOf 1) <=<
   A.mul (SoV.replicateOf 2)

square ::
   (Ring.C a, IsConst a,
    SoV.Replicate a v, SoV.Fraction v, SoV.Real v) =>
   Value v -> CodeGenFunction r (Value v)
square =
   A.sub (SoV.replicateOf 1) <=<
   A.mul (SoV.replicateOf 2) <=<
   SoV.truncate <=<
   A.mul (SoV.replicateOf 2)

triangle ::
   (Field.C a, IsConst a,
    SoV.Replicate a v, SoV.Fraction v, SoV.Real v) =>
   Value v -> CodeGenFunction r (Value v)
triangle =
   flip A.sub (SoV.replicateOf 1) <=<
   SoV.abs <=<
   flip A.sub (SoV.replicateOf 2) <=<
   A.mul (SoV.replicateOf 4) <=<
   SoV.incPhase (SoV.replicateOf 0.75)

approxSine2 ::
   (Ring.C a, IsConst a, SoV.Replicate a v, SoV.Real v) =>
   Value v -> CodeGenFunction r (Value v)
approxSine2 t = do
   x <- saw t
   A.mul (SoV.replicateOf 4) =<<
      A.mul x =<<
      A.sub (SoV.replicateOf 1) =<<
      SoV.abs x

approxSine3 ::
   (Field.C a, IsConst a,
    SoV.Replicate a v, SoV.Fraction v, SoV.Real v) =>
   Value v -> CodeGenFunction r (Value v)
approxSine3 t = do
   x <- triangle t
   A.mul (SoV.replicateOf 0.5) =<<
      A.mul x =<<
      A.sub (SoV.replicateOf 3) =<<
      A.mul x x

approxSine4 ::
   (Field.C a, IsConst a, SoV.Replicate a v, SoV.Real v) =>
   Value v -> CodeGenFunction r (Value v)
approxSine4 t = do
   x <- saw t
   ax <- SoV.abs x
   sax <- A.sub (SoV.replicateOf 1) ax
   A.mul (SoV.replicateOf (16/5)) =<<
      A.mul x =<<
      A.mul sax =<<
      A.add (SoV.replicateOf 1) =<<
      A.mul sax ax

{- |
For the distortion factor @recip pi@ you get the closest approximation
to an undistorted cosine or sine.
We have chosen this scaling in order to stay with field operations.
-}
rationalApproxCosine1, rationalApproxSine1 ::
   (Field.C a, IsConst a, SoV.Replicate a v, SoV.Real v, IsFloating v) =>
   Value v -> Value v -> CodeGenFunction r (Value v)
rationalApproxCosine1 k t = do
   num2 <-
      A.square =<<
      A.mul k =<<
      A.add (SoV.replicateOf (-1)) =<<
      A.mul (SoV.replicateOf 2) t
   den2 <-
      A.square =<<
      A.mul t =<<
      A.sub (SoV.replicateOf 1) t
   M.liftR2 A.fdiv
      (A.sub num2 den2)
      (A.add num2 den2)

rationalApproxSine1 k t = do
   num <-
      A.mul k =<<
      A.add (SoV.replicateOf (-1)) =<<
      A.mul (SoV.replicateOf 2) t
   den <-
      A.mul t =<<
      A.sub (SoV.replicateOf 1) t
   M.liftR2 A.fdiv
      (A.mul (SoV.replicateOf (-2)) =<< A.mul num den)
      (M.liftR2 A.add (A.square num) (A.square den))


trapezoidSkew ::
   (Field.C a, IsConst a,
    SoV.Replicate a v, SoV.Fraction v, SoV.Real v) =>
   Value v -> Value v -> CodeGenFunction r (Value v)
trapezoidSkew p =
   SoV.max (SoV.replicateOf (-1)) <=<
   SoV.min (SoV.replicateOf 1) <=<
   flip A.fdiv p <=<
   A.sub (SoV.replicateOf 1) <=<
   A.mul (SoV.replicateOf 2)

sine ::
   (Trans.C a, IsFloating a, IsConst a) =>
--   Value a -> TValue r a
   Value a -> CodeGenFunction r (Value a)
sine t =
   A.sin =<< A.mul t =<< Value.decons Value.twoPi



{- |
This can be used for preprocessing the phase
in order to generate locally faster oscillating waves.
For example

> triangle <=< replicate (valueOf 2.5)

shrinks a triangle wave such that 2.5 periods fit into one.
-}
replicate ::
   (Field.C a, IsConst a,
    SoV.Replicate a v, SoV.Fraction v, SoV.Real v) =>
   Value v -> Value v -> CodeGenFunction r (Value v)
replicate k =
   SoV.fraction <=<
   A.mul k <=<
   flip A.sub (SoV.replicateOf 0.5) <=<
   SoV.incPhase (SoV.replicateOf 0.5)

{- |
Preprocess the phase such that the first half of a wave
is expanded to one period and shifted by 90 degree.
E.g.

> sine <=< halfEnvelope

generates a sequence of sine bows that starts and ends with the maximum.
Such a signal can be used to envelope an oscillation
generated using 'replicate'.
-}
halfEnvelope ::
   (Field.C a, IsConst a,
    SoV.Replicate a v, SoV.Fraction v, SoV.Real v) =>
   Value v -> CodeGenFunction r (Value v)
halfEnvelope =
   A.mul (SoV.replicateOf 0.5) <=<
   SoV.incPhase (SoV.replicateOf 0.5)

partial ::
   (LLVM.IsPrimitive i, LLVM.IsPrimitive a,
    LLVM.IsInteger i, IsFloating a,
    SoV.Replicate a v, SoV.Fraction v) =>
   (Value v -> CodeGenFunction r (Value v)) ->
   Value i ->
   (Value v -> CodeGenFunction r (Value v))
partial w n t =
   w =<<
   SoV.signedFraction =<<
   A.mul t =<<
   SoV.replicate =<<
   LLVM.sitofp n