with Single_Unbounded_Merge_Lists;  use Single_Unbounded_Merge_Lists;
with Merge_Types;  use Merge_Types;
with Ada.Numerics.Discrete_Random;
with Ada.Text_IO;  use Ada.Text_IO;
with Ada.Integer_Text_IO;  use Ada.Integer_Text_IO;
with Ada.Command_Line;  use Ada.Command_Line;

procedure Test_Single_Unbounded_Merge is

   procedure Print (C : Container_Type) is

      procedure Process (I : Iterator_Type) is
         M : Merge_Type := Element (I);
      begin
         Put (M.I, Width => 0);
         Put (M.C);
         Put (' ');
      end;

      procedure Iterate is
        new Single_Unbounded_Merge_Lists.Generic_Iteration;

   begin

      Put (Length (C), Width => 0);
      Put (": ");

      if Is_Empty (C) then
         Put ("<empty>");
      else
         Iterate (C);
      end if;

      New_Line;

   end Print;


   function Is_Sorted (C : Container_Type) return Boolean is

      I, J : Iterator_Type;
   begin
      if Length (C) <= 1 then
         return True;
      end if;

      I := First (C);

      loop

         J := Succ (I);

         if J = Back (C) then
            return True;
         end if;

         if Element (J) < Element (I) then
            return False;
         end if;

         I := J;

      end loop;
   end Is_Sorted;


   function Count (C : Container_Type) return Natural is
      I : Iterator_Type := First (C);
      N : Integer'Base := 0;
   begin
      while I /= Back (C) loop
         N := N + 1;
         Increment (I);
      end loop;

      return N;
   end;


   procedure Sort is
      new Single_Unbounded_Merge_Lists.Generic_Sort;

   procedure Merge is
      new Single_Unbounded_Merge_Lists.Generic_Merge;

   package Random_Integers is
      new Ada.Numerics.Discrete_Random (Integer);

   use Random_Integers;

   G : Generator;

   C1, C2 : Container_Type;

   N : Natural;

   I, J : Iterator_Type;

   Seed : Integer := 0;

