with Unchecked_Deallocation;
separate (Compilation_Unit.Produce_Metrics)
task body Halstead_State_Machine is
    --=============================================================
    -- This task body gives the user the ability to either modify =
    -- or find out the present state of the Hastead abstract      =
    -- state machine. Accept statements are provided to change the=
    -- present number of operators, operands, and parameters.     =
    -- Accept statements are also provided to calculate many of   =
    -- Halstead's metrics plus give back each all the present     =
    -- values and results. All the calculations are accomplished  =
    -- by using operations imported from various generic packages.=
    --                                                            =
    --  written by GER with help from Brad and JM                 =
    --=============================================================

    Current_Program_Unit : Program_Unit_Kind;
    -- holds the type of current program unit

    Stored_Operator : Halstead_Operator.Definition;
    -- An operator that needs to be stored, received through a
    -- task rendezvous

    type Pointer_To_Operand is access Halstead_Operand.Definition;
    Stored_Operand_Pointer : Pointer_To_Operand;
    -- An operand that needs to be stored, received through a
    -- task rendezvous
    procedure Free_Operand_Node is
       new Unchecked_Deallocation (Object => Halstead_Operand.Definition,
                                   Name => Pointer_To_Operand);

    Current_Parameter_Count : Natural := 0;
    -- The number of formal parameters

    Delta_Parameter_Count : Natural;
    -- A place holder for the parameter count

    Number_Of_Unique_Operators : Natural := 0;
    -- The current total of unique operators

    Number_Of_Unique_Operands : Natural := 0;
    -- The current total of unique operands

    --** changed 11/18/85 by J. Margono
    --** reason: definitions of n1 and n1* were mixed up
    Number_Of_Potential_Operators : Natural := 0;
    -- The current total of unique operators (n1*)

    --** changed 11/18/85 by J. Margono
    --** reason: definitions of n2 and n2* were mixed up
    Number_Of_Potential_Operands : Natural := 0;
    -- The current total of unique operands (n2*)

    Total_Number_Of_Operators : Natural := 0;
    -- The current total of all operators

    Total_Number_Of_Operands : Natural := 0;
    -- The current total of all operands

    Size_Of_Vocabulary : Halstead_Lower_Case_N.Result := 0;
    -- The current size of vocabulary for this compilation unit

    Program_Length : Halstead_Upper_Case_N.Result := 0;
    -- The current Program length for this compilation unit

    Current_Program_Volume : Halstead_V.Result := 0.0;
    -- The current program volume for this compilation unit

    Summation_Of_V_Star : Halstead_V_Star.Result := 0.0;
    -- A summation of the potential operator volume

    This_V_Star : Halstead_V_Star.Result := 0.0;
    -- An intermediate value of V* in the summation series

    Number_Of_Entries : Natural := 0;
    -- holds the number of entries in a task specification or
    -- number of accept statements in a task body

    Current_Potential_Program_Volume : Halstead_V_Star.Result := 0.0;
    -- The current amount of potential program volume

    Current_Program_Level : Halstead_L.Result := 0.0;
    -- The current program level for this compilation unit

    Current_Difficulty_Level : Halstead_D.Result := 0.0;
    -- The current difficulty level for this compilation unit

    Current_Effort : Halstead_E.Result := 0.0;
    -- The current effort for this compilation unit

    Current_Number_Of_Delivered_Bugs : Halstead_B_Hat.Result := 0.0;
    -- The current number of Delivered bugs

    Current_Error_Proneness : Halstead_D_Hat.Result := 0.0;
    -- The current amount of Halstead's error proneness

    Current_Time : Halstead_T.Result := 0.0;
    -- The current value of Halstead's time to understand

    Current_Fault_Rate : Halstead_B_Over_P.Result := 0.0;
    -- The current value of Halstead's fault rate

    Current_Physical_Lines : Loc_Physical_Lines.Count;
    -- The current number of physical lines. This total
    -- be received from the Lines-Of-Code task

    Current_Operator_List : Halstead_Operator_List.List;
    -- A list that will contain all the unique operators and
    -- their frequencies

    Current_Operand_List : Halstead_Operand_List.List;
    -- A list that will contain all the unique operands and
    -- their frequencies

    function Calculate_Lower_Case_N1
                (Operator_List : in Halstead_Operator_List.List) return Natural
        renames Halstead_Operator_List.Length_Of;
    -- Calculate the number of unique operators

    function Calculate_Lower_Case_N2
                (Operand_List : Halstead_Operand_List.List) return Natural
        renames Halstead_Operand_List.Length_Of;
    -- Calculate the number of unique operands

    function "+" (Left : in Halstead_V_Star.Result;
                  Right : in Halstead_V_Star.Result)
                 return Halstead_V_Star.Result renames Halstead_V_Star."+";
    -- A renames must be used because an implicit + sign is not
    -- directly visible in this task. We could have used a 'use' but
    -- we want to enforce the use of qualifiers.

    procedure Calculate_Upper_Case_N1
                 (N1 : out Natural;
                  Operator_List : in Halstead_Operator_List.List) is
        --=============================================================
        -- This operation is used to calculate N1 (Total number of    =
        -- operators). This is done by using a generic iterate        =
        -- procedure inside of the generic Halstead_List. The iterate =
        -- procedure will just add up the individual frequency of each=
        -- operator in the list of operator.                          =
        --                                                            =
        --  written by GER with help from Brad and JM                 =
        --=============================================================

        Initial_N1 : Natural := 0;
        -- The inital value of N1 is stored here

        procedure Add_Up_The_Operators (Item : in Halstead_Operator.Definition;
                                        Frequency_Of_Item : in Natural) is
            -- This procedure will be used to instantiate the iterate
            -- procedure in the Halstead_List generic. The procedure is
            -- used in the generic to add up the frequency of all the
            -- operators

        begin
            -- Add_Up_The_Operators
            Initial_N1 := Initial_N1 + Frequency_Of_Item;
        end Add_Up_The_Operators;

        -- Instantiate the iterate procedure in the Halstead generic.
        -- This will allow us to find out the total number of operators
        procedure Iterate_Operators is new Halstead_Operator_List.Iterate
                                              (Process => Add_Up_The_Operators);

    begin
        -- Calculate_Upper_Case_N1

        Iterate_Operators (Operator_List);
        N1 := Initial_N1;

    end Calculate_Upper_Case_N1;


    procedure Calculate_Upper_Case_N2
                 (N2 : out Natural;
                  Operand_List : in Halstead_Operand_List.List) is
        --=============================================================
        -- This operation is used to calculate N2 (Total number of    =
        -- operands). This is done by using a generic iterate         =
        -- procedure inside of the generic Halstead_List. The iterate =
        -- procedure will just add up the individual frequency of each=
        -- operand in the list of operands.                           =
        --                                                            =
        --  written by GER with help from Brad and JM                 =
        --=============================================================

        Initial_N2 : Natural := 0;
        -- The inital value of N2 is stored here

        procedure Add_Up_The_Operands (Item : in Halstead_Operand.Definition;
                                       Frequency_Of_Item : in Natural) is
            -- This procedure will be used to instantiate the iterate
            -- procedure in the Halstead_List generic. The procedure is
            -- used in the generic to add up the frequency of all the
            -- operands

        begin
            -- Add_Up_The_Operands
            Initial_N2 := Initial_N2 + Frequency_Of_Item;
        end Add_Up_The_Operands;

        -- Instantiate the iterate procedure in the Halstead generic.
        -- This will allow us to find out the total number of operands
        procedure Iterate_Operands is new Halstead_Operand_List.Iterate
                                             (Process => Add_Up_The_Operands);

    begin
        -- Calculate_Upper_Case_N2

        Iterate_Operands (Operand_List);
        N2 := Initial_N2;

    end Calculate_Upper_Case_N2;


