OSDN Git Service

2011-08-03 Olivier Hainque <hainque@adacore.com>
[pf3gnuchains/gcc-fork.git] / gcc / ada / g-pehage.adb
1 ------------------------------------------------------------------------------
2 --                                                                          --
3 --                         GNAT COMPILER COMPONENTS                         --
4 --                                                                          --
5 --        G N A T . P E R F E C T _ H A S H _ G E N E R A T O R S           --
6 --                                                                          --
7 --                                 B o d y                                  --
8 --                                                                          --
9 --                     Copyright (C) 2002-2011, AdaCore                     --
10 --                                                                          --
11 -- GNAT is free software;  you can  redistribute it  and/or modify it under --
12 -- terms of the  GNU General Public License as published  by the Free Soft- --
13 -- ware  Foundation;  either version 3,  or (at your option) any later ver- --
14 -- sion.  GNAT is distributed in the hope that it will be useful, but WITH- --
15 -- OUT ANY WARRANTY;  without even the  implied warranty of MERCHANTABILITY --
16 -- or FITNESS FOR A PARTICULAR PURPOSE.                                     --
17 --                                                                          --
18 -- As a special exception under Section 7 of GPL version 3, you are granted --
19 -- additional permissions described in the GCC Runtime Library Exception,   --
20 -- version 3.1, as published by the Free Software Foundation.               --
21 --                                                                          --
22 -- You should have received a copy of the GNU General Public License and    --
23 -- a copy of the GCC Runtime Library Exception along with this program;     --
24 -- see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see    --
25 -- <http://www.gnu.org/licenses/>.                                          --
26 --                                                                          --
27 -- GNAT was originally developed  by the GNAT team at  New York University. --
28 -- Extensive contributions were provided by Ada Core Technologies Inc.      --
29 --                                                                          --
30 ------------------------------------------------------------------------------
31
32 with Ada.IO_Exceptions;       use Ada.IO_Exceptions;
33 with Ada.Characters.Handling; use Ada.Characters.Handling;
34 with Ada.Directories;
35
36 with GNAT.Heap_Sort_G;
37 with GNAT.OS_Lib;      use GNAT.OS_Lib;
38 with GNAT.Table;
39
40 package body GNAT.Perfect_Hash_Generators is
41
42    --  We are using the algorithm of J. Czech as described in Zbigniew J.
43    --  Czech, George Havas, and Bohdan S. Majewski ``An Optimal Algorithm for
44    --  Generating Minimal Perfect Hash Functions'', Information Processing
45    --  Letters, 43(1992) pp.257-264, Oct.1992
46
47    --  This minimal perfect hash function generator is based on random graphs
48    --  and produces a hash function of the form:
49
50    --             h (w) = (g (f1 (w)) + g (f2 (w))) mod m
51
52    --  where f1 and f2 are functions that map strings into integers, and g is
53    --  a function that maps integers into [0, m-1]. h can be order preserving.
54    --  For instance, let W = {w_0, ..., w_i, ..., w_m-1}, h can be defined
55    --  such that h (w_i) = i.
56
57    --  This algorithm defines two possible constructions of f1 and f2. Method
58    --  b) stores the hash function in less memory space at the expense of
59    --  greater CPU time.
60
61    --  a) fk (w) = sum (for i in 1 .. length (w)) (Tk (i, w (i))) mod n
62
63    --     size (Tk) = max (for w in W) (length (w)) * size (used char set)
64
65    --  b) fk (w) = sum (for i in 1 .. length (w)) (Tk (i) * w (i)) mod n
66
67    --     size (Tk) = max (for w in W) (length (w)) but the table lookups are
68    --     replaced by multiplications.
69
70    --  where Tk values are randomly generated. n is defined later on but the
71    --  algorithm recommends to use a value a little bit greater than 2m. Note
72    --  that for large values of m, the main memory space requirements comes
73    --  from the memory space for storing function g (>= 2m entries).
74
75    --  Random graphs are frequently used to solve difficult problems that do
76    --  not have polynomial solutions. This algorithm is based on a weighted
77    --  undirected graph. It comprises two steps: mapping and assignment.
78
79    --  In the mapping step, a graph G = (V, E) is constructed, where = {0, 1,
80    --  ..., n-1} and E = {(for w in W) (f1 (w), f2 (w))}. In order for the
81    --  assignment step to be successful, G has to be acyclic. To have a high
82    --  probability of generating an acyclic graph, n >= 2m. If it is not
83    --  acyclic, Tk have to be regenerated.
84
85    --  In the assignment step, the algorithm builds function g. As G is
86    --  acyclic, there is a vertex v1 with only one neighbor v2. Let w_i be
87    --  the word such that v1 = f1 (w_i) and v2 = f2 (w_i). Let g (v1) = 0 by
88    --  construction and g (v2) = (i - g (v1)) mod n (or h (i) - g (v1) mod n).
89    --  If word w_j is such that v2 = f1 (w_j) and v3 = f2 (w_j), g (v3) = (j -
90    --  g (v2)) mod (or to be general, (h (j) - g (v2)) mod n). If w_i has no
91    --  neighbor, then another vertex is selected. The algorithm traverses G to
92    --  assign values to all the vertices. It cannot assign a value to an
93    --  already assigned vertex as G is acyclic.
94
95    subtype Word_Id   is Integer;
96    subtype Key_Id    is Integer;
97    subtype Vertex_Id is Integer;
98    subtype Edge_Id   is Integer;
99    subtype Table_Id  is Integer;
100
101    No_Vertex : constant Vertex_Id := -1;
102    No_Edge   : constant Edge_Id   := -1;
103    No_Table  : constant Table_Id  := -1;
104
105    type Word_Type is new String_Access;
106    procedure Free_Word (W : in out Word_Type) renames Free;
107    function New_Word (S : String) return Word_Type;
108
109    procedure Resize_Word (W : in out Word_Type; Len : Natural);
110    --  Resize string W to have a length Len
111
112    type Key_Type is record
113       Edge : Edge_Id;
114    end record;
115    --  A key corresponds to an edge in the algorithm graph
116
117    type Vertex_Type is record
118       First : Edge_Id;
119       Last  : Edge_Id;
120    end record;
121    --  A vertex can be involved in several edges. First and Last are the bounds
122    --  of an array of edges stored in a global edge table.
123
124    type Edge_Type is record
125       X   : Vertex_Id;
126       Y   : Vertex_Id;
127       Key : Key_Id;
128    end record;
129    --  An edge is a peer of vertices. In the algorithm, a key is associated to
130    --  an edge.
131
132    package WT is new GNAT.Table (Word_Type, Word_Id, 0, 32, 32);
133    package IT is new GNAT.Table (Integer, Integer, 0, 32, 32);
134    --  The two main tables. WT is used to store the words in their initial
135    --  version and in their reduced version (that is words reduced to their
136    --  significant characters). As an instance of GNAT.Table, WT does not
137    --  initialize string pointers to null. This initialization has to be done
138    --  manually when the table is allocated. IT is used to store several
139    --  tables of components containing only integers.
140
141    function Image (Int : Integer; W : Natural := 0) return String;
142    function Image (Str : String;  W : Natural := 0) return String;
143    --  Return a string which includes string Str or integer Int preceded by
144    --  leading spaces if required by width W.
145
146    function Trim_Trailing_Nuls (Str : String) return String;
147    --  Return Str with trailing NUL characters removed
148
149    Output : File_Descriptor renames GNAT.OS_Lib.Standout;
150    --  Shortcuts
151
152    EOL : constant Character := ASCII.LF;
153
154    Max  : constant := 78;
155    Last : Natural  := 0;
156    Line : String (1 .. Max);
157    --  Use this line to provide buffered IO
158
159    procedure Add (C : Character);
160    procedure Add (S : String);
161    --  Add a character or a string in Line and update Last
162
163    procedure Put
164      (F  : File_Descriptor;
165       S  : String;
166       F1 : Natural;
167       L1 : Natural;
168       C1 : Natural;
169       F2 : Natural;
170       L2 : Natural;
171       C2 : Natural);
172    --  Write string S into file F as a element of an array of one or two
173    --  dimensions. Fk (resp. Lk and Ck) indicates the first (resp last and
174    --  current) index in the k-th dimension. If F1 = L1 the array is considered
175    --  as a one dimension array. This dimension is described by F2 and L2. This
176    --  routine takes care of all the parenthesis, spaces and commas needed to
177    --  format correctly the array. Moreover, the array is well indented and is
178    --  wrapped to fit in a 80 col line. When the line is full, the routine
179    --  writes it into file F. When the array is completed, the routine adds
180    --  semi-colon and writes the line into file F.
181
182    procedure New_Line (File : File_Descriptor);
183    --  Simulate Ada.Text_IO.New_Line with GNAT.OS_Lib
184
185    procedure Put (File : File_Descriptor; Str : String);
186    --  Simulate Ada.Text_IO.Put with GNAT.OS_Lib
187
188    procedure Put_Used_Char_Set (File : File_Descriptor; Title : String);
189    --  Output a title and a used character set
190
191    procedure Put_Int_Vector
192      (File   : File_Descriptor;
193       Title  : String;
194       Vector : Integer;
195       Length : Natural);
196    --  Output a title and a vector
197
198    procedure Put_Int_Matrix
199      (File  : File_Descriptor;
200       Title : String;
201       Table : Table_Id;
202       Len_1 : Natural;
203       Len_2 : Natural);
204    --  Output a title and a matrix. When the matrix has only one non-empty
205    --  dimension (Len_2 = 0), output a vector.
206
207    procedure Put_Edges (File : File_Descriptor; Title : String);
208    --  Output a title and an edge table
209
210    procedure Put_Initial_Keys (File : File_Descriptor; Title : String);
211    --  Output a title and a key table
212
213    procedure Put_Reduced_Keys (File : File_Descriptor; Title : String);
214    --  Output a title and a key table
215
216    procedure Put_Vertex_Table (File : File_Descriptor; Title : String);
217    --  Output a title and a vertex table
218
219    function Ada_File_Base_Name (Pkg_Name : String) return String;
220    --  Return the base file name (i.e. without .ads/.adb extension) for an
221    --  Ada source file containing the named package, using the standard GNAT
222    --  file-naming convention. For example, if Pkg_Name is "Parent.Child", we
223    --  return "parent-child".
224
225    ----------------------------------
226    -- Character Position Selection --
227    ----------------------------------
228
229    --  We reduce the maximum key size by selecting representative positions
230    --  in these keys. We build a matrix with one word per line. We fill the
231    --  remaining space of a line with ASCII.NUL. The heuristic selects the
232    --  position that induces the minimum number of collisions. If there are
233    --  collisions, select another position on the reduced key set responsible
234    --  of the collisions. Apply the heuristic until there is no more collision.
235
236    procedure Apply_Position_Selection;
237    --  Apply Position selection and build the reduced key table
238
239    procedure Parse_Position_Selection (Argument : String);
240    --  Parse Argument and compute the position set. Argument is list of
241    --  substrings separated by commas. Each substring represents a position
242    --  or a range of positions (like x-y).
243
244    procedure Select_Character_Set;
245    --  Define an optimized used character set like Character'Pos in order not
246    --  to allocate tables of 256 entries.
247
248    procedure Select_Char_Position;
249    --  Find a min char position set in order to reduce the max key length. The
250    --  heuristic selects the position that induces the minimum number of
251    --  collisions. If there are collisions, select another position on the
252    --  reduced key set responsible of the collisions. Apply the heuristic until
253    --  there is no collision.
254
255    -----------------------------
256    -- Random Graph Generation --
257    -----------------------------
258
259    procedure Random (Seed : in out Natural);
260    --  Simulate Ada.Discrete_Numerics.Random
261
262    procedure Generate_Mapping_Table
263      (Tab  : Table_Id;
264       L1   : Natural;
265       L2   : Natural;
266       Seed : in out Natural);
267    --  Random generation of the tables below. T is already allocated
268
269    procedure Generate_Mapping_Tables
270      (Opt  : Optimization;
271       Seed : in out Natural);
272    --  Generate the mapping tables T1 and T2. They are used to define fk (w) =
273    --  sum (for i in 1 .. length (w)) (Tk (i, w (i))) mod n. Keys, NK and Chars
274    --  are used to compute the matrix size.
275
276    ---------------------------
277    -- Algorithm Computation --
278    ---------------------------
279
280    procedure Compute_Edges_And_Vertices (Opt : Optimization);
281    --  Compute the edge and vertex tables. These are empty when a self loop is
282    --  detected (f1 (w) = f2 (w)). The edge table is sorted by X value and then
283    --  Y value. Keys is the key table and NK the number of keys. Chars is the
284    --  set of characters really used in Keys. NV is the number of vertices
285    --  recommended by the algorithm. T1 and T2 are the mapping tables needed to
286    --  compute f1 (w) and f2 (w).
287
288    function Acyclic return Boolean;
289    --  Return True when the graph is acyclic. Vertices is the current vertex
290    --  table and Edges the current edge table.
291
292    procedure Assign_Values_To_Vertices;
293    --  Execute the assignment step of the algorithm. Keys is the current key
294    --  table. Vertices and Edges represent the random graph. G is the result of
295    --  the assignment step such that:
296    --    h (w) = (g (f1 (w)) + g (f2 (w))) mod m
297
298    function Sum
299      (Word  : Word_Type;
300       Table : Table_Id;
301       Opt   : Optimization) return Natural;
302    --  For an optimization of CPU_Time return
303    --    fk (w) = sum (for i in 1 .. length (w)) (Tk (i, w (i))) mod n
304    --  For an optimization of Memory_Space return
305    --    fk (w) = sum (for i in 1 .. length (w)) (Tk (i) * w (i)) mod n
306    --  Here NV = n
307
308    -------------------------------
309    -- Internal Table Management --
310    -------------------------------
311
312    function Allocate (N : Natural; S : Natural := 1) return Table_Id;
313    --  Allocate N * S ints from IT table
314
315    ----------
316    -- Keys --
317    ----------
318
319    Keys : Table_Id := No_Table;
320    NK   : Natural  := 0;
321    --  NK : Number of Keys
322
323    function Initial (K : Key_Id) return Word_Id;
324    pragma Inline (Initial);
325
326    function Reduced (K : Key_Id) return Word_Id;
327    pragma Inline (Reduced);
328
329    function  Get_Key (N : Key_Id) return Key_Type;
330    procedure Set_Key (N : Key_Id; Item : Key_Type);
331    --  Get or Set Nth element of Keys table
332
333    ------------------
334    -- Char_Pos_Set --
335    ------------------
336
337    Char_Pos_Set     : Table_Id := No_Table;
338    Char_Pos_Set_Len : Natural;
339    --  Character Selected Position Set
340
341    function  Get_Char_Pos (P : Natural) return Natural;
342    procedure Set_Char_Pos (P : Natural; Item : Natural);
343    --  Get or Set the string position of the Pth selected character
344
345    -------------------
346    -- Used_Char_Set --
347    -------------------
348
349    Used_Char_Set     : Table_Id := No_Table;
350    Used_Char_Set_Len : Natural;
351    --  Used Character Set : Define a new character mapping. When all the
352    --  characters are not present in the keys, in order to reduce the size
353    --  of some tables, we redefine the character mapping.
354
355    function  Get_Used_Char (C : Character) return Natural;
356    procedure Set_Used_Char (C : Character; Item : Natural);
357
358    ------------
359    -- Tables --
360    ------------
361
362    T1     : Table_Id := No_Table;
363    T2     : Table_Id := No_Table;
364    T1_Len : Natural;
365    T2_Len : Natural;
366    --  T1  : Values table to compute F1
367    --  T2  : Values table to compute F2
368
369    function  Get_Table (T : Integer; X, Y : Natural) return Natural;
370    procedure Set_Table (T : Integer; X, Y : Natural; Item : Natural);
371
372    -----------
373    -- Graph --
374    -----------
375
376    G     : Table_Id := No_Table;
377    G_Len : Natural;
378    --  Values table to compute G
379
380    NT : Natural := Default_Tries;
381    --  Number of tries running the algorithm before raising an error
382
383    function  Get_Graph (N : Natural) return Integer;
384    procedure Set_Graph (N : Natural; Item : Integer);
385    --  Get or Set Nth element of graph
386
387    -----------
388    -- Edges --
389    -----------
390
391    Edge_Size : constant := 3;
392    Edges     : Table_Id := No_Table;
393    Edges_Len : Natural;
394    --  Edges  : Edge table of the random graph G
395
396    function  Get_Edges (F : Natural) return Edge_Type;
397    procedure Set_Edges (F : Natural; Item : Edge_Type);
398
399    --------------
400    -- Vertices --
401    --------------
402
403    Vertex_Size : constant := 2;
404
405    Vertices : Table_Id := No_Table;
406    --  Vertex table of the random graph G
407
408    NV : Natural;
409    --  Number of Vertices
410
411    function  Get_Vertices (F : Natural) return Vertex_Type;
412    procedure Set_Vertices (F : Natural; Item : Vertex_Type);
413    --  Comments needed ???
414
415    K2V : Float;
416    --  Ratio between Keys and Vertices (parameter of Czech's algorithm)
417
418    Opt : Optimization;
419    --  Optimization mode (memory vs CPU)
420
421    Max_Key_Len : Natural := 0;
422    Min_Key_Len : Natural := 0;
423    --  Maximum and minimum of all the word length
424
425    S : Natural;
426    --  Seed
427
428    function Type_Size (L : Natural) return Natural;
429    --  Given the last L of an unsigned integer type T, return its size
430
431    -------------
432    -- Acyclic --
433    -------------
434
435    function Acyclic return Boolean is
436       Marks : array (0 .. NV - 1) of Vertex_Id := (others => No_Vertex);
437
438       function Traverse (Edge : Edge_Id; Mark : Vertex_Id) return Boolean;
439       --  Propagate Mark from X to Y. X is already marked. Mark Y and propagate
440       --  it to the edges of Y except the one representing the same key. Return
441       --  False when Y is marked with Mark.
442
443       --------------
444       -- Traverse --
445       --------------
446
447       function Traverse (Edge : Edge_Id; Mark : Vertex_Id) return Boolean is
448          E : constant Edge_Type := Get_Edges (Edge);
449          K : constant Key_Id    := E.Key;
450          Y : constant Vertex_Id := E.Y;
451          M : constant Vertex_Id := Marks (E.Y);
452          V : Vertex_Type;
453
454       begin
455          if M = Mark then
456             return False;
457
458          elsif M = No_Vertex then
459             Marks (Y) := Mark;
460             V := Get_Vertices (Y);
461
462             for J in V.First .. V.Last loop
463
464                --  Do not propagate to the edge representing the same key
465
466                if Get_Edges (J).Key /= K
467                  and then not Traverse (J, Mark)
468                then
469                   return False;
470                end if;
471             end loop;
472          end if;
473
474          return True;
475       end Traverse;
476
477       Edge  : Edge_Type;
478
479    --  Start of processing for Acyclic
480
481    begin
482       --  Edges valid range is
483
484       for J in 1 .. Edges_Len - 1 loop
485
486          Edge := Get_Edges (J);
487
488          --  Mark X of E when it has not been already done
489
490          if Marks (Edge.X) = No_Vertex then
491             Marks (Edge.X) := Edge.X;
492          end if;
493
494          --  Traverse E when this has not already been done
495
496          if Marks (Edge.Y) = No_Vertex
497            and then not Traverse (J, Edge.X)
498          then
499             return False;
500          end if;
501       end loop;
502
503       return True;
504    end Acyclic;
505
506    ------------------------
507    -- Ada_File_Base_Name --
508    ------------------------
509
510    function Ada_File_Base_Name (Pkg_Name : String) return String is
511    begin
512       --  Convert to lower case, then replace '.' with '-'
513
514       return Result : String := To_Lower (Pkg_Name) do
515          for J in Result'Range loop
516             if Result (J) = '.' then
517                Result (J) := '-';
518             end if;
519          end loop;
520       end return;
521    end Ada_File_Base_Name;
522
523    ---------
524    -- Add --
525    ---------
526
527    procedure Add (C : Character) is
528       pragma Assert (C /= ASCII.NUL);
529    begin
530       Line (Last + 1) := C;
531       Last := Last + 1;
532    end Add;
533
534    ---------
535    -- Add --
536    ---------
537
538    procedure Add (S : String) is
539       Len : constant Natural := S'Length;
540    begin
541       for J in S'Range loop
542          pragma Assert (S (J) /= ASCII.NUL);
543          null;
544       end loop;
545
546       Line (Last + 1 .. Last + Len) := S;
547       Last := Last + Len;
548    end Add;
549
550    --------------
551    -- Allocate --
552    --------------
553
554    function Allocate (N : Natural; S : Natural := 1) return Table_Id is
555       L : constant Integer := IT.Last;
556    begin
557       IT.Set_Last (L + N * S);
558
559       --  Initialize, so debugging printouts don't trip over uninitialized
560       --  components.
561
562       for J in L + 1 .. IT.Last loop
563          IT.Table (J) := -1;
564       end loop;
565
566       return L + 1;
567    end Allocate;
568
569    ------------------------------
570    -- Apply_Position_Selection --
571    ------------------------------
572
573    procedure Apply_Position_Selection is
574    begin
575       for J in 0 .. NK - 1 loop
576          declare
577             IW : constant String := WT.Table (Initial (J)).all;
578             RW : String (1 .. IW'Length) := (others => ASCII.NUL);
579             N  : Natural := IW'First - 1;
580
581          begin
582             --  Select the characters of Word included in the position
583             --  selection.
584
585             for C in 0 .. Char_Pos_Set_Len - 1 loop
586                exit when IW (Get_Char_Pos (C)) = ASCII.NUL;
587                N := N + 1;
588                RW (N) := IW (Get_Char_Pos (C));
589             end loop;
590
591             --  Build the new table with the reduced word. Be careful
592             --  to deallocate the old version to avoid memory leaks.
593
594             Free_Word (WT.Table (Reduced (J)));
595             WT.Table (Reduced (J)) := New_Word (RW);
596             Set_Key (J, (Edge => No_Edge));
597          end;
598       end loop;
599    end Apply_Position_Selection;
600
601    -------------------------------
602    -- Assign_Values_To_Vertices --
603    -------------------------------
604
605    procedure Assign_Values_To_Vertices is
606       X : Vertex_Id;
607
608       procedure Assign (X : Vertex_Id);
609       --  Execute assignment on X's neighbors except the vertex that we are
610       --  coming from which is already assigned.
611
612       ------------
613       -- Assign --
614       ------------
615
616       procedure Assign (X : Vertex_Id) is
617          E : Edge_Type;
618          V : constant Vertex_Type := Get_Vertices (X);
619
620       begin
621          for J in V.First .. V.Last loop
622             E := Get_Edges (J);
623
624             if Get_Graph (E.Y) = -1 then
625                Set_Graph (E.Y, (E.Key - Get_Graph (X)) mod NK);
626                Assign (E.Y);
627             end if;
628          end loop;
629       end Assign;
630
631    --  Start of processing for Assign_Values_To_Vertices
632
633    begin
634       --  Value -1 denotes an uninitialized value as it is supposed to
635       --  be in the range 0 .. NK.
636
637       if G = No_Table then
638          G_Len := NV;
639          G := Allocate (G_Len, 1);
640       end if;
641
642       for J in 0 .. G_Len - 1 loop
643          Set_Graph (J, -1);
644       end loop;
645
646       for K in 0 .. NK - 1 loop
647          X := Get_Edges (Get_Key (K).Edge).X;
648
649          if Get_Graph (X) = -1 then
650             Set_Graph (X, 0);
651             Assign (X);
652          end if;
653       end loop;
654
655       for J in 0 .. G_Len - 1 loop
656          if Get_Graph (J) = -1 then
657             Set_Graph (J, 0);
658          end if;
659       end loop;
660
661       if Verbose then
662          Put_Int_Vector (Output, "Assign Values To Vertices", G, G_Len);
663       end if;
664    end Assign_Values_To_Vertices;
665
666    -------------
667    -- Compute --
668    -------------
669
670    procedure Compute (Position : String := Default_Position) is
671       Success : Boolean := False;
672
673    begin
674       if NK = 0 then
675          raise Program_Error with "keywords set cannot be empty";
676       end if;
677
678       if Verbose then
679          Put_Initial_Keys (Output, "Initial Key Table");
680       end if;
681
682       if Position'Length /= 0 then
683          Parse_Position_Selection (Position);
684       else
685          Select_Char_Position;
686       end if;
687
688       if Verbose then
689          Put_Int_Vector
690            (Output, "Char Position Set", Char_Pos_Set, Char_Pos_Set_Len);
691       end if;
692
693       Apply_Position_Selection;
694
695       if Verbose then
696          Put_Reduced_Keys (Output, "Reduced Keys Table");
697       end if;
698
699       Select_Character_Set;
700
701       if Verbose then
702          Put_Used_Char_Set (Output, "Character Position Table");
703       end if;
704
705       --  Perform Czech's algorithm
706
707       for J in 1 .. NT loop
708          Generate_Mapping_Tables (Opt, S);
709          Compute_Edges_And_Vertices (Opt);
710
711          --  When graph is not empty (no self-loop from previous operation) and
712          --  not acyclic.
713
714          if 0 < Edges_Len and then Acyclic then
715             Success := True;
716             exit;
717          end if;
718       end loop;
719
720       if not Success then
721          raise Too_Many_Tries;
722       end if;
723
724       Assign_Values_To_Vertices;
725    end Compute;
726
727    --------------------------------
728    -- Compute_Edges_And_Vertices --
729    --------------------------------
730
731    procedure Compute_Edges_And_Vertices (Opt : Optimization) is
732       X           : Natural;
733       Y           : Natural;
734       Key         : Key_Type;
735       Edge        : Edge_Type;
736       Vertex      : Vertex_Type;
737       Not_Acyclic : Boolean := False;
738
739       procedure Move (From : Natural; To : Natural);
740       function Lt (L, R : Natural) return Boolean;
741       --  Subprograms needed for GNAT.Heap_Sort_G
742
743       --------
744       -- Lt --
745       --------
746
747       function Lt (L, R : Natural) return Boolean is
748          EL : constant Edge_Type := Get_Edges (L);
749          ER : constant Edge_Type := Get_Edges (R);
750       begin
751          return EL.X < ER.X or else (EL.X = ER.X and then EL.Y < ER.Y);
752       end Lt;
753
754       ----------
755       -- Move --
756       ----------
757
758       procedure Move (From : Natural; To : Natural) is
759       begin
760          Set_Edges (To, Get_Edges (From));
761       end Move;
762
763       package Sorting is new GNAT.Heap_Sort_G (Move, Lt);
764
765    --  Start of processing for Compute_Edges_And_Vertices
766
767    begin
768       --  We store edges from 1 to 2 * NK and leave zero alone in order to use
769       --  GNAT.Heap_Sort_G.
770
771       Edges_Len := 2 * NK + 1;
772
773       if Edges = No_Table then
774          Edges := Allocate (Edges_Len, Edge_Size);
775       end if;
776
777       if Vertices = No_Table then
778          Vertices := Allocate (NV, Vertex_Size);
779       end if;
780
781       for J in 0 .. NV - 1 loop
782          Set_Vertices (J, (No_Vertex, No_Vertex - 1));
783       end loop;
784
785       --  For each w, X = f1 (w) and Y = f2 (w)
786
787       for J in 0 .. NK - 1 loop
788          Key := Get_Key (J);
789          Key.Edge := No_Edge;
790          Set_Key (J, Key);
791
792          X := Sum (WT.Table (Reduced (J)), T1, Opt);
793          Y := Sum (WT.Table (Reduced (J)), T2, Opt);
794
795          --  Discard T1 and T2 as soon as we discover a self loop
796
797          if X = Y then
798             Not_Acyclic := True;
799             exit;
800          end if;
801
802          --  We store (X, Y) and (Y, X) to ease assignment step
803
804          Set_Edges (2 * J + 1, (X, Y, J));
805          Set_Edges (2 * J + 2, (Y, X, J));
806       end loop;
807
808       --  Return an empty graph when self loop detected
809
810       if Not_Acyclic then
811          Edges_Len := 0;
812
813       else
814          if Verbose then
815             Put_Edges      (Output, "Unsorted Edge Table");
816             Put_Int_Matrix (Output, "Function Table 1", T1,
817                             T1_Len, T2_Len);
818             Put_Int_Matrix (Output, "Function Table 2", T2,
819                             T1_Len, T2_Len);
820          end if;
821
822          --  Enforce consistency between edges and keys. Construct Vertices and
823          --  compute the list of neighbors of a vertex First .. Last as Edges
824          --  is sorted by X and then Y. To compute the neighbor list, sort the
825          --  edges.
826
827          Sorting.Sort (Edges_Len - 1);
828
829          if Verbose then
830             Put_Edges      (Output, "Sorted Edge Table");
831             Put_Int_Matrix (Output, "Function Table 1", T1,
832                             T1_Len, T2_Len);
833             Put_Int_Matrix (Output, "Function Table 2", T2,
834                             T1_Len, T2_Len);
835          end if;
836
837          --  Edges valid range is 1 .. 2 * NK
838
839          for E in 1 .. Edges_Len - 1 loop
840             Edge := Get_Edges (E);
841             Key  := Get_Key (Edge.Key);
842
843             if Key.Edge = No_Edge then
844                Key.Edge := E;
845                Set_Key (Edge.Key, Key);
846             end if;
847
848             Vertex := Get_Vertices (Edge.X);
849
850             if Vertex.First = No_Edge then
851                Vertex.First := E;
852             end if;
853
854             Vertex.Last := E;
855             Set_Vertices (Edge.X, Vertex);
856          end loop;
857
858          if Verbose then
859             Put_Reduced_Keys (Output, "Key Table");
860             Put_Edges        (Output, "Edge Table");
861             Put_Vertex_Table (Output, "Vertex Table");
862          end if;
863       end if;
864    end Compute_Edges_And_Vertices;
865
866    ------------
867    -- Define --
868    ------------
869
870    procedure Define
871      (Name      : Table_Name;
872       Item_Size : out Natural;
873       Length_1  : out Natural;
874       Length_2  : out Natural)
875    is
876    begin
877       case Name is
878          when Character_Position =>
879             Item_Size := 8;
880             Length_1  := Char_Pos_Set_Len;
881             Length_2  := 0;
882
883          when Used_Character_Set =>
884             Item_Size := 8;
885             Length_1  := 256;
886             Length_2  := 0;
887
888          when Function_Table_1
889            |  Function_Table_2 =>
890             Item_Size := Type_Size (NV);
891             Length_1  := T1_Len;
892             Length_2  := T2_Len;
893
894          when Graph_Table =>
895             Item_Size := Type_Size (NK);
896             Length_1  := NV;
897             Length_2  := 0;
898       end case;
899    end Define;
900
901    --------------
902    -- Finalize --
903    --------------
904
905    procedure Finalize is
906    begin
907       if Verbose then
908          Put (Output, "Finalize");
909          New_Line (Output);
910       end if;
911
912       --  Deallocate all the WT components (both initial and reduced
913       --  ones) to avoid memory leaks.
914
915       for W in 0 .. WT.Last loop
916          --  Note: WT.Table (NK) is a temporary variable, do not free it since
917          --  this would cause a double free.
918
919          if W /= NK then
920             Free_Word (WT.Table (W));
921          end if;
922       end loop;
923
924       WT.Release;
925       IT.Release;
926
927       --  Reset all variables for next usage
928
929       Keys := No_Table;
930
931       Char_Pos_Set     := No_Table;
932       Char_Pos_Set_Len := 0;
933
934       Used_Char_Set     := No_Table;
935       Used_Char_Set_Len := 0;
936
937       T1 := No_Table;
938       T2 := No_Table;
939
940       T1_Len := 0;
941       T2_Len := 0;
942
943       G     := No_Table;
944       G_Len := 0;
945
946       Edges     := No_Table;
947       Edges_Len := 0;
948
949       Vertices := No_Table;
950       NV       := 0;
951
952       NK := 0;
953       Max_Key_Len := 0;
954       Min_Key_Len := 0;
955    end Finalize;
956
957    ----------------------------
958    -- Generate_Mapping_Table --
959    ----------------------------
960
961    procedure Generate_Mapping_Table
962      (Tab  : Integer;
963       L1   : Natural;
964       L2   : Natural;
965       Seed : in out Natural)
966    is
967    begin
968       for J in 0 .. L1 - 1 loop
969          for K in 0 .. L2 - 1 loop
970             Random (Seed);
971             Set_Table (Tab, J, K, Seed mod NV);
972          end loop;
973       end loop;
974    end Generate_Mapping_Table;
975
976    -----------------------------
977    -- Generate_Mapping_Tables --
978    -----------------------------
979
980    procedure Generate_Mapping_Tables
981      (Opt  : Optimization;
982       Seed : in out Natural)
983    is
984    begin
985       --  If T1 and T2 are already allocated no need to do it twice. Reuse them
986       --  as their size has not changed.
987
988       if T1 = No_Table and then T2 = No_Table then
989          declare
990             Used_Char_Last : Natural := 0;
991             Used_Char      : Natural;
992
993          begin
994             if Opt = CPU_Time then
995                for P in reverse Character'Range loop
996                   Used_Char := Get_Used_Char (P);
997                   if Used_Char /= 0 then
998                      Used_Char_Last := Used_Char;
999                      exit;
1000                   end if;
1001                end loop;
1002             end if;
1003
1004             T1_Len := Char_Pos_Set_Len;
1005             T2_Len := Used_Char_Last + 1;
1006             T1 := Allocate (T1_Len * T2_Len);
1007             T2 := Allocate (T1_Len * T2_Len);
1008          end;
1009       end if;
1010
1011       Generate_Mapping_Table (T1, T1_Len, T2_Len, Seed);
1012       Generate_Mapping_Table (T2, T1_Len, T2_Len, Seed);
1013
1014       if Verbose then
1015          Put_Used_Char_Set (Output, "Used Character Set");
1016          Put_Int_Matrix (Output, "Function Table 1", T1,
1017                         T1_Len, T2_Len);
1018          Put_Int_Matrix (Output, "Function Table 2", T2,
1019                         T1_Len, T2_Len);
1020       end if;
1021    end Generate_Mapping_Tables;
1022
1023    ------------------
1024    -- Get_Char_Pos --
1025    ------------------
1026
1027    function Get_Char_Pos (P : Natural) return Natural is
1028       N : constant Natural := Char_Pos_Set + P;
1029    begin
1030       return IT.Table (N);
1031    end Get_Char_Pos;
1032
1033    ---------------
1034    -- Get_Edges --
1035    ---------------
1036
1037    function Get_Edges (F : Natural) return Edge_Type is
1038       N : constant Natural := Edges + (F * Edge_Size);
1039       E : Edge_Type;
1040    begin
1041       E.X   := IT.Table (N);
1042       E.Y   := IT.Table (N + 1);
1043       E.Key := IT.Table (N + 2);
1044       return E;
1045    end Get_Edges;
1046
1047    ---------------
1048    -- Get_Graph --
1049    ---------------
1050
1051    function Get_Graph (N : Natural) return Integer is
1052    begin
1053       return IT.Table (G + N);
1054    end Get_Graph;
1055
1056    -------------
1057    -- Get_Key --
1058    -------------
1059
1060    function Get_Key (N : Key_Id) return Key_Type is
1061       K : Key_Type;
1062    begin
1063       K.Edge := IT.Table (Keys + N);
1064       return K;
1065    end Get_Key;
1066
1067    ---------------
1068    -- Get_Table --
1069    ---------------
1070
1071    function Get_Table (T : Integer; X, Y : Natural) return Natural is
1072       N : constant Natural := T + (Y * T1_Len) + X;
1073    begin
1074       return IT.Table (N);
1075    end Get_Table;
1076
1077    -------------------
1078    -- Get_Used_Char --
1079    -------------------
1080
1081    function Get_Used_Char (C : Character) return Natural is
1082       N : constant Natural := Used_Char_Set + Character'Pos (C);
1083    begin
1084       return IT.Table (N);
1085    end Get_Used_Char;
1086
1087    ------------------
1088    -- Get_Vertices --
1089    ------------------
1090
1091    function Get_Vertices (F : Natural) return Vertex_Type is
1092       N : constant Natural := Vertices + (F * Vertex_Size);
1093       V : Vertex_Type;
1094    begin
1095       V.First := IT.Table (N);
1096       V.Last  := IT.Table (N + 1);
1097       return V;
1098    end Get_Vertices;
1099
1100    -----------
1101    -- Image --
1102    -----------
1103
1104    function Image (Int : Integer; W : Natural := 0) return String is
1105       B : String (1 .. 32);
1106       L : Natural := 0;
1107
1108       procedure Img (V : Natural);
1109       --  Compute image of V into B, starting at B (L), incrementing L
1110
1111       ---------
1112       -- Img --
1113       ---------
1114
1115       procedure Img (V : Natural) is
1116       begin
1117          if V > 9 then
1118             Img (V / 10);
1119          end if;
1120
1121          L := L + 1;
1122          B (L) := Character'Val ((V mod 10) + Character'Pos ('0'));
1123       end Img;
1124
1125    --  Start of processing for Image
1126
1127    begin
1128       if Int < 0 then
1129          L := L + 1;
1130          B (L) := '-';
1131          Img (-Int);
1132       else
1133          Img (Int);
1134       end if;
1135
1136       return Image (B (1 .. L), W);
1137    end Image;
1138
1139    -----------
1140    -- Image --
1141    -----------
1142
1143    function Image (Str : String; W : Natural := 0) return String is
1144       Len : constant Natural := Str'Length;
1145       Max : Natural := Len;
1146
1147    begin
1148       if Max < W then
1149          Max := W;
1150       end if;
1151
1152       declare
1153          Buf : String (1 .. Max) := (1 .. Max => ' ');
1154
1155       begin
1156          for J in 0 .. Len - 1 loop
1157             Buf (Max - Len + 1 + J) := Str (Str'First + J);
1158          end loop;
1159
1160          return Buf;
1161       end;
1162    end Image;
1163
1164    -------------
1165    -- Initial --
1166    -------------
1167
1168    function Initial (K : Key_Id) return Word_Id is
1169    begin
1170       return K;
1171    end Initial;
1172
1173    ----------------
1174    -- Initialize --
1175    ----------------
1176
1177    procedure Initialize
1178      (Seed   : Natural;
1179       K_To_V : Float        := Default_K_To_V;
1180       Optim  : Optimization := Memory_Space;
1181       Tries  : Positive     := Default_Tries)
1182    is
1183    begin
1184       if Verbose then
1185          Put (Output, "Initialize");
1186          New_Line (Output);
1187       end if;
1188
1189       --  Deallocate the part of the table concerning the reduced words.
1190       --  Initial words are already present in the table. We may have reduced
1191       --  words already there because a previous computation failed. We are
1192       --  currently retrying and the reduced words have to be deallocated.
1193
1194       for W in Reduced (0) .. WT.Last loop
1195          Free_Word (WT.Table (W));
1196       end loop;
1197
1198       IT.Init;
1199
1200       --  Initialize of computation variables
1201
1202       Keys := No_Table;
1203
1204       Char_Pos_Set     := No_Table;
1205       Char_Pos_Set_Len := 0;
1206
1207       Used_Char_Set     := No_Table;
1208       Used_Char_Set_Len := 0;
1209
1210       T1 := No_Table;
1211       T2 := No_Table;
1212
1213       T1_Len := 0;
1214       T2_Len := 0;
1215
1216       G     := No_Table;
1217       G_Len := 0;
1218
1219       Edges     := No_Table;
1220       Edges_Len := 0;
1221
1222       Vertices := No_Table;
1223       NV       := 0;
1224
1225       S    := Seed;
1226       K2V  := K_To_V;
1227       Opt  := Optim;
1228       NT   := Tries;
1229
1230       if K2V <= 2.0 then
1231          raise Program_Error with "K to V ratio cannot be lower than 2.0";
1232       end if;
1233
1234       --  Do not accept a value of K2V too close to 2.0 such that once
1235       --  rounded up, NV = 2 * NK because the algorithm would not converge.
1236
1237       NV := Natural (Float (NK) * K2V);
1238       if NV <= 2 * NK then
1239          NV := 2 * NK + 1;
1240       end if;
1241
1242       Keys := Allocate (NK);
1243
1244       --  Resize initial words to have all of them at the same size
1245       --  (so the size of the largest one).
1246
1247       for K in 0 .. NK - 1 loop
1248          Resize_Word (WT.Table (Initial (K)), Max_Key_Len);
1249       end loop;
1250
1251       --  Allocated the table to store the reduced words. As WT is a
1252       --  GNAT.Table (using C memory management), pointers have to be
1253       --  explicitly initialized to null.
1254
1255       WT.Set_Last (Reduced (NK - 1));
1256
1257       --  Note: Reduced (0) = NK + 1
1258
1259       WT.Table (NK) := null;
1260
1261       for W in 0 .. NK - 1 loop
1262          WT.Table (Reduced (W)) := null;
1263       end loop;
1264    end Initialize;
1265
1266    ------------
1267    -- Insert --
1268    ------------
1269
1270    procedure Insert (Value : String) is
1271       Len  : constant Natural := Value'Length;
1272
1273    begin
1274       if Verbose then
1275          Put (Output, "Inserting """ & Value & """");
1276          New_Line (Output);
1277       end if;
1278
1279       for J in Value'Range loop
1280          pragma Assert (Value (J) /= ASCII.NUL);
1281          null;
1282       end loop;
1283
1284       WT.Set_Last (NK);
1285       WT.Table (NK) := New_Word (Value);
1286       NK := NK + 1;
1287
1288       if Max_Key_Len < Len then
1289          Max_Key_Len := Len;
1290       end if;
1291
1292       if Min_Key_Len = 0 or else Len < Min_Key_Len then
1293          Min_Key_Len := Len;
1294       end if;
1295    end Insert;
1296
1297    --------------
1298    -- New_Line --
1299    --------------
1300
1301    procedure New_Line (File : File_Descriptor) is
1302    begin
1303       if Write (File, EOL'Address, 1) /= 1 then
1304          raise Program_Error;
1305       end if;
1306    end New_Line;
1307
1308    --------------
1309    -- New_Word --
1310    --------------
1311
1312    function New_Word (S : String) return Word_Type is
1313    begin
1314       return new String'(S);
1315    end New_Word;
1316
1317    ------------------------------
1318    -- Parse_Position_Selection --
1319    ------------------------------
1320
1321    procedure Parse_Position_Selection (Argument : String) is
1322       N : Natural          := Argument'First;
1323       L : constant Natural := Argument'Last;
1324       M : constant Natural := Max_Key_Len;
1325
1326       T : array (1 .. M) of Boolean := (others => False);
1327
1328       function Parse_Index return Natural;
1329       --  Parse argument starting at index N to find an index
1330
1331       -----------------
1332       -- Parse_Index --
1333       -----------------
1334
1335       function Parse_Index return Natural is
1336          C : Character := Argument (N);
1337          V : Natural   := 0;
1338
1339       begin
1340          if C = '$' then
1341             N := N + 1;
1342             return M;
1343          end if;
1344
1345          if C not in '0' .. '9' then
1346             raise Program_Error with "cannot read position argument";
1347          end if;
1348
1349          while C in '0' .. '9' loop
1350             V := V * 10 + (Character'Pos (C) - Character'Pos ('0'));
1351             N := N + 1;
1352             exit when L < N;
1353             C := Argument (N);
1354          end loop;
1355
1356          return V;
1357       end Parse_Index;
1358
1359    --  Start of processing for Parse_Position_Selection
1360
1361    begin
1362       --  Empty specification means all the positions
1363
1364       if L < N then
1365          Char_Pos_Set_Len := M;
1366          Char_Pos_Set := Allocate (Char_Pos_Set_Len);
1367
1368          for C in 0 .. Char_Pos_Set_Len - 1 loop
1369             Set_Char_Pos (C, C + 1);
1370          end loop;
1371
1372       else
1373          loop
1374             declare
1375                First, Last : Natural;
1376
1377             begin
1378                First := Parse_Index;
1379                Last  := First;
1380
1381                --  Detect a range
1382
1383                if N <= L and then Argument (N) = '-' then
1384                   N := N + 1;
1385                   Last := Parse_Index;
1386                end if;
1387
1388                --  Include the positions in the selection
1389
1390                for J in First .. Last loop
1391                   T (J) := True;
1392                end loop;
1393             end;
1394
1395             exit when L < N;
1396
1397             if Argument (N) /= ',' then
1398                raise Program_Error with "cannot read position argument";
1399             end if;
1400
1401             N := N + 1;
1402          end loop;
1403
1404          --  Compute position selection length
1405
1406          N := 0;
1407          for J in T'Range loop
1408             if T (J) then
1409                N := N + 1;
1410             end if;
1411          end loop;
1412
1413          --  Fill position selection
1414
1415          Char_Pos_Set_Len := N;
1416          Char_Pos_Set := Allocate (Char_Pos_Set_Len);
1417
1418          N := 0;
1419          for J in T'Range loop
1420             if T (J) then
1421                Set_Char_Pos (N, J);
1422                N := N + 1;
1423             end if;
1424          end loop;
1425       end if;
1426    end Parse_Position_Selection;
1427
1428    -------------
1429    -- Produce --
1430    -------------
1431
1432    procedure Produce
1433      (Pkg_Name   : String  := Default_Pkg_Name;
1434       Use_Stdout : Boolean := False)
1435    is
1436       File : File_Descriptor := Standout;
1437
1438       Status : Boolean;
1439       --  For call to Close
1440
1441       function Array_Img (N, T, R1 : String; R2 : String := "") return String;
1442       --  Return string "N : constant array (R1[, R2]) of T;"
1443
1444       function Range_Img (F, L : Natural; T : String := "") return String;
1445       --  Return string "[T range ]F .. L"
1446
1447       function Type_Img (L : Natural) return String;
1448       --  Return the larger unsigned type T such that T'Last < L
1449
1450       ---------------
1451       -- Array_Img --
1452       ---------------
1453
1454       function Array_Img
1455         (N, T, R1 : String;
1456          R2       : String := "") return String
1457       is
1458       begin
1459          Last := 0;
1460          Add ("   ");
1461          Add (N);
1462          Add (" : constant array (");
1463          Add (R1);
1464
1465          if R2 /= "" then
1466             Add (", ");
1467             Add (R2);
1468          end if;
1469
1470          Add (") of ");
1471          Add (T);
1472          Add (" :=");
1473          return Line (1 .. Last);
1474       end Array_Img;
1475
1476       ---------------
1477       -- Range_Img --
1478       ---------------
1479
1480       function Range_Img (F, L : Natural; T : String := "") return String is
1481          FI  : constant String  := Image (F);
1482          FL  : constant Natural := FI'Length;
1483          LI  : constant String  := Image (L);
1484          LL  : constant Natural := LI'Length;
1485          TL  : constant Natural := T'Length;
1486          RI  : String (1 .. TL + 7 + FL + 4 + LL);
1487          Len : Natural := 0;
1488
1489       begin
1490          if TL /= 0 then
1491             RI (Len + 1 .. Len + TL) := T;
1492             Len := Len + TL;
1493             RI (Len + 1 .. Len + 7) := " range ";
1494             Len := Len + 7;
1495          end if;
1496
1497          RI (Len + 1 .. Len + FL) := FI;
1498          Len := Len + FL;
1499          RI (Len + 1 .. Len + 4) := " .. ";
1500          Len := Len + 4;
1501          RI (Len + 1 .. Len + LL) := LI;
1502          Len := Len + LL;
1503          return RI (1 .. Len);
1504       end Range_Img;
1505
1506       --------------
1507       -- Type_Img --
1508       --------------
1509
1510       function Type_Img (L : Natural) return String is
1511          S : constant String := Image (Type_Size (L));
1512          U : String  := "Unsigned_  ";
1513          N : Natural := 9;
1514
1515       begin
1516          for J in S'Range loop
1517             N := N + 1;
1518             U (N) := S (J);
1519          end loop;
1520
1521          return U (1 .. N);
1522       end Type_Img;
1523
1524       F : Natural;
1525       L : Natural;
1526       P : Natural;
1527
1528       FName : String := Ada_File_Base_Name (Pkg_Name) & ".ads";
1529       --  Initially, the name of the spec file, then modified to be the name of
1530       --  the body file. Not used if Use_Stdout is True.
1531
1532    --  Start of processing for Produce
1533
1534    begin
1535
1536       if Verbose and then not Use_Stdout then
1537          Put (Output,
1538               "Producing " & Ada.Directories.Current_Directory & "/" & FName);
1539          New_Line (Output);
1540       end if;
1541
1542       if not Use_Stdout then
1543          File := Create_File (FName, Binary);
1544
1545          if File = Invalid_FD then
1546             raise Program_Error with "cannot create: " & FName;
1547          end if;
1548       end if;
1549
1550       Put      (File, "package ");
1551       Put      (File, Pkg_Name);
1552       Put      (File, " is");
1553       New_Line (File);
1554       Put      (File, "   function Hash (S : String) return Natural;");
1555       New_Line (File);
1556       Put      (File, "end ");
1557       Put      (File, Pkg_Name);
1558       Put      (File, ";");
1559       New_Line (File);
1560
1561       if not Use_Stdout then
1562          Close (File, Status);
1563
1564          if not Status then
1565             raise Device_Error;
1566          end if;
1567       end if;
1568
1569       if not Use_Stdout then
1570
1571          --  Set to body file name
1572
1573          FName (FName'Last) := 'b';
1574
1575          File := Create_File (FName, Binary);
1576
1577          if File = Invalid_FD then
1578             raise Program_Error with "cannot create: " & FName;
1579          end if;
1580       end if;
1581
1582       Put      (File, "with Interfaces; use Interfaces;");
1583       New_Line (File);
1584       New_Line (File);
1585       Put      (File, "package body ");
1586       Put      (File, Pkg_Name);
1587       Put      (File, " is");
1588       New_Line (File);
1589       New_Line (File);
1590
1591       if Opt = CPU_Time then
1592          Put      (File, Array_Img ("C", Type_Img (256), "Character"));
1593          New_Line (File);
1594
1595          F := Character'Pos (Character'First);
1596          L := Character'Pos (Character'Last);
1597
1598          for J in Character'Range loop
1599             P := Get_Used_Char (J);
1600             Put (File, Image (P), 1, 0, 1, F, L, Character'Pos (J));
1601          end loop;
1602
1603          New_Line (File);
1604       end if;
1605
1606       F := 0;
1607       L := Char_Pos_Set_Len - 1;
1608
1609       Put      (File, Array_Img ("P", "Natural", Range_Img (F, L)));
1610       New_Line (File);
1611
1612       for J in F .. L loop
1613          Put (File, Image (Get_Char_Pos (J)), 1, 0, 1, F, L, J);
1614       end loop;
1615
1616       New_Line (File);
1617
1618       case Opt is
1619          when CPU_Time =>
1620             Put_Int_Matrix
1621               (File,
1622                Array_Img ("T1", Type_Img (NV),
1623                           Range_Img (0, T1_Len - 1),
1624                           Range_Img (0, T2_Len - 1, Type_Img (256))),
1625                T1, T1_Len, T2_Len);
1626
1627          when Memory_Space =>
1628             Put_Int_Matrix
1629               (File,
1630                Array_Img ("T1", Type_Img (NV),
1631                           Range_Img (0, T1_Len - 1)),
1632                T1, T1_Len, 0);
1633       end case;
1634
1635       New_Line (File);
1636
1637       case Opt is
1638          when CPU_Time =>
1639             Put_Int_Matrix
1640               (File,
1641                Array_Img ("T2", Type_Img (NV),
1642                           Range_Img (0, T1_Len - 1),
1643                           Range_Img (0, T2_Len - 1, Type_Img (256))),
1644                T2, T1_Len, T2_Len);
1645
1646          when Memory_Space =>
1647             Put_Int_Matrix
1648               (File,
1649                Array_Img ("T2", Type_Img (NV),
1650                           Range_Img (0, T1_Len - 1)),
1651                T2, T1_Len, 0);
1652       end case;
1653
1654       New_Line (File);
1655
1656       Put_Int_Vector
1657         (File,
1658          Array_Img ("G", Type_Img (NK),
1659                     Range_Img (0, G_Len - 1)),
1660          G, G_Len);
1661       New_Line (File);
1662
1663       Put      (File, "   function Hash (S : String) return Natural is");
1664       New_Line (File);
1665       Put      (File, "      F : constant Natural := S'First - 1;");
1666       New_Line (File);
1667       Put      (File, "      L : constant Natural := S'Length;");
1668       New_Line (File);
1669       Put      (File, "      F1, F2 : Natural := 0;");
1670       New_Line (File);
1671
1672       Put (File, "      J : ");
1673
1674       case Opt is
1675          when CPU_Time =>
1676             Put (File, Type_Img (256));
1677          when Memory_Space =>
1678             Put (File, "Natural");
1679       end case;
1680
1681       Put (File, ";");
1682       New_Line (File);
1683
1684       Put      (File, "   begin");
1685       New_Line (File);
1686       Put      (File, "      for K in P'Range loop");
1687       New_Line (File);
1688       Put      (File, "         exit when L < P (K);");
1689       New_Line (File);
1690       Put      (File, "         J  := ");
1691
1692       case Opt is
1693          when CPU_Time =>
1694             Put (File, "C");
1695          when Memory_Space =>
1696             Put (File, "Character'Pos");
1697       end case;
1698
1699       Put      (File, " (S (P (K) + F));");
1700       New_Line (File);
1701
1702       Put (File, "         F1 := (F1 + Natural (T1 (K");
1703
1704       if Opt = CPU_Time then
1705          Put (File, ", J");
1706       end if;
1707
1708       Put (File, "))");
1709
1710       if Opt = Memory_Space then
1711          Put (File, " * J");
1712       end if;
1713
1714       Put      (File, ") mod ");
1715       Put      (File, Image (NV));
1716       Put      (File, ";");
1717       New_Line (File);
1718
1719       Put (File, "         F2 := (F2 + Natural (T2 (K");
1720
1721       if Opt = CPU_Time then
1722          Put (File, ", J");
1723       end if;
1724
1725       Put (File, "))");
1726
1727       if Opt = Memory_Space then
1728          Put (File, " * J");
1729       end if;
1730
1731       Put      (File, ") mod ");
1732       Put      (File, Image (NV));
1733       Put      (File, ";");
1734       New_Line (File);
1735
1736       Put      (File, "      end loop;");
1737       New_Line (File);
1738
1739       Put      (File,
1740                 "      return (Natural (G (F1)) + Natural (G (F2))) mod ");
1741
1742       Put      (File, Image (NK));
1743       Put      (File, ";");
1744       New_Line (File);
1745       Put      (File, "   end Hash;");
1746       New_Line (File);
1747       New_Line (File);
1748       Put      (File, "end ");
1749       Put      (File, Pkg_Name);
1750       Put      (File, ";");
1751       New_Line (File);
1752
1753       if not Use_Stdout then
1754          Close (File, Status);
1755
1756          if not Status then
1757             raise Device_Error;
1758          end if;
1759       end if;
1760    end Produce;
1761
1762    ---------
1763    -- Put --
1764    ---------
1765
1766    procedure Put (File : File_Descriptor; Str : String) is
1767       Len : constant Natural := Str'Length;
1768    begin
1769       for J in Str'Range loop
1770          pragma Assert (Str (J) /= ASCII.NUL);
1771          null;
1772       end loop;
1773
1774       if Write (File, Str'Address, Len) /= Len then
1775          raise Program_Error;
1776       end if;
1777    end Put;
1778
1779    ---------
1780    -- Put --
1781    ---------
1782
1783    procedure Put
1784      (F  : File_Descriptor;
1785       S  : String;
1786       F1 : Natural;
1787       L1 : Natural;
1788       C1 : Natural;
1789       F2 : Natural;
1790       L2 : Natural;
1791       C2 : Natural)
1792    is
1793       Len : constant Natural := S'Length;
1794
1795       procedure Flush;
1796       --  Write current line, followed by LF
1797
1798       -----------
1799       -- Flush --
1800       -----------
1801
1802       procedure Flush is
1803       begin
1804          Put (F, Line (1 .. Last));
1805          New_Line (F);
1806          Last := 0;
1807       end Flush;
1808
1809    --  Start of processing for Put
1810
1811    begin
1812       if C1 = F1 and then C2 = F2 then
1813          Last := 0;
1814       end if;
1815
1816       if Last + Len + 3 >= Max then
1817          Flush;
1818       end if;
1819
1820       if Last = 0 then
1821          Add ("     ");
1822
1823          if F1 <= L1 then
1824             if C1 = F1 and then C2 = F2 then
1825                Add ('(');
1826
1827                if F1 = L1 then
1828                   Add ("0 .. 0 => ");
1829                end if;
1830
1831             else
1832                Add (' ');
1833             end if;
1834          end if;
1835       end if;
1836
1837       if C2 = F2 then
1838          Add ('(');
1839
1840          if F2 = L2 then
1841             Add ("0 .. 0 => ");
1842          end if;
1843
1844       else
1845          Add (' ');
1846       end if;
1847
1848       Add (S);
1849
1850       if C2 = L2 then
1851          Add (')');
1852
1853          if F1 > L1 then
1854             Add (';');
1855             Flush;
1856
1857          elsif C1 /= L1 then
1858             Add (',');
1859             Flush;
1860
1861          else
1862             Add (')');
1863             Add (';');
1864             Flush;
1865          end if;
1866
1867       else
1868          Add (',');
1869       end if;
1870    end Put;
1871
1872    ---------------
1873    -- Put_Edges --
1874    ---------------
1875
1876    procedure Put_Edges (File  : File_Descriptor; Title : String) is
1877       E  : Edge_Type;
1878       F1 : constant Natural := 1;
1879       L1 : constant Natural := Edges_Len - 1;
1880       M  : constant Natural := Max / 5;
1881
1882    begin
1883       Put (File, Title);
1884       New_Line (File);
1885
1886       --  Edges valid range is 1 .. Edge_Len - 1
1887
1888       for J in F1 .. L1 loop
1889          E := Get_Edges (J);
1890          Put (File, Image (J, M),     F1, L1, J, 1, 4, 1);
1891          Put (File, Image (E.X, M),   F1, L1, J, 1, 4, 2);
1892          Put (File, Image (E.Y, M),   F1, L1, J, 1, 4, 3);
1893          Put (File, Image (E.Key, M), F1, L1, J, 1, 4, 4);
1894       end loop;
1895    end Put_Edges;
1896
1897    ----------------------
1898    -- Put_Initial_Keys --
1899    ----------------------
1900
1901    procedure Put_Initial_Keys (File : File_Descriptor; Title : String) is
1902       F1 : constant Natural := 0;
1903       L1 : constant Natural := NK - 1;
1904       M  : constant Natural := Max / 5;
1905       K  : Key_Type;
1906
1907    begin
1908       Put (File, Title);
1909       New_Line (File);
1910
1911       for J in F1 .. L1 loop
1912          K := Get_Key (J);
1913          Put (File, Image (J, M),           F1, L1, J, 1, 3, 1);
1914          Put (File, Image (K.Edge, M),      F1, L1, J, 1, 3, 2);
1915          Put (File, Trim_Trailing_Nuls (WT.Table (Initial (J)).all),
1916                     F1, L1, J, 1, 3, 3);
1917       end loop;
1918    end Put_Initial_Keys;
1919
1920    --------------------
1921    -- Put_Int_Matrix --
1922    --------------------
1923
1924    procedure Put_Int_Matrix
1925      (File   : File_Descriptor;
1926       Title  : String;
1927       Table  : Integer;
1928       Len_1  : Natural;
1929       Len_2  : Natural)
1930    is
1931       F1 : constant Integer := 0;
1932       L1 : constant Integer := Len_1 - 1;
1933       F2 : constant Integer := 0;
1934       L2 : constant Integer := Len_2 - 1;
1935       Ix : Natural;
1936
1937    begin
1938       Put (File, Title);
1939       New_Line (File);
1940
1941       if Len_2 = 0 then
1942          for J in F1 .. L1 loop
1943             Ix := IT.Table (Table + J);
1944             Put (File, Image (Ix), 1, 0, 1, F1, L1, J);
1945          end loop;
1946
1947       else
1948          for J in F1 .. L1 loop
1949             for K in F2 .. L2 loop
1950                Ix := IT.Table (Table + J + K * Len_1);
1951                Put (File, Image (Ix), F1, L1, J, F2, L2, K);
1952             end loop;
1953          end loop;
1954       end if;
1955    end Put_Int_Matrix;
1956
1957    --------------------
1958    -- Put_Int_Vector --
1959    --------------------
1960
1961    procedure Put_Int_Vector
1962      (File   : File_Descriptor;
1963       Title  : String;
1964       Vector : Integer;
1965       Length : Natural)
1966    is
1967       F2 : constant Natural := 0;
1968       L2 : constant Natural := Length - 1;
1969
1970    begin
1971       Put (File, Title);
1972       New_Line (File);
1973
1974       for J in F2 .. L2 loop
1975          Put (File, Image (IT.Table (Vector + J)), 1, 0, 1, F2, L2, J);
1976       end loop;
1977    end Put_Int_Vector;
1978
1979    ----------------------
1980    -- Put_Reduced_Keys --
1981    ----------------------
1982
1983    procedure Put_Reduced_Keys (File : File_Descriptor; Title : String) is
1984       F1 : constant Natural := 0;
1985       L1 : constant Natural := NK - 1;
1986       M  : constant Natural := Max / 5;
1987       K  : Key_Type;
1988
1989    begin
1990       Put (File, Title);
1991       New_Line (File);
1992
1993       for J in F1 .. L1 loop
1994          K := Get_Key (J);
1995          Put (File, Image (J, M),           F1, L1, J, 1, 3, 1);
1996          Put (File, Image (K.Edge, M),      F1, L1, J, 1, 3, 2);
1997          Put (File, Trim_Trailing_Nuls (WT.Table (Reduced (J)).all),
1998                     F1, L1, J, 1, 3, 3);
1999       end loop;
2000    end Put_Reduced_Keys;
2001
2002    -----------------------
2003    -- Put_Used_Char_Set --
2004    -----------------------
2005
2006    procedure Put_Used_Char_Set (File : File_Descriptor; Title : String) is
2007       F : constant Natural := Character'Pos (Character'First);
2008       L : constant Natural := Character'Pos (Character'Last);
2009
2010    begin
2011       Put (File, Title);
2012       New_Line (File);
2013
2014       for J in Character'Range loop
2015          Put
2016            (File, Image (Get_Used_Char (J)), 1, 0, 1, F, L, Character'Pos (J));
2017       end loop;
2018    end Put_Used_Char_Set;
2019
2020    ----------------------
2021    -- Put_Vertex_Table --
2022    ----------------------
2023
2024    procedure Put_Vertex_Table (File : File_Descriptor; Title : String) is
2025       F1 : constant Natural := 0;
2026       L1 : constant Natural := NV - 1;
2027       M  : constant Natural := Max / 4;
2028       V  : Vertex_Type;
2029
2030    begin
2031       Put (File, Title);
2032       New_Line (File);
2033
2034       for J in F1 .. L1 loop
2035          V := Get_Vertices (J);
2036          Put (File, Image (J, M),       F1, L1, J, 1, 3, 1);
2037          Put (File, Image (V.First, M), F1, L1, J, 1, 3, 2);
2038          Put (File, Image (V.Last, M),  F1, L1, J, 1, 3, 3);
2039       end loop;
2040    end Put_Vertex_Table;
2041
2042    ------------
2043    -- Random --
2044    ------------
2045
2046    procedure Random (Seed : in out Natural) is
2047
2048       --  Park & Miller Standard Minimal using Schrage's algorithm to avoid
2049       --  overflow: Xn+1 = 16807 * Xn mod (2 ** 31 - 1)
2050
2051       R : Natural;
2052       Q : Natural;
2053       X : Integer;
2054
2055    begin
2056       R := Seed mod 127773;
2057       Q := Seed / 127773;
2058       X := 16807 * R - 2836 * Q;
2059
2060       Seed := (if X < 0 then X + 2147483647 else X);
2061    end Random;
2062
2063    -------------
2064    -- Reduced --
2065    -------------
2066
2067    function Reduced (K : Key_Id) return Word_Id is
2068    begin
2069       return K + NK + 1;
2070    end Reduced;
2071
2072    -----------------
2073    -- Resize_Word --
2074    -----------------
2075
2076    procedure Resize_Word (W : in out Word_Type; Len : Natural) is
2077       S1 : constant String := W.all;
2078       S2 : String (1 .. Len) := (others => ASCII.NUL);
2079       L  : constant Natural := S1'Length;
2080    begin
2081       if L /= Len then
2082          Free_Word (W);
2083          S2 (1 .. L) := S1;
2084          W := New_Word (S2);
2085       end if;
2086    end Resize_Word;
2087
2088    --------------------------
2089    -- Select_Char_Position --
2090    --------------------------
2091
2092    procedure Select_Char_Position is
2093
2094       type Vertex_Table_Type is array (Natural range <>) of Vertex_Type;
2095
2096       procedure Build_Identical_Keys_Sets
2097         (Table : in out Vertex_Table_Type;
2098          Last  : in out Natural;
2099          Pos   : Natural);
2100       --  Build a list of keys subsets that are identical with the current
2101       --  position selection plus Pos. Once this routine is called, reduced
2102       --  words are sorted by subsets and each item (First, Last) in Sets
2103       --  defines the range of identical keys.
2104       --  Need comment saying exactly what Last is ???
2105
2106       function Count_Different_Keys
2107         (Table : Vertex_Table_Type;
2108          Last  : Natural;
2109          Pos   : Natural) return Natural;
2110       --  For each subset in Sets, count the number of different keys if we add
2111       --  Pos to the current position selection.
2112
2113       Sel_Position : IT.Table_Type (1 .. Max_Key_Len);
2114       Last_Sel_Pos : Natural := 0;
2115       Max_Sel_Pos  : Natural := 0;
2116
2117       -------------------------------
2118       -- Build_Identical_Keys_Sets --
2119       -------------------------------
2120
2121       procedure Build_Identical_Keys_Sets
2122         (Table : in out Vertex_Table_Type;
2123          Last  : in out Natural;
2124          Pos   : Natural)
2125       is
2126          S : constant Vertex_Table_Type := Table (Table'First .. Last);
2127          C : constant Natural           := Pos;
2128          --  Shortcuts (why are these not renames ???)
2129
2130          F : Integer;
2131          L : Integer;
2132          --  First and last words of a subset
2133
2134          Offset : Natural;
2135          --  GNAT.Heap_Sort assumes that the first array index is 1. Offset
2136          --  defines the translation to operate.
2137
2138          function Lt (L, R : Natural) return Boolean;
2139          procedure Move (From : Natural; To : Natural);
2140          --  Subprograms needed by GNAT.Heap_Sort_G
2141
2142          --------
2143          -- Lt --
2144          --------
2145
2146          function Lt (L, R : Natural) return Boolean is
2147             C     : constant Natural := Pos;
2148             Left  : Natural;
2149             Right : Natural;
2150
2151          begin
2152             if L = 0 then
2153                Left  := NK;
2154                Right := Offset + R;
2155             elsif R = 0 then
2156                Left  := Offset + L;
2157                Right := NK;
2158             else
2159                Left  := Offset + L;
2160                Right := Offset + R;
2161             end if;
2162
2163             return WT.Table (Left)(C) < WT.Table (Right)(C);
2164          end Lt;
2165
2166          ----------
2167          -- Move --
2168          ----------
2169
2170          procedure Move (From : Natural; To : Natural) is
2171             Target, Source : Natural;
2172
2173          begin
2174             if From = 0 then
2175                Source := NK;
2176                Target := Offset + To;
2177             elsif To = 0 then
2178                Source := Offset + From;
2179                Target := NK;
2180             else
2181                Source := Offset + From;
2182                Target := Offset + To;
2183             end if;
2184
2185             WT.Table (Target) := WT.Table (Source);
2186             WT.Table (Source) := null;
2187          end Move;
2188
2189          package Sorting is new GNAT.Heap_Sort_G (Move, Lt);
2190
2191       --  Start of processing for Build_Identical_Key_Sets
2192
2193       begin
2194          Last := 0;
2195
2196          --  For each subset in S, extract the new subsets we have by adding C
2197          --  in the position selection.
2198
2199          for J in S'Range loop
2200             if S (J).First = S (J).Last then
2201                F := S (J).First;
2202                L := S (J).Last;
2203                Last := Last + 1;
2204                Table (Last) := (F, L);
2205
2206             else
2207                Offset := Reduced (S (J).First) - 1;
2208                Sorting.Sort (S (J).Last - S (J).First + 1);
2209
2210                F := S (J).First;
2211                L := F;
2212                for N in S (J).First .. S (J).Last loop
2213
2214                   --  For the last item, close the last subset
2215
2216                   if N = S (J).Last then
2217                      Last := Last + 1;
2218                      Table (Last) := (F, N);
2219
2220                   --  Two contiguous words are identical when they have the
2221                   --  same Cth character.
2222
2223                   elsif WT.Table (Reduced (N))(C) =
2224                         WT.Table (Reduced (N + 1))(C)
2225                   then
2226                      L := N + 1;
2227
2228                   --  Find a new subset of identical keys. Store the current
2229                   --  one and create a new subset.
2230
2231                   else
2232                      Last := Last + 1;
2233                      Table (Last) := (F, L);
2234                      F := N + 1;
2235                      L := F;
2236                   end if;
2237                end loop;
2238             end if;
2239          end loop;
2240       end Build_Identical_Keys_Sets;
2241
2242       --------------------------
2243       -- Count_Different_Keys --
2244       --------------------------
2245
2246       function Count_Different_Keys
2247         (Table : Vertex_Table_Type;
2248          Last  : Natural;
2249          Pos   : Natural) return Natural
2250       is
2251          N : array (Character) of Natural;
2252          C : Character;
2253          T : Natural := 0;
2254
2255       begin
2256          --  For each subset, count the number of words that are still
2257          --  different when we include Pos in the position selection. Only
2258          --  focus on this position as the other positions already produce
2259          --  identical keys.
2260
2261          for S in 1 .. Last loop
2262
2263             --  Count the occurrences of the different characters
2264
2265             N := (others => 0);
2266             for K in Table (S).First .. Table (S).Last loop
2267                C := WT.Table (Reduced (K))(Pos);
2268                N (C) := N (C) + 1;
2269             end loop;
2270
2271             --  Update the number of different keys. Each character used
2272             --  denotes a different key.
2273
2274             for J in N'Range loop
2275                if N (J) > 0 then
2276                   T := T + 1;
2277                end if;
2278             end loop;
2279          end loop;
2280
2281          return T;
2282       end Count_Different_Keys;
2283
2284    --  Start of processing for Select_Char_Position
2285
2286    begin
2287       --  Initialize the reduced words set
2288
2289       for K in 0 .. NK - 1 loop
2290          WT.Table (Reduced (K)) := New_Word (WT.Table (Initial (K)).all);
2291       end loop;
2292
2293       declare
2294          Differences          : Natural;
2295          Max_Differences      : Natural := 0;
2296          Old_Differences      : Natural;
2297          Max_Diff_Sel_Pos     : Natural := 0; -- init to kill warning
2298          Max_Diff_Sel_Pos_Idx : Natural := 0; -- init to kill warning
2299          Same_Keys_Sets_Table : Vertex_Table_Type (1 .. NK);
2300          Same_Keys_Sets_Last  : Natural := 1;
2301
2302       begin
2303          for C in Sel_Position'Range loop
2304             Sel_Position (C) := C;
2305          end loop;
2306
2307          Same_Keys_Sets_Table (1) := (0, NK - 1);
2308
2309          loop
2310             --  Preserve maximum number of different keys and check later on
2311             --  that this value is strictly incrementing. Otherwise, it means
2312             --  that two keys are strictly identical.
2313
2314             Old_Differences := Max_Differences;
2315
2316             --  The first position should not exceed the minimum key length.
2317             --  Otherwise, we may end up with an empty word once reduced.
2318
2319             Max_Sel_Pos :=
2320               (if Last_Sel_Pos = 0 then Min_Key_Len else Max_Key_Len);
2321
2322             --  Find which position increases more the number of differences
2323
2324             for J in Last_Sel_Pos + 1 .. Max_Sel_Pos loop
2325                Differences := Count_Different_Keys
2326                  (Same_Keys_Sets_Table,
2327                   Same_Keys_Sets_Last,
2328                   Sel_Position (J));
2329
2330                if Verbose then
2331                   Put (Output,
2332                        "Selecting position" & Sel_Position (J)'Img &
2333                          " results in" & Differences'Img &
2334                          " differences");
2335                   New_Line (Output);
2336                end if;
2337
2338                if Differences > Max_Differences then
2339                   Max_Differences      := Differences;
2340                   Max_Diff_Sel_Pos     := Sel_Position (J);
2341                   Max_Diff_Sel_Pos_Idx := J;
2342                end if;
2343             end loop;
2344
2345             if Old_Differences = Max_Differences then
2346                raise Program_Error with "some keys are identical";
2347             end if;
2348
2349             --  Insert selected position and sort Sel_Position table
2350
2351             Last_Sel_Pos := Last_Sel_Pos + 1;
2352             Sel_Position (Last_Sel_Pos + 1 .. Max_Diff_Sel_Pos_Idx) :=
2353               Sel_Position (Last_Sel_Pos .. Max_Diff_Sel_Pos_Idx - 1);
2354             Sel_Position (Last_Sel_Pos) := Max_Diff_Sel_Pos;
2355
2356             for P in 1 .. Last_Sel_Pos - 1 loop
2357                if Max_Diff_Sel_Pos < Sel_Position (P) then
2358                   Sel_Position (P + 1 .. Last_Sel_Pos) :=
2359                     Sel_Position (P .. Last_Sel_Pos - 1);
2360                   Sel_Position (P) := Max_Diff_Sel_Pos;
2361                   exit;
2362                end if;
2363             end loop;
2364
2365             exit when Max_Differences = NK;
2366
2367             Build_Identical_Keys_Sets
2368               (Same_Keys_Sets_Table,
2369                Same_Keys_Sets_Last,
2370                Max_Diff_Sel_Pos);
2371
2372             if Verbose then
2373                Put (Output,
2374                     "Selecting position" & Max_Diff_Sel_Pos'Img &
2375                       " results in" & Max_Differences'Img &
2376                       " differences");
2377                New_Line (Output);
2378                Put (Output, "--");
2379                New_Line (Output);
2380                for J in 1 .. Same_Keys_Sets_Last loop
2381                   for K in
2382                     Same_Keys_Sets_Table (J).First ..
2383                     Same_Keys_Sets_Table (J).Last
2384                   loop
2385                      Put (Output,
2386                           Trim_Trailing_Nuls (WT.Table (Reduced (K)).all));
2387                      New_Line (Output);
2388                   end loop;
2389                   Put (Output, "--");
2390                   New_Line (Output);
2391                end loop;
2392             end if;
2393          end loop;
2394       end;
2395
2396       Char_Pos_Set_Len := Last_Sel_Pos;
2397       Char_Pos_Set := Allocate (Char_Pos_Set_Len);
2398
2399       for C in 1 .. Last_Sel_Pos loop
2400          Set_Char_Pos (C - 1, Sel_Position (C));
2401       end loop;
2402    end Select_Char_Position;
2403
2404    --------------------------
2405    -- Select_Character_Set --
2406    --------------------------
2407
2408    procedure Select_Character_Set is
2409       Last : Natural := 0;
2410       Used : array (Character) of Boolean := (others => False);
2411       Char : Character;
2412
2413    begin
2414       for J in 0 .. NK - 1 loop
2415          for K in 0 .. Char_Pos_Set_Len - 1 loop
2416             Char := WT.Table (Initial (J))(Get_Char_Pos (K));
2417             exit when Char = ASCII.NUL;
2418             Used (Char) := True;
2419          end loop;
2420       end loop;
2421
2422       Used_Char_Set_Len := 256;
2423       Used_Char_Set := Allocate (Used_Char_Set_Len);
2424
2425       for J in Used'Range loop
2426          if Used (J) then
2427             Set_Used_Char (J, Last);
2428             Last := Last + 1;
2429          else
2430             Set_Used_Char (J, 0);
2431          end if;
2432       end loop;
2433    end Select_Character_Set;
2434
2435    ------------------
2436    -- Set_Char_Pos --
2437    ------------------
2438
2439    procedure Set_Char_Pos (P : Natural; Item : Natural) is
2440       N : constant Natural := Char_Pos_Set + P;
2441    begin
2442       IT.Table (N) := Item;
2443    end Set_Char_Pos;
2444
2445    ---------------
2446    -- Set_Edges --
2447    ---------------
2448
2449    procedure Set_Edges (F : Natural; Item : Edge_Type) is
2450       N : constant Natural := Edges + (F * Edge_Size);
2451    begin
2452       IT.Table (N)     := Item.X;
2453       IT.Table (N + 1) := Item.Y;
2454       IT.Table (N + 2) := Item.Key;
2455    end Set_Edges;
2456
2457    ---------------
2458    -- Set_Graph --
2459    ---------------
2460
2461    procedure Set_Graph (N : Natural; Item : Integer) is
2462    begin
2463       IT.Table (G + N) := Item;
2464    end Set_Graph;
2465
2466    -------------
2467    -- Set_Key --
2468    -------------
2469
2470    procedure Set_Key (N : Key_Id; Item : Key_Type) is
2471    begin
2472       IT.Table (Keys + N) := Item.Edge;
2473    end Set_Key;
2474
2475    ---------------
2476    -- Set_Table --
2477    ---------------
2478
2479    procedure Set_Table (T : Integer; X, Y : Natural; Item : Natural) is
2480       N : constant Natural := T + ((Y * T1_Len) + X);
2481    begin
2482       IT.Table (N) := Item;
2483    end Set_Table;
2484
2485    -------------------
2486    -- Set_Used_Char --
2487    -------------------
2488
2489    procedure Set_Used_Char (C : Character; Item : Natural) is
2490       N : constant Natural := Used_Char_Set + Character'Pos (C);
2491    begin
2492       IT.Table (N) := Item;
2493    end Set_Used_Char;
2494
2495    ------------------
2496    -- Set_Vertices --
2497    ------------------
2498
2499    procedure Set_Vertices (F : Natural; Item : Vertex_Type) is
2500       N : constant Natural := Vertices + (F * Vertex_Size);
2501    begin
2502       IT.Table (N)     := Item.First;
2503       IT.Table (N + 1) := Item.Last;
2504    end Set_Vertices;
2505
2506    ---------
2507    -- Sum --
2508    ---------
2509
2510    function Sum
2511      (Word  : Word_Type;
2512       Table : Table_Id;
2513       Opt   : Optimization) return Natural
2514    is
2515       S : Natural := 0;
2516       R : Natural;
2517
2518    begin
2519       case Opt is
2520          when CPU_Time =>
2521             for J in 0 .. T1_Len - 1 loop
2522                exit when Word (J + 1) = ASCII.NUL;
2523                R := Get_Table (Table, J, Get_Used_Char (Word (J + 1)));
2524                S := (S + R) mod NV;
2525             end loop;
2526
2527          when Memory_Space =>
2528             for J in 0 .. T1_Len - 1 loop
2529                exit when Word (J + 1) = ASCII.NUL;
2530                R := Get_Table (Table, J, 0);
2531                S := (S + R * Character'Pos (Word (J + 1))) mod NV;
2532             end loop;
2533       end case;
2534
2535       return S;
2536    end Sum;
2537
2538    ------------------------
2539    -- Trim_Trailing_Nuls --
2540    ------------------------
2541
2542    function Trim_Trailing_Nuls (Str : String) return String is
2543    begin
2544       for J in reverse Str'Range loop
2545          if Str (J) /= ASCII.NUL then
2546             return Str (Str'First .. J);
2547          end if;
2548       end loop;
2549
2550       return Str;
2551    end Trim_Trailing_Nuls;
2552
2553    ---------------
2554    -- Type_Size --
2555    ---------------
2556
2557    function Type_Size (L : Natural) return Natural is
2558    begin
2559       if L <= 2 ** 8 then
2560          return 8;
2561       elsif L <= 2 ** 16 then
2562          return 16;
2563       else
2564          return 32;
2565       end if;
2566    end Type_Size;
2567
2568    -----------
2569    -- Value --
2570    -----------
2571
2572    function Value
2573      (Name : Table_Name;
2574       J    : Natural;
2575       K    : Natural := 0) return Natural
2576    is
2577    begin
2578       case Name is
2579          when Character_Position =>
2580             return Get_Char_Pos (J);
2581
2582          when Used_Character_Set =>
2583             return Get_Used_Char (Character'Val (J));
2584
2585          when Function_Table_1 =>
2586             return Get_Table (T1, J, K);
2587
2588          when  Function_Table_2 =>
2589             return Get_Table (T2, J, K);
2590
2591          when Graph_Table =>
2592             return Get_Graph (J);
2593
2594       end case;
2595    end Value;
2596
2597 end GNAT.Perfect_Hash_Generators;