comp.lang.ada
 help / color / mirror / Atom feed
From: mfb@mbunix.mitre.org (Michael F Brenner)
Subject: Re: Anyone could give a complete and yet small program on the use for the generic
Date: 1997/01/03
Date: 1997-01-03T00:00:00+00:00	[thread overview]
Message-ID: <5aitud$hjr@top.mitre.org> (raw)
In-Reply-To: dewar.852260373@merv


Dear Hung-Hsien Chang,

Here is an example of a generic package that passes types, objects,
functions, and procedures. It works in both Ada-83 and Ada-95 unchanged.
However, in Ada-95, an additional feature was added called an abstract
generic. Since I am a beginner with abstract packages, someone else
should post an example of a working abstract package, so I can see
one that works myself.

You are right. Books are not the best way to learn how to program
something, even in Ada. The easiest way to learn is to look at 
working programs, modify them, and build on them. So asking for
an example of a working program is not only valid educationally, 
but IMO it is the only valid way to learn Ada. 

Below the generic specification and implementation I have placed a test
program that demonstrates that it works on a large variety of 
instantiations, including exact numbers, approximate numbers, 
fixed length strings, variable length strings, record types, etc.

I programmed this generic package and all of its instantiations myself,
and it has no bugs. <mikeb@mitre.org>

-- The package balanced_essence is subject to Ada modification to the
-- Free Software Foundations General Public LIBRARY License which 
-- provides that the code and its modifications must always remain
-- free, but anybody, even commercial firms may use it in their
-- products without those products becoming free. It is part of
-- the free Eyemusic Command and Control System.

-- This package provides skip lists which are the equivalent to 
-- balanced trees, only a little slower, and a lot easier to debug.

generic
  type keys   is private;  -- Entered in list in ascending order according to <
  first_key: keys;         -- Lowest key according to <.
  last_key:  keys;         -- Highest key according to <.
  with function  "<"         (left, right: keys) return boolean;
  with function  same        (left, right: keys) return boolean;
  with function  key_image   (key:   keys)       return string;
  with procedure assign_key  (left: in out keys; right: keys);

  type values is private;  -- A value is associated with each key.
  null_value: values;
  with function  value_image (value: values)     return string;
  with procedure assign_value (left: in out values; right: values);

  type nodes  is range <>; -- Each node has a key and a value.

  type levels is range <>; -- Levels'last should be log base 2 of nodes'last.

package balanced_essence is
  pragma elaborate_body (balanced_essence);

  -- Balanced Trees (Used for Fast Lookups)
  --
  -- This package uses a technique similar to that described in
  -- CACM, June 1990 in an article by William Pugh titled:
  -- "Skip Lists: A Probabilistic Alternative to Balanced Trees".
  -- Pugh gives program design language
  -- and an explanation of why skip lists are almost
  -- as fast to look up and faster to insert than fully balanced trees,
  -- including AVL trees and self-adjusting trees. Thus, a skip list
  -- can be considered as an almost balanced tree; the part of it that
  -- is not balanced is determined by a random number generator, and
  -- averages more than 75% balanced.
  --
  -- The faster inserts do not make up for the partial lack of balance, if
  -- objects are going to be looked up many times.
  -- The real reason you would use a skip list is that
  -- balanced trees never seem to get past their software development bugs,
  -- and therefore never realize their (theoretical) superior performance.
  -- A partially balanced tree (skip list) can be implemented quickly with
  -- the bugs eliminated during a little unit testing.
  --
  -- When symbol tables and similar associative arrays are implemented
  -- using a skip list, they run in LOG_N time for each lookup, as
  -- opposed to a linear lookup that runs in time N.
  --
  -- 1990-07-28 MB  Created
  -- 1990-08-13 SMA Added delete (node) instead of just delete (key)
  -- 1990-09-27 MB  Removed delete (node): you have to search for the key
  --                in order to build up the list of nodes that point to
  --                the key on each strand, otherwise you only delete it
  --                on the one strand, which leaves the tree corrupted.

  type behaviors_for_duplicate_keys is
    (fail,
     overwrite_values,
     raise_duplicate_exception);

  type behaviors_when_not_found is
    (raise_not_found_exception,
     insert_nothing_and_return_next_lower_node, -- for NODE return only
     insert_nothing_and_return_null_value,
     insert_null_value_and_return_it);

  duplicate: exception;
  not_found: exception;
  failure:      constant nodes := nodes'first;
  trailer_node: constant nodes := nodes'first;
  header_node:  constant nodes := nodes'succ (trailer_node);

  -- The TRAILER_NODE is the last node in the list. It is stored at
  -- location -1, just before the HEADER_NODE.

  type skip_lists is limited private;

  procedure initialize (L: in out skip_lists);
  procedure finalize   (L: in out skip_lists);
  procedure re_initialize (L: skip_lists);

  procedure delete     (L: skip_lists; k: keys);
  function  insert     (L: skip_lists; k: keys;  v: values) return nodes;
  function  search     (L: skip_lists; k: keys)  return nodes;
  function  search     (L: skip_lists; k: keys)  return values;
  function  key        (L: skip_lists; n: nodes) return keys;
  function  value      (L: skip_lists; n: nodes) return values;
  procedure tell       (L: skip_lists);

  function  next_node  (L: skip_lists; n: nodes) return nodes;

    -- To use NEXT_NODE, use a loop like the following:
    --
    --       n: nodes := header_node;
    --                                        -- HEADER_NODE defined above.
    --       loop
    --         exit when n := trailer_node;
    --                                        -- TRAILER_NODE defined above.
    --         process (n);
    --                                        -- Caller must provide PROCESS.
    --         n := next_node (n);
    --                                        -- NEXT_NODE defined above.
    --       end loop;

  function  linear_search          (L: skip_lists; k: keys) return nodes;

  procedure set_insertion_behavior (L: skip_lists;
                                    insertion_behavior:
                                    behaviors_for_duplicate_keys);
  procedure set_search_behavior    (L: skip_lists;
                                    search_behavior:
                                    behaviors_when_not_found);
  procedure set_tracing            (on: boolean);
