--  Copyright (2008-2013) Cédric Coussinet (cedric.coussinet@nomoseed.net)
--
--  This program is free software: you can redistribute it and/or modify
--  it under the terms of the GNU Affero General Public License as published
--  by the Free Software Foundation, either version 3 of the License, or
--  (at your option) any later version.
--
--  This program is distributed in the hope that it will be useful,
--  but WITHOUT ANY WARRANTY; without even the implied warranty of
--  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
--  GNU Affero General Public License for more details.

--  You should have received a copy of the GNU Affero General Public License
--  along with this program. If not, see <http://www.gnu.org/licenses/>

with Ada.Streams.Stream_IO;

with Ada.Strings.Unbounded;

with Ada.Directories;

with Basic_Sax.File_Parser;
pragma Elaborate_All (Basic_Sax.File_Parser);

with Nomo.Numerics.Accurately;
use Nomo.Numerics.Accurately;

with Nomo.Numerics.Fitting;

with Nomo.Numerics.Informations;
use Nomo.Numerics.Informations;

with Nomo.Numerics.Reals;
use Nomo.Numerics.Reals;

with Nomo.Numerics.Times;
use Nomo.Numerics.Times;

with Nomo.Type_Categories;
use Nomo.Type_Categories;

with Nomo.Interpreter_Parameters;

with Nomo.Gen_Operators;

with Nomo.Gen_Scopes;

with Nomo.Gen_External_Messages.Premises.Loading;

with Nomo.Internal_Messages.Conclusions.Loading;
use Nomo.Internal_Messages.Conclusions.Loading;

with Nomo.Premise_Properties;
use Nomo.Premise_Properties;

with Nomo.Internal_Messages.Premises.Loading;
use Nomo.Internal_Messages.Premises.Loading;

with Nomo.File_Properties;
use Nomo.File_Properties.Seed;

with Nomo.Rule_Storages_IO;

with Nomo.Gen_Factory;

with Nomo.Gen_Rule_Storages.Arrays;

with Nomo.Gen_Types_Index;

