{- |
Copyright   :  (c) Henning Thielemann 2008
License     :  GPL

Maintainer  :  synthesizer@henning-thielemann.de
Stability   :  provisional
Portability :  requires multi-parameter type classes
-}
module Synthesizer.Dimensional.Amplitude.Analysis (
    volumeMaximum,
    volumeEuclidean,
    volumeSum,
    volumeVectorMaximum,
    volumeVectorEuclidean,
    volumeVectorSum,

    directCurrentOffset,
    rectify,
    flipFlopHysteresis,

    compare,
    lessOrEqual,
  ) where

import qualified Synthesizer.Dimensional.Abstraction.RateIndependent as Ind
import qualified Synthesizer.Dimensional.Abstraction.Homogeneous as Hom

-- import qualified Synthesizer.Dimensional.RatePhantom as RP
import qualified Synthesizer.Dimensional.Straight.Signal as SigS

import qualified Synthesizer.Dimensional.Amplitude.Signal as SigA
import qualified Synthesizer.Dimensional.Amplitude.Cut    as CutD
-- import Synthesizer.Dimensional.Amplitude.Signal (toAmplitudeScalar)

import qualified Synthesizer.State.Analysis as Ana
import qualified Synthesizer.State.Signal   as Sig

import qualified Number.DimensionTerm        as DN
import qualified Algebra.DimensionTerm       as Dim

import Number.DimensionTerm ((*&))

import qualified Algebra.NormedSpace.Maximum   as NormedMax
import qualified Algebra.NormedSpace.Euclidean as NormedEuc
import qualified Algebra.NormedSpace.Sum       as NormedSum

import qualified Algebra.Algebraic           as Algebraic
import qualified Algebra.Module              as Module
import qualified Algebra.Field               as Field
import qualified Algebra.Real                as Real
import qualified Algebra.Ring                as Ring


import PreludeBase (Ord, Bool, (<=), ($), (.), uncurry, )
-- import NumericPrelude
import qualified Prelude as P



{- * Notions of volume -}

{- |
Volume based on Manhattan norm.
-}
{-# INLINE volumeMaximum #-}
volumeMaximum :: (Ind.C w, Real.C y, Dim.C u) =>
   w (SigA.S u y) y -> DN.T u y
volumeMaximum = volumeAux Ana.volumeMaximum

{- |
Volume based on Energy norm.
-}
{-# INLINE volumeEuclidean #-}
volumeEuclidean :: (Ind.C w, Algebraic.C y, Dim.C u) =>
   w (SigA.S u y) y -> DN.T u y
volumeEuclidean = volumeAux Ana.volumeEuclidean

{- |
Volume based on Sum norm.
-}
{-# INLINE volumeSum #-}
volumeSum :: (Ind.C w, Field.C y, Real.C y, Dim.C u) =>
   w (SigA.S u y) y -> DN.T u y
volumeSum = volumeAux Ana.volumeSum



{- |
Volume based on Manhattan norm.
-}
{-# INLINE volumeVectorMaximum #-}
volumeVectorMaximum :: (Ind.C w, NormedMax.C y yv, Ord y, Dim.C u) =>
   w (SigA.S u y) yv -> DN.T u y
volumeVectorMaximum = volumeAux Ana.volumeVectorMaximum

{- |
Volume based on Energy norm.
-}
{-# INLINE volumeVectorEuclidean #-}
volumeVectorEuclidean :: (Ind.C w, NormedEuc.C y yv, Algebraic.C y, Dim.C u) =>
   w (SigA.S u y) yv -> DN.T u y
volumeVectorEuclidean = volumeAux Ana.volumeVectorEuclidean

{- |
Volume based on Sum norm.
-}
{-# INLINE volumeVectorSum #-}
volumeVectorSum :: (Ind.C w, NormedSum.C y yv, Field.C y, Dim.C u) =>
   w (SigA.S u y) yv -> DN.T u y
volumeVectorSum = volumeAux Ana.volumeVectorSum


{-# INLINE volumeAux #-}
volumeAux :: (Ind.C w, Ring.C y, Dim.C u) =>
   (Sig.T yv -> y) -> w (SigA.S u y) yv -> DN.T u y
volumeAux vol x =
   vol (SigA.samples x) *& SigA.amplitude x


{- * Miscellaneous -}

{- |
Requires finite length.
This is identical to the arithmetic mean.
-}
{-# INLINE directCurrentOffset #-}
directCurrentOffset :: (Ind.C w, Field.C y, Dim.C u) =>
   w (SigA.S u y) y -> DN.T u y
directCurrentOffset =
   volumeAux Ana.directCurrentOffset

{-# INLINE rectify #-}
rectify :: (Ind.C w, Hom.C sig, Real.C y) =>
   w sig y -> w sig y
rectify = Ind.processSignal (Hom.unwrappedProcessSamples Ana.rectify)


{- |
Detect thresholds with a hysteresis.
-}
{-# INLINE flipFlopHysteresis #-}
flipFlopHysteresis :: (Ind.C w, Ord y, Field.C y, Dim.C u) =>
   (DN.T u y, DN.T u y) -> Bool ->
   w (SigA.S u y) y -> w SigS.S Bool
--   SigA.R s u y y -> SigS.Binary s
flipFlopHysteresis (lower,upper) start x =
   let l = SigA.toAmplitudeScalar x lower
       h = SigA.toAmplitudeScalar x upper
   in  Ind.processSignal
          (SigS.Cons .
           Ana.flipFlopHysteresis (l,h) start .
           SigA.privateSamples) x


{- * comparison -}

{-# INLINE compare #-}
compare ::
   (Ord y, Field.C y, Dim.C u,
    Module.C y yv, Ord yv) =>
   SigA.R s u y yv -> SigA.R s u y yv -> SigS.R s P.Ordering
compare x y =
   SigS.fromSamples $ Sig.map (uncurry P.compare) $ SigA.samples $ CutD.zip x y

{-# INLINE lessOrEqual #-}
lessOrEqual ::
   (Ord y, Field.C y, Dim.C u,
    Module.C y yv, Ord yv) =>
   SigA.R s u y yv -> SigA.R s u y yv -> SigS.Binary s
lessOrEqual x y =
   P.fmap (<= P.EQ) $ compare x y