private
  type pointers is array (levels) of nodes;

  type node_descriptors is record
    key:     keys;
    value:   values;
    pointer: pointers;
  end record;

  -- The definition of type NODE_DESCRIPTORS shows that each of the nodes
  -- contains all of the levels of pointers. Thus, it is important to
  -- decide on the number of levels of pointers before instantiating this
  -- object. A good value for the number of nodes is probably a tiny bit
  -- more than log base 2 of the number of nodes. For applications where
  -- there is a human waiting for the lookup, measure and optimize this!
  -- Or else use a hash code table or finite state auomata instead, so the
  -- human does not wait more than 0.25 seconds after pressing the key.

  type node_lists is array (nodes) of node_descriptors;

  -- The linked list implementing the balanced tree is actually a fixed array.

  type pab_skip_lists is record
    node_list:          node_lists;
    high_level:         levels;
    last_node_used:     nodes;
    update:             pointers;
    insertion_behavior: behaviors_for_duplicate_keys := fail;
    search_behavior:    behaviors_when_not_found := raise_not_found_exception;
  end record;

  type skip_lists is access pab_skip_lists;
end balanced_essence;

--
-- The package status provides status.tell and status.telln which are
-- like text_io.put and text_io.put_line with the additional features
-- that they are efficient, small, and do output simultaneously to
-- the screen and to an error collection file called status.tmp
--
-- The package rand_int provides random numbers and can be replaced
-- with any other random number generating package. It is used on
-- only one line in balanced_essence, to provide an exact (integer)
-- number in the range 0..big.   

