home *** CD-ROM | disk | FTP | other *** search
/ Amiga ISO Collection / AmigaUtilCD2.iso / Programming / GCC / GERLIB_DEV08B.LHA / gerlib / libg++ / etc / trie-gen / compact.cc < prev    next >
Encoding:
C/C++ Source or Header  |  1993-12-12  |  12.3 KB  |  401 lines

  1. /* Compact a sparse 2-D matrix.  Uses the Tarjan and Yao algorithm
  2.    taken from the article ``Storing a Sparse Table'' in CACM, 1979.
  3.  
  4.    Copyright (C) 1989 Free Software Foundation, Inc.
  5.    written by Douglas C. Schmidt (schmidt@ics.uci.edu)
  6.    
  7.    This file is part of GNU TRIE-GEN.
  8.    
  9.    GNU TRIE-GEN is free software; you can redistribute it and/or modify
  10.    it under the terms of the GNU General Public License as published by
  11.    the Free Software Foundation; either version 1, or (at your option)
  12.    any later version.
  13.    
  14.    GNU TRIE-GEN is distributed in the hope that it will be useful,
  15.    but WITHOUT ANY WARRANTY; without even the implied warranty of
  16.    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  17.    GNU General Public License for more details.
  18.    
  19.    You should have received a copy of the GNU General Public License
  20.    along with GNU trie-gen; see the file COPYING.  If not, write to
  21.    the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
  22.  
  23. #include <stdio.h>
  24. #include <builtin.h>
  25. #include <limits.h>
  26. #include <new.h>
  27. #include <std.h>
  28. #include <assert.h>
  29. #include "compact.h"
  30.  
  31. /* Essentially provides the functionality of `calloc.' */
  32.  
  33. static inline void *
  34. operator new (size_t elem_size, int size)
  35. {
  36.   void *temp = new char[elem_size * size];
  37.   memset (temp, 0, elem_size * size);
  38.   return temp;
  39. }
  40.  
  41. /* Essentially combines the functionality of `realloc' and `calloc'. */
  42.  
  43. static inline void *
  44. operator new (size_t elem_size, void *old_ptr, int old_size, int new_size)
  45.   old_ptr = new (old_ptr, new_size * elem_size) char;
  46.   memset ((char*)old_ptr + old_size * elem_size, 0,
  47.       (new_size - old_size) * elem_size);
  48.   return old_ptr;
  49. }
  50.  
  51. /* Initializes the internal form in the case that the user passes
  52.    in a pointer to an already existing 2-dimensional matrix.  Note
  53.    that by declaring the matrix to be a 1-dimensional array we
  54.    can perform the col and row offset calculations ourselves and
  55.    handle matrices with fixed, but arbitrary-sized columns and rows. */
  56.  
  57. Compact_Matrix::Compact_Matrix (ITEM_TYPE *mat, int rows, int cols):
  58.      matrix (mat), max_rows (rows), total_cols (cols), current_rows (rows)
  59. {
  60.   init (rows);
  61.  
  62.   for (int i = 0; i < max_rows; i++)
  63.     for (int j = 0; j < total_cols; j++)
  64.       if (matrix[i * total_cols + j] != 0)
  65.         {
  66.           ITEM_TYPE value = matrix[i * total_cols + j];
  67.           total_entries++;
  68.           row_vec[i].count++;
  69.           row_vec[i].col_list = new Column_Node (row_vec[i].col_list, j, value);
  70.         }
  71. }
  72.  
  73. /* Initializer for the case where we don't have a previously created
  74.    matrix to play with.  DEFAULT_ROWS represents the best first
  75.    approximation as to the number of rows in the matrix.  However, this
  76.    buffer is resized as needed, so the algorithm isn't overly penalized
  77.    for a bad first guess. */
  78.  
  79. Compact_Matrix::Compact_Matrix (int default_rows): max_rows (default_rows)
  80. {
  81.   current_rows = 0;
  82.   total_cols = 0;
  83.   matrix = 0;
  84.  
  85.   init (default_rows);
  86. }
  87.  
  88. void Compact_Matrix::init(int rows)
  89. {
  90.   max_col_count = 0;
  91.   total_entries = 0;
  92.   compressed_len = -1;
  93.   row_offsets = 0;
  94.   checks = 0;
  95.   bucket_vec = 0;
  96.   values = 0;
  97.  
  98.   row_vec = rows ? new Row_Node[rows] : 0;
  99. }
  100.  
  101. /* Returns the `matrix[i][j]' item in the sparse 2-dimensional matrix.
  102.    Note that if the matrix is very sparse the number of items in each
  103.    COL_LIST will be very short, hence linear search is not too inefficent. */
  104.  
  105. ITEM_TYPE 
  106. Compact_Matrix::operator () (int i, int j)
  107. {
  108.   assert (i >= 0 && i < current_rows && j >= 0);
  109.   
  110.   for (Column_Node *col_list = row_vec[i].col_list; col_list; col_list = col_list->next)
  111.     if (col_list->index == j)
  112.       return col_list->value;
  113.  
  114.   return 0;
  115. }
  116.  
  117. /* Sets `matrix[i][j]' to VALUE.  ROW_VEC is dynamically resized, if
  118.    necessary.  At the moment the new entry is simply pushed onto the
  119.    linked list of COL_LIST entries.  If there aren't many elements then
  120.    this will not be too inefficient for later retrieval. */
  121.  
  122. void
  123. Compact_Matrix::operator () (int i, int j, ITEM_TYPE value)
  124. {
  125.   if (i >= max_rows)
  126.     resize (((max_rows * 2) > (i + 1)) ? (max_rows * 2) : (i + 1));
  127.   row_vec[i].col_list = new Column_Node (row_vec[i].col_list, j, value);
  128.   total_entries++;
  129.   row_vec[i].count++;
  130.   if (current_rows < (i + 1))
  131.     current_rows = i + 1;
  132. }
  133.  
  134. /* Enlarges the ROW_VEC from CURR_ROWS to NEW_SIZE. */
  135.  
  136. void 
  137. Compact_Matrix::resize (int new_size)
  138. {
  139.   Row_Node *temp = new Row_Node[max_rows = new_size];
  140.   
  141.   memcpy ((void *) temp, (void *) row_vec, current_rows * sizeof *row_vec);
  142.   delete row_vec;
  143.   row_vec = temp;
  144. }
  145.  
  146. /* Calls the functions that compact the table and generate the resulting
  147.    lookup scheme. */
  148.  
  149. void
  150. Compact_Matrix::output (void)
  151. {
  152.   bucket_sort ();
  153.   first_fit_decreasing ();
  154.   output_arrays ();
  155.   output_lookup ();
  156. }
  157.  
  158. /* Performs a bucket sort so that all rows with the same number of non-null
  159.    entries are treated as part of the same equivalence class.  This
  160.    operation is very fast! */
  161.  
  162. void 
  163. Compact_Matrix::bucket_sort (void)
  164. {
  165.   for (int i = 0; i < current_rows; i++)
  166.     {
  167.       if (max_col_count < row_vec[i].count)
  168.         max_col_count = row_vec[i].count;
  169.     }
  170.   
  171.   bucket_vec = new (max_col_count + 1) Column_Node *;
  172.  
  173.   for (i = 0; i < current_rows; i++)
  174.     bucket_vec[row_vec[i].count] = new Column_Node (bucket_vec[row_vec[i].count], i, 0);
  175. }
  176.  
  177. /* Useful macros to clarify subsequent code.  They should probably be made
  178.    into member functions... */
  179. #define STARTING_ROW_OFFSET(X) (row_offsets[(X)])
  180. #define LARGEST_COL_VALUE(X) (row_vec[(X)].col_list->index)
  181. #define COL_LIST(X) (row_vec[(X)].col_list)
  182. #define COL_COUNT(X) (row_vec[(X)].count)
  183. #define COL_INDEX(X) ((X)->index)
  184. #define ROW_INDEX(X) ((X)->index)
  185.  
  186. const int     MAX_ASCII_RANGE    = 128;
  187.  
  188. /* Performs sparse 2-dimensional array compaction suitable for use with the
  189.    `double-offset indexing' (used by Bison and FLEX to compact the size of
  190.    the sparse LR parsing tables and DFA's).  This function implements the
  191.    `first fit decreasing' heuristic described in Tarjan and Yao's paper 
  192.    ``Storing a Sparse Table'' in CACM, 1979. */
  193.  
  194. void 
  195. Compact_Matrix::first_fit_decreasing (void)
  196. {
  197.   /* Bit-vector and counter that records if a row/col location is already set. */
  198.   int    current_max = current_rows
  199.     + ((total_cols > MAX_ASCII_RANGE) ? total_cols : MAX_ASCII_RANGE);
  200.   char *already_assigned = (char *) malloc (current_max);
  201.   
  202.   memset (already_assigned, 0, current_max);
  203.   row_offsets = new (current_rows) int;
  204.   values      = new (current_max) ITEM_TYPE;
  205.   checks      = new (current_max) int;
  206.  
  207.   for (int row_index = max_col_count; row_index >= 0; row_index--)
  208.     if (bucket_vec[row_index])
  209.  
  210.       /* Process every row in the `equivalence class' of rows containing the
  211.          same number of non-null column entries. */
  212.  
  213.       for (Column_Node *rows_list = bucket_vec[row_index]; rows_list; rows_list = rows_list->next)
  214.         {
  215.           int row        = ROW_INDEX (rows_list);
  216.           int row_offset = STARTING_ROW_OFFSET (row);
  217.           
  218.           /* Process every column index in the current row. */
  219.  
  220.           for (Column_Node *col_list = COL_LIST (row); ;)
  221.             {
  222.               int col = COL_INDEX (col_list);
  223.  
  224.               /* If we exceed the boundaries it's time to resize various buffers. */
  225.               if (row_offset + col >= current_max)
  226.                 {
  227.                   int   new_size = ((current_max > (row_offset + col)) ? current_max : (row_offset + col)) * 2;
  228.                   already_assigned =
  229.               (char *) realloc (already_assigned, new_size);
  230.  
  231.           memset (already_assigned + current_max, 0,
  232.               new_size - current_max);
  233.                   values = new (values, current_max, new_size) int;
  234.                   checks = new (checks, current_max, new_size) int;
  235.                   current_max *= 2;
  236.                 }
  237.               if (already_assigned[row_offset + col])
  238.                 {
  239.                   /* Efficiency hack to skip over obvious collisions. */
  240.  
  241.                   while (++row_offset + col < current_max && already_assigned[row_offset + col]) 
  242.                     ;
  243.  
  244.                   /* Reset col_list and begin again (with new row offset). */
  245.                   col_list = COL_LIST (row);
  246.                 }
  247.               else if ((col_list = col_list->next) == 0)
  248.                 break;
  249.             }
  250.  
  251.           /* No more collisions exist.  Record the positions for the next round. */
  252.  
  253.           for (col_list = COL_LIST (row); col_list; col_list = col_list->next)
  254.             {
  255.               int offset = row_offset + COL_INDEX (col_list);
  256.  
  257.               already_assigned[offset] = 1;
  258.               if (compressed_len < offset)
  259.         compressed_len = offset;
  260.               values[offset] = col_list->value;
  261.               checks[offset] = row;
  262.             }
  263.  
  264.           /* Need to reset this once all is said and done! */
  265.           STARTING_ROW_OFFSET (row) = row_offset;
  266.         }
  267.   free(already_assigned);
  268. }
  269.  
  270. /* Generates the three arrays that comprise the `double-offset index'
  271.    scheme.  This is a bit messy, since I'm trying to neatly format
  272.    the generated tables and also determine the smallest (in bytes)
  273.    type declarations necessary to represent the elements of the tables. */
  274.  
  275. void 
  276. Compact_Matrix::output_arrays ()
  277. {
  278.   const int COL_WIDTH = 12;
  279.         int max_number = 0;
  280.         int count;
  281.       
  282.   printf ("#if !defined(__STDC__) && !defined(__cplusplus)\n");
  283.   printf ("#define const\n");
  284.   printf ("#define signed\n");
  285.   printf ("#endif\n\n");
  286.   printf ("#define YY_LAST %d\n", compressed_len);
  287.       
  288.   for (int i = 0; i < current_rows; i++)
  289.     {
  290.       if (max_number < row_offsets[i])
  291.         max_number = row_offsets[i];
  292.     }
  293.  
  294.   count = max_number;
  295.   for (int field_width = 1; (count /= 10) > 0; field_width++)
  296.     ;
  297.  
  298.   printf ("\nstatic const unsigned %s yy_rows[%d] = \n{\n  ",
  299.           max_number < UCHAR_MAX ? "char" : max_number < USHRT_MAX ? "short" : "int",
  300.           current_rows);
  301.       
  302.   for (i = 0; i < current_rows; i++)
  303.     printf ("%*d,%s", field_width, row_offsets[i], (i + 1) % COL_WIDTH ? " " : "\n  ");
  304.       
  305.   max_number = 0;
  306.  
  307.   for (i = 0; i < compressed_len + 1; i++)
  308.     {
  309.       if (max_number < checks[i])
  310.         max_number = checks[i];
  311.     }
  312.  
  313.   count = max_number;
  314.   for (field_width = 1; (count /= 10) > 0; field_width++)
  315.     ;
  316.  
  317.   printf ("\n};\n\nstatic const unsigned %s yy_check[%d] = \n{\n  ",
  318.           max_number < UCHAR_MAX ? "char" : max_number < USHRT_MAX ? "short" : "int",
  319.           compressed_len + 1);
  320.       
  321.   for (i = 0; i < compressed_len + 1; i++)
  322.     printf ("%*d,%s", field_width, checks[i], (i + 1) % COL_WIDTH ? " " : "\n  ");
  323.       
  324.   max_number = 0;
  325.  
  326.   for (i = 0; i < compressed_len + 1; i++)
  327. #ifdef __GNUC__
  328.     max_number >?= abs (values[i]);
  329. #else
  330.     {
  331.       int tmp = abs (values[i]);
  332.       if (max_number < tmp)
  333.         max_number = tmp;
  334.     }
  335. #endif
  336.  
  337.   count = max_number;
  338.   for (field_width = 2; (count /= 10) > 0; field_width++)
  339.     ;
  340.  
  341.   printf ("\n};\n\nstatic const %s yy_next[%d] = \n{\n  ",
  342.           max_number < SCHAR_MAX ? (CHAR_MIN == 0 ? "signed char" : "char")
  343.       : max_number < SHRT_MAX ? "short" : "int",
  344.           compressed_len + 1);
  345.       
  346.   for (i = 0; i <= compressed_len; i++)
  347.     printf ("%*d,%s", field_width, values[i], (i + 1) % COL_WIDTH ? " " : "\n  ");
  348.       
  349.   printf ("\n};\n\n");
  350. }
  351.  
  352. /* Generates the `double-offset index' function, that provides the
  353.    value stored in the location referenced by parameters ROW and COL. */
  354.  
  355. void 
  356. Compact_Matrix::output_lookup (void)
  357. {
  358.   printf ("static inline int\nnext_state "
  359.           "(int row, int col)\n{\n  int state_index = yy_rows[row] + col;\n\n"
  360.           "  if (state_index > YY_LAST || yy_check[state_index] != row)\n    "
  361.           "return 0;\n  else\n    return yy_next[state_index];\n}\n");
  362. }
  363.  
  364. /* Useful debugging routine. */
  365.  
  366. void
  367. Compact_Matrix::dump_entries (void)
  368. {
  369.   for (int i = 0; i < current_rows; i++)
  370.     if (row_vec[i].col_list)
  371.       {
  372.         Column_Node *temp = row_vec[i].col_list;
  373.       
  374.         fprintf (stderr, "row %d's count = %d, cols = ", i, row_vec[i].count);
  375.  
  376.         do { fprintf (stderr, "%d ", temp->index); } while (temp = temp->next);
  377.  
  378.         putc ('\n', stderr);
  379.       }
  380. }
  381.  
  382. /* Useful debugging routine. */
  383.  
  384. void 
  385. Compact_Matrix::dump_bucket (void)
  386. {
  387.   for (int i = 0; i < total_entries; i++)
  388.     if (bucket_vec[i])
  389.       {
  390.         Column_Node *temp = bucket_vec[i];
  391.         
  392.         fprintf (stderr, "bucket %d = ", i);
  393.  
  394.         do { fprintf (stderr, "%d ", temp->index); } while (temp = temp->next);
  395.  
  396.         putc ('\n', stderr);
  397.       }
  398. }
  399.  
  400.