------------------------------------------------------------------------------
--                                                                          --
--                           POLYORB COMPONENTS                             --
--                                                                          --
--            P O L Y O R B . S E Q U E N C E S . B O U N D E D             --
--                                                                          --
--                                 B o d y                                  --
--                                                                          --
--         Copyright (C) 2002-2006, Free Software Foundation, Inc.          --
--                                                                          --
-- PolyORB is free software; you  can  redistribute  it and/or modify it    --
-- under terms of the  GNU General Public License as published by the  Free --
-- Software Foundation;  either version 2,  or (at your option)  any  later --
-- version. PolyORB is distributed  in the hope that it will be  useful,    --
-- but WITHOUT ANY WARRANTY;  without even the implied warranty of MERCHAN- --
-- TABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public --
-- License  for more details.  You should have received  a copy of the GNU  --
-- General Public License distributed with PolyORB; see file COPYING. If    --
-- not, write to the Free Software Foundation, 51 Franklin Street, Fifth    --
-- Floor, Boston, MA 02111-1301, USA.                                       --
--                                                                          --
--
--
--
--
--
--
--
--                  PolyORB is maintained by AdaCore                        --
--                     (email: sales@adacore.com)                           --
--                                                                          --
------------------------------------------------------------------------------

with Ada.Unchecked_Deallocation;

