unit Zespolone;

interface

uses SysUtils,Math;

type
CTyp = Double;
ComplexStringFormats=(csfI=0,csfNawiasy=1);

TComplex = class
	private
    	FReal,FImag :CTyp;
  public
      //ustalanie wartosci pol
      procedure SetReal(AReal :CTyp);
      procedure SetImag(AImag :CTyp);
      function GetNorm :CTyp;
      function GetConj :TComplex;
      function Clone :TComplex;
      //konstruktor
      constructor Create(AReal :CTyp=0;AImag :Ctyp=0); overload;
      constructor Create(Pierwowzor :TComplex); overload;
      //wlasnosci
      property Real :CTyp read FReal write SetReal;
      property Imag :CTyp read FImag write SetImag;
      property Norm :Ctyp read GetNorm;
      property Conj :TComplex read GetConj;
      //operacje na liczbach zespolonych
      procedure Dodaj(Skladnik :TComplex); overload;
      procedure Odejmij(Odjemnik :TComplex); overload;
      procedure Pomnoz(Czynnik :TComplex); overload;
      procedure Podziel(Dzielnik :TComplex); overload;
      //operacje na liczbach zespolonych i CTyp
      procedure Dodaj(Skladnik :CTyp); overload;
      procedure Odejmij(Odjemnik :CTyp); overload;
      procedure Pomnoz(Czynnik :CTyp); overload;
      procedure Podziel(Dzielnik :CTyp); overload;
      //stala urojona
      constructor I;
      //konwersja
      function ToString(Format: ComplexStringFormats = csfI) :String;
    end;

EComplexZeroDivide=class(EZeroDivide);

//TPoleComplex
PoleComplexStringFormats=(pcsfI=0,pcsfNawiasy=1,pcsfBiegunowe=2);

TPoleComplex = class(TComplex)
  public
    function GetR :CTyp;
    function GetPhi :CTyp;
    property R :CTyp read GetR;
    property Phi :CTyp read GetPhi;
    function ToString(Format: PoleComplexStringFormats = pcsfBiegunowe) :String;
  end;

//funkcje matematyczne
function Suma(Skladnik1,Skladnik2 :TComplex) :TComplex;
function Roznica(Odjemna,Odjemnik :TComplex) :TComplex;
function Iloczyn(Czynnik1,Czynnik2 :TComplex) :TComplex;
function Iloraz(Dzielna,Dzielnik :TComplex) :TComplex;
function Exp(Wykladnik :TComplex) :TComplex;
function Sin(Arg :TComplex) :TComplex;
function Cos(Arg :TComplex) :TComplex;
//funkcje konwersji
function ComplexToStr(Arg :TComplex;Format :ComplexStringFormats = csfI) :String;
function ComplexToCTyp(Arg :TComplex) :CTyp;

implementation

procedure TComplex.SetReal(AReal :CTyp);
begin
FReal:=AReal;
end;

procedure TComplex.SetImag(AImag :CTyp);
begin
FImag:=AImag;
end;

function TComplex.GetNorm :CTyp;
begin
//kwadrat normy, jak w C++
Result:=FReal*FReal+FImag*FImag;
end;

function TComplex.GetConj :TComplex;
begin
Result:=TComplex.Create(FReal,-FImag);
end;

constructor TComplex.Create(AReal,AImag :Ctyp);
begin
inherited Create;
SetReal(AReal);
SetImag(AImag);
end;

constructor TComplex.Create(Pierwowzor :TComplex);
begin
Create(Pierwowzor.Real,Pierwowzor.Imag);
end;

function TComplex.Clone :TComplex;
begin
Result:=TComplex.Create(Self);
end;

constructor TComplex.I;
begin
Create(0,1);
end;

procedure TComplex.Dodaj(Skladnik :TComplex);
begin
Real:=Real+Skladnik.Real;
Imag:=Imag+Skladnik.Imag;
end;

procedure TComplex.Odejmij(Odjemnik :TComplex);
begin
Real:=Real-Odjemnik.Real;
Imag:=Imag-Odjemnik.Imag;
end;

procedure TComplex.Pomnoz(czynnik :TComplex);
var PReal,PImag :CTyp;
begin
PReal:=Real*Czynnik.Real-Imag*Czynnik.Imag;
PImag:=Real*Czynnik.Imag+Imag*Czynnik.Real;
Real:=PReal;
Imag:=PImag;
end;

procedure TComplex.Podziel(Dzielnik :TComplex);
begin
//if Dzielnik.Norm=0 then raise Exception.Create('Dzielenie liczby zespolonej przez zero');
if Dzielnik.Norm=0 then raise EComplexZeroDivide.Create('Dzielenie liczby zespolonej przez zero');

Self.Pomnoz(Dzielnik.Conj);
Real:=Real/Dzielnik.Norm;
Imag:=Imag/Dzielnik.Norm;
end;

