--  This demonstration is a response to a suggestion by John English
--  <J.English@bton.ac.uk> about assessing different component libraries, in
--  the context of the Ada Standard Component Library WG
--  (http://www.suffix.com/Ada/SCL/). It uses part of Corey Minford
--  <minyard@acm.org>'s solution (why reinvent that wheel?!)

--  John said:

--  As a way of objectively assessing the merits of different approaches,
--  perhaps the way to do this is to code some examples; one of my
--  favourites for this is a program to list the 10 most common words in a
--  file with the number of occurrences of each, where the length of words
--  and the size of the file can be arbitrarily large. In Perl it might look
--  something like this:

--   while (<>) {                     # for each line in the input file(s)
--     chomp;                         # trim the end of the line
--     tr/A-Z/a-z/;                   # fold uppercase to lowercase
--     @words = split /\W+/;          # break the line into words
--     foreach (@words) {
--       if (/^\w+$/) {               # ignore non-words
--         $wordlist{$_}++;           # increment count in associative array
--       }                            # (key = word, val = no. of occurrences)
--     }
--   }
--   $times = 0;
--   foreach (sort {$wordlist{$b} <=> $wordlist{$a}} (keys %wordlist)) {
--     last if (++$times > 10);       # exit loop after 10 iterations
--     print "$_ : $wordlist{$_}\n";  # process array in descending order
--   }                                # of value, printing keys and values

--  What would this look like in Ada using each of the libraries you've
--  listed?  Does anyone else have favourite examples like this?

--  $Id: bcwords.ada,v 1.6 2001/09/23 15:25:10 simon Exp $

with Ada.Strings.Unbounded;
with Ada.Text_IO;
with Word_Parser;
with Word_Count_Support;

procedure Word_Count is
   Word_Found : Boolean;
   File_Done : Boolean;
   Word : Ada.Strings.Unbounded.Unbounded_String;
   Word_Bag : Word_Count_Support.BU.Bag;
   Word_Tree : Word_Count_Support.ST.AVL_Tree;
   Word_Bag_Iter : Word_Count_Support.Containers.Iterator'Class
     := Word_Count_Support.BU.New_Iterator (Word_Bag);
   procedure Word_Processor (Item : Ada.Strings.Unbounded.Unbounded_String;
                             Ok : out Boolean);
   procedure Word_Processor (Item : Ada.Strings.Unbounded.Unbounded_String;
                             Ok : out Boolean) is
      Dummy : Boolean;
   begin
      Word_Count_Support.ST.Insert
        (Word_Tree,
         Word_Count_Support.Word_Stat'
         (Word => Item,
          Count => Word_Count_Support.BU.Count (Word_Bag, Item)),
         Dummy);
      Ok := True;
   end Word_Processor;
   procedure Word_Bag_Visitor
   is new Word_Count_Support.Containers.Visit (Word_Processor);
   Number_Output : Natural := 0;
   procedure Tree_Processor (Item : Word_Count_Support.Word_Stat;
                             OK : out Boolean);
   procedure Tree_Processor (Item : Word_Count_Support.Word_Stat;
                             OK : out Boolean) is
   begin
      Ada.Text_IO.Put_Line
        (Ada.Strings.Unbounded.To_String (Item.Word)
         & " =>"
         & Positive'Image (Item.Count));
      Number_Output := Number_Output + 1;
      OK := Number_Output < 10;          --  this is where we select the top 10
   end Tree_Processor;
   procedure Tree_Visitor is new Word_Count_Support.ST.Visit (Tree_Processor);
begin
   loop
      Word_Parser.Get_Next_Word
        (Ada.Text_IO.Standard_Input, Word, Word_Found, File_Done);
      exit when not Word_Found;
      Word_Count_Support.Bags.Add (Word_Bag, Word);
   end loop;
   Word_Count_Support.Containers.Reset (Word_Bag_Iter);
   Word_Bag_Visitor (Word_Bag_Iter);
   Tree_Visitor (Word_Tree);
end Word_Count;
with Ada.Strings.Unbounded;
with BC.Containers;
with BC.Containers.Bags;
with BC.Containers.Bags.Unbounded;
with BC.Containers.Trees;
with BC.Containers.Trees.AVL;
with Global_Heap;

package Word_Count_Support is

   package Containers is new BC.Containers
     (Item => Ada.Strings.Unbounded.Unbounded_String,
        "=" => Ada.Strings.Unbounded."=");

   package Bags is new Containers.Bags;

   function Hash (S : Ada.Strings.Unbounded.Unbounded_String) return Positive;

   package BU is new Bags.Unbounded (Hash => Hash,
                                     Buckets => 1,
                                     Storage => Global_Heap.Storage);

   type Word_Stat is record
      Word : Ada.Strings.Unbounded.Unbounded_String;
      Count : Positive;
   end record;

   function ">" (L, R : Word_Stat) return Boolean;
   function "=" (L, R : Word_Stat) return Boolean;

   package Stat_Containers is new BC.Containers (Word_Stat);

   package Trees is new Stat_Containers.Trees;

   package ST is new Trees.AVL
     ("<" => ">",     --  we need the most popular first
      Storage => Global_Heap.Storage);

end Word_Count_Support;
package body Word_Count_Support is

   --  This is extraordinarily lazy, of course we should really invent
   --  some better hash function!
   function Hash
     (S : Ada.Strings.Unbounded.Unbounded_String) return Positive is
   begin
      return 1;
   end Hash;

   function ">" (L, R : Word_Stat) return Boolean is
      use type Ada.Strings.Unbounded.Unbounded_String;
   begin
      return L.Count > R.Count
        or else (L.Count = R.Count
                 and then L.Word > R.Word);
   end ">";

   function "=" (L, R : Word_Stat) return Boolean is
      use type Ada.Strings.Unbounded.Unbounded_String;
   begin
      return L.Count = R.Count
        and then L.Word = R.Word;
   end "=";

end Word_Count_Support;
--  by Corey Minyard
package body Word_Parser is

   Big_A_Pos   : Integer := Character'Pos ('A');
   Small_A_Pos : Integer := Character'Pos ('a');

   procedure Xlat_To_Lower_Case (C : in out Character);
   procedure Xlat_To_Lower_Case (C : in out Character) is
   begin
      if (C in 'A' .. 'Z') then
         C := Character'Val (Character'Pos (C) - Big_A_Pos + Small_A_Pos);
      end if;
   end Xlat_To_Lower_Case;

   procedure Get_Next_Word
     (File       : in File_Type;
      Word       : out Ada.Strings.Unbounded.Unbounded_String;
      Word_Found : out Boolean;
      File_Done  : out Boolean) is

      Tmp_Str    : String (1 .. 10);
      Word_Pos   : Positive := Tmp_Str'First;
      Input_Char : Character;
      In_Word    : Boolean := False;
   begin
      --  Start with an empty word.
      Word := Ada.Strings.Unbounded.To_Unbounded_String ("");

      File_Done := False;
      Word_Found := False;

      if (End_Of_File (File)) then
         Word_Found := False;
         File_Done := True;
      else
         loop
            Get (File, Input_Char);
            Xlat_To_Lower_Case (Input_Char);

            if (not In_Word) then
               if (Input_Char in 'a' .. 'z') then
                  In_Word := True;
                  Word_Found := True;
                  Tmp_Str (Word_Pos) := Input_Char;
                  Word_Pos := Word_Pos + 1;
               end if;
            elsif (Input_Char in 'a' .. 'z') then
               Tmp_Str (Word_Pos) := Input_Char;
               if (Word_Pos = Tmp_Str'Last) then
                  Word := Word & Tmp_Str;
                  Word_Pos := Tmp_Str'First;
               else
                  Word_Pos := Word_Pos + 1;
               end if;
            else
               exit;
            end if;

            if (End_Of_File (File)) then
               File_Done := True;
               exit;
            elsif (End_Of_Line (File) and In_Word) then
               exit;
            end if;
         end loop;

         if (Word_Pos /= Tmp_Str'First) then
            --  If we have some stuff left in the temporary string, put it into
            --  the word.
            Word := Word & Tmp_Str (Tmp_Str'First .. Word_Pos - 1);
         end if;
      end if;
   end Get_Next_Word;

end Word_Parser;
--  by Corey Minyard
with Ada.Strings.Unbounded; use type Ada.Strings.Unbounded.Unbounded_String;
with Ada.Text_IO; use Ada.Text_IO;
package Word_Parser is

   procedure Get_Next_Word
     (File       : in File_Type;
      Word       : out Ada.Strings.Unbounded.Unbounded_String;
      Word_Found : out Boolean;
      File_Done  : out Boolean);

end Word_Parser;


syntax highlighted by Code2HTML, v. 0.9.1