content: {-# LANGUAGE DataKinds #-}
  {-# LANGUAGE LambdaCase #-}
  {-# LANGUAGE OverloadedStrings #-}
  -- {-# LANGUAGE TypeFamilies, NoMonoLocalBinds #-}
  
  {-# LANGUAGE NoIncoherentInstances #-}
  {-# LANGUAGE NoMonomorphismRestriction #-}
  {-# LANGUAGE NoUndecidableInstances #-}
  
  module Vivid.UGens.Maths (
       clip
  ---   , fold -- rename cuz of haskell clashes!
     , inRange
  --   , inRect
       -- In Vivid.UGens.Filters:
     -- , integrator
     , leastChange
     , linExp
  ---   , linLin
  ---   , modDif
     , mostChange
     , mulAdd
  ---   , runningMax
  ---   , runningMin
  ---   , runningSum
  ---   , schmidt
       -- In Vivid.UGens.Analysis:
     -- , slope
  ---   , wrap
     ) where
  
  import Vivid.SC.SynthDef.Types (CalculationRate(..))
  import Vivid.SynthDef
  import Vivid.SynthDef.FromUA
  -- import Vivid.UGens.Algebraic
  import Vivid.UGens.Args
  
  import qualified Data.ByteString.UTF8 as UTF8 (fromString)
  import Data.Proxy
  
  -- | 
  --   **Note this has different behavior than 0.1!**
  --   \"lo\" is not implied by
  --   \"hi\".
  -- 
  --   If you know you always want \"lo\" to be negative \"hi\" (like -1 to 1)
  --   you can use @'biOp' 'Clip2'@
  clip :: (Args '["in"] '["lo", "hi"] a) => a -> SDBody a Signal
  clip = makeUGen
     "Clip" AR
     (Vs::Vs '["in","lo","hi"])
     (lo_ (0::Float), hi_ (1::Float))
  
  --- fold ::
  --- fold =
  
  -- | Returns 1.0 if \"in\" is between \"lo\" and \"hi\" (including
  --   equalling "lo" or "hi"). Else 0.0.
  -- 
  --   Can be audio rate (AR), control rate (KR), or fixed (IR)
  -- 
  --   Lo and hi default to 0 and 1
  inRange :: (Args '["in"] '["lo", "hi"] a) => a -> SDBody a Signal
  inRange = makeUGen
     "InRange" AR
     (Vs::Vs '["in", "lo", "hi"])
     (lo_ (0::Float), hi_ (1::Float))
  
  --- -- inRect =
  
  -- | Returns the value of whichever of the 2 signals is changing
  --   least
  -- 
  --   Its default calculation rate is the highest of its 2 inputs
  leastChange :: (ToSig s0 as, ToSig s1 as) => s0 -> s1 -> SDBody' as Signal
  leastChange = leastOrMostChange "LeastChange"
  
  leastOrMostChange :: (ToSig s0 as, ToSig s1 as) => String -> s0 -> s1 -> SDBody' as Signal
  leastOrMostChange sdName s0 s1 = do
     s0' <- toSig s0
     s1' <- toSig s1
     calcRate <- (maximum::Ord a=>[a]->a) <$> sequence (map getCalcRate [s0', s1'])
     addUGen $ UGen (UGName_S . UTF8.fromString $ sdName) calcRate [s0',s1'] 1
  
  -- | "Converts a linear range of values to an exponential range of values."
  -- 
  --   Args:
  -- 
  --   * *in* -  The input signal to convert.
  -- 
  --   * *srclo* - Lower limit of input range.
  -- 
  --   * *srchi* - Upper limit of input range.
  -- 
  --   * *dstlo* - Lower limit of output range.
  -- 
  --   * *dsthi* - Upper limit of output range.
  -- 
  --   **"The dstlo and dsthi arguments must be nonzero and have the same sign."**
  -- 
  --   This will have the same calculation rate as its \"in\" argument
  linExp :: Args '["in"] '["srclo", "srchi", "dstlo", "dsthi"] a => a -> SDBody a Signal
  linExp as = do
     in' <- as `uaArgVal` (Proxy::Proxy "in")
     getCalcRate in' >>= \case
        AR -> successGraph AR
        KR -> successGraph KR
        _ -> error "linExp: 'in' value must be at AR/KR"
   where
     successGraph calcRate = ($ as) $ makeUGen
        "LinExp" calcRate
        (Vs::Vs '["in", "srclo", "srchi", "dstlo", "dsthi"])
        (srclo_ (0::Float), srchi_ (1::Float), dstlo_ (1::Float), dsthi_ (2::Float))
  
  -- linLin ::
  -- linLin =
  
  --- modDif ::
  --- modDif =
  
  -- | Opposite of 'leastChange'
  mostChange :: (ToSig s0 as, ToSig s1 as) => s0 -> s1 -> SDBody' as Signal
  mostChange = leastOrMostChange "MostChange"
  
  mulAdd :: (Args '["in"] '["mul", "add"] a) => a -> SDBody a Signal
  mulAdd = makeUGen
     "MulAdd" AR
     (Vs::Vs '["in", "mul", "add"])
     (mul_ (1::Float), add_ (0::Float))
  
  --- runningMax ::
  --- runningMax =
  --- runningMin ::
  --- runningMin =
  --- runningSum ::
  --- runningSum =
  --- schmidt ::
  --- schmidt =
  --- wrap ::
  --- wrap =
  