begin
    -- Halstead_State_Machine

    -- Create initalized Halstead operand and operator lists
    Halstead_Operator_List.Create (Empty_List => Current_Operator_List);
    Halstead_Operand_List.Create (Empty_List => Current_Operand_List);

    Calculate_And_Get:
        loop

            select
                -- Store this operator in the list of operators

                accept Store_Operator (The_Operator : in
                                          Halstead_Operator.Definition) do
                    Stored_Operator := The_Operator;
                end Store_Operator;

                Halstead_Operator_List.Put (This_Item => Stored_Operator,
                                            Into => Current_Operator_List);

            or
                -- Store this operand in the list of operands

                accept Store_Operand (The_Operand : in
                                         Halstead_Operand.Definition) do
                    Stored_Operand_Pointer :=
                       new Halstead_Operand.Definition'(The_Operand);
                end Store_Operand;

                Halstead_Operand_List.Put (This_Item =>
                                              Stored_Operand_Pointer.all,
                                           Into => Current_Operand_List);

                Free_Operand_Node (X => Stored_Operand_Pointer);

            or
                -- Change the count of compilation unit parameters

                accept Change_Parameters_Count
                          (The_Amount_To_Change : in Positive := 1) do
                    Delta_Parameter_Count := The_Amount_To_Change;
                end Change_Parameters_Count;

                -- Find out the type of program unit by rendezvous with
                -- Program unit task

                Program_Unit.Get_Kind_Of_Program_Unit
                   (The_Kind_Of_Program_Unit => Current_Program_Unit);

                -- Determine the Current Parameter count from the kind
                -- of program unit

                case Current_Program_Unit is

                    when Task_Specification | Task_Type_Specification |
                         Task_Body | Task_Body_Stub =>
                        Current_Parameter_Count := Delta_Parameter_Count;

                    when others =>
                        -- accumulate number of parameters
                        Current_Parameter_Count :=
                           Delta_Parameter_Count + Current_Parameter_Count;

                end case;

            or
                -- Calculate the total Number of operators

                accept Calculate_Total_Number_Of_Operators;
                Calculate_Upper_Case_N1
                   (N1 => Total_Number_Of_Operators,
                    Operator_List => Current_Operator_List);

            or
                -- Calculate the total Number of operands

                accept Calculate_Total_Number_Of_Operands;
                Calculate_Upper_Case_N2 (N2 => Total_Number_Of_Operands,
                                         Operand_List => Current_Operand_List);

            or
                -- Calculate the number of unique operators

                accept Calculate_Number_Of_Unique_Operators;
                Number_Of_Unique_Operators :=
                   Calculate_Lower_Case_N1
                      (Operator_List => Current_Operator_List);

            or
                -- Calculate the number of unique operands

                accept Calculate_Number_Of_Unique_Operands;

                Number_Of_Unique_Operands :=
                   Calculate_Lower_Case_N2
                      (Operand_List => Current_Operand_List);

            or
                -- Calculate the size of vocabulary for this compilation
                -- unit

                accept Calculate_Size_Of_Vocabulary;

                Halstead_Lower_Case_N.Of_Any_Program_Unit
                   (N1 => Number_Of_Unique_Operators,
                    N2 => Number_Of_Unique_Operands,
                    N => Size_Of_Vocabulary);

            or
                -- Calculate the program length for this compilation unit

                accept Calculate_Program_Length;

                Halstead_Upper_Case_N.Of_Any_Program_Unit
                   (N1 => Total_Number_Of_Operators,
                    N2 => Total_Number_Of_Operands,
                    N => Program_Length);

            or
                -- Calculate the program volume for this compilation unit

                accept Calculate_Program_Volume;

                Halstead_V.Of_Any_Program_Unit (Lc_N => Size_Of_Vocabulary,
                                                Uc_N => Program_Length,
                                                V => Current_Program_Volume);

            or
                -- Calculate the potential operator (n1*)  for this
                -- compilation unit

                accept Calculate_Potential_Operator;

                -- Find out the type of program unit by rendezvous with
                -- Program unit task

                Program_Unit.Get_Kind_Of_Program_Unit
                   (The_Kind_Of_Program_Unit => Current_Program_Unit);

                -- Determine what the value of n1* is based on the kind
                -- of program unit

                case Current_Program_Unit is

                    when Package_Specification |
                         Package_Body | Package_Body_Stub =>
                        Number_Of_Potential_Operators :=
                           Natural (Halstead_Potential_Operator.
                                    Count_Of_Package);

                    when Generic_Procedure_Specification |
                         Generic_Function_Specification |
                         Generic_Package_Specification |
                         Generic_Procedure_Instantiation |
                         Generic_Function_Instantiation |
                         Generic_Package_Instantiation =>
                        Number_Of_Potential_Operators :=
                           Natural (Halstead_Potential_Operator.Count_Of_Generic
                                       (Formal_Parameters_Count =>
                                           Current_Parameter_Count));

                    when others =>
                        -- i.e., subprograms (body or spec.) and
                        -- task entries (or ACCEPT statements)

                        Number_Of_Potential_Operators :=
                           Natural (Halstead_Potential_Operator.
                                    Count_Of_Subprogram
                                       (Formal_Parameters_Count =>
                                           Current_Parameter_Count));

                end case;

            or
                -- Calculate the potential operand (n2*)  for this
                -- compilation unit

                accept Calculate_Potential_Operand;

                -- Find out the type of program unit by rendezvous with
                -- Program unit task

                Program_Unit.Get_Kind_Of_Program_Unit
                   (The_Kind_Of_Program_Unit => Current_Program_Unit);

                -- Determine what the value of n2* based on the kind
                -- of program unit

                case Current_Program_Unit is

                    when Package_Specification |
                         Package_Body | Package_Body_Stub =>
                        Number_Of_Potential_Operands :=
                           Natural (Halstead_Potential_Operand.
                                    Count_Of_Package);

                    when Generic_Procedure_Specification |
                         Generic_Function_Specification |
                         Generic_Package_Specification |
                         Generic_Procedure_Instantiation |
                         Generic_Function_Instantiation |
                         Generic_Package_Instantiation =>
                        Number_Of_Potential_Operands :=
                           Natural (Halstead_Potential_Operand.Count_Of_Generic
                                       (Formal_Parameters_Count =>
                                           Current_Parameter_Count));

                    when others =>
                        -- i.e., subprograms (body or spec.) and
                        -- task entries (or ACCEPT statements)

                        Number_Of_Potential_Operands :=
                           Natural (Halstead_Potential_Operand.
                                    Count_Of_Subprogram
                                       (Formal_Parameters_Count =>
                                           Current_Parameter_Count));

                end case;

            or
                -- Find the summation so far of V* plus the number
                -- of entries so far

                accept Calculate_Entry_Potential_Program_Volume;

                Halstead_V_Star.Of_Other_Program_Unit
                   (N1_Star => Halstead_Potential_Operator.Count
                                  (Number_Of_Potential_Operators),
                    N2_Star => Halstead_Potential_Operand.Count
                                  (Number_Of_Potential_Operands),
                    V_Star => This_V_Star);

                -- accumulate potential program volumes for all entries
                Summation_Of_V_Star := Summation_Of_V_Star + This_V_Star;

                -- increment the number of times this entry is called for
                -- use when the final version of v* is determined

                Number_Of_Entries := Number_Of_Entries + 1;

            or
                -- Calculate the potential program volume for this
                -- compilation unit

                accept Calculate_Potential_Program_Volume;

                -- Find out the type of program unit by rendezvous with
                -- Program unit task

                Program_Unit.Get_Kind_Of_Program_Unit
                   (The_Kind_Of_Program_Unit => Current_Program_Unit);

                case Current_Program_Unit is

                    when Task_Specification | Task_Type_Specification |
                         Task_Body | Task_Body_Stub =>

                        Halstead_V_Star.Of_Task
                           (V_Entry_Total => Summation_Of_V_Star,
                            Number_Of_Entries => Number_Of_Entries,
                            V_Star => Current_Potential_Program_Volume);

                    when others =>

                        Halstead_V_Star.Of_Other_Program_Unit
                           (N1_Star => Halstead_Potential_Operator.Count
                                          (Number_Of_Potential_Operators),
                            N2_Star => Halstead_Potential_Operand.Count
                                          (Number_Of_Potential_Operands),
                            V_Star => Current_Potential_Program_Volume);

                end case;

            or
                -- Calculate the language level for this compilation unit

                accept Calculate_Language_Level;

                Halstead_L.Of_Any_Program_Unit
                   (V => Current_Program_Volume,
                    V_Star => Current_Potential_Program_Volume,
                    L => Current_Program_Level);

            or
                -- Calculate the Difficulty level for this compilation
                -- unit

                accept Calculate_Difficulty_Level;

                Halstead_D.Of_Any_Program_Unit (L => Current_Program_Level,
                                                D => Current_Difficulty_Level);

            or
                -- Calculate the effort to implement for this
                -- compilation unit

                accept Calculate_Effort_To_Implement;

                Halstead_E.Of_Any_Program_Unit (V => Current_Program_Volume,
                                                L => Current_Program_Level,
                                                E => Current_Effort);

            or
                -- Calculate the delivered bugs for this compilation
                -- unit

                accept Calculate_Delivered_Bugs;

                Halstead_B_Hat.Of_Any_Program_Unit
                   (E => Current_Effort,
                    B_Hat => Current_Number_Of_Delivered_Bugs);

            or
                -- Calculate the error proneness for this compilation unit

                accept Calculate_Error_Proneness;

                Halstead_D_Hat.Of_Any_Program_Unit
                   (N1 => Halstead_Potential_Operator.Count
                             (Number_Of_Unique_Operators),
                    Lc_N2 => Halstead_Potential_Operand.Count
                                (Number_Of_Unique_Operands),
                    Uc_N2 => Total_Number_Of_Operands,
                    D_Hat => Current_Error_Proneness);

            or
                -- Calculate the Time To Understand for this compilation
                -- unit

                accept Calculate_Time_To_Understand;

                Halstead_T.Of_Any_Program_Unit
                   (E => Current_Effort, T => Current_Time);

            or
                -- Calculate the fault rate for this compilation unit

                accept Calculate_Fault_Rate;

                -- Rendezvous with the lines of code task to get the
                -- number of physical lines of code

                Loc.Get_Physical_Lines_Count
                   (Physical_Lines => Current_Physical_Lines);

                Halstead_B_Over_P.Of_Any_Program_Unit
                   (P => Current_Physical_Lines,
                    B_Over_P => Current_Fault_Rate);

            or
                -- Return the Number of unique operators for this
                -- compilation unit

                accept Get_Number_Of_Unique_Operators
                          (Lower_Case_N_1 : out Natural) do
                    Lower_Case_N_1 := Number_Of_Unique_Operators;
                end Get_Number_Of_Unique_Operators;

            or
                -- Return the Number of unique operands for this
                -- compilation unit

                accept Get_Number_Of_Unique_Operands
                          (Lower_Case_N_2 : out Natural) do
                    Lower_Case_N_2 := Number_Of_Unique_Operands;
                end Get_Number_Of_Unique_Operands;

            or
                -- Return the total Number of operators for this
                -- compilation unit

                accept Get_Total_Number_Of_Operators
                          (Upper_Case_N_1 : out Natural) do
                    Upper_Case_N_1 := Total_Number_Of_Operators;
                end Get_Total_Number_Of_Operators;

            or
                -- Return the total Number of operands for this
                -- compilation unit

                accept Get_Total_Number_Of_Operands
                          (Upper_Case_N_2 : out Natural) do
                    Upper_Case_N_2 := Total_Number_Of_Operands;
                end Get_Total_Number_Of_Operands;

            or
                -- Return the size of vocabulary for this compilation unit

                accept Get_Size_Of_Vocabulary
                          (Lower_Case_N : out Halstead_Lower_Case_N.Result) do
                    Lower_Case_N := Size_Of_Vocabulary;
                end Get_Size_Of_Vocabulary;

            or
                -- Return the program length for this compilation unit

                accept Get_Program_Length (Upper_Case_N : out
                                              Halstead_Upper_Case_N.Result) do
                    Upper_Case_N := Program_Length;
                end Get_Program_Length;

            or
                -- Return the program volume for this compilation unit

                accept Get_Program_Volume
                          (The_Program_Volume : out Halstead_V.Result) do
                    The_Program_Volume := Current_Program_Volume;
                end Get_Program_Volume;

            or
                -- Return the potential program volume for this
                -- compilation unit

                accept Get_Potential_Program_Volume
                          (V_Star : out Halstead_V_Star.Result) do
                    V_Star := Current_Potential_Program_Volume;
                end Get_Potential_Program_Volume;

            or
                -- Return the language level for this compilation unit

                accept Get_Language_Level
                          (The_Language_Level : out Halstead_L.Result) do
                    The_Language_Level := Current_Program_Level;
                end Get_Language_Level;

            or
                -- Return the Difficulty level for this compilation unit

                accept Get_Difficulty_Level
                          (The_Difficulty_Level : out Halstead_D.Result) do
                    The_Difficulty_Level := Current_Difficulty_Level;
                end Get_Difficulty_Level;

            or
                -- Return the effort to implement for this compilation
                -- unit

                accept Get_Effort_To_Implement
                          (The_Effort : out Halstead_E.Result) do
                    The_Effort := Current_Effort;
                end Get_Effort_To_Implement;

            or
                -- Return the delivered bugs for this compilation unit

                accept Get_Delivered_Bugs (B_Hat : out Halstead_B_Hat.Result) do
                    B_Hat := Current_Number_Of_Delivered_Bugs;
                end Get_Delivered_Bugs;

            or
                -- Return the error proneness for this compilation unit

                accept Get_Error_Proneness
                          (D_Hat : out Halstead_D_Hat.Result) do
                    D_Hat := Current_Error_Proneness;
                end Get_Error_Proneness;

            or
                -- Return the Time To Understand this compilation unit

                accept Get_Time_To_Understand
                          (The_Time : out Halstead_T.Result) do
                    The_Time := Current_Time;
                end Get_Time_To_Understand;

            or
                -- Return the fault rate for this compilation unit

                accept Get_Fault_Rate (B_Over_P : out
                                          Halstead_B_Over_P.Result) do
                    B_Over_P := Current_Fault_Rate;
                end Get_Fault_Rate;

            or
                -- Return the list of unique operators

                accept Get_The_Operator_List (Operator_List : in out
                                                 Halstead_Operator_List.List) do
                    Operator_List := Current_Operator_List;
                end Get_The_Operator_List;

            or
                -- Return the list of unique operands

                accept Get_The_Operand_List (Operand_List : in out
                                                Halstead_Operand_List.List) do
                    Operand_List := Current_Operand_List;
                end Get_The_Operand_List;

            or
                -- Terminate this task

                terminate;

            end select;

        end loop Calculate_And_Get;

end Halstead_State_Machine;