sortalgs.pp 31 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066
  1. {
  2. This file is part of the Free Pascal Run Time Library (rtl)
  3. Copyright (c) 1999-2019 by the Free Pascal development team
  4. This file provides alternative pluggable sorting algorithms,
  5. which can be used instead of the default QuickSort implementation
  6. in unit SortBase.
  7. See the file COPYING.FPC, included in this distribution,
  8. for details about the copyright.
  9. This program is distributed in the hope that it will be useful,
  10. but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  12. **********************************************************************}
  13. {$IFNDEF FPC_DOTTEDUNITS}
  14. unit SortAlgs;
  15. {$ENDIF FPC_DOTTEDUNITS}
  16. {$MODE objfpc}
  17. interface
  18. {$IFDEF FPC_DOTTEDUNITS}
  19. uses
  20. System.SortBase;
  21. {$ELSE FPC_DOTTEDUNITS}
  22. uses
  23. SortBase;
  24. {$ENDIF FPC_DOTTEDUNITS}
  25. {
  26. HeapSort
  27. Average performance: O(n log n)
  28. Worst performance: O(n log n)
  29. Extra memory use: O(1)
  30. Stable: no
  31. Additional notes: Usually slower in practice, compared to QuickSort (in the
  32. average case), but has a much better worst-case
  33. performance of O(n log n) (versus O(n*n) for QuickSort).
  34. Can be used instead of QuickSort where the risk of
  35. QuickSort's worst case scenario is not acceptable - e.g.
  36. high risk applications, security-conscious applications
  37. or applications with hard real-time requirements.
  38. On systems with small or no data caches it might perform
  39. better or comparable to QuickSort even in the average
  40. case, so might be a good general purpose choice for
  41. embedded systems as well. It's O(1) extra memory use and
  42. the fact it's not recursive also makes it a good
  43. candidate for embedded use.
  44. }
  45. procedure HeapSort_PtrList_NoContext(
  46. ItemPtrs: PPointer;
  47. ItemCount: SizeUInt;
  48. Comparer: TListSortComparer_NoContext);
  49. procedure HeapSort_PtrList_Context(
  50. ItemPtrs: PPointer;
  51. ItemCount: SizeUInt;
  52. Comparer: TListSortComparer_Context;
  53. Context: Pointer);
  54. procedure HeapSort_ItemList_Context(
  55. Items: Pointer;
  56. ItemCount, ItemSize: SizeUInt;
  57. Comparer: TListSortComparer_Context;
  58. Context: Pointer);
  59. procedure HeapSort_ItemList_CustomItemExchanger_Context(
  60. Items: Pointer;
  61. ItemCount, ItemSize: SizeUInt;
  62. Comparer: TListSortComparer_Context;
  63. Exchanger: TListSortCustomItemExchanger_Context;
  64. Context: Pointer);
  65. const
  66. HeapSort: TSortingAlgorithm = (
  67. PtrListSorter_NoContextComparer: @HeapSort_PtrList_NoContext;
  68. PtrListSorter_ContextComparer: @HeapSort_PtrList_Context;
  69. ItemListSorter_ContextComparer: @HeapSort_ItemList_Context;
  70. ItemListSorter_CustomItemExchanger_ContextComparer: @HeapSort_ItemList_CustomItemExchanger_Context;
  71. );
  72. {
  73. Randomized QuickSort
  74. Average performance: O(n log n)
  75. Worst performance: O(n*n)
  76. Extra memory use: O(log n) on the stack
  77. Stable: no
  78. Additional notes: Uses a random element as the pivot. This makes it harder
  79. to intentionally produce an input permutation that
  80. triggers its worst O(n*n) performance. Note that, while
  81. this ensures that no particular input triggers the worst
  82. case scenario, this doesn't completely eliminate the
  83. chance of it happening. There is still an extremely
  84. small chance that the random number generator generates
  85. an unlucky sequence that triggers the worst O(n*n)
  86. performance when combined with the input permutation.
  87. And it is still possible for a malicious user to
  88. deliberately construct a worst case scenario, if the
  89. random sequence can be predicted (it is generated by a
  90. pseudorandom-number generator, which means its output is
  91. deterministic, and can be predicted if the initial random
  92. seed is known. And Randomize uses the system time to
  93. initialize the random seed, which also makes it easy to
  94. predict). If these risks cannot be tolerated, a different
  95. sorting algorithm should be used.
  96. }
  97. {$ifdef FPC_HAS_FEATURE_RANDOM}
  98. procedure RandomizedQuickSort_PtrList_NoContext(
  99. ItemPtrs: PPointer;
  100. ItemCount: SizeUInt;
  101. Comparer: TListSortComparer_NoContext);
  102. procedure RandomizedQuickSort_PtrList_Context(
  103. ItemPtrs: PPointer;
  104. ItemCount: SizeUInt;
  105. Comparer: TListSortComparer_Context;
  106. Context: Pointer);
  107. procedure RandomizedQuickSort_ItemList_Context(
  108. Items: Pointer;
  109. ItemCount, ItemSize: SizeUInt;
  110. Comparer: TListSortComparer_Context;
  111. Context: Pointer);
  112. procedure RandomizedQuickSort_ItemList_CustomItemExchanger_Context(
  113. Items: Pointer;
  114. ItemCount, ItemSize: SizeUInt;
  115. Comparer: TListSortComparer_Context;
  116. Exchanger: TListSortCustomItemExchanger_Context;
  117. Context: Pointer);
  118. const
  119. RandomizedQuickSort: TSortingAlgorithm = (
  120. PtrListSorter_NoContextComparer: @RandomizedQuickSort_PtrList_NoContext;
  121. PtrListSorter_ContextComparer: @RandomizedQuickSort_PtrList_Context;
  122. ItemListSorter_ContextComparer: @RandomizedQuickSort_ItemList_Context;
  123. ItemListSorter_CustomItemExchanger_ContextComparer: @RandomizedQuickSort_ItemList_CustomItemExchanger_Context;
  124. );
  125. {$endif def FPC_HAS_FEATURE_RANDOM}
  126. {
  127. IntroSort
  128. Average performance: O(n log n)
  129. Worst performance: O(n log n)
  130. Extra memory use: O(log n) on the stack
  131. Stable: no
  132. Additional notes: Hybrid between QuickSort and HeapSort. It starts by doing
  133. QuickSort, but switches to HeapSort if the recursion
  134. depth exceeds 2*log2(n). This results in fast average
  135. performance, similar to QuickSort, combined with a good
  136. O(n log n) worst case performance, because sequences that
  137. trigger QuickSort's worst case are caught and sorted by
  138. HeapSort instead.
  139. }
  140. procedure IntroSort_PtrList_NoContext(
  141. ItemPtrs: PPointer;
  142. ItemCount: SizeUInt;
  143. Comparer: TListSortComparer_NoContext);
  144. procedure IntroSort_PtrList_Context(
  145. ItemPtrs: PPointer;
  146. ItemCount: SizeUInt;
  147. Comparer: TListSortComparer_Context;
  148. Context: Pointer);
  149. procedure IntroSort_ItemList_Context(
  150. Items: Pointer;
  151. ItemCount, ItemSize: SizeUInt;
  152. Comparer: TListSortComparer_Context;
  153. Context: Pointer);
  154. procedure IntroSort_ItemList_CustomItemExchanger_Context(
  155. Items: Pointer;
  156. ItemCount, ItemSize: SizeUInt;
  157. Comparer: TListSortComparer_Context;
  158. Exchanger: TListSortCustomItemExchanger_Context;
  159. Context: Pointer);
  160. const
  161. IntroSort: TSortingAlgorithm = (
  162. PtrListSorter_NoContextComparer: @IntroSort_PtrList_NoContext;
  163. PtrListSorter_ContextComparer: @IntroSort_PtrList_Context;
  164. ItemListSorter_ContextComparer: @IntroSort_ItemList_Context;
  165. ItemListSorter_CustomItemExchanger_ContextComparer: @IntroSort_ItemList_CustomItemExchanger_Context;
  166. );
  167. implementation
  168. {$GOTO on}
  169. {*****************************************************************************
  170. HeapSort
  171. *****************************************************************************}
  172. function HeapSort_Parent(i: SizeUInt): SizeUInt; inline;
  173. begin
  174. Result := (i - 1) div 2;
  175. end;
  176. function HeapSort_Left(i: SizeUInt): SizeUInt; inline;
  177. begin
  178. Result := 2*i + 1;
  179. end;
  180. function HeapSort_Right(i: SizeUInt): SizeUInt; inline;
  181. begin
  182. Result := 2*i + 2;
  183. end;
  184. procedure HeapSort_PtrList_NoContext(
  185. ItemPtrs: PPointer;
  186. ItemCount: SizeUInt;
  187. Comparer: TListSortComparer_NoContext);
  188. var
  189. HeapSize: SizeUInt;
  190. procedure Heapify(I: SizeUInt);
  191. label
  192. again;
  193. var
  194. L, R, Largest: SizeUInt;
  195. Q: Pointer;
  196. begin
  197. again:
  198. L := HeapSort_Left(I);
  199. R := HeapSort_Right(I);
  200. if (L < HeapSize) and (Comparer(ItemPtrs[L], ItemPtrs[I]) > 0) then
  201. Largest := L
  202. else
  203. Largest := I;
  204. if (R < HeapSize) and (Comparer(ItemPtrs[R], ItemPtrs[Largest]) > 0) then
  205. Largest := R;
  206. if Largest <> I then
  207. begin
  208. Q := ItemPtrs[I];
  209. ItemPtrs[I] := ItemPtrs[Largest];
  210. ItemPtrs[Largest] := Q;
  211. { we use goto instead of tail recursion }
  212. I := Largest;
  213. goto again;
  214. end;
  215. end;
  216. var
  217. I: SizeUInt;
  218. Q: Pointer;
  219. begin
  220. if not Assigned(ItemPtrs) or (ItemCount < 2) then
  221. exit;
  222. HeapSize := ItemCount;
  223. for I := HeapSort_Parent(ItemCount - 1) downto 0 do
  224. Heapify(I);
  225. for I := ItemCount - 1 downto 1 do
  226. begin
  227. Q := ItemPtrs[0];
  228. ItemPtrs[0] := ItemPtrs[I];
  229. ItemPtrs[I] := Q;
  230. Dec(HeapSize);
  231. Heapify(0);
  232. end;
  233. end;
  234. procedure HeapSort_PtrList_Context(
  235. ItemPtrs: PPointer;
  236. ItemCount: SizeUInt;
  237. Comparer: TListSortComparer_Context;
  238. Context: Pointer);
  239. var
  240. HeapSize: SizeUInt;
  241. procedure Heapify(I: SizeUInt);
  242. label
  243. again;
  244. var
  245. L, R, Largest: SizeUInt;
  246. Q: Pointer;
  247. begin
  248. again:
  249. L := HeapSort_Left(I);
  250. R := HeapSort_Right(I);
  251. if (L < HeapSize) and (Comparer(ItemPtrs[L], ItemPtrs[I], Context) > 0) then
  252. Largest := L
  253. else
  254. Largest := I;
  255. if (R < HeapSize) and (Comparer(ItemPtrs[R], ItemPtrs[Largest], Context) > 0) then
  256. Largest := R;
  257. if Largest <> I then
  258. begin
  259. Q := ItemPtrs[I];
  260. ItemPtrs[I] := ItemPtrs[Largest];
  261. ItemPtrs[Largest] := Q;
  262. { we use goto instead of tail recursion }
  263. I := Largest;
  264. goto again;
  265. end;
  266. end;
  267. var
  268. I: SizeUInt;
  269. Q: Pointer;
  270. begin
  271. if not Assigned(ItemPtrs) or (ItemCount < 2) then
  272. exit;
  273. HeapSize := ItemCount;
  274. for I := HeapSort_Parent(ItemCount - 1) downto 0 do
  275. Heapify(I);
  276. for I := ItemCount - 1 downto 1 do
  277. begin
  278. Q := ItemPtrs[0];
  279. ItemPtrs[0] := ItemPtrs[I];
  280. ItemPtrs[I] := Q;
  281. Dec(HeapSize);
  282. Heapify(0);
  283. end;
  284. end;
  285. procedure HeapSort_ItemList_Context(
  286. Items: Pointer;
  287. ItemCount, ItemSize: SizeUInt;
  288. Comparer: TListSortComparer_Context;
  289. Context: Pointer);
  290. var
  291. HeapSize: SizeUInt;
  292. TempBuf: Pointer;
  293. procedure Heapify(I: SizeUInt);
  294. label
  295. again;
  296. var
  297. L, R, Largest: SizeUInt;
  298. begin
  299. again:
  300. L := HeapSort_Left(I);
  301. R := HeapSort_Right(I);
  302. if (L < HeapSize) and (Comparer(Items + ItemSize*L, Items + ItemSize*I, Context) > 0) then
  303. Largest := L
  304. else
  305. Largest := I;
  306. if (R < HeapSize) and (Comparer(Items + ItemSize*R, Items + ItemSize*Largest, Context) > 0) then
  307. Largest := R;
  308. if Largest <> I then
  309. begin
  310. Move((Items + ItemSize*I)^, TempBuf^, ItemSize);
  311. Move((Items + ItemSize*Largest)^, (Items + ItemSize*I)^, ItemSize);
  312. Move(TempBuf^, (Items + ItemSize*Largest)^, ItemSize);
  313. { we use goto instead of tail recursion }
  314. I := Largest;
  315. goto again;
  316. end;
  317. end;
  318. var
  319. I: SizeUInt;
  320. begin
  321. if not Assigned(Items) or (ItemCount < 2) or (ItemSize < 1) then
  322. exit;
  323. GetMem(TempBuf, ItemSize);
  324. {$ifdef FPC_HAS_FEATURE_EXCEPTIONS}
  325. try
  326. {$endif FPC_HAS_FEATURE_EXCEPTIONS}
  327. HeapSize := ItemCount;
  328. for I := HeapSort_Parent(ItemCount - 1) downto 0 do
  329. Heapify(I);
  330. for I := ItemCount - 1 downto 1 do
  331. begin
  332. Move((Items + ItemSize*0)^, TempBuf^, ItemSize);
  333. Move((Items + ItemSize*I)^, (Items + ItemSize*0)^, ItemSize);
  334. Move(TempBuf^, (Items + ItemSize*I)^, ItemSize);
  335. Dec(HeapSize);
  336. Heapify(0);
  337. end;
  338. {$ifdef FPC_HAS_FEATURE_EXCEPTIONS}
  339. finally
  340. {$endif FPC_HAS_FEATURE_EXCEPTIONS}
  341. FreeMem(TempBuf, ItemSize);
  342. {$ifdef FPC_HAS_FEATURE_EXCEPTIONS}
  343. end;
  344. {$endif FPC_HAS_FEATURE_EXCEPTIONS}
  345. end;
  346. procedure HeapSort_ItemList_CustomItemExchanger_Context(
  347. Items: Pointer;
  348. ItemCount, ItemSize: SizeUInt;
  349. Comparer: TListSortComparer_Context;
  350. Exchanger: TListSortCustomItemExchanger_Context;
  351. Context: Pointer);
  352. var
  353. HeapSize: SizeUInt;
  354. procedure Heapify(I: SizeUInt);
  355. label
  356. again;
  357. var
  358. L, R, Largest: SizeUInt;
  359. begin
  360. again:
  361. L := HeapSort_Left(I);
  362. R := HeapSort_Right(I);
  363. if (L < HeapSize) and (Comparer(Items + ItemSize*L, Items + ItemSize*I, Context) > 0) then
  364. Largest := L
  365. else
  366. Largest := I;
  367. if (R < HeapSize) and (Comparer(Items + ItemSize*R, Items + ItemSize*Largest, Context) > 0) then
  368. Largest := R;
  369. if Largest <> I then
  370. begin
  371. Exchanger(Items + ItemSize*I, Items + ItemSize*Largest, Context);
  372. { we use goto instead of tail recursion }
  373. I := Largest;
  374. goto again;
  375. end;
  376. end;
  377. var
  378. I: SizeUInt;
  379. begin
  380. if not Assigned(Items) or (ItemCount < 2) or (ItemSize < 1) then
  381. exit;
  382. HeapSize := ItemCount;
  383. for I := HeapSort_Parent(ItemCount - 1) downto 0 do
  384. Heapify(I);
  385. for I := ItemCount - 1 downto 1 do
  386. begin
  387. Exchanger(Items + ItemSize*0, Items + ItemSize*I, Context);
  388. Dec(HeapSize);
  389. Heapify(0);
  390. end;
  391. end;
  392. {*****************************************************************************
  393. Randomized QuickSort
  394. *****************************************************************************}
  395. {$ifdef FPC_HAS_FEATURE_RANDOM}
  396. function Random_SizeUInt(L: SizeUInt): SizeUInt;
  397. begin
  398. {$if sizeof(SizeUInt)=2}
  399. Result := Random(LongInt(L));
  400. {$elseif sizeof(SizeUInt)=4}
  401. Result := Random(Int64(L));
  402. {$elseif sizeof(SizeUInt)=8}
  403. Result := Random(Int64($100000000));
  404. Result := Result or (SizeUInt(Random(Int64($100000000))) shl 32);
  405. if L <> 0 then
  406. Result := Result mod L
  407. else
  408. Result := 0;
  409. {$else}
  410. {$fatal Unexpected size of SizeUInt}
  411. {$endif}
  412. end;
  413. procedure RandomizedQuickSort_PtrList_NoContext(ItemPtrs: PPointer; L, R : SizeUInt;
  414. Comparer: TListSortComparer_NoContext);
  415. var
  416. I, J, PivotIdx : SizeUInt;
  417. P, Q : Pointer;
  418. begin
  419. repeat
  420. I := L;
  421. J := R;
  422. PivotIdx := L + Random_SizeUInt(SizeUInt(R - L));
  423. P := ItemPtrs[PivotIdx];
  424. repeat
  425. while (I < PivotIdx) and (Comparer(P, ItemPtrs[i]) > 0) do
  426. Inc(I);
  427. while (J > PivotIdx) and (Comparer(P, ItemPtrs[J]) < 0) do
  428. Dec(J);
  429. if I < J then
  430. begin
  431. Q := ItemPtrs[I];
  432. ItemPtrs[I] := ItemPtrs[J];
  433. ItemPtrs[J] := Q;
  434. if PivotIdx = I then
  435. begin
  436. PivotIdx := J;
  437. Inc(I);
  438. end
  439. else if PivotIdx = J then
  440. begin
  441. PivotIdx := I;
  442. Dec(J);
  443. end
  444. else
  445. begin
  446. Inc(I);
  447. Dec(J);
  448. end;
  449. end;
  450. until I >= J;
  451. // sort the smaller range recursively
  452. // sort the bigger range via the loop
  453. // Reasons: memory usage is O(log(n)) instead of O(n) and loop is faster than recursion
  454. if (PivotIdx - L) < (R - PivotIdx) then
  455. begin
  456. if (L + 1) < PivotIdx then
  457. RandomizedQuickSort_PtrList_NoContext(ItemPtrs, L, PivotIdx - 1, Comparer);
  458. L := PivotIdx + 1;
  459. end
  460. else
  461. begin
  462. if (PivotIdx + 1) < R then
  463. RandomizedQuickSort_PtrList_NoContext(ItemPtrs, PivotIdx + 1, R, Comparer);
  464. if (L + 1) < PivotIdx then
  465. R := PivotIdx - 1
  466. else
  467. exit;
  468. end;
  469. until L >= R;
  470. end;
  471. procedure RandomizedQuickSort_PtrList_NoContext(ItemPtrs: PPointer; ItemCount: SizeUInt; Comparer: TListSortComparer_NoContext);
  472. begin
  473. if not Assigned(ItemPtrs) or (ItemCount < 2) then
  474. exit;
  475. RandomizedQuickSort_PtrList_NoContext(ItemPtrs, 0, ItemCount - 1, Comparer);
  476. end;
  477. procedure RandomizedQuickSort_PtrList_Context(ItemPtrs: PPointer; ItemCount: SizeUInt; Comparer: TListSortComparer_Context; Context: Pointer);
  478. procedure QuickSort(L, R : SizeUInt);
  479. var
  480. I, J, PivotIdx : SizeUInt;
  481. P, Q : Pointer;
  482. begin
  483. repeat
  484. I := L;
  485. J := R;
  486. PivotIdx := L + Random_SizeUInt(SizeUInt(R - L));
  487. P := ItemPtrs[PivotIdx];
  488. repeat
  489. while (I < PivotIdx) and (Comparer(P, ItemPtrs[I], Context) > 0) do
  490. Inc(I);
  491. while (J > PivotIdx) and (Comparer(P, ItemPtrs[J], Context) < 0) do
  492. Dec(J);
  493. if I < J then
  494. begin
  495. Q := ItemPtrs[I];
  496. ItemPtrs[I] := ItemPtrs[J];
  497. ItemPtrs[J] := Q;
  498. if PivotIdx = I then
  499. begin
  500. PivotIdx := J;
  501. Inc(I);
  502. end
  503. else if PivotIdx = J then
  504. begin
  505. PivotIdx := I;
  506. Dec(J);
  507. end
  508. else
  509. begin
  510. Inc(I);
  511. Dec(J);
  512. end;
  513. end;
  514. until I >= J;
  515. // sort the smaller range recursively
  516. // sort the bigger range via the loop
  517. // Reasons: memory usage is O(log(n)) instead of O(n) and loop is faster than recursion
  518. if (PivotIdx - L) < (R - PivotIdx) then
  519. begin
  520. if (L + 1) < PivotIdx then
  521. QuickSort(L, PivotIdx - 1);
  522. L := PivotIdx + 1;
  523. end
  524. else
  525. begin
  526. if (PivotIdx + 1) < R then
  527. QuickSort(PivotIdx + 1, R);
  528. if (L + 1) < PivotIdx then
  529. R := PivotIdx - 1
  530. else
  531. exit;
  532. end;
  533. until L >= R;
  534. end;
  535. begin
  536. if not Assigned(ItemPtrs) or (ItemCount < 2) then
  537. exit;
  538. QuickSort(0, ItemCount - 1);
  539. end;
  540. procedure RandomizedQuickSort_ItemList_Context(Items: Pointer; ItemCount, ItemSize: SizeUInt; Comparer: TListSortComparer_Context; Context: Pointer);
  541. var
  542. TempBuf: Pointer;
  543. procedure QuickSort(L, R : SizeUInt);
  544. var
  545. I, J, PivotIdx : SizeUInt;
  546. P : Pointer;
  547. begin
  548. repeat
  549. I := L;
  550. J := R;
  551. PivotIdx := L + Random_SizeUInt(SizeUInt(R - L));
  552. P := Items + ItemSize*PivotIdx;
  553. repeat
  554. while (I < PivotIdx) and (Comparer(P, Items + ItemSize*I, Context) > 0) do
  555. Inc(I);
  556. while (J > PivotIdx) and (Comparer(P, Items + ItemSize*J, Context) < 0) do
  557. Dec(J);
  558. if I < J then
  559. begin
  560. Move((Items + ItemSize*I)^, TempBuf^, ItemSize);
  561. Move((Items + ItemSize*J)^, (Items + ItemSize*I)^, ItemSize);
  562. Move(TempBuf^, (Items + ItemSize*J)^, ItemSize);
  563. if PivotIdx = I then
  564. begin
  565. PivotIdx := J;
  566. P := Items + ItemSize*PivotIdx;
  567. Inc(I);
  568. end
  569. else if PivotIdx = J then
  570. begin
  571. PivotIdx := I;
  572. P := Items + ItemSize*PivotIdx;
  573. Dec(J);
  574. end
  575. else
  576. begin
  577. Inc(I);
  578. Dec(J);
  579. end;
  580. end;
  581. until I >= J;
  582. // sort the smaller range recursively
  583. // sort the bigger range via the loop
  584. // Reasons: memory usage is O(log(n)) instead of O(n) and loop is faster than recursion
  585. if (PivotIdx - L) < (R - PivotIdx) then
  586. begin
  587. if (L + 1) < PivotIdx then
  588. QuickSort(L, PivotIdx - 1);
  589. L := PivotIdx + 1;
  590. end
  591. else
  592. begin
  593. if (PivotIdx + 1) < R then
  594. QuickSort(PivotIdx + 1, R);
  595. if (L + 1) < PivotIdx then
  596. R := PivotIdx - 1
  597. else
  598. exit;
  599. end;
  600. until L >= R;
  601. end;
  602. begin
  603. if not Assigned(Items) or (ItemCount < 2) or (ItemSize < 1) then
  604. exit;
  605. GetMem(TempBuf, ItemSize);
  606. try
  607. QuickSort(0, ItemCount - 1);
  608. finally
  609. FreeMem(TempBuf, ItemSize);
  610. end;
  611. end;
  612. procedure RandomizedQuickSort_ItemList_CustomItemExchanger_Context(
  613. Items: Pointer;
  614. ItemCount, ItemSize: SizeUInt;
  615. Comparer: TListSortComparer_Context;
  616. Exchanger: TListSortCustomItemExchanger_Context;
  617. Context: Pointer);
  618. procedure QuickSort(L, R : SizeUInt);
  619. var
  620. I, J, PivotIdx : SizeUInt;
  621. P : Pointer;
  622. begin
  623. repeat
  624. I := L;
  625. J := R;
  626. PivotIdx := L + Random_SizeUInt(SizeUInt(R - L));
  627. P := Items + ItemSize*PivotIdx;
  628. repeat
  629. while (I < PivotIdx) and (Comparer(P, Items + ItemSize*I, Context) > 0) do
  630. Inc(I);
  631. while (J > PivotIdx) and (Comparer(P, Items + ItemSize*J, Context) < 0) do
  632. Dec(J);
  633. if I < J then
  634. begin
  635. Exchanger(Items + ItemSize*I, Items + ItemSize*J, Context);
  636. if PivotIdx = I then
  637. begin
  638. PivotIdx := J;
  639. P := Items + ItemSize*PivotIdx;
  640. Inc(I);
  641. end
  642. else if PivotIdx = J then
  643. begin
  644. PivotIdx := I;
  645. P := Items + ItemSize*PivotIdx;
  646. Dec(J);
  647. end
  648. else
  649. begin
  650. Inc(I);
  651. Dec(J);
  652. end;
  653. end;
  654. until I >= J;
  655. // sort the smaller range recursively
  656. // sort the bigger range via the loop
  657. // Reasons: memory usage is O(log(n)) instead of O(n) and loop is faster than recursion
  658. if (PivotIdx - L) < (R - PivotIdx) then
  659. begin
  660. if (L + 1) < PivotIdx then
  661. QuickSort(L, PivotIdx - 1);
  662. L := PivotIdx + 1;
  663. end
  664. else
  665. begin
  666. if (PivotIdx + 1) < R then
  667. QuickSort(PivotIdx + 1, R);
  668. if (L + 1) < PivotIdx then
  669. R := PivotIdx - 1
  670. else
  671. exit;
  672. end;
  673. until L >= R;
  674. end;
  675. begin
  676. if not Assigned(Items) or (ItemCount < 2) or (ItemSize < 1) then
  677. exit;
  678. QuickSort(0, ItemCount - 1);
  679. end;
  680. {$endif def FPC_HAS_FEATURE_RANDOM}
  681. {*****************************************************************************
  682. IntroSort
  683. *****************************************************************************}
  684. function IntLog2(a: Word): Integer; inline;
  685. begin
  686. Result := BsrWord(a);
  687. end;
  688. function IntLog2(a: LongWord): Integer; inline;
  689. begin
  690. Result := BsrDWord(a);
  691. end;
  692. function IntLog2(a: QWord): Integer; inline;
  693. begin
  694. Result := BsrQWord(a);
  695. end;
  696. procedure IntroSort_PtrList_NoContext(ItemPtrs: PPointer; L, R : SizeUInt;
  697. Comparer: TListSortComparer_NoContext;
  698. MaxDepth: Integer);
  699. var
  700. I, J, PivotIdx : SizeUInt;
  701. P, Q : Pointer;
  702. begin
  703. repeat
  704. if MaxDepth > 0 then
  705. Dec(MaxDepth)
  706. else
  707. begin
  708. HeapSort_PtrList_NoContext(@ItemPtrs[L], (R - L) + 1, Comparer);
  709. exit;
  710. end;
  711. I := L;
  712. J := R;
  713. PivotIdx := L + ((R - L) shr 1); { same as ((L + R) div 2), but without the possibility of overflow }
  714. P := ItemPtrs[PivotIdx];
  715. repeat
  716. while (I < PivotIdx) and (Comparer(P, ItemPtrs[i]) > 0) do
  717. Inc(I);
  718. while (J > PivotIdx) and (Comparer(P, ItemPtrs[J]) < 0) do
  719. Dec(J);
  720. if I < J then
  721. begin
  722. Q := ItemPtrs[I];
  723. ItemPtrs[I] := ItemPtrs[J];
  724. ItemPtrs[J] := Q;
  725. if PivotIdx = I then
  726. begin
  727. PivotIdx := J;
  728. Inc(I);
  729. end
  730. else if PivotIdx = J then
  731. begin
  732. PivotIdx := I;
  733. Dec(J);
  734. end
  735. else
  736. begin
  737. Inc(I);
  738. Dec(J);
  739. end;
  740. end;
  741. until I >= J;
  742. // sort the smaller range recursively
  743. // sort the bigger range via the loop
  744. // Reasons: memory usage is O(log(n)) instead of O(n) and loop is faster than recursion
  745. if (PivotIdx - L) < (R - PivotIdx) then
  746. begin
  747. if (L + 1) < PivotIdx then
  748. IntroSort_PtrList_NoContext(ItemPtrs, L, PivotIdx - 1, Comparer, MaxDepth);
  749. L := PivotIdx + 1;
  750. end
  751. else
  752. begin
  753. if (PivotIdx + 1) < R then
  754. IntroSort_PtrList_NoContext(ItemPtrs, PivotIdx + 1, R, Comparer, MaxDepth);
  755. if (L + 1) < PivotIdx then
  756. R := PivotIdx - 1
  757. else
  758. exit;
  759. end;
  760. until L >= R;
  761. end;
  762. procedure IntroSort_PtrList_NoContext(ItemPtrs: PPointer; ItemCount: SizeUInt; Comparer: TListSortComparer_NoContext);
  763. begin
  764. if not Assigned(ItemPtrs) or (ItemCount < 2) then
  765. exit;
  766. IntroSort_PtrList_NoContext(ItemPtrs, 0, ItemCount - 1, Comparer, 2*IntLog2(ItemCount));
  767. end;
  768. procedure IntroSort_PtrList_Context(ItemPtrs: PPointer; ItemCount: SizeUInt; Comparer: TListSortComparer_Context; Context: Pointer);
  769. procedure IntroSort(L, R : SizeUInt; MaxDepth: Integer);
  770. var
  771. I, J, PivotIdx : SizeUInt;
  772. P, Q : Pointer;
  773. begin
  774. repeat
  775. if MaxDepth > 0 then
  776. Dec(MaxDepth)
  777. else
  778. begin
  779. HeapSort_PtrList_Context(@ItemPtrs[L], (R - L) + 1, Comparer, Context);
  780. exit;
  781. end;
  782. I := L;
  783. J := R;
  784. PivotIdx := L + ((R - L) shr 1); { same as ((L + R) div 2), but without the possibility of overflow }
  785. P := ItemPtrs[PivotIdx];
  786. repeat
  787. while (I < PivotIdx) and (Comparer(P, ItemPtrs[I], Context) > 0) do
  788. Inc(I);
  789. while (J > PivotIdx) and (Comparer(P, ItemPtrs[J], Context) < 0) do
  790. Dec(J);
  791. if I < J then
  792. begin
  793. Q := ItemPtrs[I];
  794. ItemPtrs[I] := ItemPtrs[J];
  795. ItemPtrs[J] := Q;
  796. if PivotIdx = I then
  797. begin
  798. PivotIdx := J;
  799. Inc(I);
  800. end
  801. else if PivotIdx = J then
  802. begin
  803. PivotIdx := I;
  804. Dec(J);
  805. end
  806. else
  807. begin
  808. Inc(I);
  809. Dec(J);
  810. end;
  811. end;
  812. until I >= J;
  813. // sort the smaller range recursively
  814. // sort the bigger range via the loop
  815. // Reasons: memory usage is O(log(n)) instead of O(n) and loop is faster than recursion
  816. if (PivotIdx - L) < (R - PivotIdx) then
  817. begin
  818. if (L + 1) < PivotIdx then
  819. IntroSort(L, PivotIdx - 1, MaxDepth);
  820. L := PivotIdx + 1;
  821. end
  822. else
  823. begin
  824. if (PivotIdx + 1) < R then
  825. IntroSort(PivotIdx + 1, R, MaxDepth);
  826. if (L + 1) < PivotIdx then
  827. R := PivotIdx - 1
  828. else
  829. exit;
  830. end;
  831. until L >= R;
  832. end;
  833. begin
  834. if not Assigned(ItemPtrs) or (ItemCount < 2) then
  835. exit;
  836. IntroSort(0, ItemCount - 1, 2*IntLog2(ItemCount));
  837. end;
  838. procedure IntroSort_ItemList_Context(Items: Pointer; ItemCount, ItemSize: SizeUInt; Comparer: TListSortComparer_Context; Context: Pointer);
  839. var
  840. TempBuf: Pointer;
  841. procedure IntroSort(L, R : SizeUInt; MaxDepth: Integer);
  842. var
  843. I, J, PivotIdx : SizeUInt;
  844. P : Pointer;
  845. begin
  846. repeat
  847. if MaxDepth > 0 then
  848. Dec(MaxDepth)
  849. else
  850. begin
  851. HeapSort_ItemList_Context(Items + ItemSize*L, (R - L) + 1, ItemSize, Comparer, Context);
  852. exit;
  853. end;
  854. I := L;
  855. J := R;
  856. PivotIdx := L + ((R - L) shr 1); { same as ((L + R) div 2), but without the possibility of overflow }
  857. P := Items + ItemSize*PivotIdx;
  858. repeat
  859. while (I < PivotIdx) and (Comparer(P, Items + ItemSize*I, Context) > 0) do
  860. Inc(I);
  861. while (J > PivotIdx) and (Comparer(P, Items + ItemSize*J, Context) < 0) do
  862. Dec(J);
  863. if I < J then
  864. begin
  865. Move((Items + ItemSize*I)^, TempBuf^, ItemSize);
  866. Move((Items + ItemSize*J)^, (Items + ItemSize*I)^, ItemSize);
  867. Move(TempBuf^, (Items + ItemSize*J)^, ItemSize);
  868. if PivotIdx = I then
  869. begin
  870. PivotIdx := J;
  871. P := Items + ItemSize*PivotIdx;
  872. Inc(I);
  873. end
  874. else if PivotIdx = J then
  875. begin
  876. PivotIdx := I;
  877. P := Items + ItemSize*PivotIdx;
  878. Dec(J);
  879. end
  880. else
  881. begin
  882. Inc(I);
  883. Dec(J);
  884. end;
  885. end;
  886. until I >= J;
  887. // sort the smaller range recursively
  888. // sort the bigger range via the loop
  889. // Reasons: memory usage is O(log(n)) instead of O(n) and loop is faster than recursion
  890. if (PivotIdx - L) < (R - PivotIdx) then
  891. begin
  892. if (L + 1) < PivotIdx then
  893. IntroSort(L, PivotIdx - 1, MaxDepth);
  894. L := PivotIdx + 1;
  895. end
  896. else
  897. begin
  898. if (PivotIdx + 1) < R then
  899. IntroSort(PivotIdx + 1, R, MaxDepth);
  900. if (L + 1) < PivotIdx then
  901. R := PivotIdx - 1
  902. else
  903. exit;
  904. end;
  905. until L >= R;
  906. end;
  907. begin
  908. if not Assigned(Items) or (ItemCount < 2) or (ItemSize < 1) then
  909. exit;
  910. GetMem(TempBuf, ItemSize);
  911. {$ifdef FPC_HAS_FEATURE_EXCEPTIONS}
  912. try
  913. IntroSort(0, ItemCount - 1, 2*IntLog2(ItemCount));
  914. finally
  915. FreeMem(TempBuf, ItemSize);
  916. end;
  917. {$else FPC_HAS_FEATURE_EXCEPTIONS}
  918. IntroSort(0, ItemCount - 1, 2*IntLog2(ItemCount));
  919. FreeMem(TempBuf, ItemSize);
  920. {$endif FPC_HAS_FEATURE_EXCEPTIONS}
  921. end;
  922. procedure IntroSort_ItemList_CustomItemExchanger_Context(
  923. Items: Pointer;
  924. ItemCount, ItemSize: SizeUInt;
  925. Comparer: TListSortComparer_Context;
  926. Exchanger: TListSortCustomItemExchanger_Context;
  927. Context: Pointer);
  928. procedure IntroSort(L, R : SizeUInt; MaxDepth: Integer);
  929. var
  930. I, J, PivotIdx : SizeUInt;
  931. P : Pointer;
  932. begin
  933. repeat
  934. if MaxDepth > 0 then
  935. Dec(MaxDepth)
  936. else
  937. begin
  938. HeapSort_ItemList_CustomItemExchanger_Context(Items + ItemSize*L, (R - L) + 1, ItemSize, Comparer, Exchanger, Context);
  939. exit;
  940. end;
  941. I := L;
  942. J := R;
  943. PivotIdx := L + ((R - L) shr 1); { same as ((L + R) div 2), but without the possibility of overflow }
  944. P := Items + ItemSize*PivotIdx;
  945. repeat
  946. while (I < PivotIdx) and (Comparer(P, Items + ItemSize*I, Context) > 0) do
  947. Inc(I);
  948. while (J > PivotIdx) and (Comparer(P, Items + ItemSize*J, Context) < 0) do
  949. Dec(J);
  950. if I < J then
  951. begin
  952. Exchanger(Items + ItemSize*I, Items + ItemSize*J, Context);
  953. if PivotIdx = I then
  954. begin
  955. PivotIdx := J;
  956. P := Items + ItemSize*PivotIdx;
  957. Inc(I);
  958. end
  959. else if PivotIdx = J then
  960. begin
  961. PivotIdx := I;
  962. P := Items + ItemSize*PivotIdx;
  963. Dec(J);
  964. end
  965. else
  966. begin
  967. Inc(I);
  968. Dec(J);
  969. end;
  970. end;
  971. until I >= J;
  972. // sort the smaller range recursively
  973. // sort the bigger range via the loop
  974. // Reasons: memory usage is O(log(n)) instead of O(n) and loop is faster than recursion
  975. if (PivotIdx - L) < (R - PivotIdx) then
  976. begin
  977. if (L + 1) < PivotIdx then
  978. IntroSort(L, PivotIdx - 1, MaxDepth);
  979. L := PivotIdx + 1;
  980. end
  981. else
  982. begin
  983. if (PivotIdx + 1) < R then
  984. IntroSort(PivotIdx + 1, R, MaxDepth);
  985. if (L + 1) < PivotIdx then
  986. R := PivotIdx - 1
  987. else
  988. exit;
  989. end;
  990. until L >= R;
  991. end;
  992. begin
  993. if not Assigned(Items) or (ItemCount < 2) or (ItemSize < 1) then
  994. exit;
  995. IntroSort(0, ItemCount - 1, 2*IntLog2(ItemCount));
  996. end;
  997. end.