OSDN Git Service

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