packages feed

language-Modula2-0.1: examples/Modula-2_Libraries/andrea-m2/lib/generic/packarray.mod

IMPLEMENTATION MODULE PackArray;

(* Array of small cardinal values packed into bit items,
   see the corresponding definition *)
(* V1.1, J. Andrea, Jun.22/93 -add Duplicate *)
(* V1.0, J. Andrea, Jun.15/93 *)
(* This code may be freely used and distributed, it may not be sold. *)

FROM SYSTEM  IMPORT ADDRESS, TSIZE;
FROM Storage IMPORT ALLOCATE, DEALLOCATE;

TYPE
  ImplementationType = BITSET;
  DataType           = CARDINAL;

  Array = POINTER TO RECORD
             length   :CARDINAL;   (* dimensions of the array *)
             start    :ADDRESS;    (* memory address of the first item *)
             min, max :DataType;   (* range of data items *)
             bytes    :CARDINAL;   (* number of bytes in whole array *)
             bits     :CARDINAL;   (* how many bits used to implement values *)
             aligned  :BOOLEAN;    (* bits fit exactly in a word *)
           END;

VAR
  bytes_per_word :CARDINAL;               (* #bytes in a single item *)
  bits_per_word  :CARDINAL;               (* # bits *)
  adra, adrb     :POINTER TO ImplementationType;  (* generic pointers *)

(* -------------------------------------------------------- *)
PROCEDURE InRange( a :Array; element :CARDINAL ) :BOOLEAN;
(* is the specified row/col item in this array ? *)
BEGIN
  RETURN ( element >= 1 ) & ( element <= a^.length );
END InRange;

(* -------------------------------------------------------- *)
PROCEDURE Offsets( a :Array; element :CARDINAL;
                   VAR word, word_bit,
                       bits_in_first, bits_in_second :CARDINAL );
(* The element should be known to be valid at this point. *)
(* Calculate the memory offset to the element item from the first item *)

VAR
  element_first, element_last, word_first, word_last :CARDINAL;
BEGIN

       (* In absolute counts, find the end points of this element *)
       (* These are not zero based *)

  element_last  := a^.bits * element;
  element_first := element_last - a^.bits + 1;

       (* In which word does the current element begin *)
       (* This one is zero based, so the "-1" is needed *)

  word := ( element_first - 1 ) DIV bits_per_word;

       (* In absolute counts, where does the current word begin *)
       (* This is not zero based *)

  word_first := 1 + word * bits_per_word;

       (* The current element's first bit begins somewhere in the word *)
       (* This is also zero based, so no +1 *)

  word_bit := element_first - word_first;

  IF a^.aligned THEN
    bits_in_first  := a^.bits;
    bits_in_second := 0;
  ELSE

    (* Otherwise figure out how much of this element is spread across the
       two contiguous words, if at all *)

       (* In absolute counts, the last bit in the word is *)

    word_last := word_first + bits_per_word - 1;

    IF element_last > word_last THEN

      (* Find the distance from the beginning of the element to the end
         of the word *)

      bits_in_first  := word_last - element_first + 1;
      bits_in_second := a^.bits - bits_in_first;

    ELSE
      bits_in_first  := a^.bits;
      bits_in_second := 0;
    END;
  END;
END Offsets;

(* -------------------------------------------------------- *)
PROCEDURE Build( VAR a :Array; minimum, maximum, n_elements :CARDINAL );
VAR
  total_bits, total_words :CARDINAL;
  i, k, n :CARDINAL;
  temp    :DataType;
BEGIN
  NEW( a );

  IF n_elements = 0 THEN n_elements := 1 END;

  IF minimum > maximum THEN
    temp    := maximum;
    maximum := minimum;
    minimum := temp;
  END;

  a^.min := minimum;
  a^.max := maximum;

  a^.length := n_elements;

  (* here is the range *)
  n := maximum - minimum + 1;

  (* compute the number of bits needed to implement a number of that size *)
  i := 0;
  k := 1;
  WHILE k < n DO
    i := i + 1;
    k := k * 2;
  END;

  a^.bits := i;
  
  a^.aligned := bits_per_word MOD a^.bits = 0;
  
  (* so the total number of bits for the whole array *)
  total_bits := a^.bits * n_elements;

  (* round total number of bits up to fill a whole word *)
  k := total_bits MOD bits_per_word;
  IF k # 0 THEN
    total_bits := total_bits + bits_per_word - k;
  END;

  (* number of words to use *)
  total_words := total_bits DIV bits_per_word;

  (* and how many bytes in total *)
  a^.bytes := total_words * bytes_per_word;

  ALLOCATE( a^.start, a^.bytes );

  (* zero out that piece of memory *)
  k := 0;
  FOR i := 1 TO total_words DO
     adra  := a^.start + k;
     adra^ := {};
     k     := k + bytes_per_word;
  END;

END Build;

(* -------------------------------------------------------- *)
PROCEDURE Destroy( VAR a :Array );
BEGIN
  DEALLOCATE( a^.start, a^.bytes );
  DISPOSE( a );
END Destroy;

(* -------------------------------------------------------- *)
PROCEDURE Put( a :Array; element :CARDINAL; x :DataType );
VAR
  result                :ImplementationType;
  which_word            :CARDINAL;
  element_bit, word_bit :CARDINAL;
  first_set, second_set :CARDINAL;
  location              :CARDINAL;
    
  (* ------------------------------ *)
  PROCEDURE Bits( n :CARDINAL );
  VAR
    i :CARDINAL;
  BEGIN
    FOR i := 1 TO n DO
       IF element_bit IN result THEN
         INCL( adra^, word_bit );
       ELSE
         EXCL( adra^, word_bit );
       END;
       INC( word_bit );
       INC( element_bit );
    END;
   END Bits;
   
BEGIN
  IF InRange( a, element ) THEN

    IF x < a^.min THEN
      x := a^.min;
    ELSIF x > a^.max THEN
      x := a^.max;
    END;
    result := BITSET( x - a^.min );

    Offsets( a, element, which_word, word_bit, first_set, second_set );
    location := which_word * bytes_per_word;
    adra := a^.start + location;

    element_bit := 0;
    Bits( first_set );

    IF second_set # 0 THEN
      (* move to the second word and get the rest of the bits from it *)
      adra     := a^.start + ( location + bytes_per_word );
      word_bit := 0;
      Bits( second_set );
   END;

  END;
END Put;

(* -------------------------------------------------------- *)
PROCEDURE Get( a :Array; element :CARDINAL ) :DataType;
VAR
  result                :ImplementationType;
  which_word            :CARDINAL;
  element_bit, word_bit :CARDINAL;
  first_set, second_set :CARDINAL;
  location              :CARDINAL;
  
  (* ----------------------- *)
  PROCEDURE Bits( n :CARDINAL );
  VAR
    i :CARDINAL;
  BEGIN
    FOR i := 1 TO n DO
      IF word_bit IN adra^ THEN
        INCL( result, element_bit );
      END;
      INC( element_bit );
      INC( word_bit );
    END; 
  END Bits;
  
BEGIN

  IF NOT InRange( a, element ) THEN
    RETURN 0;
  ELSE

    Offsets( a, element, which_word, word_bit, first_set, second_set );
    location := which_word * bytes_per_word;
    adra     := a^.start + location;

    result := {};

    element_bit := 0;
    Bits( first_set );

    IF second_set # 0 THEN
      (* move to the second word and get the rest of the bits from it *)
      adra := a^.start + ( location + bytes_per_word );
      word_bit := 0;
      Bits( second_set );
    END;

    RETURN CARDINAL( result ) + a^.min;
  END;

END Get;

(* -------------------------------------------------------- *)
PROCEDURE Size( a :Array; VAR bits, bytes, elements :CARDINAL );
BEGIN
  bytes    := a^.bytes;
  bits     := a^.bits;
  elements := a^.length;
END Size;

(* -------------------------------------------------------- *)
PROCEDURE Min( a :Array ) :DataType;
VAR
  i         :CARDINAL;
  x, result :DataType;
BEGIN

  result := Get( a, 1 );

  (* Since the absolute minimum is predefined then might as well stop
     if that minimum is reached *)

  i := 2;
  WHILE ( i <= a^.length ) & ( result > a^.min ) DO
    x := Get( a, i );
    IF x < result THEN result := x END;
    i := i + 1;
  END;

  RETURN result;
END Min;

(* -------------------------------------------------------- *)
PROCEDURE Max( a :Array ) :DataType;
VAR
  i         :CARDINAL;
  x, result :DataType;
BEGIN

  result := Get( a, 1 );

  (* Since the absolute maximum is predefined then might as well stop
     if that minimum is reached *)

  i := 2;
  WHILE ( i <= a^.length ) & ( result < a^.min ) DO
    x := Get( a, i );
    IF x > result THEN result := x END;
    i := i + 1;
  END;

  RETURN result;
END Max;

(* -------------------------------------------------------- *)
PROCEDURE Compare( a, b :Array ) :BOOLEAN;
VAR
  same :BOOLEAN;
  i, k :CARDINAL;
  total_words :CARDINAL;
BEGIN

  IF a^.length # b^.length THEN
    same := FALSE;
  ELSE

    same := TRUE;

    total_words := a^.bytes DIV bytes_per_word;
    i := 1;
    k := 0;
    WHILE same & ( i <= total_words ) DO
      adra := a^.start + k;
      adrb := b^.start + k;
      same := adra^ = adrb^;
      i    := i + 1;
      k    := k + bytes_per_word;
    END;

  END;

  RETURN same;

END Compare;

(* -------------------------------------------------------- *)
PROCEDURE Assign( a :Array; x :DataType );
VAR
  i :CARDINAL;
BEGIN
  FOR i := 1 TO a^.length DO
     Put( a, i, x );
  END;
END Assign;

(* -------------------------------------------------------- *)
PROCEDURE Copy( a, b :Array );
VAR
  i, k, total_words :CARDINAL;
BEGIN
  IF ( a^.min = b^.min ) & ( a^.max = b^.max ) & ( a^.length = b^.length ) THEN

    total_words := a^.bytes DIV bytes_per_word;

    k := 0;
    FOR i := 1 TO total_words DO
       adra  := a^.start + k;
       adrb  := b^.start + k;
       adrb^ := adra^;
       k     := k + bytes_per_word;
    END;

  END;
END Copy;

(* -------------------------------------------------------- *)
PROCEDURE Duplicate( a :Array; VAR b :Array );
BEGIN
  Build( b, a^.min, a^.max, a^.length );
  Copy( a, b );
END Duplicate;


BEGIN

  bytes_per_word := TSIZE( ImplementationType );
  bits_per_word  := bytes_per_word * 8;

END PackArray.