GLS.RedBlackTree.pas 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786
  1. //
  2. // This unit is part of the GLScene Engine, http://glscene.org
  3. //
  4. unit GLS.RedBlackTree;
  5. (*
  6. Black tree routines
  7. USAGE
  8. The TRedBlackTree generic class behaves somewhat like a TList:
  9. it stores _Value_ by _Key_
  10. and uses the same comparison function as TList.Sort (TListSortCompare).
  11. Functions Clear, Add, Delete, First and Last are equivalent,
  12. except that First and Last return a _Key_ so they
  13. can be used for comparisons in loops.
  14. All _Key_ occur only once in the tree if DuplicateKeys if False:
  15. when the same value is added twice, the second one is not stored.
  16. When DuplicateKeys is enabled the second comparison function is used
  17. for sort _Value_ and it duplicates not allowed.
  18. To be able to manage the tree, the Create constructor has a argument
  19. specifying the comparison function that should be used.
  20. The function Find can be used to find a _Value_ that was put in the tree,
  21. it searches for the given _Key_ using the comparison function given
  22. at time of object creation.
  23. The functions NextKey and PrevKey can be used to "walk" through the tree:
  24. given a _Key_, NextKey replace it with the smallest key that
  25. is larger than _Key_, PrevKey returns the largest key that is
  26. smaller than _Key_. For Last and First key result not returned.
  27. *)
  28. interface
  29. {$I GLScene.inc}
  30. uses
  31. System.Classes;
  32. type
  33. TRBColor = (clRed, clBlack);
  34. {$IFDEF USE_GENERIC_PREFIX}
  35. generic
  36. {$ENDIF}
  37. GRedBlackTree<TKey, TValue> = class public type TKeyCompareFunc =
  38. function(const Item1, Item2: TKey): Integer;
  39. TValueCompareFunc =
  40. function(const Item1, Item2: TValue): Boolean;
  41. TForEachProc =
  42. procedure(AKey: TKey; AValue: TValue; out AContinue: Boolean);
  43. TRBNode = class Key: TKey;
  44. Left, Right, Parent, Twin: TRBNode;
  45. Color: TRBColor;
  46. Value: TValue;
  47. end;
  48. var
  49. FRoot: TRBNode;
  50. FLeftmost: TRBNode;
  51. FRightmost: TRBNode;
  52. FLastFound: TRBNode;
  53. FLastNode: TRBNode;
  54. FCount: Integer;
  55. FKeyCompareFunc: TKeyCompareFunc;
  56. FDuplicateKeys: Boolean;
  57. FValueCompareFunc: TValueCompareFunc;
  58. FOnChange: TNotifyEvent;
  59. function FindNode(const Key: TKey): TRBNode;
  60. procedure RotateLeft(var x: TRBNode);
  61. procedure RotateRight(var x: TRBNode);
  62. function Minimum(var x: TRBNode): TRBNode;
  63. function Maximum(var x: TRBNode): TRBNode;
  64. function GetFirst: TKey;
  65. function GetLast: TKey;
  66. procedure SetDuplicateKeys(Value: Boolean);
  67. class procedure FastErase(x: TRBNode);
  68. public
  69. constructor Create(KeyCompare: TKeyCompareFunc;
  70. ValueCompare: TValueCompareFunc);
  71. destructor Destroy; override;
  72. procedure Clear;
  73. // Find value by key.
  74. function Find(const Key: TKey; out Value: TValue): Boolean;
  75. function NextKey(var Key: TKey; out Value: TValue): Boolean;
  76. function PrevKey(var Key: TKey; out Value: TValue): Boolean;
  77. function NextDublicate(out Value: TValue): Boolean;
  78. procedure Add(const Key: TKey; const Value: TValue);
  79. procedure Delete(const Key: TKey);
  80. procedure ForEach(AProc: TForEachProc);
  81. property Count: Integer read FCount;
  82. property First: TKey read GetFirst;
  83. property Last: TKey read GetLast;
  84. property DuplicateKeys: Boolean read FDuplicateKeys
  85. write SetDuplicateKeys;
  86. property OnChange: TNotifyEvent read FOnChange
  87. write FOnChange;
  88. end;
  89. function CompareInteger(const Item1, Item2: Integer): Integer;
  90. // --------------------------------------------------------------
  91. implementation
  92. // --------------------------------------------------------------
  93. function CompareInteger(const Item1, Item2: Integer): Integer;
  94. begin
  95. if Item1 < Item2 then
  96. begin
  97. Result := -1;
  98. end
  99. else if (Item1 = Item2) then
  100. begin
  101. Result := 0;
  102. end
  103. else
  104. begin
  105. Result := 1;
  106. end
  107. end;
  108. constructor GRedBlackTree<TKey, TValue>.Create(KeyCompare: TKeyCompareFunc;
  109. ValueCompare: TValueCompareFunc);
  110. begin
  111. inherited Create;
  112. Assert(Assigned(KeyCompare));
  113. FKeyCompareFunc := KeyCompare;
  114. FValueCompareFunc := ValueCompare;
  115. FRoot := nil;
  116. FLeftmost := nil;
  117. FRightmost := nil;
  118. FDuplicateKeys := Assigned(ValueCompare);
  119. end;
  120. destructor GRedBlackTree<TKey, TValue>.Destroy;
  121. begin
  122. Clear;
  123. inherited Destroy;
  124. end;
  125. class procedure GRedBlackTree<TKey, TValue>.FastErase(x: TRBNode);
  126. var
  127. y: TRBNode;
  128. begin
  129. if (x.Left <> nil) then
  130. FastErase(x.Left);
  131. if (x.Right <> nil) then
  132. FastErase(x.Right);
  133. repeat
  134. y := x;
  135. x := x.Twin;
  136. y.Destroy;
  137. until x = nil;
  138. end;
  139. procedure GRedBlackTree<TKey, TValue>.Clear;
  140. begin
  141. if (FRoot <> nil) then
  142. FastErase(FRoot);
  143. FRoot := nil;
  144. FLeftmost := nil;
  145. FRightmost := nil;
  146. FCount := 0;
  147. if Assigned(FOnChange) then
  148. FOnChange(Self);
  149. end;
  150. function GRedBlackTree<TKey, TValue>.Find(const Key: TKey;
  151. out Value: TValue): Boolean;
  152. begin
  153. FLastFound := FindNode(Key);
  154. Result := Assigned(FLastFound);
  155. if Result then
  156. Value := FLastFound.Value;
  157. end;
  158. function GRedBlackTree<TKey, TValue>.FindNode(const Key: TKey): TRBNode;
  159. var
  160. cmp: Integer;
  161. begin
  162. Result := FRoot;
  163. while (Result <> nil) do
  164. begin
  165. cmp := FKeyCompareFunc(Result.Key, Key);
  166. if cmp < 0 then
  167. begin
  168. Result := Result.Right;
  169. end
  170. else if cmp > 0 then
  171. begin
  172. Result := Result.Left;
  173. end
  174. else
  175. begin
  176. break;
  177. end;
  178. end;
  179. end;
  180. function GRedBlackTree<TKey, TValue>.NextDublicate(out Value: TValue): Boolean;
  181. begin
  182. if Assigned(FLastFound) then
  183. begin
  184. if Assigned(FLastFound.Twin) then
  185. begin
  186. FLastFound := FLastFound.Twin;
  187. Value := FLastFound.Value;
  188. exit(True);
  189. end;
  190. end;
  191. Result := False;
  192. end;
  193. procedure GRedBlackTree<TKey, TValue>.RotateLeft(var x: TRBNode);
  194. var
  195. y: TRBNode;
  196. begin
  197. y := x.Right;
  198. x.Right := y.Left;
  199. if (y.Left <> nil) then
  200. begin
  201. y.Left.Parent := x;
  202. end;
  203. y.Parent := x.Parent;
  204. if (x = FRoot) then
  205. begin
  206. FRoot := y;
  207. end
  208. else if (x = x.Parent.Left) then
  209. begin
  210. x.Parent.Left := y;
  211. end
  212. else
  213. begin
  214. x.Parent.Right := y;
  215. end;
  216. y.Left := x;
  217. x.Parent := y;
  218. end;
  219. procedure GRedBlackTree<TKey, TValue>.RotateRight(var x: TRBNode);
  220. var
  221. y: TRBNode;
  222. begin
  223. y := x.Left;
  224. x.Left := y.Right;
  225. if (y.Right <> nil) then
  226. begin
  227. y.Right.Parent := x;
  228. end;
  229. y.Parent := x.Parent;
  230. if (x = FRoot) then
  231. begin
  232. FRoot := y;
  233. end
  234. else if (x = x.Parent.Right) then
  235. begin
  236. x.Parent.Right := y;
  237. end
  238. else
  239. begin
  240. x.Parent.Left := y;
  241. end;
  242. y.Right := x;
  243. x.Parent := y;
  244. end;
  245. function GRedBlackTree<TKey, TValue>.Minimum(var x: TRBNode): TRBNode;
  246. begin
  247. Result := x;
  248. while (Result.Left <> nil) do
  249. Result := Result.Left;
  250. end;
  251. function GRedBlackTree<TKey, TValue>.Maximum(var x: TRBNode): TRBNode;
  252. begin
  253. Result := x;
  254. while (Result.Right <> nil) do
  255. Result := Result.Right;
  256. end;
  257. procedure GRedBlackTree<TKey, TValue>.Add(const Key: TKey; const Value: TValue);
  258. var
  259. x, y, z, zpp: TRBNode;
  260. cmp: Integer;
  261. begin
  262. z := TRBNode.Create;
  263. { Initialize fields in new node z }
  264. z.Key := Key;
  265. z.Left := nil;
  266. z.Right := nil;
  267. z.Color := clRed;
  268. z.Value := Value;
  269. z.Twin := nil;
  270. { Maintain FLeftmost and FRightmost nodes }
  271. if ((FLeftmost = nil) or (FKeyCompareFunc(Key, FLeftmost.Key) < 0)) then
  272. begin
  273. FLeftmost := z;
  274. end;
  275. if ((FRightmost = nil) or (FKeyCompareFunc(FRightmost.Key, Key) < 0)) then
  276. begin
  277. FRightmost := z;
  278. end;
  279. { Insert node z }
  280. y := nil;
  281. x := FRoot;
  282. while (x <> nil) do
  283. begin
  284. y := x;
  285. cmp := FKeyCompareFunc(Key, x.Key);
  286. if cmp < 0 then
  287. x := x.Left
  288. else if cmp > 0 then
  289. x := x.Right
  290. else
  291. begin
  292. { Key already exists in tree. }
  293. if FDuplicateKeys then
  294. begin
  295. { Check twins chain for value dublicate. }
  296. repeat
  297. if FValueCompareFunc(Value, x.Value) then
  298. begin
  299. y := nil;
  300. break;
  301. end;
  302. y := x;
  303. x := x.Twin;
  304. until x = nil;
  305. if Assigned(y) then
  306. begin
  307. { Add dublicate key to end of twins chain. }
  308. y.Twin := z;
  309. Inc(FCount);
  310. if Assigned(FOnChange) then
  311. FOnChange(Self);
  312. exit;
  313. end;
  314. { Value already exists in tree. }
  315. end;
  316. z.Destroy;
  317. // a jzombi: memory leak: if we don't put it in the tree, we shouldn't hold it in the memory
  318. exit;
  319. end;
  320. end;
  321. z.Parent := y;
  322. if (y = nil) then
  323. begin
  324. FRoot := z;
  325. end
  326. else if (FKeyCompareFunc(Key, y.Key) < 0) then
  327. begin
  328. y.Left := z;
  329. end
  330. else
  331. begin
  332. y.Right := z;
  333. end;
  334. { Rebalance tree }
  335. while ((z <> FRoot) and (z.Parent.Color = clRed)) do
  336. begin
  337. zpp := z.Parent.Parent;
  338. if (z.Parent = zpp.Left) then
  339. begin
  340. y := zpp.Right;
  341. if ((y <> nil) and (y.Color = clRed)) then
  342. begin
  343. z.Parent.Color := clBlack;
  344. y.Color := clBlack;
  345. zpp.Color := clRed;
  346. z := zpp;
  347. end
  348. else
  349. begin
  350. if (z = z.Parent.Right) then
  351. begin
  352. z := z.Parent;
  353. RotateLeft(z);
  354. end;
  355. z.Parent.Color := clBlack;
  356. zpp.Color := clRed;
  357. RotateRight(zpp);
  358. end;
  359. end
  360. else
  361. begin
  362. y := zpp.Left;
  363. if ((y <> nil) and (y.Color = clRed)) then
  364. begin
  365. z.Parent.Color := clBlack;
  366. y.Color := clBlack;
  367. zpp.Color := clRed; // c jzombi: zpp.color := clRed;
  368. z := zpp;
  369. end
  370. else
  371. begin
  372. if (z = z.Parent.Left) then
  373. begin
  374. z := z.Parent;
  375. RotateRight(z);
  376. end;
  377. z.Parent.Color := clBlack;
  378. zpp.Color := clRed; // c jzombi: zpp.color := clRed;
  379. RotateLeft(zpp);
  380. end;
  381. end;
  382. end;
  383. FRoot.Color := clBlack;
  384. Inc(FCount);
  385. if Assigned(FOnChange) then
  386. FOnChange(Self);
  387. end;
  388. procedure GRedBlackTree<TKey, TValue>.Delete(const Key: TKey);
  389. var
  390. w, x, y, z, x_parent: TRBNode;
  391. tmpcol: TRBColor;
  392. begin
  393. z := FindNode(Key);
  394. if z = nil then
  395. exit;
  396. y := z;
  397. x := nil;
  398. x_parent := nil;
  399. if (y.Left = nil) then
  400. begin { z has at most one non-null child. y = z. }
  401. x := y.Right; { x might be null. }
  402. end
  403. else
  404. begin
  405. if (y.Right = nil) then
  406. begin { z has exactly one non-null child. y = z. }
  407. x := y.Left; { x is not null. }
  408. end
  409. else
  410. begin
  411. { z has two non-null children. Set y to }
  412. y := y.Right; { z's successor. x might be null. }
  413. while (y.Left <> nil) do
  414. begin
  415. y := y.Left;
  416. end;
  417. x := y.Right;
  418. end;
  419. end;
  420. if (y <> z) then
  421. begin
  422. { "copy y's sattelite data into z" }
  423. { relink y in place of z. y is z's successor }
  424. z.Left.Parent := y;
  425. y.Left := z.Left;
  426. if (y <> z.Right) then
  427. begin
  428. x_parent := y.Parent;
  429. if (x <> nil) then
  430. begin
  431. x.Parent := y.Parent;
  432. end;
  433. y.Parent.Left := x; { y must be a child of left }
  434. y.Right := z.Right;
  435. z.Right.Parent := y;
  436. end
  437. else
  438. begin
  439. x_parent := y;
  440. end;
  441. if (FRoot = z) then
  442. begin
  443. FRoot := y;
  444. end
  445. else if (z.Parent.Left = z) then
  446. begin
  447. z.Parent.Left := y;
  448. end
  449. else
  450. begin
  451. z.Parent.Right := y;
  452. end;
  453. y.Parent := z.Parent;
  454. tmpcol := y.Color;
  455. y.Color := z.Color;
  456. z.Color := tmpcol;
  457. y := z;
  458. { y now points to node to be actually deleted }
  459. end
  460. else
  461. begin { y = z }
  462. x_parent := y.Parent;
  463. if (x <> nil) then
  464. begin
  465. x.Parent := y.Parent;
  466. end;
  467. if (FRoot = z) then
  468. begin
  469. FRoot := x;
  470. end
  471. else
  472. begin
  473. if (z.Parent.Left = z) then
  474. begin
  475. z.Parent.Left := x;
  476. end
  477. else
  478. begin
  479. z.Parent.Right := x;
  480. end;
  481. end;
  482. if (FLeftmost = z) then
  483. begin
  484. if (z.Right = nil) then
  485. begin { z.left must be null also }
  486. FLeftmost := z.Parent;
  487. end
  488. else
  489. begin
  490. FLeftmost := Minimum(x);
  491. end;
  492. end;
  493. if (FRightmost = z) then
  494. begin
  495. if (z.Left = nil) then
  496. begin { z.right must be null also }
  497. FRightmost := z.Parent;
  498. end
  499. else
  500. begin { x == z.left }
  501. FRightmost := Maximum(x);
  502. end;
  503. end;
  504. end;
  505. { Rebalance tree }
  506. if (y.Color = clBlack) then
  507. begin
  508. while ((x <> FRoot) and ((x = nil) or (x.Color = clBlack))) do
  509. begin
  510. if (x = x_parent.Left) then
  511. begin
  512. w := x_parent.Right;
  513. if (w.Color = clRed) then
  514. begin
  515. w.Color := clBlack;
  516. x_parent.Color := clRed;
  517. RotateLeft(x_parent);
  518. w := x_parent.Right;
  519. end;
  520. if (((w.Left = nil) or (w.Left.Color = clBlack)) and
  521. ((w.Right = nil) or (w.Right.Color = clBlack))) then
  522. begin
  523. w.Color := clRed;
  524. x := x_parent;
  525. x_parent := x_parent.Parent;
  526. end
  527. else
  528. begin
  529. if ((w.Right = nil) or (w.Right.Color = clBlack)) then
  530. begin
  531. w.Left.Color := clBlack;
  532. w.Color := clRed;
  533. RotateRight(w);
  534. w := x_parent.Right;
  535. end;
  536. w.Color := x_parent.Color;
  537. x_parent.Color := clBlack;
  538. if (w.Right <> nil) then
  539. begin
  540. w.Right.Color := clBlack;
  541. end;
  542. RotateLeft(x_parent);
  543. x := FRoot; { break; }
  544. end
  545. end
  546. else
  547. begin
  548. { same as above, with right <. left. }
  549. w := x_parent.Left;
  550. if (w.Color = clRed) then
  551. begin
  552. w.Color := clBlack;
  553. x_parent.Color := clRed;
  554. RotateRight(x_parent);
  555. w := x_parent.Left;
  556. end;
  557. if (((w.Right = nil) or (w.Right.Color = clBlack)) and
  558. ((w.Left = nil) or (w.Left.Color = clBlack))) then
  559. begin
  560. w.Color := clRed;
  561. x := x_parent;
  562. x_parent := x_parent.Parent;
  563. end
  564. else
  565. begin
  566. if ((w.Left = nil) or (w.Left.Color = clBlack)) then
  567. begin
  568. w.Right.Color := clBlack;
  569. w.Color := clRed;
  570. RotateLeft(w);
  571. w := x_parent.Left;
  572. end;
  573. w.Color := x_parent.Color;
  574. x_parent.Color := clBlack;
  575. if (w.Left <> nil) then
  576. begin
  577. w.Left.Color := clBlack;
  578. end;
  579. RotateRight(x_parent);
  580. x := FRoot; { break; }
  581. end;
  582. end;
  583. end;
  584. if (x <> nil) then
  585. begin
  586. x.Color := clBlack;
  587. end;
  588. end;
  589. while Assigned(y.Twin) do
  590. begin
  591. z := y;
  592. y := y.Twin;
  593. z.Destroy;
  594. end;
  595. y.Destroy;
  596. Dec(FCount);
  597. if Assigned(FOnChange) then
  598. FOnChange(Self);
  599. end;
  600. function GRedBlackTree<TKey, TValue>.NextKey(var Key: TKey;
  601. out Value: TValue): Boolean;
  602. var
  603. x, y: TRBNode;
  604. begin
  605. if Assigned(FLastNode) and (FKeyCompareFunc(FLastNode.Key, Key) = 0) then
  606. x := FLastNode
  607. else
  608. x := FindNode(Key);
  609. if x = nil then
  610. exit;
  611. if (x.Right <> nil) then
  612. begin
  613. x := x.Right;
  614. while (x.Left <> nil) do
  615. begin
  616. x := x.Left;
  617. end;
  618. end
  619. else if (x.Parent <> nil) then
  620. begin
  621. y := x.Parent;
  622. while Assigned(y) and (x = y.Right) do
  623. begin
  624. x := y;
  625. y := y.Parent;
  626. end;
  627. if (x.Right <> y) then
  628. x := y;
  629. end
  630. else
  631. x := FRoot;
  632. if x = nil then
  633. exit(False);
  634. Key := x.Key;
  635. FLastNode := x;
  636. Value := x.Value;
  637. Result := True;
  638. end;
  639. function GRedBlackTree<TKey, TValue>.PrevKey(var Key: TKey;
  640. out Value: TValue): Boolean;
  641. var
  642. x, y: TRBNode;
  643. begin
  644. if Assigned(FLastNode) and (FKeyCompareFunc(FLastNode.Key, Key) = 0) then
  645. x := FLastNode
  646. else
  647. x := FindNode(Key);
  648. if x = nil then
  649. exit(False);
  650. if (x.Left <> nil) then
  651. begin
  652. y := x.Left;
  653. while (y.Right <> nil) do
  654. begin
  655. y := y.Right;
  656. end;
  657. x := y;
  658. end
  659. else if (x.Parent <> nil) then
  660. begin
  661. y := x.Parent;
  662. while (x = y.Left) do
  663. begin
  664. x := y;
  665. y := y.Parent;
  666. end;
  667. x := y;
  668. end
  669. else
  670. x := FRoot;
  671. if x = nil then
  672. exit(False);
  673. Key := x.Key;
  674. FLastNode := x;
  675. Value := x.Value;
  676. Result := True;
  677. end;
  678. function GRedBlackTree<TKey, TValue>.GetFirst: TKey;
  679. begin
  680. Result := FLeftmost.Key;
  681. end;
  682. function GRedBlackTree<TKey, TValue>.GetLast: TKey;
  683. begin
  684. Result := FRightmost.Key;
  685. end;
  686. procedure GRedBlackTree<TKey, TValue>.ForEach(AProc: TForEachProc);
  687. var
  688. x, y, z: TRBNode;
  689. cont: Boolean;
  690. begin
  691. if Assigned(FLeftmost) then
  692. begin
  693. x := FLeftmost;
  694. repeat
  695. z := x;
  696. repeat
  697. AProc(z.Key, z.Value, cont);
  698. if not cont then
  699. exit;
  700. z := z.Twin;
  701. until z = nil;
  702. // Next node
  703. if (x.Right <> nil) then
  704. begin
  705. x := x.Right;
  706. while (x.Left <> nil) do
  707. begin
  708. x := x.Left;
  709. end;
  710. end
  711. else if (x.Parent <> nil) then
  712. begin
  713. y := x.Parent;
  714. while (x = y.Right) do
  715. begin
  716. x := y;
  717. y := y.Parent;
  718. end;
  719. if (x.Right <> y) then
  720. x := y;
  721. end
  722. else
  723. x := FRoot;
  724. until x = FRightmost;
  725. if cont and (FLeftmost <> FRightmost) then
  726. AProc(FRightmost.Key, FRightmost.Value, cont);
  727. end;
  728. end;
  729. procedure GRedBlackTree<TKey, TValue>.SetDuplicateKeys(Value: Boolean);
  730. begin
  731. if Value and Assigned(FValueCompareFunc) then
  732. FDuplicateKeys := True
  733. else
  734. FDuplicateKeys := False;
  735. end;
  736. end.