{
   This file is part of the Free Pascal FCL library.
   BSD parts (c) 2011 Vlado Boza

   See the file COPYING.FPC, included in this distribution,
   for details about the copyright.

   This program 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.

**********************************************************************}
{$mode objfpc}

{$IFNDEF FPC_DOTTEDUNITS}
unit garrayutils;
{$ENDIF FPC_DOTTEDUNITS}

interface

const MaxDepth=60;
const InsertSortThreshold=16;

{TCompare is comparing class, which should have class method c(a,b:TValue):boolean, which returns true if a is less than b}
type
  generic TOrderingArrayUtils<TArr, Tvalue, TCompare>=class
  private
    class procedure Sortrange(var Arr:TArr; Start,Fin,d:SizeUInt);
    class procedure HeapSort(var Arr:TArr; Start,Fin:SizeUInt);
    class procedure InsertSort(var Arr:TArr; Start,Fin:SizeUInt);
    class function Left(a:SizeUInt):SizeUInt;inline;
    class function Right(a:SizeUInt):SizeUInt;inline;
    class procedure Heapify(var Arr: TArr; Position:SizeUInt; Start,Fin:SizeUInt);
    class function Parent(a:SizeUInt):SizeUInt;inline;
  public
    class procedure Sort(var Arr: TArr; size:SizeUInt);
    class function NextPermutation(var Arr: TArr; size:SizeUInt):boolean;
  end;

  generic TArrayUtils<TArr, Tvalue>=class
  public
    class procedure RandomShuffle(Arr: TArr; size: SizeUInt);
  end;

implementation

class function TOrderingArrayUtils.Left(a:SizeUInt):SizeUInt;inline;
begin
  Left:=((a+1)shl 1)-1;
end;

class function TOrderingArrayUtils.Right(a:SizeUInt):SizeUInt;inline;
begin
  Right:=(a+1) shl 1;
end;

class function TOrderingArrayUtils.Parent(a:SizeUInt):SizeUInt;inline;
begin
  Parent:=(a-1)shr 1;
end;

class procedure TOrderingArrayUtils.Heapify(var Arr: TArr; Position:SizeUInt; Start,Fin:SizeUInt);
var mpos,l,r:SizeUInt; temp:TValue;
begin
  while(true) do 
  begin
    mpos:=Position;
    l:=Left(Position-Start)+Start;
    r:=Right(Position-Start)+Start;
    if (l<Fin) AND (TCompare.c(Arr[mpos],Arr[l])) then
      mpos:=l;
    if (r<Fin) AND (TCompare.c(Arr[mpos],Arr[r])) then
      mpos:=r;
    if mpos = Position then break;
    
    temp:=Arr[Position];
    Arr[Position]:=Arr[mpos];
    Arr[mpos]:=temp;
    Position:=mpos;
  end;
end;

class procedure TOrderingArrayUtils.Sort(var Arr:TArr; size:SizeUInt);inline;
begin
  Sortrange(Arr,0,size,0);
  InsertSort(Arr,0,size);
end;

class procedure TOrderingArrayUtils.Sortrange(var Arr:TArr; Start,Fin,d:SizeUInt);
var pivot,temp:Tvalue; i,j,k,l:SizeInt;
begin
  if (Fin-Start) <= InsertSortThreshold then
  begin
    InsertSort(Arr,Start,Fin);
    exit;
  end;
  if d>=maxdepth then
  begin
    HeapSort(Arr, Start, Fin);
    exit;
  end;
{median of 3} 
  j:=Start;
  k:=Fin-1;
  l:=(Start+Fin)div 2;
  if(TCompare.c(Arr[j],Arr[k])) and (TCompare.c(Arr[j],Arr[l])) then
  begin
    if(TCompare.c(Arr[k],Arr[l])) then
    begin
      temp:=Arr[k];
      Arr[k]:=Arr[j];
      Arr[j]:=temp;
    end else 
    begin
      temp:=Arr[l];
      Arr[l]:=Arr[j];
      Arr[j]:=temp;
    end;
  end
  else if(TCompare.c(Arr[k],Arr[j])) and (TCompare.c(Arr[l],Arr[j])) then
  begin
    if(TCompare.c(Arr[l],Arr[k])) then
    begin
      temp:=Arr[k];
      Arr[k]:=Arr[j];
      Arr[j]:=temp;
    end else
    begin
      temp:=Arr[l];
      Arr[l]:=Arr[j];
      Arr[j]:=temp;
    end;
  end;