package body PolyORB.Sequences.Bounded is

   ------------
   -- Length --
   ------------

   function Length (Source : Sequence) return Length_Range is
   begin
      return Source.Length;
   end Length;

   ----------
   -- Free --
   ----------

   procedure Free (X : in out Element_Array_Access) is
      procedure Deallocate is
        new Ada.Unchecked_Deallocation
        (Element_Array, Element_Array_Access);
   begin
      Deallocate (X);
   end Free;

   -----------------
   -- To_Sequence --
   -----------------

   function To_Sequence
     (Source : Element_Array;
      Drop   : Truncation := Error)
      return Sequence
   is
      Source_Length : constant Natural := Source'Length;
      Result        : Sequence;
   begin

      if Source_Length <= Max_Length then

         Result.Length := Source_Length;
         Result.Content (1 .. Source_Length) := Source;

      else

         case Drop is

            when Left =>
               Result.Length := Max_Length;
               Result.Content (1 .. Max_Length) :=
                 Source (Source'Last - (Max_Length - 1) .. Source'Last);

            when Right =>
               Result.Length := Max_Length;
               Result.Content (1 .. Max_Length) :=
                 Source (Source'First .. Source'First + Max_Length - 1);

            when Error =>
               raise Length_Error;

         end case;

      end if;

      return Result;
   end To_Sequence;

   -----------------
   -- To_Sequence --
   -----------------

   function To_Sequence (Length : Length_Range) return Sequence is

      Result : Sequence;

   begin

      Result.Length := Length;
      Result.Content (1 .. Length) := Null_Element_Array;
      return Result;

   end To_Sequence;

   ---------
   -- Set --
   ---------

   procedure Set
     (Item   : in out Sequence;
      Source : Element_Array;
      Drop   : Truncation := Error) is
   begin
      Item := To_Sequence (Source, Drop);
   end Set;

   ----------------------
   -- To_Element_Array --
   ----------------------

   function To_Element_Array (Source : Sequence) return Element_Array is
   begin

      return Source.Content (1 .. Source.Length);

   end To_Element_Array;

   ------------
   -- Append --
   ------------

   function Append
     (Left, Right : Sequence;
      Drop        : Truncation := Error)
      return Sequence
   is

      Left_Length : constant Length_Range := Left.Length;
      Right_Length : constant Length_Range := Right.Length;
      Total_Length : constant Natural := Left_Length + Right_Length;
      Result : Sequence;

   begin

      if Total_Length <= Max_Length then

         --  Resulting sequence will still fit

         Result.Length := Total_Length;
         Result.Content (1 .. Left_Length) :=
           Left.Content (1 .. Left_Length);
         Result.Content (Left_Length + 1 .. Total_Length) :=
           Right.Content (1 .. Right_Length);

      else

         --  Resulting sequence is too large

         Result.Length := Max_Length;
         case Drop is

            when PolyORB.Sequences.Left =>
               if Right_Length < Max_Length then

                  Result.Content (1 .. Max_Length - Right_Length) :=
                    Left.Content (Left_Length -
                                  (Max_Length - Right_Length) + 1 ..
                                  Left_Length);
                  Result.Content
                    (Max_Length - Right_Length + 1 .. Max_Length) :=
                    Right.Content (1 .. Right_Length);

               else

                  Result.Content := Right.Content;

               end if;

            when PolyORB.Sequences.Right =>
               if Left_Length < Max_Length then

                  Result.Content (1 .. Left_Length) :=
                    Left.Content (1 .. Left_Length);
                  Result.Content (Left_Length + 1 .. Max_Length) :=
                    Right.Content (1 .. Max_Length - Left_Length);

               else

                  Result.Content := Left.Content;

               end if;

            when PolyORB.Sequences.Error =>
               raise Length_Error;

         end case;

      end if;
      return Result;

   end Append;

   ------------
   -- Append --
   ------------

   function Append
     (Left  : Sequence;
      Right : Element_Array;
      Drop  : Truncation := Error)
      return Sequence
   is

      Left_Length : constant Length_Range := Left.Length;
      Right_Length : constant Length_Range := Right'Length;
      Total_Length : constant Natural := Left_Length + Right_Length;
      Result : Sequence;

   begin

      if Total_Length <= Max_Length then

         --  Resulting sequence will still fit

         Result.Length := Total_Length;
         Result.Content (1 .. Left_Length) :=
           Left.Content (1 .. Left_Length);
         Result.Content (Left_Length + 1 .. Total_Length) := Right;

      else

         --  Resulting sequence is too large

         Result.Length := Max_Length;
         case Drop is

            when PolyORB.Sequences.Left =>
               if Right_Length < Max_Length then

                  Result.Content (1 .. Max_Length - Right_Length) :=
                    Left.Content (Left_Length -
                                  (Max_Length - Right_Length) + 1 ..
                                  Left_Length);
                  Result.Content
                    (Max_Length - Right_Length + 1 .. Max_Length) :=
                    Right;

               else

                  Result.Content (1 .. Max_Length) :=
                    Right (Right'Last - (Max_Length - 1) .. Right'Last);

               end if;

            when PolyORB.Sequences.Right =>
               if Left_Length < Max_Length then

                  Result.Content (1 .. Left_Length) :=
                    Left.Content (1 .. Left_Length);
                  Result.Content (Left_Length + 1 .. Max_Length) :=
                    Right (Right'First .. Right'First - 1 +
                           Max_Length - Left_Length);

               else

                  Result.Content := Left.Content;

               end if;

            when PolyORB.Sequences.Error =>
               raise Length_Error;

         end case;

      end if;
      return Result;

   end Append;

   ------------
   -- Append --
   ------------

   function Append
     (Left  : Element_Array;
      Right : Sequence;
      Drop  : Truncation := Error)
      return Sequence
   is

      Left_Length : constant Length_Range := Left'Length;
      Right_Length : constant Length_Range := Right.Length;
      Total_Length : constant Natural := Left_Length + Right_Length;
      Result : Sequence;

   begin

      if Total_Length <= Max_Length then

         --  Resulting sequence will still fit

         Result.Length := Total_Length;
         Result.Content (1 .. Left_Length) := Left;
         Result.Content (Left_Length + 1 .. Total_Length) :=
           Right.Content (1 .. Right_Length);

      else

         --  Resulting sequence is too large

         Result.Length := Max_Length;
         case Drop is

            when PolyORB.Sequences.Left =>
               if Right_Length < Max_Length then

                  Result.Content (1 .. Max_Length - Right_Length) :=
                    Left (Left'Last - (Max_Length - Right_Length) + 1 ..
                          Left'Last);
                  Result.Content
                    (Max_Length - Right_Length + 1 .. Max_Length) :=
                    Right.Content (1 .. Right_Length);

               else

                  Result.Content (1 .. Max_Length) :=
                    Right.Content (Right_Length - Max_Length + 1 ..
                                   Right_Length);

               end if;

            when PolyORB.Sequences.Right =>
               if Left_Length < Max_Length then

                  Result.Content (1 .. Left_Length) := Left;
                  Result.Content (Left_Length + 1 .. Max_Length) :=
                    Right.Content (1 .. Max_Length - Left_Length);

               else

                  Result.Content (1 .. Max_Length) :=
                    Left (Left'First .. Left'First + Max_Length - 1);

               end if;

            when PolyORB.Sequences.Error =>
               raise Length_Error;

         end case;

      end if;

      return Result;
   end Append;

   ------------
   -- Append --
   ------------

   function Append
     (Left  : Sequence;
      Right : Element;
      Drop  : Truncation := Error)
      return Sequence
   is

      Left_Length : constant Length_Range := Left.Length;
      Result : Sequence;

   begin

      if Left_Length < Max_Length then

         --  Resulting sequence will still fit

         Result.Length := Left_Length + 1;
         Result.Content (1 .. Left_Length) :=
           Left.Content (1 .. Left_Length);
         Result.Content (Left_Length + 1) := Right;
         return Result;

      else

         --  Resulting sequence is too large

         case Drop is

            when PolyORB.Sequences.Left =>
               Result.Length := Max_Length;
               Result.Content (1 .. Max_Length - 1) :=
                 Left.Content (2 .. Max_Length);
               Result.Content (Max_Length) := Right;
               return Result;

            when PolyORB.Sequences.Right =>
               return Left;

            when PolyORB.Sequences.Error =>
               raise Length_Error;

         end case;

      end if;

   end Append;

   ------------
   -- Append --
   ------------

   function Append
     (Left  : Element;
      Right : Sequence;
      Drop  : Truncation := Error)
      return Sequence
   is

      Right_Length : constant Length_Range := Right.Length;
      Result : Sequence;

   begin

      if Right_Length < Max_Length then

         --  Resulting sequence will still fit

         Result.Length := Right_Length + 1;
         Result.Content (1) := Left;
         Result.Content (2 .. Right_Length + 1) :=
           Right.Content (1 .. Right_Length);
         return Result;

      else

         --  Resulting sequence is too large

         case Drop is

            when PolyORB.Sequences.Left =>
               return Right;

            when PolyORB.Sequences.Right =>
               Result.Length := Max_Length;
               Result.Content (1) := Left;
               Result.Content (2 .. Max_Length) :=
                 Right.Content (1 .. Max_Length - 1);
               return Result;

            when PolyORB.Sequences.Error =>
               raise Length_Error;

         end case;

      end if;

   end Append;

   ------------
   -- Append --
   ------------

   procedure Append
     (Source   : in out Sequence;
      New_Item : Sequence;
      Drop     : Truncation := Error)
   is

      Source_Length : constant Length_Range := Source.Length;
      New_Length : constant Length_Range := New_Item.Length;
      Total_Length : constant Natural := Source_Length + New_Length;

   begin

      if Total_Length <= Max_Length then

         --  Resulting sequence will still fit

         Source.Length := Total_Length;
         Source.Content (Source_Length + 1 .. Total_Length) :=
           New_Item.Content (1 .. New_Length);

      else

         --  Resulting sequence is too large

         Source.Length := Max_Length;
         case Drop is

            when PolyORB.Sequences.Left =>
               if New_Length < Max_Length then

                  Source.Content (1 .. Max_Length - New_Length) :=
                    Source.Content (Source_Length -
                                    (Max_Length - New_Length) + 1 ..
                                    Source_Length);
                  Source.Content
                    (Max_Length - New_Length + 1 .. Max_Length) :=
                    New_Item.Content (1 .. New_Length);

               else -- New_Length = Max_Length

                  Source.Content := New_Item.Content;

               end if;

            when PolyORB.Sequences.Right =>
               if Source_Length < Max_Length then

                  Source.Content (Source_Length + 1 .. Max_Length) :=
                    New_Item.Content (1 .. Max_Length - Source_Length);

               else -- Source_Length = Max_Length

                  null; -- do nothing

               end if;

            when PolyORB.Sequences.Error =>
               raise Length_Error;

         end case;

      end if;

   end Append;

   ------------
   -- Append --
   ------------

   procedure Append
     (Source   : in out Sequence;
      New_Item : Element_Array;
      Drop     : Truncation := Error)
   is

      Source_Length : constant Length_Range := Source.Length;
      New_Length : constant Length_Range := New_Item'Length;
      Total_Length : constant Natural := Source_Length + New_Length;

   begin

      if Total_Length <= Max_Length then

         --  Resulting sequence will still fit

         Source.Length := Total_Length;
         Source.Content (Source_Length + 1 .. Total_Length) := New_Item;

      else

         --  Resulting sequence is too large

         Source.Length := Max_Length;
         case Drop is

            when PolyORB.Sequences.Left =>
               if New_Length < Max_Length then

                  Source.Content (1 .. Max_Length - New_Length) :=
                    Source.Content (Source_Length -
                                    (Max_Length - New_Length) + 1 ..
                                    Source_Length);
                  Source.Content
                    (Max_Length - New_Length + 1 .. Max_Length) :=
                    New_Item;

               else

                  Source.Content (1 .. Max_Length) :=
                    New_Item (New_Item'Last - Max_Length + 1 ..
                              New_Item'Last);

               end if;

            when PolyORB.Sequences.Right =>
               if Source_Length < Max_Length then

                  Source.Content (Source_Length + 1 .. Max_Length) :=
                    New_Item (New_Item'First ..
                              New_Item'First + Max_Length -
                              Source_Length - 1);

               end if;

            when PolyORB.Sequences.Error =>
               raise Length_Error;

         end case;

      end if;

   end Append;

   ------------
   -- Append --
   ------------

   procedure Append
     (Source   : in out Sequence;
      New_Item : Element;
      Drop     : Truncation := Error) is

      Source_Length : constant Length_Range := Source.Length;

   begin

      if Source_Length < Max_Length then

         --  Resulting sequence will still fit

         Source.Length := Source_Length + 1;
         Source.Content (Source_Length + 1) := New_Item;

      else

         --  Resulting sequence is too large

         case Drop is

            when PolyORB.Sequences.Left =>
               Source.Content (1 .. Max_Length - 1) :=
                 Source.Content (2 .. Max_Length);
               Source.Content (Max_Length) := New_Item;

            when PolyORB.Sequences.Right =>
               null; -- do nothing

            when PolyORB.Sequences.Error =>
               raise Length_Error;

         end case;

      end if;

   end Append;

   ---------
   -- "&" --
   ---------

   function "&" (Left, Right : Sequence) return Sequence is

      Left_Length : constant Length_Range := Left.Length;
      Right_Length : constant Length_Range := Right.Length;
      Total_Length : constant Natural := Left_Length + Right_Length;
      Result : Sequence;

   begin

      if Total_Length <= Max_Length then

         Result.Length := Total_Length;
         Result.Content (1 .. Left_Length) :=
           Left.Content (1 .. Left_Length);
         Result.Content (Left_Length + 1 .. Total_Length) :=
           Right.Content (1 .. Right_Length);

      else

         raise Length_Error;

      end if;
      return Result;

   end "&";

   ---------
   -- "&" --
   ---------

   function "&" (Left : Sequence; Right : Element_Array)
                 return Sequence is

      Left_Length : constant Length_Range := Left.Length;
      Total_Length : constant Natural := Left_Length + Right'Length;
      Result : Sequence;

   begin

      if Total_Length <= Max_Length then

         Result.Length := Total_Length;
         Result.Content (1 .. Left_Length) :=
           Left.Content (1 .. Left_Length);
         Result.Content (Left_Length + 1 .. Total_Length) := Right;

      else

         raise Length_Error;

      end if;

      return Result;

   end "&";

   ---------
   -- "&" --
   ---------

   function "&" (Left : Element_Array; Right : Sequence)
                 return Sequence is

      Left_Length : constant Length_Range := Left'Length;
      Right_Length : constant Length_Range := Right.Length;
      Total_Length : constant Natural := Left_Length + Right_Length;
      Result : Sequence;

   begin

      if Total_Length <= Max_Length then

         Result.Length := Total_Length;
         Result.Content (1 .. Left_Length) := Left;
         Result.Content (Left_Length + 1 .. Total_Length) :=
           Right.Content (1 .. Right_Length);

      else

         raise Length_Error;

      end if;

      return Result;

   end "&";

   ---------
   -- "&" --
   ---------

   function "&" (Left : Sequence; Right : Element) return Sequence is

      Left_Length : constant Length_Range := Left.Length;
      Result : Sequence;

   begin

      if Left_Length < Max_Length then

         Result.Length := Left_Length + 1;
         Result.Content (1 .. Left_Length) :=
           Left.Content (1 .. Left_Length);
         Result.Content (Result.Length) := Right;

      else

         raise Length_Error;

      end if;
      return Result;

   end "&";

   ---------
   -- "&" --
   ---------

   function "&" (Left : Element; Right : Sequence) return Sequence is

      Right_Length : constant Length_Range := Right.Length;
      Result : Sequence;

   begin

      if Right_Length < Max_Length then

         Result.Length := Right_Length + 1;
         Result.Content (1) := Left;
         Result.Content (2 .. Result.Length) :=
           Right.Content (1 .. Right_Length);

      else

         raise Length_Error;

      end if;
      return Result;

   end "&";

   ----------------
   -- Element_Of --
   ----------------

   function Element_Of
     (Source : Sequence; Index : Positive) return Element is
   begin

      if Index <= Source.Length then

         return Source.Content (Index);

      else

         raise Index_Error;

      end if;

   end Element_Of;

   ---------------------
   -- Replace_Element --
   ---------------------

   procedure Replace_Element
     (Source : in out Sequence;
      Index  : Positive;
      By     : Element)
   is
   begin

      if Index <= Source.Length then

         Source.Content (Index) := By;

      else

         raise Index_Error;

      end if;

   end Replace_Element;

   -----------
   -- Slice --
   -----------

   function Slice
     (Source : Sequence;
      Low    : Positive;
      High   : Natural)
      return Element_Array
   is
   begin

      if Low <= Source.Length and then High <= Source.Length then

         return Source.Content (Low .. High);

      else

         raise Index_Error;

      end if;

   end Slice;

   ---------
   -- "=" --
   ---------

   function "=" (Left, Right : Sequence) return Boolean is
   begin

      return Left.Length = Right.Length and then
        Left.Content (1 .. Left.Length) =
        Right.Content (1 .. Right.Length);

   end "=";

   ---------
   -- "=" --
   ---------

   function "=" (Left : Sequence; Right : Element_Array)
                 return Boolean is
   begin

      return Left.Length = Right'Length and then
        Left.Content (1 .. Left.Length) = Right;

   end "=";

   ---------
   -- "=" --
   ---------

   function "=" (Left : Element_Array; Right : Sequence)
                 return Boolean is
   begin

      return Left'Length = Right.Length and then
        Left = Right.Content (1 .. Right.Length);

   end "=";

   -----------
   -- Index --
   -----------

   function Index
     (Source  : Sequence;
      Pattern : Element_Array;
      Going   : Direction := Forward)
      return Natural
   is
   begin

      if Pattern = Null_Element_Array then

         raise Pattern_Error;

      end if;

      case Going is

         when Forward =>
            for J in 1 .. Source.Length - Pattern'Length + 1 loop

               if Pattern = Source.Content (J ..
                                            J + Pattern'Length - 1) then
                  return J;
               end if;

            end loop;

         when Backward =>
            for J in reverse 1 .. Source.Length - Pattern'Length + 1 loop

               if Pattern = Source.Content (J ..
                                            J + Pattern'Length - 1) then
                  return J;
               end if;

            end loop;

      end case;

      --  No match found yet
      return 0;

   end Index;

   -----------
   -- Count --
   -----------

   function Count
     (Source  : Sequence;
      Pattern : Element_Array)
      return Natural
   is

      N : Natural;
      J : Natural;

   begin

      if Pattern = Null_Element_Array then
         raise Pattern_Error;
      end if;

      N := 0;
      J := 1;

      while J <= Source.Length - (Pattern'Length - 1) loop

         if Source.Content (J .. J + Pattern'Length - 1) = Pattern then
            N := N + 1;
            J := J + Pattern'Length;
         else
            J := J + 1;
         end if;

      end loop;
      return N;

   end Count;

   -------------------
   -- Replace_Slice --
   -------------------

   function Replace_Slice
     (Source : Sequence;
      Low    : Positive;
      High   : Natural;
      By     : Element_Array;
      Drop   : Truncation := Error)
      return Sequence
   is

      Source_Length : constant Length_Range := Source.Length;
      By_Length     : constant Integer := By'Length;
      Total_Length  : constant Integer :=
        Source_Length + By_Length + Low - High - 1;
      Drop_Length   : constant Integer := Total_Length - Max_Length;
      Result : Sequence;

   begin

      if Low > Source_Length + 1 then

         raise Index_Error;

      elsif High < Low then

         return Insert (Source => Source,
                        Before => Low,
                        New_Item => By,
                        Drop => Drop);

      elsif Total_Length > Max_Length then

         Result.Length := Max_Length;
         case Drop is

            when PolyORB.Sequences.Left =>
               Result.Content (Max_Length - (Source_Length - High) + 1 ..
                               Max_Length) :=
                 Source.Content (High + 1 .. Source_Length);
               if Drop_Length >= Low - 1 then

                  Result.Content
                    (1 .. Max_Length - (Source_Length - High)) :=
                    By (By'Last -
                        (Max_Length - (Source_Length - High)) + 1 ..
                        By'Last);

               else

                  Result.Content
                    (Low - Drop_Length ..
                     Max_Length - (Source_Length - High)) := By;
                  Result.Content (1 .. Low - (Drop_Length + 1)) :=
                    Source.Content (Drop_Length + 1 .. Low - 1);

               end if;

            when PolyORB.Sequences.Right =>
               Result.Content (1 .. Low - 1) :=
                 Source.Content (1 .. Low - 1);
               if Drop_Length > Source_Length - High then

                  Result.Content (Low .. Max_Length) :=
                    By (By'First .. By'First + Max_Length - Low);

               else

                  Result.Content (Low .. Low + By_Length - 1) := By;
                  Result.Content (Low + By_Length .. Max_Length) :=
                    Source.Content (High + 1 ..
                                    Source_Length - Drop_Length);

               end if;

            when PolyORB.Sequences.Error =>
               raise Length_Error;

         end case;

      else

         Result.Length := Total_Length;
         Result.Content (1 .. Low - 1) := Source.Content (1 .. Low - 1);
         Result.Content (Low .. Low + By_Length - 1) := By;
         Result.Content (Low + By_Length .. Total_Length) :=
           Source.Content (High + 1 .. Source_Length);

      end if;

      return Result;

   end Replace_Slice;

   -------------------
   -- Replace_Slice --
   -------------------

   procedure Replace_Slice
     (Source : in out Sequence;
      Low    : Positive;
      High   : Natural;
      By     : Element_Array;
      Drop   : Truncation := Error)
   is
      Source_Length : constant Length_Range := Source.Length;
      By_Length     : constant Integer := By'Length;
      Total_Length  : constant Integer :=
        Source_Length + By_Length + Low - High - 1;
      Drop_Length   : constant Integer := Total_Length - Max_Length;
   begin

      if Low > Source_Length + 1 then

         raise Index_Error;

      elsif High < Low then

         Insert (Source => Source,
                 Before => Low,
                 New_Item => By,
                 Drop => Drop);

      elsif Total_Length > Max_Length then

         Source.Length := Max_Length;
         case Drop is

            when PolyORB.Sequences.Left =>
               Source.Content (Max_Length - (Source_Length - High) + 1 ..
                               Max_Length) :=
                 Source.Content (High + 1 .. Source_Length);
               if Drop_Length >= Low - 1 then

                  Source.Content
                    (1 .. Max_Length - (Source_Length - High)) :=
                    By (By'Last -
                        (Max_Length - (Source_Length - High)) + 1 ..
                        By'Last);

               else

                  Source.Content (1 .. Low - (Drop_Length + 1)) :=
                    Source.Content (Drop_Length + 1 .. Low - 1);
                  Source.Content
                    (Low - Drop_Length ..
                     Max_Length - (Source_Length - High)) := By;

               end if;

            when PolyORB.Sequences.Right =>
               if Drop_Length > Source_Length - High then

                  Source.Content (Low .. Max_Length) :=
                    By (By'First .. By'First + Max_Length - Low);

               else

                  Source.Content (Low + By_Length .. Max_Length) :=
                    Source.Content (High + 1 ..
                                    Source_Length - Drop_Length);
                  Source.Content (Low .. Low + By_Length - 1) := By;

               end if;

            when PolyORB.Sequences.Error =>
               raise Length_Error;

         end case;

      else

         Source.Length := Total_Length;
         Source.Content (Low .. Low + By_Length - 1) := By;
         Source.Content (Low + By_Length .. Total_Length) :=
           Source.Content (High + 1 .. Source_Length);

      end if;

   end Replace_Slice;

   ------------
   -- Insert --
   ------------

   function Insert
     (Source   : Sequence;
      Before   : Positive;
      New_Item : Element_Array;
      Drop     : Truncation := Error)
      return Sequence
   is
      Source_Length : constant Length_Range := Source.Length;
      New_Length    : constant Natural := New_Item'Length;
      Total_Length  : constant Natural := Source_Length + New_Length;
      Drop_Length   : constant Integer := Total_Length - Max_Length;
      Result        : Sequence;
   begin

      if Before > Source_Length + 1 then

         raise Index_Error;

      elsif Total_Length > Max_Length then

         Result.Length := Max_Length;
         case Drop is

            when PolyORB.Sequences.Left =>
               Result.Content
                 (Max_Length - Source_Length + Before .. Max_Length) :=
                 Source.Content (Before .. Source_Length);
               if Drop_Length >= Before - 1 then

                  Result.Content
                    (1 .. Max_Length - Source_Length + Before - 1) :=
                    New_Item (New_Item'Last - Max_Length +
                              Source_Length - (Before - 1) + 1 ..
                              New_Item'Last);

               else

                  Result.Content
                    (Before - Drop_Length ..
                     Max_Length - Source_Length + Before - 1) :=
                    New_Item;
                  Result.Content (1 .. Before - 1 - Drop_Length) :=
                    Source.Content (Drop_Length + 1 .. Before - 1);

               end if;

            when PolyORB.Sequences.Right =>
               Result.Content (1 .. Before - 1) :=
                 Source.Content (1 .. Before - 1);
               if Drop_Length > (Source_Length - (Before - 1)) then

                  Result.Content (Before .. Max_Length) :=
                    New_Item (New_Item'First ..
                              New_Item'First + Max_Length - Before);

               else

                  Result.Content (Before .. Before + New_Length - 1) :=
                    New_Item;
                  Result.Content (Before + New_Length .. Max_Length) :=
                    Source.Content (Before ..
                                    Source_Length - Drop_Length);

               end if;

            when PolyORB.Sequences.Error =>
               raise Length_Error;

         end case;

      else

         Result.Length := Total_Length;
         Result.Content (1 .. Before - 1) :=
           Source.Content (1 .. Before - 1);
         Result.Content (Before .. Before + New_Length - 1) := New_Item;
         Result.Content (Before + New_Length .. Total_Length) :=
           Source.Content (Before .. Source_Length);

      end if;

      return Result;

   end Insert;

   ------------
   -- Insert --
   ------------

   procedure Insert
     (Source   : in out Sequence;
      Before   : Positive;
      New_Item : Element_Array;
      Drop     : Truncation := Error)
   is
      Source_Length : constant Length_Range := Source.Length;
      New_Length    : constant Natural := New_Item'Length;
      Total_Length  : constant Natural := Source_Length + New_Length;
      Drop_Length   : constant Integer := Total_Length - Max_Length;
   begin

      if Before > Source_Length + 1 then

         raise Index_Error;

      elsif Total_Length > Max_Length then

         Source.Length := Max_Length;
         case Drop is

            when PolyORB.Sequences.Left =>
               Source.Content
                 (Max_Length - Source_Length + Before .. Max_Length) :=
                 Source.Content (Before .. Source_Length);
               if Drop_Length >= Before - 1 then

                  Source.Content
                    (1 .. Max_Length - Source_Length + Before - 1) :=
                    New_Item (New_Item'Last - Max_Length +
                              Source_Length - (Before - 1) + 1 ..
                              New_Item'Last);

               else

                  Source.Content (1 .. Before - 1 - Drop_Length) :=
                    Source.Content (Drop_Length + 1 .. Before - 1);
                  Source.Content
                    (Before - Drop_Length ..
                     Max_Length - Source_Length + (Before - 1)) :=
                    New_Item;

               end if;

            when PolyORB.Sequences.Right =>
               if Drop_Length > (Source_Length - (Before - 1)) then

                  Source.Content (Before .. Max_Length) :=
                    New_Item (New_Item'First ..
                              New_Item'First + Max_Length - Before);

               else

                  Source.Content (Before + New_Length .. Max_Length) :=
                    Source.Content (Before ..
                                    Source_Length - Drop_Length);
                  Source.Content (Before .. Before + New_Length - 1) :=
                    New_Item;

               end if;

            when PolyORB.Sequences.Error =>
               raise Length_Error;

         end case;

      else

         Source.Length := Total_Length;
         Source.Content (Before + New_Length .. Total_Length) :=
           Source.Content (Before .. Source_Length);
         Source.Content (Before .. Before + New_Length - 1) := New_Item;

      end if;

   end Insert;

   ---------------
   -- Overwrite --
   ---------------

   function Overwrite
     (Source   : Sequence;
      Position : Positive;
      New_Item : Element_Array;
      Drop     : Truncation := Error)
      return Sequence
   is
      Source_Length : constant Length_Range := Source.Length;
      New_Length    : constant Integer := New_Item'Length;
      Endpos        : constant Natural := Position + New_Length - 1;
      Drop_Length   : constant Integer := Endpos - Max_Length;
      Result        : Sequence;
   begin

      if Position > Source_Length + 1 then

         raise Index_Error;

      elsif New_Item'Length = 0 then

         return Source;

      elsif Endpos <= Source_Length then

         Result.Length := Source.Length;
         Result.Content (1 .. Source_Length) :=
           Source.Content (1 .. Source_Length);
         Result.Content (Position .. Endpos) := New_Item;
         return Result;

      elsif Endpos <= Max_Length then

         Result.Length := Endpos;
         Result.Content (1 .. Position - 1) :=
           Source.Content (1 .. Position - 1);
         Result.Content (Position .. Endpos) := New_Item;
         return Result;

      else

         Result.Length := Max_Length;
         case Drop is

            when PolyORB.Sequences.Left =>
               if New_Length >= Max_Length then

                  Result.Content (1 .. Max_Length) :=
                    New_Item (New_Item'Last - Max_Length + 1 ..
                              New_Item'Last);
                  return Result;

               else

                  Result.Content (1 .. Max_Length - New_Length) :=
                    Source.Content (Drop_Length + 1 .. Position - 1);
                  Result.Content
                    (Max_Length - New_Length + 1 .. Max_Length) :=
                    New_Item;
                  return Result;

               end if;

            when PolyORB.Sequences.Right =>
               Result.Content (1 .. Position - 1) :=
                 Source.Content (1 .. Position - 1);
               Result.Content (Position .. Max_Length) :=
                 New_Item (New_Item'First .. New_Item'Last - Drop_Length);
               return Result;

            when PolyORB.Sequences.Error =>
               raise Length_Error;

         end case;

      end if;

   end Overwrite;

   ---------------
   -- Overwrite --
   ---------------

   procedure Overwrite
     (Source   : in out Sequence;
      Position : Positive;
      New_Item : Element_Array;
      Drop     : Truncation := Error)
   is
      Source_Length : constant Length_Range := Source.Length;
      New_Length    : constant Integer := New_Item'Length;
      Endpos        : constant Natural := Position + New_Length - 1;
      Drop_Length   : constant Integer := Endpos - Max_Length;
   begin

      if Position > Source_Length + 1 then

         raise Index_Error;

      elsif Endpos <= Source_Length then

         Source.Content (Position .. Endpos) := New_Item;

      elsif Endpos <= Max_Length then

         Source.Length := Endpos;
         Source.Content (Position .. Endpos) := New_Item;

      else

         Source.Length := Max_Length;
         case Drop is

            when PolyORB.Sequences.Left =>
               if New_Length > Max_Length then

                  Source.Content (1 .. Max_Length) :=
                    New_Item (New_Item'Last - Max_Length + 1 ..
                              New_Item'Last);

               else

                  Source.Content (1 .. Max_Length - New_Length) :=
                    Source.Content (Drop_Length + 1 .. Position - 1);
                  Source.Content
                    (Max_Length - New_Length + 1 .. Max_Length) :=
                    New_Item;

               end if;

            when PolyORB.Sequences.Right =>
               Source.Content (Position .. Max_Length) :=
                 New_Item (New_Item'First .. New_Item'Last - Drop_Length);

            when PolyORB.Sequences.Error =>
               raise Length_Error;

         end case;

      end if;

   end Overwrite;

   ------------
   -- Delete --
   ------------

   function Delete
     (Source  : Sequence;
      From    : Positive;
      Through : Natural)
      return Sequence
   is
      Source_Length : constant Length_Range := Source.Length;
      Num_Delete    : constant Integer := Through - From + 1;
      Result        : Sequence;
   begin

      if Num_Delete <= 0 then

         return Source;

      elsif From > Source_Length + 1 then

         raise Index_Error;

      elsif Through >= Source_Length then

         Result.Length := From - 1;
         Result.Content (1 .. From - 1) := Source.Content (1 .. From - 1);
         return Result;

      else

         Result.Length := Source_Length - Num_Delete;
         Result.Content (1 .. From - 1) := Source.Content (1 .. From - 1);
         Result.Content (From .. Result.Length) :=
           Source.Content (Through + 1 .. Source_Length);
         return Result;

      end if;

   end Delete;

   ------------
   -- Delete --
   ------------

   procedure Delete
     (Source  : in out Sequence;
      From    : Positive;
      Through : Natural)
   is
      Source_Length : constant Length_Range := Source.Length;
      Num_Delete    : constant Integer := Through - From + 1;
   begin

      if Num_Delete <= 0 then

         return;

      elsif From > Source_Length + 1 then

         raise Index_Error;

      elsif Through <= Source_Length then

         Source.Length := From - 1;

      else

         Source.Length := Source_Length - Num_Delete;
         Source.Content (From .. Source.Length) :=
           Source.Content (Through + 1 .. Source_Length);

      end if;

   end Delete;

   ----------
   -- Head --
   ----------

   function Head
     (Source : Sequence;
      Count  : Natural;
      Pad    : Element;
      Drop   : Truncation := Error)
      return Sequence
   is
      Source_Length : constant Natural := Source.Length;
      Npad          : constant Integer := Count - Source_Length;
      Result        : Sequence;
   begin

      if Npad <= 0 then

         Result.Length := Count;
         Result.Content (1 .. Count) := Source.Content (1 .. Count);

      elsif Count <= Max_Length then

         Result.Length := Count;
         Result.Content (1 .. Source_Length) :=
           Source.Content (1 .. Source_Length);
         Result.Content (Source_Length + 1 .. Count) := (others => Pad);

      else

         Result.Length := Max_Length;
         case Drop is

            when PolyORB.Sequences.Left =>
               if Npad >= Max_Length then

                  Result.Content := (others => Pad);

               else

                  Result.Content (1 .. Max_Length - Npad) :=
                    Source.Content (Count - Max_Length + 1 ..
                                    Source_Length);
                  Result.Content (Max_Length - Npad + 1 .. Max_Length) :=
                    (others => Pad);

               end if;

            when PolyORB.Sequences.Right =>
               Result.Content (1 .. Source_Length) :=
                 Source.Content (1 .. Source_Length);
               Result.Content (Source_Length + 1 .. Max_Length) :=
                 (others => Pad);

            when PolyORB.Sequences.Error =>
               raise Length_Error;

         end case;

      end if;
      return Result;

   end Head;

   ----------
   -- Head --
   ----------

   procedure Head
     (Source : in out Sequence;
      Count  : Natural;
      Pad    : Element;
      Drop   : Truncation := Error)
   is
      Source_Length : constant Natural := Source.Length;
      Npad          : constant Integer := Count - Source_Length;
      Temp          : Element_Array (1 .. Max_Length);
   begin

      if Npad <= 0 then

         Source.Length := Count;

      elsif Count <= Max_Length then

         Source.Length := Count;
         Source.Content (Source_Length + 1 .. Count) := (others => Pad);

      else

         Source.Length := Max_Length;
         case Drop is

            when PolyORB.Sequences.Left =>
               if Npad > Max_Length then

                  Source.Content := (others => Pad);

               else

                  Temp := Source.Content;
                  Source.Content (1 .. Max_Length - Npad) :=
                    Temp (Count - Max_Length + 1 .. Source_Length);

                  for J in Max_Length - Npad + 1 .. Max_Length loop
                     Source.Content (J) := Pad;
                  end loop;

               end if;

            when PolyORB.Sequences.Right =>
               Source.Content (Source_Length + 1 .. Max_Length) :=
                 (others => Pad);

            when PolyORB.Sequences.Error =>
               raise Length_Error;

         end case;

      end if;

   end Head;

   ----------
   -- Tail --
   ----------

   function Tail
     (Source : Sequence;
      Count  : Natural;
      Pad    : Element;
      Drop   : Truncation := Error)
      return Sequence
   is
      Source_Length : constant Natural := Source.Length;
      Npad          : constant Integer := Count - Source_Length;
      Result        : Sequence;
   begin

      if Npad <= 0 then

         Result.Length := Count;
         Result.Content (1 .. Count) :=
           Source.Content (Source_Length - Count + 1 .. Source_Length);

      elsif Count <= Max_Length then

         Result.Length := Count;
         Result.Content (1 .. Npad) := (others => Pad);
         Result.Content (Npad + 1 .. Count) :=
           Source.Content (1 .. Source_Length);

      else

         Result.Length := Max_Length;
         case Drop is

            when PolyORB.Sequences.Left =>
               Result.Content (1 .. Max_Length - Source_Length) :=
                 (others => Pad);
               Result.Content
                 (Max_Length - Source_Length + 1 .. Max_Length) :=
                 Source.Content (1 .. Source_Length);

            when PolyORB.Sequences.Right =>
               if Npad >= Max_Length then

                  Result.Content := (others => Pad);

               else

                  Result.Content (1 .. Npad) := (others => Pad);
                  Result.Content (Npad + 1 .. Max_Length) :=
                    Source.Content (1 .. Max_Length - Npad);

               end if;

            when PolyORB.Sequences.Error =>
               raise Length_Error;

         end case;

      end if;

      return Result;

   end Tail;

   --

   procedure Tail (Source : in out Sequence;
                   Count : Natural;
                   Pad : Element;
                   Drop : Truncation := Error) is

      Source_Length : constant Natural := Source.Length;
      Npad : constant Integer := Count - Source_Length;
      Temp : constant Element_Array (1 .. Max_Length) := Source.Content;

   begin

      if Npad <= 0 then

         Source.Length := Count;
         Source.Content (1 .. Count) :=
           Temp (Source_Length - (Count - 1) .. Source_Length);

      elsif Count <= Max_Length then

         Source.Length := Count;
         Source.Content (1 .. Npad) := (others => Pad);
         Source.Content (Npad + 1 .. Count) := Temp (1 .. Source_Length);

      else

         Source.Length := Max_Length;
         case Drop is

            when PolyORB.Sequences.Left =>
               for J in 1 .. Max_Length - Source_Length loop
                  Source.Content (J) := Pad;
               end loop;
               Source.Content
                 (Max_Length - Source_Length + 1 .. Max_Length) :=
                 Temp (1 .. Source_Length);

            when PolyORB.Sequences.Right =>
               if Npad >= Max_Length then

                  Source.Content := (others => Pad);

               else

                  Source.Content (1 .. Npad) := (others => Pad);
                  Source.Content (Npad + 1 .. Max_Length) :=
                    Temp (1 .. Max_Length - Npad);

               end if;

            when PolyORB.Sequences.Error =>
               raise Length_Error;

         end case;

      end if;

   end Tail;

   ---------
   -- "*" --
   ---------

   function "*" (Left : Natural; Right : Element) return Sequence is

      Result : Sequence;

   begin

      if Left <= Max_Length then

         Result.Length := Left;
         for J in 1 .. Left loop
            Result.Content (J) := Right;
         end loop;

      else

         raise Length_Error;

      end if;

      return Result;

   end "*";

   ---------
   -- "*" --
   ---------

   function "*" (Left : Natural; Right : Element_Array)
                 return Sequence is

      Right_Length : constant Natural := Right'Length;
      Total_Length : constant Natural := Left * Right_Length;
      Pos : Positive := 1;
      Result : Sequence;

   begin

      if Total_Length <= Max_Length then

         Result.Length := Total_Length;
         if Total_Length > 0 then

            for J in 1 .. Left loop

               Result.Content (Pos .. Pos + Right_Length - 1) := Right;
               Pos := Pos + Right_Length;

            end loop;

         end if;

      else

         raise Index_Error;

      end if;
      return Result;

   end "*";

   ---------
   -- "*" --
   ---------

   function "*" (Left : Natural; Right : Sequence) return Sequence is

      Right_Length : constant Length_Range := Right.Length;
      Total_Length : constant Natural := Left * Right_Length;
      Pos : Positive := 1;
      Result : Sequence;

   begin

      if Total_Length <= Max_Length then

         Result.Length := Total_Length;

         if Total_Length > 0 then

            for J in 1 .. Left loop

               Result.Content (Pos .. Pos + Right_Length - 1) :=
                 Right.Content (1 .. Right_Length);
               Pos := Pos + Right_Length;

            end loop;

         end if;

      else

         raise Length_Error;

      end if;
      return Result;

   end "*";

   ---------------
   -- Replicate --
   ---------------

   function Replicate
     (Count : Natural;
      Item  : Element;
      Drop  : Truncation := Error)
      return Sequence
   is
      Result : Sequence;
   begin

      case Drop is

         when Error =>
            raise Length_Error;

         when others =>
            if Count <= Max_Length then

               Result.Length := Count;

            else

               Result.Length := Max_Length;

            end if;

      end case;
      Result.Content (1 .. Result.Length) := (others => Item);
      return Result;

   end Replicate;

   ---------------
   -- Replicate --
   ---------------

   function Replicate
     (Count : Natural;
      Item  : Element_Array;
      Drop  : Truncation := Error)
      return Sequence
   is
      Item_Length  : constant Integer := Item'Length;
      Total_Length : constant Integer := Count * Item_Length;
      Result       : Sequence;
      Indx         : Positive;
   begin

      if Total_Length <= Max_Length then

         Result.Length := Total_Length;
         if Total_Length > 0 then

            Indx := 1;
            for J in 1 .. Count loop
               Result.Content (Indx .. Indx + Item_Length - 1) := Item;
               Indx := Indx + Item_Length;
            end loop;

         end if;

      else

         Result.Length := Max_Length;
         case Drop is

            when PolyORB.Sequences.Left =>
               Indx := Max_Length;
               while Indx - Item_Length >= 1 loop

                  Result.Content (Indx - (Item_Length - 1) .. Indx) :=
                    Item;
                  Indx := Indx - Item_Length;

               end loop;
               Result.Content (1 .. Indx) :=
                 Item (Item'Last - Indx + 1 .. Item'Last);

            when PolyORB.Sequences.Right =>
               Indx := 1;
               while Indx + Item_Length <= Max_Length + 1 loop

                  Result.Content (Indx .. Indx + Item_Length - 1) := Item;
                  Indx := Indx + Item_Length;

               end loop;
               Result.Content (Indx .. Max_Length) :=
                 Item (Item'First .. Item'First + Max_Length - Indx);

            when PolyORB.Sequences.Error =>
               raise Length_Error;

         end case;

      end if;
      return Result;

   end Replicate;

   ---------------
   -- Replicate --
   ---------------

   function Replicate
     (Count : Natural;
      Item  : Sequence;
      Drop  : Truncation := Error)
      return Sequence
   is
   begin

      return Replicate (Count => Count,
                        Item => Item.Content (1 .. Item.Length),
                        Drop => Drop);

   end Replicate;

end PolyORB.Sequences.Bounded;
