grbtree.pas 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669
  1. { Red Black Tree implementation.
  2. Copyright (c) 2013 by Inoussa OUEDRAOGO
  3. Inspired by ideas of Julienne Walker
  4. see http://www.eternallyconfuzzled.com/tuts/datastructures/jsw_tut_bst1.aspx
  5. The source code is distributed under the Library GNU
  6. General Public License with the following modification:
  7. - object files and libraries linked into an application may be
  8. distributed without source code.
  9. If you didn't receive a copy of the file COPYING, contact:
  10. Free Software Foundation
  11. 675 Mass Ave
  12. Cambridge, MA 02139
  13. USA
  14. This program is distributed in the hope that it will be useful,
  15. but WITHOUT ANY WARRANTY; without even the implied warranty of
  16. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. }
  17. unit grbtree;
  18. {$ifdef FPC}
  19. {$mode delphi}
  20. {$H+}
  21. {$endif FPC}
  22. {$TYPEDADDRESS ON}
  23. {$define RB_DEBUG}
  24. interface
  25. const
  26. HEIGHT_LIMIT = 64;
  27. type
  28. {KCOMP = class
  29. public
  30. // Return
  31. // * if A>B then 1
  32. // * if A=B then 0
  33. // * if A<B then -1
  34. class function Compare(const A, B : TRBTreeNodeData) : Integer;
  35. end; }
  36. TRBTree<T, KCOMP> = class
  37. public type
  38. TRBTreeNodeData = T;
  39. PRBTreeNode = ^TRBTreeNode;
  40. PRBTreeAllocator = ^TRBTreeAllocator;
  41. TRBTreeNode = record
  42. Links : array[Boolean] of PRBTreeNode;
  43. Data : TRBTreeNodeData;
  44. Red : Boolean;
  45. end;
  46. TRBTreeNodeCreator = function(AContext : Pointer) : PRBTreeNode;
  47. TRBTreeNodeDestructor = procedure(ANode : PRBTreeNode; AContext : Pointer);
  48. TRBTreeAllocator = record
  49. CreateNode : TRBTreeNodeCreator;
  50. FreeNode : TRBTreeNodeDestructor;
  51. end;
  52. TRBTreeNodeComparator = KCOMP;
  53. ThisType = TRBTree<T,KCOMP>;
  54. private type
  55. TBaseIterator = record
  56. Tree : ThisType;
  57. StartingNode : PRBTreeNode;
  58. StartingDir : Boolean;
  59. Current : PRBTreeNode;
  60. Top : NativeInt;
  61. Path : array[0..(HEIGHT_LIMIT-1)] of PRBTreeNode;
  62. end;
  63. PBaseIterator = ^TBaseIterator;
  64. public type
  65. TIterator = class
  66. private
  67. FHandle : PBaseIterator;
  68. FResetState : Boolean;
  69. private
  70. public
  71. constructor Create(AHandle : PBaseIterator);
  72. destructor Destroy;override;
  73. procedure Reset();
  74. function MoveNext() : Boolean;inline;
  75. function MovePrevious() : Boolean;inline;
  76. function GetCurrent : TRBTreeNodeData;inline;
  77. function GetCurrentNode : PRBTreeNode;inline;
  78. end;
  79. public var
  80. Root : PRBTreeNode;
  81. //FSize : Integer;
  82. Allocator : TRBTreeAllocator;
  83. Comparator : TRBTreeNodeComparator;
  84. private
  85. class function TreeCreateIterator() : PBaseIterator;static;inline;
  86. class procedure TreeFreeIterator(AItem : PBaseIterator);static;inline;
  87. class procedure TreeInitIterator(
  88. AIterator : PBaseIterator;
  89. const ATree : ThisType;
  90. const AStartingNode : PRBTreeNode;
  91. const ADirection : Boolean
  92. );static;
  93. class function TreeIteratorMove(
  94. AIterator : PBaseIterator;
  95. ADirection : Boolean
  96. ) : PRBTreeNode;static;
  97. class function TreeIteratorMoveNext(AIterator : PBaseIterator) : PRBTreeNode;static;inline;
  98. class function TreeIteratorMovePrevious(AIterator : PBaseIterator) : PRBTreeNode;static;inline;
  99. function CreateIterator(
  100. const ANode : PRBTreeNode;
  101. const ADirection : Boolean
  102. ) : TIterator;inline;
  103. private
  104. class function DefaultCreateNode(AContext : Pointer) : PRBTreeNode;static;
  105. class procedure DefaultFreeNode(ANode : PRBTreeNode; AContext : Pointer);static;
  106. function InitNode(ANode : PRBTreeNode; AData : TRBTreeNodeData) : PRBTreeNode;inline;
  107. function IsRed(ANode : PRBTreeNode): Boolean;inline;
  108. function RotateDouble(ARoot : PRBTreeNode; const ADir : Boolean) : PRBTreeNode;inline;
  109. function RotateSingle(ARoot : PRBTreeNode; const ADir : Boolean) : PRBTreeNode;
  110. public
  111. constructor Create(const AAllocator : PRBTreeAllocator);overload;
  112. constructor Create();overload;
  113. destructor Destroy;override;
  114. procedure Clear();
  115. function FindNode(const AData : TRBTreeNodeData) : PRBTreeNode;
  116. function Insert(const AData : TRBTreeNodeData) : PRBTreeNode;
  117. function Remove(const AData : TRBTreeNodeData) : Boolean;
  118. function CreateForwardIterator(const ANode : PRBTreeNode) : TIterator;overload;inline;
  119. function CreateForwardIterator() : TIterator;overload;inline;
  120. function CreateBackwardIterator(const ANode : PRBTreeNode) : TIterator;overload;inline;
  121. function CreateBackwardIterator() : TIterator;overload;inline;
  122. {$ifdef RB_DEBUG}
  123. function SelfAssert(ARoot : PRBTreeNode; var AErrorMessage : string) : Boolean;overload;
  124. function SelfAssert(var AErrorMessage : string) : Boolean;overload;
  125. {$endif RB_DEBUG}
  126. end;
  127. TOrdinalComparator<T> = class
  128. public type
  129. TOrdinalType = T;
  130. public
  131. // Return
  132. // * if A>B then 1
  133. // * if A=B then 0
  134. // * if A<B then -1
  135. class function Compare(const A, B : TOrdinalType) : Integer;static;inline;
  136. end;
  137. implementation
  138. { TRBTree<T> }
  139. function TRBTree<T,KCOMP>.IsRed(ANode : PRBTreeNode): Boolean;inline;
  140. begin
  141. Result := (ANode <> nil) and ANode^.Red;
  142. end;
  143. function TRBTree<T,KCOMP>.InitNode(ANode: PRBTreeNode; AData: TRBTreeNodeData): PRBTreeNode;inline;
  144. begin
  145. Result := ANode;
  146. Result^.Data := AData;
  147. Result^.Red := True;
  148. Result^.Links[False] := nil;
  149. Result^.Links[True] := nil;
  150. end;
  151. function TRBTree<T,KCOMP>.RotateDouble(ARoot: PRBTreeNode; const ADir: Boolean): PRBTreeNode;inline;
  152. begin
  153. ARoot^.Links[not ADir] := RotateSingle(ARoot^.Links[not ADir], not ADir );
  154. Result := RotateSingle(ARoot,ADir);
  155. end;
  156. function TRBTree<T,KCOMP>.RotateSingle(ARoot: PRBTreeNode; const ADir: Boolean): PRBTreeNode;
  157. var
  158. t : PRBTreeNode;
  159. begin
  160. t := ARoot^.Links[not ADir];
  161. ARoot^.Links[not ADir] := t^.Links[ADir];
  162. t^.Links[ADir] := ARoot;
  163. ARoot^.Red := True;
  164. t^.Red := False;
  165. Result := t;
  166. end;
  167. class function TRBTree<T,KCOMP>.TreeCreateIterator() : PBaseIterator;static;
  168. begin
  169. Result := AllocMem(SizeOf(TBaseIterator));
  170. end;
  171. class procedure TRBTree<T,KCOMP>.TreeFreeIterator(AItem : PBaseIterator);static;
  172. begin
  173. if (AItem <> nil) then
  174. FreeMem(AItem,SizeOf(AItem^));
  175. end;
  176. class procedure TRBTree<T,KCOMP>.TreeInitIterator(
  177. AIterator : PBaseIterator;
  178. const ATree : ThisType;
  179. const AStartingNode : PRBTreeNode;
  180. const ADirection : Boolean
  181. );static;
  182. begin
  183. AIterator^.Tree := ATree;
  184. AIterator^.StartingNode := AStartingNode;
  185. AIterator^.StartingDir := ADirection;
  186. if (AStartingNode = nil) then
  187. AIterator^.Current := AIterator^.Tree.Root
  188. else
  189. AIterator^.Current := AStartingNode;
  190. AIterator^.Top := 0;
  191. // Save the path for later traversal
  192. if (AIterator^.Current <> nil) then begin
  193. while (AIterator^.Current^.Links[ADirection] <> nil) do begin
  194. AIterator^.Path[AIterator^.Top] := AIterator^.Current;
  195. Inc(AIterator^.Top);
  196. AIterator^.Current := AIterator^.Current^.Links[ADirection];
  197. end;
  198. end;
  199. end;
  200. class function TRBTree<T,KCOMP>.TreeIteratorMove(
  201. AIterator : PBaseIterator;
  202. ADirection : Boolean
  203. ) : PRBTreeNode;static;
  204. var
  205. last : PRBTreeNode;
  206. begin
  207. Result := nil;
  208. if (AIterator^.Current = nil) then
  209. exit;
  210. if (AIterator^.Current^.Links[ADirection] <> nil) then begin
  211. // Continue down this branch
  212. AIterator^.Path[AIterator^.Top] := AIterator^.Current;
  213. Inc(AIterator^.Top);
  214. AIterator^.Current := AIterator^.Current^.Links[ADirection];
  215. while ( AIterator^.Current^.Links[not ADirection] <> nil) do begin
  216. AIterator^.Path[AIterator^.Top] := AIterator^.Current;
  217. Inc(AIterator^.Top);
  218. AIterator^.Current := AIterator^.Current^.Links[not ADirection];
  219. end;
  220. end else begin
  221. // Move to the next branch
  222. repeat
  223. if (AIterator^.Top = 0) then begin
  224. AIterator^.Current := nil;
  225. break;
  226. end;
  227. last := AIterator^.Current;
  228. Dec(AIterator^.Top);
  229. AIterator^.Current := AIterator^.Path[AIterator^.Top];
  230. until (last <> AIterator^.Current^.Links[ADirection]);
  231. end;
  232. Result := AIterator^.Current;
  233. end;
  234. class function TRBTree<T,KCOMP>.TreeIteratorMoveNext(
  235. AIterator : PBaseIterator
  236. ) : PRBTreeNode;static;
  237. begin
  238. Result := TreeIteratorMove(AIterator,True);
  239. end;
  240. class function TRBTree<T,KCOMP>.TreeIteratorMovePrevious(
  241. AIterator : PBaseIterator
  242. ) : PRBTreeNode;static;
  243. begin
  244. Result := TreeIteratorMove(AIterator,False);
  245. end;
  246. function TRBTree<T,KCOMP>.CreateIterator(
  247. const ANode : PRBTreeNode;
  248. const ADirection : Boolean
  249. ) : TIterator;
  250. var
  251. h : PBaseIterator;
  252. begin
  253. h := TreeCreateIterator();
  254. TreeInitIterator(h,Self,ANode,ADirection);
  255. Result := TIterator.Create(h);
  256. end;
  257. class function TRBTree<T,KCOMP>.DefaultCreateNode(AContext: Pointer): PRBTreeNode;
  258. begin
  259. New(Result);
  260. end;
  261. class procedure TRBTree<T,KCOMP>.DefaultFreeNode(ANode: PRBTreeNode; AContext: Pointer);
  262. begin
  263. Dispose(ANode);
  264. end;
  265. constructor TRBTree<T,KCOMP>.Create(const AAllocator : PRBTreeAllocator);
  266. begin
  267. Root := nil;
  268. Allocator := AAllocator^;
  269. //Comparator := TRBTreeNodeComparator.Create();
  270. end;
  271. constructor TRBTree < T, KCOMP > .Create();
  272. var
  273. a : TRBTreeAllocator;
  274. begin
  275. a.CreateNode := TRBTreeNodeCreator(DefaultCreateNode);
  276. a.FreeNode := TRBTreeNodeDestructor(DefaultFreeNode);
  277. Create(@a);
  278. end;
  279. destructor TRBTree<T,KCOMP>.Destroy;
  280. begin
  281. Clear();
  282. //Comparator.Free();
  283. inherited;
  284. end;
  285. procedure TRBTree<T,KCOMP>.Clear();
  286. var
  287. it, save : PRBTreeNode;
  288. begin
  289. it := Root;
  290. while (it <> nil) do begin
  291. if (it^.Links[False] <> nil) then begin
  292. // Right rotation
  293. save := it^.Links[False];
  294. it^.Links[False] := save^.Links[True];
  295. save^.Links[True] := it;
  296. end else begin
  297. save := it^.Links[True];
  298. Allocator.FreeNode(it,Self);
  299. end;
  300. it := save;
  301. end;
  302. end;
  303. function TRBTree<T,KCOMP>.FindNode(const AData: TRBTreeNodeData): PRBTreeNode;
  304. var
  305. it : PRBTreeNode;
  306. cp : TRBTreeNodeComparator;
  307. dir : Boolean;
  308. begin
  309. Result := nil;
  310. it := Root;
  311. if (it = nil) then
  312. exit;
  313. cp := Comparator;
  314. while (it <> nil) do begin
  315. if (cp.Compare(it^.Data,AData) = 0) then begin
  316. Result := it;
  317. Break;
  318. end;
  319. dir := (cp.Compare(it^.Data,AData) < 0);
  320. it := it^.Links[dir];
  321. end;
  322. end;
  323. function TRBTree<T,KCOMP>.Insert(const AData: TRBTreeNodeData): PRBTreeNode;
  324. var
  325. head : TRBTreeNode;
  326. g, t : PRBTreeNode; // Grandparent & parent
  327. p, q : PRBTreeNode; // Iterator & parent
  328. dir, last, dir2 : Boolean;
  329. cp : TRBTreeNodeComparator;
  330. begin
  331. if (Root = nil) then begin
  332. // Empty tree case
  333. Root := InitNode(Allocator.CreateNode(Self),AData);
  334. Result := Root;
  335. end else begin
  336. FillChar(head,SizeOf(head),0); // False tree root
  337. dir := False;
  338. last := False;
  339. // Set up helpers
  340. t := @head;
  341. g := nil;
  342. p := nil;
  343. t^.Links[True] := Root;
  344. q := t^.Links[True];
  345. // Search down the tree
  346. cp := Comparator;
  347. while True do begin
  348. if (q = nil) then begin
  349. // Insert new node at the bottom
  350. q := InitNode(Allocator.CreateNode(Self),AData);
  351. p^.Links[dir] := q;
  352. end else if IsRed(q^.Links[False]) and IsRed(q^.Links[True]) then begin
  353. // Color flip
  354. q^.Red := True;
  355. q^.Links[False]^.Red := False;
  356. q^.Links[True]^.Red := False;
  357. end;
  358. // Fix red violation
  359. if IsRed(q) and IsRed(p) then begin
  360. dir2 := (t^.Links[True] = g);
  361. if (q = p^.Links[last]) then
  362. t^.Links[dir2] := RotateSingle(g, not last)
  363. else
  364. t^.Links[dir2] := RotateDouble(g, not last );
  365. end;
  366. // Stop if found
  367. if (cp.Compare(q^.Data,AData) = 0) then begin
  368. Result := q;
  369. break;
  370. end;
  371. last := dir;
  372. dir := (cp.Compare(q^.Data,AData) < 0);
  373. // Update helpers
  374. if (g <> nil) then
  375. t := g;
  376. g := p;
  377. p := q;
  378. q := q^.Links[dir];
  379. end;
  380. // Update root
  381. Root := head.Links[True];
  382. end;
  383. // Make root black
  384. Root^.Red := False;
  385. end;
  386. function TRBTree<T,KCOMP>.Remove(const AData: TRBTreeNodeData): Boolean;
  387. var
  388. head : TRBTreeNode;
  389. q, p, g, f, s : PRBTreeNode;
  390. dir, last, dir2 : Boolean;
  391. cp : TRBTreeNodeComparator;
  392. begin
  393. Result := False;
  394. if (Root = nil) then
  395. exit;
  396. FillChar(head,SizeOf(head),0); // False tree root
  397. f := nil;
  398. dir := True;
  399. // Set up helpers
  400. q := @head;
  401. p := nil;
  402. g := nil;
  403. q^.Links[True] := Root;
  404. // Search and push a red down
  405. cp := Comparator;
  406. while (q^.Links[dir] <> nil) do begin
  407. last := dir;
  408. // Update helpers
  409. g := p;
  410. p := q;
  411. q := q^.Links[dir];
  412. dir := (cp.Compare(q^.Data,AData) < 0);
  413. // Save found node
  414. if (cp.Compare(q^.Data,AData) = 0) then
  415. f := q;
  416. // Push the red node down
  417. if not(IsRed(q)) and not(IsRed(q^.Links[dir])) then begin
  418. if IsRed(q^.Links[not dir]) then begin
  419. p^.Links[last] := RotateSingle(q,dir);
  420. p := p^.Links[last];
  421. end else if not IsRed(q^.Links[not dir]) then begin
  422. s := p^.Links[not last];
  423. if (s <> nil) then begin
  424. if not(IsRed(s^.Links[not last])) and not(IsRed(s^.Links[last])) then begin
  425. // Color flip
  426. p^.Red := False;
  427. s^.Red := True;
  428. q^.Red := True;
  429. end else begin
  430. dir2 := (g^.Links[True] = p);
  431. if IsRed(s^.Links[last]) then
  432. g^.Links[dir2] := RotateDouble(p,last)
  433. else if IsRed(s^.Links[not last]) then
  434. g^.Links[dir2] := RotateSingle(p,last);
  435. // Ensure correct coloring
  436. g^.Links[dir2]^.Red := True;
  437. q^.Red := g^.Links[dir2]^.Red;
  438. g^.Links[dir2]^.Links[False]^.Red := False;
  439. g^.Links[dir2]^.Links[True]^.Red := False;
  440. end;
  441. end;
  442. end;
  443. end;
  444. end;
  445. // Replace and remove if found
  446. if (f <> nil) then begin
  447. f^.Data := q^.Data;
  448. p^.Links[(p^.Links[True] = q)] :=
  449. q^.Links[(q^.Links[False] = nil)];
  450. Allocator.FreeNode(q,Self);
  451. Result := True;
  452. end;
  453. // Update root and make it black
  454. Root := head.Links[True];
  455. if (Root <> nil) then
  456. Root^.Red := False;
  457. end;
  458. function TRBTree<T,KCOMP>.CreateForwardIterator(const ANode : PRBTreeNode) : TIterator;
  459. begin
  460. Result := CreateIterator(ANode,False);
  461. end;
  462. function TRBTree<T,KCOMP>.CreateForwardIterator() : TIterator;
  463. begin
  464. Result := CreateForwardIterator(Root);
  465. end;
  466. function TRBTree<T,KCOMP>.CreateBackwardIterator(const ANode : PRBTreeNode) : TIterator;
  467. begin
  468. Result := CreateIterator(ANode,True);
  469. end;
  470. function TRBTree<T,KCOMP>.CreateBackwardIterator() : TIterator;
  471. begin
  472. Result := CreateBackwardIterator(Root);
  473. end;
  474. {$ifdef RB_DEBUG}
  475. function TRBTree<T,KCOMP>.SelfAssert(ARoot : PRBTreeNode; var AErrorMessage: string): Boolean;
  476. var
  477. lh, rh : Boolean;
  478. ln, rn : PRBTreeNode;
  479. e : string;
  480. begin
  481. AErrorMessage := '';
  482. if (ARoot = nil) then begin
  483. Result := True;
  484. exit;
  485. end;
  486. e := '';
  487. ln := ARoot^.Links[False];
  488. rn := ARoot^.Links[True];
  489. // Consecutive red links
  490. if IsRed(ARoot) then begin
  491. if IsRed(ln) or IsRed(rn) then begin
  492. AErrorMessage := 'Red violation';
  493. Result := False;
  494. exit;
  495. end;
  496. end;
  497. lh := SelfAssert(ln,e);
  498. AErrorMessage := AErrorMessage + ' ' + e;
  499. rh := SelfAssert(rn,e);
  500. AErrorMessage := AErrorMessage + ' ' + e;
  501. // Invalid binary search tree
  502. if ( ( (ln <> nil) and (Comparator.Compare(ln^.Data,ARoot^.Data) >= 0) ) or
  503. ( (rn <> nil) and (Comparator.Compare(rn^.Data,ARoot^.Data) <= 0) ) )
  504. then begin
  505. AErrorMessage := AErrorMessage + ' ' + 'Binary tree violation';
  506. Result := False;
  507. Exit;
  508. end;
  509. // Black height mismatch
  510. if ( lh and rh and (lh <> rh) ) then begin
  511. AErrorMessage := AErrorMessage + ' ' + 'Black violation';
  512. Result := False;
  513. Exit;
  514. end;
  515. Result := lh and rh;
  516. end;
  517. function TRBTree<T,KCOMP>.SelfAssert(var AErrorMessage: string): Boolean;
  518. begin
  519. Result := Self.SelfAssert(Root, AErrorMessage);
  520. end;
  521. {$endif RB_DEBUG}
  522. constructor TRBTree<T,KCOMP>.TIterator.Create(AHandle : PBaseIterator);
  523. begin
  524. inherited Create();
  525. FHandle := AHandle;
  526. FResetState := True;
  527. end;
  528. destructor TRBTree<T,KCOMP>.TIterator.Destroy();
  529. begin
  530. TreeFreeIterator(FHandle);
  531. inherited Destroy;
  532. end;
  533. function TRBTree<T,KCOMP>.TIterator.MoveNext : Boolean;
  534. begin
  535. if FResetState then begin
  536. FResetState := False;
  537. Result := (FHandle^.Current <> nil);
  538. exit;
  539. end;
  540. Result := (TreeIteratorMoveNext(FHandle) <> nil);
  541. end;
  542. function TRBTree<T,KCOMP>.TIterator.MovePrevious : Boolean;
  543. begin
  544. if FResetState then begin
  545. FResetState := False;
  546. Result := (FHandle^.Current <> nil);
  547. exit;
  548. end;
  549. Result := (TreeIteratorMovePrevious(FHandle) <> nil);
  550. end;
  551. function TRBTree<T,KCOMP>.TIterator.GetCurrent : TRBTreeNodeData;
  552. begin
  553. Result := GetCurrentNode()^.Data;
  554. end;
  555. function TRBTree<T,KCOMP>.TIterator.GetCurrentNode : PRBTreeNode;
  556. begin
  557. Result := FHandle^.Current;
  558. end;
  559. procedure TRBTree<T,KCOMP>.TIterator.Reset();
  560. begin
  561. FResetState := True;
  562. TreeInitIterator(FHandle,FHandle^.Tree,FHandle^.StartingNode,FHandle^.StartingDir)
  563. end;
  564. { TOrdinalComparator<T> }
  565. class function TOrdinalComparator<T>.Compare(const A, B: TOrdinalType): Integer;
  566. begin
  567. if (A = B) then
  568. exit(0);
  569. if (A > B) then
  570. exit(1);
  571. exit(-1);
  572. end;
  573. end.