{partition} 
  pivot:=Arr[Start];

  i:=Start-1;
  j:=Fin;
  repeat 
    repeat
      dec(j);
    until (not (TCompare.c(pivot,Arr[j])));
   
    
    repeat
      inc(i);
    until (not (TCompare.c(Arr[i],pivot)));
    if(i < j) then
    begin
      temp:=Arr[i];
      Arr[i]:=Arr[j];
      Arr[j]:=temp;
    end;
  until (i>=j);

  Sortrange(Arr, Start, j+1, d+1);
  Sortrange(Arr, j+1, Fin, d+1);
end;

class procedure TOrderingArrayUtils.InsertSort(var Arr:TArr; Start,Fin:SizeUInt);inline;
var i,j:SizeUInt; temp:Tvalue;
begin
  for i:=Start+1 to Fin-1 do
  begin
    j:=i;
    temp:=Arr[i];
    while (j>0) and (TCompare.c(temp,Arr[j-1])) do
    begin
      Arr[j]:=Arr[j-1];
      dec(j);
    end;
    Arr[j]:=temp;
  end;
end;

class procedure TOrderingArrayUtils.HeapSort(var Arr: TArr; Start,Fin:SizeUInt);
var i,cur,next,l,r,size:SizeUInt; temp:Tvalue;
begin
{buildHeap}
  size:=Fin-Start;
  for i:=((size div 2)-1) downto 0 do 
    Heapify(Arr, i+Start, Start, Fin);
{bottomup HeapSort}
  for i:=size-1 downto 1 do
  begin
    Fin:=Fin-1;
    cur:=Start;
    temp:=Arr[Start];
    while(true) do
    begin
      l:=Left(cur-Start)+Start;
      if l>=Fin then 
        break;
      next:=l;
      r:=Right(cur-Start)+Start;
      if (r<Fin) AND (TCompare.c(Arr[l],Arr[r])) then
        next:=r;
      Arr[cur]:=Arr[next];
      cur:=next;
    end;
    Arr[cur]:=temp;
    temp:=Arr[i+Start];
    Arr[i+Start]:=Arr[cur];
    Arr[cur]:=temp;
    l:=Parent(cur-Start)+Start;
    while (cur <> 0) AND (TCompare.c(Arr[l],Arr[cur])) do
    begin
      temp:=Arr[cur];
      Arr[cur]:=Arr[l];
      Arr[l]:=temp;
      cur:=l;
      l:=Parent(cur-Start)+Start;
    end;
  end;
end;

class function TOrderingArrayUtils.NextPermutation(var Arr: TArr; size: SizeUInt):boolean;
var i,f:SizeUInt; temp:TValue;
begin
  f := size;
  for i:=size-1 downto 1 do begin 
    if (TCompare.c(arr[i-1], arr[i])) then begin
      f := i-1;
      break;
    end;
  end;
  if f = size then exit(false);
  for i:=size-1 downto 1 do begin
    if (TCompare.c(arr[f], arr[i])) then begin
      temp:=arr[f]; arr[f] := arr[i]; arr[i] := temp;
      break;
    end;
  end;
  i:= size-1;
  inc(f);
  while (i > f) do begin
    temp:=arr[f]; arr[f] := arr[i]; arr[i] := temp;
    dec(i); inc(f);
  end;
  NextPermutation := true;
end;

class procedure TArrayUtils.RandomShuffle(Arr: TArr; size: SizeUInt);
var i,r:SizeUInt; temp:Tvalue;
begin
  for i:=size-1 downto 1 do begin
    r:=random(Int64(i));
    temp:=Arr[r];
    Arr[r]:=Arr[i];
    Arr[i]:=temp;
  end;
end;


end.
