home *** CD-ROM | disk | FTP | other *** search
/ Programmer's ROM - The Computer Language Library / programmersrom.iso / ada / misc / search.ada < prev    next >
Encoding:
Text File  |  1988-05-03  |  19.5 KB  |  485 lines

  1.  
  2. -------- SIMTEL20 Ada Software Repository Prologue ------------
  3. --                                                           -*
  4. -- Unit name    : generic package Search_Utilities
  5. -- Version      : 1.0 (MOOV115)
  6. -- Author       : Geoffrey O. Mendal
  7. --              : Stanford University
  8. --              : Computer Systems Laboratory
  9. --              : Stanford, CA  94305
  10. --              : (415) 497-1414 or 497-1175
  11. -- DDN Address  : Mendal@SU-SIERRA.ARPA
  12. -- Copyright    : (c) 1985 Geoffrey O. Mendal
  13. -- Date created : Mon 11 Nov 85
  14. -- Release date : Sun 25 Dec 85
  15. -- Last update  : MENDAL Sun 25 Dec 85
  16. -- Machine/System Compiled/Run on : DG MV10000, ROLM ADE
  17. --                                  VAX 11/780, DEC ACS
  18. --                                  RATIONAL R1000
  19. -- Dependent Units : package SYSTEM
  20. --                                                           -*
  21. ---------------------------------------------------------------
  22. --                                                           -*
  23. -- Keywords     :  SEARCH
  24. ----------------:  SEARCH UTILITIES
  25. --
  26. -- Abstract     :  This generic package contains binary and
  27. ----------------:  sequential searching routines for arrays.
  28. ----------------:  A full paper describing this unit's
  29. ----------------:  capabilities is available by contacting the
  30. ----------------:  author at the above address.
  31. --                                                           -*
  32. ------------------ Revision history ---------------------------
  33. --                                                           -*
  34. -- DATE         VERSION              AUTHOR     HISTORY
  35. -- 12/29/85     1.0 (MOOV115)     Mendal     Initial Release
  36. --                                                           -*
  37. ------------------ Distribution and Copyright -----------------
  38. --                                                           -*
  39. -- This prologue must be included in all copies of this software.
  40. --
  41. -- This software is copyright by the author.
  42. --
  43. -- This software is released to the Ada community.
  44. -- This software is released to the Public Domain (note:
  45. --   software released to the Public Domain is not subject
  46. --   to copyright protection).
  47. -- Restrictions on use or distribution:  NONE
  48. --                                                           -*
  49. ------------------ Disclaimer ---------------------------------
  50. --                                                           -*
  51. -- This software and its documentation are provided "AS IS" and
  52. -- without any expressed or implied warranties whatsoever.
  53. -- No warranties as to performance, merchantability, or fitness
  54. -- for a particular purpose exist.
  55. --
  56. -- Because of the diversity of conditions and hardware under
  57. -- which this software may be used, no warranty of fitness for
  58. -- a particular purpose is offered.  The user is advised to
  59. -- test the software thoroughly before relying on it.  The user
  60. -- must assume the entire risk and liability of using this
  61. -- software.
  62. --
  63. -- In no event shall any person or organization of people be
  64. -- held responsible for any direct, indirect, consequential
  65. -- or inconsequential damages or lost profits.
  66. --                                                           -*
  67. -------------------END-PROLOGUE--------------------------------
  68.  
  69. -- Search_Utilities is a generic searching package. The SEARCH subprograms
  70. -- will search a one dimensional array of any data type
  71. -- indexed by discrete type components.
  72.  
  73. -- Note that the component type of the array is not restricted to simple
  74. -- types. An array of records or allocators can be searched. If the
  75. -- component type is a record or allocator, then the generic formal
  76. -- subprogram parameter "<" below must be specified as a selector
  77. -- function.
  78.  
  79. with SYSTEM;  -- predefined package SYSTEM
  80.  
  81. generic
  82.   type Component_Type is limited private;  -- type of component to search for
  83.   type Index_Type     is (<>);             -- type of array index
  84.  
  85.   -- the following generic formal type is required due to Ada's
  86.   -- strong typing requirements. The SEARCH subprograms cannot handle
  87.   -- anonymous array types. This type will match any unconstrained
  88.   -- array type.
  89.  
  90.   type Array_Type is array(Index_Type range <>) of Component_Type;
  91.  
  92.   -- the following generic formal subprogram parameter defaults to
  93.   -- an ascending order strategy. If records or access types are to
  94.   -- be searched, a selector function must be specified. If the array
  95.   -- is not ordered, the selector function need not be supplied.
  96.   -- (See examples #2 and #3 below).
  97.  
  98.   with function "<"(Left,Right : in Component_Type) return BOOLEAN is <>;
  99.  
  100.   -- the following generic formal subprogram parameter defaults to
  101.   -- the predefined equality operator. It can be used to specify a
  102.   -- user-defined equality operation (see example #4 below).
  103.  
  104.   with function "="(Left, Right : in Component_Type) return BOOLEAN is <>;
  105. package Search_Utilities is
  106.   Search_Utilities_Version : constant STRING := "1.0 MOOV115";
  107.  
  108.   Component_Type_is_Unconstrained : exception;
  109.  
  110.   -- the following type should be used to specify how the data is
  111.   -- ordered. The default is Not_Ordered. However, significant CPU time
  112.   -- can be saved if the data is ordered and the default, Not_Ordered,
  113.   -- is overridden.
  114.  
  115.   -- if the data are ordered, then if two or more components in the array
  116.   -- can match the search component provided, then the component location
  117.   -- returned by SEARCH should be thought of as an arbitrary selection
  118.   -- from amongst those possible match-components.
  119.  
  120.   -- if the data are not ordered, then if two or more components in the
  121.   -- array can match the search component provided, then the component
  122.   -- location returned by SEARCH will be the one closest to
  123.   -- Search_Array'FIRST.
  124.  
  125.   type Data_Order_Type is (Ordered,Not_Ordered);
  126.  
  127.   -- the following type declaration should be used to specify the
  128.   -- instrumentation analysis data that can be returned by the
  129.   -- SEARCH procedure below. -1 is only returned if an overflow in
  130.   -- calculations has occurred. The SEARCH subprograms will not terminate
  131.   -- if an overflow in instrumentation analysis data calculations has
  132.   -- occurred.
  133.  
  134.   type Performance_Instrumentation_Type is range -1 .. SYSTEM.MAX_INT;
  135.  
  136.   -- the following procedure will search a one dimensional array of
  137.   -- components. It can search an ordered or unordered array. If
  138.   -- an ordered array is specified, it defaults to an ascending
  139.   -- order (which can be overridden by the user). The array components
  140.   -- must only support equality, inequality, and assignment (private
  141.   -- types). The array indices can be of any discrete type. The number
  142.   -- of comparisons can also be returned.
  143.  
  144.   procedure SEARCH(
  145.     Component                        : in     Component_Type;
  146.     Search_Array                     : in     Array_Type;
  147.     Location_Found                   :    out Index_Type;
  148.     Component_Found                  :    out BOOLEAN;
  149.     Number_of_Comparisons            :    out Performance_Instrumentation_Type;
  150.     Order_Strategy                   : in     Data_Order_Type := Not_Ordered;
  151.     No_Match_Index                   : in     Index_Type      := Index_Type'LAST);
  152.  
  153.   -- the following overloading of procedure SEARCH should be used when
  154.   -- no instrumentation analysis data are required.
  155.  
  156.   procedure SEARCH(
  157.     Component                        : in     Component_Type;
  158.     Search_Array                     : in     Array_Type;
  159.     Location_Found                   :    out Index_Type;
  160.     Component_Found                  :    out BOOLEAN;
  161.     Order_Strategy                   : in     Data_Order_Type := Not_Ordered;
  162.     No_Match_Index                   : in     Index_Type      := Index_Type'LAST);
  163.     
  164.   -- the following overloading of function SEARCH should be used when
  165.   -- the user only wants to know if the component exists or not.
  166.   
  167.   function SEARCH(
  168.     Component                        : in Component_Type;
  169.     Search_Array                     : in Array_Type;
  170.     Order_Strategy                   : in Data_Order_Type := Not_Ordered)
  171.     return BOOLEAN;
  172.     
  173.   -- the following overloading of function SEARCH should be used when
  174.   -- the component is definitely known to exist and only the location
  175.   -- is required. (Note that No_Match_Index may be used to return a
  176.   -- no match index value... but this won't work in all cases.)
  177.   
  178.   function SEARCH(
  179.     Component                        : in Component_Type;
  180.     Search_Array                     : in Array_Type;
  181.     Order_Strategy                   : in Data_Order_Type := Not_Ordered;
  182.     No_Match_Index                   : in Index_Type      := Index_Type'LAST)
  183.     return Index_Type;
  184. end Search_Utilities;
  185.  
  186. -- Example uses/instantiations:
  187. --   EXAMPLE #1: Search an ascending ordered array of enumeration literals
  188. --   with Search_Utilities,TEXT_IO;
  189. --   . . .
  190. --   type My_Component_Type is (Red,Blue,Green,Orange);
  191. --   type My_Index_Type is (Sun,Mon,Tue,Wed,Thu,Fri,Sat);
  192. --   type My_Array_Type is array(My_Index_Type range <>) of My_Component_Type;
  193. --   . . .
  194. --   My_Component : My_Component_Type;
  195. --   My_Array     : My_Array_Type(Mon .. Fri);
  196. --   Location     : My_Index_Type;
  197. --   Found        : BOOLEAN;
  198. --   . . .
  199. --   package My_Search is new Search_Utilities(
  200. --     Component_Type => My_Component_Type,
  201. --     Index_Type     => My_Index_Type,
  202. --     Array_Type     => My_Array_Type);
  203. --   . . .
  204. --   My_Search.SEARCH(
  205. --     Component             => My_Component,
  206. --     Search_Array          => My_Array,
  207. --     Location_Found        => Location,
  208. --     Component_Found       => Found,
  209. --     Order_Strategy        => My_Search.Ordered);
  210. --   if Found then
  211. --     TEXT_IO.PUT_LINE("Component found");
  212. --   else
  213. --     TEXT_IO.PUT_LINE("Component not found");
  214. --   end if;
  215. --   . . .
  216. --   end;  -- end of driver routine
  217. --   -------------------------------------------------------------------
  218. --   EXAMPLE #2: Search an unordered array of records
  219. --   with Search_Utilities,TEXT_IO;
  220. --   . . .
  221. --   type My_Component_Type is
  222. --     record
  223. --       Field1 : INTEGER;
  224. --       Field2 : FLOAT;
  225. --     end record;
  226. --   subtype My_Index_Type is INTEGER range -100 .. 100;
  227. --   type My_Array_Type is array(My_Index_Type range <>) of My_Component_Type;
  228. --   . . .
  229. --   My_Component : My_Component_Type;
  230. --   My_Array     : My_Array_Type(-100 .. 100);
  231. --   . . .
  232. --   package My_Search is new Search_Utilities(
  233. --     Component_Type => My_Component_Type,
  234. --     Index_Type  => My_Index_Type,
  235. --     Array_Type   => My_Array_Type);
  236. --   . . .
  237. --   if My_Search.SEARCH(
  238. --        Component        => My_Component,
  239. --        Search_Array     => My_Array) then
  240. --     TEXT_IO.PUT_LINE("Component found");
  241. --   else
  242. --     TEXT_IO.PUT_LINE("Component not found");
  243. --   end if;
  244. --   . . .
  245. --   end;  -- end of driver routine
  246. --   -------------------------------------------------------------------
  247. --   Example #3: search an array of ordered access types
  248. --   with Search_Utilities, TEXT_IO;
  249. --   . . .
  250. --   type Taxpayer_Type;  -- an incomplete type declaration
  251. --   type Taxpayer_Access_Type is access Taxpayer_Type;
  252. --   type Taxpayer_Type is
  253. --     record
  254. --       Name              : STRING(1 .. 40);
  255. --       Age               : NATURAL;
  256. --       ID_Number         : POSITIVE;  -- social security number
  257. --     end record;
  258. --   type My_Index_Type is range 1 .. 1_000_000;
  259. --   type My_Array_Type is array(My_Index_Type range <>) of Taxpayer_Access_Type;
  260. --   . . .
  261. --   function Descending_Taxpayer_Search(Left,Right : in Taxpayer_Access_Type) return BOOLEAN;
  262. --   . . .
  263. --   package My_Search is new Search_Utilities(Taxpayer_Access_Type,
  264. --     My_Index_Type,My_Array_Type,Descending_Taxpayer_Search);
  265. --   . . .
  266. --   My_Array                 : My_Array_Type(1 .. 1_000_000);
  267. --   Number_of_Comparisons    : My_Search.Performance_Instrumentation_Type;
  268. --   A_Component              : Taxpayer_Access_Type;
  269. --   Location                 : My_Index_Type;
  270. --   Found                    : BOOLEAN;
  271. --   . . .
  272. --   function Descending_Taxpayer_Search(Left,Right : in Taxpayer_Access_Type)
  273. --     return BOOLEAN is
  274. --   begin
  275. --     return (Left.Name > Right.Name) or
  276. --            ((Left.Name = Right.Name) and (Left.ID_Number > Right.ID_Number));
  277. --   end Descending_Taxpayer_Search;
  278. --   . . .
  279. --   My_Search.SEARCH(A_Component,My_Array,Location,Found,Num_Compares,
  280. --     My_Search.Ordered);
  281. --   if Found then
  282. --     TEXT_IO.PUT_LINE("Component found. Took " &
  283. --       Search_Utilities.Performance_Instrumentation_Type'IMAGE(Num_Compares) &
  284. --       " comparisons.");
  285. --   end if;
  286. --   . . .
  287. --   end;   -- end of the driver routine
  288. --   ---------------------------------------------------------------------------
  289. --   EXAMPLE #4: Search an array of floating point numbers
  290. --   with Search_Utilities;
  291. --   . . .
  292. --   type My_Array_Type is array(POSITIVE range <>) of FLOAT;
  293. --   . . .
  294. --   My_Array : My_Array_Type(1..10);
  295. --   Location : POSITIVE;
  296. --   Found    : BOOLEAN;
  297. --   . . .
  298. --   function My_Equality(L, R : in FLOAT) return BOOLEAN is
  299. --   begin
  300. --     . . .  -- check for "close enough" on equality
  301. --     return <some BOOLEAN expression>
  302. --   end My_Equality;
  303. --   . . .
  304. --   package My_Search_Utilities is new Search_Utilities(FLOAT,POSITIVE,My_Array_Type,
  305. --     My_Equality);
  306. --   . . .
  307. --   My_Search.SEARCH(3.14159,My_Array,Location,Found);
  308. --   . . .
  309. --   end;  -- end of the driver routine
  310.  
  311. package body Search_Utilities is
  312.   procedure SEARCH(
  313.     Component                        : in     Component_Type;
  314.     Search_Array                     : in     Array_Type;
  315.     Location_Found                   :    out Index_Type;
  316.     Component_Found                  :    out BOOLEAN;
  317.     Number_of_Comparisons            :    out Performance_Instrumentation_Type;
  318.     Order_Strategy                   : in     Data_Order_Type := Not_Ordered;
  319.     No_Match_Index                   : in     Index_Type     := Index_Type'LAST) is
  320.  
  321.     Local_Comparisons : Performance_Instrumentation_Type := 0;
  322.  
  323.     -- the procedure below is a utility routine
  324.  
  325.     procedure Update_Performance_Instrumentation(
  326.       Instrumentation_Count : in out Performance_Instrumentation_Type) is
  327.     begin
  328.       -- bump the counter unless an overflow has occurred
  329.  
  330.       if Instrumentation_Count /= Performance_Instrumentation_Type'FIRST then
  331.         if Instrumentation_Count /= Performance_Instrumentation_Type'LAST then
  332.           Instrumentation_Count := Instrumentation_Count + 1;
  333.         else
  334.           Instrumentation_Count := Performance_Instrumentation_Type'FIRST;
  335.         end if;
  336.       end if;
  337.     end Update_Performance_Instrumentation;
  338.  
  339.     -- the two local procedures beLow perform two types of searches
  340.     -- on the array: a binary search (if data is ordered), and
  341.     -- a sequential search (if data is not ordered).
  342.  
  343.     procedure Binary_Search is
  344.       High : Index_Type range Search_Array'FIRST .. Search_Array'LAST
  345.         := Search_Array'LAST;
  346.       Low  : Index_Type range Search_Array'FIRST .. Search_Array'LAST
  347.         := Search_Array'FIRST;
  348.       Curr : Index_Type range Search_Array'FIRST .. Search_Array'LAST
  349.         := Index_Type'VAL((Index_Type'POS(High) + Index_Type'POS(Low)) / 2);
  350.     begin
  351.       while (Search_Array(Curr) /= Component) and (High > Low) loop
  352.         Update_Performance_Instrumentation(Local_Comparisons);
  353.  
  354.         if Search_Array(Curr) < Component then
  355.           if Curr = Search_Array'LAST then
  356.             exit;  -- can't go any further, component not found
  357.           else
  358.             Low := Index_Type'SUCC(Curr);
  359.           end if;
  360.         elsif Curr = Search_Array'FIRST then
  361.           exit;  -- can't go any further, component not found
  362.         else
  363.           High := Index_Type'PRED(Curr);
  364.         end if;
  365.  
  366.         Curr := Index_Type'VAL((Index_Type'POS(High) +
  367.           Index_Type'POS(Low)) / 2);
  368.       end loop;
  369.  
  370.       if Search_Array(Curr) = Component then
  371.         Location_Found := Curr;
  372.         Component_Found := TRUE;
  373.       else
  374.         Location_Found := No_Match_Index;
  375.         Component_Found := FALSE;
  376.       end if;
  377.     end Binary_Search;
  378.  
  379.     -- Sequential_Search will search for the component starting at the
  380.     -- beginning of the array. This search technique is used only if
  381.     -- the user's data is not sorted.
  382.  
  383.     procedure Sequential_Search is
  384.       Index : Index_Type range Search_Array'FIRST .. Search_Array'LAST
  385.         := Search_Array'FIRST;
  386.     begin
  387.       while (Index /= Search_Array'LAST) and
  388.             (Search_Array(Index) /= Component) loop
  389.         Update_Performance_Instrumentation(Local_Comparisons);
  390.         Index := Index_Type'SUCC(Index);
  391.       end loop;
  392.  
  393.       if Search_Array(Index) = Component then
  394.         Location_Found := Index;
  395.         Component_Found := TRUE;
  396.       else
  397.         Location_Found := No_Match_Index;
  398.         Component_Found := FALSE;
  399.       end if;
  400.     end Sequential_Search;
  401.  
  402.   -- body of SEARCH folLows below
  403.  
  404.   begin
  405.     -- check for an unconstrained component type
  406.  
  407.     if not Component_Type'CONSTRAINED then
  408.       raise Component_Type_is_Unconstrained;
  409.     end if;
  410.  
  411.     -- check for null array... a special case.
  412.  
  413.     if Search_Array'LAST < Search_Array'FIRST then
  414.       Location_Found := No_Match_Index;
  415.       Component_Found := FALSE;
  416.     else
  417.       case Order_Strategy is
  418.         when Not_Ordered =>
  419.           Sequential_Search;  -- search an unordered array
  420.         when Ordered =>
  421.           Binary_Search;      -- search an ordered array
  422.       end case;
  423.     end if;
  424.  
  425.     Number_of_Comparisons := Local_Comparisons;
  426.   end SEARCH;
  427.  
  428.   -- the following overloading of SEARCH is used when instrumentation
  429.   -- analysis data are not required.
  430.  
  431.   procedure SEARCH(
  432.     Component                  : in     Component_Type;
  433.     Search_Array               : in     Array_Type;
  434.     Location_Found             :    out Index_Type;
  435.     Component_Found            :    out BOOLEAN;
  436.     Order_Strategy             : in     Data_Order_Type := Not_Ordered;
  437.     No_Match_Index             : in     Index_Type     := Index_Type'LAST) is
  438.  
  439.     Dummy_Comparisons : Performance_Instrumentation_Type;
  440.   begin
  441.     SEARCH(Component,Search_Array,Location_Found,Component_Found,
  442.       Dummy_Comparisons,Order_Strategy,No_Match_Index);
  443.   end SEARCH;
  444.  
  445.   -- the following overloading of SEARCH should be used when only a
  446.   -- boolean result is desired.
  447.  
  448.   function SEARCH(
  449.     Component                  : in Component_Type;
  450.     Search_Array               : in Array_Type;
  451.     Order_Strategy             : in Data_Order_Type := Not_Ordered)
  452.     return BOOLEAN is
  453.     
  454.     Component_Found   : BOOLEAN;
  455.     Dummy_Location    : Index_Type;
  456.     Dummy_Comparisons : Performance_Instrumentation_Type;
  457.   begin
  458.     SEARCH(Component,Search_Array,Dummy_Location,Component_Found,
  459.       Dummy_Comparisons,Order_Strategy);
  460.       
  461.     return Component_Found;
  462.   end SEARCH;
  463.  
  464.   -- the following overloading of SEARCH should be used when only an
  465.   -- index result is desired.
  466.  
  467.   function SEARCH(
  468.     Component                  : in Component_Type;
  469.     Search_Array               : in Array_Type;
  470.     Order_Strategy             : in Data_Order_Type := Not_Ordered;
  471.     No_Match_Index             : in Index_Type      := Index_Type'LAST)
  472.     return Index_Type is
  473.     
  474.     Location_Found    : Index_Type;
  475.     Dummy_Component   : BOOLEAN;
  476.     Dummy_Comparisons : Performance_Instrumentation_Type;
  477.   begin
  478.     SEARCH(Component,Search_Array,Location_Found,Dummy_Component,
  479.       Dummy_Comparisons,Order_Strategy,No_Match_Index);
  480.       
  481.     return Location_Found;
  482.   end SEARCH;
  483. end Search_Utilities;
  484. -------
  485.