{-# LANGUAGE TypeOperators, TypeSynonymInstances, FlexibleInstances #-}
{-# OPTIONS -fno-warn-orphans #-}
-- | Strict complex doubles.
module Data.Array.Repa.Algorithms.Complex
        ( Complex
        , mag
        , arg)
where


-- | Complex doubles.
type Complex 
        = (Double, Double)

instance Num Complex where

  {-# INLINE abs #-}
  abs x                 = (mag x, 0)

  {-# INLINE signum #-}
  signum (re, _)        = (signum re, 0)

  {-# INLINE fromInteger #-}
  fromInteger n         = (fromInteger n, 0.0)

  {-# INLINE (+) #-}
  (r, i) + (r', i')     = (r+r', i+i')

  {-# INLINE (-) #-}
  (r, i) - (r', i')     = (r-r', i-i')

  {-# INLINE (*) #-}
  (r, i) * (r', i')     = (r*r' - i*i', r*i' + r'*i)


instance Fractional Complex where
  {-# INLINE (/) #-}
  (a, b) / (c, d)               
        = let   den     = c^(2 :: Int) + d^(2 :: Int)
                re      = (a * c + b * d) / den
                im      = (b * c - a * d) / den
          in    (re, im)
        
  fromRational x        = (fromRational x, 0)
        
-- | Take the magnitude of a complex number.
mag :: Complex -> Double
{-# INLINE mag #-}
mag (r, i)      = sqrt (r * r + i * i)


-- | Take the argument (phase) of a complex number, in the range [-pi .. pi].
arg :: Complex -> Double
{-# INLINE arg #-}
arg (re, im)
 = normaliseAngle $ atan2 im re

 where  normaliseAngle :: Double -> Double
        normaliseAngle f
         | f < - pi     
         = normaliseAngle (f + 2 * pi)
        
         | f > pi
         = normaliseAngle (f - 2 * pi)

         | otherwise
         = f
