     with CharAK;
     with ErroAK; use ErroAK;
     with ConsAK;
     with Text_IO;
     with TypeAK; use TypeAK;

     package body StriAK is
     ---------------------------------------------------------------------------
     ---------------------------------------------------------------------------
     -- Copyright (C) 1994 David Wallace Croft.  All rights reserved.
     -- String manipulation.
     -- Use InStr'last instead of Length(InStr).
     ---------------------------------------------------------------------------
     ---------------------------------------------------------------------------

     procedure Copy (
		 New_Str: out string;
		 Copied_Str: in string;
		 Start: in positive;
		 Stop: in natural := 0) is
     ---------------------------------------------------------------------------
     -- If New_Str <= Copied_Str, New_Str will be a copy of Copied_Str
     -- starting at character Start of Copied_Str and stopping at Stop;
     -- and uncopied character slots in New_Str will be filled with nuls.
     --
     -- If New_Str > Copied_Str, Copied_Str will be copied onto New_Str
     -- at character Start of New_Str and stop at character Stop;
     -- uncopied character slots in New_Str will be left as is.
     --
     -- Note:  Start refers to Copied_Str in the first case and New_Str
     -- in the second case.
     --
     -- If Stop is zero, Stop will be set to Copied_Str'last.
     -- If Stop < Start, New_Str is all Nuls.
     ---------------------------------------------------------------------------
       Temp_Stop: natural := Stop;
     begin
       if Temp_Stop = 0 then
	 Temp_Stop := Copied_Str'last;
       end if;
       if Temp_Stop < Start then
	 New_Str := (others => ASCII.Nul);
	 return;
       end if;
       if New_Str'last <= Copied_Str'last then
	 for index in 1..(Temp_Stop - Start + 1) loop
	   New_Str(index) := Copied_Str(index - 1 + Start);
	 end loop;
	 for index in (Temp_Stop - Start + 2)..New_Str'last loop
	   New_Str(index) := ASCII.Nul;
	 end loop;
       else
	 for index in Copied_Str'range loop
	   New_Str(Start + index - 1) := Copied_Str(index);
	 end loop;
       end if;
     exception
       when others =>
	 ErroAK.Notify("StriAK.Copy");
	 raise;
     end Copy;

     Procedure Define_Valid_Characters(
		 Valid_Characters: out Valid_Characters_Type;
		 Definition_Str: in string := "") is
     ---------------------------------------------------------------------------
     -- Takes in Definition_Str and parses to determine what characters are
     -- allowed for every slot in the answer string.
     --
     -- Valid_Characters must be constrained to the number of characters in
     -- the answer.
     --
     -- ""  == Accepts anything.
     -- " " == Ignored.
     -- "," == Delimits one character from another.  If there are not enough
     --        commas, subsequent character answers will be have the same
     --        limit as the preceding one.  If there are too many commas,
     --        the limits will be ignored.
     -- "-" == All characters accepted between the last character and the next
     --        character (except for space, comma, and dash).
     --
     -- Example:
     --   IN:   Definition_Str is ", ,  ,,,,,,,,A-DFI,-M,YNyn"
     --         and Valid_Characters is contrained to a range of 1..30
     --   OUT:  Valid_Characters(1..10) will accept anything.  Spaces ignored.
     --         Valid_Characters(11) will only accept A,B,C,D, F, or I.
     --         Valid_Characters(12) will accept ASCII.Nul to M.
     --         Valid_Characters(13) will only accept Y, N, y, or n.
     --         Valid_Characters(14..30) will only accept Y, N, y, or n.
     ---------------------------------------------------------------------------
       All_Characters_Valid: constant Character_Is_Valid_Type
	 := (others => true);
       All_Characters_Invalid: constant Character_Is_Valid_Type
	 := (others => false);
       All_Nuls: constant string(Definition_Str'range) := (others => ASCII.Nul);
       Answer_Index: positive range 1..(Valid_Characters'last + 1) := 1;
       Def_Str: string(Definition_Str'range);
       Last_Character: character := ASCII.Nul;
       type Operation_Type is (Not_Set, Ranged);
       Operation: Operation_Type := Not_Set;
       VC: Valid_Characters_Type ( Valid_Characters'range )
	 := ( others => All_Characters_Invalid );
     begin
       if ( Definition_Str = "" ) or ( Definition_Str = All_Nuls ) then
	 Valid_Characters := ( others => All_Characters_Valid );
	   return;
       end if;
       Def_Str := Strip_White_Space ( Definition_Str );
       for index in Def_Str'range loop
	 exit when Answer_Index = VC'last + 1;
	 if Def_Str ( index ) = ',' then
	   if Last_Character = ASCII.Nul then
	     VC ( Answer_Index ) := All_Characters_Valid;
	   end if;
	   Answer_Index := Answer_Index + 1;
	   Last_Character := ASCII.Nul;
	   Operation := Not_Set;
	 elsif Def_Str ( index ) = '-' and then not ( Operation = Ranged ) then
	   Operation := Ranged;
	 else
	   case Operation is
	     when Not_Set =>
	       VC ( Answer_Index ) ( Def_Str ( index ) ) := true;
	     when Ranged =>
	       for Ranged_Index in Last_Character..Def_Str ( index ) loop
		 VC ( Answer_Index ) ( Ranged_Index ) := true;
	       end loop;
	   end case;
	   Last_Character := Def_Str ( index );
	 end if;
       end loop;
       for index in (Answer_Index + 1)..VC'last loop
	 VC(index) := VC(Answer_Index);
       end loop;
       Valid_Characters := VC;
     exception
       when others => ErroAK.Notify("StriAK.Define_Valid_Answers");
		      raise;
     end Define_Valid_Characters;

     function  Filtered (
		 In_Str: in string;
		 Filter_Str: in string ) return string is
     ---------------------------------------------------------------------------
       In_Filter_Str : boolean;
       Position      : integer := In_Str'first;
       Temp          : string ( In_Str'range );
     begin
       for index in In_Str'range loop
	 In_Filter_Str := false;
	 for index2 in Filter_Str'range loop
	   if In_Str ( index ) = Filter_Str ( index2 ) then
	     In_Filter_Str := true;
	     exit;
	   end if;
	 end loop;
	 if not In_Filter_Str then
	   Temp ( Position ) := In_Str ( index );
	   Position := Position + 1;
	 end if;
       end loop;
       for index in Position .. In_Str'last loop
	 Temp ( index ) := ASCII.NUL;
       end loop;
       return Temp;
     end Filtered;

     function  Image
		 ( Item   : in integer;
		   Width  : in Text_IO.Field := integer'width;
		   Base   : in Text_IO.Number_Base := 10;
		   Signed : in boolean   := true;
		   Zeroed : in boolean   := false
		 ) return string is
     ----------------------------------------------------------------------
     begin
       return Image ( long_integer ( Item ), Width, Base, Signed, Zeroed );
     end Image;

     function  Image (
       Item   : in long_integer;
       Width  : in Text_IO.Field := long_integer'width;
       Base   : in Text_IO.Number_Base := 10;
       Signed : in boolean   := true;
       Zeroed : in boolean   := false )
       return string is
     ----------------------------------------------------------------------
       W       : positive := long_integer'image ( Item )'last;
       Largest : positive := Width;
     begin
       if W > Width then
	 Largest := W;
       end if;
       declare
	 Temp : string ( 1..Largest ) := ( others => ' ' );
       begin
	 if Signed then
	   Temp ( ( Temp'last - W + 1 )..Temp'last )
	     := long_integer'image ( Item );
	   if Item >= 0 then
	     Temp ( 1 ) := '+';
	   else
	     Temp ( Temp'last - W + 1 ) := ' ';
	     Temp ( 1 ) := '-';
	   end if;
	 else
	   Temp ( ( Temp'last - W + 2 )..Temp'last )
	     := long_integer'image ( Item ) ( 2..W );
	 end if;
	 if Zeroed then
	   for index in Temp'range loop
	     if Temp ( index ) = ' ' then
	       Temp ( index ) := '0';
	     end if;
	   end loop;
	 end if;
	 if Largest > Width then
	   return Temp ( ( Temp'last - Width + 1 )..Temp'last );
	 else
	   return Temp;
	 end if;
       end;
     exception
       when others =>
	 ErroAK.Notify ( "StriAK.Image ( long_integer )" );
	 raise;
     end Image;

     function Is_Long_Integer (
       Pos_Str  : in string;
       Min      : in long_integer := long_integer'first;
       Max      : in long_integer := long_integer'last )
       return boolean is
     ----------------------------------------------------------------------
     -- change Pos_Str to Long_Int_Str.
     ----------------------------------------------------------------------
       Temp : boolean := true;
       Test : long_integer;
     begin
       begin
	 Test := long_integer'value ( Pos_Str );
	 Temp := ( Min <= Test ) and ( Test <= Max );
	 for index in Pos_Str'range loop
	   Temp := Temp and
	     ( Locate ( Pos_Str ( index ), "+-0123456789" ) /= 0 );
	 end loop;
       exception
	 when others =>
	   Temp := false;
       end;
       return Temp;
     end Is_Long_Integer;

     function Locate (
       Search_Char    : in character;
       Search_Str     : in string;
       Start_Position : in positive := 1 )
       return natural is
     ----------------------------------------------------------------------
     -- Returns 0 if not found from Start_Pos to last.
     ----------------------------------------------------------------------
       Char_Found : boolean := false;
       Str_Index  : natural;
     begin
       for index in Start_Position..Search_Str'last loop
	 Str_Index := index;
	 Char_Found := Search_Str ( Str_Index ) = Search_Char;
	 exit when Char_Found;
       end loop;
       if Char_Found then
	 return Str_Index;
       else
	 return 0;
       end if;
     exception
       when others => ErroAK.Notify("StriAK.Locate ( Search_Char )");
		      raise;
     end Locate;

     function  Locate (
       Hunted_Str     : in string;
       Search_Str     : in string;
       Start_Position : in positive := 1 ) return positive is
     ---------------------------------------------------------------------------
     -- Returns ( Search_Str'last + 1 ) if not found from Start_Position to end.
     ---------------------------------------------------------------------------
       Str_Found: boolean := false;
       Str_Index: positive;
     begin
       for index in Start_Position..Search_Str'last loop
	 Str_Index := index;
	 Str_Found := Search_Str ( index..( index - 1 + Hunted_Str'length ) )
	   = Hunted_Str;
	 exit when Str_Found;
       end loop;
       if Str_Found then
	 return Str_Index;
       else
	 return Search_Str'last + 1;
       end if;
     exception
       when others =>
	 ErroAK.Notify ( "StriAK.Locate ( Hunted_Str )" );
	 raise;
     end Locate;

     function  Merge (
       Dominant  : in string;
       Recessive : in string )
       return string is
     ---------------------------------------------------------------------------
       Min,
       Max : natural;
     begin
       if Recessive'first < Dominant'first then
	 Min := Recessive'first;
       else
	 Min := Dominant'first;
       end if;
       if Recessive'last > Dominant'last then
	 Max := Recessive'last;
       else
	 Max := Dominant'last;
       end if;
       declare
	 Temp : string ( Min..Max );
       begin
	 Temp ( Recessive'range ) := Recessive;
	 for Char in Dominant'range loop
	   if Dominant ( Char ) /= ASCII.Nul then
	     Temp ( Char ) := Dominant ( Char );
	   end if;
	 end loop;
	 return Temp;
       end;
     exception
       when others =>
	 Notify ( "StriAK.Merge" );
	 raise;
     end Merge;

     procedure MultiChar(InChar: in character; InStr: in out string) is
     ---------------------------------------------------------------------------
     begin
       for index in InStr'range loop
	 InStr(index) := InChar;
       end loop;
     end MultiChar;

     procedure Parse_Between(
		 Parsed_Str: out string;
		 Char: in character;
		 After_Char_Num: in natural;
		 In_Str: in string) is
     ---------------------------------------------------------------------------
       Start_Copy: positive;
       Stop_Copy: natural := 0;
     begin
       for index in 0..After_Char_Num loop
	 Start_Copy := Stop_Copy + 1;
	 Stop_Copy := Locate( Char, In_Str, Start_Position => Start_Copy);
	 if Stop_Copy = 0 then
	   Stop_Copy := In_Str'last + 1;
	 end if;
       end loop;
       Copy( Parsed_Str, In_Str, Start_Copy, Stop_Copy - 1);
     exception
       when others =>
	 ErroAK.Notify("StriAK.Parse_Between");
	 raise;
     end Parse_Between;

     procedure Parse_Range(
		 InStr: in string;
		 Position: in positive;
		 Lo_Num: out natural;
		 Hi_Num: out natural;
		 New_Position: out positive) is
     ---------------------------------------------------------------------------
     -- Hi_Num will always be >= Lo_Num.
     ---------------------------------------------------------------------------
       On_Lo_Num: boolean := false;
       On_Hi_Num: boolean := false;
       Temp_Lo_Num: natural := 0;
       Temp_Num: natural := 0;
     begin
       Temp_Lo_Num := 0;
       Hi_Num := 0;
       for index in Position..InStr'last loop
	 New_Position := index;
	 if CharAK.Is_Natural(InStr(index)) then
	   Temp_Num := Temp_Num*10
	     + natural'value(CharAK.Char_To_Strg(InStr(index)));
	   if not(On_Lo_Num) and not(On_Hi_Num) then
	     On_Lo_Num := true;
	   end if;
	 else
	   if On_Hi_Num then
	     if Temp_Num < Temp_Lo_Num then
	       Hi_Num := Temp_Lo_Num;
	     else
	       Hi_Num := Temp_Num;
	     end if;
	     exit;
	   end if;
	   if On_Lo_Num then
	     Temp_Lo_Num := Temp_Num;
	     Temp_Num := 0;
	     On_Lo_Num := false;
	     On_Hi_Num := true;
	   end if;
	 end if;
       end loop;
       Lo_Num := Temp_Lo_Num;
     end Parse_Range;

     function Shift (
       Shifted : string;
       Left    : integer := 1 )
       return string is
     ----------------------------------------------------------------------
       Temp : string ( Shifted'range );
     begin
       for Char in Shifted'range loop
	 if ( Char + Left ) in Shifted'range then
	   Temp ( Char ) := Shifted ( Char + Left );
	 else
	   Temp ( Char ) := ASCII.Nul;
	 end if;
       end loop;
       return Temp;
     end Shift;

     procedure Show_Valid_Characters(
		 Valid_Characters: in Valid_Characters_Type) is
     ---------------------------------------------------------------------------
       Index2: character range character'first..character'last;
     begin
       for index in Valid_Characters'range loop
	 Text_IO.Put_Line ( "Answer " & positive'image ( index ) );
	 for Index2 in character'first..character'last loop
	   Text_IO.Put_Line ( "  " & Index2
	     & boolean'image ( Valid_Characters ( index ) ( index2 ) ) );
	 end loop;
       end loop;
     end Show_Valid_Characters;

     procedure Split(
		 Left_Side,
		 Right_Side: out string;
		 Split_Str: in string;
		 Position: in natural) is
     ----------------------------------------------------------------------
     begin
       if Position = 0 then
	 Left_Side := (others => ASCII.Nul);
	 Right_Side := Split_Str;
	 return;
       end if;
       Copy( Left_Side, Split_Str, 1, Position - 1);
       Copy( Right_Side, Split_Str, Position + 1);
     exception
       when others =>
	 ErroAK.Notify("StriAK.Split(natural)");
	 raise;
     end Split;

     procedure Split(
		 Left_Side,
		 Right_Side: out string;
		 Split_Str: in string;
		 Split_Char: in character;
		 On_Count: in positive := 1) is
     ---------------------------------------------------------------------------
       Position: natural := 0;
     begin
       for index in 1..On_Count loop
	 Position := Locate( Split_Char, Split_Str, Position + 1);
       end loop;
       if Position = 0 then
	 Position := Split_Str'last + 1;
       end if;
       Split( Left_Side, Right_Side, Split_Str, Position);
     exception
       when others =>
	 ErroAK.Notify("StriAK.Split(character)");
	 raise;
     end Split;

     function Strip_White_Space (
       InStr: in string ) return string is
     ----------------------------------------------------------------------
     -- Strips out spaces, tabs, and ASCII.Nul.
     ----------------------------------------------------------------------
       TempIndex: positive := 1;
       TempStr: string(InStr'range);
     begin
       for index in InStr'range loop
	 if Locate ( InStr ( index ), ' ' & ASCII.HT & ASCII.Nul ) = 0 then
	   TempStr ( TempIndex ) := InStr ( Index );
	   TempIndex := TempIndex + 1;
	 end if;
       end loop;
       for index in TempIndex..InStr'last loop
	 TempStr(index) := ' ';
       end loop;
       return TempStr;
     end Strip_White_Space;

     function Trim_White_Space (
       Str : in string )
       return string is
     ----------------------------------------------------------------------
     -- Trims off space, ASCII.HT, ASCII.Nul from the right of the string.
     -- The returned string will have a shorter length.
     ----------------------------------------------------------------------
     begin
       for Last_Char in reverse Str'range loop
	 case Str ( Last_Char ) is
	   when ' '| ASCII.Nul | ASCII.HT => null;
	   when others => return Str ( Str'first..Last_Char );
	 end case;
       end loop;
       return ""; -- in case everything was stripped
     end Trim_White_Space;

     function Upcase(InStr: in string) return string is
     ---------------------------------------------------------------------------
       OutStr: string(InStr'range) := InStr;
     begin
       for index in InStr'range loop
	 OutStr(index) := CharAK.Upcase(InStr(index));
       end loop;
       return OutStr;
     end Upcase;

     procedure Value (
		 Status    :    out Status_Type;
		 Value_Int :    out integer;
		 Str_In    : in     string ) is
     ---------------------------------------------------------------------------
     begin
       Status := OK;
       Value_Int := integer'value ( Str_In );
     exception
       when Constraint_Error => Status := Error_Constraint;
       when others =>
	 raise;
     end Value;

     function Value (
       Str_Float : string )
       return float is
     ----------------------------------------------------------------------
       Temp : float;
       Last : natural;
       package Float_IO is new Text_IO.Float_IO ( float );
       use Float_IO;
     begin
       Get ( Str_Float, Temp, Last );
       return Temp;
     end Value;

--   function Value (
--     Str_Float : string )
--     return float is
--   ----------------------------------------------------------------------
--   -- Description :  Converts a string to a float.
--   --                Limited to 4 digits after the decimal point.
--   --                "1.0000" ==> 1.0; "1.00000" ==> Error
--   ----------------------------------------------------------------------
--     Exponent     : integer := 0;
--     Is_Integer   : boolean := false;
--     Is_Positive  : boolean := true;
--     Place        : integer := 0;
--     Position     : integer range 1..Str_Float'length := 2;
--     Status_Value : Status_Type;
--     Temp_Float   : float   := 0.0;
--     Temp_Str     : string ( 1..Trim ( Filter ( Str_Float, "_,"
--                      & White_Space_Default ) )'last )
--                    := Trim ( Filter ( Str_Float, "_,"
--                      & White_Space_Default ) );
--   begin
--     if Temp_Str ( 1 ) = '-' then
--       Is_Positive := false;
--     elsif Temp_Str ( 1 ) = '+' then
--       Is_Positive := true;
--     else
--       Is_Positive := true;
--       Position := 1;
--     end if;
--     for index in Position..Temp_Str'last loop
--       Position := index;
--       exit when Temp_Str ( index ) = '.';
--       if Temp_Str ( index ) not in ( '0'..'9' ) then
--         raise Constraint_Error;
--       end if;
--       Temp_Float := Temp_Float * 10.0
--         + float ( integer'value ( Temp_Str ( index..index ) ) );
--     end loop;
--     for index in ( Position + 1 ).. Temp_Str'last loop
--       Position := index;
--       Place := Place + 1;
--       exit when ( Temp_Str ( index ) = 'E' ) or ( Temp_Str ( index ) = 'e' );
--       exit when ( Temp_Str ( index ) = ASCII.NUL )
--         or ( Temp_Str ( index ) = ' ' );
--       if Temp_Str ( index ) not in ( '0'..'9' ) then
--         raise Constraint_Error;
--       end if;
--       -- Here is where the 4-digits after the decimal limitation is.
--       -- 10 ** 5 = 100,000 > 32k max integer
--       -- Cannot change ( 10 ** Place ) to long_integer as long_integers
--       -- cannot be converted to float.
--       Temp_Float := Temp_Float
--         + float ( integer'value ( Temp_Str ( index..index ) ) )
--           / float ( 10 ** Place );
--     end loop;
--     if Temp_Str ( Position ) = 'E' or Temp_Str ( Position ) = 'e' then
--       Value ( Status_Value, Exponent,
--         Temp_Str ( Position + 1 .. Temp_Str'last ) );
--       if Status_Value /= OK then
--         raise Constraint_Error;
--       end if;
--       Temp_Float := Temp_Float * ( 10.0 ** Exponent );
--     end if;
--     if Is_Positive then
--       return +Temp_Float;
--     else
--       return -Temp_Float;
--     end if;
--   end Value;

     ---------------------------------------------------------------------------
     ---------------------------------------------------------------------------
     end StriAK;