-- Iteration control package body ( for test development )
--   This version is instrumented and may interefere with some
--   types of tests

with Cpu_Time_Clock;  -- various choices on tape
with Calendar; -- used for WALL clock times
with System; -- used to get value of TICK
with Text_Io;  -- only for diagnostics
with Duration_Io;

package body Iteration is -- A000032.ADA

--
-- CPU time variables
--
    Control_Time_Initial : Duration; -- sampled from CPU_TIME_CLOCK at beginning
    Control_Time_Final : Duration;  -- sampled from CPU_TIME_CLOCK at end
    Control_Duration : Duration; -- (FINAL-INITIAL) the measured time in seconds
    Test_Time_Initial : Duration; -- ditto for TEST
    Test_Time_Final : Duration;
    Test_Duration : Duration;
--
-- WALL time variables
--
    Wall_Control_Time_Initial : Duration; -- sampled from CLOCK at beginning
    Wall_Control_Time_Final : Duration;  -- sampled from CLOCK at end
    Wall_Control_Duration :
       Duration; -- (FINAL-INITIAL) measured time in seconds
    Wall_Test_Time_Initial : Duration; -- ditto for TEST
    Wall_Test_Time_Final : Duration;
    Wall_Test_Duration : Duration;
--
    Minimum_Time : Duration := 1.0; -- required minimum value of test time
    Temp_Time : Float; -- for scaling to microseconds
    Iteration_Count : Integer;  -- change to make timing stable
    Check : Integer; -- saved from STOP_TEST call for scaling

    procedure Start_Control is
    begin
        Control_Time_Initial := Cpu_Time_Clock;
        Wall_Control_Time_Initial := Calendar.Seconds (Calendar.Clock);
    end Start_Control;

    procedure Stop_Control (Global : Integer; Check : Integer) is
    begin
        Control_Time_Final := Cpu_Time_Clock;
        Control_Duration := Control_Time_Final - Control_Time_Initial;
        Wall_Control_Time_Final := Calendar.Seconds (Calendar.Clock);
        Wall_Control_Duration := Wall_Control_Time_Final -
                                    Wall_Control_Time_Initial;
--
        if Check /= Global then
            Text_Io.Put_Line (" Fix control loop before making measurements.");
            Text_Io.Put_Line (Integer'Image (Global) & " = GLOBAL ");
            raise Program_Error;
        end if;
        Text_Io.Put_Line ("Iteration " & Integer'Image (Iteration_Count));
        Duration_Io.Put (Control_Time_Initial);
        Duration_Io.Put (Control_Time_Final);
        Duration_Io.Put (Control_Duration);
        Text_Io.New_Line;
    end Stop_Control;

    procedure Start_Test is
    begin
        Test_Time_Initial := Cpu_Time_Clock;
        Wall_Test_Time_Initial := Calendar.Seconds (Calendar.Clock);
    end Start_Test;

    procedure Stop_Test (Global : Integer; Check : Integer) is
    begin
        Test_Time_Final := Cpu_Time_Clock;
        Test_Duration := Test_Time_Final - Test_Time_Initial;
        Wall_Test_Time_Final := Calendar.Seconds (Calendar.Clock);
        Wall_Test_Duration := Wall_Test_Time_Final - Wall_Test_Time_Initial;
--
        Iteration.Check := Check;
        if Check /= Global then
            Text_Io.Put_Line (" Fix test loop before making measurements.");
            Text_Io.Put_Line (Integer'Image (Global) & " = GLOBAL ");
            raise Program_Error;
        end if;
    end Stop_Test;

    procedure Feature_Times (Cpu_Time : out Duration;
                             Wall_Time : out Duration) is
    begin
--
--  compute scaled results
--
        begin
            Temp_Time := Float (Test_Duration - Control_Duration);
            Temp_Time := (1_000_000.0 * Temp_Time) /
                            (Float (Iteration_Count) * Float (Check));
            Cpu_Time := Duration (Temp_Time);
        exception
            when others =>  -- bail out if trouble in conversion
                Cpu_Time := 0.0;
        end;
--
        begin
            Temp_Time := Float (Wall_Test_Duration - Wall_Control_Duration);
            Temp_Time := (1_000_000.0 * Temp_Time) /
                            (Float (Iteration_Count) * Float (Check));
            Wall_Time := Duration (Temp_Time);
        exception
            when others =>
                Wall_Time := 0.0;
        end;

    end Feature_Times;


    procedure Initialize (Iteration_Count : out Integer) is
    begin
        Iteration_Count := 1;
        Iteration.Iteration_Count := 1;
    end Initialize;

    procedure Test_Stable (Iteration_Count : in out Integer;
                           Stable : out Boolean) is
    begin
        if Test_Duration > Minimum_Time then
            Stable := True;
        elsif Iteration_Count >= 16384 then
            Text_Io.Put_Line ("***** INCOMPLETE MEASUREMENT *****");
            Stable := True;
        else
            Iteration_Count := Iteration_Count + Iteration_Count;
            Iteration.Iteration_Count := Iteration_Count;
            Stable := False;
        end if;  
    end Test_Stable;


--
begin

    if System.Tick * 100 > Minimum_Time then
        Minimum_Time := System.Tick * 100;
    end if;

    if Duration'Small * 100 > Minimum_Time then
        Minimum_Time := Duration'Small * 100;
    end if;

-- MINIMUM_TIME is now the larger of 1.0 second,
--                                   100*SYSTEM.TICK,
--                                   100*DURATION'SMALL

    Control_Duration := 0.0;
    Wall_Control_Duration := 0.0;

end Iteration;