
(********************************************************************)
(*                                                                  *)
(*  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 "math.s7i";


const type: complex is new object struct
    var float: re is 0.0;
    var float: im is 0.0;
  end struct;


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


(**
 *  Return a complex number from its real part.
 *)
const func complex: complex (in float: re) is func
  result
    var complex: result is complex.value;
  begin
    result.re := re;
    result.im := 0.0;
  end func;


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


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


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


const func boolean: (ref complex: num1) = (ref complex: num2) is
  return num1.re = num2.re and num1.im = num2.im;


const func boolean: (ref complex: num1) <> (ref complex: num2) is
  return num1.re <> num2.re or num1.im <> num2.im;


(**
 *  Return the given complex number.
 *)
const func complex: + (in complex: num1) is func
  result
    var complex: result is complex.value;
  begin
    result.re := num1.re;
    result.im := num1.im;
  end func;


(**
 *  Return the negated value of a complex number.
 *)
const func complex: - (in complex: num1) is func
  result
    var complex: result is complex.value;
  begin
    result.re := -num1.re;
    result.im := -num1.im;
  end func;


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


(**
 *  Return the sum of two complex numbers.
 *)
const func complex: (in complex: num1) + (in complex: num2) is func
  result
    var complex: result is complex.value;
  begin
    result.re := num1.re + num2.re;
    result.im := num1.im + num2.im;
  end func;


(**
 *  Return the difference of two complex numbers.
 *)
const func complex: (in complex: num1) - (in complex: num2) is func
  result
    var complex: result is complex.value;
  begin
    result.re := num1.re - num2.re;
    result.im := num1.im - num2.im;
  end func;


(**
 *  Return the product of two complex numbers.
 *)
const func complex: (in complex: num1) * (in complex: num2) is func
  result
    var complex: result is complex.value;
  begin
    result.re := num1.re * num2.re - num1.im * num2.im;
    result.im := num1.re * num2.im + num1.im * num2.re;
  end func;


(**
 *  Return the quotient of two complex numbers.
 *)
const func complex: (in complex: num1) / (in complex: num2) is func
  result
    var complex: result is complex.value;
  local
    var float: divisor is 0.0;
  begin
    divisor := num2.re * num2.re + num2.im * num2.im;
    result.re := (num1.re * num2.re + num1.im * num2.im) / divisor;
    result.im := (num1.im * num2.re - num1.re * num2.im) / divisor;
  end func;


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


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


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


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


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


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


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


(**
 *  Return num1 to the power of int2.
 *)
const func complex: (in complex: num1) ** (in integer: int2) is func
  result
    var complex: result is complex.value;
  local
    var float: r is 0.0;
    var float: phi is 0.0;
  begin
    r := abs(num1) ** int2;
    phi := arg(num1) * flt(int2);
    result.re := r * cos(phi);
    result.im := r * sin(phi);
  end func;


(**
 *  Compares two complex numbers in lexicographic order.
 *  Returns -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: num1, in complex: num2) is func
  result
    var integer: result is 0;
  begin
    result := compare(num1.re, num2.re);
    if result = 0 then
      result := compare(num1.im, num2.im);
    end if;
  end func;


(**
 *  Return the hash value of a complex number.
 *)
const func integer: hashCode (in complex: num) is
  return hashCode(num.re) + hashCode(num.im);


(**
 *  Return the conversion of a complex number to a string.
 *)
const func string: str (in complex: num) is func
  result
    var string: result is "";
  begin
    result := str(num.im) <& "i";
    if result[1] = '-' then
      result := str(num.re) &       result;
    else
      result := str(num.re) & "+" & result;
    end if;
  end func;


(**
 *  Return the conversion of a string to a complex.
 *)
const func complex: (attr complex) parse (in string: stri) is func
  result
    var complex: result 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
      result.re := float parse (stri[.. pred(pos)]);
      result.im := float parse (stri[pos .. pred(length(stri))]);
    else
      raise RANGE_ERROR;
    end if;
  end func;


(**
 *  Return the conversion of a complex number to a string.
 *  The numDigits parameter specifies the number of digits
 *  after the decimal point.
 *)
const func string: (in complex: num) digits (in integer: numDigits) is func
  result
    var string: result is "";
  begin
    result := num.im digits numDigits <& "i";
    if result[1] = '-' then
      result := num.re digits numDigits <&        result;
    else
      result := num.re digits numDigits <& "+" <& result;
    end if;
  end func;


enable_io(complex);
