1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066 |
- {
- This file is part of the Free Pascal Run Time Library (rtl)
- Copyright (c) 1999-2019 by the Free Pascal development team
- This file provides alternative pluggable sorting algorithms,
- which can be used instead of the default QuickSort implementation
- in unit SortBase.
- 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.
- **********************************************************************}
- {$IFNDEF FPC_DOTTEDUNITS}
- unit SortAlgs;
- {$ENDIF FPC_DOTTEDUNITS}
- {$MODE objfpc}
- interface
- {$IFDEF FPC_DOTTEDUNITS}
- uses
- System.SortBase;
- {$ELSE FPC_DOTTEDUNITS}
- uses
- SortBase;
- {$ENDIF FPC_DOTTEDUNITS}
- {
- HeapSort
- Average performance: O(n log n)
- Worst performance: O(n log n)
- Extra memory use: O(1)
- Stable: no
- Additional notes: Usually slower in practice, compared to QuickSort (in the
- average case), but has a much better worst-case
- performance of O(n log n) (versus O(n*n) for QuickSort).
- Can be used instead of QuickSort where the risk of
- QuickSort's worst case scenario is not acceptable - e.g.
- high risk applications, security-conscious applications
- or applications with hard real-time requirements.
- On systems with small or no data caches it might perform
- better or comparable to QuickSort even in the average
- case, so might be a good general purpose choice for
- embedded systems as well. It's O(1) extra memory use and
- the fact it's not recursive also makes it a good
- candidate for embedded use.
- }
- procedure HeapSort_PtrList_NoContext(
- ItemPtrs: PPointer;
- ItemCount: SizeUInt;
- Comparer: TListSortComparer_NoContext);
- procedure HeapSort_PtrList_Context(
- ItemPtrs: PPointer;
- ItemCount: SizeUInt;
- Comparer: TListSortComparer_Context;
- Context: Pointer);
- procedure HeapSort_ItemList_Context(
- Items: Pointer;
- ItemCount, ItemSize: SizeUInt;
- Comparer: TListSortComparer_Context;
- Context: Pointer);
- procedure HeapSort_ItemList_CustomItemExchanger_Context(
- Items: Pointer;
- ItemCount, ItemSize: SizeUInt;
- Comparer: TListSortComparer_Context;
- Exchanger: TListSortCustomItemExchanger_Context;
- Context: Pointer);
- const
- HeapSort: TSortingAlgorithm = (
- PtrListSorter_NoContextComparer: @HeapSort_PtrList_NoContext;
- PtrListSorter_ContextComparer: @HeapSort_PtrList_Context;
- ItemListSorter_ContextComparer: @HeapSort_ItemList_Context;
- ItemListSorter_CustomItemExchanger_ContextComparer: @HeapSort_ItemList_CustomItemExchanger_Context;
- );
- {
- Randomized QuickSort
- Average performance: O(n log n)
- Worst performance: O(n*n)
- Extra memory use: O(log n) on the stack
- Stable: no
- Additional notes: Uses a random element as the pivot. This makes it harder
- to intentionally produce an input permutation that
- triggers its worst O(n*n) performance. Note that, while
- this ensures that no particular input triggers the worst
- case scenario, this doesn't completely eliminate the
- chance of it happening. There is still an extremely
- small chance that the random number generator generates
- an unlucky sequence that triggers the worst O(n*n)
- performance when combined with the input permutation.
- And it is still possible for a malicious user to
- deliberately construct a worst case scenario, if the
- random sequence can be predicted (it is generated by a
- pseudorandom-number generator, which means its output is
- deterministic, and can be predicted if the initial random
- seed is known. And Randomize uses the system time to
- initialize the random seed, which also makes it easy to
- predict). If these risks cannot be tolerated, a different
- sorting algorithm should be used.
- }
- {$ifdef FPC_HAS_FEATURE_RANDOM}
- procedure RandomizedQuickSort_PtrList_NoContext(
- ItemPtrs: PPointer;
- ItemCount: SizeUInt;
- Comparer: TListSortComparer_NoContext);
- procedure RandomizedQuickSort_PtrList_Context(
- ItemPtrs: PPointer;
- ItemCount: SizeUInt;
- Comparer: TListSortComparer_Context;
- Context: Pointer);
- procedure RandomizedQuickSort_ItemList_Context(
- Items: Pointer;
- ItemCount, ItemSize: SizeUInt;
- Comparer: TListSortComparer_Context;
- Context: Pointer);
- procedure RandomizedQuickSort_ItemList_CustomItemExchanger_Context(
- Items: Pointer;
- ItemCount, ItemSize: SizeUInt;
- Comparer: TListSortComparer_Context;
- Exchanger: TListSortCustomItemExchanger_Context;
- Context: Pointer);
- const
- RandomizedQuickSort: TSortingAlgorithm = (
- PtrListSorter_NoContextComparer: @RandomizedQuickSort_PtrList_NoContext;
- PtrListSorter_ContextComparer: @RandomizedQuickSort_PtrList_Context;
- ItemListSorter_ContextComparer: @RandomizedQuickSort_ItemList_Context;
- ItemListSorter_CustomItemExchanger_ContextComparer: @RandomizedQuickSort_ItemList_CustomItemExchanger_Context;
- );
- {$endif def FPC_HAS_FEATURE_RANDOM}
- {
- IntroSort
- Average performance: O(n log n)
- Worst performance: O(n log n)
- Extra memory use: O(log n) on the stack
- Stable: no
- Additional notes: Hybrid between QuickSort and HeapSort. It starts by doing
- QuickSort, but switches to HeapSort if the recursion
- depth exceeds 2*log2(n). This results in fast average
- performance, similar to QuickSort, combined with a good
- O(n log n) worst case performance, because sequences that
- trigger QuickSort's worst case are caught and sorted by
- HeapSort instead.
- }
- procedure IntroSort_PtrList_NoContext(
- ItemPtrs: PPointer;
- ItemCount: SizeUInt;
- Comparer: TListSortComparer_NoContext);
- procedure IntroSort_PtrList_Context(
- ItemPtrs: PPointer;
- ItemCount: SizeUInt;
- Comparer: TListSortComparer_Context;
- Context: Pointer);
- procedure IntroSort_ItemList_Context(
- Items: Pointer;
- ItemCount, ItemSize: SizeUInt;
- Comparer: TListSortComparer_Context;
- Context: Pointer);
- procedure IntroSort_ItemList_CustomItemExchanger_Context(
- Items: Pointer;
- ItemCount, ItemSize: SizeUInt;
- Comparer: TListSortComparer_Context;
- Exchanger: TListSortCustomItemExchanger_Context;
- Context: Pointer);
- const
- IntroSort: TSortingAlgorithm = (
- PtrListSorter_NoContextComparer: @IntroSort_PtrList_NoContext;
- PtrListSorter_ContextComparer: @IntroSort_PtrList_Context;
- ItemListSorter_ContextComparer: @IntroSort_ItemList_Context;
- ItemListSorter_CustomItemExchanger_ContextComparer: @IntroSort_ItemList_CustomItemExchanger_Context;
- );
- implementation
- {$GOTO on}
- {*****************************************************************************
- HeapSort
- *****************************************************************************}
- function HeapSort_Parent(i: SizeUInt): SizeUInt; inline;
- begin
- Result := (i - 1) div 2;
- end;
- function HeapSort_Left(i: SizeUInt): SizeUInt; inline;
- begin
- Result := 2*i + 1;
- end;
- function HeapSort_Right(i: SizeUInt): SizeUInt; inline;
- begin
- Result := 2*i + 2;
- end;
- procedure HeapSort_PtrList_NoContext(
- ItemPtrs: PPointer;
- ItemCount: SizeUInt;
- Comparer: TListSortComparer_NoContext);
- var
- HeapSize: SizeUInt;
- procedure Heapify(I: SizeUInt);
- label
- again;
- var
- L, R, Largest: SizeUInt;
- Q: Pointer;
- begin
- again:
- L := HeapSort_Left(I);
- R := HeapSort_Right(I);
- if (L < HeapSize) and (Comparer(ItemPtrs[L], ItemPtrs[I]) > 0) then
- Largest := L
- else
- Largest := I;
- if (R < HeapSize) and (Comparer(ItemPtrs[R], ItemPtrs[Largest]) > 0) then
- Largest := R;
- if Largest <> I then
- begin
- Q := ItemPtrs[I];
- ItemPtrs[I] := ItemPtrs[Largest];
- ItemPtrs[Largest] := Q;
- { we use goto instead of tail recursion }
- I := Largest;
- goto again;
- end;
- end;
- var
- I: SizeUInt;
- Q: Pointer;
- begin
- if not Assigned(ItemPtrs) or (ItemCount < 2) then
- exit;
- HeapSize := ItemCount;
- for I := HeapSort_Parent(ItemCount - 1) downto 0 do
- Heapify(I);
- for I := ItemCount - 1 downto 1 do
- begin
- Q := ItemPtrs[0];
- ItemPtrs[0] := ItemPtrs[I];
- ItemPtrs[I] := Q;
- Dec(HeapSize);
- Heapify(0);
- end;
- end;
- procedure HeapSort_PtrList_Context(
- ItemPtrs: PPointer;
- ItemCount: SizeUInt;
- Comparer: TListSortComparer_Context;
- Context: Pointer);
- var
- HeapSize: SizeUInt;
- procedure Heapify(I: SizeUInt);
- label
- again;
- var
- L, R, Largest: SizeUInt;
- Q: Pointer;
- begin
- again:
- L := HeapSort_Left(I);
- R := HeapSort_Right(I);
- if (L < HeapSize) and (Comparer(ItemPtrs[L], ItemPtrs[I], Context) > 0) then
- Largest := L
- else
- Largest := I;
- if (R < HeapSize) and (Comparer(ItemPtrs[R], ItemPtrs[Largest], Context) > 0) then
- Largest := R;
- if Largest <> I then
- begin
- Q := ItemPtrs[I];
- ItemPtrs[I] := ItemPtrs[Largest];
- ItemPtrs[Largest] := Q;
- { we use goto instead of tail recursion }
- I := Largest;
- goto again;
- end;
- end;
- var
- I: SizeUInt;
- Q: Pointer;
- begin
- if not Assigned(ItemPtrs) or (ItemCount < 2) then
- exit;
- HeapSize := ItemCount;
- for I := HeapSort_Parent(ItemCount - 1) downto 0 do
- Heapify(I);
- for I := ItemCount - 1 downto 1 do
- begin
- Q := ItemPtrs[0];
- ItemPtrs[0] := ItemPtrs[I];
- ItemPtrs[I] := Q;
- Dec(HeapSize);
- Heapify(0);
- end;
- end;
- procedure HeapSort_ItemList_Context(
- Items: Pointer;
- ItemCount, ItemSize: SizeUInt;
- Comparer: TListSortComparer_Context;
- Context: Pointer);
- var
- HeapSize: SizeUInt;
- TempBuf: Pointer;
- procedure Heapify(I: SizeUInt);
- label
- again;
- var
- L, R, Largest: SizeUInt;
- begin
- again:
- L := HeapSort_Left(I);
- R := HeapSort_Right(I);
- if (L < HeapSize) and (Comparer(Items + ItemSize*L, Items + ItemSize*I, Context) > 0) then
- Largest := L
- else
- Largest := I;
- if (R < HeapSize) and (Comparer(Items + ItemSize*R, Items + ItemSize*Largest, Context) > 0) then
- Largest := R;
- if Largest <> I then
- begin
- Move((Items + ItemSize*I)^, TempBuf^, ItemSize);
- Move((Items + ItemSize*Largest)^, (Items + ItemSize*I)^, ItemSize);
- Move(TempBuf^, (Items + ItemSize*Largest)^, ItemSize);
- { we use goto instead of tail recursion }
- I := Largest;
- goto again;
- end;
- end;
- var
- I: SizeUInt;
- begin
- if not Assigned(Items) or (ItemCount < 2) or (ItemSize < 1) then
- exit;
- GetMem(TempBuf, ItemSize);
-
- {$ifdef FPC_HAS_FEATURE_EXCEPTIONS}
- try
- {$endif FPC_HAS_FEATURE_EXCEPTIONS}
- HeapSize := ItemCount;
- for I := HeapSort_Parent(ItemCount - 1) downto 0 do
- Heapify(I);
- for I := ItemCount - 1 downto 1 do
- begin
- Move((Items + ItemSize*0)^, TempBuf^, ItemSize);
- Move((Items + ItemSize*I)^, (Items + ItemSize*0)^, ItemSize);
- Move(TempBuf^, (Items + ItemSize*I)^, ItemSize);
- Dec(HeapSize);
- Heapify(0);
- end;
- {$ifdef FPC_HAS_FEATURE_EXCEPTIONS}
- finally
- {$endif FPC_HAS_FEATURE_EXCEPTIONS}
- FreeMem(TempBuf, ItemSize);
- {$ifdef FPC_HAS_FEATURE_EXCEPTIONS}
- end;
- {$endif FPC_HAS_FEATURE_EXCEPTIONS}
- end;
- procedure HeapSort_ItemList_CustomItemExchanger_Context(
- Items: Pointer;
- ItemCount, ItemSize: SizeUInt;
- Comparer: TListSortComparer_Context;
- Exchanger: TListSortCustomItemExchanger_Context;
- Context: Pointer);
- var
- HeapSize: SizeUInt;
- procedure Heapify(I: SizeUInt);
- label
- again;
- var
- L, R, Largest: SizeUInt;
- begin
- again:
- L := HeapSort_Left(I);
- R := HeapSort_Right(I);
- if (L < HeapSize) and (Comparer(Items + ItemSize*L, Items + ItemSize*I, Context) > 0) then
- Largest := L
- else
- Largest := I;
- if (R < HeapSize) and (Comparer(Items + ItemSize*R, Items + ItemSize*Largest, Context) > 0) then
- Largest := R;
- if Largest <> I then
- begin
- Exchanger(Items + ItemSize*I, Items + ItemSize*Largest, Context);
- { we use goto instead of tail recursion }
- I := Largest;
- goto again;
- end;
- end;
- var
- I: SizeUInt;
- begin
- if not Assigned(Items) or (ItemCount < 2) or (ItemSize < 1) then
- exit;
- HeapSize := ItemCount;
- for I := HeapSort_Parent(ItemCount - 1) downto 0 do
- Heapify(I);
- for I := ItemCount - 1 downto 1 do
- begin
- Exchanger(Items + ItemSize*0, Items + ItemSize*I, Context);
- Dec(HeapSize);
- Heapify(0);
- end;
- end;
- {*****************************************************************************
- Randomized QuickSort
- *****************************************************************************}
- {$ifdef FPC_HAS_FEATURE_RANDOM}
- function Random_SizeUInt(L: SizeUInt): SizeUInt;
- begin
- {$if sizeof(SizeUInt)=2}
- Result := Random(LongInt(L));
- {$elseif sizeof(SizeUInt)=4}
- Result := Random(Int64(L));
- {$elseif sizeof(SizeUInt)=8}
- Result := Random(Int64($100000000));
- Result := Result or (SizeUInt(Random(Int64($100000000))) shl 32);
- if L <> 0 then
- Result := Result mod L
- else
- Result := 0;
- {$else}
- {$fatal Unexpected size of SizeUInt}
- {$endif}
- end;
- procedure RandomizedQuickSort_PtrList_NoContext(ItemPtrs: PPointer; L, R : SizeUInt;
- Comparer: TListSortComparer_NoContext);
- var
- I, J, PivotIdx : SizeUInt;
- P, Q : Pointer;
- begin
- repeat
- I := L;
- J := R;
- PivotIdx := L + Random_SizeUInt(SizeUInt(R - L));
- P := ItemPtrs[PivotIdx];
- repeat
- while (I < PivotIdx) and (Comparer(P, ItemPtrs[i]) > 0) do
- Inc(I);
- while (J > PivotIdx) and (Comparer(P, ItemPtrs[J]) < 0) do
- Dec(J);
- if I < J then
- begin
- Q := ItemPtrs[I];
- ItemPtrs[I] := ItemPtrs[J];
- ItemPtrs[J] := Q;
- if PivotIdx = I then
- begin
- PivotIdx := J;
- Inc(I);
- end
- else if PivotIdx = J then
- begin
- PivotIdx := I;
- Dec(J);
- end
- else
- begin
- Inc(I);
- Dec(J);
- end;
- end;
- until I >= J;
- // sort the smaller range recursively
- // sort the bigger range via the loop
- // Reasons: memory usage is O(log(n)) instead of O(n) and loop is faster than recursion
- if (PivotIdx - L) < (R - PivotIdx) then
- begin
- if (L + 1) < PivotIdx then
- RandomizedQuickSort_PtrList_NoContext(ItemPtrs, L, PivotIdx - 1, Comparer);
- L := PivotIdx + 1;
- end
- else
- begin
- if (PivotIdx + 1) < R then
- RandomizedQuickSort_PtrList_NoContext(ItemPtrs, PivotIdx + 1, R, Comparer);
- if (L + 1) < PivotIdx then
- R := PivotIdx - 1
- else
- exit;
- end;
- until L >= R;
- end;
- procedure RandomizedQuickSort_PtrList_NoContext(ItemPtrs: PPointer; ItemCount: SizeUInt; Comparer: TListSortComparer_NoContext);
- begin
- if not Assigned(ItemPtrs) or (ItemCount < 2) then
- exit;
- RandomizedQuickSort_PtrList_NoContext(ItemPtrs, 0, ItemCount - 1, Comparer);
- end;
- procedure RandomizedQuickSort_PtrList_Context(ItemPtrs: PPointer; ItemCount: SizeUInt; Comparer: TListSortComparer_Context; Context: Pointer);
- procedure QuickSort(L, R : SizeUInt);
- var
- I, J, PivotIdx : SizeUInt;
- P, Q : Pointer;
- begin
- repeat
- I := L;
- J := R;
- PivotIdx := L + Random_SizeUInt(SizeUInt(R - L));
- P := ItemPtrs[PivotIdx];
- repeat
- while (I < PivotIdx) and (Comparer(P, ItemPtrs[I], Context) > 0) do
- Inc(I);
- while (J > PivotIdx) and (Comparer(P, ItemPtrs[J], Context) < 0) do
- Dec(J);
- if I < J then
- begin
- Q := ItemPtrs[I];
- ItemPtrs[I] := ItemPtrs[J];
- ItemPtrs[J] := Q;
- if PivotIdx = I then
- begin
- PivotIdx := J;
- Inc(I);
- end
- else if PivotIdx = J then
- begin
- PivotIdx := I;
- Dec(J);
- end
- else
- begin
- Inc(I);
- Dec(J);
- end;
- end;
- until I >= J;
- // sort the smaller range recursively
- // sort the bigger range via the loop
- // Reasons: memory usage is O(log(n)) instead of O(n) and loop is faster than recursion
- if (PivotIdx - L) < (R - PivotIdx) then
- begin
- if (L + 1) < PivotIdx then
- QuickSort(L, PivotIdx - 1);
- L := PivotIdx + 1;
- end
- else
- begin
- if (PivotIdx + 1) < R then
- QuickSort(PivotIdx + 1, R);
- if (L + 1) < PivotIdx then
- R := PivotIdx - 1
- else
- exit;
- end;
- until L >= R;
- end;
- begin
- if not Assigned(ItemPtrs) or (ItemCount < 2) then
- exit;
- QuickSort(0, ItemCount - 1);
- end;
- procedure RandomizedQuickSort_ItemList_Context(Items: Pointer; ItemCount, ItemSize: SizeUInt; Comparer: TListSortComparer_Context; Context: Pointer);
- var
- TempBuf: Pointer;
- procedure QuickSort(L, R : SizeUInt);
- var
- I, J, PivotIdx : SizeUInt;
- P : Pointer;
- begin
- repeat
- I := L;
- J := R;
- PivotIdx := L + Random_SizeUInt(SizeUInt(R - L));
- P := Items + ItemSize*PivotIdx;
- repeat
- while (I < PivotIdx) and (Comparer(P, Items + ItemSize*I, Context) > 0) do
- Inc(I);
- while (J > PivotIdx) and (Comparer(P, Items + ItemSize*J, Context) < 0) do
- Dec(J);
- if I < J then
- begin
- Move((Items + ItemSize*I)^, TempBuf^, ItemSize);
- Move((Items + ItemSize*J)^, (Items + ItemSize*I)^, ItemSize);
- Move(TempBuf^, (Items + ItemSize*J)^, ItemSize);
- if PivotIdx = I then
- begin
- PivotIdx := J;
- P := Items + ItemSize*PivotIdx;
- Inc(I);
- end
- else if PivotIdx = J then
- begin
- PivotIdx := I;
- P := Items + ItemSize*PivotIdx;
- Dec(J);
- end
- else
- begin
- Inc(I);
- Dec(J);
- end;
- end;
- until I >= J;
- // sort the smaller range recursively
- // sort the bigger range via the loop
- // Reasons: memory usage is O(log(n)) instead of O(n) and loop is faster than recursion
- if (PivotIdx - L) < (R - PivotIdx) then
- begin
- if (L + 1) < PivotIdx then
- QuickSort(L, PivotIdx - 1);
- L := PivotIdx + 1;
- end
- else
- begin
- if (PivotIdx + 1) < R then
- QuickSort(PivotIdx + 1, R);
- if (L + 1) < PivotIdx then
- R := PivotIdx - 1
- else
- exit;
- end;
- until L >= R;
- end;
- begin
- if not Assigned(Items) or (ItemCount < 2) or (ItemSize < 1) then
- exit;
- GetMem(TempBuf, ItemSize);
- try
- QuickSort(0, ItemCount - 1);
- finally
- FreeMem(TempBuf, ItemSize);
- end;
- end;
- procedure RandomizedQuickSort_ItemList_CustomItemExchanger_Context(
- Items: Pointer;
- ItemCount, ItemSize: SizeUInt;
- Comparer: TListSortComparer_Context;
- Exchanger: TListSortCustomItemExchanger_Context;
- Context: Pointer);
- procedure QuickSort(L, R : SizeUInt);
- var
- I, J, PivotIdx : SizeUInt;
- P : Pointer;
- begin
- repeat
- I := L;
- J := R;
- PivotIdx := L + Random_SizeUInt(SizeUInt(R - L));
- P := Items + ItemSize*PivotIdx;
- repeat
- while (I < PivotIdx) and (Comparer(P, Items + ItemSize*I, Context) > 0) do
- Inc(I);
- while (J > PivotIdx) and (Comparer(P, Items + ItemSize*J, Context) < 0) do
- Dec(J);
- if I < J then
- begin
- Exchanger(Items + ItemSize*I, Items + ItemSize*J, Context);
- if PivotIdx = I then
- begin
- PivotIdx := J;
- P := Items + ItemSize*PivotIdx;
- Inc(I);
- end
- else if PivotIdx = J then
- begin
- PivotIdx := I;
- P := Items + ItemSize*PivotIdx;
- Dec(J);
- end
- else
- begin
- Inc(I);
- Dec(J);
- end;
- end;
- until I >= J;
- // sort the smaller range recursively
- // sort the bigger range via the loop
- // Reasons: memory usage is O(log(n)) instead of O(n) and loop is faster than recursion
- if (PivotIdx - L) < (R - PivotIdx) then
- begin
- if (L + 1) < PivotIdx then
- QuickSort(L, PivotIdx - 1);
- L := PivotIdx + 1;
- end
- else
- begin
- if (PivotIdx + 1) < R then
- QuickSort(PivotIdx + 1, R);
- if (L + 1) < PivotIdx then
- R := PivotIdx - 1
- else
- exit;
- end;
- until L >= R;
- end;
- begin
- if not Assigned(Items) or (ItemCount < 2) or (ItemSize < 1) then
- exit;
- QuickSort(0, ItemCount - 1);
- end;
- {$endif def FPC_HAS_FEATURE_RANDOM}
- {*****************************************************************************
- IntroSort
- *****************************************************************************}
- function IntLog2(a: Word): Integer; inline;
- begin
- Result := BsrWord(a);
- end;
- function IntLog2(a: LongWord): Integer; inline;
- begin
- Result := BsrDWord(a);
- end;
- function IntLog2(a: QWord): Integer; inline;
- begin
- Result := BsrQWord(a);
- end;
- procedure IntroSort_PtrList_NoContext(ItemPtrs: PPointer; L, R : SizeUInt;
- Comparer: TListSortComparer_NoContext;
- MaxDepth: Integer);
- var
- I, J, PivotIdx : SizeUInt;
- P, Q : Pointer;
- begin
- repeat
- if MaxDepth > 0 then
- Dec(MaxDepth)
- else
- begin
- HeapSort_PtrList_NoContext(@ItemPtrs[L], (R - L) + 1, Comparer);
- exit;
- end;
- I := L;
- J := R;
- PivotIdx := L + ((R - L) shr 1); { same as ((L + R) div 2), but without the possibility of overflow }
- P := ItemPtrs[PivotIdx];
- repeat
- while (I < PivotIdx) and (Comparer(P, ItemPtrs[i]) > 0) do
- Inc(I);
- while (J > PivotIdx) and (Comparer(P, ItemPtrs[J]) < 0) do
- Dec(J);
- if I < J then
- begin
- Q := ItemPtrs[I];
- ItemPtrs[I] := ItemPtrs[J];
- ItemPtrs[J] := Q;
- if PivotIdx = I then
- begin
- PivotIdx := J;
- Inc(I);
- end
- else if PivotIdx = J then
- begin
- PivotIdx := I;
- Dec(J);
- end
- else
- begin
- Inc(I);
- Dec(J);
- end;
- end;
- until I >= J;
- // sort the smaller range recursively
- // sort the bigger range via the loop
- // Reasons: memory usage is O(log(n)) instead of O(n) and loop is faster than recursion
- if (PivotIdx - L) < (R - PivotIdx) then
- begin
- if (L + 1) < PivotIdx then
- IntroSort_PtrList_NoContext(ItemPtrs, L, PivotIdx - 1, Comparer, MaxDepth);
- L := PivotIdx + 1;
- end
- else
- begin
- if (PivotIdx + 1) < R then
- IntroSort_PtrList_NoContext(ItemPtrs, PivotIdx + 1, R, Comparer, MaxDepth);
- if (L + 1) < PivotIdx then
- R := PivotIdx - 1
- else
- exit;
- end;
- until L >= R;
- end;
- procedure IntroSort_PtrList_NoContext(ItemPtrs: PPointer; ItemCount: SizeUInt; Comparer: TListSortComparer_NoContext);
- begin
- if not Assigned(ItemPtrs) or (ItemCount < 2) then
- exit;
- IntroSort_PtrList_NoContext(ItemPtrs, 0, ItemCount - 1, Comparer, 2*IntLog2(ItemCount));
- end;
- procedure IntroSort_PtrList_Context(ItemPtrs: PPointer; ItemCount: SizeUInt; Comparer: TListSortComparer_Context; Context: Pointer);
- procedure IntroSort(L, R : SizeUInt; MaxDepth: Integer);
- var
- I, J, PivotIdx : SizeUInt;
- P, Q : Pointer;
- begin
- repeat
- if MaxDepth > 0 then
- Dec(MaxDepth)
- else
- begin
- HeapSort_PtrList_Context(@ItemPtrs[L], (R - L) + 1, Comparer, Context);
- exit;
- end;
- I := L;
- J := R;
- PivotIdx := L + ((R - L) shr 1); { same as ((L + R) div 2), but without the possibility of overflow }
- P := ItemPtrs[PivotIdx];
- repeat
- while (I < PivotIdx) and (Comparer(P, ItemPtrs[I], Context) > 0) do
- Inc(I);
- while (J > PivotIdx) and (Comparer(P, ItemPtrs[J], Context) < 0) do
- Dec(J);
- if I < J then
- begin
- Q := ItemPtrs[I];
- ItemPtrs[I] := ItemPtrs[J];
- ItemPtrs[J] := Q;
- if PivotIdx = I then
- begin
- PivotIdx := J;
- Inc(I);
- end
- else if PivotIdx = J then
- begin
- PivotIdx := I;
- Dec(J);
- end
- else
- begin
- Inc(I);
- Dec(J);
- end;
- end;
- until I >= J;
- // sort the smaller range recursively
- // sort the bigger range via the loop
- // Reasons: memory usage is O(log(n)) instead of O(n) and loop is faster than recursion
- if (PivotIdx - L) < (R - PivotIdx) then
- begin
- if (L + 1) < PivotIdx then
- IntroSort(L, PivotIdx - 1, MaxDepth);
- L := PivotIdx + 1;
- end
- else
- begin
- if (PivotIdx + 1) < R then
- IntroSort(PivotIdx + 1, R, MaxDepth);
- if (L + 1) < PivotIdx then
- R := PivotIdx - 1
- else
- exit;
- end;
- until L >= R;
- end;
- begin
- if not Assigned(ItemPtrs) or (ItemCount < 2) then
- exit;
- IntroSort(0, ItemCount - 1, 2*IntLog2(ItemCount));
- end;
- procedure IntroSort_ItemList_Context(Items: Pointer; ItemCount, ItemSize: SizeUInt; Comparer: TListSortComparer_Context; Context: Pointer);
- var
- TempBuf: Pointer;
- procedure IntroSort(L, R : SizeUInt; MaxDepth: Integer);
- var
- I, J, PivotIdx : SizeUInt;
- P : Pointer;
- begin
- repeat
- if MaxDepth > 0 then
- Dec(MaxDepth)
- else
- begin
- HeapSort_ItemList_Context(Items + ItemSize*L, (R - L) + 1, ItemSize, Comparer, Context);
- exit;
- end;
- I := L;
- J := R;
- PivotIdx := L + ((R - L) shr 1); { same as ((L + R) div 2), but without the possibility of overflow }
- P := Items + ItemSize*PivotIdx;
- repeat
- while (I < PivotIdx) and (Comparer(P, Items + ItemSize*I, Context) > 0) do
- Inc(I);
- while (J > PivotIdx) and (Comparer(P, Items + ItemSize*J, Context) < 0) do
- Dec(J);
- if I < J then
- begin
- Move((Items + ItemSize*I)^, TempBuf^, ItemSize);
- Move((Items + ItemSize*J)^, (Items + ItemSize*I)^, ItemSize);
- Move(TempBuf^, (Items + ItemSize*J)^, ItemSize);
- if PivotIdx = I then
- begin
- PivotIdx := J;
- P := Items + ItemSize*PivotIdx;
- Inc(I);
- end
- else if PivotIdx = J then
- begin
- PivotIdx := I;
- P := Items + ItemSize*PivotIdx;
- Dec(J);
- end
- else
- begin
- Inc(I);
- Dec(J);
- end;
- end;
- until I >= J;
- // sort the smaller range recursively
- // sort the bigger range via the loop
- // Reasons: memory usage is O(log(n)) instead of O(n) and loop is faster than recursion
- if (PivotIdx - L) < (R - PivotIdx) then
- begin
- if (L + 1) < PivotIdx then
- IntroSort(L, PivotIdx - 1, MaxDepth);
- L := PivotIdx + 1;
- end
- else
- begin
- if (PivotIdx + 1) < R then
- IntroSort(PivotIdx + 1, R, MaxDepth);
- if (L + 1) < PivotIdx then
- R := PivotIdx - 1
- else
- exit;
- end;
- until L >= R;
- end;
- begin
- if not Assigned(Items) or (ItemCount < 2) or (ItemSize < 1) then
- exit;
- GetMem(TempBuf, ItemSize);
- {$ifdef FPC_HAS_FEATURE_EXCEPTIONS}
- try
- IntroSort(0, ItemCount - 1, 2*IntLog2(ItemCount));
- finally
- FreeMem(TempBuf, ItemSize);
- end;
- {$else FPC_HAS_FEATURE_EXCEPTIONS}
- IntroSort(0, ItemCount - 1, 2*IntLog2(ItemCount));
- FreeMem(TempBuf, ItemSize);
- {$endif FPC_HAS_FEATURE_EXCEPTIONS}
- end;
- procedure IntroSort_ItemList_CustomItemExchanger_Context(
- Items: Pointer;
- ItemCount, ItemSize: SizeUInt;
- Comparer: TListSortComparer_Context;
- Exchanger: TListSortCustomItemExchanger_Context;
- Context: Pointer);
- procedure IntroSort(L, R : SizeUInt; MaxDepth: Integer);
- var
- I, J, PivotIdx : SizeUInt;
- P : Pointer;
- begin
- repeat
- if MaxDepth > 0 then
- Dec(MaxDepth)
- else
- begin
- HeapSort_ItemList_CustomItemExchanger_Context(Items + ItemSize*L, (R - L) + 1, ItemSize, Comparer, Exchanger, Context);
- exit;
- end;
- I := L;
- J := R;
- PivotIdx := L + ((R - L) shr 1); { same as ((L + R) div 2), but without the possibility of overflow }
- P := Items + ItemSize*PivotIdx;
- repeat
- while (I < PivotIdx) and (Comparer(P, Items + ItemSize*I, Context) > 0) do
- Inc(I);
- while (J > PivotIdx) and (Comparer(P, Items + ItemSize*J, Context) < 0) do
- Dec(J);
- if I < J then
- begin
- Exchanger(Items + ItemSize*I, Items + ItemSize*J, Context);
- if PivotIdx = I then
- begin
- PivotIdx := J;
- P := Items + ItemSize*PivotIdx;
- Inc(I);
- end
- else if PivotIdx = J then
- begin
- PivotIdx := I;
- P := Items + ItemSize*PivotIdx;
- Dec(J);
- end
- else
- begin
- Inc(I);
- Dec(J);
- end;
- end;
- until I >= J;
- // sort the smaller range recursively
- // sort the bigger range via the loop
- // Reasons: memory usage is O(log(n)) instead of O(n) and loop is faster than recursion
- if (PivotIdx - L) < (R - PivotIdx) then
- begin
- if (L + 1) < PivotIdx then
- IntroSort(L, PivotIdx - 1, MaxDepth);
- L := PivotIdx + 1;
- end
- else
- begin
- if (PivotIdx + 1) < R then
- IntroSort(PivotIdx + 1, R, MaxDepth);
- if (L + 1) < PivotIdx then
- R := PivotIdx - 1
- else
- exit;
- end;
- until L >= R;
- end;
- begin
- if not Assigned(Items) or (ItemCount < 2) or (ItemSize < 1) then
- exit;
- IntroSort(0, ItemCount - 1, 2*IntLog2(ItemCount));
- end;
- end.
|