#include<math.h>
#include<stdio.h>
#include<stdlib.h>

typedef struct {
  double real;
  double imaginary;
} complexNumber;

void printComplexNumber(complexNumber *c);
void multiplyComplex(const complexNumber *x, const complexNumber *y, complexNumber *z);
complexNumber multiplyComplexByValue(complexNumber x, complexNumber y);
complexNumber * multiplyComplexVersionTwo(const complexNumber *x, const complexNumber *y);
void quadraticRoots(double a, double b, double c, complexNumber *r1, complexNumber *r2);
complexNumber * disc(double a, double b, double c);

int main(int argc, char *argv[])
{
  complexNumber z, z2;
  complexNumber x;
  complexNumber *ptr_z = NULL;
  x.real = 0;
  x.imaginary = 2;
  complexNumber y;
  y.real = 1;
  y.imaginary = 1;
  printf("x = ");
  printComplexNumber(&x);
  printf("\n");
  printf("y = ");
  printComplexNumber(&y);
  printf("\n");

  /* version 1, all three passed by reference */
  multiplyComplex(&x,&y,&z);
  printf("z = x * y = ");
  printComplexNumber(&z);
  printf("\n");

  /* version 1.5, all three by value: BAD */
  z2 = multiplyComplexByValue(x,y);
  printf("z2 = x * y = ");
  printComplexNumber(&z2);
  printf("   (by value)\n");

  /* version 2, x and y passed by reference, pointer returned */
  ptr_z = multiplyComplexVersionTwo(&x,&y);
  printf("ptr_z = x * y = ");
  printComplexNumber(ptr_z);
  printf("\n");

  double a = 2.0, b = 1.0, c = 2.0;
  complexNumber r1, r2;
  quadraticRoots(a, b, c, &r1, &r2);
  printf("Roots of (%f)x^2 + (%f)x + (%f) are: \n", a, b, c);
  printf(" r1 = ");
  printComplexNumber(&r1);
  printf("\n");
  printf(" r2 = ");
  printComplexNumber(&r2);
  printf("\n");

  /* another, with real roots */
  a = 1.0;
  b = 4.0;
  c = 2.0;
  quadraticRoots(a, b, c, &r1, &r2);
  printf("Roots of (%f)x^2 + (%f)x + (%f) are: \n", a, b, c);
  printf(" r1 = ");
  printComplexNumber(&r1);
  printf("\n");
  printf(" r2 = ");
  printComplexNumber(&r2);
  printf("\n");


  return 0;
}

void printComplexNumber(complexNumber *c)
{
  printf("%f + %fi", c->real, c->imaginary);
}

void multiplyComplex(const complexNumber *x, const complexNumber *y, complexNumber *z)
{
  /**
   *  We pass x, y with the const modifier: a promise that we will not make
   *  changes to x or y in this function.  If, say we did:
   *  y->real = 2.0;
   *  the compiler would detect this and give an error.
   */
   z->real = x->real * y->real - x->imaginary * y->imaginary;
   z->imaginary = x->imaginary * y->real + x->real * y->imaginary;
   return;
}

complexNumber multiplyComplexByValue(complexNumber x, complexNumber y)
{

  complexNumber z;
  z.real = x.real * y.real - x.imaginary * y.imaginary;
  z.imaginary = x.imaginary * y.real + x.real * y.imaginary;
  return z; 
}

complexNumber * multiplyComplexVersionTwo(const complexNumber *x, const complexNumber *y)
{
  complexNumber *z = NULL;
  z = (complexNumber *)malloc(sizeof(complexNumber));
  z->real = x->real * y->real - x->imaginary * y->imaginary;
  z->imaginary = x->imaginary * y->real + x->real * y->imaginary;
  return z;
}

complexNumber * disc(double a, double b, double c)
{
  complexNumber *z = NULL;
  z = (complexNumber *)malloc(sizeof(complexNumber));
  double d = b*b - 4*a*c;

  if(d >= 0)
  {
    z->real = sqrt(d);
    z->imaginary = 0;
  }
  else
  {
    z->real = 0;
    z->imaginary = sqrt(fabs(d));
  }
  return z;
}

void quadraticRoots(double a, double b, double c, complexNumber *r1, complexNumber *r2)
{
  complexNumber *D = disc(a,b,c);

  r1->real = (-b + D->real) / (2*a);
  r1->imaginary = D->imaginary / (2*a);

  r2->real = (-b - D->real) / (2*a);
  r2->imaginary = -D->imaginary / (2*a);

  return;

}

