------------------------------------------------------------------------------
--                                                                          --
--                        CHARLES CONTAINER LIBRARY                         --
--                                                                          --
--              Copyright (C) 2001-2004 Matthew J Heaney                    --
--                                                                          --
-- The Charles Container Library ("Charles") 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.  Charles is distributed in the    --
-- hope that it will be useful, but WITHOUT ANY WARRANTY; without even the  --
-- implied warranty of MERCHANTABILITY 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       --
-- Charles;  see file COPYING.TXT.  If not, write to the Free Software      --
-- Foundation,  59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.    --
--                                                                          --
-- As a special exception, if other files instantiate generics from this    --
-- unit, or you link this unit with other files to produce an executable,   --
-- this unit does not by itself cause the resulting executable to be        --
-- covered by the GNU General Public License.  This exception does not      --
-- however invalidate any other reasons why the executable file might be    --
-- covered by the GNU Public License.                                       --
--                                                                          --
-- Charles is maintained by Matthew J Heaney.                               --
--                                                                          --
-- http://charles.tigris.org/                                               --
-- http://home.earthlink.net/~matthewjheaney/index.html                     --
-- mailto:matthewjheaney@earthlink.net                                      --
--                                                                          --
------------------------------------------------------------------------------

procedure Charles.Algorithms.Generic_Quicksort
  (First, Back : in Iterator_Type) is

   Length : constant Offset_Type'Base := Back - First;

   Pivot : Iterator_Type;

   Lo, Mid, Hi : Iterator_Type;

begin

   if Length <= 1 then
      return;
   end if;

   Lo := First;

   Hi := Back - 1;

   if Length = 2 then

      if not Is_Less (Lo, Hi) then
         Swap (Lo, Hi);
      end if;

      return;

   end if;

--      x < y < z
--      x < z < y
--      z < x < y
--      y < x < z
--      y < z < x
--      z < y < x

   Mid := Lo + (Hi - Lo) / 2;

   --Debug (Lo, "low iter");
   --Debug (Mid, "middle iter");
   --Debug (Hi, "high iter");

   if Is_Less (Lo, Mid) then

      if Is_Less (Lo, Hi) then

         if Is_Less (Mid, Hi) then

            --Debug ("swapping lo and mid (mid is median)");

            Swap (Lo, Mid);

         else

            --Debug ("swapping lo and hi (hi is median)");

            Swap (Lo, Hi);

         end if;

      else

         --Debug ("lo is median");

         null;  --lo is median

      end if;

   elsif Is_Less (Lo, Hi) then

      --Debug ("lo is median");

      null; --lo is median

   elsif Is_Less (Mid, Hi) then

      --Debug ("swapping lo and hi (hi is median)");

      Swap (Lo, Hi);

   else

      --Debug ("swapping lo and mid (mid is median)");

      Swap (Lo, Mid);

   end if;

   Pivot := Lo;
   --Debug (Pivot, "median of three is done");

   Outer :
   loop

      loop
         exit Outer when not (Pivot < Hi);

         if Is_Less (Hi, Pivot) then
            --Debug (Hi, "hi < pivot");
            Swap (Hi, Pivot);
            Pivot := Hi;
            Lo := Lo + 1;
            exit;
         else
            --Debug (Hi, "hi >= pivot");
            Hi := Hi - 1;
         end if;
      end loop;

      loop
         exit Outer when not (Lo < Pivot);

         if Is_Less (Lo, Pivot) then
            --Debug (Lo, "lo < pivot");
            Lo := Lo + 1;
         else
            --Debug (Lo, "lo >= pivot");
            Swap (Lo, Pivot);
            Pivot := Lo;
            Hi := Hi - 1;
            exit;
         end if;
      end loop;

   end loop Outer;

   --Debug (pivot, "partition done");

   Generic_Quicksort (First, Pivot);
   Generic_Quicksort (Pivot + 1, Back);

end Charles.Algorithms.Generic_Quicksort;
