{**************************************************************************************************}
{                                                                                                  }
{ Project JEDI Code Library (JCL)                                                                  }
{                                                                                                  }
{ The contents of this file are subject to the Mozilla Public License Version 1.1 (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.mozilla.org/MPL/                                                           }
{                                                                                                  }
{ Software distributed under the License is distributed on an "AS IS" basis, WITHOUT WARRANTY OF   }
{ ANY KIND, either express or implied. See the License for the specific language governing rights  }
{ and limitations under the License.                                                               }
{                                                                                                  }
{ The Original Code is JclComplex.pas.                                                             }
{                                                                                                  }
{ The Initial Developer of the Original Code is Alexei Koudinov. Portions created by               }
{ Alexei Koudinov are Copyright (C) of Alexei Koudinov. All Rights Reserved.                       }                         
{                                                                                                  }
{ Contributor(s):                                                                                  }
{   Marcel van Brakel                                                                              }
{   Alexei Koudinov                                                                                }
{   Robert Marquardt (marquardt)                                                                   }
{   Robert Rossmair (rrossmair)                                                                    }
{   Matthias Thoma  (mthoma)                                                                       }
{   Petr Vones (pvones)                                                                            }        
{                                                                                                  }
{**************************************************************************************************}
{                                                                                                  }
{ Class for working with complex numbers.                                                          }
{                                                                                                  }
{**************************************************************************************************}
{                                                                                                  }
{ Last modified: $Date::                                                                         $ }
{ Revision:      $Rev::                                                                          $ }
{ Author:        $Author::                                                                       $ }
{                                                                                                  }
{**************************************************************************************************}

unit JclComplex;

{$I jcl.inc}

interface

uses
  {$IFDEF UNITVERSIONING}
  JclUnitVersioning,
  {$ENDIF UNITVERSIONING}
  {$IFDEF HAS_UNITSCOPE}
  System.SysUtils,
  {$ELSE ~HAS_UNITSCOPE}
  SysUtils,
  {$ENDIF ~HAS_UNITSCOPE}
  JclBase, JclMath, JclResources, JclStrings;

const
  TComplex_VERSION = 5.01;

type
  TComplexKind = (crRectangular, crPolar);

  TCoords = record
    X: Float;     // rectangular real
    Y: Float;     // rectangular imaginary
    R: Float;     // polar radius
    Theta: Float; // polar angle
  end;

  TRectCoord = record
    X: Float;
    Y: Float;
  end;

  TJclComplex = class(TObject)
  private   {z = x + yi}
    FCoord: TCoords;
    FFracLen: Byte;
    function MiscalcFloat(const X: Float): Float;
    procedure MiscalcComplex; // eliminates miscalculation
    procedure FillCoords(const ComplexType: TComplexKind);
    function GetRectangularString: string;
    function GetPolarString: string;
    procedure SetRectangularString(StrToParse: string);
    procedure SetPolarString(StrToParse: string);
    procedure SetFracLen(const X: Byte);
    function GetRadius: Float;
    function GetAngle: Float;
    function NormalizeAngle(const Value: Float): Float;
  protected
    function Assign(const Coord: TCoords; const ComplexType: TComplexKind = crRectangular): TJclComplex; overload;
    function CoreAdd(const First, Second: TRectCoord): TRectCoord;
    function CoreDiv(const First, Second: TRectCoord): TRectCoord;
    function CoreMul(const First, Second: TRectCoord): TRectCoord;
    function CoreSub(const First, Second: TRectCoord): TRectCoord;
    function CoreLn (const LnValue: TRectCoord): TRectCoord;
    function CoreExp(const ExpValue: TRectCoord): TRectCoord;
    function CorePwr(First, Second, Polar: TRectCoord): TRectCoord;
    function CoreIntPwr(First: TRectCoord; const Polar: TRectCoord; const Pwr: Integer): TRectCoord;
    function CoreRealPwr(First: TRectCoord; const Polar: TRectCoord; const Pwr: Float): TRectCoord;
    function CoreRoot(First: TRectCoord; const Polar: TRectCoord; const K, N: Word): TRectCoord;
    function CoreCos(const Value: TRectCoord): TRectCoord;
    function CoreSin(const Value: TRectCoord): TRectCoord;
    function CoreTan(const Value: TRectCoord): TRectCoord;
    function CoreCot(const Value: TRectCoord): TRectCoord;
    function CoreSec(const Value: TRectCoord): TRectCoord;
    function CoreCsc(const Value: TRectCoord): TRectCoord;
    function CoreCosH(const Value: TRectCoord): TRectCoord;
    function CoreSinH(const Value: TRectCoord): TRectCoord;
    function CoreTanH(const Value: TRectCoord): TRectCoord;
    function CoreCotH(const Value: TRectCoord): TRectCoord;
    function CoreSecH(const Value: TRectCoord): TRectCoord;
    function CoreCscH(const Value: TRectCoord): TRectCoord;
    function CoreI0(const Value: TRectCoord): TRectCoord;
    function CoreJ0(const Value: TRectCoord): TRectCoord;
    function CoreApproxLnGamma(const Value: TRectCoord): TRectCoord;
    function CoreLnGamma(Value: TRectCoord): TRectCoord;
    function CoreGamma(const Value: TRectCoord): TRectCoord;
  public
    //----------- constructors
    constructor Create; overload;
    constructor Create(const X, Y: Float; const ComplexType: TComplexKind = crRectangular); overload;

    //----------- complex numbers assignment routines
    function Assign(const X, Y: Float; const ComplexType: TComplexKind = crRectangular): TJclComplex; overload;
    function AssignZero: TJclComplex;
    function AssignOne: TJclComplex;
    function Duplicate: TJclComplex;

    //----------- arithmetics -- modify the object itself
    function CAdd(const AddValue: TJclComplex): TJclComplex; overload;
    function CAdd(const X, Y: Float; const ComplexType: TComplexKind = crRectangular): TJclComplex; overload;
    function CDiv(const DivValue: TJclComplex): TJclComplex; overload;
    function CDiv(const X, Y: Float; const ComplexType: TComplexKind = crRectangular): TJclComplex; overload;
    function CMul(const MulValue: TJclComplex): TJclComplex; overload;
    function CMul(const X, Y: Float; const ComplexType: TComplexKind = crRectangular): TJclComplex; overload;
    function CSub(const SubValue: TJclComplex): TJclComplex; overload;
    function CSub(const X, Y: Float; const ComplexType: TComplexKind = crRectangular): TJclComplex; overload;
    function CNeg: TJclComplex;
    function CConjugate: TJclComplex;

    //----------- arithmetics -- creates new resulting object
    function CNewAdd(const AddValue: TJclComplex): TJclComplex; overload;
    function CNewAdd(const X, Y: Float; const ComplexType: TComplexKind = crRectangular): TJclComplex; overload;
    function CNewDiv(const DivValue: TJclComplex): TJclComplex; overload;
    function CNewDiv(const X, Y: Float; const ComplexType: TComplexKind = crRectangular): TJclComplex; overload;
    function CNewMul(const MulValue: TJclComplex): TJclComplex; overload;
    function CNewMul(const X, Y: Float; const ComplexType: TComplexKind = crRectangular): TJclComplex; overload;
    function CNewSub(const SubValue: TJclComplex): TJclComplex; overload;
    function CNewSub(const X, Y: Float; const ComplexType: TComplexKind = crRectangular): TJclComplex; overload;
    function CNewNeg: TJclComplex;
    function CNewConjugate: TJclComplex;

    //----------- natural log and exponential functions -- modify the object itself
    function CLn: TJclComplex;
    function CExp: TJclComplex;
    function CPwr(const PwrValue: TJclComplex): TJclComplex; overload;
    function CPwr(const X, Y: Float; const ComplexType: TComplexKind = crRectangular): TJclComplex; overload;
    function CIntPwr(const Pwr: Integer): TJclComplex;
    function CRealPwr(const Pwr: Float): TJclComplex;
    function CRoot(const K, N: Word): TJclComplex;
    function CSqrt: TJclComplex;

    //----------- natural log and exponential functions -- create new resulting object
    function CNewLn: TJclComplex;
    function CNewExp: TJclComplex;
    function CNewPwr(PwrValue: TJclComplex): TJclComplex; overload;
    function CNewPwr(const X, Y: Float; const ComplexType: TComplexKind = crRectangular): TJclComplex; overload;
    function CNewIntPwr(const Pwr: Integer): TJclComplex;
    function CNewRealPwr(const Pwr: Float): TJclComplex;
    function CNewRoot(const K, N: Word): TJclComplex;
    function CNewSqrt: TJclComplex;

    //----------- trigonometric functions -- modify the object itself
    function CCos: TJclComplex;
    function CSin: TJclComplex;
    function CTan: TJclComplex;
    function CCot: TJclComplex;
    function CSec: TJclComplex;
    function CCsc: TJclComplex;

    //----------- trigonometric functions -- create new resulting object
    function CNewCsc: TJclComplex;
    function CNewCos: TJclComplex;
    function CNewSin: TJclComplex;
    function CNewTan: TJclComplex;
    function CNewCot: TJclComplex;
    function CNewSec: TJclComplex;

    //----------- complex hyperbolic functions -- modify the object itself
    function CCosH: TJclComplex;
    function CSinH: TJclComplex;
    function CTanH: TJclComplex;
    function CCotH: TJclComplex;
    function CSecH: TJclComplex;
    function CCscH: TJclComplex;

    //----------- complex hyperbolic functions -- create new resulting object
    function CNewCosH: TJclComplex;
    function CNewSinH: TJclComplex;
    function CNewTanH: TJclComplex;
    function CNewCotH: TJclComplex;
    function CNewSecH: TJclComplex;
    function CNewCscH: TJclComplex;

    //----------- complex Bessel functions of order zero -- modify the object itself
    function CI0: TJclComplex;
    function CJ0: TJclComplex;

    //----------- complex Bessel functions of order zero -- create new resulting object
    function CNewI0: TJclComplex;
    function CNewJ0: TJclComplex;

    //----------- complex Gamma functions -- modify the object itself
    function CApproxLnGamma: TJclComplex;
    function CLnGamma: TJclComplex;
    function CGamma: TJclComplex;

    //----------- complex Gamma functions -- create new resulting object
    function CNewApproxLnGamma: TJclComplex;
    function CNewLnGamma: TJclComplex;
    function CNewGamma: TJclComplex;

    //----------- miscellaneous routines
    function AbsoluteValue: Float; overload;
    function AbsoluteValue(const Coord: TRectCoord): Float; overload;
    function AbsoluteValueSqr: Float; overload;
    function AbsoluteValueSqr(const Coord: TRectCoord): Float; overload;
    function FormatExtended(const X: Float): string;

    property FracLength: Byte read FFracLen write SetFracLen default 8;

    //----------- getting different parts of the number
    property RealPart: Float read FCoord.X;
    property ImaginaryPart: Float read FCoord.Y;
    property Radius: Float read GetRadius;
    property Angle: Float read GetAngle;

    //----------- format output
    property AsString: string read GetRectangularString write SetRectangularString;
    property AsPolarString: string read GetPolarString write SetPolarString;
  end;

var
  ComplexPrecision: Float = 1E-14;

const
  MaxTerm: Byte = 35;
  EpsilonSqr: Float = 1E-20;

{$IFDEF UNITVERSIONING}
const
  UnitVersioning: TUnitVersionInfo = (
    RCSfile: '$URL$';
    Revision: '$Revision$';
    Date: '$Date$';
    LogPath: 'JCL\source\common';
    Extra: '';
    Data: nil
    );
{$ENDIF UNITVERSIONING}

implementation

const
  MaxFracLen = 18;
  RectOne: TRectCoord = (X: 1.0; Y: 0.0);
  RectZero: TRectCoord = (X: 0.0; Y: 0.0);
  RectInfinity: TRectCoord = (X: Infinity; Y: Infinity);

function Coordinates(const cX, cY: Float; CoordType: TComplexKind): TCoords;
begin
  case CoordType of
    crRectangular:
      begin
        Result.X := cX;
        Result.Y := cY;
        Result.R := 0.0;
        Result.Theta := 0.0;
      end;
    crPolar:
      begin
        Result.X := 0.0;
        Result.Y := 0.0;
        Result.R := cX;
        Result.Theta := cY;
      end;
  end;
end;

function RectCoord(const X, Y: Float): TRectCoord; overload;
begin
  Result.X := X;
  Result.Y := Y;
end;

function RectCoord(Value: TJclComplex): TRectCoord; overload;
begin
  Result.X := Value.FCoord.X;
  Result.Y := Value.FCoord.Y;
end;

//=== { TJclComplex } ========================================================

constructor TJclComplex.Create;
begin
  inherited Create;
  AssignZero;
  FFracLen := MaxFracLen;
end;

constructor TJclComplex.Create(const X, Y: Float; const ComplexType: TComplexKind);
begin
  inherited Create;
  Assign(X, Y, ComplexType);
  FFracLen := MaxFracLen;
end;

procedure TJclComplex.FillCoords(const ComplexType: TComplexKind);
begin
  MiscalcComplex;
  case ComplexType of
    crPolar:
      begin
        FCoord.X := FCoord.R * JclMath.Cos(FCoord.Theta);
        FCoord.Y := FCoord.R * JclMath.Sin(FCoord.Theta);
      end;
    crRectangular:
      if FCoord.X = 0.0 then
      begin
        FCoord.R := Abs(FCoord.Y);
        if FCoord.Y <> 0 then
          FCoord.Theta := PiOn2 * Sgn(FCoord.Y)
        else
          FCoord.Theta := PiOn2; // Fixes corner case where Sgn(0) = 0
      end
      else
      begin
        FCoord.R := AbsoluteValue;
        FCoord.Theta := System.ArcTan(FCoord.Y / FCoord.X);
        if FCoord.X < 0.0 then
        begin
          if FCoord.Y <> 0 then
            FCoord.Theta := FCoord.Theta + Pi * Sgn(FCoord.Y)
          else
            FCoord.Theta := FCoord.Theta + Pi; // Fixes corner case where Sgn(0) = 0
        end;
      end;
  end;
  MiscalcComplex;
end;

function TJclComplex.MiscalcFloat(const X: Float): Float;
begin
  Result := X;
  if Abs(Result) < ComplexPrecision then
    Result := 0.0;
end;

procedure TJclComplex.MiscalcComplex; // eliminates miscalculation
begin
  FCoord.X := MiscalcFloat(FCoord.X);
  FCoord.Y := MiscalcFloat(FCoord.Y);
  FCoord.R := MiscalcFloat(FCoord.R);
  if FCoord.R = 0.0 then
    FCoord.Theta := 0.0
  else
    FCoord.Theta := MiscalcFloat(FCoord.Theta);
end;

function TJclComplex.Assign(const X, Y: Float; const ComplexType: TComplexKind): TJclComplex;
begin
  Result := Assign(Coordinates(X, Y, ComplexType), ComplexType);
end;

function TJclComplex.Assign(const Coord: TCoords; const ComplexType: TComplexKind): TJclComplex;
begin
  FCoord := Coord;
  FillCoords(ComplexType);
  MiscalcComplex;
  Result := Self;
end;

function TJclComplex.AssignZero: TJclComplex;
begin
  Result := Assign(0.0, 0.0, crRectangular);
end;

function TJclComplex.AssignOne: TJclComplex;
begin
  Result := Assign(1.0, 0.0, crRectangular);
end;

function TJclComplex.GetRectangularString: string;
const
  cImaginary = 'i';
begin
  MiscalcComplex;
  if (FCoord.X = 0.0) and (FCoord.Y = 0.0) then
    Result := '0'
  else
  if FCoord.X <> 0.0 then
  begin
    Result := FormatExtended(FCoord.X);
    if FCoord.Y > 0.0 then
      Result := Result + '+'
    else
    if FCoord.Y < 0.0 then
      Result := Result + '-';
    if FCoord.Y <> 0.0 then
      Result := Result + FormatExtended(Abs(FCoord.Y)) + cImaginary;
  end
  else
    Result := FormatExtended(FCoord.Y) + cImaginary;
end;

function TJclComplex.GetPolarString: string;
begin
  FillCoords(crRectangular);
  Result := FormatExtended(FCoord.R) + '*CIS(' + FormatExtended(FCoord.Theta) + ')';
end;

procedure TJclComplex.SetRectangularString(StrToParse: string);
var
  SignPos: Integer;
  RealPart, ImagPart: Float;
begin
  StrToParse := StrRemoveChars(StrToParse, CharIsSpace);
  SignPos := StrFind('+', StrToParse, 2);
  if SignPos = 0 then
    SignPos := StrFind('-', StrToParse, 2);
  if SignPos > 0 then
  begin
    try
      RealPart := StrToFloat(Copy(StrToParse, 1, SignPos - 1));
    except
      raise EJclMathError.CreateRes(@RsComplexInvalidString);
    end;
    try
      ImagPart := StrToFloat(Copy(StrToParse, SignPos, Length(StrToParse) - SignPos));
    except
      raise EJclMathError.CreateRes(@RsComplexInvalidString);
    end;
  end
  else
  begin
    if (StrToParse[Length(StrToParse)] = 'i') or (StrToParse[Length(StrToParse)] = 'I') then
    begin
      RealPart := 0.0;
      try
        ImagPart := StrToFloat(Copy(StrToParse, 1, Length(StrToParse) - 1));
      except
        raise EJclMathError.CreateRes(@RsComplexInvalidString);
      end;
    end
    else
    begin
      try
        RealPart := StrToFloat(StrToParse);
      except
        raise EJclMathError.CreateRes(@RsComplexInvalidString);
      end;
      ImagPart := 0.0;
    end;
  end;
  Assign(RealPart, ImagPart, crRectangular);
end;

procedure TJclComplex.SetPolarString(StrToParse: string);
var
  AstPos: Integer;
  Radius, Angle: Float;
begin
  StrToParse := AnsiUpperCase(StrRemoveChars(StrToParse, CharIsSpace));
  AstPos := Pos('*', StrToParse);
  if AstPos = 0 then
    raise EJclMathError.CreateRes(@RsComplexInvalidString);
  try
    Radius := StrToFloat(StrLeft(StrToParse, AstPos - 1));
  except
    raise EJclMathError.CreateRes(@RsComplexInvalidString);
  end;
  AstPos := Pos('(', StrToParse);
  if AstPos = 0 then
    raise EJclMathError.CreateRes(@RsComplexInvalidString);
  try
    Angle := StrToFloat(Copy(StrToParse, AstPos + 1, Length(StrToParse) - AstPos - 1));
  except
    raise EJclMathError.CreateRes(@RsComplexInvalidString);
  end;
  Assign(Radius, Angle, crPolar);
end;

function TJclComplex.Duplicate: TJclComplex;
begin
  Result := TJclComplex.Create(FCoord.X, FCoord.Y, crRectangular);
  Result.FFracLen := FFracLen;
end;

//=== arithmetics ============================================================

function TJclComplex.CoreAdd(const First, Second: TRectCoord): TRectCoord;
begin
  Result.X := First.X + Second.X;
  Result.Y := First.Y + Second.Y;
end;

function TJclComplex.CAdd(const AddValue: TJclComplex): TJclComplex;
var
  ResCoord: TRectCoord;
begin
  ResCoord := CoreAdd(RectCoord(Self), RectCoord(AddValue));
  FCoord.X := ResCoord.X;
  FCoord.Y := ResCoord.Y;
  Result := Self;
end;

function TJclComplex.CAdd(const X, Y: Float; const ComplexType: TComplexKind): TJclComplex;
var
  NewComplex: TJclComplex;
begin
  NewComplex := TJclComplex.Create(X, Y, ComplexType);
  try
    Result := CAdd(NewComplex);
  finally
    NewComplex.Free;
  end;
end;

function TJclComplex.CNewAdd(const AddValue: TJclComplex): TJclComplex;
var
  ResCoord: TRectCoord;
begin
  ResCoord := CoreAdd(RectCoord(Self), RectCoord(AddValue));
  Result := TJclComplex.Create(ResCoord.X, ResCoord.Y, crRectangular);
  Result.FFracLen := FFracLen;
end;

function TJclComplex.CNewAdd(const X, Y: Float; const ComplexType: TComplexKind): TJclComplex;
var
  NewComplex: TJclComplex;
begin
  NewComplex := TJclComplex.Create(X, Y, ComplexType);
  try
    Result := CNewAdd(NewComplex);
  finally
    NewComplex.Free;
  end;
end;

function TJclComplex.CoreDiv(const First, Second: TRectCoord): TRectCoord;
var
  Denom: Float;
begin
  Denom := Sqr(Second.X) + Sqr(Second.Y);
  Result.X := (First.X * Second.X + First.Y * Second.Y) / Denom;
  Result.Y := (First.Y * Second.X - First.X * Second.Y) / Denom;
end;

function TJclComplex.CDiv(const DivValue: TJclComplex): TJclComplex;
var
  ResCoord: TRectCoord;
begin
  ResCoord := CoreDiv(RectCoord(Self), RectCoord(DivValue));
  FCoord.X := ResCoord.X;
  FCoord.Y := ResCoord.Y;
  Result := Self;
end;

function TJclComplex.CDiv(const X, Y: Float; const ComplexType: TComplexKind): TJclComplex;
var
  NewComplex: TJclComplex;
begin
  NewComplex := TJclComplex.Create(X, Y, ComplexType);
  try
    Result := CDiv(NewComplex);
  finally
    NewComplex.Free;
  end;
end;

function TJclComplex.CNewDiv(const DivValue: TJclComplex): TJclComplex;
var
  ResCoord: TRectCoord;
begin
  ResCoord := CoreDiv(RectCoord(Self), RectCoord(DivValue));
  Result := TJclComplex.Create(ResCoord.X, ResCoord.Y, crRectangular);
  Result.FFracLen := FFracLen;
end;

function TJclComplex.CNewDiv(const X, Y: Float; const ComplexType: TComplexKind): TJclComplex;
var
  NewComplex: TJclComplex;
begin
  NewComplex := TJclComplex.Create(X, Y, ComplexType);
  try
    Result := CNewDiv(NewComplex);
  finally
    NewComplex.Free;
  end;
end;

function TJclComplex.CoreMul(const First, Second: TRectCoord): TRectCoord;
begin
  Result.X := First.X * Second.X - First.Y * Second.Y;
  Result.Y := First.X * Second.Y + First.Y * Second.X;
end;

function TJclComplex.CMul(const MulValue: TJclComplex): TJclComplex;
var
  ResCoord: TRectCoord;
begin
  ResCoord := CoreMul(RectCoord(Self), RectCoord(MulValue));
  FCoord.X := ResCoord.X;
  FCoord.Y := ResCoord.Y;
  Result := Self;
end;

function TJclComplex.CMul(const X, Y: Float; const ComplexType: TComplexKind): TJclComplex;
var
  NewComplex: TJclComplex;
begin
  NewComplex := TJclComplex.Create(X, Y, ComplexType);
  try
    Result := CMul(NewComplex);
  finally
    NewComplex.Free;
  end;
end;

function TJclComplex.CNewMul(const MulValue: TJclComplex): TJclComplex;
var
  ResCoord: TRectCoord;
begin
  ResCoord := CoreMul(RectCoord(Self), RectCoord(MulValue));
  Result := TJclComplex.Create(ResCoord.X, ResCoord.Y, crRectangular);
  Result.FFracLen := FFracLen;
end;

function TJclComplex.CNewMul(const X, Y: Float; const ComplexType: TComplexKind): TJclComplex;
var
  NewComplex: TJclComplex;
begin
  NewComplex := TJclComplex.Create(X, Y, ComplexType);
  try
    Result := CNewMul(NewComplex);
  finally
    NewComplex.Free;
  end;
end;

function TJclComplex.CoreSub(const First, Second: TRectCoord): TRectCoord;
begin
  Result.X := First.X - Second.X;
  Result.Y := First.Y - Second.Y;
end;

function TJclComplex.CSub(const SubValue: TJclComplex): TJclComplex;
var
  ResValue: TRectCoord;
begin
  ResValue := CoreSub(RectCoord(Self), RectCoord(SubValue));
  FCoord.X := ResValue.X;
  FCoord.Y := ResValue.Y;
  Result := Self;
end;

function TJclComplex.CSub(const X, Y: Float; const ComplexType: TComplexKind): TJclComplex;
var
  NewComplex: TJclComplex;
begin
  NewComplex := TJclComplex.Create(X, Y, ComplexType);
  try
    Result := CSub(NewComplex);
  finally
    NewComplex.Free;
  end;
end;

function TJclComplex.CNewSub(const SubValue: TJclComplex): TJclComplex;
var
  ResValue: TRectCoord;
begin
  ResValue := CoreSub(RectCoord(Self), RectCoord(SubValue));
  Result := TJclComplex.Create(ResValue.X, ResValue.Y, crRectangular);
  Result.FFracLen := FFracLen;
end;

function TJclComplex.CNewSub(const X, Y: Float; const ComplexType: TComplexKind): TJclComplex;
var
  NewComplex: TJclComplex;
begin
  NewComplex := TJclComplex.Create(X, Y, ComplexType);
  try
    Result := CNewSub(NewComplex);
  finally
    NewComplex.Free;
  end;
end;

function TJclComplex.CNeg;
begin
  FCoord.X := -FCoord.X;
  FCoord.Y := -FCoord.Y;
  Result := Self;
end;

function TJclComplex.CNewNeg;
begin
  Result := TJclComplex.Create(-FCoord.X, -FCoord.Y, crRectangular);
  Result.FFracLen := FFracLen;
end;

function TJclComplex.CConjugate;
begin
  FCoord.Y := -FCoord.Y;
  Result := Self;
end;

function TJclComplex.CNewConjugate;
begin
  Result := TJclComplex.Create(FCoord.X, -FCoord.Y, crRectangular);
  Result.FFracLen := FFracLen;
end;

//=== natural log and exponential functions ==================================

function TJclComplex.CoreLn(const LnValue: TRectCoord): TRectCoord;
begin
  Result.X := System.Ln(LnValue.X);
  Result.Y := NormalizeAngle(LnValue.Y);
end;

function TJclComplex.CLn: TJclComplex;
var
  ResCoord: TRectCoord;
begin
  FillCoords(crRectangular);
  ResCoord := CoreLn(RectCoord(FCoord.R, FCoord.Theta));
  FCoord.X := ResCoord.X;
  FCoord.Y := ResCoord.Y;
  Result := Self;
end;

function TJclComplex.CNewLn: TJclComplex;
var
  ResCoord: TRectCoord;
begin
  FillCoords(crRectangular);
  ResCoord := CoreLn(RectCoord(FCoord.R, FCoord.Theta));
  Result := TJclComplex.Create(ResCoord.X, ResCoord.Y, crRectangular);
  Result.FFracLen := FFracLen;
end;

function TJclComplex.CoreExp(const ExpValue: TRectCoord): TRectCoord;
var
  ExpX: Float;
begin
  ExpX := JclMath.Exp(ExpValue.X);
  Result.X := ExpX * JclMath.Cos(ExpValue.Y);
  Result.Y := ExpX * JclMath.Sin(ExpValue.Y);
end;

function TJclComplex.CExp: TJclComplex;
var
  ResCoord: TRectCoord;
begin
  ResCoord := CoreExp(RectCoord(FCoord.X, FCoord.Y));
  FCoord.X := ResCoord.X;
  FCoord.Y := ResCoord.Y;
  Result := Self;
end;

function TJclComplex.CNewExp: TJclComplex;
var
  ResCoord: TRectCoord;
begin
  ResCoord := CoreExp(RectCoord(FCoord.X, FCoord.Y));
  Result := TJclComplex.Create(ResCoord.X, ResCoord.Y, crRectangular);
  Result.FFracLen := FFracLen;
end;

function TJclComplex.CorePwr(First, Second, Polar: TRectCoord): TRectCoord;
begin
  First.X := MiscalcFloat(First.X);
  First.Y := MiscalcFloat(First.Y);
  Second.X := MiscalcFloat(Second.X);
  Second.Y := MiscalcFloat(Second.Y);
  if AbsoluteValueSqr(First) = 0.0 then
    if AbsoluteValueSqr(Second) = 0.0 then
      Result := RectOne
    else
      Result := RectZero
  else
    Result := CoreExp(CoreMul(Second, CoreLn(Polar)));
end;

function TJclComplex.CPwr(const PwrValue: TJclComplex): TJclComplex;
var
  ResValue: TRectCoord;
begin
  FillCoords(crRectangular);
  ResValue := CorePwr(RectCoord(Self), RectCoord(PwrValue), RectCoord(FCoord.R, FCoord.Theta));
  FCoord.X := ResValue.X;
  FCoord.Y := ResValue.Y;
  Result := Self;
end;

function TJclComplex.CPwr(const X, Y: Float; const ComplexType: TComplexKind): TJclComplex;
var
  NewComplex: TJclComplex;
begin
  NewComplex := TJclComplex.Create(X, Y, ComplexType);
  try
    Result := CPwr(NewComplex);
  finally
    NewComplex.Free;
  end;
end;

function TJclComplex.CNewPwr(PwrValue: TJclComplex): TJclComplex;
var
  ResValue: TRectCoord;
begin
  FillCoords(crRectangular);
  ResValue := CorePwr(RectCoord(Self), RectCoord(PwrValue), RectCoord(FCoord.R, FCoord.Theta));
  Result := TJclComplex.Create(ResValue.X, ResValue.Y, crRectangular);
  Result.FFracLen := FFracLen;
end;

function TJclComplex.CNewPwr(const X, Y: Float; const ComplexType: TComplexKind): TJclComplex;
var
  NewComplex: TJclComplex;
begin
  NewComplex := TJclComplex.Create(X, Y, ComplexType);
  try
    Result := CNewPwr(NewComplex);
  finally
    NewComplex.Free;
  end;
end;

function TJclComplex.CoreIntPwr(First: TRectCoord; const Polar: TRectCoord; const Pwr: Integer): TRectCoord;
begin
  First.X := MiscalcFloat(First.X);
  First.Y := MiscalcFloat(First.Y);
  if AbsoluteValueSqr(First) = 0.0 then
    if Pwr = 0 then
      Result := RectOne
    else
      Result := RectZero
  else
    Result := RectCoord(PowerInt(Polar.X, Pwr), NormalizeAngle(Pwr * Polar.Y));
end;

function TJclComplex.CIntPwr(const Pwr: Integer): TJclComplex;
var
  ResValue: TRectCoord;
begin
  FillCoords(crRectangular);
  ResValue := CoreIntPwr(RectCoord(Self), RectCoord(FCoord.R, FCoord.Theta), Pwr);
  FCoord.R := ResValue.X;
  FCoord.Theta := ResValue.Y;
  FillCoords(crPolar);
  Result := Self;
end;

function TJclComplex.CNewIntPwr(const Pwr: Integer): TJclComplex;
var
  ResValue: TRectCoord;
begin
  FillCoords(crRectangular);
  ResValue := CoreIntPwr(RectCoord(Self), RectCoord(FCoord.R, FCoord.Theta), Pwr);
  Result := TJclComplex.Create(ResValue.X, ResValue.Y, crPolar);
  Result.FFracLen := FFracLen;
end;

function TJclComplex.CoreRealPwr(First: TRectCoord; const Polar: TRectCoord; const Pwr: Float): TRectCoord;
begin
  First.X := MiscalcFloat(First.X);
  First.Y := MiscalcFloat(First.Y);
  if AbsoluteValueSqr(First) = 0.0 then
    if MiscalcFloat(Pwr) = 0.0 then
      Result := RectOne
    else
      Result := RectZero
  else
    Result := RectCoord(Power(Polar.X, Pwr), NormalizeAngle(Pwr * Polar.Y));
end;

function TJclComplex.CRealPwr(const Pwr: Float): TJclComplex;
var
  ResValue: TRectCoord;
begin
  FillCoords(crRectangular);
  ResValue := CoreRealPwr(RectCoord(Self), RectCoord(FCoord.R, FCoord.Theta), Pwr);
  FCoord.R := ResValue.X;
  FCoord.Theta := ResValue.Y;
  FillCoords(crPolar);
  Result := Self;
end;

function TJclComplex.CNewRealPwr(const Pwr: Float): TJclComplex;
var
  ResValue: TRectCoord;
begin
  FillCoords(crRectangular);
  ResValue := CoreRealPwr(RectCoord(Self), RectCoord(FCoord.R, FCoord.Theta), Pwr);
  Result := TJclComplex.Create(ResValue.X, ResValue.Y, crPolar);
  Result.FFracLen := FFracLen;
end;

function TJclComplex.CoreRoot(First: TRectCoord; const Polar: TRectCoord; const K, N: Word): TRectCoord;
begin
  First.X := MiscalcFloat(First.X);
  First.Y := MiscalcFloat(First.Y);
  if AbsoluteValue(First) = 0.0 then
    Result := RectZero
  else
    Result := RectCoord(Power(Polar.X, 1.0 / N), NormalizeAngle((Polar.Y + K * TwoPi) / N));
end;

function TJclComplex.CRoot(const K, N: Word): TJclComplex;
var
  ResValue: TRectCoord;
begin
  FillCoords(crRectangular);
  ResValue := CoreRoot(RectCoord(Self), RectCoord(FCoord.R, FCoord.Theta), K, N);
  FCoord.R := ResValue.X;
  FCoord.Theta := ResValue.Y;
  FillCoords(crPolar);
  Result := Self;
end;

function TJclComplex.CNewRoot(const K, N: Word): TJclComplex;
var
  ResValue: TRectCoord;
begin
  FillCoords(crRectangular);
  ResValue := CoreRoot(RectCoord(Self), RectCoord(FCoord.R, FCoord.Theta), K, N);
  Result := TJclComplex.Create(ResValue.X, ResValue.Y, crPolar);
  Result.FFracLen := FFracLen;
end;

function TJclComplex.CSqrt: TJclComplex;
begin
  Result := CRoot(0, 2);
end;

function TJclComplex.CNewSqrt: TJclComplex;
begin
  Result := CNewRoot(0, 2);
end;

//=== trigonometric functions ================================================

function TJclComplex.CoreCos(const Value: TRectCoord): TRectCoord;
begin
  Result := RectCoord(JclMath.Cos(Value.X) * JclMath.CosH(Value.Y), -JclMath.Sin(Value.X) * JclMath.SinH(Value.Y));
end;

function TJclComplex.CCos: TJclComplex;
var
  ResValue: TRectCoord;
begin
  ResValue := CoreCos(RectCoord(Self));
  FCoord.X := ResValue.X;
  FCoord.Y := ResValue.Y;
  Result := Self;
end;

function TJclComplex.CNewCos: TJclComplex;
var
  ResValue: TRectCoord;
begin
  ResValue := CoreCos(RectCoord(Self));
  Result := TJclComplex.Create(ResValue.X, ResValue.Y, crRectangular);
  Result.FFracLen := FFracLen;
end;

function TJclComplex.CoreSin(const Value: TRectCoord): TRectCoord;
begin
  Result := RectCoord(JclMath.Sin(Value.X) * JclMath.CosH(Value.Y), JclMath.Cos(Value.X) * JclMath.SinH(Value.Y));
end;

function TJclComplex.CSin: TJclComplex;
var
  ResValue: TRectCoord;
begin
  ResValue := CoreSin(RectCoord(Self));
  FCoord.X := ResValue.X;
  FCoord.Y := ResValue.Y;
  Result := Self;
end;

function TJclComplex.CNewSin: TJclComplex;
var
  ResValue: TRectCoord;
begin
  ResValue := CoreSin(RectCoord(Self));
  Result := TJclComplex.Create(ResValue.X, ResValue.Y, crRectangular);
  Result.FFracLen := FFracLen;
end;

function TJclComplex.CoreTan(const Value: TRectCoord): TRectCoord;
var
  TempValue: Float;
begin
  TempValue := JclMath.Cos(2.0 * Value.X) + JclMath.CosH(2.0 * Value.Y);
  if MiscalcFloat(TempValue) <> 0.0 then
    Result := RectCoord(JclMath.Sin(2.0 * Value.X) / TempValue, JclMath.SinH(2.0 * Value.Y) / TempValue)
  else
    Result := RectInfinity;
end;

function TJclComplex.CTan: TJclComplex;
var
  ResValue: TRectCoord;
begin
  ResValue := CoreTan(RectCoord(Self));
  FCoord.X := ResValue.X;
  FCoord.Y := ResValue.Y;
  Result := Self;
end;

function TJclComplex.CNewTan: TJclComplex;
var
  ResValue: TRectCoord;
begin
  ResValue := CoreTan(RectCoord(Self));
  Result := TJclComplex.Create(ResValue.X, ResValue.Y, crRectangular);
  Result.FFracLen := FFracLen;
end;

function TJclComplex.CoreCot(const Value: TRectCoord): TRectCoord;
var
  TempValue: Float;
begin
  TempValue := JclMath.Cosh(2.0 * Value.Y) - JclMath.Cos(2.0 * Value.X);
  if MiscalcFloat(TempValue) <> 0.0 then
    Result := RectCoord(JclMath.Sin(2.0 * Value.X) / TempValue, -JclMath.SinH(2.0 * Value.Y) / TempValue)
  else
    Result := RectInfinity;
end;

function TJclComplex.CCot: TJclComplex;
var
  ResValue: TRectCoord;
begin
  ResValue := CoreCot(RectCoord(Self));
  FCoord.X := ResValue.X;
  FCoord.Y := ResValue.Y;
  Result := Self;
end;

function TJclComplex.CNewCot: TJclComplex;
var
  ResValue: TRectCoord;
begin
  ResValue := CoreCot(RectCoord(Self));
  Result := TJclComplex.Create(ResValue.X, ResValue.Y, crRectangular);
  Result.FFracLen := FFracLen;
end;

function TJclComplex.CoreSec(const Value: TRectCoord): TRectCoord;
var
  TempValue: TRectCoord;
begin
  TempValue := CoreCos(Value);
  if MiscalcFloat(AbsoluteValue(TempValue)) <> 0.0 then
    Result := CoreDiv(RectOne, TempValue)
  else
    Result := RectInfinity;
end;

function TJclComplex.CSec: TJclComplex;
var
  ResValue: TRectCoord;
begin
  ResValue := CoreSec(RectCoord(Self));
  FCoord.X := ResValue.X;
  FCoord.Y := ResValue.Y;
  Result := Self;
end;

function TJclComplex.CNewSec: TJclComplex;
var
  ResValue: TRectCoord;
begin
  ResValue := CoreSec(RectCoord(Self));
  Result := TJclComplex.Create(ResValue.X, ResValue.Y, crRectangular);
  Result.FFracLen := FFracLen;
end;

function TJclComplex.CoreCsc(const Value: TRectCoord): TRectCoord;
var
  TempValue: TRectCoord;
begin
  TempValue := CoreSin(Value);
  if MiscalcFloat(AbsoluteValue(TempValue)) <> 0.0 then
    Result := CoreDiv(RectOne, TempValue)
  else
    Result := RectInfinity;
end;

function TJclComplex.CCsc: TJclComplex;
var
  ResValue: TRectCoord;
begin
  ResValue := CoreCsc(RectCoord(Self));
  FCoord.X := ResValue.X;
  FCoord.Y := ResValue.Y;
  Result := Self;
end;

function TJclComplex.CNewCsc: TJclComplex;
var
  ResValue: TRectCoord;
begin
  ResValue := CoreCsc(RectCoord(Self));
  Result := TJclComplex.Create(ResValue.X, ResValue.Y, crRectangular);
  Result.FFracLen := FFracLen;
end;

//=== hyperbolic functions ===================================================

function TJclComplex.CoreCosH(const Value: TRectCoord): TRectCoord;
begin
  Result := RectCoord(JclMath.CosH(Value.X) * JclMath.Cos(Value.Y), JclMath.SinH(Value.X) * JclMath.Sin(Value.Y));
end;

function TJclComplex.CCosH: TJclComplex;
var
  ResValue: TRectCoord;
begin
  ResValue := CoreCosH(RectCoord(Self));
  FCoord.X := ResValue.X;
  FCoord.Y := ResValue.Y;
  Result := Self;
end;

function TJclComplex.CNewCosH: TJclComplex;
var
  ResValue: TRectCoord;
begin
  ResValue := CoreCosH(RectCoord(Self));
  Result := TJclComplex.Create(ResValue.X, ResValue.Y, crRectangular);
  Result.FFracLen := FFracLen;
end;

function TJclComplex.CoreSinH(const Value: TRectCoord): TRectCoord;
begin
  Result := RectCoord(JclMath.SinH(Value.X) * JclMath.Cos(Value.Y), JclMath.CosH(Value.X) * JclMath.Sin(Value.Y));
end;

function TJclComplex.CSinH: TJclComplex;
var
  ResValue: TRectCoord;
begin
  ResValue := CoreSinH(RectCoord(Self));
  FCoord.X := ResValue.X;
  FCoord.Y := ResValue.Y;
  Result := Self;
end;

function TJclComplex.CNewSinH: TJclComplex;
var
  ResValue: TRectCoord;
begin
  ResValue := CoreSinH(RectCoord(Self));
  Result := TJclComplex.Create(ResValue.X, ResValue.Y, crRectangular);
  Result.FFracLen := FFracLen;
end;

function TJclComplex.CoreTanH(const Value: TRectCoord): TRectCoord;
var
  TempValue: Float;
begin
  TempValue := JclMath.CosH(2.0 * Value.X) + JclMath.Cos(2.0 * Value.Y);
  if MiscalcFloat(TempValue) <> 0.0 then
    Result := RectCoord(JclMath.SinH(2.0 * Value.X) / TempValue, JclMath.Sin(2.0 * Value.Y) / TempValue)
  else
    Result := RectInfinity;
end;

function TJclComplex.CTanH: TJclComplex;
var
  ResValue: TRectCoord;
begin
  ResValue := CoreTanH(RectCoord(Self));
  FCoord.X := ResValue.X;
  FCoord.Y := ResValue.Y;
  Result := Self;
end;

function TJclComplex.CNewTanH: TJclComplex;
var
  ResValue: TRectCoord;
begin
  ResValue := CoreTanH(RectCoord(Self));
  Result := TJclComplex.Create(ResValue.X, ResValue.Y, crRectangular);
  Result.FFracLen := FFracLen;
end;

function TJclComplex.CoreCotH(const Value: TRectCoord): TRectCoord;
var
  TempValue: Float;
begin
  TempValue := JclMath.Cosh(2.0 * Value.X) - JclMath.Cos(2.0 * Value.Y);
  if MiscalcFloat(TempValue) <> 0.0 then
    Result := RectCoord(JclMath.SinH(2.0 * Value.X) / TempValue, -JclMath.Sin(2.0 * Value.Y) / TempValue)
  else
    Result := RectInfinity;
end;

function TJclComplex.CCotH: TJclComplex;
var
  ResValue: TRectCoord;
begin
  ResValue := CoreCotH(RectCoord(Self));
  FCoord.X := ResValue.X;
  FCoord.Y := ResValue.Y;
  Result := Self;
end;

function TJclComplex.CNewCotH: TJclComplex;
var
  ResValue: TRectCoord;
begin
  ResValue := CoreCotH(RectCoord(Self));
  Result := TJclComplex.Create(ResValue.X, ResValue.Y, crRectangular);
  Result.FFracLen := FFracLen;
end;

function TJclComplex.CoreSecH(const Value: TRectCoord): TRectCoord;
var
  TempValue: TRectCoord;
begin
  TempValue := CoreCosH(Value);
  if MiscalcFloat(AbsoluteValue(TempValue)) <> 0.0 then
    Result := CoreDiv(RectOne, TempValue)
  else
    Result := RectInfinity;
end;

function TJclComplex.CSecH: TJclComplex;
var
  ResValue: TRectCoord;
begin
  ResValue := CoreSecH(RectCoord(Self));
  FCoord.X := ResValue.X;
  FCoord.Y := ResValue.Y;
  Result := Self;
end;

function TJclComplex.CNewSecH: TJclComplex;
var
  ResValue: TRectCoord;
begin
  ResValue := CoreSecH(RectCoord(Self));
  Result := TJclComplex.Create(ResValue.X, ResValue.Y, crRectangular);
  Result.FFracLen := FFracLen;
end;

function TJclComplex.CoreCscH(const Value: TRectCoord): TRectCoord;
var
  TempValue: TRectCoord;
begin
  TempValue := CoreSinH(Value);
  if MiscalcFloat(AbsoluteValue(TempValue)) <> 0.0 then
    Result := CoreDiv(RectOne, TempValue)
  else
    Result := RectInfinity;
end;

function TJclComplex.CCscH: TJclComplex;
var
  ResValue: TRectCoord;
begin
  ResValue := CoreCscH(RectCoord(Self));
  FCoord.X := ResValue.X;
  FCoord.Y := ResValue.Y;
  Result := Self;
end;

function TJclComplex.CNewCscH: TJclComplex;
var
  ResValue: TRectCoord;
begin
  ResValue := CoreCscH(RectCoord(Self));
  Result := TJclComplex.Create(ResValue.X, ResValue.Y, crRectangular);
  Result.FFracLen := FFracLen;
end;

//=== complex Bessel functions of order zero =================================

function TJclComplex.CoreI0(const Value: TRectCoord): TRectCoord;
var
  ZSqr25, Term: TRectCoord;
  I: Integer;
  SizeSqr: Float;
begin
  Result := RectOne;
  ZSqr25 := CoreMul(Value, Value);
  ZSqr25 := RectCoord(0.25 * ZSqr25.X, 0.25 * ZSqr25.Y);
  Term := ZSqr25;
  Result := CoreAdd(Result, ZSqr25);
  I := 1;
  repeat
    Term := CoreMul(ZSqr25, Term);
    Inc(I);
    Term := RectCoord(Term.X / Sqr(I), Term.Y / Sqr(I));
    Result := CoreAdd(Result, Term);
    SizeSqr := Sqr(Term.X) + Sqr(Term.Y);
  until (I > MaxTerm) or (SizeSqr < EpsilonSqr);
end;

function TJclComplex.CI0: TJclComplex;
var
  ResValue: TRectCoord;
begin
  ResValue := CoreI0(RectCoord(Self));
  FCoord.X := ResValue.X;
  FCoord.Y := ResValue.Y;
  Result := Self;
end;

function TJclComplex.CNewI0: TJclComplex;
var
  ResValue: TRectCoord;
begin
  ResValue := CoreI0(RectCoord(Self));
  Result := TJclComplex.Create(ResValue.X, ResValue.Y, crRectangular);
  Result.FFracLen := FFracLen;
end;

function TJclComplex.CoreJ0(const Value: TRectCoord): TRectCoord;
var
  ZSqr25, Term: TRectCoord;
  I: Integer;
  SizeSqr: Float;
  AddFlag: Boolean;
begin
  Result := RectOne;
  ZSqr25 := CoreMul(Value, Value);
  ZSqr25 := RectCoord(0.25 * ZSqr25.X, 0.25 * ZSqr25.Y);
  Term := ZSqr25;
  Result := CoreSub(Result, ZSqr25);
  AddFlag := False;
  I := 1;
  repeat
    Term := CoreMul(ZSqr25, Term);
    Inc(I);
    AddFlag := not AddFlag;
    Term := RectCoord(Term.X / Sqr(I), Term.Y / Sqr(I));
    if AddFlag then
      Result := CoreAdd(Result, Term)
    else
      Result := CoreSub(Result, Term);
    SizeSqr := Sqr(Term.X) + Sqr(Term.Y);
  until (I > MaxTerm) or (SizeSqr < EpsilonSqr);
end;

function TJclComplex.CJ0: TJclComplex;
var
  ResValue: TRectCoord;
begin
  ResValue := CoreJ0(RectCoord(Self));
  FCoord.X := ResValue.X;
  FCoord.Y := ResValue.Y;
  Result := Self;
end;

function TJclComplex.CNewJ0: TJclComplex;
var
  ResValue: TRectCoord;
begin
  ResValue := CoreJ0(RectCoord(Self));
  Result := TJclComplex.Create(ResValue.X, ResValue.Y, crRectangular);
  Result.FFracLen := FFracLen;
end;

function TJclComplex.CoreApproxLnGamma(const Value: TRectCoord): TRectCoord;
const
  C: array [1..8] of Float =
   (1.0 / 12.0, -1.0 / 360.0, 1.0 / 1260.0, -1.0 / 1680.0,
    1.0 / 1188.0, -691.0 / 360360.0, 1.0 / 156.0, -3617.0 / 122400.0);
var
  I: Integer;
  Powers: array [1..8] of TRectCoord;
  Temp1, Temp2: TRectCoord;
begin
  Temp1 := CoreLn(Value);
  Temp2 := RectCoord(Value.X - 0.5, Value.Y);
  Result := CoreAdd(Temp1, Temp2);
  Result := CoreSub(Result, Value);
  Result.X := Result.X + hLn2PI;

  Temp1 := RectOne;
  Powers[1] := CoreDiv(Temp1, Value);
  Temp2 := CoreMul(powers[1], Powers[1]);
  for I := 2 to 8 do
    Powers[I] := CoreMul(Powers[I - 1], Temp2);
  for I := 8 downto 1 do
  begin
    Temp1 := RectCoord(C[I] * Powers[I].X, C[I] * Powers[I].Y);
    Result := CoreAdd(Result, Temp1);
  end;
end;

function TJclComplex.CApproxLnGamma: TJclComplex;
var
  ResValue: TRectCoord;
begin
  ResValue := CoreApproxLnGamma(RectCoord(Self));
  FCoord.X := ResValue.X;
  FCoord.Y := ResValue.Y;
  Result := Self;
end;

function TJclComplex.CNewApproxLnGamma: TJclComplex;
var
  ResValue: TRectCoord;
begin
  ResValue := CoreApproxLnGamma(RectCoord(Self));
  Result := TJclComplex.Create(ResValue.X, ResValue.Y, crRectangular);
  Result.FFracLen := FFracLen;
end;

function TJclComplex.CoreLnGamma(Value: TRectCoord): TRectCoord;
var
  LNA, Temp: TRectCoord;
begin
  if (Value.X <= 0.0) and (MiscalcFloat(Value.Y) = 0.0) then
    if MiscalcFloat(Int(Value.X - 1E-8) - Value.X) = 0.0 then
    begin
      Result := RectInfinity;
      Exit;
    end;

  if Value.Y < 0.0 then
  begin
    Value := RectCoord(Value.X, -Value.Y);
    Result := CoreLnGamma(Value);
    Result := RectCoord(Result.X, -Result.Y);
  end
  else
  begin
    if Value.X < 9.0 then
    begin
      LNA := CoreLn(Value);
      Value := RectCoord(Value.X + 1, Value.Y);
      Temp := CoreLnGamma(Value);
      Result := CoreSub(Temp, LNA);
    end
    else
      CoreApproxLnGamma(Value);
  end;
end;

function TJclComplex.CLnGamma: TJclComplex;
var
  ResValue: TRectCoord;
begin
  ResValue := CoreLnGamma(RectCoord(Self));
  FCoord.X := ResValue.X;
  FCoord.Y := ResValue.Y;
  Result := Self;
end;

function TJclComplex.CNewLnGamma: TJclComplex;
var
  ResValue: TRectCoord;
begin
  ResValue := CoreLnGamma(RectCoord(Self));
  Result := TJclComplex.Create(ResValue.X, ResValue.Y, crRectangular);
  Result.FFracLen := FFracLen;
end;

function TJclComplex.CoreGamma(const Value: TRectCoord): TRectCoord;
var
  LNZ: TRectCoord;
begin
  LNZ := CoreLnGamma(Value);
  if LNZ.X > 75.0 then
    Result := RectInfinity
  else
    if LNZ.X < -200.0 then
      Result := RectZero
    else
      Result := CoreExp(LNZ);
end;

function TJclComplex.CGamma: TJclComplex;
var
  ResValue: TRectCoord;
begin
  ResValue := CoreGamma(RectCoord(Self));
  FCoord.X := ResValue.X;
  FCoord.Y := ResValue.Y;
  Result := Self;
end;

function TJclComplex.CNewGamma: TJclComplex;
var
  ResValue: TRectCoord;
begin
  ResValue := CoreGamma(RectCoord(Self));
  Result := TJclComplex.Create(ResValue.X, ResValue.Y, crRectangular);
  Result.FFracLen := FFracLen;
end;

//=== miscellaneous ==========================================================

function TJclComplex.AbsoluteValue: Float;
begin
  Result := Sqrt(Sqr(FCoord.X) + Sqr(FCoord.Y));
end;

function TJclComplex.AbsoluteValue(const Coord: TRectCoord): Float;
begin
  Result := Sqrt(Sqr(Coord.X) + Sqr(Coord.Y));
end;

function TJclComplex.AbsoluteValueSqr: Float;
begin
  Result := Sqr(FCoord.X) + Sqr(FCoord.Y);
end;

function TJclComplex.AbsoluteValueSqr(const Coord: TRectCoord): Float;
begin
  Result := Sqr(Coord.X) + Sqr(Coord.Y);
end;

function TJclComplex.FormatExtended(const X: Float): string;
begin
  Result := FloatToStrF(X, ffFixed, FFracLen, FFracLen);
end;

procedure TJclComplex.SetFracLen(const X: Byte);
begin
  if X > MaxFracLen then
    FFracLen := MaxFracLen
  else
    FFracLen := X;
end;

function TJclComplex.GetRadius: Float;
begin
  FillCoords(crRectangular);
  Result := FCoord.R;
end;

function TJclComplex.GetAngle: Float;
begin
  FillCoords(crRectangular);
  Result := FCoord.Theta;
end;

function TJclComplex.NormalizeAngle(const Value: Float): Float;
var
  N: Integer;
begin
  Result := Value;
  if Result > Pi then
  begin
    N := Floor((Result + Pi) / TwoPi);
    Result := Result - N * TwoPi;
  end
  else
  if Result < -Pi then
  begin
    N := Floor((-Result + Pi) / TwoPi);
    Result := Result + N * TwoPi;
  end;
end;

{$IFDEF UNITVERSIONING}
initialization
  RegisterUnitVersion(HInstance, UnitVersioning);

finalization
  UnregisterUnitVersion(HInstance);
{$ENDIF UNITVERSIONING}

end.
