/*
   Copyright 2010 Aaron J. Radke

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

       http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.
*/
package cc.drx

object Complex{
   def angle(a:Angle):Complex = new Complex(a.cos,  a.sin)
   //TODO use apply instead of polar for a more type inference based api
   def angle(mag:Double,a:Angle):Complex = new Complex(a.cos*mag,  a.sin*mag)
   val i = new Complex(0d,1d)
   val r = new Complex(1d,0d)
}
case class Complex(r:Double,i:Double){

  def toVec = Vec(r,i)

  //---complex ops
  def +(b:Complex):Complex = Complex(r+b.r, i+b.i)
  def -(b:Complex):Complex = Complex(r-b.r, i-b.i)

  def *(b:Complex):Complex = Complex(r*b.r - i*b.i,  i*b.r + r*b.i)
  def /(b:Complex):Complex = if(b.r == 0 && b.i == 0) Complex(0,0)/*TODO throw error*/ else {
                           val  den = b.normSq
                           val rnum = r*b.r + i*b.i
                           val inum = i*b.r - r*b.i
                           Complex(rnum/den, inum/den)
                         }
  //---scalar ops
  /**negate the vector*/
  def unary_- :Complex   = new Complex(-r , -i)

  //TODO should we supply an implicit double to complex?? it should promote just like int, goes to double, double goes to complex
  def +(v:Double):Complex = Complex(r+v, i)
  def -(v:Double):Complex = Complex(r-v, i)
  def *(s:Double):Complex = new Complex(r*s, i*s)
  def /(s:Double):Complex = new Complex(r/s, i/s)

  2017-07-30("use normSq instead", "0.2.13")
  lazy val norm2:Double = normSq
  def normSq:Double = r*r + i*i
  def norm:Double = normSq.sqrt
  def abs:Double = norm

  def conj = Complex(r,-i)
  def sqrt:Complex = if(i == 0){
    if(r < 0) Complex(0,r.abs.sqrt)
    else Complex(r.sqrt,0)
  } else {
    val d = norm/2
    val a = r/2
    val gamma =  (d + a).sqrt
    val delta =  i.sgn * (d - a).sqrt
    Complex(gamma,delta)
  }
  def exp:Complex = Complex(i.cos, i.sin) * r.exp
  def arg:Double = i atan2 r
  def log:Complex= Complex(r.log, arg)
  def phase:Angle = Angle(arg)

   def dist(that:Complex):Double = (that-this).norm
}