(********************************************************************)
(*                                                                  *)
(*  complex.s7i   Complex support library                           *)
(*  Copyright (C) 2007, 2008  Thomas Mertes                         *)
(*                                                                  *)
(*  This file is part of the Seed7 Runtime Library.                 *)
(*                                                                  *)
(*  The Seed7 Runtime Library is free software; you can             *)
(*  redistribute it and/or modify it under the terms of the GNU     *)
(*  Lesser General Public License as published by the Free Software *)
(*  Foundation; either version 2.1 of the License, or (at your      *)
(*  option) any later version.                                      *)
(*                                                                  *)
(*  The Seed7 Runtime Library is distributed in the hope that it    *)
(*  will be useful, but WITHOUT ANY WARRANTY; without even the      *)
(*  implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR *)
(*  PURPOSE.  See the GNU Lesser General Public License for more    *)
(*  details.                                                        *)
(*                                                                  *)
(*  You should have received a copy of the GNU Lesser General       *)
(*  Public License along with this program; if not, write to the    *)
(*  Free Software Foundation, Inc., 51 Franklin Street,             *)
(*  Fifth Floor, Boston, MA  02110-1301, USA.                       *)
(*                                                                  *)
(********************************************************************)


include "enable_io.s7i";
include "float.s7i";
include "math.s7i";


(**
 *  Complex numbers represented with [[float]] real part and imaginary part.
 *  Complex literals do not exist.
 *)
const type: complex is new object struct
    var float: re is 0.0;
    var float: im is 0.0;
  end struct;


(**
 *  Create a complex number from its real and imaginary part.
 *  @return the created complex number.
 *)
const func complex: complex (in float: re, in float: im) is func
  result
    var complex: aComplex is complex.value;
  begin
    aComplex.re := re;
    aComplex.im := im;
  end func;


(**
 *  Create a complex number from its real part.
 *  @return the created complex number.
 *)
const func complex: complex (in float: re) is func
  result
    var complex: aComplex is complex.value;
  begin
    aComplex.re := re;
    aComplex.im := 0.0;
  end func;


(**
 *  Create a complex number from polar coordinates.
 *  @return the created complex number.
 *)
const func complex: polar (in float: magnitude, in float: angle) is func
  result
    var complex: aComplex is complex.value;
  begin
    aComplex.re := magnitude * cos(angle);
    aComplex.im := magnitude * sin(angle);
  end func;


(**
 *  Convert an [[integer]] to a complex.
 *  @return the result of the conversion.
 *)
const func complex: (attr complex) conv (in integer: re) is func
  result
    var complex: aComplex is complex.value;
  begin
    aComplex.re := flt(re);
    aComplex.im := 0.0;
  end func;


(**
 *  Conversion a [[float]] to a complex.
 *  @return the result of the conversion.
 *)
const func complex: (attr complex) conv (in float: re) is func
  result
    var complex: aComplex is complex.value;
  begin
    aComplex.re := re;
    aComplex.im := 0.0;
  end func;


(**
 *  Check if two complex numbers are equal.
 *  @return TRUE if both numbers are equal, FALSE otherwise.
 *)
const func boolean: (ref complex: number1) = (ref complex: number2) is
  return number1.re = number2.re and number1.im = number2.im;


(**
 *  Check if two complex numbers are not equal.
 *  @return FALSE if both numbers are equal, TRUE otherwise.
 *)
const func boolean: (ref complex: number1) <> (ref complex: number2) is
  return number1.re <> number2.re or number1.im <> number2.im;


(**
 *  Plus sign for complex numbers.
 *  @return its operand unchanged.
 *)
const func complex: + (in complex: number) is
  return number;


(**
 *  Minus sign, negate a complex number.
 *  @return the negated value of the number.
 *)
const func complex: - (in complex: number) is func
  result
    var complex: negatedNumber is complex.value;
  begin
    negatedNumber.re := -number.re;
    negatedNumber.im := -number.im;
  end func;


(**
 *  Compute the conjugated value of a complex number.
 *  @return the complex conjugate of the number.
 *)
const func complex: conj (in complex: number) is func
  result
    var complex: conjugate is complex.value;
  begin
    conjugate.re := number.re;
    conjugate.im := -number.im;
  end func;