procedure TComplex.Dodaj(Skladnik :CTyp);
begin
Dodaj(TComplex.Create(Skladnik));
//Real:=Real+Skladnik;
end;

procedure TComplex.Odejmij(Odjemnik :CTyp);
begin
Odejmij(TComplex.Create(Odjemnik));
//Real:=Real+Odjemnik;
end;

procedure TComplex.Pomnoz(Czynnik :CTyp);
begin
Pomnoz(TComplex.Create(Czynnik));
//Real:=Real*Czynnik;
//Imag:=Imag*Czynnik;
end;

procedure TComplex.Podziel(Dzielnik :CTyp);
begin
Podziel(TComplex.Create(Dzielnik));
//if Dzielnik=0 then raise EComplexZeroDivide.Create('Dzielenie liczby zespolonej przez zero');
//Real:=Real/Dzielnik;
//Imag:=Imag/Dzielnik;
end;

function TComplex.ToString(Format: ComplexStringFormats = csfI) :String;
begin
case Format of
  csfNawiasy:  Result:='('+FloatToStr(Real)+','+FloatToStr(Imag)+')';
  csfI:
    if Imag>=0 then Result:=FloatToStr(Real)+'+i'+FloatToStr(Imag)
             else Result:=FloatToStr(Real)+'-i'+FloatToStr(Abs(Imag));
  end;
end;

function Suma(Skladnik1,Skladnik2 :TComplex) :TComplex;
begin
Result:=TComplex.Create(Skladnik1);
Result.Dodaj(Skladnik2);
end;

function Roznica(Odjemna,Odjemnik :TComplex) :TComplex;
begin
Result:=TComplex.Create(Odjemna);
Result.Odejmij(Odjemnik);
end;

function Iloczyn(Czynnik1,Czynnik2 :TComplex) :TComplex;
begin
Result:=TComplex.Create(Czynnik1);
Result.Pomnoz(Czynnik2);
end;

function Iloraz(Dzielna,Dzielnik :TComplex) :TComplex;
begin
Result:=TComplex.Create(Dzielna);
Result.Podziel(Dzielnik);
end;

function Exp(Wykladnik :TComplex) :TComplex;
begin
{$IF NOT DEFINED(CLR)}
//VCL.Win32
Result:=TComplex.Create(System.Cos(Wykladnik.Imag),System.Sin(Wykladnik.Imag));
Result.Pomnoz(System.Exp(Wykladnik.Real));
{$ELSE}
//VCL.NET, WindowsForms
Result:=TComplex.Create(Borland.Delphi.System.Cos(Wykladnik.Imag),Borland.Delphi.System.Sin(Wykladnik.Imag)); //VCL.NET
Result.Pomnoz(Borland.Delphi.System.Exp(Wykladnik.Real));
{$IFEND}
end;

//funkcje trygonometryczne przez rozklad na exp
function Sin(Arg :TComplex) :TComplex;
var PlusIArg,MinusIArg :TComplex;
begin
PlusIArg:=Iloczyn(TComplex.I,Arg);
MinusIArg:=Roznica(TComplex.Create,PlusIArg);
Result:=Roznica(Exp(PlusIArg),Exp(MinusIArg));
Result.Podziel(2);
Result.Podziel(TComplex.I);
end;

function Cos(Arg :TComplex) :TComplex;
var PlusIArg,MinusIArg :TComplex;
begin
PlusIArg:=Iloczyn(TComplex.I,Arg);
MinusIArg:=Roznica(TComplex.Create,PlusIArg);
Result:=Suma(Exp(PlusIArg),Exp(MinusIArg));
Result.Podziel(2);
end;

function ComplexToStr(Arg :TComplex;Format :ComplexStringFormats = csfI) :String;
begin
Result:=Arg.ToString(Format);
end;

function ComplexToCTyp(Arg :TComplex) :CTyp;
begin
if Arg.Imag<>0 then raise Exception.Create('Konwersja nie jest mozliwa (wartość urojona niezerowa)')
               else Result:=Arg.Real;
end;

function TPoleComplex.GetR :CTyp;
begin
Result:=Sqrt(Norm);
end;

//ArcTan2 wymaga Math
//zmieniam zakres z -Pi:Pi na 0:2Pi
function TPoleComplex.GetPhi :CTyp;
begin
Result:=ArcTan2(Imag,Real);
if Result<0 then Result:=Result+2*Pi;
end;

function TPoleComplex.ToString(Format: PoleComplexStringFormats = pcsfBiegunowe) :String;
begin
case Format of
  pcsfI:         Result:=inherited ToString(csfI);
  pcsfNawiasy:   Result:=inherited ToString(csfNawiasy);
  pcsfBiegunowe: Result:='r='+FloatToStr(R)+', phi='+FloatToStr(Phi);
  end;
end;

end.
