{-# LANGUAGE NoImplicitPrelude #-}
{- |
Plain interpolation functions.
-}
module Synthesizer.Interpolation.Core (
   linear,
   cubic,
   cubicAlt,
   ) where

import qualified Algebra.Module    as Module
import qualified Algebra.Field     as Field

import Synthesizer.Utility (affineComb, )

import NumericPrelude.Numeric
import NumericPrelude.Base



{-# INLINE linear #-}
linear ::
   (Module.C a v) =>
   v -> v -> a -> v
linear x0 x1 phase = affineComb phase (x0,x1)

{-# INLINE cubic #-}
cubic ::
   (Module.C a v, Field.C a) =>
   v -> v -> v -> v -> a -> v
cubic xm1 x0 x1 x2 t =
   let lipm12 = affineComb t (xm1,x2)
       lip01  = affineComb t (x0, x1)
       three  = 3 `asTypeOf` t
   in  lip01 + (t*(t-1)/2) *>
                  (lipm12 + (x0+x1) - three *> lip01)

{- |
The interpolators for module operations
do not simply compute a straight linear combination of some vectors.
Instead they add then scale, then add again, and so on.
This is efficient whenever scaling and addition is cheap.
In this case they might save multiplications.
I can't say much about numeric cancellations, however.
-}
{-# INLINE cubicAlt #-}
cubicAlt ::
   (Module.C a v, Field.C a) =>
   v -> v -> v -> v -> a -> v
cubicAlt xm1 x0 x1 x2 t =
   let half = 1/2 `asTypeOf` t
   in  cubicHalf    t  x0 (half *> (x1-xm1)) +
       cubicHalf (1-t) x1 (half *> (x0-x2))

{- |
@\t -> cubicHalf t x x'@ has a double zero at 1 and
at 0 it has value x and slope x'.
-}
{-# INLINE cubicHalf #-}
cubicHalf :: (Module.C t y) => t -> y -> y -> y
cubicHalf t x x' =
   (t-1)^2 *> ((1+2*t)*>x + t*>x')