(**
 *  Add two complex numbers.
 *  @return the sum of the two numbers.
 *)
const func complex: (in complex: summand1) + (in complex: summand2) is func
  result
    var complex: sum is complex.value;
  begin
    sum.re := summand1.re + summand2.re;
    sum.im := summand1.im + summand2.im;
  end func;


(**
 *  Compute the subtraction of two complex numbers.
 *  @return the difference of the two numbers.
 *)
const func complex: (in complex: minuend) - (in complex: subtrahend) is func
  result
    var complex: difference is complex.value;
  begin
    difference.re := minuend.re - subtrahend.re;
    difference.im := minuend.im - subtrahend.im;
  end func;


(**
 *  Multiply two complex numbers.
 *  @return the product of the two numbers.
 *)
const func complex: (in complex: factor1) * (in complex: factor2) is func
  result
    var complex: product is complex.value;
  begin
    product.re := factor1.re * factor2.re - factor1.im * factor2.im;
    product.im := factor1.re * factor2.im + factor1.im * factor2.re;
  end func;


(**
 *  Copmpute the division of two complex numbers.
 *  @return the quotient of the division.
 *)
const func complex: (in complex: dividend) / (in complex: divisor) is func
  result
    var complex: quotient is complex.value;
  local
    var float: common_divisor is 0.0;
  begin
    common_divisor := divisor.re * divisor.re + divisor.im * divisor.im;
    quotient.re := (dividend.re * divisor.re + dividend.im * divisor.im) / common_divisor;
    quotient.im := (dividend.im * divisor.re - dividend.re * divisor.im) / common_divisor;
  end func;


(**
 *  Increment a complex number by a delta.
 *)
const proc: (inout complex: number) +:= (in complex: delta) is func
  begin
    number.re +:= delta.re;
    number.im +:= delta.im;
  end func;


(**
 *  Decrement a complex number by a delta.
 *)
const proc: (inout complex: number) -:= (in complex: delta) is func
  begin
    number.re -:= delta.re;
    number.im -:= delta.im;
  end func;


(**
 *  Multiply a complex number by a factor and assign the result back to number.
 *)
const proc: (inout complex: number) *:= (in complex: factor) is func
  local
    var float: real_part is 0.0;
  begin
    real_part := number.re * factor.re - number.im * factor.im;
    number.im := number.re * factor.im + number.im * factor.re;
    number.re := real_part;
  end func;


(**
 *  Divide a complex number by a divisor and assign the result back to number.
 *)
const proc: (inout complex: number) /:= (in complex: divisor) is func
  local
    var float: common_divisor is 0.0;
    var float: real_part is 0.0;
  begin
    common_divisor := divisor.re * divisor.re + divisor.im * divisor.im;
    real_part := (number.re * divisor.re + number.im * divisor.im) / common_divisor;
    number.im := (number.im * divisor.re - number.re * divisor.im) / common_divisor;
    number.re := real_part;
  end func;


(**
 *  Compute the absolute value of a complex number.
 *  @return the absolute value.
 *)
const func float: abs (in complex: number) is
  return sqrt(number.re ** 2 + number.im ** 2);


(**
 *  Compute the square of the absolute value of a complex number.
 *  @return the square of the absolute value.
 *)
const func float: sqrAbs (in complex: number) is
  return number.re ** 2 + number.im ** 2;


(**
 *  Compute the argument of a complex number.
 *  This is the angle of the polar form of the complex number.
 *  @return the argument of the complex number.
 *)
const func float: arg (in complex: number) is
  return atan2(number.im, number.re);


(**
 *  Compute the exponentiation of a complex base with an [[integer]] exponent.
 *  @return the result of the exponentation.
 *)
const func complex: (in complex: base) ** (in integer: exponent) is func
  result
    var complex: power is complex.value;
  local
    var float: r is 0.0;
    var float: phi is 0.0;
  begin
    r := abs(base) ** exponent;
    phi := arg(base) * flt(exponent);
    power.re := r * cos(phi);
    power.im := r * sin(phi);
  end func;


