{$MODESWITCH RESULT+}
{$GOTO ON}
unit testfhtunit;
interface
uses Math, Sysutils, Ap, ftbase, fft, fht;

function TestFHT(Silent : Boolean):Boolean;
function testfhtunit_test_silent():Boolean;
function testfhtunit_test():Boolean;

implementation

procedure RefFHTR1D(var A : TReal1DArray; N : AlglibInteger);forward;
procedure RefFHTR1DInv(var A : TReal1DArray; N : AlglibInteger);forward;


(*************************************************************************
Test
*************************************************************************)
function TestFHT(Silent : Boolean):Boolean;
var
    N : AlglibInteger;
    I : AlglibInteger;
    R1 : TReal1DArray;
    R2 : TReal1DArray;
    R3 : TReal1DArray;
    MaxN : AlglibInteger;
    BiDiErr : Double;
    RefErr : Double;
    ErrTol : Double;
    RefErrors : Boolean;
    BiDiErrors : Boolean;
    WasErrors : Boolean;
begin
    MaxN := 128;
    ErrTol := 100000*Power(MaxN, AP_Double(3)/2)*MachineEpsilon;
    BiDiErrors := False;
    RefErrors := False;
    WasErrors := False;
    
    //
    // Test bi-directional error: norm(x-invFHT(FHT(x)))
    //
    BiDiErr := 0;
    N:=1;
    while N<=MaxN do
    begin
        
        //
        // FHT/invFHT
        //
        SetLength(R1, N);
        SetLength(R2, N);
        SetLength(R3, N);
        I:=0;
        while I<=N-1 do
        begin
            R1[I] := 2*RandomReal-1;
            R2[I] := R1[I];
            R3[I] := R1[I];
            Inc(I);
        end;
        FHTR1D(R2, N);
        FHTR1DInv(R2, N);
        FHTR1DInv(R3, N);
        FHTR1D(R3, N);
        I:=0;
        while I<=N-1 do
        begin
            BiDiErr := Max(BiDiErr, AbsReal(R1[I]-R2[I]));
            BiDiErr := Max(BiDiErr, AbsReal(R1[I]-R3[I]));
            Inc(I);
        end;
        Inc(N);
    end;
    BiDiErrors := BiDiErrors or AP_FP_Greater(BiDiErr,ErrTol);
    
    //
    // Test against reference O(N^2) implementation
    //
    RefErr := 0;
    N:=1;
    while N<=MaxN do
    begin
        
        //
        // FHT
        //
        SetLength(R1, N);
        SetLength(R2, N);
        I:=0;
        while I<=N-1 do
        begin
            R1[I] := 2*RandomReal-1;
            R2[I] := R1[I];
            Inc(I);
        end;
        FHTR1D(R1, N);
        RefFHTR1D(R2, N);
        I:=0;
        while I<=N-1 do
        begin
            RefErr := Max(RefErr, AbsReal(R1[I]-R2[I]));
            Inc(I);
        end;
        
        //
        // inverse FHT
        //
        SetLength(R1, N);
        SetLength(R2, N);
        I:=0;
        while I<=N-1 do
        begin
            R1[I] := 2*RandomReal-1;
            R2[I] := R1[I];
            Inc(I);
        end;
        FHTR1DInv(R1, N);
        RefFHTR1DInv(R2, N);
        I:=0;
        while I<=N-1 do
        begin
            RefErr := Max(RefErr, AbsReal(R1[I]-R2[I]));
            Inc(I);
        end;
        Inc(N);
    end;
    RefErrors := RefErrors or AP_FP_Greater(RefErr,ErrTol);
    
    //
    // end
    //
    WasErrors := BiDiErrors or RefErrors;
    if  not Silent then
    begin
        Write(Format('TESTING FHT'#13#10'',[]));
        Write(Format('FINAL RESULT:                             ',[]));
        if WasErrors then
        begin
            Write(Format('FAILED'#13#10'',[]));
        end
        else
        begin
            Write(Format('OK'#13#10'',[]));
        end;
        Write(Format('* BI-DIRECTIONAL TEST:                    ',[]));
        if BiDiErrors then
        begin
            Write(Format('FAILED'#13#10'',[]));
        end
        else
        begin
            Write(Format('OK'#13#10'',[]));
        end;
        Write(Format('* AGAINST REFERENCE FHT:                  ',[]));
        if RefErrors then
        begin
            Write(Format('FAILED'#13#10'',[]));
        end
        else
        begin
            Write(Format('OK'#13#10'',[]));
        end;
        if WasErrors then
        begin
            Write(Format('TEST FAILED'#13#10'',[]));
        end
        else
        begin
            Write(Format('TEST PASSED'#13#10'',[]));
        end;
    end;
    Result :=  not WasErrors;
end;


(*************************************************************************
Reference FHT
*************************************************************************)
procedure RefFHTR1D(var A : TReal1DArray; N : AlglibInteger);
var
    Buf : TReal1DArray;
    I : AlglibInteger;
    J : AlglibInteger;
    V : Double;
begin
    Assert(N>0, 'RefFHTR1D: incorrect N!');
    SetLength(Buf, N);
    I:=0;
    while I<=N-1 do
    begin
        V := 0;
        J:=0;
        while J<=N-1 do
        begin
            V := V+A[J]*(Cos(2*Pi*I*J/N)+Sin(2*Pi*I*J/N));
            Inc(J);
        end;
        Buf[I] := V;
        Inc(I);
    end;
    I:=0;
    while I<=N-1 do
    begin
        A[I] := Buf[I];
        Inc(I);
    end;
end;


(*************************************************************************
Reference inverse FHT
*************************************************************************)
procedure RefFHTR1DInv(var A : TReal1DArray; N : AlglibInteger);
var
    I : AlglibInteger;
begin
    Assert(N>0, 'RefFHTR1DInv: incorrect N!');
    RefFHTR1D(A, N);
    I:=0;
    while I<=N-1 do
    begin
        A[I] := A[I]/N;
        Inc(I);
    end;
end;


(*************************************************************************
Silent unit test
*************************************************************************)
function testfhtunit_test_silent():Boolean;
begin
    Result := TestFHT(True);
end;


(*************************************************************************
Unit test
*************************************************************************)
function testfhtunit_test():Boolean;
begin
    Result := TestFHT(False);
end;


end.