begin

   if Argument_Count >= 1 then
      Seed := Integer'Value (Argument (1));
   end if;

   Reset (G, Seed);

   for Index in 1 .. 50 loop

      Put (Index, Width => 0);
      New_Line;

      N := Random (G) mod 21;

      for I in 1 .. N loop
         Append (C1, New_Item => (Random (G) mod 26, 'a'));
      end loop;

      N := Random (G) mod 21;

      for I in 1 .. N loop
         Append (C2, New_Item => (Random (G) mod 26, 'b'));
      end loop;

      Print (C1);
      Print (C2);
      --New_Line;

      I := First (C1);

      if I /= Back (C1) then
         N := Random (G) mod Length (C1);

         for X in 1 .. N loop
            Increment (I);
         end loop;
      end if;

      J := First (C1);

      if J /= Back (C1) then
         N := Random (G) mod Length (C1);

         for X in 1 .. N loop
            Increment (J);
         end loop;
      end if;

      Put ("splice #1: I=");

      if I = Back (C1) then
         Put ("<back>");
      else
         Put (Element (I).I, Width => 0);
         Put (Element (I).C);
      end if;

      Put (" J=");

      if J = Back (C1) then
         Put ("<back>");
      else
         Put (Element (J).I, Width => 0);
         Put (Element (J).C);
      end if;

      New_Line;

      --Splice_After (C1, I, C2);
      Splice_After (C1, Position => I, Pred => J);

      Print (C1);
      Print (C2);
      --New_Line;

      I := First (C1);

      if I /= Back (C1) then
         N := Random (G) mod Length (C1);

         for X in 1 .. N loop
            Increment (I);
         end loop;
      end if;

      J := First (C2);

      if J /= Back (C2) then
         N := Random (G) mod Length (C2);

         for X in 1 .. N loop
            Increment (J);
         end loop;
      end if;

      Put ("splice #2: I=");

      if I = Back (C1) then
         Put ("<back>");
      else
         Put (Element (I).I, Width => 0);
         Put (Element (I).C);
      end if;

      Put (" J=");

      if J = Back (C2) then
         Put ("<back>");
      else
         Put (Element (J).I, Width => 0);
         Put (Element (J).C);
      end if;

      New_Line;

      if I = Null_Iterator then

         if J = Null_Iterator then

            --move c2.first to front of c1

            declare
               K : constant Iterator_Type := First (C2);
               N1 : constant Natural := Length (C1);
               N2 : constant Natural := Length (C2);
            begin
               Splice_After (C1, I, C2, J);

               Print (C1);
               Print (C2);
               New_Line;

               if N2 > 0 then
                  pragma Assert (K = First (C1));
                  pragma Assert (Length (C1) = Count (C1));
                  pragma Assert (Length (C2) = Count (C2));
                  pragma Assert (Length (C1) = N1 + 1);
                  pragma Assert (Length (C2) = N2 - 1);
                  null;
               end if;
            end;

         elsif J = Last (C2) then

            declare
               K : constant Iterator_Type := Succ (J);
               pragma Assert (K = Null_Iterator);

               C1b : constant Container_Type := C1;
               C2b : constant Container_Type := C2;
               N1 : constant Natural := Length (C1);
               N2 : constant Natural := Length (C2);
            begin
               Splice_After (C1, I, C2, J);

               Print (C1);
               Print (C2);
               New_Line;

               pragma Assert (C1 = C1b);
               pragma Assert (C2 = C2b);
               pragma Assert (Length (C1) = Count (C1));
               pragma Assert (Length (C2) = Count (C2));
               pragma Assert (Length (C1) = N1);
               pragma Assert (Length (C2) = N2);
            end;

         else

            --move succ c2.j to front of c1

            declare
               K : constant Iterator_Type := Succ (J);
               pragma Assert (K /= Null_Iterator);
               N1 : constant Natural := Length (C1);
               N2 : constant Natural := Length (C2);
            begin
               Splice_After (C1, I, C2, J);

               Print (C1);
               Print (C2);
               New_Line;

               pragma Assert (K = First (C1));
               pragma Assert (Length (C1) = Count (C1));
               pragma Assert (Length (C2) = Count (C2));
               pragma Assert (Length (C1) = N1 + 1);
               pragma Assert (Length (C2) = N2 - 1);
            end;

         end if;

      elsif J = Null_Iterator then

         --move c2.first to after c1.i

         declare
            K : constant Iterator_Type := First (C2);
            N1 : constant Natural := Length (C1);
            N2 : constant Natural := Length (C2);
         begin
            Splice_After (C1, I, C2, J);

            Print (C1);
            Print (C2);
            New_Line;

            if N2 > 0 then
               pragma Assert (K = Succ (I));
               pragma Assert (Length (C1) = Count (C1));
               pragma Assert (Length (C2) = Count (C2));
               pragma Assert (Length (C1) = N1 + 1);
               pragma Assert (Length (C2) = N2 - 1);
               null;
            end if;
         end;

      elsif J = Last (C2) then

         declare
            K : constant Iterator_Type := Succ (J);
            pragma Assert (K = Null_Iterator);

            C1b : constant Container_Type := C1;
            C2b : constant Container_Type := C2;
            N1 : constant Natural := Length (C1);
            N2 : constant Natural := Length (C2);
         begin
            Splice_After (C1, I, C2, J);

            Print (C1);
            Print (C2);
            New_Line;

            pragma Assert (C1 = C1b);
            pragma Assert (C2 = C2b);
            pragma Assert (Length (C1) = Count (C1));
            pragma Assert (Length (C2) = Count (C2));
            pragma Assert (Length (C1) = N1);
            pragma Assert (Length (C2) = N2);
         end;

      else

         --move succ c2.j to after c1.i

         declare
            K : constant Iterator_Type := Succ (J);
            N1 : constant Natural := Length (C1);
            N2 : constant Natural := Length (C2);
         begin
            Splice_After (C1, I, C2, J);

            Print (C1);
            Print (C2);
            New_Line;

            pragma Assert (K = Succ (I));
            pragma Assert (Length (C1) = Count (C1));
            pragma Assert (Length (C2) = Count (C2));
            pragma Assert (Length (C1) = N1 + 1);
            pragma Assert (Length (C2) = N2 - 1);
         end;

      end if;

      Put_Line ("sorting");

      Sort (C1);
      pragma Assert (Is_Sorted (C1));

      Sort (C2);
      pragma Assert (Is_Sorted (C2));

      Print (C1);
      Print (C2);
      New_Line;

      Put_Line ("merging");

      Merge (C1, C2);

      Print (C1);
      New_Line (2);

      pragma Assert (Is_Empty (C2));
      pragma Assert (Is_Sorted (C1));

      Clear (C1);
      Clear (C2);

   end loop;


end Test_Single_Unbounded_Merge;

