#include <math.h>
#include <iostream>
#include <stdlib.h>
#include "hcomplex.hpp"
using namespace std;


HComplex::HComplex(int _type){
  type = _type; 
}

HComplex::HComplex(int _type, double _x, double _y){
  type = _type; 
  x = _x;
  y = _y;
}


void HComplex::set(int _type, double _x, double _y){
  type = _type; 
  x = _x;
  y = _y;
}
void HComplex::set(double _x, double _y){
  x = _x;
  y = _y;
}


//void HypToCart2(HPoint& m, double r, double d){
void HypToCart2(HComplex& m, double r, double d){
  double d2 = d / 2.0;   
  double k = (1/cos(d2)) - tan(d2);
  double rd2 = r + d2;
  m.x = k * cos(rd2);
  m.y = k * sin(rd2);
}

//void HypToCart(HCoord& h, HPoint& m){
void HypToCart(HComplex& h, HComplex& m){
  HypToCart2(m, h.x, h.y);
}

//void AffixToCart(HAffix& z, HPoint&m){
// OK if z == m
void AffixToCart(HComplex& z, HComplex& m){
  double r = z.x;
  double t = z.y;
  m.x = r * cos(t);
  m.y = r * sin(t);
}

//void CartToAffix(HPoint& m, HAffix& z){
// OK if z == m
void CartToAffix(HComplex& m, HComplex& z){
  double r = sqrt(m.x * m.x + m.y * m.y);
  double x = (r)? m.x / r : 0;
  z.x = r;

  if (m.y > 0) z.y = acos(x);
  else z.y = -acos(x);
  //if (x) z.t = atan(m.y/x);
  //else z.t = atan(m.y/0.000000001);
}


void AddToModule(HComplex& s, double rplus){
  HComplex datapole;
  HComplex datacart;

  switch(s.type){
  case COORDHYP :
    HypToCart(s, datacart);
    CartToAffix(datacart, datapole);
    break;

  case COORDPOL :
    datapole.set(s.x, s.y);
    break;

  case COORDCART : 
    CartToAffix(s, datapole);
    break;
  }

  datapole.x += (datapole.x >=0) ? rplus : -rplus;
  AffixToCart(datapole, s);
}


void AddComplex(HComplex& res, HComplex& a, HComplex& b){
  HComplex a2, b2;
  
  switch (a.type){
  case COORDPOL :
    AffixToCart(a, a2);
    break;

  case COORDCART :
    a2 = a;
    break;

  case COORDHYP :
    HypToCart(a, a2);
    break;
  }
  
  switch (b.type){
  case COORDPOL :
    AffixToCart(b, b2);
    break;

  case COORDCART :
    b2 = b;
    break;

  case COORDHYP :
    HypToCart(b, b2);
    break;
  }
  
  res.type = COORDCART;
  res.x = a2.x + b2.x;
  res.y = a2.y + b2.y;
}


void AddComplexReal(HComplex& res, HComplex& c, double r){
  //HPoint m;
  HComplex m;

  if (c.type == COORDCART){
    m.x = c.x;
    m.y = c.y;
  }

  if (c.type == COORDPOL){
    AffixToCart(c, m);
  }

  res.type = COORDCART;
  res.x = r + m.x;
  res.y = m.y;
}


void MulComplexReal(HComplex& res, HComplex& c, double r){
  if (c.type == COORDCART){
    res.type = COORDCART;
    res.x = r * c.x;
    res.y = r * c.y;
  }

  if (c.type == COORDPOL){
    res.type = COORDPOL;
    res.x = r * c.x;
   }
}

extern void InvSignComplex(HComplex& res, HComplex& c){
  if (c.type == COORDCART){
    res.type = COORDCART;
    res.x = -c.x;
    res.y = -c.y;
  }

  if (c.type == COORDPOL){
    res.type = COORDPOL;
    res.y = c.y +H_PI;
  }
}

void ConjugateComplex(HComplex& res, HComplex& c){
  if (c.type == COORDCART){
    res.type = COORDCART;
    res.x = c.x;
    res.y = -c.y;
  }

  if (c.type == COORDPOL){
    res.type = COORDPOL;
    res.y = -c.y;
  }
}

void MulComplex(HComplex& res, HComplex& a, HComplex& b){
  HComplex a2, b2;

  switch (a.type){
  case COORDPOL : 
    AffixToCart(a, a2);
    break;

  case COORDCART : 
    a2 = a;
    break;

  case COORDHYP :
    HypToCart(a, a2);
    break;
  }

  switch (b.type){
  case COORDPOL : 
    AffixToCart(b, b2);
    break;

  case COORDCART : 
    b2 = b;
    break;

  case COORDHYP : 
    HypToCart(b, b2);
    break;
  }
   
   res.type = COORDCART;
   res.x =  a2.x * b2.x - a2.y * b2.y;
   res.y =  a2.x * b2.y + b2.x * a2.y;
   //((HAffix*)res.data).r = pa.r;
   //((HAffix*)res.data).r = pa.r * pb.r;
   //((HAffix*)res.data).t = pa.t;
   //((HAffix*)res.data).t = pa.t + pb.t;
}


void InvComplex(HComplex& res, HComplex& c){
   HComplex c2;
   double   denom;

   switch (c.type){
   case COORDPOL : 
     AffixToCart(c, c2);
     break;
     
   case COORDCART :
     c2 = c;
     break;
    
   case COORDHYP :
     HypToCart(c, c2);
     break;
   }

   res.type = COORDCART;
   denom = c2.x * c2.x - c2.y * c2.y;
   res.x = c2.x / denom;
   res.y = c2.y / denom;
}


double ComplexNorm(HComplex& c){
  HComplex c2;
  
  switch (c.type){
  case COORDPOL : 
    return c.x > 0 ? c.x : -c.x;
    
  case COORDCART :
    c2 = c;
    break;

  case COORDHYP :
    HypToCart(c, c2);
    break;
   }

   return sqrt((c2.x * c2.x) + (c2.y * c2.y));
}

extern double ComplexNorm2(HComplex& c){
  HComplex c2;

  switch (c.type){
  case COORDPOL : 
    return (c.x * c.x);

  case COORDCART : 
    c2 = c;
    break;

  case COORDHYP :
    HypToCart(c, c2);
    break;
   }

   return ( c2.x * c2.x) + (c2.y * c2.y);
}


double InnerProdComplex(HComplex& a, HComplex& b){
  HComplex a2, b2;

  switch (a.type){
  case COORDPOL :
    AffixToCart(a, a2);
    break;

  case COORDCART :
    a2 = a;
    break;

  case COORDHYP :
    HypToCart(a, a2);
    break;
   }

  switch (b.type){
  case COORDPOL :
    AffixToCart(b, b2);
    break;

  case COORDCART :
    b2 = b;
    break;

  case COORDHYP :
    HypToCart(b, b2);
    break;
   }

   //MulComplexReal(&a_, &a_, 1.0/ComplexNorm(&a_));
   //MulComplexReal(&b_, &b_, 1.0/ComplexNorm(&b_));
   return a2.x * b2.x + b2.y * a2.y;
}

/*
extern void InvComplex(HComplex* res, HComplex* c){
   sComplex c_;
   sAffix   cc;

   c_.data = (void*)&cc;

   if (c.type == COORDPOL) CopyComplex(c, &c_);
   else CartToAffix((HPoint*)c.data, &cc);

   res.type = COORDPOL;
   ((HAffix*)res.data).r = 1.0/cc.r;
   ((HAffix*)res.data).t = -cc.t;
}
*/