separate (Nomo.Compiler)
procedure Compile (Code_File                    : String;
                   Number_Of_Types              : Positive;
                   Number_Of_Rules              : Positive;
                   Number_Of_External_Operators : Natural;
                   Number_Of_Internal_Operators : Natural;
                   Number_Of_External_Scopes    : Natural;
                   Number_Of_Internal_Scopes    : Natural;
                   Maximum_Of_Premises          : Natural;
                   Maximum_Of_Components        : Natural;
                   Number_Of_Input_Types        : Natural) is

   package Types_Index is new Gen_Types_Index (Number_Of_Types);

   package Factory is new Gen_Factory (Types_Index,
                                       Maximum_Of_Premises,
                                       Maximum_Of_Components,
                                       Types_Index.Type_Index (Number_Of_Input_Types));
   use Factory;

   package Operators is new Gen_Operators (Number_Of_External_Operators,
                                           Number_Of_Internal_Operators,
                                           Internal_Type_Index);

   package Scopes is new Gen_Scopes(Number_Of_External_Scopes,
                                    Number_Of_Internal_Scopes,
                                    Natural(External_Type_Index'Last),
                                    Natural(Internal_Type_Index'Last),
                                    External_Type_Index,
                                    Internal_Type_Index);

   package External_Messages_Premises_Loading is new External_Messages_Premises.Loading;

   type Local_Full_Rule_Storage is new Rule_Storages.Full_Rule_Storage with null record;

   package Full_Rule_Storages_Arrays is new Rule_Storages.Arrays (Local_Full_Rule_Storage,
                                                                  Number_Of_Rules);

   type Premises_Property is array (Premises_Index.Premise_Index) of Premise_Property;

   type Code_Reader is new Basic_Sax.File_Parser.Reader with record
      Binary_Name           : access String;
      Binary_Path           : access String;
      Engine                : Interpreter_Parameters.Engine_Parameters;
      Dynamic               : Interpreter_Parameters.Dynamic_Parameters;
      External_Operators    : access Operators.External_Operators := new Operators.External_Operators;
      Internal_Operators    : access Operators.Internal_Operators := new Operators.Internal_Operators;
      External_Scopes       : access Scopes.External_Scopes := new Scopes.External_Scopes;
      Internal_Scopes       : access Scopes.Internal_Scopes := new Scopes.Internal_Scopes;
      Directory             : access Types_Directories.Types_Directory := new Types_Directories.Types_Directory;
      Rules                 : access Full_Rule_Storages_Arrays.Full_Rule_Storages := new Full_Rule_Storages_Arrays.Full_Rule_Storages;
      Type_Current          : Types_Index.Type_Index := 0;
      Rule_Current          : Natural := 0;
      Is_Internal           : Boolean;
      Information_Tolerance : Boolean;
      Time_Span_Tolerance   : Strictly_Positive_Real;
      Credibility_Tolerance : Strictly_Positive_Real;
      Information           : Positive_Integer;
      Time_Span             : Time_Interval;
      Credibility           : Real_0_To_1;
      Properties            : Premises_Property;
      Index                 : Components_Index.Component_Index;
      Time_Span_Min         : Negative_Time_Interval;
      Time_Span_Max         : Positive_Time_Interval;
      Period                : Positive_Time_Interval;
   end record;

   procedure Start_Element (Handler : in out Code_Reader;
                            Name    : in String;
                            A       : in Basic_Sax.Attributes);
   pragma Unreferenced (Start_Element);

   procedure End_Element (Handler : in out Code_Reader;
                          Name    : in String);
   pragma Unreferenced (End_Element);

   procedure Start_Element (Handler : in out Code_Reader;
                            Name    : in String;
                            A       : in Basic_Sax.Attributes) is
      use Ada.Strings.Unbounded;
      use Types_Index;
      use Factory.Components_Index;
      Local_Name : constant String := Name (Index (To_Unbounded_String (Name), ":") + 1 .. Name'Last);
      Current_Index : Natural;
      List : Unbounded_String;

      function Get_Time_Span (Attribute : in String) return Time_Interval;
      function Get_Time_Span (Attribute : in String) return Time_Interval is
      begin
         if Attribute'Length > 4 and then Attribute (Attribute'First..Attribute'First + 3) = "MIN+" then
            return Handler.Time_Span_Min + Positive_Time_Interval'Value (Attribute (Attribute'First + 4..Attribute'Last));
         elsif Attribute = "MIN" then
            return Handler.Time_Span_Min;
         else
            return Time_Interval'Value (Attribute);
         end if;
      end Get_Time_Span;

   begin
      if Local_Name = "code" then
         Handler.Engine.Number_Of_External_Operators := Number_Of_External_Operators;
         Handler.Engine.Number_Of_Internal_Operators := Number_Of_Internal_Operators;
         Handler.Engine.Number_Of_External_Scopes := Number_Of_External_Scopes;
         Handler.Engine.Number_Of_Internal_Scopes := Number_Of_Internal_Scopes;
         Handler.Engine.Number_Of_Rules := Number_Of_Rules;
         Handler.Engine.Number_Of_Types := Number_Of_Types;
         Handler.Engine.Maximum_Of_Components := Maximum_Of_Components;
         Handler.Engine.Maximum_Of_Premises := Maximum_Of_Premises;
         Handler.Engine.Number_Of_Input_Types := Number_Of_Input_Types;
         for I in 1 .. Basic_Sax.Get_Quantity (A) loop
            if Basic_Sax.Get_Name (A, I) = "unit" then
               Handler.Binary_Name := new String'(Basic_Sax.Get_Value (A, I));
            elsif Basic_Sax.Get_Name (A, I) = "binaryLocation" then
               Handler.Binary_Path := new String'(Basic_Sax.Get_Value (A, I));
            end if;
         end loop;
      elsif Local_Name = "maximum_of_premises" then
         Handler.Engine.Maximum_Of_Premises := Positive'Value (Basic_Sax.Get_Value (A, 1));
      elsif Local_Name = "maximum_of_rules_by_type" then
         Handler.Engine.Maximum_Of_Rules_By_Type := Positive'Value (Basic_Sax.Get_Value (A, 1));
      elsif Local_Name = "maximum_of_maximizations" then
         Handler.Engine.Maximum_Of_Maximizations := Numerics.Fitting.Count (Natural'Value (Basic_Sax.Get_Value (A, 1)) + 1);
      elsif Local_Name = "maximum_of_internal_events" then
         Handler.Engine.Maximum_Of_Internal_Events := Positive'Value (Basic_Sax.Get_Value (A, 1));
      elsif Local_Name = "maximum_of_external_events" then
         Handler.Engine.Maximum_Of_External_Events := Positive'Value (Basic_Sax.Get_Value (A, 1));
      elsif Local_Name = "frequency" then
         Handler.Engine.Frequency := Strictly_Positive_Real'Value (Basic_Sax.Get_Value (A, 1));
         Handler.Period := Time_Interval(1000.0 / Handler.Engine.Frequency);
      elsif Local_Name = "time_span_limit" then
         Handler.Engine.Time_Span_Limit := Positive_Time_Interval'Value (Basic_Sax.Get_Value (A, 1));
         Handler.Time_Span_Max := (Handler.Engine.Time_Span_Limit / Handler.Period) * Handler.Period;
         Handler.Time_Span_Min := (Time_Interval'First / Handler.Period) * Handler.Period;
      elsif Local_Name = "check_cover" then
         Handler.Dynamic.Check_Cover := Real'Value (Basic_Sax.Get_Value (A, 1));
      elsif Local_Name = "bid_rate" then
         Handler.Dynamic.Bid_Rate := Real_Accurately_0_To_1'Value (Basic_Sax.Get_Value (A, 1));
      elsif Local_Name = "tax_rate" then
         Handler.Dynamic.Tax_Rate := Real_Accurately_0_To_1'Value (Basic_Sax.Get_Value (A, 1));
      elsif Local_Name = "reimbursement_rate" then
         Handler.Dynamic.Reimbursement_Rate := Real_Accurately_0_To_1'Value (Basic_Sax.Get_Value (A, 1));
      elsif Local_Name = "reward_rate" then
         Handler.Dynamic.Reward_Rate := Real_Accurately_0_To_1'Value (Basic_Sax.Get_Value (A, 1));
      elsif Local_Name = "forget" then
         Handler.Dynamic.Forget := Real_Accurately_0_To_1'Value (Basic_Sax.Get_Value (A, 1));
      elsif Local_Name = "type" then
         Handler.Type_Current := Types_Index.Type_Index'Succ(Handler.Type_Current);
         for I in 1 .. Basic_Sax.Get_Quantity (A) loop
            if Basic_Sax.Get_Name (A, I) = "id" then
               pragma Assert (Handler.Type_Current = Types_Index.Type_Index'Value (Basic_Sax.Get_Value (A, I)));
               null;
            elsif Basic_Sax.Get_Name (A, I) = "category" then
               Handler.Directory.all(Handler.Type_Current).Category := Type_Category'Value (Basic_Sax.Get_Value (A, I));
            elsif Basic_Sax.Get_Name (A, I) = "predicted_by" then
               Handler.Directory.all(Handler.Type_Current).Predicted_By := Types_Index.Type_Index'Value (Basic_Sax.Get_Value (A, I));
            elsif Basic_Sax.Get_Name (A, I) = "rewarded_by" then
               Handler.Directory.all(Handler.Type_Current).Rewarded_By := Types_Index.Type_Index'Value (Basic_Sax.Get_Value (A, I));
            elsif Basic_Sax.Get_Name (A, I) = "twined_with" then
               Handler.Directory.all(Handler.Type_Current).Twined_With := Types_Index.Type_Index'Value (Basic_Sax.Get_Value (A, I));
            elsif Basic_Sax.Get_Name (A, I) = "check_associated" then
               Handler.Directory.all(Handler.Type_Current).Check_Associated := Types_Index.Type_Index'Value (Basic_Sax.Get_Value (A, I));
            elsif Basic_Sax.Get_Name (A, I) = "landmark_associated" then
               Handler.Directory.all(Handler.Type_Current).Landmark_Associated := Types_Index.Type_Index'Value (Basic_Sax.Get_Value (A, I));
            elsif Basic_Sax.Get_Name (A, I) = "input_associated" then
               Handler.Directory.all(Handler.Type_Current).Input_Associated := Types_Index.Type_Index'Value (Basic_Sax.Get_Value (A, I));
            elsif Basic_Sax.Get_Name (A, I) = "command_associated" then
               Handler.Directory.all(Handler.Type_Current).Command_Associated := Types_Index.Type_Index'Value (Basic_Sax.Get_Value (A, I));
            elsif Basic_Sax.Get_Name (A, I) = "perception_associated" then
               Handler.Directory.all(Handler.Type_Current).Perception_Associated := Types_Index.Type_Index'Value (Basic_Sax.Get_Value (A, I));
            elsif Basic_Sax.Get_Name (A, I) = "data_size" then
               Handler.Directory.all (Handler.Type_Current).Data_Size := Positive'Value (Basic_Sax.Get_Value (A, I));
            end if;
         end loop;
      elsif Local_Name = "operator_input" then
         Current_Index := 0;
         for I in 1 .. Basic_Sax.Get_Quantity (A) loop
            if Basic_Sax.Get_Name (A, I) = "id" then
               Current_Index := Positive'Value (Basic_Sax.Get_Value (A, I));
            end if;
         end loop;
         for I in 1 .. Basic_Sax.Get_Quantity (A) loop
            if Basic_Sax.Get_Name (A, I) ="information_tolerance" then
               if Basic_Sax.Get_Value (A, I) = "INF" then
                  Handler.External_Operators.all (Current_Index).Information_Tolerance := Positive_Infinity;
               elsif Positive_Real'Value (Basic_Sax.Get_Value (A, I)) = 0.0 then
                  Handler.External_Operators.all (Current_Index).Information_Tolerance := Zero_Plus;
               else
                  Handler.External_Operators.all (Current_Index).Information_Tolerance := 2.0 * Real'Value (Basic_Sax.Get_Value (A, I)) ** 2;
               end if;
            end if;
         end loop;
      elsif Local_Name = "operator_other" then
         Current_Index := 0;
         for I in 1 .. Basic_Sax.Get_Quantity (A) loop
            if Basic_Sax.Get_Name (A, I) = "id" then
               Current_Index := Positive'Value (Basic_Sax.Get_Value (A, I));
            end if;
         end loop;
         for I in 1 .. Basic_Sax.Get_Quantity (A) loop
            if Basic_Sax.Get_Name (A, I) = "relevance" then
               Handler.Internal_Operators.all (Current_Index).Relevance := Real_Accurately_0_To_1'Value (Basic_Sax.Get_Value (A, I));
               Handler.Internal_Operators.all (Current_Index).Is_Conclusion := True;
            elsif Basic_Sax.Get_Name (A, I) = "fitting_nbr" then
               if Basic_Sax.Get_Value (A, I) = "INF" then
                  Handler.Internal_Operators.all (Current_Index).Fitting_Nbr  := Handler.Engine.Maximum_Of_Maximizations;
               else
                  Handler.Internal_Operators.all (Current_Index).Fitting_Nbr  := Numerics.Fitting.Count (Natural'Value (Basic_Sax.Get_Value (A, I)) + 1);
               end if;
            elsif Basic_Sax.Get_Name (A, I) ="credibility_tolerance" then
               if Basic_Sax.Get_Value (A, I) = "INF" then
                  Handler.Internal_Operators.all (Current_Index).Credibility_Tolerance := Positive_Infinity;
               elsif Positive_Real'Value (Basic_Sax.Get_Value (A, I)) = 0.0 then
                  Handler.Internal_Operators.all (Current_Index).Credibility_Tolerance := Zero_Plus;
               else
                  Handler.Internal_Operators.all (Current_Index).Credibility_Tolerance := 2.0 * Real'Value (Basic_Sax.Get_Value (A, I)) ** 2;
               end if;
            elsif Basic_Sax.Get_Name (A, I) ="timespan_tolerance" then
               if Basic_Sax.Get_Value (A, I) = "INF" then
                  Handler.Internal_Operators.all (Current_Index).Time_Span_Tolerance := Positive_Infinity;
               elsif Positive_Real'Value (Basic_Sax.Get_Value (A, I)) = 0.0 then
                  Handler.Internal_Operators.all (Current_Index).Time_Span_Tolerance := Zero_Plus;
               else
                  Handler.Internal_Operators.all (Current_Index).Time_Span_Tolerance := 2.0 * Real'Value (Basic_Sax.Get_Value (A, I)) ** 2;
               end if;
            elsif Basic_Sax.Get_Name (A, I) ="information_tolerance" then
               if Basic_Sax.Get_Value (A, I) = "INF" then
                  Handler.Internal_Operators.all (Current_Index).Information_Tolerance := True;
               else
                  Handler.Internal_Operators.all (Current_Index).Information_Tolerance := False;
               end if;
            elsif Basic_Sax.Get_Name (A, I) ="information_value" then
               Handler.Internal_Operators.all (Current_Index).Information_Imposed := True;
               Handler.Internal_Operators.all (Current_Index).Information_Value := Positive_Integer'Value (Basic_Sax.Get_Value (A, I));
            elsif Basic_Sax.Get_Name (A, I) ="timespan_value" then
               Handler.Internal_Operators.all (Current_Index).Time_Span_Imposed := True;
               Handler.Internal_Operators.all (Current_Index).Time_Span_Value := Get_Time_Span (Basic_Sax.Get_Value (A, I));
            elsif Basic_Sax.Get_Name (A, I) ="credibility_value" then
               Handler.Internal_Operators.all (Current_Index).Credibility_Imposed := True;
               Handler.Internal_Operators.all (Current_Index).Credibility_Value := Real_0_To_1'Value (Basic_Sax.Get_Value (A, I));
            elsif Basic_Sax.Get_Name (A, I) ="type" then
               Handler.Internal_Operators.all (Current_Index).Type_Imposed := True;
               Handler.Internal_Operators.all (Current_Index).Type_Value := Internal_Type_Index'Value (Basic_Sax.Get_Value (A, I));
            end if;
         end loop;
      elsif Local_Name = "scope_input" or Local_Name = "scope_other" then
         Current_Index := 0;
         for I in 1 .. Basic_Sax.Get_Quantity (A) loop
            if Basic_Sax.Get_Name (A, I) ="target" then
               List := To_Unbounded_String(Basic_Sax.Get_Value (A, I));
            elsif Basic_Sax.Get_Name (A, I) = "id" then
               Current_Index := Positive'Value (Basic_Sax.Get_Value (A, I));
            end if;
         end loop;
         if List /= "" then
            declare
               I            : Natural := 1;
               J            : Natural := Index (List, " ", 1);
               Target_Index : Positive := 1;
            begin
               if Local_Name = "scope_input" then
                  while J/=0 loop
                     Handler.External_Scopes.all (Current_Index).List (Target_Index) := Types_Index.Type_Index'Value (Slice(List, I, J));
                     I := J + 1;
                     J := Index (List," ", I);
                     Target_Index := Target_Index + 1;
                  end loop;
                  Handler.External_Scopes.all (Current_Index).List (Target_Index) := Types_Index.Type_Index'Value (Slice (List, I, Length(List)));
                  Handler.External_Scopes.all (Current_Index).Last := Target_Index;
               else
                  while J/=0 loop
                     Handler.Internal_Scopes.all (Current_Index).List(Target_Index) := Types_Index.Type_Index'Value (Slice(List, I, J));
                     I := J + 1;
                     J := Index (List, " ", I);
                     Target_Index := Target_Index + 1;
                  end loop;
                  Handler.Internal_Scopes.all (Current_Index).List (Target_Index) := Types_Index.Type_Index'Value (Slice(List, I, Length(List)));
                  Handler.Internal_Scopes.all (Current_Index).Last := Target_Index;
               end if;
            end;
         end if;
      elsif Local_Name = "rule" then
         Handler.Rule_Current := Handler.Rule_Current + 1;
         Handler.Rules.all (Handler.Rule_Current).Internal_Condition_Types := (others => Internal_Type_Index'Last);
         Handler.Rules.all (Handler.Rule_Current).Internal_Condition_Properties := (others => Premises_Index.Premise_Index'First);
         Handler.Rules.all (Handler.Rule_Current).External_Conclusion := (others => Real'First);
         Handler.Rules.all (Handler.Rule_Current).Internal_Premises_Number := 0;
         Handler.Index := Components_Index.Component_Index'First;
         for I in 1 .. Basic_Sax.Get_Quantity (A) loop
            if Basic_Sax.Get_Name (A, I) = "id" then
               Handler.Rules.all(Handler.Rule_Current).Id := Positive'Value (Basic_Sax.Get_Value (A, I));
            elsif Basic_Sax.Get_Name (A, I) = "relevance" then
               Handler.Rules.all(Handler.Rule_Current).Relevance := Real_Accurately_0_To_1'Value (Basic_Sax.Get_Value (A, I));
            elsif Basic_Sax.Get_Name (A, I) = "fitting_nbr" then
               if Basic_Sax.Get_Value (A, I) = "INF" then
                  Handler.Rules.all(Handler.Rule_Current).Fitting_Nbr := Handler.Engine.Maximum_Of_Maximizations;
               else
                  Handler.Rules.all(Handler.Rule_Current).Fitting_Nbr := Numerics.Fitting.Count (Natural'Value (Basic_Sax.Get_Value (A, I)) + 1);
               end if;
            end if;
         end loop;
      elsif Local_Name = "conclusion" then
         declare
            use Internal_Messages;
            Category : Type_Categories.Type_Category;
            pragma Unreferenced (Category);
         begin
            Handler.Time_Span := 0;
            for I in 1 .. Basic_Sax.Get_Quantity (A) loop
               if Basic_Sax.Get_Name (A, I) = "type" then
                  Handler.Rules.all(Handler.Rule_Current).Conclusion_Type := Types_Index.Type_Index'Value (Basic_Sax.Get_Value (A, I));
               elsif Basic_Sax.Get_Name (A, I) = "category" then
                  Category := Type_Categories.Type_Category'Value (Basic_Sax.Get_Value (A, I));
               elsif Basic_Sax.Get_Name (A, I) = "value" then
                  Handler.Information := Positive_Integer'Value (Basic_Sax.Get_Value (A, I));
               elsif Basic_Sax.Get_Name (A, I) = "delay" then
                  declare
                     Attribute : constant String := Basic_Sax.Get_Value (A, I);
                  begin
                     if Attribute'Length > 4 and then Attribute (Attribute'First..Attribute'First + 3) = "MAX-" then
                        Handler.Time_Span :=  Handler.Time_Span_Min + Positive_Time_Interval'Value (Attribute (Attribute'First + 4..Attribute'Last));
                     elsif Attribute = "MAX" then
                        Handler.Time_Span :=  Handler.Time_Span_Min;
                     else
                        Handler.Time_Span := - Positive_Time_Interval'Value (Attribute);
                     end if;
                  end;
               end if;
            end loop;
            Set (Internal_Message (Handler.Rules.all(Handler.Rule_Current).Internal_Conclusion), Handler.Information, Handler.Time_Span);
         end;
      elsif Local_Name = "premise" then
         declare
            Category : Type_Categories.Type_Category;
            Id       : Types_Index.Type_Index_Strict;
         begin
            for I in 1 .. Basic_Sax.Get_Quantity (A) loop
               if Basic_Sax.Get_Name (A, I) = "type" then
                  Id := Types_Index.Type_Index_Strict'Value (Basic_Sax.Get_Value (A, I));
               elsif Basic_Sax.Get_Name (A, I) = "category" then
                  Category := Type_Categories.Type_Category'Value (Basic_Sax.Get_Value (A, I));
               end if;
            end loop;
            if Category = Input then
               Handler.Rules.all (Handler.Rule_Current).External_Condition_Type := Id;
               Handler.Is_Internal := False;
            else
               Handler.Rules.all (Handler.Rule_Current).Internal_Premises_Number := Premises_Index.Premise_Index'Succ(Handler.Rules.all(Handler.Rule_Current).Internal_Premises_Number);
               Handler.Rules.all (Handler.Rule_Current).Internal_Condition_Types (Handler.Rules.all (Handler.Rule_Current).Internal_Premises_Number) := Id;
               Handler.Is_Internal := True;
            end if;
         end;
         if Handler.Is_Internal then
            for I in 1 .. Basic_Sax.Get_Quantity (A) loop
               if Basic_Sax.Get_Name (A, I) = "property" then
                  if Basic_Sax.Get_Value (A, I) = "inhibitory" then
                     Handler.Properties (Handler.Rules.all (Handler.Rule_Current).Internal_Premises_Number) := Inhibitory_Evidence;
                  else
                     Handler.Properties (Handler.Rules.all (Handler.Rule_Current).Internal_Premises_Number) := Excitatory_Evidence;
                  end if;
               end if;
            end loop;
         end if;
      elsif Local_Name = "information" then
         if Handler.Is_Internal then
            for I in 1 .. Basic_Sax.Get_Quantity (A) loop
               if Basic_Sax.Get_Name (A, I) = "tolerance" then
                  if Basic_Sax.Get_Value (A, I) = "INF" then
                     Handler.Information_Tolerance := True;
                  else
                     Handler.Information_Tolerance := False;
                  end if;
               elsif Basic_Sax.Get_Name (A, I) = "value" then
                  Handler.Information := Positive_Integer'Value (Basic_Sax.Get_Value (A, I));
               end if;
            end loop;
         else
            declare
               use External_Messages_Premises_Loading;
               Tolerance   : Strictly_Positive_Real;
               Information : Real;
            begin
               for I in 1 .. Basic_Sax.Get_Quantity (A) loop
                  if Basic_Sax.Get_Name (A, I) = "tolerance" then
                     if Basic_Sax.Get_Value (A, I) = "INF" then
                        Tolerance := Positive_Infinity;
                     elsif Positive_Real'Value (Basic_Sax.Get_Value (A, I)) = 0.0 then
                        Tolerance := Zero_Plus;
                     else
                        Tolerance := Strictly_Positive_Real'Value (Basic_Sax.Get_Value (A, I));
                        Tolerance := 2.0 * Tolerance ** 2 ;
                     end if;
                  elsif Basic_Sax.Get_Name (A, I) = "value" then
                     Information := Real'Value (Basic_Sax.Get_Value (A, I));
                  end if;
               end loop;
               Set_Component (Handler.Rules.all(Handler.Rule_Current).External_Condition, Handler.Index, Information, Tolerance, Handler.Rules.all(Handler.Rule_Current).Fitting_Nbr);
               if Handler.Index /= Component_Index'Last then
                  Handler.Index := Component_Index'Succ (Handler.Index);
               end if;
            end;
         end if;
      elsif Local_Name = "output" then
         Handler.Rules.all (Handler.Rule_Current).External_Conclusion (Handler.Index) := Real'Value (Basic_Sax.Get_Value (A, 1));
         Handler.Rules.all (Handler.Rule_Current).External_Conclusion_Size := Handler.Index;
         if Handler.Index /= Component_Index'Last then
            Handler.Index := Component_Index'Succ(Handler.Index);
         end if;
      elsif Local_Name = "timespan" then
         for I in 1 .. Basic_Sax.Get_Quantity (A) loop
            if Basic_Sax.Get_Name (A, I) = "tolerance" then
               if Basic_Sax.Get_Value (A, I) = "INF" then
                  Handler.Time_Span_Tolerance := Positive_Infinity;
               elsif Positive_Real'Value (Basic_Sax.Get_Value (A, I)) = 0.0 then
                  Handler.Time_Span_Tolerance := Zero_Plus;
               else
                  Handler.Time_Span_Tolerance := Strictly_Positive_Real'Value (Basic_Sax.Get_Value (A, I));
                  Handler.Time_Span_Tolerance := 2.0 * Handler.Time_Span_Tolerance ** 2 ;
               end if;
            elsif Basic_Sax.Get_Name (A, I) = "value" then
               Handler.Time_Span := Get_Time_Span (Basic_Sax.Get_Value (A, I));
               if Handler.Time_Span < 0 then
                  if Handler.Properties (Handler.Rules.all (Handler.Rule_Current).Internal_Premises_Number) = Excitatory_Evidence then
                     Handler.Properties (Handler.Rules.all (Handler.Rule_Current).Internal_Premises_Number) := Excitatory_Intention;
                  else
                     Handler.Properties (Handler.Rules.all (Handler.Rule_Current).Internal_Premises_Number) := Inhibitory_Intention;
                  end if;
               end if;
            end if;
         end loop;
      elsif Local_Name = "credibility" then
         for I in 1 .. Basic_Sax.Get_Quantity (A) loop
            if Basic_Sax.Get_Name (A, I) = "tolerance" then
               if Basic_Sax.Get_Value (A, I) = "INF" then
                  Handler.Credibility_Tolerance := Positive_Infinity;
               elsif Positive_Real'Value (Basic_Sax.Get_Value (A, I)) = 0.0 then
                  Handler.Credibility_Tolerance := Zero_Plus;
               else
                  Handler.Credibility_Tolerance := Strictly_Positive_Real'Value (Basic_Sax.Get_Value (A, I));
                  Handler.Credibility_Tolerance := 2.0 * Handler.Credibility_Tolerance ** 2 ;
               end if;
            elsif Basic_Sax.Get_Name (A, I) = "value" then
               Handler.Credibility := Real_0_To_1'Value (Basic_Sax.Get_Value (A, I));
            end if;
         end loop;
      end if;
   end Start_Element;

   procedure End_Element (Handler : in out Code_Reader;
                          Name    : in String) is
      use Ada.Strings.Unbounded;
      use Types_Index;
      use Factory.Premises_Index;
      Local_Name : constant String := Name(Index(To_Unbounded_String(Name),":") + Name'First..Name'Last);
   begin
      if Local_Name = "premise" and then Handler.Is_Internal then
         Set (Handler.Rules.all (Handler.Rule_Current).Internal_Condition(Handler.Rules.all(Handler.Rule_Current).Internal_Premises_Number),
              Handler.Information,
              Handler.Information_Tolerance,
              Handler.Time_Span,
              Handler.Time_Span_Tolerance,
              Handler.Credibility,
              Handler.Credibility_Tolerance,
              Handler.Rules.all(Handler.Rule_Current).Fitting_Nbr);
      elsif Local_Name = "rule" then
         for I in Premise_Index'Succ(Premise_Index'First) .. Handler.Rules.all (Handler.Rule_Current).Internal_Premises_Number loop
            Handler.Rules.all (Handler.Rule_Current).Internal_Condition_Properties (Handler.Properties (I)) := I;
         end loop;
      end if;
   end End_Element;

   package Full_Rule_Storages_Io is new Rule_Storages_Io.Gen_Bytes (Local_Full_Rule_Storage'Size);

   procedure Write (Stream : in not null Ada.Streams.Stream_IO.Stream_Access;
                    Rule_Storage : in Local_Full_Rule_Storage);

   procedure Write (Stream : in not null Ada.Streams.Stream_IO.Stream_Access;
                    Rule_Storage : in Local_Full_Rule_Storage) is
      use Full_Rule_Storages_Io;
      Buffer : Bytes;
      for Buffer'Address use Rule_Storage'Address;
   begin
      Bytes'Write (Stream, Buffer);
   end Write;

   use Ada.Streams.Stream_IO;

   Reader        : Code_Reader;
   Binary_File   : File_Type;
   Binary_Stream : Stream_Access;
   Datas         : Data_Counts := (others => 1);
   Datas_Count   : Count;
begin

   Basic_Sax.File_Parser.Parse (Reader, Code_File);
   declare
      Binary_Name : String renames Reader.Binary_Name.all;
   begin
      if Code_File'Length /=  Binary_Name'Length + File_Properties.XML.Encoded_Unit_Extension'Length then
         Ada.Directories.Set_Directory (Code_File (Code_File'First..(Code_File'Last- Binary_Name'Length - File_Properties.XML.Encoded_Unit_Extension'Length - 1)));
      end if;
      if Reader.Binary_Path.all'Length /= 0 then
         if not Ada.Directories.Exists (Reader.Binary_Path.all) then
            Ada.Directories.Create_Path (Reader.Binary_Path.all);
         end if;
         Ada.Directories.Set_Directory (Ada.Directories.Full_Name (Reader.Binary_Path.all) );
      end if;
      Create (Binary_File, Out_File, Binary_Name & Extension);
   end;

   Binary_Stream := Stream (Binary_File);
   String'Write (Binary_Stream, Header);
   Datas_Count := Index (Binary_File);
   Data_Counts'Write (Binary_Stream, Datas);
   Datas (Engine) := Index (Binary_File);
   Interpreter_Parameters.Engine_Parameters'Write (Binary_Stream, Reader.Engine);
   Datas (Dynamic) := Index (Binary_File);
   Interpreter_Parameters.Dynamic_Parameters'Write (Binary_Stream, Reader.Dynamic);
   Datas (Types) := Index (Binary_File);
   Types_Directories.Types_Directory'Write (Binary_Stream, Reader.Directory.all);
   Datas (Rules) := Index (Binary_File);
   for I in Reader.Rules.all'Range loop
      Write (Binary_Stream, Reader.Rules.all (I));
   end loop;
   Datas (Operator_External) := Index (Binary_File);
   Operators.External_Operators'Write (Binary_Stream, Reader.External_Operators.all);
   Datas (Operator_Internal) := Index (Binary_File);
   Operators.Internal_Operators'Write (Binary_Stream, Reader.Internal_Operators.all);
   Datas (Scope_External) := Index (Binary_File);
   Scopes.External_Scopes'Write (Binary_Stream, Reader.External_Scopes.all);
   Datas (Scope_Internal) := Index (Binary_File);
   Scopes.Internal_Scopes'Write (Binary_Stream, Reader.Internal_Scopes.all);
   Set_Index (Binary_File, Datas_Count);
   Data_Counts'Write (Binary_Stream, Datas);
end Compile;
