program LnGammaComplexDemo;
{$codepage utf8}
uses
Math, SysUtils;
type
TComplex = record
Re, Im: Double;
end;
TRefTest = record
Z: TComplex;
Expected: TComplex;
LabelText: string;
end;
function Complex(Re, Im: Double): TComplex;
begin
Result.Re := Re;
Result.Im := Im;
end;
function CAdd(a, b: TComplex): TComplex;
begin
Result.Re := a.Re + b.Re;
Result.Im := a.Im + b.Im;
end;
function CSub(a, b: TComplex): TComplex;
begin
Result.Re := a.Re - b.Re;
Result.Im := a.Im - b.Im;
end;
function CMul(a, b: TComplex): TComplex;
begin
Result.Re := a.Re * b.Re - a.Im * b.Im;
Result.Im := a.Re * b.Im + a.Im * b.Re;
end;
function CDiv(a, b: TComplex): TComplex;
var
denom: Double;
begin
denom := b.Re * b.Re + b.Im * b.Im;
Result.Re := (a.Re * b.Re + a.Im * b.Im) / denom;
Result.Im := (a.Im * b.Re - a.Re * b.Im) / denom;
end;
function CLog(z: TComplex): TComplex;
begin
Result.Re := Ln(Hypot(z.Re, z.Im));
Result.Im := ArcTan2(z.Im, z.Re);
end;
function CExp(z: TComplex): TComplex;
var
expRe: Double;
begin
expRe := Exp(z.Re);
Result.Re := expRe * Cos(z.Im);
Result.Im := expRe * Sin(z.Im);
end;
function CSin(z: TComplex): TComplex;
begin
Result.Re := Sin(z.Re) * Cosh(z.Im);
Result.Im := Cos(z.Re) * Sinh(z.Im);
end;
function CLnGamma(z: TComplex): TComplex;
const
g = 7.0;
lanczosCoef: array[0..8] of Double = (
0.99999999999980993, 676.5203681218851, -1259.1392167224028,
771.32342877765313, -176.61502916214059, 12.507343278686905,
-0.13857109526572012, 9.9843695780195716e-6, 1.5056327351493116e-7
);
var
i: Integer;
z1, sum, t, tmp: TComplex;
begin
if z.Re < 0.5 then
begin
tmp := CMul(Complex(Pi, 0.0), z);
Result := CSub(
CSub(
CLog(Complex(Pi, 0.0)),
CLog(CSin(tmp))
),
CLnGamma(CSub(Complex(1.0, 0.0), z))
);
Exit;
end;
z1 := CSub(z, Complex(1.0, 0.0));
sum := Complex(lanczosCoef[0], 0.0);
for i := 1 to 8 do
sum := CAdd(sum, CDiv(Complex(lanczosCoef[i], 0.0), CAdd(z1, Complex(i, 0.0))));
t := CAdd(z1, Complex(g + 0.5, 0.0));
Result := CAdd(
Complex(Ln(Sqrt(2 * Pi)), 0.0),
CSub(
CMul(CAdd(z1, Complex(0.5, 0.0)), CLog(t)),
t
)
);
Result := CAdd(Result, CLog(sum));
end;
procedure RunTests;
const
Eps = 1e-14;
var
res, delta: TComplex;
err, normExp: Double;
i: Integer;
tests: array[1..7] of TRefTest = (
(Z: (Re: 1.0; Im: 0.0); Expected: (Re: 0.0; Im: 0.0); LabelText: 'lnГ(1) = 0'),
(Z: (Re: 0.5; Im: 0.0); Expected: (Re: 0.572364942924700087; Im: 0.0); LabelText: 'lnГ(0.5) = ln(sqrt(pi))'),
(Z: (Re: 30.0; Im: 0.0); Expected: (Re: 71.257038967168009; Im: 0.0); LabelText: 'lnГ(30)'),
(Z: (Re: 0.1; Im: -30.0); Expected: (Re: -47.5654235556991727; Im: -71.4063250634621394); LabelText: 'lnГ(0.1 - 30i)'),
(Z: (Re: 0.1; Im: 30); Expected: (Re: -47.5654235556991727; Im: 71.4063250634621394); LabelText: 'lnГ(0.1 + 30i)'),
(Z: (Re: 0.1; Im: -100.0); Expected: (Re: -158.002761620672605; Im: -359.888316732655004); LabelText: 'lnГ(0.1 - 100i)'),
(Z: (Re: 0.1; Im: 100.0); Expected: (Re: -158.002761620672605; Im: 359.888316732655004); LabelText: 'lnГ(0.1 + 100i)')
);
begin
for i := 1 to Length(tests) do
begin
WriteLn('Test ', i, ': ', tests[i].LabelText);
WriteLn(Format(' z = %.6f + %.6fi', [tests[i].Z.Re, tests[i].Z.Im]));
res := CLnGamma(tests[i].Z);
WriteLn(Format(' Computed: %.15f + %.15fi', [res.Re, res.Im]));
WriteLn(Format(' Expected: %.15f + %.15fi', [tests[i].Expected.Re, tests[i].Expected.Im]));
delta := CSub(res, tests[i].Expected);
normExp := Hypot(tests[i].Expected.Re, tests[i].Expected.Im);
if normExp = 0 then
err := Hypot(res.Re, res.Im)
else
err := Hypot(delta.Re, delta.Im) / normExp;
WriteLn(Format(' Rel Error: %.3e', [err]));
if err < Eps then
WriteLn(' Passed')
else
WriteLn(' Failed');
WriteLn;
end;
end;
begin
RunTests;
Readln;
end.