with status;
with rand_int;
package body balanced_essence is

  tracing_bal: boolean := False;

  procedure assert (condition: boolean; message: string) is
    -- It is essential for efficiency that this be a
    -- local procedure, and that it be compiled in-line.
    fatal_balanced_tree: exception;
  begin
    if not condition then
      status.telln(message);
      raise fatal_balanced_tree;    
    end if;
  end assert;
  pragma inline (assert);

  function ">" (left, right: keys) return boolean is
  begin
    return not same (left, right) and then not (left<right);
  end ">";

  procedure check_range (key: keys) is
  begin
    assert (key>first_key and key<last_key, "key:bad");
  end check_range;

  procedure set_p (L: skip_lists; n: nodes; i: levels; p: nodes) is
  begin
    L.node_list(n).pointer(i) := p;
  end set_p;

  procedure re_initialize (L: skip_lists) is
  begin
    L.last_node_used := header_node;

      -- LAST_USED_NODE is set to the HEADER_NODE,
      -- indicating an empty list.

    for i in levels loop
      set_p (L, header_node,  i, trailer_node);
      set_p (L, trailer_node, i, trailer_node);
    end loop;

      -- All pointers in the header node and the
      -- trailer node point at the trailer node.

    assign_key   (L.node_list(trailer_node).key,   last_key);
    assign_value (L.node_list(trailer_node).value, null_value);

      -- Dummy keys are inserted in the TRAILER_NODE that
      -- are larger than any other possible key.

    assign_key   (L.node_list(header_node).key,    first_key);
    assign_value (L.node_list(header_node).value,  null_value);

      -- Dummy keys are also inserted in the HEADER_NODE
      -- that are smaller than any other possible key.
      -- The necessity for this is not commonly known. Beware.
      -- We learned about this through hard experience.

    L.high_level     := 1;

      -- The HIGH_LEVEL is set to 1, so that a search of the list will
      -- immediately come up empty (since the HEADER_NODE points to the
      -- TRAILER_NODE which contains the highest possible key).

    L.insertion_behavior := raise_duplicate_exception;

      -- The default insertion behavior is that
      -- inserting a duplicate record is fatal.

    L.search_behavior    := raise_not_found_exception;

      -- The default search behavior is that
      -- not finding a key is fatal.

  end re_initialize;

  procedure initialize (L: in out skip_lists) is
  begin
    L := new pab_skip_lists;

      -- If you got to here with a SIGSEGV, there was
      -- enough virtual storage to allocate the tree.

    re_initialize (L);

      -- The tree is initialized.

  end initialize;

  procedure finalize (L: in out skip_lists) is
  begin
    re_initialize (L);
    L.insertion_behavior := fail;

      -- Note that we do NOT free the nodes of L,
      -- which were NOT dynamically allocated (at
      -- least not by us), and we do NOT free the tree,
      -- which WAS dynamically allocated by us.
      -- By simply reinitializing, we can reuse the tree.

  end finalize;

  procedure tell (L: skip_lists) is
                                    -- The TELL method traces a balanced tree.
    pointers:      string (1..64);
    pointers_upto: natural := 0;
  begin
    status.tell ("hi-level and node");
    status.tell (levels'image(L.high_level));
    status.telln(nodes'image(L.last_node_used));

    for node in nodes range -1..L.last_node_used loop
      pointers_upto := 0;
      for i in pointers'range loop
        pointers (i) := ' ';
      end loop;
      for level in levels loop
        declare
          this_pointer: constant nodes  := L.node_list(node).pointer(level);
          message:      constant string := nodes'image(this_pointer);
        begin
          exit when pointers_upto + message'length > pointers'length;
                                        -- The message will fit in the buffer.
          pointers (pointers_upto+1..pointers_upto+message'length) := message;
          pointers_upto := pointers_upto + message'length;
        end;
      end loop;
      status.tell (key_image  (L.node_list(node).key));
      status.tell (nodes'image(node));
      status.tell (" V ");
      status.tell (value_image(L.node_list(node).value));
      status.tell (" P");
      status.telln(pointers   (1..pointers_upto));
    end loop;
  end tell;

-- Object Access Functions

  function key (L: skip_lists; n: nodes) return keys is
  begin
    return L.node_list(n).key;
  end key;

  function p (L: skip_lists; n: nodes; i: levels) return nodes is
  begin
    return L.node_list(n).pointer(i);
  end p;

  procedure is_okay (L: skip_lists) is
  begin
    if L=null or else L.insertion_behavior = fail then
      raise not_found;
      -- It could have raised a unique exCeption, but
      -- you exPect using an uninitialized tree to do an
      -- unexpected thing, so this one throws NOT_FOUND.
    end if;
  end is_okay;

  procedure update_search (L:           skip_lists;
                           k:           keys;
                           before_node: out nodes;
                           after_node:  out nodes) is

    -- This is the kernel of the INSERT, DELETE, and SEARCH.

    x: nodes := header_node;
    y: nodes;
  begin
    if tracing_bal then
      status.tell ("usearch [");
      status.tell (key_image(k));
      status.tell ("] ");
      check_range (k);
    end if;

    for i in reverse levels'first..L.high_level loop -- for each level

      -- Within each level, find the first key at least as big as K.
      loop
        y := p(L,x,i);
        exit when not (key(L,y)<k);
        x := y;
      end loop;

      L.update(i) := x; -- Update the pointers for insert and delete,
                        -- not used by the visible SEARCH.
    end loop;

    -- The before_node points at the node, you see,
    -- that points at where the key would be,
    -- if the key were in the tree.

    before_node := x;

    if tracing_bal then
      status.tell ("before node=");
      status.tell (nodes'image(x));
    end if;

    after_node := p (L, x, 1);

    -- If the key is in the tree,
    -- X is now pointing at it.
    -- Otherwise, X is now just AFTER
    -- where the key would  be.

  end update_search;

  procedure delete (L: skip_lists; k: keys) is
    pre_x: nodes; -- node that points to x
    x:     nodes;
  begin
    is_okay (L);
    check_range (k);
    update_search (L, k, pre_x, x);

    if same (key (L, x), k) then
      assert (P(L,L.update(1),1)=x, "del:P(L,L.u(1),1)/=x");
      assert (same (key(L,x), k),   "del:key(L,x) /= k");

      for i in levels'first .. L.high_level loop
        exit when P (L, L.update(i), i) /= x;
        set_p       (L, L.update(i), i, P(L,x,i));
      end loop;

      -- free_node (x);

                     -- Instead of freeing it, we shall waste it.
                     -- We did not, after all, allocate it, and
                     -- it is probably not even a pointer type.
                     -- These lists are temporary; we insert and search.
                     -- The entire list is deallocated simultaneously,
                     -- when you leave the address space of the list.
                     -- The programmer expects that deleting is rare.
                     -- If deleting is not rare, then redo this design,
                     -- by using a heap instead of an array of nodes.

      loop
        exit when L.high_level = 1;
        exit when P(L, header_node, L.high_level) = trailer_node;
        L.high_level := L.high_level - 1;
      end loop;
    end if;
  end delete;

  function random_level return levels is
    level:    levels   := 1;
    big:      constant := 8192;
    fraction: constant := big / 2;
    use rand_int;
  begin
    loop
        -- Spin the "dice" until the sum of the probabilities exceed 0.5.
      exit when rand_int.random_modulus mod big >= fraction;
      level := level+1;
      exit when level = levels'last;
    end loop;
    return level;
  end random_level;

  function insert (L: skip_lists; k: keys; v: values) return nodes is
    new_level: levels;
    pre_x:     nodes; -- node that points to x
    x:         nodes;
    node:      nodes;
  begin
    is_okay (L);

    if tracing_bal then
      status.tell ("INS key=");
      status.tell (key_image(k));
      status.tell (" Value=[");
      status.telln (value_image(v));
      check_range (k);
    end if;

    update_search (L, k, pre_x, x);

    if not same (key (L, x), k) then
      new_level := random_level;
      if new_level > L.high_level then
        for i in L.high_level + 1 .. new_level loop
          L.update(i) := header_node;
        end loop;
        L.high_level := new_level;
      end if;
      assert (L.last_node_used < nodes'last, "ins:oflo");
      L.last_node_used := L.last_node_used + 1;

      -- The new node is "allocated".

      x                := L.last_node_used;

      for i in levels'first .. new_level loop
        node := P(L, L.update(i), i);
        set_p (L, x,           i, node);
        set_p (L, L.update(i), i, x);
      end loop;

      assign_key (L.node_list(x).key, K);
    elsif L.insertion_behavior = raise_duplicate_exception then
      raise duplicate;
    else
      null;
    end if;
    assign_value (L.node_list(x).value, v); -- In either case, set the value.
    return x;
  end insert;

  function next_node (L: skip_lists; n: nodes) return nodes is
  begin

    -- This permits iteration through the balanced tree, starting
    -- at any value of N (particularly starting at the HEADER_NODE),
    -- and proceeeding until all nodes are visited, when the return
    -- value will be TRAILER_NODE.

    is_okay (L);
    return p (L,n,levels'first);
  end next_node;

  function linear_search (L: skip_lists; k: keys) return nodes is
    node:                 nodes := HEADER_NODE;
    number_of_searches:   nodes := 0;
    end_of_list: constant nodes := L.last_node_used + 1;
  begin
    is_okay (L);

    loop
      exit when node = TRAILER_Node;
      exit when number_of_searches > end_of_list;
      exit when same (key (L, node), k);
      number_of_searches := number_of_searches + 1;
      node := next_node (L, node);
    end loop;

    assert (number_of_searches <= end_of_list, "lin:oflo");
    return node;
  end linear_search;

  function search (L: skip_lists; k: keys) return nodes is
    pre_x:    nodes;
    x:        nodes;
    y:        nodes;
  begin
    update_search (L, k, pre_x, x);
    if same (key (L, x), k) then
      y:=x;
    else
      case L.search_behavior is
        when insert_nothing_and_return_next_lower_node => y:=PRE_X;
        when insert_nothing_and_return_null_value => y:=failure;
        when insert_null_value_and_return_it      => y:=insert(L,k,null_value);
        when raise_not_found_exception            => raise not_found;
      end case;
    end if;

    return y;
  end search;

  function search (L: skip_lists; k: keys) return values is
    pre_x:    nodes;
    x:        nodes;
    y:        nodes;
    v:        values;
  begin
    update_search (L, k, pre_x, x);
    if same (key (L, x), k) then
      v:=value(L,x);
    else
      case L.search_behavior is
        when insert_nothing_and_return_next_lower_node => raise not_found;
        when insert_nothing_and_return_null_value => v:=null_value;
        when insert_null_value_and_return_it      => y:=insert(L,k,null_value);
                                                     v:=null_value;
        when raise_not_found_exception            => raise not_found;
      end case;
    end if;
    return v;
  end search;

  procedure set_insertion_behavior (L: skip_lists;
                                    insertion_behavior:
                                    behaviors_for_duplicate_keys) is
  begin
    L.insertion_behavior := insertion_behavior;
  end set_insertion_behavior;

  procedure set_search_behavior (L: skip_lists;
                                 search_behavior:
                                 behaviors_when_not_found) is
  begin
    L.search_behavior := search_behavior;
  end set_search_behavior;

  procedure set_tracing (on: boolean) is
  begin
    tracing_bal := on;
  end set_tracing;

  function value (L: skip_lists; n: nodes) return values is
  begin
    assert (n/=header_node, "val:n=hd");
    return L.node_list(n).value;
  end value;

begin
  assert (nodes'first = -1 and levels'first = 1, "API");
end balanced_essence;


with balanced_essence;
with fixed_string_control;
with status;
procedure tsbal is

-- Testing the Balanced Trees

procedure test_bal_easy is
  use status;

  type test_values  is range -1..128;
  type nodes        is range -1..20;
  type levels       is range 1..8;
  type tests        is range 1..10;
  type value_lists  is array (tests) of test_values;
  input_values:  constant value_lists := (33,36,39,31,32,35,38,37,34,40);
  n:             nodes;
  v:             test_values;
  telling:       constant boolean := FALSE;
  x:             nodes;

  type target_keys  is range 0..128;
  type key_lists    is array (tests) of target_keys;
  input_keys:    constant key_lists   := ( 3, 6, 9, 1, 2, 5, 8, 7, 4,10);

  function image (value: test_values) return string is
  begin
    return test_values'image (value);
  end image;

  procedure assign (left: in out test_values; right: test_values) is
  begin
    left := right;
  end assign;

  procedure test_bal_simple is
    package my_keys is
      subtype test_keys    is target_keys;

      procedure assign (left: in out test_keys; right: test_keys);
      function image (key:   test_keys) return string;
      function same (left, right: test_keys) return boolean;
    end my_keys;

    use my_keys;

    package skip_list is new balanced_essence (
      keys         => test_keys,
      first_key    => test_keys'first,
      last_key     => test_keys'last,
      "<"          => "<",
      same         => same,
      key_image    => image,
      assign_key   => assign,
      values       => test_values,
      null_value   => -1,
      assign_value => assign,
      nodes        => nodes,
      levels       => levels,
      value_image  => image);

    use skip_list;

    list: skip_lists;
    node: nodes;

    package body my_keys is
      procedure assign (left: in out test_keys; right: test_keys) is
      begin
        left := right;
      end assign;

      function image (key:   test_keys) return string is
      begin
        return test_keys'image (key);
      end image;

      function same (left, right: test_keys) return boolean is
      begin
          return left = right;
      end same;
    end my_keys;

  begin
    initialize (list);
    for T in tests loop
      node := insert (list, input_keys(T), input_values(T));
    end loop;

    for T in tests loop
      n := search (list, test_keys(T));
      v := value  (list, n);
      telln ("n=" &
             nodes'image(n) &
             "  value " &
             image (v));
    end loop;

    x := header_node;
    for T in tests loop
      x := next_node (list, x);
      telln ("next x=" &
             image (value (list, x)));
    end loop;
      telln("about to display the entire list");
      tell(list);
      telln("about to display the entire list");
    for T in tests range 1..3 loop
      delete (list, input_keys (T));
    end loop;
      tell (list);
    telln ("skip list test 1: 7 items in ascending order (no 3,6,9)");

    x := header_node;
    for T in tests loop
      x := next_node (list, x);
      telln ("            next x=" &
             test_values'image (value (list, x)));
    end loop;

  end test_bal_simple;


  procedure test_bal_2 is
    package my_keys is
      type test_keys    is access target_keys;

      procedure assign (left: in out test_keys; right: test_keys);
      function image (key:   test_keys) return string;
      function same (left, right: test_keys) return boolean;
      function less_than (left, right: test_keys) return boolean;
    end my_keys;

    use my_keys;

    package skip_list is new balanced_essence (
      keys         => test_keys,
      first_key    => new target_keys'(target_keys'first),
      last_key     => new target_keys'(target_keys'last),
      "<"          => less_than,
      same         => same,
      key_image    => image,
      assign_key   => assign,
      values       => test_values,
      null_value   => -1,
      assign_value => assign,
      nodes        => nodes,
      levels       => levels,
      value_image  => image);

    use skip_list;
    list: skip_lists;
    node: nodes;

    package body my_keys is
      procedure assign (left: in out test_keys; right: test_keys) is
      begin
        left := right; -- Copy the pointers without allocating a new copy.
      end assign;

      function image (key:   test_keys) return string is
      begin
        if key=null then
           report("Fatal: image: key is null");
        end if;
        return target_keys'image (key.all);
      end image;

      function less_than (left, right: test_keys) return boolean is
      begin
        if left=null or right=null then
           report("Fatal: less_than: argument is null");
        end if;
        return left.all < right.all;
      end less_than;

      function same (left, right: test_keys) return boolean is
      begin
        if left=null or right=null then
           report("Fatal: same: argument is null");
        end if;
        return left.all = right.all;
      end same;
    end my_keys;

  begin
    telln("started test_bal_2");
    initialize(list);
    for T in tests loop
      node := insert (list, new target_keys'(input_keys(T)), input_values(T));
    end loop;
    telln("inserted test_bal_2");

    for T in tests loop
      n := search (list, new target_keys'(target_keys(T)));
      v := value  (list, n);
      telln ("n=" &
             nodes'image(n) &
             "  value " &
             image (v));
    end loop;

    x := header_node;
    for T in tests loop
      x := next_node (list, x);
      telln ("next x=" & image (value (list, x)));
    end loop;

      tell(list);
    for T in tests range 1..3 loop
      delete (list, new target_keys'(input_keys (T)));
    end loop;
      tell (list);
    telln ("skip list test 2: 7 items in ascending order (no 3,6,9)");

    x := header_node;
    for T in tests loop
      x := next_node (list, x);
      telln ("  next x=" &
             test_values'image (value (list, x)));
    end loop;

  end test_bal_2;

begin

  -- The Tests are:
  --
  --   1. Key=Number
  --      Value=Number
  --
  --   2. Key=String
  --      Value=String
  --
  --   3. Key=Pointer to String
  --      Value=String
  --
  --   4. Key=Pointer to String
  --      Value=Pointer to String
  --
  --   5. Key=Fixed_String
  --      Value=Fixed_String
  --

  telln ("starting test_bal_easy");
  test_bal_simple;
  test_bal_2;
  telln ("test_bal_easy worked");
end test_bal_easy;


procedure test_bal_hard is
   use status;

  type nodes        is range -1..20;
  type levels       is range 1..8;
  type tests is range 1..10;
  type test_values  is access string;
  type value_lists is array (tests) of test_values;
  input_values:  constant value_lists := (
    new string'("33 thirty_three"),
    new string'("36 thirty_six"),
    new string'("39 thirty_nine"),
    new string'("31 thirty-one"),
    new string'("32 thirty-two"),
    new string'("35 thirty-five"),
    new string'("38 thirty-eight"),
    new string'("37 thirty-seven"),
    new string'("34 thirty-four"),
    new string'("40 forty"));

  output_values: value_lists;
  n:             nodes;
  v:             test_values;
  telling:       constant boolean := FALSE;
  x:             nodes;

  function image (value: test_values) return string is
  begin
    if value = null then
      return "!!";
    else
      return value.all;
    end if;
  end image;

  procedure assign (left: in out test_values; right: test_values) is
  begin
    left := right; -- just copying the pointer, not the object.
  end assign;

  procedure test_bal_3 is
    type target_keys     is range 0..128;
    type key_lists   is array (tests) of target_keys;
    input_keys:    constant key_lists   := ( 3, 6, 9, 1, 2, 5, 8, 7, 4,10);

    package my_keys is
      type test_keys    is access target_keys;

      procedure assign (left: in out test_keys; right: test_keys);
      function image (key:   test_keys) return string;
      function same (left, right: test_keys) return boolean;
      function less_than (left, right: test_keys) return boolean;
    end my_keys;

    use my_keys;

    package skip_list is new balanced_essence (
      keys         => test_keys,
      first_key    => new target_keys'(target_keys'first),
      last_key     => new target_keys'(target_keys'last),
      "<"          => less_than,
      same         => same,
      key_image    => image,
      assign_key   => assign,
      values       => test_values,
      null_value   => new string'(""),
      assign_value => assign,
      nodes        => nodes,
      levels       => levels,
      value_image  => image);

    use skip_list;
    list: skip_lists;
    node: nodes;

    package body my_keys is
      procedure assign (left: in out test_keys; right: test_keys) is
      begin
        left := right; -- Copy the pointers without allocating a new copy.
      end assign;

      function image (key:   test_keys) return string is
      begin
        if key=null then
           report("Fatal: image is null");
        end if;
        return target_keys'image (key.all);
      end image;

      function less_than (left, right: test_keys) return boolean is
      begin
        if left=null or right=null then
           report("Fatal: less_than is null");
        end if;
        return left.all < right.all;
      end less_than;

      function same (left, right: test_keys) return boolean is
      begin
        if left=null or right=null then
           report("Fatal: same is null");
        end if;
        return left.all = right.all;
      end same;
    end my_keys;

  begin
    initialize(list);
    for T in tests loop
      node := insert (list, new target_keys'(input_keys(T)), input_values(T));
    end loop;

    for T in tests loop
      n := search (list, new target_keys'(target_keys(T)));
      v := value  (list, n);
      telln ("n=" &
             nodes'image(n) &
             "  value " &
             image (v));
    end loop;

    x := header_node;
    for T in tests loop
      x := next_node (list, x);
      telln ("text x=" &
             image (value (list, x)));
    end loop;

      tell(list);
    for T in tests range 1..3 loop
      delete (list, new target_keys'(input_keys (T)));
    end loop;
      tell (list);
    telln ("skip list test 3: 7 items in ascending order (no 3,6,9)");

    x := header_node;
    for T in tests loop
      x := next_node (list, x);
      telln ("  next x=" &
             image (value (list, x)));
    end loop;

  end test_bal_3;

  procedure test_bal_4 is
    subtype target_keys is string;
    type test_keys is access target_keys;
    type key_lists   is array (tests) of test_keys;
    input_keys:    constant key_lists   := (
       new string'(" 3"),
       new string'(" 6"),
       new string'(" 9"),
       new string'(" 1"),
       new string'(" 2"),
       new string'(" 5"),
       new string'(" 8"),
       new string'(" 7"),
       new string'(" 4"),
       new string'("10"));

    package my_keys is
      procedure assign (left: in out test_keys; right: test_keys);
      function image (key:   test_keys) return string;
      function same (left, right: test_keys) return boolean;
      function less_than (left, right: test_keys) return boolean;
    end my_keys;

    use my_keys;

    package skip_list is new balanced_essence (
      keys         => test_keys,
      first_key    => new target_keys'(""),
      last_key     => new target_keys'("~~~~~~~~"),
      "<"          => less_than,
      same         => same,
      key_image    => image,
      assign_key   => assign,
      values       => test_values,
      null_value   => new string'(""),
      assign_value => assign,
      nodes        => nodes,
      levels       => levels,
      value_image  => image);

    use skip_list;
    list: skip_lists;
    node: nodes;

    package body my_keys is
      procedure assign (left: in out test_keys; right: test_keys) is
      begin
        left := right; -- Copy the pointers without allocating a new copy.
      end assign;

      function image (key:   test_keys) return string is
      begin
        if key=null then
           report("Fatal: image is null");
        end if;
        return key.all;
      end image;

      function less_than (left, right: test_keys) return boolean is
      begin
        if left=null or right=null then
           report("Fatal: less_than is null");
        end if;
        return left.all < right.all;
      end less_than;

      function same (left, right: test_keys) return boolean is
      begin
        if left=null or right=null then
           report("Fatal: same is null");
        end if;
        return left.all = right.all;
      end same;
    end my_keys;

  begin
    initialize(list);
    for T in tests loop
      node := insert (list, input_keys(T), input_values(T));
    end loop;

    for T in tests loop
      n := search (list, input_keys(T));
      v := value  (list, n);
      telln ("n=" &
             nodes'image(n) &
             "  value " &
             image (v));
    end loop;

    x := header_node;
    for T in tests loop
      x := next_node (list, x);
      telln ("next x=" &
             image (value (list, x)));
    end loop;

      tell(list);
    for T in tests range 1..3 loop
      delete (list, input_keys (T));
    end loop;
      tell (list);
    telln ("skip list test 4: 7 items in ascending order (no 3,6,9)");

    x := header_node;
    for T in tests loop
      x := next_node (list, x);
      telln ("next x=" &
             image (value (list, x)));
    end loop;

  end test_bal_4;

begin

  -- The Tests are:
  --
  --   1. Key=Number
  --      Value=Number
  --
  --   2. Key=String
  --      Value=String
  --
  --   3. Key=Pointer to String
  --      Value=String
  --
  --   4. Key=Pointer to String
  --      Value=Pointer to String
  --
  --   5. Key=Fixed_String
  --      Value=Fixed_String
  --

  telln ("started test_bal_hard");
  test_bal_3;
  test_bal_4;
  telln ("test_bal_hard worked");
end test_bal_hard;



procedure test_bal_fixed_1 is
  use status;
  use fixed_string_control;

  type nodes        is range -1..20;
  type levels       is range 1..8;
  type tests is range 1..10;
  subtype test_values  is fixed_strings;
  type value_lists is array (tests) of test_values;
  input_values:  constant value_lists := (
    fixed_string("33 thirty_three"),
    fixed_string("36 thirty_six"),
    fixed_string("39 thirty_nine"),
    fixed_string("31 thirty-one"),
    fixed_string("32 thirty-two"),
    fixed_string("35 thirty-five"),
    fixed_string("38 thirty-eight"),
    fixed_string("37 thirty-seven"),
    fixed_string("34 thirty-four"),
    fixed_string("40 forty"));

  output_values: value_lists;
  n:             nodes;
  v:             test_values;
  telling:       constant boolean := FALSE;
  x:             nodes;

  subtype target_keys is string;
  subtype test_keys is fixed_strings;
  type key_lists   is array (tests) of test_keys;
  input_keys:    constant key_lists   := (
    fixed_string(" 3"),
    fixed_string(" 6"),
    fixed_string(" 9"),
    fixed_string(" 1"),
    fixed_string(" 2"),
    fixed_string(" 5"),
    fixed_string(" 8"),
    fixed_string(" 7"),
    fixed_string(" 4"),
    fixed_string("10"));


  package skip_list is new balanced_essence (
    keys         => test_keys,
    first_key    => null_fixed_string,
    last_key     => fixed_string("~~~~~~~~"),
    "<"          => "<",
    same         => same,
    key_image    => image,
    assign_key   => assign,
    values       => test_values,
    null_value   => null_fixed_string,
    assign_value => assign,
    nodes        => nodes,
    levels       => levels,
    value_image  => image);

  use skip_list;
  list: skip_lists;
  node: nodes;

  procedure iterate is
     this_key: fixed_strings;
  begin
     x := header_node;
     for T in tests loop
       x := next_node (list, x);
       telln ("next x=" &
              image (value (list, x)));
       this_key := key (list,x);
       if search (list, this_key) = failure then
          tell(list);
          report("Failed: test_search a");
       elsif linear_search (list, this_key) = failure then
          tell(list);
          report("Failed: test_search b");
       elsif search (list, input_keys (T)) = failure then
          tell(list);
          report("Failed: test_search c");
       elsif linear_search (list, input_keys (T)) = failure then
          tell(list);
          report("Failed: test_search d");
       end if;
     end loop;
  end iterate;

begin
  telln ("test_bal_fixed_1 started");
  initialize(list);
  for T in tests loop
    node := insert (list, input_keys(T), input_values(T));
  end loop;

  for T in tests loop
    n := search (list, input_keys(T));
    v := value  (list, n);
    telln ("n=" &
           nodes'image(n) &
           "  value " &
           image (v));
  end loop;

  iterate;

    tell(list);
  for T in tests range 1..3 loop
    delete (list, input_keys (T));
  end loop;
    tell (list);
  telln ("skip list test 4: 7 items in ascending order (no 3,6,9)");

  x := header_node;
  for T in tests loop
    x := next_node (list, x);
    telln ("next x=" &
           image (value (list, x)));
  end loop;
  telln ("test_bal_fixed_1 worked");
end test_bal_fixed_1;



procedure test_bal_fixed_2 is
   use status;
  use fixed_string_control;

  type nodes        is range -1..20;
  type levels       is range 1..8;
  type tests is range 1..10;
  subtype test_values  is fixed_strings;
  type value_lists is array (tests) of test_values;
  input_values:  constant value_lists := (
    fixed_string("33 thirty_three"),
    fixed_string("36 thirty_six"),
    fixed_string("39 thirty_nine"),
    fixed_string("31 thirty-one"),
    fixed_string("32 thirty-two"),
    fixed_string("35 thirty-five"),
    fixed_string("38 thirty-eight"),
    fixed_string("37 thirty-seven"),
    fixed_string("34 thirty-four"),
    fixed_string("40 forty"));

  output_values: value_lists;
  n:             nodes;
  v:             test_values;
  telling:       constant boolean := FALSE;
  x:             nodes;

  subtype target_keys is string;
  subtype test_keys is fixed_strings;
  type key_lists   is array (tests) of test_keys;
  input_keys:    constant key_lists   := (
    fixed_string(" 3"),
    fixed_string(" 6"),
    fixed_string(" 9"),
    fixed_string(" 1"),
    fixed_string(" 2"),
    fixed_string(" 5"),
    fixed_string(" 8"),
    fixed_string(" 7"),
    fixed_string(" 4"),
    fixed_string("10"));


  package skip_list is new balanced_essence (
    keys         => test_keys,
    first_key    => null_fixed_string,
    last_key     => fixed_string("~~~~~~~~"),
    "<"          => "<",
    same         => same,
    key_image    => image,
    assign_key   => assign,
    values       => test_values,
    null_value   => null_fixed_string,
    assign_value => assign,
    nodes        => nodes,
    levels       => levels,
    value_image  => image);

  use skip_list;
  list: skip_lists;
  node: nodes;

  procedure iterate is
     this_key: fixed_strings;
  begin
     x := header_node;
     for T in tests loop
       x := next_node (list, x);
       telln ("next x=" &
              image (value (list, x)));
       this_key := key (list,x);
       if search (list, this_key) = failure then
          tell(list);
          report("Failed: test_search a");
       elsif linear_search (list, this_key) = failure then
          tell(list);
          report("Failed test_search b");
       elsif search (list, input_keys (T)) = failure then
          tell(list);
          report("Failed test_search c");
       elsif linear_search (list, input_keys (T)) = failure then
          tell(list);
          report("Failed test_search d");
       end if;
     end loop;
  end iterate;

begin
  initialize(list);
  for T in tests loop
    node := insert (list, input_keys(T), input_values(T));
  end loop;

  for T in tests loop
    n := search (list, input_keys(T));
    v := value  (list, n);
    telln ("n=" &
           nodes'image(n) &
           "  value " &
           image (v));
  end loop;

  iterate;

    tell(list);
  for T in tests range 1..3 loop
    delete (list, input_keys (T));
  end loop;
    tell (list);
  telln ("skip list test 4: 7 items in ascending order (no 3,6,9)");

  x := header_node;
  for T in tests loop
    x := next_node (list, x);
    telln ("next x=" &
           image (value (list, x)));
  end loop;

  telln ("test_bal_fixed_2 worked");
end test_bal_fixed_2;


procedure test_bal_fixed_3 is
  use status;
  use fixed_string_control;

  type nodes        is range -1..20;
  type levels       is range 1..8;
  type tests is range 1..17;
  subtype test_values  is fixed_strings;
  type value_lists is array (tests) of test_values;
  input_values:  constant value_lists := (
    fixed_string("33 thirty_three"),
    fixed_string("36 thirty_six"),
    fixed_string("39 thirty_nine"),
    fixed_string("31 thirty-one"),
    fixed_string("32 thirty-two"),
    fixed_string("35 thirty-five"),
    fixed_string("38 thirty-eight"),
    fixed_string("37 thirty-seven"),
    fixed_string("34 thirty-four"),
    fixed_string("E1 extra 1"),
    fixed_string("E2 extra 2"),
    fixed_string("E3 extra 3"),
    fixed_string("E4 extra 4"),
    fixed_string("E5 extra 5"),
    fixed_string("E6 extra 6"),
    fixed_string("E7 extra 6"),
    fixed_string("40 forty"));

  output_values: value_lists;
  n:             nodes;
  v:             test_values;
  telling:       constant boolean := FALSE;
  x:             nodes;

  subtype target_keys is string;
  subtype test_keys is fixed_strings;
  type key_lists   is array (tests) of test_keys;
  input_keys:    constant key_lists   := (
     fixed_string("MODATA"),
     fixed_string("PB6Q.3"),
     fixed_string("PB6Q.3HI"),
     fixed_string("PDS1.3.2.1"),
     fixed_string("PDS1.3.2.2.1"),
     fixed_string("PDS1.3.2.2"),
     fixed_string("PDS1.3.3.1"),
     fixed_string("PDS1.3.3.2"),
     fixed_string("PPS3.1.9.8.1"),
     fixed_string("PPS3.4.1.1.3.1"),
     fixed_string("PPS3.4.1.1.3"),
     fixed_string("PPS3.4.1.1"),
     fixed_string("PPS3.4.1.2"),
     fixed_string("PPS3.4.1.3.3"),
     fixed_string("PPS3.4.1.3"),
     fixed_string("SRCCBMBMEM"),
     fixed_string("SRCCMEMO"));

  package skip_list is new balanced_essence (
    keys         => test_keys,
    first_key    => null_fixed_string,
    last_key     => fixed_string("~~~~~~~~"),
    "<"          => "<",
    same         => same,
    key_image    => image,
    assign_key   => assign,
    values       => test_values,
    null_value   => null_fixed_string,
    assign_value => assign,
    nodes        => nodes,
    levels       => levels,
    value_image  => image);

  use skip_list;
  list: skip_lists;
  node: nodes;

  procedure iterate is
     this_key: fixed_strings;
  begin
     x := header_node;
     for T in tests loop
       x := next_node (list, x);
       telln ("next x=" &
              image (value (list, x)));
       this_key := key (list,x);
       if search (list, this_key) = failure then
          tell(list);
          report("Failed test_search 5");
       elsif linear_search (list, this_key) = failure then
          tell(list);
          report("Failed test_search 6");
       elsif search (list, input_keys (T)) = failure then
          tell(list);
          report("Failed test_search 7");
       elsif linear_search (list, input_keys (T)) = failure then
          tell(list);
          report("Failed test_search 8");
       end if;
     end loop;
  end iterate;

begin
  telln ("test_bal_fixed_2 started");
  initialize(list);
  for T in tests loop
    node := insert (list, input_keys(T), input_values(T));
  end loop;

  for T in tests loop
    n := search (list, input_keys(T));
    v := value  (list, n);
    telln ("n=" &
           nodes'image(n) &
           "  value " &
           image (v));
  end loop;

  iterate;

    tell(list);
  for T in tests range 1..3 loop
    delete (list, input_keys (T));
  end loop;
    tell (list);
  telln ("skip list test 4: 7 items in ascending order (no 3,6,9)");

  x := header_node;
  for T in tests loop
    x := next_node (list, x);
    telln ("next x=" &
           image (value (list, x)));
  end loop;

  telln ("test_bal_fixed_3 worked");
end test_bal_fixed_3;

begin
  test_bal_easy;
  test_bal_hard;
  test_bal_fixed_1;
  test_bal_fixed_2;
  test_bal_fixed_3;
  status.telln("tsbal passed");
end tsbal;




  parent reply	other threads:[~1997-01-03  0:00 UTC|newest]

Thread overview: 13+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
1997-01-02  0:00 Anyone could give a complete and yet small program on the use for the generic Hung-Hsien Chang
1997-01-02  0:00 ` Robert Dewar
1997-01-03  0:00   ` Jon S Anthony
1997-01-03  0:00   ` Michael F Brenner [this message]
1997-01-04  0:00     ` Rich Maggio
1997-01-08  0:00       ` "Bugs" (Was: " Richard Riehle
1997-01-08  0:00         ` Robert Dewar
1997-01-27  0:00           ` "Bugs" Richard Riehle
     [not found]           ` <Pine.GSO.3.95.970114142048.28412A-100000@nunic.nu.e>
1997-01-29  0:00             ` "Bugs" Jim Hopper
1997-01-29  0:00               ` "Bugs" Arthur Evans Jr
1997-01-29  0:00               ` "Bugs" Mike Ryer
1997-01-08  0:00         ` "Bugs" (Was: Anyone could give a complete and yet small program on the use for the generic Larry Kilgallen
1997-01-10  0:00     ` Robert I. Eachus
replies disabled

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox