with Integer_Unbounded_Double_Lists;  use Integer_Unbounded_Double_Lists;
with Ada.Numerics.Discrete_Random;
with Ada.Text_IO;  use Ada.Text_IO;
with Ada.Integer_Text_IO;  use Ada.Integer_Text_IO;
with AI302.Containers;  use AI302.Containers;

procedure Test_Unbounded_Double_List is

   procedure Print (C : List) is
      procedure Process (I : Cursor) is
      begin
         Put (Element (I), Width => 0);
         Put (' ');
      end;

      procedure Iterate is
         new Generic_Iteration;
   begin
      Put (Size_Type'Image (Length (C)));
      Put (": ");

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

      New_Line;
   end;


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

   use Random_Integers;

   G : Generator;

   procedure Sort is new Generic_Sort;

   function Is_Sorted (C : List) return Boolean is

      I : Cursor := First (C);
      --J : constant Iterator_Type := Back (C);

      E : Integer := -1;

   begin

      while not Is_Null (I) loop

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

         E := Element (I);

         Next (I);

      end loop;

      return True;

   end Is_Sorted;


   function Count (C : List) return Size_Type is
      N : Size_Type'Base := 0;
      I : Cursor := First (C);
   begin
      while not Is_Null (I) loop
         N := N + 1;
         Next (I);
      end loop;

      return N;
   end;

   function Rev_Count (C : List) return Size_Type is
      N : Size_Type'Base := 0;
      I : Cursor := Last (C);
   begin
      while not Is_Null (I) loop
         N := N + 1;
         Previous (I);
      end loop;

      return N;
   end;


   procedure Populate (C : in out List) is

      I, J : Cursor;
      E : Integer;
      N : Size_Type;

   begin

      while Length (C) < 100 loop

         I := First (C);

         for Index in 1 .. Random (G) mod Integer (Length (C) + 1) loop
            Next (I);
         end loop;

         E := Random (G) mod 1000;
         N := Length (C);

--           Put ("e=");
--           Put (E, Width => 0);
--           Put (" n=");
--           Put (N, Width => 0);
--           New_Line;

         Insert
           (Container => C,
            Before    => I,
            New_Item  => E,
            Position  => J);

         pragma Assert (Element (J) = E);
         pragma Assert (Length (C) = N + 1);
         pragma Assert (Next (J) = I);

         I := Find (C, E);

         while I /= J loop
            I := Find (C, E, Position => Next (I));
         end loop;

         I := First (C);

         for Index in 1 .. Random (G) mod Integer (Length (C) + 1) loop
            Next (I);
         end loop;

         J := First (C);

         for Index in 1 .. Random (G) mod Integer (Length (C) + 1) loop
            Next (J);
         end loop;

--         Print (C);

--           Put ("i=");

--           if I = Null_Cursor then
--              Put ("<null>");
--           else
--              Put (Element (I), Width => 0);
--           end if;

--           Put (" j=");

--           if J = Null_Cursor then
--              Put ("<null>");
--           else
--              Put (Element (J), Width => 0);
--           end if;

--           New_Line;

         N := Length (C);
         Splice (C, Before => I, Position => J);
         pragma Assert (Count (C) = N);
         pragma Assert (J = Null_Cursor
                          or else J = I
                          or else Next (J) = I);

         declare
            C2 : List := C;
            pragma Assert (C2 = C);
         begin
            Assign (Target => C2, Source => C);
            pragma Assert (C2 = C);

            --Print (C2);
            Reverse_Container (C2);

            --Print (C2);
            Reverse_Container (C2);

            pragma Assert (C2 = C);
            --New_Line (2);

            C2 := C;
            pragma Assert (C2 = C);

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

--         Print (C);
--         New_Line;

      end loop;

   end Populate;


   C, C2 : List;
   I, J : Cursor;

   E : Integer;
   N : Size_Type;

begin

   Reset (G);

   Put_Line ("inserting");

   Assign (Target => C2, Source => C);
   pragma Assert (C2 = C);
   Reverse_Container (C);
   pragma Assert (C2 = C);
   Sort (C);
   pragma Assert (Is_Sorted (C));

   Populate (C);

   Put_Line ("deleting");

   while not Is_Empty (C) loop

      Sort (C);
      pragma Assert (Is_Sorted (C));

      I := First (C);
      pragma Assert (I /= Null_Cursor);

      for Index in 1 .. Random (G) mod Integer (Length (C)) loop
         Next (I);
      end loop;

      pragma Assert (I /= Null_Cursor);

      N := Length (C);

      if I = Last (C) then
         Delete_First (C);
      else
         Delete (C, Position => I);
      end if;

      pragma Assert (Length (C) = N - 1);

   end loop;

   Sort (C);

   Put_Line ("inserting");

   Populate (C);

   Put_Line ("deleting");

   while not Is_Empty (C) loop

      Sort (C);
      pragma Assert (Is_Sorted (C));
      pragma Assert (Count (C) = Length (C));
      pragma Assert (Rev_Count (C) = Length (C));

      I := First (C);

      for Index in 1 .. Random (G) mod Integer (Length (C) + 1) loop
         Next (I);
      end loop;

      J := First (C);

      for Index in 1 .. Random (G) mod Integer (Length (C) + 1) loop
         Next (J);
      end loop;

      Splice (C, I, J);
      pragma Assert (J = Null_Cursor
                       or else J = I
                       or else Next (J) = I);

      pragma Assert (J = Null_Cursor
                       or else I = Null_Cursor
                       or else I = J
                       or else Previous (I) = J);

      N := Length (C);

      Delete_First (C);
      pragma Assert (Length (C) = N - 1);
      pragma Assert (Count (C) = Length (C));
      pragma Assert (Rev_Count (C) = Length (C));

   end loop;

   pragma Assert (Is_Empty (C));

   Sort (C);
   pragma Assert (Is_Sorted (C));
   pragma Assert (Count (C) = Length (C));
   pragma Assert (Rev_Count (C) = Length (C));

   Put_Line ("inserting");

   Populate (C);

   Put_Line ("deleting");

   while not Is_Empty (C) loop

--        Put (Length (C), Width => 0);
--        New_Line;

      Sort (C);
      pragma Assert (Is_Sorted (C));
      pragma Assert (Count (C) = Length (C));
      pragma Assert (Rev_Count (C) = Length (C));

      I := First (C);

      for Index in 1 .. Random (G) mod Integer (Length (C)) loop
         Next (I);
      end loop;

      E := Element (I);

      N := Length (C);

      Delete (C, Item => E);

      pragma Assert (Length (C) < N);
      pragma Assert (not Is_In (E, C));

   end loop;

   Sort (C);
   pragma Assert (Is_Sorted (C));
   pragma Assert (Count (C) = Length (C));
   pragma Assert (Rev_Count (C) = Length (C));

end Test_Unbounded_Double_List;