(**
 *  Compare two complex numbers in lexicographic order.
 *  @return -1, 0 or 1 if the first argument is considered to be
 *          respectively less than, equal to, or greater than the
 *          second.
 *)
const func integer: compare (in complex: number1, in complex: number2) is func
  result
    var integer: signumValue is 0;
  begin
    signumValue := compare(number1.re, number2.re);
    if signumValue = 0 then
      signumValue := compare(number1.im, number2.im);
    end if;
  end func;


(**
 *  Compute the hash value of a complex number.
 *  @return the hash value.
 *)
const func integer: hashCode (in complex: num) is
  return hashCode(num.re) mod 16#40000000 + hashCode(num.im) mod 16#40000000;


(**
 *  Convert a complex number to a [[string]].
 *  The number is converted to a string with real and imaginary
 *  part (e.g.: "1.5-2.8i"). Real and imaginary part are separated
 *  with a + or - sign.
 *  @return the string result of the conversion.
 *  @exception MEMORY_ERROR Not enough memory to represent the result.
 *)
const func string: str (in complex: number) is func
  result
    var string: stri is "";
  begin
    stri := str(number.im) <& "i";
    if stri[1] = '-' then
      stri := str(number.re) &       stri;
    else
      stri := str(number.re) & "+" & stri;
    end if;
  end func;


(**
 *  Convert a [[string]] to a complex number.
 *  The string must contain real and imaginary part (e.g.: "1.5-2.8i").
 *  Real and imaginary part must be separated with a + or - sign.
 *  @return the complex result of the conversion.
 *  @exception RANGE_ERROR When stri contains not a valid complex value.
 *)
const func complex: (attr complex) parse (in string: stri) is func
  result
    var complex: aComplex is complex.value;
  local
    var integer: pos is 0;
    var integer: pos2 is 0;
  begin
    pos := rpos(stri, '+');
    pos2 := rpos(stri, '-');
    if pos2 > pos then
      pos := pos2;
    end if;
    if pos <> 0 and stri[length(stri)] = 'i' then
      aComplex.re := float parse (stri[.. pred(pos)]);
      aComplex.im := float parse (stri[pos .. pred(length(stri))]);
    else
      raise RANGE_ERROR;
    end if;
  end func;


(**
 *  Convert a complex to a [[string]] in decimal fixed point notation.
 *  The number is converted to a string with real and imaginary
 *  part (e.g.: "1.5-2.8i"). Real and imaginary part are separated
 *  with a + or - sign. The ''precision'' parameter specifies the number
 *  of digits after the decimal point of the real and imaginary part.
 *  When the ''precision'' is zero the decimal point is omitted.
 *   complex(3.1415) digits 2   returns "3.14+0.00i"
 *  @return the string result of the conversion.
 *  @exception RANGE_ERROR When the ''precision'' is negative.
 *  @exception MEMORY_ERROR Not enough memory to represent the result.
 *)
const func string: (in complex: number) digits (in integer: precision) is func
  result
    var string: stri is "";
  begin
    stri := number.im digits precision <& "i";
    if stri[1] = '-' then
      stri := number.re digits precision <&        stri;
    else
      stri := number.re digits precision <& "+" <& stri;
    end if;
  end func;


(**
 *  Convert a complex to a [[string]] in scientific notation.
 *  The number is converted to a string with real and imaginary
 *  part (e.g.: "1.5e+2-2.8e+0i"). Real and imaginary part are separated
 *  with a + or - sign. The ''precision'' parameter specifies the number
 *  of digits after the decimal point of the real and imaginary part.
 *  When the ''precision'' is zero the decimal point is omitted.
 *   complex(31415.9, 27182.8) sci 4   returns "3.1416e+4+2.7183e+4i"
 *  @return the string result of the conversion.
 *  @exception RANGE_ERROR When the ''precision'' is negative.
 *  @exception MEMORY_ERROR Not enough memory to represent the result.
 *)
const func string: (in complex: number) sci (in integer: precision) is func
  result
    var string: stri is "";
  begin
    stri := number.im sci precision <& "i";
    if stri[1] = '-' then
      stri := number.re sci precision <&        stri;
    else
      stri := number.re sci precision <& "+" <& stri;
    end if;
  end func;


enable_io(complex);