```{-# LANGUAGE NoImplicitPrelude #-}
module Synthesizer.State.Interpolation where

import qualified Synthesizer.Interpolation as Interpolation
import Synthesizer.Interpolation
(T, offset, number, func, )

import qualified Synthesizer.State.Signal  as Sig

-- import qualified Algebra.Module    as Module
import qualified Algebra.RealField as RealField
-- import qualified Algebra.Field     as Field
import qualified Algebra.Ring      as Ring

import Data.Maybe (fromMaybe)

import PreludeBase
import NumericPrelude

{-* Interpolation with various padding methods -}

{-# INLINE zeroPad #-}
zeroPad :: (RealField.C t) =>
(T t y -> t -> Sig.T y -> a) ->
y -> T t y -> t -> Sig.T y -> a
zeroPad interpolate z ip phase x =
let (phInt, phFrac) = splitFraction phase
in  interpolate ip phFrac
(delayPad z (offset ip - phInt) (Sig.append x (Sig.repeat z)))

{-# INLINE constantPad #-}
constantPad :: (RealField.C t) =>
(T t y -> t -> Sig.T y -> a) ->
T t y -> t -> Sig.T y -> a
constantPad interpolate ip phase x =
let (phInt, phFrac) = splitFraction phase
do (xFirst,_) <- Sig.viewL x
return (delayPad xFirst (offset ip - phInt) (Sig.extendConstant x))
in  interpolate ip phFrac

{- |
Only for finite input signals.
-}
{-# INLINE cyclicPad #-}
cyclicPad :: (RealField.C t) =>
(T t y -> t -> Sig.T y -> a) ->
T t y -> t -> Sig.T y -> a
cyclicPad interpolate ip phase x =
let (phInt, phFrac) = splitFraction phase
in  interpolate ip phFrac
(Sig.drop (mod (phInt - offset ip) (Sig.length x)) (Sig.cycle x))

{- |
The extrapolation may miss some of the first and some of the last points
-}
{-# INLINE extrapolationPad #-}
extrapolationPad :: (RealField.C t) =>
(T t y -> t -> Sig.T y -> a) ->
T t y -> t -> Sig.T y -> a
extrapolationPad interpolate ip phase =
interpolate ip (phase - fromIntegral (offset ip))
{-
This example shows pikes, although there shouldn't be any:
plotList (take 100 \$ interpolate (Zero (0::Double)) ipCubic (-0.9::Double) (repeat 0.03) [1,0,1,0.8])
-}

{-* Helper methods for interpolation of multiple nodes -}

{-# INLINE skip #-}
skip :: (RealField.C t) =>
T t y -> (t, Sig.T y) -> (t, Sig.T y)
skip ip (phase0, x0) =
let (n, frac) = splitFraction phase0
(m, x1) = Sig.dropMarginRem (number ip) n x0
in  (fromIntegral m + frac, x1)

{-# INLINE single #-}
single :: (RealField.C t) =>
T t y -> t -> Sig.T y -> y
single ip phase0 x0 =
uncurry (func ip) \$ skip ip (phase0, x0)
--   curry (uncurry (func ip) . skip ip)
{-
GNUPlot.plotFunc [] (GNUPlot.linearScale 1000 (0,2)) (\t -> single linear (t::Double) [0,4,1::Double])
-}

{-* Helper functions -}

{-# INLINE delayPad #-}
delayPad :: y -> Int -> Sig.T y -> Sig.T y
delayPad z n =
if n<0
then Sig.drop (negate n)
else Sig.append (Sig.replicate n z)
```