mirror of
https://gitlab.com/freepascal.org/lazarus/lazarus.git
synced 2025-05-11 18:42:37 +02:00
4159 lines
108 KiB
ObjectPascal
4159 lines
108 KiB
ObjectPascal
{
|
|
This file is part of the Free Pascal run time library.
|
|
Copyright (c) 2014 by Maciej Izak (hnb)
|
|
member of the Free Sparta development team (http://freesparta.com)
|
|
|
|
Copyright(c) 2004-2014 DaThoX
|
|
|
|
It contains the Free Pascal generics library
|
|
|
|
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.
|
|
|
|
Acknowledgment
|
|
|
|
Thanks to Sphere 10 Software (http://sphere10.com) for sponsoring
|
|
many new types and major refactoring of entire library
|
|
|
|
Thanks to mORMot (http://synopse.info) project for the best implementations
|
|
of hashing functions like crc32c and xxHash32 :)
|
|
|
|
**********************************************************************}
|
|
|
|
unit Generics.Collections;
|
|
|
|
{$MODE DELPHI}{$H+}
|
|
{$MACRO ON}
|
|
{$COPERATORS ON}
|
|
{$DEFINE CUSTOM_DICTIONARY_CONSTRAINTS := TKey, TValue, THashFactory}
|
|
{$DEFINE OPEN_ADDRESSING_CONSTRAINTS := TKey, TValue, THashFactory, TProbeSequence}
|
|
{$DEFINE CUCKOO_CONSTRAINTS := TKey, TValue, THashFactory, TCuckooCfg}
|
|
{$DEFINE TREE_CONSTRAINTS := TKey, TValue, TInfo}
|
|
{$WARNINGS OFF}
|
|
{$HINTS OFF}
|
|
{$OVERFLOWCHECKS OFF}
|
|
{$RANGECHECKS OFF}
|
|
|
|
interface
|
|
|
|
uses
|
|
RtlConsts, Classes, SysUtils, Generics.MemoryExpanders, Generics.Defaults,
|
|
Generics.Helpers, Generics.Strings;
|
|
|
|
{ FPC BUGS related to Generics.* (54 bugs, 19 fixed)
|
|
REGRESSION: 26483, 26481
|
|
FIXED REGRESSION: 26480, 26482
|
|
|
|
CRITICAL: 24848(!!!), 24872(!), 25607(!), 26030, 25917, 25918, 25620, 24283, 24254, 24287 (Related to? 24872)
|
|
IMPORTANT: 23862(!), 24097, 24285, 24286 (Similar to? 24285), 24098, 24609 (RTL inconsistency), 24534,
|
|
25606, 25614, 26177, 26195
|
|
OTHER: 26484, 24073, 24463, 25593, 25596, 25597, 25602, 26181 (or MYBAD?)
|
|
CLOSED BUT IMO STILL TO FIX: 25601(!), 25594
|
|
FIXED: 25610(!), 24064, 24071, 24282, 24458, 24867, 24871, 25604, 25600, 25605, 25598, 25603, 25929, 26176, 26180,
|
|
26193, 24072
|
|
MYBAD: 24963, 25599
|
|
}
|
|
|
|
{ LAZARUS BUGS related to Generics.* (7 bugs, 0 fixed)
|
|
CRITICAL: 25613
|
|
OTHER: 25595, 25612, 25615, 25617, 25618, 25619
|
|
}
|
|
|
|
{.$define EXTRA_WARNINGS}
|
|
{.$define ENABLE_METHODS_WITH_TEnumerableWithPointers}
|
|
|
|
type
|
|
EAVLTree = class(Exception);
|
|
EIndexedAVLTree = class(EAVLTree);
|
|
|
|
TDuplicates = Classes.TDuplicates;
|
|
|
|
{$ifdef VER3_0_0}
|
|
TArray<T> = array of T;
|
|
{$endif}
|
|
|
|
// bug #24254 workaround
|
|
// should be TArray = record class procedure Sort<T>(...) etc.
|
|
TBinarySearchResult = record
|
|
FoundIndex, CandidateIndex: SizeInt;
|
|
CompareResult: SizeInt;
|
|
end;
|
|
|
|
TCustomArrayHelper<T> = class abstract
|
|
private
|
|
type
|
|
// bug #24282
|
|
TComparerBugHack = TComparer<T>;
|
|
protected
|
|
// modified QuickSort from classes\lists.inc
|
|
class procedure QuickSort(var AValues: array of T; ALeft, ARight: SizeInt; const AComparer: IComparer<T>);
|
|
virtual; abstract;
|
|
public
|
|
class procedure Sort(var AValues: array of T); overload;
|
|
class procedure Sort(var AValues: array of T;
|
|
const AComparer: IComparer<T>); overload;
|
|
class procedure Sort(var AValues: array of T;
|
|
const AComparer: IComparer<T>; AIndex, ACount: SizeInt); overload;
|
|
|
|
class function BinarySearch(constref AValues: array of T; constref AItem: T;
|
|
out ASearchResult: TBinarySearchResult; const AComparer: IComparer<T>;
|
|
AIndex, ACount: SizeInt): Boolean; virtual; abstract; overload;
|
|
class function BinarySearch(constref AValues: array of T; constref AItem: T;
|
|
out AFoundIndex: SizeInt; const AComparer: IComparer<T>;
|
|
AIndex, ACount: SizeInt): Boolean; virtual; abstract; overload;
|
|
class function BinarySearch(constref AValues: array of T; constref AItem: T;
|
|
out AFoundIndex: SizeInt; const AComparer: IComparer<T>): Boolean; overload;
|
|
class function BinarySearch(constref AValues: array of T; constref AItem: T;
|
|
out AFoundIndex: SizeInt): Boolean; overload;
|
|
class function BinarySearch(constref AValues: array of T; constref AItem: T;
|
|
out ASearchResult: TBinarySearchResult; const AComparer: IComparer<T>): Boolean; overload;
|
|
class function BinarySearch(constref AValues: array of T; constref AItem: T;
|
|
out ASearchResult: TBinarySearchResult): Boolean; overload;
|
|
end {$ifdef EXTRA_WARNINGS}experimental{$endif}; // will be renamed to TCustomArray (bug #24254)
|
|
|
|
TArrayHelper<T> = class(TCustomArrayHelper<T>)
|
|
protected
|
|
// modified QuickSort from classes\lists.inc
|
|
class procedure QuickSort(var AValues: array of T; ALeft, ARight: SizeInt; const AComparer: IComparer<T>); override;
|
|
public
|
|
class function BinarySearch(constref AValues: array of T; constref AItem: T;
|
|
out ASearchResult: TBinarySearchResult; const AComparer: IComparer<T>;
|
|
AIndex, ACount: SizeInt): Boolean; override; overload;
|
|
class function BinarySearch(constref AValues: array of T; constref AItem: T;
|
|
out AFoundIndex: SizeInt; const AComparer: IComparer<T>;
|
|
AIndex, ACount: SizeInt): Boolean; override; overload;
|
|
end {$ifdef EXTRA_WARNINGS}experimental{$endif}; // will be renamed to TArray (bug #24254)
|
|
|
|
TCollectionNotification = (cnAdded, cnRemoved, cnExtracted);
|
|
TCollectionNotifyEvent<T> = procedure(ASender: TObject; constref AItem: T; AAction: TCollectionNotification)
|
|
of object;
|
|
|
|
{ TEnumerator }
|
|
|
|
TEnumerator<T> = class abstract
|
|
protected
|
|
function DoGetCurrent: T; virtual; abstract;
|
|
function DoMoveNext: boolean; virtual; abstract;
|
|
public
|
|
property Current: T read DoGetCurrent;
|
|
function MoveNext: boolean;
|
|
end;
|
|
|
|
{ TEnumerable }
|
|
|
|
TEnumerable<T> = class abstract
|
|
public type
|
|
PT = ^T;
|
|
protected // no forward generics declarations (needed by TPointersCollection<T, PT>), this should be moved into TEnumerableWithPointers
|
|
function GetPtrEnumerator: TEnumerator<PT>; virtual; abstract;
|
|
protected
|
|
function ToArrayImpl(ACount: SizeInt): TArray<T>; overload; // used by descendants
|
|
protected
|
|
function DoGetEnumerator: TEnumerator<T>; virtual; abstract;
|
|
public
|
|
function GetEnumerator: TEnumerator<T>; inline;
|
|
function ToArray: TArray<T>; virtual; overload;
|
|
end;
|
|
|
|
// error: no memory left for TCustomPointersEnumerator<PT> version
|
|
TCustomPointersEnumerator<T, PT> = class abstract(TEnumerator<PT>);
|
|
|
|
TCustomPointersCollection<T, PT> = object
|
|
strict private type
|
|
TLocalEnumerable = TEnumerable<T>; // compiler has bug for directly usage of TEnumerable<T>
|
|
protected
|
|
function Enumerable: TLocalEnumerable; inline;
|
|
public
|
|
function GetEnumerator: TEnumerator<PT>;
|
|
end;
|
|
|
|
TEnumerableWithPointers<T> = class(TEnumerable<T>)
|
|
strict private type
|
|
TPointersCollection = TCustomPointersCollection<T, PT>;
|
|
PPointersCollection = ^TPointersCollection;
|
|
private
|
|
function GetPtr: PPointersCollection; inline;
|
|
public
|
|
property Ptr: PPointersCollection read GetPtr;
|
|
end;
|
|
|
|
// More info: http://stackoverflow.com/questions/5232198/about-vectors-growth
|
|
// TODO: custom memory managers (as constraints)
|
|
{$DEFINE CUSTOM_LIST_CAPACITY_INC := Result + Result div 2} // ~approximation to golden ratio: n = n * 1.5 }
|
|
// {$DEFINE CUSTOM_LIST_CAPACITY_INC := Result * 2} // standard inc
|
|
TCustomList<T> = class abstract(TEnumerableWithPointers<T>)
|
|
public type
|
|
PT = ^T;
|
|
protected
|
|
type // bug #24282
|
|
TArrayHelperBugHack = TArrayHelper<T>;
|
|
private
|
|
FOnNotify: TCollectionNotifyEvent<T>;
|
|
function GetCapacity: SizeInt; inline;
|
|
protected
|
|
FLength: SizeInt;
|
|
FItems: array of T;
|
|
|
|
function PrepareAddingItem: SizeInt; virtual;
|
|
function PrepareAddingRange(ACount: SizeInt): SizeInt; virtual;
|
|
procedure Notify(constref AValue: T; ACollectionNotification: TCollectionNotification); virtual;
|
|
function DoRemove(AIndex: SizeInt; ACollectionNotification: TCollectionNotification): T; virtual;
|
|
procedure SetCapacity(AValue: SizeInt); virtual; abstract;
|
|
function GetCount: SizeInt; virtual;
|
|
public
|
|
function ToArray: TArray<T>; override; final;
|
|
|
|
property Count: SizeInt read GetCount;
|
|
property Capacity: SizeInt read GetCapacity write SetCapacity;
|
|
property OnNotify: TCollectionNotifyEvent<T> read FOnNotify write FOnNotify;
|
|
|
|
procedure TrimExcess; virtual; abstract;
|
|
end;
|
|
|
|
TCustomListEnumerator<T> = class abstract(TEnumerator<T>)
|
|
private
|
|
FList: TCustomList<T>;
|
|
FIndex: SizeInt;
|
|
protected
|
|
function DoMoveNext: boolean; override;
|
|
function DoGetCurrent: T; override;
|
|
function GetCurrent: T; virtual;
|
|
public
|
|
constructor Create(AList: TCustomList<T>);
|
|
end;
|
|
|
|
TCustomListWithPointers<T> = class(TCustomList<T>)
|
|
public type
|
|
TPointersEnumerator = class(TCustomPointersEnumerator<T, PT>)
|
|
protected
|
|
FList: TCustomListWithPointers<T>;
|
|
FIndex: SizeInt;
|
|
function DoMoveNext: boolean; override;
|
|
function DoGetCurrent: PT; override;
|
|
public
|
|
constructor Create(AList: TCustomListWithPointers<T>);
|
|
end;
|
|
protected
|
|
function GetPtrEnumerator: TEnumerator<PT>; override;
|
|
end;
|
|
|
|
TList<T> = class(TCustomListWithPointers<T>)
|
|
private var
|
|
FComparer: IComparer<T>;
|
|
protected
|
|
// bug #24287 - workaround for generics type name conflict (Identifier not found)
|
|
// next bug workaround - for another error related to previous workaround
|
|
// change order (method must be declared before TEnumerator declaration)
|
|
function DoGetEnumerator: {Generics.Collections.}TEnumerator<T>; override;
|
|
public
|
|
// with this type declaration i found #24285, #24285
|
|
type
|
|
// bug workaround
|
|
TEnumerator = class(TCustomListEnumerator<T>);
|
|
|
|
function GetEnumerator: TEnumerator; reintroduce;
|
|
protected
|
|
procedure SetCapacity(AValue: SizeInt); override;
|
|
procedure SetCount(AValue: SizeInt);
|
|
procedure InitializeList; virtual;
|
|
procedure InternalInsert(AIndex: SizeInt; constref AValue: T);
|
|
private
|
|
function GetItem(AIndex: SizeInt): T;
|
|
procedure SetItem(AIndex: SizeInt; const AValue: T);
|
|
public
|
|
constructor Create; overload;
|
|
constructor Create(const AComparer: IComparer<T>); overload;
|
|
constructor Create(ACollection: TEnumerable<T>); overload;
|
|
{$IFDEF ENABLE_METHODS_WITH_TEnumerableWithPointers}
|
|
constructor Create(ACollection: TEnumerableWithPointers<T>); overload;
|
|
{$ENDIF}
|
|
destructor Destroy; override;
|
|
|
|
function Add(constref AValue: T): SizeInt; virtual;
|
|
procedure AddRange(constref AValues: array of T); virtual; overload;
|
|
procedure AddRange(const AEnumerable: IEnumerable<T>); overload;
|
|
procedure AddRange(AEnumerable: TEnumerable<T>); overload;
|
|
{$IFDEF ENABLE_METHODS_WITH_TEnumerableWithPointers}
|
|
procedure AddRange(AEnumerable: TEnumerableWithPointers<T>); overload;
|
|
{$ENDIF}
|
|
|
|
procedure Insert(AIndex: SizeInt; constref AValue: T); virtual;
|
|
procedure InsertRange(AIndex: SizeInt; constref AValues: array of T); virtual; overload;
|
|
procedure InsertRange(AIndex: SizeInt; const AEnumerable: IEnumerable<T>); overload;
|
|
procedure InsertRange(AIndex: SizeInt; const AEnumerable: TEnumerable<T>); overload;
|
|
{$IFDEF ENABLE_METHODS_WITH_TEnumerableWithPointers}
|
|
procedure InsertRange(AIndex: SizeInt; const AEnumerable: TEnumerableWithPointers<T>); overload;
|
|
{$ENDIF}
|
|
|
|
function Remove(constref AValue: T): SizeInt;
|
|
procedure Delete(AIndex: SizeInt); inline;
|
|
procedure DeleteRange(AIndex, ACount: SizeInt);
|
|
function ExtractIndex(const AIndex: SizeInt): T; overload;
|
|
function Extract(constref AValue: T): T; overload;
|
|
|
|
procedure Exchange(AIndex1, AIndex2: SizeInt); virtual;
|
|
procedure Move(AIndex, ANewIndex: SizeInt); virtual;
|
|
|
|
function First: T; inline;
|
|
function Last: T; inline;
|
|
|
|
procedure Clear;
|
|
|
|
function Contains(constref AValue: T): Boolean; inline;
|
|
function IndexOf(constref AValue: T): SizeInt; virtual;
|
|
function LastIndexOf(constref AValue: T): SizeInt; virtual;
|
|
|
|
procedure Reverse;
|
|
|
|
procedure TrimExcess; override;
|
|
|
|
procedure Sort; overload;
|
|
procedure Sort(const AComparer: IComparer<T>); overload;
|
|
function BinarySearch(constref AItem: T; out AIndex: SizeInt): Boolean; overload;
|
|
function BinarySearch(constref AItem: T; out AIndex: SizeInt; const AComparer: IComparer<T>): Boolean; overload;
|
|
|
|
property Count: SizeInt read FLength write SetCount;
|
|
property Items[Index: SizeInt]: T read GetItem write SetItem; default;
|
|
end;
|
|
|
|
TCollectionSortStyle = (cssNone,cssUser,cssAuto);
|
|
TCollectionSortStyles = Set of TCollectionSortStyle;
|
|
|
|
TSortedList<T> = class(TList<T>)
|
|
private
|
|
FDuplicates: TDuplicates;
|
|
FSortStyle: TCollectionSortStyle;
|
|
function GetSorted: boolean;
|
|
procedure SetSorted(AValue: boolean);
|
|
procedure SetSortStyle(AValue: TCollectionSortStyle);
|
|
protected
|
|
procedure InitializeList; override;
|
|
public
|
|
function Add(constref AValue: T): SizeInt; override; overload;
|
|
procedure AddRange(constref AValues: array of T); override; overload;
|
|
procedure Insert(AIndex: SizeInt; constref AValue: T); override;
|
|
procedure Exchange(AIndex1, AIndex2: SizeInt); override;
|
|
procedure Move(AIndex, ANewIndex: SizeInt); override;
|
|
procedure InsertRange(AIndex: SizeInt; constref AValues: array of T); override; overload;
|
|
property Duplicates: TDuplicates read FDuplicates write FDuplicates;
|
|
property Sorted: Boolean read GetSorted write SetSorted;
|
|
property SortStyle: TCollectionSortStyle read FSortStyle write SetSortStyle;
|
|
|
|
function ConsistencyCheck(ARaiseException: boolean = true): boolean; virtual;
|
|
end;
|
|
|
|
TThreadList<T> = class
|
|
private
|
|
FList: TList<T>;
|
|
FDuplicates: TDuplicates;
|
|
FLock: TRTLCriticalSection;
|
|
public
|
|
constructor Create;
|
|
destructor Destroy; override;
|
|
|
|
procedure Add(constref AValue: T);
|
|
procedure Remove(constref AValue: T);
|
|
procedure Clear;
|
|
|
|
function LockList: TList<T>;
|
|
procedure UnlockList; inline;
|
|
|
|
property Duplicates: TDuplicates read FDuplicates write FDuplicates;
|
|
end;
|
|
|
|
TQueue<T> = class(TCustomList<T>)
|
|
public type
|
|
TPointersEnumerator = class(TCustomPointersEnumerator<T, PT>)
|
|
protected
|
|
FQueue: TQueue<T>;
|
|
FIndex: SizeInt;
|
|
function DoMoveNext: boolean; override;
|
|
function DoGetCurrent: PT; override;
|
|
public
|
|
constructor Create(AQueue: TQueue<T>);
|
|
end;
|
|
protected
|
|
function GetPtrEnumerator: TEnumerator<PT>; override;
|
|
protected
|
|
// bug #24287 - workaround for generics type name conflict (Identifier not found)
|
|
// next bug workaround - for another error related to previous workaround
|
|
// change order (function must be declared before TEnumerator declaration}
|
|
function DoGetEnumerator: {Generics.Collections.}TEnumerator<T>; override;
|
|
public
|
|
type
|
|
TEnumerator = class(TCustomListEnumerator<T>)
|
|
public
|
|
constructor Create(AQueue: TQueue<T>);
|
|
end;
|
|
|
|
function GetEnumerator: TEnumerator; reintroduce;
|
|
private
|
|
FLow: SizeInt;
|
|
protected
|
|
procedure SetCapacity(AValue: SizeInt); override;
|
|
function DoRemove(AIndex: SizeInt; ACollectionNotification: TCollectionNotification): T; override;
|
|
function GetCount: SizeInt; override;
|
|
public
|
|
constructor Create(ACollection: TEnumerable<T>); overload;
|
|
{$IFDEF ENABLE_METHODS_WITH_TEnumerableWithPointers}
|
|
constructor Create(ACollection: TEnumerableWithPointers<T>); overload;
|
|
{$ENDIF}
|
|
destructor Destroy; override;
|
|
procedure Enqueue(constref AValue: T);
|
|
function Dequeue: T;
|
|
function Extract: T;
|
|
function Peek: T;
|
|
procedure Clear;
|
|
procedure TrimExcess; override;
|
|
end;
|
|
|
|
TStack<T> = class(TCustomListWithPointers<T>)
|
|
protected
|
|
// bug #24287 - workaround for generics type name conflict (Identifier not found)
|
|
// next bug workaround - for another error related to previous workaround
|
|
// change order (function must be declared before TEnumerator declaration}
|
|
function DoGetEnumerator: {Generics.Collections.}TEnumerator<T>; override;
|
|
public
|
|
type
|
|
TEnumerator = class(TCustomListEnumerator<T>);
|
|
|
|
function GetEnumerator: TEnumerator; reintroduce;
|
|
protected
|
|
function DoRemove(AIndex: SizeInt; ACollectionNotification: TCollectionNotification): T; override;
|
|
procedure SetCapacity(AValue: SizeInt); override;
|
|
public
|
|
constructor Create(ACollection: TEnumerable<T>); overload;
|
|
{$IFDEF ENABLE_METHODS_WITH_TEnumerableWithPointers}
|
|
constructor Create(ACollection: TEnumerableWithPointers<T>); overload;
|
|
{$ENDIF}
|
|
destructor Destroy; override;
|
|
procedure Clear;
|
|
procedure Push(constref AValue: T);
|
|
function Pop: T; inline;
|
|
function Peek: T;
|
|
function Extract: T; inline;
|
|
procedure TrimExcess; override;
|
|
end;
|
|
|
|
TObjectList<T: class> = class(TList<T>)
|
|
private
|
|
FObjectsOwner: Boolean;
|
|
protected
|
|
procedure Notify(constref AValue: T; ACollectionNotification: TCollectionNotification); override;
|
|
public
|
|
constructor Create(AOwnsObjects: Boolean = True); overload;
|
|
constructor Create(const AComparer: IComparer<T>; AOwnsObjects: Boolean = True); overload;
|
|
constructor Create(ACollection: TEnumerable<T>; AOwnsObjects: Boolean = True); overload;
|
|
{$IFDEF ENABLE_METHODS_WITH_TEnumerableWithPointers}
|
|
constructor Create(ACollection: TEnumerableWithPointers<T>; AOwnsObjects: Boolean = True); overload;
|
|
{$ENDIF}
|
|
property OwnsObjects: Boolean read FObjectsOwner write FObjectsOwner;
|
|
end;
|
|
|
|
TObjectQueue<T: class> = class(TQueue<T>)
|
|
private
|
|
FObjectsOwner: Boolean;
|
|
protected
|
|
procedure Notify(constref AValue: T; ACollectionNotification: TCollectionNotification); override;
|
|
public
|
|
constructor Create(AOwnsObjects: Boolean = True); overload;
|
|
constructor Create(ACollection: TEnumerable<T>; AOwnsObjects: Boolean = True); overload;
|
|
{$IFDEF ENABLE_METHODS_WITH_TEnumerableWithPointers}
|
|
constructor Create(ACollection: TEnumerableWithPointers<T>; AOwnsObjects: Boolean = True); overload;
|
|
{$ENDIF}
|
|
procedure Dequeue;
|
|
property OwnsObjects: Boolean read FObjectsOwner write FObjectsOwner;
|
|
end;
|
|
|
|
TObjectStack<T: class> = class(TStack<T>)
|
|
private
|
|
FObjectsOwner: Boolean;
|
|
protected
|
|
procedure Notify(constref AValue: T; ACollectionNotification: TCollectionNotification); override;
|
|
public
|
|
constructor Create(AOwnsObjects: Boolean = True); overload;
|
|
constructor Create(ACollection: TEnumerable<T>; AOwnsObjects: Boolean = True); overload;
|
|
{$IFDEF ENABLE_METHODS_WITH_TEnumerableWithPointers}
|
|
constructor Create(ACollection: TEnumerableWithPointers<T>; AOwnsObjects: Boolean = True); overload;
|
|
{$ENDIF}
|
|
function Pop: T;
|
|
property OwnsObjects: Boolean read FObjectsOwner write FObjectsOwner;
|
|
end;
|
|
|
|
PObject = ^TObject;
|
|
|
|
{$I inc\generics.dictionariesh.inc}
|
|
|
|
{ TCustomHashSet<T> }
|
|
|
|
TCustomSet<T> = class(TEnumerableWithPointers<T>)
|
|
protected
|
|
FOnNotify: TCollectionNotifyEvent<T>;
|
|
public type
|
|
PT = ^T;
|
|
protected type
|
|
TCustomSetEnumerator = class(TEnumerator<T>)
|
|
protected var
|
|
FEnumerator: TEnumerator<T>;
|
|
function DoMoveNext: boolean; override;
|
|
function DoGetCurrent: T; override;
|
|
function GetCurrent: T; virtual; abstract;
|
|
public
|
|
constructor Create(ASet: TCustomSet<T>); virtual; abstract;
|
|
destructor Destroy; override;
|
|
end;
|
|
protected
|
|
function DoGetEnumerator: TEnumerator<T>; override;
|
|
function GetCount: SizeInt; virtual; abstract;
|
|
function GetCapacity: SizeInt; virtual; abstract;
|
|
procedure SetCapacity(AValue: SizeInt); virtual; abstract;
|
|
function GetOnNotify: TCollectionNotifyEvent<T>; virtual; abstract;
|
|
procedure SetOnNotify(AValue: TCollectionNotifyEvent<T>); virtual; abstract;
|
|
public
|
|
constructor Create; virtual; abstract; overload;
|
|
constructor Create(ACollection: TEnumerable<T>); overload;
|
|
{$IFDEF ENABLE_METHODS_WITH_TEnumerableWithPointers}
|
|
constructor Create(ACollection: TEnumerableWithPointers<T>); overload;
|
|
{$ENDIF}
|
|
function GetEnumerator: TCustomSetEnumerator; reintroduce; virtual; abstract;
|
|
|
|
function Add(constref AValue: T): Boolean; virtual; abstract;
|
|
function Remove(constref AValue: T): Boolean; virtual; abstract;
|
|
function Extract(constref AValue: T): T; virtual; abstract;
|
|
|
|
procedure Clear; virtual; abstract;
|
|
function Contains(constref AValue: T): Boolean; virtual; abstract;
|
|
function AddRange(constref AValues: array of T): Boolean; overload;
|
|
function AddRange(const AEnumerable: IEnumerable<T>): Boolean; overload;
|
|
function AddRange(AEnumerable: TEnumerable<T>): Boolean; overload;
|
|
{$IFDEF ENABLE_METHODS_WITH_TEnumerableWithPointers}
|
|
function AddRange(AEnumerable: TEnumerableWithPointers<T>): Boolean; overload;
|
|
{$ENDIF}
|
|
procedure UnionWith(AHashSet: TCustomSet<T>);
|
|
procedure IntersectWith(AHashSet: TCustomSet<T>);
|
|
procedure ExceptWith(AHashSet: TCustomSet<T>);
|
|
procedure SymmetricExceptWith(AHashSet: TCustomSet<T>);
|
|
|
|
property Count: SizeInt read GetCount;
|
|
property Capacity: SizeInt read GetCapacity write SetCapacity;
|
|
procedure TrimExcess; virtual; abstract;
|
|
|
|
property OnNotify: TCollectionNotifyEvent<T> read GetOnNotify write SetOnNotify;
|
|
end;
|
|
|
|
{ THashSet<T> }
|
|
|
|
THashSet<T> = class(TCustomSet<T>)
|
|
private
|
|
procedure InternalDictionaryNotify(ASender: TObject; constref AItem: T; AAction: TCollectionNotification);
|
|
protected
|
|
FInternalDictionary: TOpenAddressingLP<T, TEmptyRecord>;
|
|
public type
|
|
THashSetEnumerator = class(TCustomSetEnumerator)
|
|
protected type
|
|
TDictionaryEnumerator = TDictionary<T, TEmptyRecord>.TKeyEnumerator;
|
|
function GetCurrent: T; override;
|
|
public
|
|
constructor Create(ASet: TCustomSet<T>); override;
|
|
end;
|
|
|
|
TPointersEnumerator = class(TCustomPointersEnumerator<T, PT>)
|
|
protected
|
|
FEnumerator: TEnumerator<PT>;
|
|
function DoMoveNext: boolean; override;
|
|
function DoGetCurrent: PT; override;
|
|
public
|
|
constructor Create(AHashSet: THashSet<T>);
|
|
end;
|
|
protected
|
|
function GetPtrEnumerator: TEnumerator<PT>; override;
|
|
function GetCount: SizeInt; override;
|
|
function GetCapacity: SizeInt; override;
|
|
procedure SetCapacity(AValue: SizeInt); override;
|
|
function GetOnNotify: TCollectionNotifyEvent<T>; override;
|
|
procedure SetOnNotify(AValue: TCollectionNotifyEvent<T>); override;
|
|
public
|
|
constructor Create; override; overload;
|
|
constructor Create(const AComparer: IEqualityComparer<T>); virtual; overload;
|
|
destructor Destroy; override;
|
|
function GetEnumerator: TCustomSetEnumerator; override;
|
|
|
|
function Add(constref AValue: T): Boolean; override;
|
|
function Remove(constref AValue: T): Boolean; override;
|
|
function Extract(constref AValue: T): T; override;
|
|
|
|
procedure Clear; override;
|
|
function Contains(constref AValue: T): Boolean; override;
|
|
|
|
procedure TrimExcess; override;
|
|
end;
|
|
|
|
TPair<TKey, TValue, TInfo> = record
|
|
public
|
|
Key: TKey;
|
|
Value: TValue;
|
|
Info: TInfo;
|
|
end;
|
|
|
|
TAVLTreeNode<TREE_CONSTRAINTS, TTree> = record
|
|
private type
|
|
TNodePair = TPair<TREE_CONSTRAINTS>;
|
|
public type
|
|
PNode = ^TAVLTreeNode<TREE_CONSTRAINTS, TTree>;
|
|
public
|
|
Parent, Left, Right: PNode;
|
|
Balance: Integer;
|
|
Data: TNodePair;
|
|
function Successor: PNode;
|
|
function Precessor: PNode;
|
|
function TreeDepth: integer;
|
|
procedure ConsistencyCheck(ATree: TObject); // workaround for internal error 2012101001 (no generic forward declarations)
|
|
function GetCount: SizeInt;
|
|
property Key: TKey read Data.Key write Data.Key;
|
|
property Value: TValue read Data.Value write Data.Value;
|
|
property Info: TInfo read Data.Info write Data.Info;
|
|
end;
|
|
|
|
TCustomTreeEnumerator<T, PNode, TTree> = class abstract(TEnumerator<T>)
|
|
protected
|
|
FCurrent: PNode;
|
|
FTree: TTree;
|
|
function DoGetCurrent: T; override;
|
|
function GetCurrent: T; virtual; abstract;
|
|
public
|
|
constructor Create(ATree: TObject);
|
|
property Current: T read GetCurrent;
|
|
end;
|
|
|
|
TTreeEnumerable<TTreeEnumerator, TTreePointersEnumerator,
|
|
T, PT, PNode, TTree> = class abstract(TEnumerableWithPointers<T>)
|
|
private
|
|
FTree: TTree;
|
|
function GetCount: SizeInt; inline;
|
|
protected
|
|
function GetPtrEnumerator: TEnumerator<PT>; override;
|
|
function DoGetEnumerator: TTreeEnumerator; override;
|
|
public
|
|
constructor Create(ATree: TTree);
|
|
function ToArray: TArray<T>; override; final;
|
|
property Count: SizeInt read GetCount;
|
|
end;
|
|
|
|
TAVLTreeEnumerator<T, PNode, TTree> = class(TCustomTreeEnumerator<T, PNode, TTree>)
|
|
protected
|
|
FLowToHigh: boolean;
|
|
function DoMoveNext: Boolean; override;
|
|
public
|
|
constructor Create(ATree: TObject; ALowToHigh: boolean = true);
|
|
property LowToHigh: boolean read FLowToHigh;
|
|
end;
|
|
|
|
TNodeNotifyEvent<PNode> = procedure(ASender: TObject; ANode: PNode; AAction: TCollectionNotification; ADispose: boolean) of object;
|
|
|
|
TCustomAVLTreeMap<TREE_CONSTRAINTS> = class
|
|
private type
|
|
TTree = class(TCustomAVLTreeMap<TREE_CONSTRAINTS>);
|
|
public type
|
|
TNode = TAVLTreeNode<TREE_CONSTRAINTS, TTree>;
|
|
PNode = ^TNode;
|
|
PPNode = ^PNode;
|
|
TTreePair = TPair<TKey, TValue>;
|
|
PKey = ^TKey;
|
|
PValue = ^TValue;
|
|
private type
|
|
// type exist only for generic constraint in TNodeCollection (non functional - PPNode has no sense)
|
|
TPNodeEnumerator = class(TAVLTreeEnumerator<PPNode, PNode, TTree>);
|
|
private var
|
|
FDuplicates: TDuplicates;
|
|
FComparer: IComparer<TKey>;
|
|
protected
|
|
FCount: SizeInt;
|
|
FRoot: PNode;
|
|
FKeys: TEnumerable<TKey>;
|
|
FValues: TEnumerable<TValue>;
|
|
FOnNodeNotify: TNodeNotifyEvent<PNode>;
|
|
FOnKeyNotify: TCollectionNotifyEvent<TKey>;
|
|
FOnValueNotify: TCollectionNotifyEvent<TValue>;
|
|
|
|
procedure NodeAdded(ANode: PNode); virtual;
|
|
procedure DeletingNode(ANode: PNode; AOrigin: boolean); virtual;
|
|
|
|
function DoRemove(ANode: PNode; ACollectionNotification: TCollectionNotification; ADispose: boolean): TValue;
|
|
procedure DisposeAllNodes(ANode: PNode); overload;
|
|
|
|
function Compare(constref ALeft, ARight: TKey): Integer; inline;
|
|
function FindPredecessor(ANode: PNode): PNode;
|
|
function FindInsertNode(ANode: PNode; out AInsertNode: PNode): Integer;
|
|
|
|
procedure RotateRightRight(ANode: PNode); virtual;
|
|
procedure RotateLeftLeft(ANode: PNode); virtual;
|
|
procedure RotateRightLeft(ANode: PNode); virtual;
|
|
procedure RotateLeftRight(ANode: PNode); virtual;
|
|
|
|
procedure KeyNotify(constref AKey: TKey; ACollectionNotification: TCollectionNotification); inline;
|
|
procedure ValueNotify(constref AValue: TValue; ACollectionNotification: TCollectionNotification); inline;
|
|
procedure NodeNotify(ANode: PNode; ACollectionNotification: TCollectionNotification; ADispose: boolean); inline;
|
|
procedure SetValue(var AValue: TValue; constref ANewValue: TValue);
|
|
|
|
// for reporting
|
|
procedure WriteStr(AStream: TStream; const AText: string);
|
|
public type
|
|
TPairEnumerator = class(TAVLTreeEnumerator<TTreePair, PNode, TTree>)
|
|
protected
|
|
function GetCurrent: TTreePair; override;
|
|
end;
|
|
|
|
TNodeEnumerator = class(TAVLTreeEnumerator<PNode, PNode, TTree>)
|
|
protected
|
|
function GetCurrent: PNode; override;
|
|
end;
|
|
|
|
TKeyEnumerator = class(TAVLTreeEnumerator<TKey, PNode, TTree>)
|
|
protected
|
|
function GetCurrent: TKey; override;
|
|
end;
|
|
|
|
TPKeyEnumerator = class(TAVLTreeEnumerator<PKey, PNode, TTree>)
|
|
protected
|
|
function GetCurrent: PKey; override;
|
|
end;
|
|
|
|
TValueEnumerator = class(TAVLTreeEnumerator<TValue, PNode, TTree>)
|
|
protected
|
|
function GetCurrent: TValue; override;
|
|
end;
|
|
|
|
TPValueEnumerator = class(TAVLTreeEnumerator<PValue, PNode, TTree>)
|
|
protected
|
|
function GetCurrent: PValue; override;
|
|
end;
|
|
|
|
TNodeCollection = class(TTreeEnumerable<TNodeEnumerator, TPNodeEnumerator, PNode, PPNode, PNode, TTree>)
|
|
private
|
|
property Ptr; // PPNode has no sense, so hide enumerator for PPNode
|
|
end;
|
|
|
|
TKeyCollection = class(TTreeEnumerable<TKeyEnumerator, TPKeyEnumerator, TKey, PKey, PNode, TTree>);
|
|
|
|
TValueCollection = class(TTreeEnumerable<TValueEnumerator, TPValueEnumerator, TValue, PValue, PNode, TTree>);
|
|
private
|
|
FNodes: TNodeCollection;
|
|
function GetNodeCollection: TNodeCollection;
|
|
procedure InternalAdd(ANode, AParent: PNode); overload;
|
|
function InternalAdd(ANode: PNode; ADispisable: boolean): PNode; overload;
|
|
procedure InternalDelete(ANode: PNode);
|
|
function GetKeys: TKeyCollection;
|
|
function GetValues: TValueCollection;
|
|
public
|
|
constructor Create; virtual; overload;
|
|
constructor Create(const AComparer: IComparer<TKey>); virtual; overload;
|
|
|
|
function NewNode: PNode;
|
|
function NewNodeArray(ACount: SizeInt): PNode; overload;
|
|
procedure NewNodeArray(out AArray: TArray<PNode>; ACount: SizeInt); overload;
|
|
procedure DisposeNode(ANode: PNode);
|
|
procedure DisposeNodeArray(ANode: PNode; ACount: SizeInt); overload;
|
|
procedure DisposeNodeArray(var AArray: TArray<PNode>); overload;
|
|
|
|
destructor Destroy; override;
|
|
function AddNode(ANode: PNode): boolean; overload; inline;
|
|
function Add(constref APair: TTreePair): PNode; overload; inline;
|
|
function Add(constref AKey: TKey; constref AValue: TValue): PNode; overload; inline;
|
|
function Remove(constref AKey: TKey; ADisposeNode: boolean = true): boolean;
|
|
function ExtractPair(constref AKey: TKey; ADisposeNode: boolean = true): TTreePair; overload;
|
|
function ExtractPair(constref ANode: PNode; ADispose: boolean = true): TTreePair; overload;
|
|
function ExtractNode(constref AKey: TKey; ADisposeNode: boolean): PNode; overload;
|
|
function ExtractNode(ANode: PNode; ADispose: boolean): PNode; overload;
|
|
procedure Delete(ANode: PNode; ADispose: boolean = true); inline;
|
|
|
|
function GetEnumerator: TPairEnumerator;
|
|
property Nodes: TNodeCollection read GetNodeCollection;
|
|
|
|
procedure Clear(ADisposeNodes: Boolean = true); virtual;
|
|
|
|
function FindLowest: PNode;
|
|
function FindHighest: PNode;
|
|
|
|
property Count: SizeInt read FCount;
|
|
|
|
property Root: PNode read FRoot;
|
|
function Find(constref AKey: TKey): PNode;
|
|
function ContainsKey(constref AKey: TKey; out ANode: PNode): boolean; overload; inline;
|
|
function ContainsKey(constref AKey: TKey): boolean; overload; inline;
|
|
|
|
procedure ConsistencyCheck; virtual;
|
|
procedure WriteTreeNode(AStream: TStream; ANode: PNode);
|
|
procedure WriteReportToStream(AStream: TStream);
|
|
function NodeToReportStr(ANode: PNode): string; virtual;
|
|
function ReportAsString: string;
|
|
|
|
property Keys: TKeyCollection read GetKeys;
|
|
property Values: TValueCollection read GetValues;
|
|
property Duplicates: TDuplicates read FDuplicates write FDuplicates;
|
|
|
|
property OnNodeNotify: TNodeNotifyEvent<PNode> read FOnNodeNotify write FOnNodeNotify;
|
|
property OnKeyNotify: TCollectionNotifyEvent<TKey> read FOnKeyNotify write FOnKeyNotify;
|
|
property OnValueNotify: TCollectionNotifyEvent<TValue> read FOnValueNotify write FOnValueNotify;
|
|
end;
|
|
|
|
TAVLTreeMap<TKey, TValue> = class(TCustomAVLTreeMap<TKey, TValue, TEmptyRecord>)
|
|
end;
|
|
|
|
TIndexedAVLTreeMap<TKey, TValue> = class(TCustomAVLTreeMap<TKey, TValue, SizeInt>)
|
|
protected
|
|
FLastNode: PNode;
|
|
FLastIndex: SizeInt;
|
|
|
|
procedure RotateRightRight(ANode: PNode); override;
|
|
procedure RotateLeftLeft(ANode: PNode); override;
|
|
procedure RotateRightLeft(ANode: PNode); override;
|
|
procedure RotateLeftRight(ANode: PNode); override;
|
|
|
|
procedure NodeAdded(ANode: PNode); override;
|
|
procedure DeletingNode(ANode: PNode; AOrigin: boolean); override;
|
|
public
|
|
function GetNodeAtIndex(AIndex: SizeInt): PNode;
|
|
function NodeToIndex(ANode: PNode): SizeInt;
|
|
|
|
procedure ConsistencyCheck; override;
|
|
function NodeToReportStr(ANode: PNode): string; override;
|
|
end;
|
|
|
|
TAVLTree<T> = class(TAVLTreeMap<T, TEmptyRecord>)
|
|
protected
|
|
property OnKeyNotify;
|
|
property OnValueNotify;
|
|
public type
|
|
TItemEnumerator = TKeyEnumerator;
|
|
public
|
|
function Add(constref AValue: T): PNode; reintroduce; inline;
|
|
function AddNode(ANode: PNode): boolean; reintroduce; inline;
|
|
|
|
property OnNotify: TCollectionNotifyEvent<T> read FOnKeyNotify write FOnKeyNotify;
|
|
end;
|
|
|
|
TIndexedAVLTree<T> = class(TIndexedAVLTreeMap<T, TEmptyRecord>)
|
|
protected
|
|
property OnKeyNotify;
|
|
property OnValueNotify;
|
|
public type
|
|
TItemEnumerator = TKeyEnumerator;
|
|
public
|
|
function Add(constref AValue: T): PNode; reintroduce; inline;
|
|
function AddNode(ANode: PNode): boolean; reintroduce; inline;
|
|
|
|
property OnNotify: TCollectionNotifyEvent<T> read FOnKeyNotify write FOnKeyNotify;
|
|
end;
|
|
|
|
TSortedSet<T> = class(TCustomSet<T>)
|
|
private
|
|
procedure InternalAVLTreeNotify(ASender: TObject; constref AItem: T; AAction: TCollectionNotification);
|
|
protected
|
|
FInternalTree: TAVLTree<T>;
|
|
public type
|
|
TSortedSetEnumerator = class(TCustomSetEnumerator)
|
|
protected type
|
|
TTreeEnumerator = TAVLTree<T>.TItemEnumerator;
|
|
function GetCurrent: T; override;
|
|
public
|
|
constructor Create(ASet: TCustomSet<T>); override;
|
|
end;
|
|
|
|
TPointersEnumerator = class(TCustomPointersEnumerator<T, PT>)
|
|
protected
|
|
FEnumerator: TEnumerator<PT>;
|
|
function DoMoveNext: boolean; override;
|
|
function DoGetCurrent: PT; override;
|
|
public
|
|
constructor Create(ASortedSet: TSortedSet<T>);
|
|
end;
|
|
protected
|
|
function GetPtrEnumerator: TEnumerator<PT>; override;
|
|
function GetCount: SizeInt; override;
|
|
function GetCapacity: SizeInt; override;
|
|
procedure SetCapacity(AValue: SizeInt); override;
|
|
function GetOnNotify: TCollectionNotifyEvent<T>; override;
|
|
procedure SetOnNotify(AValue: TCollectionNotifyEvent<T>); override;
|
|
public
|
|
constructor Create; override; overload;
|
|
constructor Create(const AComparer: IComparer<T>); virtual; overload;
|
|
destructor Destroy; override;
|
|
function GetEnumerator: TCustomSetEnumerator; override;
|
|
|
|
function Add(constref AValue: T): Boolean; override;
|
|
function Remove(constref AValue: T): Boolean; override;
|
|
function Extract(constref AValue: T): T; override;
|
|
procedure Clear; override;
|
|
function Contains(constref AValue: T): Boolean; override;
|
|
|
|
procedure TrimExcess; override;
|
|
end;
|
|
|
|
TSortedHashSet<T> = class(TCustomSet<T>)
|
|
private
|
|
procedure InternalDictionaryNotify(ASender: TObject; constref AItem: PT; AAction: TCollectionNotification);
|
|
protected
|
|
FInternalDictionary: TOpenAddressingLP<PT, TEmptyRecord>;
|
|
FInternalTree: TAVLTree<T>;
|
|
function DoGetEnumerator: TEnumerator<T>; override;
|
|
function GetCount: SizeInt; override;
|
|
function GetCapacity: SizeInt; override;
|
|
procedure SetCapacity(AValue: SizeInt); override;
|
|
function GetOnNotify: TCollectionNotifyEvent<T>; override;
|
|
procedure SetOnNotify(AValue: TCollectionNotifyEvent<T>); override;
|
|
protected type
|
|
TSortedHashSetEqualityComparer = class(TInterfacedObject, IEqualityComparer<PT>)
|
|
private
|
|
FComparer: IComparer<T>;
|
|
FEqualityComparer: IEqualityComparer<T>;
|
|
function Equals(constref ALeft, ARight: PT): Boolean;
|
|
function GetHashCode(constref AValue: PT): UInt32;
|
|
public
|
|
constructor Create(const AComparer: IComparer<T>); overload;
|
|
constructor Create(const AEqualityComparer: IEqualityComparer<T>); overload;
|
|
constructor Create(const AComparer: IComparer<T>; const AEqualityComparer: IEqualityComparer<T>); overload;
|
|
end;
|
|
public type
|
|
TSortedHashSetEnumerator = class(TCustomSetEnumerator)
|
|
protected type
|
|
TTreeEnumerator = TAVLTree<T>.TItemEnumerator;
|
|
function GetCurrent: T; override;
|
|
public
|
|
constructor Create(ASet: TCustomSet<T>); override;
|
|
end;
|
|
|
|
TPointersEnumerator = class(TCustomPointersEnumerator<T, PT>)
|
|
protected
|
|
FEnumerator: TEnumerator<PT>;
|
|
function DoMoveNext: boolean; override;
|
|
function DoGetCurrent: PT; override;
|
|
public
|
|
constructor Create(ASortedHashSet: TSortedHashSet<T>);
|
|
end;
|
|
protected
|
|
function GetPtrEnumerator: TEnumerator<PT>; override;
|
|
public
|
|
constructor Create; override; overload;
|
|
constructor Create(const AComparer: IEqualityComparer<T>); overload;
|
|
constructor Create(const AComparer: IComparer<T>); overload;
|
|
constructor Create(const AComparer: IComparer<T>; const AEqualityComparer: IEqualityComparer<T>); overload;
|
|
destructor Destroy; override;
|
|
function GetEnumerator: TCustomSetEnumerator; override;
|
|
|
|
function Add(constref AValue: T): Boolean; override;
|
|
function Remove(constref AValue: T): Boolean; override;
|
|
function Extract(constref AValue: T): T; override;
|
|
procedure Clear; override;
|
|
function Contains(constref AValue: T): Boolean; override;
|
|
|
|
procedure TrimExcess; override;
|
|
end;
|
|
|
|
function InCircularRange(ABottom, AItem, ATop: SizeInt): Boolean;
|
|
|
|
var
|
|
EmptyRecord: TEmptyRecord;
|
|
|
|
implementation
|
|
|
|
function InCircularRange(ABottom, AItem, ATop: SizeInt): Boolean;
|
|
begin
|
|
Result :=
|
|
(ABottom < AItem) and (AItem <= ATop )
|
|
or (ATop < ABottom) and (AItem > ABottom)
|
|
or (ATop < ABottom ) and (AItem <= ATop );
|
|
end;
|
|
|
|
{ TCustomArrayHelper<T> }
|
|
|
|
class function TCustomArrayHelper<T>.BinarySearch(constref AValues: array of T; constref AItem: T;
|
|
out AFoundIndex: SizeInt; const AComparer: IComparer<T>): Boolean;
|
|
begin
|
|
Result := BinarySearch(AValues, AItem, AFoundIndex, AComparer, Low(AValues), Length(AValues));
|
|
end;
|
|
|
|
class function TCustomArrayHelper<T>.BinarySearch(constref AValues: array of T; constref AItem: T;
|
|
out AFoundIndex: SizeInt): Boolean;
|
|
begin
|
|
Result := BinarySearch(AValues, AItem, AFoundIndex, TComparerBugHack.Default, Low(AValues), Length(AValues));
|
|
end;
|
|
|
|
class function TCustomArrayHelper<T>.BinarySearch(constref AValues: array of T; constref AItem: T;
|
|
out ASearchResult: TBinarySearchResult; const AComparer: IComparer<T>): Boolean;
|
|
begin
|
|
Result := BinarySearch(AValues, AItem, ASearchResult, AComparer, Low(AValues), Length(AValues));
|
|
end;
|
|
|
|
class function TCustomArrayHelper<T>.BinarySearch(constref AValues: array of T; constref AItem: T;
|
|
out ASearchResult: TBinarySearchResult): Boolean;
|
|
begin
|
|
Result := BinarySearch(AValues, AItem, ASearchResult, TComparerBugHack.Default, Low(AValues), Length(AValues));
|
|
end;
|
|
|
|
class procedure TCustomArrayHelper<T>.Sort(var AValues: array of T);
|
|
begin
|
|
QuickSort(AValues, Low(AValues), High(AValues), TComparerBugHack.Default);
|
|
end;
|
|
|
|
class procedure TCustomArrayHelper<T>.Sort(var AValues: array of T;
|
|
const AComparer: IComparer<T>);
|
|
begin
|
|
QuickSort(AValues, Low(AValues), High(AValues), AComparer);
|
|
end;
|
|
|
|
class procedure TCustomArrayHelper<T>.Sort(var AValues: array of T;
|
|
const AComparer: IComparer<T>; AIndex, ACount: SizeInt);
|
|
begin
|
|
if ACount <= 1 then
|
|
Exit;
|
|
QuickSort(AValues, AIndex, Pred(AIndex + ACount), AComparer);
|
|
end;
|
|
|
|
{ TArrayHelper<T> }
|
|
|
|
class procedure TArrayHelper<T>.QuickSort(var AValues: array of T; ALeft, ARight: SizeInt;
|
|
const AComparer: IComparer<T>);
|
|
var
|
|
I, J: SizeInt;
|
|
P, Q: T;
|
|
begin
|
|
if ((ARight - ALeft) <= 0) or (Length(AValues) = 0) then
|
|
Exit;
|
|
repeat
|
|
I := ALeft;
|
|
J := ARight;
|
|
P := AValues[ALeft + (ARight - ALeft) shr 1];
|
|
repeat
|
|
while AComparer.Compare(AValues[I], P) < 0 do
|
|
Inc(I);
|
|
while AComparer.Compare(AValues[J], P) > 0 do
|
|
Dec(J);
|
|
if I <= J then
|
|
begin
|
|
if I <> J then
|
|
begin
|
|
Q := AValues[I];
|
|
AValues[I] := AValues[J];
|
|
AValues[J] := Q;
|
|
end;
|
|
Inc(I);
|
|
Dec(J);
|
|
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 J - ALeft < ARight - I then
|
|
begin
|
|
if ALeft < J then
|
|
QuickSort(AValues, ALeft, J, AComparer);
|
|
ALeft := I;
|
|
end
|
|
else
|
|
begin
|
|
if I < ARight then
|
|
QuickSort(AValues, I, ARight, AComparer);
|
|
ARight := J;
|
|
end;
|
|
until ALeft >= ARight;
|
|
end;
|
|
|
|
class function TArrayHelper<T>.BinarySearch(constref AValues: array of T; constref AItem: T;
|
|
out ASearchResult: TBinarySearchResult; const AComparer: IComparer<T>;
|
|
AIndex, ACount: SizeInt): Boolean;
|
|
var
|
|
imin, imax, imid: Int32;
|
|
begin
|
|
// continually narrow search until just one element remains
|
|
imin := AIndex;
|
|
imax := Pred(AIndex + ACount);
|
|
|
|
// http://en.wikipedia.org/wiki/Binary_search_algorithm
|
|
while (imin < imax) do
|
|
begin
|
|
imid := imin + ((imax - imin) shr 1);
|
|
|
|
// code must guarantee the interval is reduced at each iteration
|
|
// assert(imid < imax);
|
|
// note: 0 <= imin < imax implies imid will always be less than imax
|
|
|
|
ASearchResult.CompareResult := AComparer.Compare(AValues[imid], AItem);
|
|
// reduce the search
|
|
if (ASearchResult.CompareResult < 0) then
|
|
imin := imid + 1
|
|
else
|
|
begin
|
|
imax := imid;
|
|
if ASearchResult.CompareResult = 0 then
|
|
begin
|
|
ASearchResult.FoundIndex := imid;
|
|
ASearchResult.CandidateIndex := imid;
|
|
Exit(True);
|
|
end;
|
|
end;
|
|
end;
|
|
// At exit of while:
|
|
// if A[] is empty, then imax < imin
|
|
// otherwise imax == imin
|
|
|
|
// deferred test for equality
|
|
|
|
if (imax = imin) then
|
|
begin
|
|
ASearchResult.CompareResult := AComparer.Compare(AValues[imin], AItem);
|
|
ASearchResult.CandidateIndex := imin;
|
|
if (ASearchResult.CompareResult = 0) then
|
|
begin
|
|
ASearchResult.FoundIndex := imin;
|
|
Exit(True);
|
|
end else
|
|
begin
|
|
ASearchResult.FoundIndex := -1;
|
|
Exit(False);
|
|
end;
|
|
end
|
|
else
|
|
begin
|
|
ASearchResult.CompareResult := 0;
|
|
ASearchResult.FoundIndex := -1;
|
|
ASearchResult.CandidateIndex := -1;
|
|
Exit(False);
|
|
end;
|
|
end;
|
|
|
|
class function TArrayHelper<T>.BinarySearch(constref AValues: array of T; constref AItem: T;
|
|
out AFoundIndex: SizeInt; const AComparer: IComparer<T>;
|
|
AIndex, ACount: SizeInt): Boolean;
|
|
var
|
|
imin, imax, imid: Int32;
|
|
LCompare: SizeInt;
|
|
begin
|
|
// continually narrow search until just one element remains
|
|
imin := AIndex;
|
|
imax := Pred(AIndex + ACount);
|
|
|
|
// http://en.wikipedia.org/wiki/Binary_search_algorithm
|
|
while (imin < imax) do
|
|
begin
|
|
imid := imin + ((imax - imin) shr 1);
|
|
|
|
// code must guarantee the interval is reduced at each iteration
|
|
// assert(imid < imax);
|
|
// note: 0 <= imin < imax implies imid will always be less than imax
|
|
|
|
LCompare := AComparer.Compare(AValues[imid], AItem);
|
|
// reduce the search
|
|
if (LCompare < 0) then
|
|
imin := imid + 1
|
|
else
|
|
begin
|
|
imax := imid;
|
|
if LCompare = 0 then
|
|
begin
|
|
AFoundIndex := imid;
|
|
Exit(True);
|
|
end;
|
|
end;
|
|
end;
|
|
// At exit of while:
|
|
// if A[] is empty, then imax < imin
|
|
// otherwise imax == imin
|
|
|
|
// deferred test for equality
|
|
|
|
LCompare := AComparer.Compare(AValues[imin], AItem);
|
|
if (imax = imin) and (LCompare = 0) then
|
|
begin
|
|
AFoundIndex := imin;
|
|
Exit(True);
|
|
end
|
|
else
|
|
begin
|
|
AFoundIndex := -1;
|
|
Exit(False);
|
|
end;
|
|
end;
|
|
|
|
{ TEnumerator<T> }
|
|
|
|
function TEnumerator<T>.MoveNext: boolean;
|
|
begin
|
|
Exit(DoMoveNext);
|
|
end;
|
|
|
|
{ TEnumerable<T> }
|
|
|
|
function TEnumerable<T>.ToArrayImpl(ACount: SizeInt): TArray<T>;
|
|
var
|
|
i: SizeInt;
|
|
LEnumerator: TEnumerator<T>;
|
|
begin
|
|
SetLength(Result, ACount);
|
|
|
|
try
|
|
LEnumerator := GetEnumerator;
|
|
|
|
i := 0;
|
|
while LEnumerator.MoveNext do
|
|
begin
|
|
Result[i] := LEnumerator.Current;
|
|
Inc(i);
|
|
end;
|
|
finally
|
|
LEnumerator.Free;
|
|
end;
|
|
end;
|
|
|
|
function TEnumerable<T>.GetEnumerator: TEnumerator<T>;
|
|
begin
|
|
Exit(DoGetEnumerator);
|
|
end;
|
|
|
|
function TEnumerable<T>.ToArray: TArray<T>;
|
|
var
|
|
LEnumerator: TEnumerator<T>;
|
|
LBuffer: TList<T>;
|
|
begin
|
|
LBuffer := TList<T>.Create;
|
|
try
|
|
LEnumerator := GetEnumerator;
|
|
|
|
while LEnumerator.MoveNext do
|
|
LBuffer.Add(LEnumerator.Current);
|
|
|
|
Result := LBuffer.ToArray;
|
|
finally
|
|
LBuffer.Free;
|
|
LEnumerator.Free;
|
|
end;
|
|
end;
|
|
|
|
{ TCustomPointersCollection<T, PT> }
|
|
|
|
function TCustomPointersCollection<T, PT>.Enumerable: TLocalEnumerable;
|
|
begin
|
|
Result := TLocalEnumerable(@Self);
|
|
end;
|
|
|
|
function TCustomPointersCollection<T, PT>.GetEnumerator: TEnumerator<PT>;
|
|
begin
|
|
Result := Enumerable.GetPtrEnumerator;
|
|
end;
|
|
|
|
{ TEnumerableWithPointers<T> }
|
|
|
|
function TEnumerableWithPointers<T>.GetPtr: PPointersCollection;
|
|
begin
|
|
Result := PPointersCollection(Self);
|
|
end;
|
|
|
|
{ TCustomList<T> }
|
|
|
|
function TCustomList<T>.PrepareAddingItem: SizeInt;
|
|
begin
|
|
Result := Length(FItems);
|
|
|
|
if (FLength < 4) and (Result < 4) then
|
|
SetLength(FItems, 4)
|
|
else if FLength = High(FLength) then
|
|
OutOfMemoryError
|
|
else if FLength = Result then
|
|
SetLength(FItems, CUSTOM_LIST_CAPACITY_INC);
|
|
|
|
Result := FLength;
|
|
Inc(FLength);
|
|
end;
|
|
|
|
function TCustomList<T>.PrepareAddingRange(ACount: SizeInt): SizeInt;
|
|
begin
|
|
if ACount < 0 then
|
|
raise EArgumentOutOfRangeException.CreateRes(@SArgumentOutOfRange);
|
|
if ACount = 0 then
|
|
Exit(FLength - 1);
|
|
|
|
if (FLength = 0) and (Length(FItems) = 0) then
|
|
SetLength(FItems, 4)
|
|
else if FLength = High(FLength) then
|
|
OutOfMemoryError;
|
|
|
|
Result := Length(FItems);
|
|
while Pred(FLength + ACount) >= Result do
|
|
begin
|
|
SetLength(FItems, CUSTOM_LIST_CAPACITY_INC);
|
|
Result := Length(FItems);
|
|
end;
|
|
|
|
Result := FLength;
|
|
Inc(FLength, ACount);
|
|
end;
|
|
|
|
function TCustomList<T>.ToArray: TArray<T>;
|
|
begin
|
|
Result := ToArrayImpl(Count);
|
|
end;
|
|
|
|
function TCustomList<T>.GetCount: SizeInt;
|
|
begin
|
|
Result := FLength;
|
|
end;
|
|
|
|
procedure TCustomList<T>.Notify(constref AValue: T; ACollectionNotification: TCollectionNotification);
|
|
begin
|
|
if Assigned(FOnNotify) then
|
|
FOnNotify(Self, AValue, ACollectionNotification);
|
|
end;
|
|
|
|
function TCustomList<T>.DoRemove(AIndex: SizeInt; ACollectionNotification: TCollectionNotification): T;
|
|
begin
|
|
if (AIndex < 0) or (AIndex >= FLength) then
|
|
raise EArgumentOutOfRangeException.CreateRes(@SArgumentOutOfRange);
|
|
|
|
Result := FItems[AIndex];
|
|
Dec(FLength);
|
|
|
|
FItems[AIndex] := Default(T);
|
|
if AIndex <> FLength then
|
|
begin
|
|
System.Move(FItems[AIndex + 1], FItems[AIndex], (FLength - AIndex) * SizeOf(T));
|
|
FillChar(FItems[FLength], SizeOf(T), 0);
|
|
end;
|
|
|
|
Notify(Result, ACollectionNotification);
|
|
end;
|
|
|
|
function TCustomList<T>.GetCapacity: SizeInt;
|
|
begin
|
|
Result := Length(FItems);
|
|
end;
|
|
|
|
{ TCustomListEnumerator<T> }
|
|
|
|
function TCustomListEnumerator<T>.DoMoveNext: boolean;
|
|
begin
|
|
Inc(FIndex);
|
|
Result := (FList.FLength <> 0) and (FIndex < FList.FLength)
|
|
end;
|
|
|
|
function TCustomListEnumerator<T>.DoGetCurrent: T;
|
|
begin
|
|
Result := GetCurrent;
|
|
end;
|
|
|
|
function TCustomListEnumerator<T>.GetCurrent: T;
|
|
begin
|
|
Result := FList.FItems[FIndex];
|
|
end;
|
|
|
|
constructor TCustomListEnumerator<T>.Create(AList: TCustomList<T>);
|
|
begin
|
|
inherited Create;
|
|
FIndex := -1;
|
|
FList := AList;
|
|
end;
|
|
|
|
{ TCustomListWithPointers<T>.TPointersEnumerator }
|
|
|
|
function TCustomListWithPointers<T>.TPointersEnumerator.DoMoveNext: boolean;
|
|
begin
|
|
Inc(FIndex);
|
|
Result := (FList.FLength <> 0) and (FIndex < FList.FLength)
|
|
end;
|
|
|
|
function TCustomListWithPointers<T>.TPointersEnumerator.DoGetCurrent: PT;
|
|
begin
|
|
Result := @FList.FItems[FIndex];;
|
|
end;
|
|
|
|
constructor TCustomListWithPointers<T>.TPointersEnumerator.Create(AList: TCustomListWithPointers<T>);
|
|
begin
|
|
inherited Create;
|
|
FIndex := -1;
|
|
FList := AList;
|
|
end;
|
|
|
|
{ TCustomListWithPointers<T> }
|
|
|
|
function TCustomListWithPointers<T>.GetPtrEnumerator: TEnumerator<PT>;
|
|
begin
|
|
Result := TPointersEnumerator.Create(Self);
|
|
end;
|
|
|
|
{ TList<T> }
|
|
|
|
procedure TList<T>.InitializeList;
|
|
begin
|
|
end;
|
|
|
|
constructor TList<T>.Create;
|
|
begin
|
|
InitializeList;
|
|
FComparer := TComparer<T>.Default;
|
|
end;
|
|
|
|
constructor TList<T>.Create(const AComparer: IComparer<T>);
|
|
begin
|
|
InitializeList;
|
|
FComparer := AComparer;
|
|
end;
|
|
|
|
constructor TList<T>.Create(ACollection: TEnumerable<T>);
|
|
var
|
|
LItem: T;
|
|
begin
|
|
Create;
|
|
for LItem in ACollection do
|
|
Add(LItem);
|
|
end;
|
|
|
|
{$IFDEF ENABLE_METHODS_WITH_TEnumerableWithPointers}
|
|
constructor TList<T>.Create(ACollection: TEnumerableWithPointers<T>);
|
|
var
|
|
LItem: PT;
|
|
begin
|
|
Create;
|
|
for LItem in ACollection.Ptr^ do
|
|
Add(LItem^);
|
|
end;
|
|
{$ENDIF}
|
|
|
|
destructor TList<T>.Destroy;
|
|
begin
|
|
SetCapacity(0);
|
|
end;
|
|
|
|
procedure TList<T>.SetCapacity(AValue: SizeInt);
|
|
begin
|
|
if AValue < Count then
|
|
Count := AValue;
|
|
|
|
SetLength(FItems, AValue);
|
|
end;
|
|
|
|
procedure TList<T>.SetCount(AValue: SizeInt);
|
|
begin
|
|
if AValue < 0 then
|
|
raise EArgumentOutOfRangeException.CreateRes(@SArgumentOutOfRange);
|
|
|
|
if AValue > Capacity then
|
|
Capacity := AValue;
|
|
if AValue < Count then
|
|
DeleteRange(AValue, Count - AValue);
|
|
|
|
FLength := AValue;
|
|
end;
|
|
|
|
function TList<T>.GetItem(AIndex: SizeInt): T;
|
|
begin
|
|
if (AIndex < 0) or (AIndex >= Count) then
|
|
raise EArgumentOutOfRangeException.CreateRes(@SArgumentOutOfRange);
|
|
|
|
Result := FItems[AIndex];
|
|
end;
|
|
|
|
procedure TList<T>.SetItem(AIndex: SizeInt; const AValue: T);
|
|
begin
|
|
if (AIndex < 0) or (AIndex >= Count) then
|
|
raise EArgumentOutOfRangeException.CreateRes(@SArgumentOutOfRange);
|
|
Notify(FItems[AIndex], cnRemoved);
|
|
FItems[AIndex] := AValue;
|
|
Notify(AValue, cnAdded);
|
|
end;
|
|
|
|
function TList<T>.GetEnumerator: TEnumerator;
|
|
begin
|
|
Result := TEnumerator.Create(Self);
|
|
end;
|
|
|
|
function TList<T>.DoGetEnumerator: {Generics.Collections.}TEnumerator<T>;
|
|
begin
|
|
Result := GetEnumerator;
|
|
end;
|
|
|
|
function TList<T>.Add(constref AValue: T): SizeInt;
|
|
begin
|
|
Result := PrepareAddingItem;
|
|
FItems[Result] := AValue;
|
|
Notify(AValue, cnAdded);
|
|
end;
|
|
|
|
procedure TList<T>.AddRange(constref AValues: array of T);
|
|
begin
|
|
InsertRange(Count, AValues);
|
|
end;
|
|
|
|
procedure TList<T>.AddRange(const AEnumerable: IEnumerable<T>);
|
|
var
|
|
LValue: T;
|
|
begin
|
|
for LValue in AEnumerable do
|
|
Add(LValue);
|
|
end;
|
|
|
|
procedure TList<T>.AddRange(AEnumerable: TEnumerable<T>);
|
|
var
|
|
LValue: T;
|
|
begin
|
|
for LValue in AEnumerable do
|
|
Add(LValue);
|
|
end;
|
|
|
|
{$IFDEF ENABLE_METHODS_WITH_TEnumerableWithPointers}
|
|
procedure TList<T>.AddRange(AEnumerable: TEnumerableWithPointers<T>);
|
|
var
|
|
LValue: PT;
|
|
begin
|
|
for LValue in AEnumerable.Ptr^ do
|
|
Add(LValue^);
|
|
end;
|
|
{$ENDIF}
|
|
|
|
procedure TList<T>.InternalInsert(AIndex: SizeInt; constref AValue: T);
|
|
begin
|
|
if AIndex <> PrepareAddingItem then
|
|
begin
|
|
System.Move(FItems[AIndex], FItems[AIndex + 1], ((Count - AIndex) - 1) * SizeOf(T));
|
|
FillChar(FItems[AIndex], SizeOf(T), 0);
|
|
end;
|
|
|
|
FItems[AIndex] := AValue;
|
|
Notify(AValue, cnAdded);
|
|
end;
|
|
|
|
procedure TList<T>.Insert(AIndex: SizeInt; constref AValue: T);
|
|
begin
|
|
if (AIndex < 0) or (AIndex > Count) then
|
|
raise EArgumentOutOfRangeException.CreateRes(@SArgumentOutOfRange);
|
|
|
|
InternalInsert(AIndex, AValue);
|
|
end;
|
|
|
|
procedure TList<T>.InsertRange(AIndex: SizeInt; constref AValues: array of T);
|
|
var
|
|
i: SizeInt;
|
|
LLength: SizeInt;
|
|
LValue: ^T;
|
|
begin
|
|
if (AIndex < 0) or (AIndex > Count) then
|
|
raise EArgumentOutOfRangeException.CreateRes(@SArgumentOutOfRange);
|
|
|
|
LLength := Length(AValues);
|
|
if LLength = 0 then
|
|
Exit;
|
|
|
|
if AIndex <> PrepareAddingRange(LLength) then
|
|
begin
|
|
System.Move(FItems[AIndex], FItems[AIndex + LLength], ((Count - AIndex) - LLength) * SizeOf(T));
|
|
FillChar(FItems[AIndex], SizeOf(T) * LLength, 0);
|
|
end;
|
|
|
|
LValue := @AValues[0];
|
|
for i := AIndex to Pred(AIndex + LLength) do
|
|
begin
|
|
FItems[i] := LValue^;
|
|
Notify(LValue^, cnAdded);
|
|
Inc(LValue);
|
|
end;
|
|
end;
|
|
|
|
procedure TList<T>.InsertRange(AIndex: SizeInt; const AEnumerable: IEnumerable<T>);
|
|
var
|
|
LValue: T;
|
|
i: SizeInt;
|
|
begin
|
|
if (AIndex < 0) or (AIndex > Count) then
|
|
raise EArgumentOutOfRangeException.CreateRes(@SArgumentOutOfRange);
|
|
|
|
i := 0;
|
|
for LValue in AEnumerable do
|
|
begin
|
|
InternalInsert(Aindex + i, LValue);
|
|
Inc(i);
|
|
end;
|
|
end;
|
|
|
|
procedure TList<T>.InsertRange(AIndex: SizeInt; const AEnumerable: TEnumerable<T>);
|
|
var
|
|
LValue: T;
|
|
i: SizeInt;
|
|
begin
|
|
if (AIndex < 0) or (AIndex > Count) then
|
|
raise EArgumentOutOfRangeException.CreateRes(@SArgumentOutOfRange);
|
|
|
|
i := 0;
|
|
for LValue in AEnumerable do
|
|
begin
|
|
InternalInsert(Aindex + i, LValue);
|
|
Inc(i);
|
|
end;
|
|
end;
|
|
|
|
{$IFDEF ENABLE_METHODS_WITH_TEnumerableWithPointers}
|
|
procedure TList<T>.InsertRange(AIndex: SizeInt; const AEnumerable: TEnumerableWithPointers<T>);
|
|
var
|
|
LValue: PT;
|
|
i: SizeInt;
|
|
begin
|
|
if (AIndex < 0) or (AIndex > Count) then
|
|
raise EArgumentOutOfRangeException.CreateRes(@SArgumentOutOfRange);
|
|
|
|
i := 0;
|
|
for LValue in AEnumerable.Ptr^ do
|
|
begin
|
|
InternalInsert(Aindex + i, LValue^);
|
|
Inc(i);
|
|
end;
|
|
end;
|
|
{$ENDIF}
|
|
|
|
function TList<T>.Remove(constref AValue: T): SizeInt;
|
|
begin
|
|
Result := IndexOf(AValue);
|
|
if Result >= 0 then
|
|
DoRemove(Result, cnRemoved);
|
|
end;
|
|
|
|
procedure TList<T>.Delete(AIndex: SizeInt);
|
|
begin
|
|
DoRemove(AIndex, cnRemoved);
|
|
end;
|
|
|
|
procedure TList<T>.DeleteRange(AIndex, ACount: SizeInt);
|
|
var
|
|
LDeleted: array of T;
|
|
i: SizeInt;
|
|
LMoveDelta: SizeInt;
|
|
begin
|
|
if ACount = 0 then
|
|
Exit;
|
|
|
|
if (ACount < 0) or (AIndex < 0) or (AIndex + ACount > Count) then
|
|
raise EArgumentOutOfRangeException.CreateRes(@SArgumentOutOfRange);
|
|
|
|
SetLength(LDeleted, ACount);
|
|
System.Move(FItems[AIndex], LDeleted[0], ACount * SizeOf(T));
|
|
|
|
LMoveDelta := Count - (AIndex + ACount);
|
|
|
|
if LMoveDelta = 0 then
|
|
FillChar(FItems[AIndex], ACount * SizeOf(T), #0)
|
|
else
|
|
begin
|
|
System.Move(FItems[AIndex + ACount], FItems[AIndex], LMoveDelta * SizeOf(T));
|
|
FillChar(FItems[Count - ACount], ACount * SizeOf(T), #0);
|
|
end;
|
|
|
|
Dec(FLength, ACount);
|
|
|
|
for i := 0 to High(LDeleted) do
|
|
Notify(LDeleted[i], cnRemoved);
|
|
end;
|
|
|
|
function TList<T>.ExtractIndex(const AIndex: SizeInt): T;
|
|
begin
|
|
Result := DoRemove(AIndex, cnExtracted);
|
|
end;
|
|
|
|
function TList<T>.Extract(constref AValue: T): T;
|
|
var
|
|
LIndex: SizeInt;
|
|
begin
|
|
LIndex := IndexOf(AValue);
|
|
if LIndex < 0 then
|
|
Exit(Default(T));
|
|
|
|
Result := DoRemove(LIndex, cnExtracted);
|
|
end;
|
|
|
|
procedure TList<T>.Exchange(AIndex1, AIndex2: SizeInt);
|
|
var
|
|
LTemp: T;
|
|
begin
|
|
LTemp := FItems[AIndex1];
|
|
FItems[AIndex1] := FItems[AIndex2];
|
|
FItems[AIndex2] := LTemp;
|
|
end;
|
|
|
|
procedure TList<T>.Move(AIndex, ANewIndex: SizeInt);
|
|
var
|
|
LTemp: T;
|
|
begin
|
|
if ANewIndex = AIndex then
|
|
Exit;
|
|
|
|
if (ANewIndex < 0) or (ANewIndex >= Count) then
|
|
raise EArgumentOutOfRangeException.CreateRes(@SArgumentOutOfRange);
|
|
|
|
LTemp := FItems[AIndex];
|
|
FItems[AIndex] := Default(T);
|
|
|
|
if AIndex < ANewIndex then
|
|
System.Move(FItems[Succ(AIndex)], FItems[AIndex], (ANewIndex - AIndex) * SizeOf(T))
|
|
else
|
|
System.Move(FItems[ANewIndex], FItems[Succ(ANewIndex)], (AIndex - ANewIndex) * SizeOf(T));
|
|
|
|
FillChar(FItems[ANewIndex], SizeOf(T), #0);
|
|
FItems[ANewIndex] := LTemp;
|
|
end;
|
|
|
|
function TList<T>.First: T;
|
|
begin
|
|
Result := Items[0];
|
|
end;
|
|
|
|
function TList<T>.Last: T;
|
|
begin
|
|
Result := Items[Pred(Count)];
|
|
end;
|
|
|
|
procedure TList<T>.Clear;
|
|
begin
|
|
SetCount(0);
|
|
SetCapacity(0);
|
|
end;
|
|
|
|
procedure TList<T>.TrimExcess;
|
|
begin
|
|
SetCapacity(Count);
|
|
end;
|
|
|
|
function TList<T>.Contains(constref AValue: T): Boolean;
|
|
begin
|
|
Result := IndexOf(AValue) >= 0;
|
|
end;
|
|
|
|
function TList<T>.IndexOf(constref AValue: T): SizeInt;
|
|
var
|
|
i: SizeInt;
|
|
begin
|
|
for i := 0 to Count - 1 do
|
|
if FComparer.Compare(AValue, FItems[i]) = 0 then
|
|
Exit(i);
|
|
Result := -1;
|
|
end;
|
|
|
|
function TList<T>.LastIndexOf(constref AValue: T): SizeInt;
|
|
var
|
|
i: SizeInt;
|
|
begin
|
|
for i := Count - 1 downto 0 do
|
|
if FComparer.Compare(AValue, FItems[i]) = 0 then
|
|
Exit(i);
|
|
Result := -1;
|
|
end;
|
|
|
|
procedure TList<T>.Reverse;
|
|
var
|
|
a, b: SizeInt;
|
|
LTemp: T;
|
|
begin
|
|
a := 0;
|
|
b := Count - 1;
|
|
while a < b do
|
|
begin
|
|
LTemp := FItems[a];
|
|
FItems[a] := FItems[b];
|
|
FItems[b] := LTemp;
|
|
Inc(a);
|
|
Dec(b);
|
|
end;
|
|
end;
|
|
|
|
procedure TList<T>.Sort;
|
|
begin
|
|
TArrayHelperBugHack.Sort(FItems, FComparer, 0, Count);
|
|
end;
|
|
|
|
procedure TList<T>.Sort(const AComparer: IComparer<T>);
|
|
begin
|
|
TArrayHelperBugHack.Sort(FItems, AComparer, 0, Count);
|
|
end;
|
|
|
|
function TList<T>.BinarySearch(constref AItem: T; out AIndex: SizeInt): Boolean;
|
|
begin
|
|
Result := TArrayHelperBugHack.BinarySearch(FItems, AItem, AIndex, FComparer, 0, Count);
|
|
end;
|
|
|
|
function TList<T>.BinarySearch(constref AItem: T; out AIndex: SizeInt; const AComparer: IComparer<T>): Boolean;
|
|
begin
|
|
Result := TArrayHelperBugHack.BinarySearch(FItems, AItem, AIndex, AComparer, 0, Count);
|
|
end;
|
|
|
|
{ TSortedList<T> }
|
|
|
|
procedure TSortedList<T>.InitializeList;
|
|
begin
|
|
FSortStyle := cssAuto;
|
|
end;
|
|
|
|
function TSortedList<T>.Add(constref AValue: T): SizeInt;
|
|
var
|
|
LSearchResult: TBinarySearchResult;
|
|
begin
|
|
if SortStyle <> cssAuto then
|
|
Exit(inherited Add(AValue));
|
|
if TArrayHelperBugHack.BinarySearch(FItems, AValue, LSearchResult, FComparer, 0, Count) then
|
|
case FDuplicates of
|
|
dupAccept: Result := LSearchResult.FoundIndex;
|
|
dupIgnore: Exit(LSearchResult.FoundIndex);
|
|
dupError: raise EListError.Create(SCollectionDuplicate);
|
|
end
|
|
else
|
|
begin
|
|
if LSearchResult.CandidateIndex = -1 then
|
|
Result := 0
|
|
else
|
|
if LSearchResult.CompareResult > 0 then
|
|
Result := LSearchResult.CandidateIndex
|
|
else
|
|
Result := LSearchResult.CandidateIndex + 1;
|
|
end;
|
|
|
|
InternalInsert(Result, AValue);
|
|
end;
|
|
|
|
procedure TSortedList<T>.Insert(AIndex: SizeInt; constref AValue: T);
|
|
begin
|
|
if FSortStyle = cssAuto then
|
|
raise EListError.Create(SSortedListError)
|
|
else
|
|
inherited;
|
|
end;
|
|
|
|
procedure TSortedList<T>.Exchange(AIndex1, AIndex2: SizeInt);
|
|
begin
|
|
if FSortStyle = cssAuto then
|
|
raise EListError.Create(SSortedListError)
|
|
else
|
|
inherited;
|
|
end;
|
|
|
|
procedure TSortedList<T>.Move(AIndex, ANewIndex: SizeInt);
|
|
begin
|
|
if FSortStyle = cssAuto then
|
|
raise EListError.Create(SSortedListError)
|
|
else
|
|
inherited;
|
|
end;
|
|
|
|
procedure TSortedList<T>.AddRange(constref AValues: array of T);
|
|
var
|
|
i: T;
|
|
begin
|
|
for i in AValues do
|
|
Add(i);
|
|
end;
|
|
|
|
procedure TSortedList<T>.InsertRange(AIndex: SizeInt; constref AValues: array of T);
|
|
var
|
|
LValue: T;
|
|
i: SizeInt;
|
|
begin
|
|
if (AIndex < 0) or (AIndex > Count) then
|
|
raise EArgumentOutOfRangeException.CreateRes(@SArgumentOutOfRange);
|
|
|
|
i := 0;
|
|
for LValue in AValues do
|
|
begin
|
|
InternalInsert(AIndex + i, LValue);
|
|
Inc(i);
|
|
end;
|
|
end;
|
|
|
|
function TSortedList<T>.GetSorted: boolean;
|
|
begin
|
|
Result := FSortStyle in [cssAuto, cssUser];
|
|
end;
|
|
|
|
procedure TSortedList<T>.SetSorted(AValue: boolean);
|
|
begin
|
|
if AValue then
|
|
SortStyle := cssAuto
|
|
else
|
|
SortStyle := cssNone;
|
|
end;
|
|
|
|
procedure TSortedList<T>.SetSortStyle(AValue: TCollectionSortStyle);
|
|
begin
|
|
if FSortStyle = AValue then
|
|
Exit;
|
|
if AValue = cssAuto then
|
|
Sort;
|
|
FSortStyle := AValue;
|
|
end;
|
|
|
|
function TSortedList<T>.ConsistencyCheck(ARaiseException: boolean = true): boolean;
|
|
var
|
|
i: Integer;
|
|
LCompare: SizeInt;
|
|
begin
|
|
if Sorted then
|
|
for i := 0 to Count-2 do
|
|
begin
|
|
LCompare := FComparer.Compare(FItems[i], FItems[i+1]);
|
|
if LCompare = 0 then
|
|
begin
|
|
if Duplicates <> dupAccept then
|
|
if ARaiseException then
|
|
raise EListError.Create(SCollectionDuplicate)
|
|
else
|
|
Exit(False)
|
|
end
|
|
else
|
|
if LCompare > 0 then
|
|
if ARaiseException then
|
|
raise EListError.Create(SCollectionInconsistency)
|
|
else
|
|
Exit(False)
|
|
end;
|
|
Result := True;
|
|
end;
|
|
|
|
{ TThreadList<T> }
|
|
|
|
constructor TThreadList<T>.Create;
|
|
begin
|
|
inherited Create;
|
|
FDuplicates:=dupIgnore;
|
|
{$ifdef FPC_HAS_FEATURE_THREADING}
|
|
InitCriticalSection(FLock);
|
|
{$endif}
|
|
FList := TList<T>.Create;
|
|
end;
|
|
|
|
destructor TThreadList<T>.Destroy;
|
|
begin
|
|
LockList;
|
|
try
|
|
FList.Free;
|
|
inherited Destroy;
|
|
finally
|
|
UnlockList;
|
|
{$ifdef FPC_HAS_FEATURE_THREADING}
|
|
DoneCriticalSection(FLock);
|
|
{$endif}
|
|
end;
|
|
end;
|
|
|
|
procedure TThreadList<T>.Add(constref AValue: T);
|
|
begin
|
|
LockList;
|
|
try
|
|
if (Duplicates = dupAccept) or (FList.IndexOf(AValue) = -1) then
|
|
FList.Add(AValue)
|
|
else if Duplicates = dupError then
|
|
raise EArgumentException.CreateRes(@SDuplicatesNotAllowed);
|
|
finally
|
|
UnlockList;
|
|
end;
|
|
end;
|
|
|
|
procedure TThreadList<T>.Remove(constref AValue: T);
|
|
begin
|
|
LockList;
|
|
try
|
|
FList.Remove(AValue);
|
|
finally
|
|
UnlockList;
|
|
end;
|
|
end;
|
|
|
|
procedure TThreadList<T>.Clear;
|
|
begin
|
|
LockList;
|
|
try
|
|
FList.Clear;
|
|
finally
|
|
UnlockList;
|
|
end;
|
|
end;
|
|
|
|
function TThreadList<T>.LockList: TList<T>;
|
|
begin
|
|
Result:=FList;
|
|
{$ifdef FPC_HAS_FEATURE_THREADING}
|
|
System.EnterCriticalSection(FLock);
|
|
{$endif}
|
|
end;
|
|
|
|
procedure TThreadList<T>.UnlockList;
|
|
begin
|
|
{$ifdef FPC_HAS_FEATURE_THREADING}
|
|
System.LeaveCriticalSection(FLock);
|
|
{$endif}
|
|
end;
|
|
|
|
{ TQueue<T>.TPointersEnumerator }
|
|
|
|
function TQueue<T>.TPointersEnumerator.DoMoveNext: boolean;
|
|
begin
|
|
Inc(FIndex);
|
|
Result := (FQueue.FLength <> 0) and (FIndex < FQueue.FLength)
|
|
end;
|
|
|
|
function TQueue<T>.TPointersEnumerator.DoGetCurrent: PT;
|
|
begin
|
|
Result := @FQueue.FItems[FIndex];
|
|
end;
|
|
|
|
constructor TQueue<T>.TPointersEnumerator.Create(AQueue: TQueue<T>);
|
|
begin
|
|
inherited Create;
|
|
FIndex := Pred(AQueue.FLow);
|
|
FQueue := AQueue;
|
|
end;
|
|
|
|
{ TQueue<T>.TEnumerator }
|
|
|
|
constructor TQueue<T>.TEnumerator.Create(AQueue: TQueue<T>);
|
|
begin
|
|
inherited Create(AQueue);
|
|
|
|
FIndex := Pred(AQueue.FLow);
|
|
end;
|
|
|
|
{ TQueue<T> }
|
|
|
|
function TQueue<T>.GetPtrEnumerator: TEnumerator<PT>;
|
|
begin
|
|
Result := TPointersenumerator.Create(Self);
|
|
end;
|
|
|
|
function TQueue<T>.GetEnumerator: TEnumerator;
|
|
begin
|
|
Result := TEnumerator.Create(Self);
|
|
end;
|
|
|
|
function TQueue<T>.DoGetEnumerator: {Generics.Collections.}TEnumerator<T>;
|
|
begin
|
|
Result := GetEnumerator;
|
|
end;
|
|
|
|
function TQueue<T>.DoRemove(AIndex: SizeInt; ACollectionNotification: TCollectionNotification): T;
|
|
begin
|
|
Result := FItems[AIndex];
|
|
FItems[AIndex] := Default(T);
|
|
Inc(FLow);
|
|
if FLow = FLength then
|
|
begin
|
|
FLow := 0;
|
|
FLength := 0;
|
|
end;
|
|
Notify(Result, ACollectionNotification);
|
|
end;
|
|
|
|
procedure TQueue<T>.SetCapacity(AValue: SizeInt);
|
|
begin
|
|
if AValue < Count then
|
|
raise EArgumentOutOfRangeException.CreateRes(@SArgumentOutOfRange);
|
|
|
|
if AValue = FLength then
|
|
Exit;
|
|
|
|
if (Count > 0) and (FLow > 0) then
|
|
begin
|
|
Move(FItems[FLow], FItems[0], Count * SizeOf(T));
|
|
FillChar(FItems[Count], (FLength - Count) * SizeOf(T), #0);
|
|
end;
|
|
|
|
SetLength(FItems, AValue);
|
|
FLength := Count;
|
|
FLow := 0;
|
|
end;
|
|
|
|
function TQueue<T>.GetCount: SizeInt;
|
|
begin
|
|
Result := FLength - FLow;
|
|
end;
|
|
|
|
constructor TQueue<T>.Create(ACollection: TEnumerable<T>);
|
|
var
|
|
LItem: T;
|
|
begin
|
|
for LItem in ACollection do
|
|
Enqueue(LItem);
|
|
end;
|
|
|
|
{$IFDEF ENABLE_METHODS_WITH_TEnumerableWithPointers}
|
|
constructor TQueue<T>.Create(ACollection: TEnumerableWithPointers<T>);
|
|
var
|
|
LItem: PT;
|
|
begin
|
|
for LItem in ACollection.Ptr^ do
|
|
Enqueue(LItem^);
|
|
end;
|
|
{$ENDIF}
|
|
|
|
destructor TQueue<T>.Destroy;
|
|
begin
|
|
Clear;
|
|
end;
|
|
|
|
procedure TQueue<T>.Enqueue(constref AValue: T);
|
|
var
|
|
LIndex: SizeInt;
|
|
begin
|
|
LIndex := PrepareAddingItem;
|
|
FItems[LIndex] := AValue;
|
|
Notify(AValue, cnAdded);
|
|
end;
|
|
|
|
function TQueue<T>.Dequeue: T;
|
|
begin
|
|
Result := DoRemove(FLow, cnRemoved);
|
|
end;
|
|
|
|
function TQueue<T>.Extract: T;
|
|
begin
|
|
Result := DoRemove(FLow, cnExtracted);
|
|
end;
|
|
|
|
function TQueue<T>.Peek: T;
|
|
begin
|
|
if (Count = 0) then
|
|
raise EArgumentOutOfRangeException.CreateRes(@SArgumentOutOfRange);
|
|
|
|
Result := FItems[FLow];
|
|
end;
|
|
|
|
procedure TQueue<T>.Clear;
|
|
begin
|
|
while Count <> 0 do
|
|
Dequeue;
|
|
FLow := 0;
|
|
FLength := 0;
|
|
end;
|
|
|
|
procedure TQueue<T>.TrimExcess;
|
|
begin
|
|
SetCapacity(Count);
|
|
end;
|
|
|
|
{ TStack<T> }
|
|
|
|
function TStack<T>.GetEnumerator: TEnumerator;
|
|
begin
|
|
Result := TEnumerator.Create(Self);
|
|
end;
|
|
|
|
function TStack<T>.DoGetEnumerator: {Generics.Collections.}TEnumerator<T>;
|
|
begin
|
|
Result := GetEnumerator;
|
|
end;
|
|
|
|
constructor TStack<T>.Create(ACollection: TEnumerable<T>);
|
|
var
|
|
LItem: T;
|
|
begin
|
|
for LItem in ACollection do
|
|
Push(LItem);
|
|
end;
|
|
|
|
{$IFDEF ENABLE_METHODS_WITH_TEnumerableWithPointers}
|
|
constructor TStack<T>.Create(ACollection: TEnumerableWithPointers<T>);
|
|
var
|
|
LItem: PT;
|
|
begin
|
|
for LItem in ACollection.Ptr^ do
|
|
Push(LItem^);
|
|
end;
|
|
{$ENDIF}
|
|
|
|
function TStack<T>.DoRemove(AIndex: SizeInt; ACollectionNotification: TCollectionNotification): T;
|
|
begin
|
|
if AIndex < 0 then
|
|
raise EArgumentOutOfRangeException.CreateRes(@SArgumentOutOfRange);
|
|
|
|
Result := FItems[AIndex];
|
|
FItems[AIndex] := Default(T);
|
|
Dec(FLength);
|
|
Notify(Result, ACollectionNotification);
|
|
end;
|
|
|
|
destructor TStack<T>.Destroy;
|
|
begin
|
|
Clear;
|
|
end;
|
|
|
|
procedure TStack<T>.Clear;
|
|
begin
|
|
while Count <> 0 do
|
|
Pop;
|
|
end;
|
|
|
|
procedure TStack<T>.SetCapacity(AValue: SizeInt);
|
|
begin
|
|
if AValue < Count then
|
|
AValue := Count;
|
|
|
|
SetLength(FItems, AValue);
|
|
end;
|
|
|
|
procedure TStack<T>.Push(constref AValue: T);
|
|
var
|
|
LIndex: SizeInt;
|
|
begin
|
|
LIndex := PrepareAddingItem;
|
|
FItems[LIndex] := AValue;
|
|
Notify(AValue, cnAdded);
|
|
end;
|
|
|
|
function TStack<T>.Pop: T;
|
|
begin
|
|
Result := DoRemove(FLength - 1, cnRemoved);
|
|
end;
|
|
|
|
function TStack<T>.Peek: T;
|
|
begin
|
|
if (Count = 0) then
|
|
raise EArgumentOutOfRangeException.CreateRes(@SArgumentOutOfRange);
|
|
|
|
Result := FItems[FLength - 1];
|
|
end;
|
|
|
|
function TStack<T>.Extract: T;
|
|
begin
|
|
Result := DoRemove(FLength - 1, cnExtracted);
|
|
end;
|
|
|
|
procedure TStack<T>.TrimExcess;
|
|
begin
|
|
SetCapacity(Count);
|
|
end;
|
|
|
|
{ TObjectList<T> }
|
|
|
|
procedure TObjectList<T>.Notify(constref AValue: T; ACollectionNotification: TCollectionNotification);
|
|
begin
|
|
inherited Notify(AValue, ACollectionNotification);
|
|
|
|
if FObjectsOwner and (ACollectionNotification = cnRemoved) then
|
|
TObject(AValue).Free;
|
|
end;
|
|
|
|
constructor TObjectList<T>.Create(AOwnsObjects: Boolean);
|
|
begin
|
|
inherited Create;
|
|
|
|
FObjectsOwner := AOwnsObjects;
|
|
end;
|
|
|
|
constructor TObjectList<T>.Create(const AComparer: IComparer<T>; AOwnsObjects: Boolean);
|
|
begin
|
|
inherited Create(AComparer);
|
|
|
|
FObjectsOwner := AOwnsObjects;
|
|
end;
|
|
|
|
constructor TObjectList<T>.Create(ACollection: TEnumerable<T>; AOwnsObjects: Boolean);
|
|
begin
|
|
inherited Create(ACollection);
|
|
|
|
FObjectsOwner := AOwnsObjects;
|
|
end;
|
|
|
|
{$IFDEF ENABLE_METHODS_WITH_TEnumerableWithPointers}
|
|
constructor TObjectList<T>.Create(ACollection: TEnumerableWithPointers<T>; AOwnsObjects: Boolean);
|
|
begin
|
|
inherited Create(ACollection);
|
|
|
|
FObjectsOwner := AOwnsObjects;
|
|
end;
|
|
{$ENDIF}
|
|
|
|
{ TObjectQueue<T> }
|
|
|
|
procedure TObjectQueue<T>.Notify(constref AValue: T; ACollectionNotification: TCollectionNotification);
|
|
begin
|
|
inherited Notify(AValue, ACollectionNotification);
|
|
if FObjectsOwner and (ACollectionNotification = cnRemoved) then
|
|
TObject(AValue).Free;
|
|
end;
|
|
|
|
constructor TObjectQueue<T>.Create(AOwnsObjects: Boolean);
|
|
begin
|
|
inherited Create;
|
|
|
|
FObjectsOwner := AOwnsObjects;
|
|
end;
|
|
|
|
constructor TObjectQueue<T>.Create(ACollection: TEnumerable<T>; AOwnsObjects: Boolean);
|
|
begin
|
|
inherited Create(ACollection);
|
|
|
|
FObjectsOwner := AOwnsObjects;
|
|
end;
|
|
|
|
{$IFDEF ENABLE_METHODS_WITH_TEnumerableWithPointers}
|
|
constructor TObjectQueue<T>.Create(ACollection: TEnumerableWithPointers<T>; AOwnsObjects: Boolean);
|
|
begin
|
|
inherited Create(ACollection);
|
|
|
|
FObjectsOwner := AOwnsObjects;
|
|
end;
|
|
{$ENDIF}
|
|
|
|
procedure TObjectQueue<T>.Dequeue;
|
|
begin
|
|
inherited Dequeue;
|
|
end;
|
|
|
|
{ TObjectStack<T> }
|
|
|
|
procedure TObjectStack<T>.Notify(constref AValue: T; ACollectionNotification: TCollectionNotification);
|
|
begin
|
|
inherited Notify(AValue, ACollectionNotification);
|
|
if FObjectsOwner and (ACollectionNotification = cnRemoved) then
|
|
TObject(AValue).Free;
|
|
end;
|
|
|
|
constructor TObjectStack<T>.Create(AOwnsObjects: Boolean);
|
|
begin
|
|
inherited Create;
|
|
|
|
FObjectsOwner := AOwnsObjects;
|
|
end;
|
|
|
|
constructor TObjectStack<T>.Create(ACollection: TEnumerable<T>; AOwnsObjects: Boolean);
|
|
begin
|
|
inherited Create(ACollection);
|
|
|
|
FObjectsOwner := AOwnsObjects;
|
|
end;
|
|
|
|
{$IFDEF ENABLE_METHODS_WITH_TEnumerableWithPointers}
|
|
constructor TObjectStack<T>.Create(ACollection: TEnumerableWithPointers<T>; AOwnsObjects: Boolean);
|
|
begin
|
|
inherited Create(ACollection);
|
|
|
|
FObjectsOwner := AOwnsObjects;
|
|
end;
|
|
{$ENDIF}
|
|
|
|
function TObjectStack<T>.Pop: T;
|
|
begin
|
|
Result := inherited Pop;
|
|
end;
|
|
|
|
{$I inc\generics.dictionaries.inc}
|
|
|
|
{ TCustomSet<T>.TCustomSetEnumerator }
|
|
|
|
function TCustomSet<T>.TCustomSetEnumerator.DoMoveNext: boolean;
|
|
begin
|
|
Result := FEnumerator.DoMoveNext;
|
|
end;
|
|
|
|
function TCustomSet<T>.TCustomSetEnumerator.DoGetCurrent: T;
|
|
begin
|
|
Result := FEnumerator.DoGetCurrent;
|
|
end;
|
|
|
|
destructor TCustomSet<T>.TCustomSetEnumerator.Destroy;
|
|
begin
|
|
FEnumerator.Free;
|
|
end;
|
|
|
|
{ TCustomSet<T> }
|
|
|
|
function TCustomSet<T>.DoGetEnumerator: Generics.Collections.TEnumerator<T>;
|
|
begin
|
|
Result := GetEnumerator;
|
|
end;
|
|
|
|
constructor TCustomSet<T>.Create(ACollection: TEnumerable<T>);
|
|
var
|
|
i: T;
|
|
begin
|
|
Create;
|
|
for i in ACollection do
|
|
Add(i);
|
|
end;
|
|
|
|
{$IFDEF ENABLE_METHODS_WITH_TEnumerableWithPointers}
|
|
constructor TCustomSet<T>.Create(ACollection: TEnumerableWithPointers<T>);
|
|
var
|
|
i: PT;
|
|
begin
|
|
Create;
|
|
for i in ACollection.Ptr^ do
|
|
Add(i^);
|
|
end;
|
|
{$ENDIF}
|
|
|
|
function TCustomSet<T>.AddRange(constref AValues: array of T): Boolean;
|
|
var
|
|
i: T;
|
|
begin
|
|
Result := True;
|
|
for i in AValues do
|
|
Result := Add(i) and Result;
|
|
end;
|
|
|
|
function TCustomSet<T>.AddRange(const AEnumerable: IEnumerable<T>): Boolean;
|
|
var
|
|
i: T;
|
|
begin
|
|
Result := True;
|
|
for i in AEnumerable do
|
|
Result := Add(i) and Result;
|
|
end;
|
|
|
|
function TCustomSet<T>.AddRange(AEnumerable: TEnumerable<T>): Boolean;
|
|
var
|
|
i: T;
|
|
begin
|
|
Result := True;
|
|
for i in AEnumerable do
|
|
Result := Add(i) and Result;
|
|
end;
|
|
|
|
{$IFDEF ENABLE_METHODS_WITH_TEnumerableWithPointers}
|
|
function TCustomSet<T>.AddRange(AEnumerable: TEnumerableWithPointers<T>): Boolean;
|
|
var
|
|
i: PT;
|
|
begin
|
|
Result := True;
|
|
for i in AEnumerable.Ptr^ do
|
|
Result := Add(i^) and Result;
|
|
end;
|
|
{$ENDIF}
|
|
|
|
procedure TCustomSet<T>.UnionWith(AHashSet: TCustomSet<T>);
|
|
var
|
|
i: PT;
|
|
begin
|
|
for i in AHashSet.Ptr^ do
|
|
Add(i^);
|
|
end;
|
|
|
|
procedure TCustomSet<T>.IntersectWith(AHashSet: TCustomSet<T>);
|
|
var
|
|
LList: TList<PT>;
|
|
i: PT;
|
|
begin
|
|
LList := TList<PT>.Create;
|
|
|
|
for i in Ptr^ do
|
|
if not AHashSet.Contains(i^) then
|
|
LList.Add(i);
|
|
|
|
for i in LList do
|
|
Remove(i^);
|
|
|
|
LList.Free;
|
|
end;
|
|
|
|
procedure TCustomSet<T>.ExceptWith(AHashSet: TCustomSet<T>);
|
|
var
|
|
i: PT;
|
|
begin
|
|
for i in AHashSet.Ptr^ do
|
|
Remove(i^);
|
|
end;
|
|
|
|
procedure TCustomSet<T>.SymmetricExceptWith(AHashSet: TCustomSet<T>);
|
|
var
|
|
LList: TList<PT>;
|
|
i: PT;
|
|
begin
|
|
LList := TList<PT>.Create;
|
|
|
|
for i in AHashSet.Ptr^ do
|
|
if Contains(i^) then
|
|
LList.Add(i)
|
|
else
|
|
Add(i^);
|
|
|
|
for i in LList do
|
|
Remove(i^);
|
|
|
|
LList.Free;
|
|
end;
|
|
|
|
{ THashSet<T>.THashSetEnumerator }
|
|
|
|
function THashSet<T>.THashSetEnumerator.GetCurrent: T;
|
|
begin
|
|
Result := TDictionaryEnumerator(FEnumerator).GetCurrent;
|
|
end;
|
|
|
|
constructor THashSet<T>.THashSetEnumerator.Create(ASet: TCustomSet<T>);
|
|
begin
|
|
TDictionaryEnumerator(FEnumerator) := THashSet<T>(ASet).FInternalDictionary.Keys.DoGetEnumerator;
|
|
end;
|
|
|
|
{ THashSet<T>.TPointersEnumerator }
|
|
|
|
function THashSet<T>.TPointersEnumerator.DoMoveNext: boolean;
|
|
begin
|
|
Result := FEnumerator.MoveNext;
|
|
end;
|
|
|
|
function THashSet<T>.TPointersEnumerator.DoGetCurrent: PT;
|
|
begin
|
|
Result := FEnumerator.Current;
|
|
end;
|
|
|
|
constructor THashSet<T>.TPointersEnumerator.Create(AHashSet: THashSet<T>);
|
|
begin
|
|
FEnumerator := AHashSet.FInternalDictionary.Keys.Ptr^.GetEnumerator;
|
|
end;
|
|
|
|
{ THashSet<T> }
|
|
|
|
procedure THashSet<T>.InternalDictionaryNotify(ASender: TObject; constref AItem: T; AAction: TCollectionNotification);
|
|
begin
|
|
FOnNotify(Self, AItem, AAction);
|
|
end;
|
|
|
|
function THashSet<T>.GetPtrEnumerator: TEnumerator<PT>;
|
|
begin
|
|
Result := TPointersEnumerator.Create(Self);
|
|
end;
|
|
|
|
function THashSet<T>.GetCount: SizeInt;
|
|
begin
|
|
Result := FInternalDictionary.Count;
|
|
end;
|
|
|
|
function THashSet<T>.GetCapacity: SizeInt;
|
|
begin
|
|
Result := FInternalDictionary.Capacity;
|
|
end;
|
|
|
|
procedure THashSet<T>.SetCapacity(AValue: SizeInt);
|
|
begin
|
|
FInternalDictionary.Capacity := AValue;
|
|
end;
|
|
|
|
function THashSet<T>.GetOnNotify: TCollectionNotifyEvent<T>;
|
|
begin
|
|
Result := FInternalDictionary.OnKeyNotify;
|
|
end;
|
|
|
|
procedure THashSet<T>.SetOnNotify(AValue: TCollectionNotifyEvent<T>);
|
|
begin
|
|
FOnNotify := AValue;
|
|
if Assigned(AValue) then
|
|
FInternalDictionary.OnKeyNotify := InternalDictionaryNotify
|
|
else
|
|
FInternalDictionary.OnKeyNotify := nil;
|
|
end;
|
|
|
|
function THashSet<T>.GetEnumerator: TCustomSetEnumerator;
|
|
begin
|
|
Result := THashSetEnumerator.Create(Self);
|
|
end;
|
|
|
|
constructor THashSet<T>.Create;
|
|
begin
|
|
FInternalDictionary := TOpenAddressingLP<T, TEmptyRecord>.Create;
|
|
end;
|
|
|
|
constructor THashSet<T>.Create(const AComparer: IEqualityComparer<T>);
|
|
begin
|
|
FInternalDictionary := TOpenAddressingLP<T, TEmptyRecord>.Create(AComparer);
|
|
end;
|
|
|
|
destructor THashSet<T>.Destroy;
|
|
begin
|
|
FInternalDictionary.Free;
|
|
end;
|
|
|
|
function THashSet<T>.Add(constref AValue: T): Boolean;
|
|
begin
|
|
Result := not FInternalDictionary.ContainsKey(AValue);
|
|
if Result then
|
|
FInternalDictionary.Add(AValue, EmptyRecord);
|
|
end;
|
|
|
|
function THashSet<T>.Remove(constref AValue: T): Boolean;
|
|
var
|
|
LIndex: SizeInt;
|
|
begin
|
|
LIndex := FInternalDictionary.FindBucketIndex(AValue);
|
|
Result := LIndex >= 0;
|
|
if Result then
|
|
FInternalDictionary.DoRemove(LIndex, cnRemoved);
|
|
end;
|
|
|
|
function THashSet<T>.Extract(constref AValue: T): T;
|
|
var
|
|
LIndex: SizeInt;
|
|
begin
|
|
LIndex := FInternalDictionary.FindBucketIndex(AValue);
|
|
if LIndex < 0 then
|
|
Exit(Default(T));
|
|
|
|
Result := AValue;
|
|
FInternalDictionary.DoRemove(LIndex, cnExtracted);
|
|
end;
|
|
|
|
procedure THashSet<T>.Clear;
|
|
begin
|
|
FInternalDictionary.Clear;
|
|
end;
|
|
|
|
function THashSet<T>.Contains(constref AValue: T): Boolean;
|
|
begin
|
|
Result := FInternalDictionary.ContainsKey(AValue);
|
|
end;
|
|
|
|
procedure THashSet<T>.TrimExcess;
|
|
begin
|
|
FInternalDictionary.TrimExcess;
|
|
end;
|
|
|
|
{ TAVLTreeNode<TREE_CONSTRAINTS, TTree> }
|
|
|
|
function TAVLTreeNode<TREE_CONSTRAINTS, TTree>.Successor: PNode;
|
|
begin
|
|
Result:=Right;
|
|
if Result<>nil then begin
|
|
while (Result.Left<>nil) do Result:=Result.Left;
|
|
end else begin
|
|
Result:=@Self;
|
|
while (Result.Parent<>nil) and (Result.Parent.Right=Result) do
|
|
Result:=Result.Parent;
|
|
Result:=Result.Parent;
|
|
end;
|
|
end;
|
|
|
|
function TAVLTreeNode<TREE_CONSTRAINTS, TTree>.Precessor: PNode;
|
|
begin
|
|
Result:=Left;
|
|
if Result<>nil then begin
|
|
while (Result.Right<>nil) do Result:=Result.Right;
|
|
end else begin
|
|
Result:=@Self;
|
|
while (Result.Parent<>nil) and (Result.Parent.Left=Result) do
|
|
Result:=Result.Parent;
|
|
Result:=Result.Parent;
|
|
end;
|
|
end;
|
|
|
|
function TAVLTreeNode<TREE_CONSTRAINTS, TTree>.TreeDepth: integer;
|
|
// longest WAY down. e.g. only one node => 0 !
|
|
var LeftDepth, RightDepth: integer;
|
|
begin
|
|
if Left<>nil then
|
|
LeftDepth:=Left.TreeDepth+1
|
|
else
|
|
LeftDepth:=0;
|
|
if Right<>nil then
|
|
RightDepth:=Right.TreeDepth+1
|
|
else
|
|
RightDepth:=0;
|
|
if LeftDepth>RightDepth then
|
|
Result:=LeftDepth
|
|
else
|
|
Result:=RightDepth;
|
|
end;
|
|
|
|
procedure TAVLTreeNode<TREE_CONSTRAINTS, TTree>.ConsistencyCheck(ATree: TObject);
|
|
var
|
|
LTree: TTree absolute ATree;
|
|
LeftDepth: SizeInt;
|
|
RightDepth: SizeInt;
|
|
begin
|
|
// test left child
|
|
if Left<>nil then begin
|
|
if Left.Parent<>@Self then
|
|
raise EAVLTree.Create('Left.Parent<>Self');
|
|
if LTree.Compare(Left.Data.Key,Data.Key)>0 then
|
|
raise EAVLTree.Create('Compare(Left.Data,Data)>0');
|
|
Left.ConsistencyCheck(LTree);
|
|
end;
|
|
// test right child
|
|
if Right<>nil then begin
|
|
if Right.Parent<>@Self then
|
|
raise EAVLTree.Create('Right.Parent<>Self');
|
|
if LTree.Compare(Data.Key,Right.Data.Key)>0 then
|
|
raise EAVLTree.Create('Compare(Data,Right.Data)>0');
|
|
Right.ConsistencyCheck(LTree);
|
|
end;
|
|
// test balance
|
|
if Left<>nil then
|
|
LeftDepth:=Left.TreeDepth+1
|
|
else
|
|
LeftDepth:=0;
|
|
if Right<>nil then
|
|
RightDepth:=Right.TreeDepth+1
|
|
else
|
|
RightDepth:=0;
|
|
if Balance<>(LeftDepth-RightDepth) then
|
|
raise EAVLTree.CreateFmt('Balance[%d]<>(RightDepth[%d]-LeftDepth[%d])', [Balance, RightDepth, LeftDepth]);
|
|
end;
|
|
|
|
function TAVLTreeNode<TREE_CONSTRAINTS, TTree>.GetCount: SizeInt;
|
|
begin
|
|
Result:=1;
|
|
if Assigned(Left) then Inc(Result,Left.GetCount);
|
|
if Assigned(Right) then Inc(Result,Right.GetCount);
|
|
end;
|
|
|
|
{ TCustomTreeEnumerator<T, PNode, TTree> }
|
|
|
|
function TCustomTreeEnumerator<T, PNode, TTree>.DoGetCurrent: T;
|
|
begin
|
|
Result := GetCurrent;
|
|
end;
|
|
|
|
constructor TCustomTreeEnumerator<T, PNode, TTree>.Create(ATree: TObject);
|
|
begin
|
|
TObject(FTree) := ATree;
|
|
end;
|
|
|
|
{ TTreeEnumerable<TTreeEnumerator, TTreePointersEnumerator, T, PT, TREE_CONSTRAINTS> }
|
|
|
|
function TTreeEnumerable<TTreeEnumerator, TTreePointersEnumerator, T, PT, PNode, TTree>.GetCount: SizeInt;
|
|
begin
|
|
Result := FTree.Count;
|
|
end;
|
|
|
|
function TTreeEnumerable<TTreeEnumerator, TTreePointersEnumerator, T, PT, PNode, TTree>.GetPtrEnumerator: TEnumerator<PT>;
|
|
begin
|
|
Result := TTreePointersEnumerator.Create(FTree);
|
|
end;
|
|
|
|
constructor TTreeEnumerable<TTreeEnumerator, TTreePointersEnumerator, T, PT, PNode, TTree>.Create(
|
|
ATree: TTree);
|
|
begin
|
|
FTree := ATree;
|
|
end;
|
|
|
|
function TTreeEnumerable<TTreeEnumerator, TTreePointersEnumerator, T, PT, PNode, TTree>.
|
|
DoGetEnumerator: TTreeEnumerator;
|
|
begin
|
|
Result := TTreeEnumerator.Create(FTree);
|
|
end;
|
|
|
|
function TTreeEnumerable<TTreeEnumerator, TTreePointersEnumerator, T, PT, PNode, TTree>.ToArray: TArray<T>;
|
|
begin
|
|
Result := ToArrayImpl(FTree.Count);
|
|
end;
|
|
|
|
{ TAVLTreeEnumerator<T, PNode, TTree> }
|
|
|
|
function TAVLTreeEnumerator<T, PNode, TTree>.DoMoveNext: Boolean;
|
|
begin
|
|
if FLowToHigh then begin
|
|
if FCurrent<>nil then
|
|
FCurrent:=FCurrent.Successor
|
|
else
|
|
FCurrent:=FTree.FindLowest;
|
|
end else begin
|
|
if FCurrent<>nil then
|
|
FCurrent:=FCurrent.Precessor
|
|
else
|
|
FCurrent:=FTree.FindHighest;
|
|
end;
|
|
Result:=FCurrent<>nil;
|
|
end;
|
|
|
|
constructor TAVLTreeEnumerator<T, PNode, TTree>.Create(ATree: TObject; ALowToHigh: boolean);
|
|
begin
|
|
inherited Create(ATree);
|
|
FLowToHigh:=aLowToHigh;
|
|
end;
|
|
|
|
{ TCustomAVLTreeMap<TREE_CONSTRAINTS>.TPairEnumerator }
|
|
|
|
function TCustomAVLTreeMap<TREE_CONSTRAINTS>.TPairEnumerator.GetCurrent: TTreePair;
|
|
begin
|
|
Result := TTreePair((@FCurrent.Data)^);
|
|
end;
|
|
|
|
{ TCustomAVLTreeMap<TREE_CONSTRAINTS>.TNodeEnumerator }
|
|
|
|
function TCustomAVLTreeMap<TREE_CONSTRAINTS>.TNodeEnumerator.GetCurrent: PNode;
|
|
begin
|
|
Result := FCurrent;
|
|
end;
|
|
|
|
{ TCustomAVLTreeMap<TREE_CONSTRAINTS>.TKeyEnumerator }
|
|
|
|
function TCustomAVLTreeMap<TREE_CONSTRAINTS>.TKeyEnumerator.GetCurrent: TKey;
|
|
begin
|
|
Result := FCurrent.Key;
|
|
end;
|
|
|
|
{ TCustomAVLTreeMap<TREE_CONSTRAINTS>.TPKeyEnumerator }
|
|
|
|
function TCustomAVLTreeMap<TREE_CONSTRAINTS>.TPKeyEnumerator.GetCurrent: PKey;
|
|
begin
|
|
Result := @FCurrent.Data.Key;
|
|
end;
|
|
|
|
{ TCustomAVLTreeMap<TREE_CONSTRAINTS>.TValueEnumerator }
|
|
|
|
function TCustomAVLTreeMap<TREE_CONSTRAINTS>.TValueEnumerator.GetCurrent: TValue;
|
|
begin
|
|
Result := FCurrent.Value;
|
|
end;
|
|
|
|
{ TCustomAVLTreeMap<TREE_CONSTRAINTS>.TValueEnumerator }
|
|
|
|
function TCustomAVLTreeMap<TREE_CONSTRAINTS>.TPValueEnumerator.GetCurrent: PValue;
|
|
begin
|
|
Result := @FCurrent.Data.Value;
|
|
end;
|
|
|
|
{ TCustomAVLTreeMap<TREE_CONSTRAINTS> }
|
|
|
|
procedure TCustomAVLTreeMap<TREE_CONSTRAINTS>.NodeAdded(ANode: PNode);
|
|
begin
|
|
end;
|
|
|
|
procedure TCustomAVLTreeMap<TREE_CONSTRAINTS>.DeletingNode(ANode: PNode; AOrigin: boolean);
|
|
begin
|
|
end;
|
|
|
|
function TCustomAVLTreeMap<TREE_CONSTRAINTS>.DoRemove(ANode: PNode;
|
|
ACollectionNotification: TCollectionNotification; ADispose: boolean): TValue;
|
|
begin
|
|
if ANode=nil then
|
|
raise EArgumentNilException.CreateRes(@SArgumentNilNode);
|
|
|
|
if (ANode.Left = nil) or (ANode.Right = nil) then
|
|
DeletingNode(ANode, true);
|
|
|
|
InternalDelete(ANode);
|
|
|
|
Dec(FCount);
|
|
NodeNotify(ANode, ACollectionNotification, ADispose);
|
|
|
|
if ADispose then
|
|
Dispose(ANode);
|
|
end;
|
|
|
|
procedure TCustomAVLTreeMap<TREE_CONSTRAINTS>.DisposeAllNodes(ANode: PNode);
|
|
begin
|
|
if ANode.Left<>nil then
|
|
DisposeAllNodes(ANode.Left);
|
|
if ANode.Right<>nil then
|
|
DisposeAllNodes(ANode.Right);
|
|
|
|
NodeNotify(ANode, cnRemoved, true);
|
|
Dispose(ANode);
|
|
end;
|
|
|
|
function TCustomAVLTreeMap<TREE_CONSTRAINTS>.Compare(constref ALeft, ARight: TKey): Integer; inline;
|
|
begin
|
|
Result := FComparer.Compare(ALeft, ARight);
|
|
end;
|
|
|
|
function TCustomAVLTreeMap<TREE_CONSTRAINTS>.FindPredecessor(ANode: PNode): PNode;
|
|
begin
|
|
if ANode <> nil then
|
|
begin
|
|
if ANode.Left <> nil then
|
|
begin
|
|
ANode := ANode.Left;
|
|
while ANode.Right <> nil do ANode := ANode.Right;
|
|
end
|
|
else
|
|
repeat
|
|
Result := ANode;
|
|
ANode := ANode.Parent;
|
|
until (ANode = nil) or (ANode.Right = Result);
|
|
end;
|
|
Result := ANode;
|
|
end;
|
|
|
|
function TCustomAVLTreeMap<TREE_CONSTRAINTS>.FindInsertNode(ANode: PNode; out AInsertNode: PNode): Integer;
|
|
begin
|
|
AInsertNode := FRoot;
|
|
if AInsertNode = nil then // first item in tree
|
|
Exit(0);
|
|
|
|
repeat
|
|
Result := Compare(ANode.Key,AInsertNode.Key);
|
|
if Result < 0 then
|
|
begin
|
|
Result:=-1;
|
|
if AInsertNode.Left = nil then
|
|
Exit;
|
|
AInsertNode := AInsertNode.Left;
|
|
end
|
|
else
|
|
begin
|
|
if Result > 0 then
|
|
Result:=1;
|
|
if AInsertNode.Right = nil then
|
|
Exit;
|
|
AInsertNode := AInsertNode.Right;
|
|
if Result = 0 then
|
|
Break;
|
|
end;
|
|
until false;
|
|
|
|
// for equal items (when item already exist) we need to keep 0 result
|
|
while true do
|
|
if Compare(ANode.Key,AInsertNode.Key) < 0 then
|
|
begin
|
|
if AInsertNode.Left = nil then
|
|
Exit;
|
|
AInsertNode := AInsertNode.Left;
|
|
end
|
|
else
|
|
begin
|
|
if AInsertNode.Right = nil then
|
|
Exit;
|
|
AInsertNode := AInsertNode.Right;
|
|
end;
|
|
end;
|
|
|
|
procedure TCustomAVLTreeMap<TREE_CONSTRAINTS>.RotateRightRight(ANode: PNode);
|
|
var
|
|
LNode, LParent: PNode;
|
|
begin
|
|
LNode := ANode.Right;
|
|
LParent := ANode.Parent;
|
|
|
|
ANode.Right := LNode.Left;
|
|
if ANode.Right <> nil then
|
|
ANode.Right.Parent := ANode;
|
|
|
|
LNode.Left := ANode;
|
|
LNode.Parent := LParent;
|
|
ANode.Parent := LNode;
|
|
|
|
if LParent <> nil then
|
|
begin
|
|
if LParent.Left = ANode then
|
|
LParent.Left := LNode
|
|
else
|
|
LParent.Right := LNode;
|
|
end
|
|
else
|
|
FRoot := LNode;
|
|
|
|
if LNode.Balance = -1 then
|
|
begin
|
|
ANode.Balance := 0;
|
|
LNode.Balance := 0;
|
|
end
|
|
else
|
|
begin
|
|
ANode.Balance := -1;
|
|
LNode.Balance := 1;
|
|
end
|
|
end;
|
|
|
|
procedure TCustomAVLTreeMap<TREE_CONSTRAINTS>.RotateLeftLeft(ANode: PNode);
|
|
var
|
|
LNode, LParent: PNode;
|
|
begin
|
|
LNode := ANode.Left;
|
|
LParent := ANode.Parent;
|
|
|
|
ANode.Left := LNode.Right;
|
|
if ANode.Left <> nil then
|
|
ANode.Left.Parent := ANode;
|
|
|
|
LNode.Right := ANode;
|
|
LNode.Parent := LParent;
|
|
ANode.Parent := LNode;
|
|
|
|
if LParent <> nil then
|
|
begin
|
|
if LParent.Left = ANode then
|
|
LParent.Left := LNode
|
|
else
|
|
LParent.Right := LNode;
|
|
end
|
|
else
|
|
FRoot := LNode;
|
|
|
|
if LNode.Balance = 1 then
|
|
begin
|
|
ANode.Balance := 0;
|
|
LNode.Balance := 0;
|
|
end
|
|
else
|
|
begin
|
|
ANode.Balance := 1;
|
|
LNode.Balance := -1;
|
|
end
|
|
end;
|
|
|
|
procedure TCustomAVLTreeMap<TREE_CONSTRAINTS>.RotateRightLeft(ANode: PNode);
|
|
var
|
|
LRight, LLeft, LParent: PNode;
|
|
begin
|
|
LRight := ANode.Right;
|
|
LLeft := LRight.Left;
|
|
LParent := ANode.Parent;
|
|
|
|
LRight.Left := LLeft.Right;
|
|
if LRight.Left <> nil then
|
|
LRight.Left.Parent := LRight;
|
|
|
|
ANode.Right := LLeft.Left;
|
|
if ANode.Right <> nil then
|
|
ANode.Right.Parent := ANode;
|
|
|
|
LLeft.Left := ANode;
|
|
LLeft.Right := LRight;
|
|
ANode.Parent := LLeft;
|
|
LRight.Parent := LLeft;
|
|
|
|
LLeft.Parent := LParent;
|
|
|
|
if LParent <> nil then
|
|
begin
|
|
if LParent.Left = ANode then
|
|
LParent.Left := LLeft
|
|
else
|
|
LParent.Right := LLeft;
|
|
end
|
|
else
|
|
FRoot := LLeft;
|
|
|
|
if LLeft.Balance = -1 then
|
|
ANode.Balance := 1
|
|
else
|
|
ANode.Balance := 0;
|
|
|
|
if LLeft.Balance = 1 then
|
|
LRight.Balance := -1
|
|
else
|
|
LRight.Balance := 0;
|
|
|
|
LLeft.Balance := 0;
|
|
end;
|
|
|
|
procedure TCustomAVLTreeMap<TREE_CONSTRAINTS>.RotateLeftRight(ANode: PNode);
|
|
var
|
|
LLeft, LRight, LParent: PNode;
|
|
begin
|
|
LLeft := ANode.Left;
|
|
LRight := LLeft.Right;
|
|
LParent := ANode.Parent;
|
|
|
|
LLeft.Right := LRight.Left;
|
|
if LLeft.Right <> nil then
|
|
LLeft.Right.Parent := LLeft;
|
|
|
|
ANode.Left := LRight.Right;
|
|
if ANode.Left <> nil then
|
|
ANode.Left.Parent := ANode;
|
|
|
|
LRight.Right := ANode;
|
|
LRight.Left := LLeft;
|
|
ANode.Parent := LRight;
|
|
LLeft.Parent := LRight;
|
|
|
|
LRight.Parent := LParent;
|
|
|
|
if LParent <> nil then
|
|
begin
|
|
if LParent.Left = ANode then
|
|
LParent.Left := LRight
|
|
else
|
|
LParent.Right := LRight;
|
|
end
|
|
else
|
|
FRoot := LRight;
|
|
|
|
if LRight.Balance = 1 then
|
|
ANode.Balance := -1
|
|
else
|
|
ANode.Balance := 0;
|
|
if LRight.Balance = -1 then
|
|
LLeft.Balance := 1
|
|
else
|
|
LLeft.Balance := 0;
|
|
|
|
LRight.Balance := 0;
|
|
end;
|
|
|
|
procedure TCustomAVLTreeMap<TREE_CONSTRAINTS>.KeyNotify(constref AKey: TKey; ACollectionNotification: TCollectionNotification);
|
|
begin
|
|
if Assigned(FOnKeyNotify) then
|
|
FOnKeyNotify(Self, AKey, ACollectionNotification);
|
|
end;
|
|
|
|
procedure TCustomAVLTreeMap<TREE_CONSTRAINTS>.ValueNotify(constref AValue: TValue; ACollectionNotification: TCollectionNotification);
|
|
begin
|
|
if Assigned(FOnValueNotify) then
|
|
FOnValueNotify(Self, AValue, ACollectionNotification);
|
|
end;
|
|
|
|
procedure TCustomAVLTreeMap<TREE_CONSTRAINTS>.NodeNotify(ANode: PNode; ACollectionNotification: TCollectionNotification; ADispose: boolean);
|
|
begin
|
|
if Assigned(FOnValueNotify) then
|
|
FOnNodeNotify(Self, ANode, ACollectionNotification, ADispose);
|
|
KeyNotify(ANode.Key, ACollectionNotification);
|
|
ValueNotify(ANode.Value, ACollectionNotification);
|
|
end;
|
|
|
|
procedure TCustomAVLTreeMap<TREE_CONSTRAINTS>.SetValue(var AValue: TValue; constref ANewValue: TValue);
|
|
var
|
|
LOldValue: TValue;
|
|
begin
|
|
LOldValue := AValue;
|
|
AValue := ANewValue;
|
|
|
|
ValueNotify(LOldValue, cnRemoved);
|
|
ValueNotify(ANewValue, cnAdded);
|
|
end;
|
|
|
|
procedure TCustomAVLTreeMap<TREE_CONSTRAINTS>.WriteStr(AStream: TStream; const AText: string);
|
|
begin
|
|
if AText='' then exit;
|
|
AStream.Write(AText[1],Length(AText));
|
|
end;
|
|
|
|
function TCustomAVLTreeMap<TREE_CONSTRAINTS>.GetNodeCollection: TNodeCollection;
|
|
begin
|
|
if not Assigned(FNodes) then
|
|
FNodes := TNodeCollection.Create(TTree(Self));
|
|
Result := FNodes;
|
|
end;
|
|
|
|
procedure TCustomAVLTreeMap<TREE_CONSTRAINTS>.InternalAdd(ANode, AParent: PNode);
|
|
begin
|
|
Inc(FCount);
|
|
|
|
ANode.Parent := AParent;
|
|
NodeAdded(ANode);
|
|
|
|
if AParent=nil then
|
|
begin
|
|
FRoot := ANode;
|
|
Exit;
|
|
end;
|
|
|
|
// balance after insert
|
|
|
|
if AParent.Balance<>0 then
|
|
AParent.Balance := 0
|
|
else
|
|
begin
|
|
if AParent.Left = ANode then
|
|
AParent.Balance := 1
|
|
else
|
|
AParent.Balance := -1;
|
|
|
|
ANode := AParent.Parent;
|
|
|
|
while ANode <> nil do
|
|
begin
|
|
if ANode.Balance<>0 then
|
|
begin
|
|
if ANode.Balance = 1 then
|
|
begin
|
|
if ANode.Right = AParent then
|
|
ANode.Balance := 0
|
|
else if AParent.Balance = -1 then
|
|
RotateLeftRight(ANode)
|
|
else
|
|
RotateLeftLeft(ANode);
|
|
end
|
|
else
|
|
begin
|
|
if ANode.Left = AParent then
|
|
ANode.Balance := 0
|
|
else if AParent^.Balance = 1 then
|
|
RotateRightLeft(ANode)
|
|
else
|
|
RotateRightRight(ANode);
|
|
end;
|
|
Break;
|
|
end;
|
|
|
|
if ANode.Left = AParent then
|
|
ANode.Balance := 1
|
|
else
|
|
ANode.Balance := -1;
|
|
|
|
AParent := ANode;
|
|
ANode := ANode.Parent;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
function TCustomAVLTreeMap<TREE_CONSTRAINTS>.InternalAdd(ANode: PNode; ADispisable: boolean): PNode;
|
|
var
|
|
LParent: PNode;
|
|
begin
|
|
Result := ANode;
|
|
case FindInsertNode(ANode, LParent) of
|
|
-1: LParent.Left := ANode;
|
|
0:
|
|
if Assigned(LParent) then
|
|
case FDuplicates of
|
|
dupAccept: LParent.Right := ANode;
|
|
dupIgnore:
|
|
begin
|
|
LParent.Right := nil;
|
|
if ADispisable then
|
|
Dispose(ANode);
|
|
Exit(LParent);
|
|
end;
|
|
dupError:
|
|
begin
|
|
LParent.Right := nil;
|
|
if ADispisable then
|
|
Dispose(ANode);
|
|
Result := nil;
|
|
raise EListError.Create(SCollectionDuplicate);
|
|
end;
|
|
end;
|
|
1: LParent.Right := ANode;
|
|
end;
|
|
|
|
InternalAdd(ANode, LParent);
|
|
NodeNotify(ANode, cnAdded, false);
|
|
end;
|
|
|
|
procedure TCustomAVLTreeMap<TREE_CONSTRAINTS>.InternalDelete(ANode: PNode);
|
|
var
|
|
t, y, z: PNode;
|
|
LNest: boolean;
|
|
begin
|
|
if (ANode.Left <> nil) and (ANode.Right <> nil) then
|
|
begin
|
|
y := FindPredecessor(ANode);
|
|
y.Info := ANode.Info;
|
|
DeletingNode(y, false);
|
|
InternalDelete(y);
|
|
LNest := false;
|
|
end
|
|
else
|
|
begin
|
|
if ANode.Left <> nil then
|
|
begin
|
|
y := ANode.Left;
|
|
ANode.Left := nil;
|
|
end
|
|
else
|
|
begin
|
|
y := ANode.Right;
|
|
ANode.Right := nil;
|
|
end;
|
|
ANode.Balance := 0;
|
|
LNest := true;
|
|
end;
|
|
|
|
if y <> nil then
|
|
begin
|
|
y.Parent := ANode.Parent;
|
|
y.Left := ANode.Left;
|
|
if y.Left <> nil then
|
|
y.Left.Parent := y;
|
|
y.Right := ANode.Right;
|
|
if y.Right <> nil then
|
|
y.Right.Parent := y;
|
|
y.Balance := ANode.Balance;
|
|
end;
|
|
|
|
if ANode.Parent <> nil then
|
|
begin
|
|
if ANode.Parent.Left = ANode then
|
|
ANode.Parent.Left := y
|
|
else
|
|
ANode.Parent.Right := y;
|
|
end
|
|
else
|
|
FRoot := y;
|
|
|
|
if LNest then
|
|
begin
|
|
z := y;
|
|
y := ANode.Parent;
|
|
while y <> nil do
|
|
begin
|
|
if y.Balance = 0 then
|
|
begin
|
|
if y.Left = z then
|
|
y.Balance := -1
|
|
else
|
|
y.Balance := 1;
|
|
break;
|
|
end
|
|
else
|
|
begin
|
|
if ((y.Balance = 1) and (y.Left = z)) or ((y.Balance = -1) and (y.Right = z)) then
|
|
begin
|
|
y.Balance := 0;
|
|
z := y;
|
|
y := y.Parent;
|
|
end
|
|
else
|
|
begin
|
|
if y.Left = z then
|
|
t := y.Right
|
|
else
|
|
t := y.Left;
|
|
if t.Balance = 0 then
|
|
begin
|
|
if y.Balance = 1 then
|
|
RotateLeftLeft(y)
|
|
else
|
|
RotateRightRight(y);
|
|
break;
|
|
end
|
|
else if y.Balance = t.Balance then
|
|
begin
|
|
if y.Balance = 1 then
|
|
RotateLeftLeft(y)
|
|
else
|
|
RotateRightRight(y);
|
|
z := t;
|
|
y := t.Parent;
|
|
end
|
|
else
|
|
begin
|
|
if y.Balance = 1 then
|
|
RotateLeftRight(y)
|
|
else
|
|
RotateRightLeft(y);
|
|
z := y.Parent;
|
|
y := z.Parent;
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end;
|
|
end;
|
|
|
|
function TCustomAVLTreeMap<TREE_CONSTRAINTS>.GetKeys: TKeyCollection;
|
|
begin
|
|
if not Assigned(FKeys) then
|
|
FKeys := TKeyCollection.Create(TTree(Self));
|
|
Result := TKeyCollection(FKeys);
|
|
end;
|
|
|
|
function TCustomAVLTreeMap<TREE_CONSTRAINTS>.GetValues: TValueCollection;
|
|
begin
|
|
if not Assigned(FValues) then
|
|
FValues := TValueCollection.Create(TTree(Self));
|
|
Result := TValueCollection(FValues);
|
|
end;
|
|
|
|
constructor TCustomAVLTreeMap<TREE_CONSTRAINTS>.Create;
|
|
begin
|
|
FComparer := TComparer<TKey>.Default;
|
|
end;
|
|
|
|
constructor TCustomAVLTreeMap<TREE_CONSTRAINTS>.Create(const AComparer: IComparer<TKey>);
|
|
begin
|
|
FComparer := AComparer;
|
|
end;
|
|
|
|
function TCustomAVLTreeMap<TREE_CONSTRAINTS>.NewNode: PNode;
|
|
begin
|
|
Result := AllocMem(SizeOf(TNode));
|
|
Initialize(Result^);
|
|
end;
|
|
|
|
function TCustomAVLTreeMap<TREE_CONSTRAINTS>.NewNodeArray(ACount: SizeInt): PNode;
|
|
begin
|
|
Result := AllocMem(ACount * SizeOf(TNode));
|
|
Initialize(Result^, ACount);
|
|
end;
|
|
|
|
procedure TCustomAVLTreeMap<TREE_CONSTRAINTS>.NewNodeArray(out AArray: TArray<PNode>; ACount: SizeInt);
|
|
var
|
|
i: Integer;
|
|
begin
|
|
SetLength(AArray, ACount);
|
|
for i := 0 to ACount-1 do
|
|
AArray[i] := NewNode;
|
|
end;
|
|
|
|
procedure TCustomAVLTreeMap<TREE_CONSTRAINTS>.DisposeNode(ANode: PNode);
|
|
begin
|
|
Dispose(ANode);
|
|
end;
|
|
|
|
procedure TCustomAVLTreeMap<TREE_CONSTRAINTS>.DisposeNodeArray(ANode: PNode; ACount: SizeInt);
|
|
begin
|
|
Finalize(ANode^, ACount);
|
|
FreeMem(ANode);
|
|
end;
|
|
|
|
procedure TCustomAVLTreeMap<TREE_CONSTRAINTS>.DisposeNodeArray(var AArray: TArray<PNode>);
|
|
var
|
|
i: Integer;
|
|
begin
|
|
for i := 0 to High(AArray) do
|
|
Dispose(AArray[i]);
|
|
AArray := nil;
|
|
end;
|
|
|
|
destructor TCustomAVLTreeMap<TREE_CONSTRAINTS>.Destroy;
|
|
begin
|
|
FKeys.Free;
|
|
FValues.Free;
|
|
FNodes.Free;
|
|
Clear;
|
|
end;
|
|
|
|
function TCustomAVLTreeMap<TREE_CONSTRAINTS>.AddNode(ANode: PNode): boolean;
|
|
begin
|
|
Result := ANode=InternalAdd(ANode, false);
|
|
end;
|
|
|
|
function TCustomAVLTreeMap<TREE_CONSTRAINTS>.Add(constref APair: TTreePair): PNode;
|
|
begin
|
|
Result := NewNode;
|
|
Result.Data.Key := APair.Key;
|
|
Result.Data.Value := APair.Value;
|
|
Result := InternalAdd(Result, true);
|
|
end;
|
|
|
|
function TCustomAVLTreeMap<TREE_CONSTRAINTS>.Add(constref AKey: TKey; constref AValue: TValue): PNode;
|
|
begin
|
|
Result := NewNode;
|
|
Result.Data.Key := AKey;
|
|
Result.Data.Value := AValue;
|
|
Result := InternalAdd(Result, true);
|
|
end;
|
|
|
|
function TCustomAVLTreeMap<TREE_CONSTRAINTS>.Remove(constref AKey: TKey; ADisposeNode: boolean): boolean;
|
|
var
|
|
LNode: PNode;
|
|
begin
|
|
LNode:=Find(AKey);
|
|
if LNode<>nil then begin
|
|
Delete(LNode, ADisposeNode);
|
|
Result:=true;
|
|
end else
|
|
Result:=false;
|
|
end;
|
|
|
|
function TCustomAVLTreeMap<TREE_CONSTRAINTS>.ExtractPair(constref AKey: TKey; ADisposeNode: boolean): TTreePair;
|
|
var
|
|
LNode: PNode;
|
|
begin
|
|
LNode:=Find(AKey);
|
|
if LNode<>nil then
|
|
begin
|
|
Result.Key := AKey;
|
|
Result.Value := DoRemove(LNode, cnExtracted, ADisposeNode);
|
|
end else
|
|
Result := Default(TTreePair);
|
|
end;
|
|
|
|
function TCustomAVLTreeMap<TREE_CONSTRAINTS>.ExtractPair(constref ANode: PNode; ADispose: boolean = true): TTreePair;
|
|
begin
|
|
Result.Key := ANode.Key;
|
|
Result.Value := DoRemove(ANode, cnExtracted, ADispose);
|
|
end;
|
|
|
|
function TCustomAVLTreeMap<TREE_CONSTRAINTS>.ExtractNode(constref AKey: TKey; ADisposeNode: boolean): PNode;
|
|
begin
|
|
Result:=Find(AKey);
|
|
if Result<>nil then
|
|
begin
|
|
DoRemove(Result, cnExtracted, false);
|
|
if ADisposeNode then
|
|
Result := nil;
|
|
end;
|
|
end;
|
|
|
|
function TCustomAVLTreeMap<TREE_CONSTRAINTS>.ExtractNode(ANode: PNode; ADispose: boolean): PNode;
|
|
begin
|
|
DoRemove(ANode, cnExtracted, ADispose);
|
|
if ADispose then
|
|
Result := nil
|
|
else
|
|
Result := ANode;
|
|
end;
|
|
|
|
procedure TCustomAVLTreeMap<TREE_CONSTRAINTS>.Delete(ANode: PNode; ADispose: boolean);
|
|
begin
|
|
DoRemove(ANode, cnRemoved, ADispose);
|
|
end;
|
|
|
|
procedure TCustomAVLTreeMap<TREE_CONSTRAINTS>.Clear(ADisposeNodes: Boolean);
|
|
begin
|
|
if (FRoot<>nil) and ADisposeNodes then
|
|
DisposeAllNodes(FRoot);
|
|
fRoot:=nil;
|
|
FCount:=0;
|
|
end;
|
|
|
|
function TCustomAVLTreeMap<TREE_CONSTRAINTS>.GetEnumerator: TPairEnumerator;
|
|
begin
|
|
Result := TPairEnumerator.Create(Self, true);
|
|
end;
|
|
|
|
function TCustomAVLTreeMap<TREE_CONSTRAINTS>.FindLowest: PNode;
|
|
begin
|
|
Result:=FRoot;
|
|
if Result<>nil then
|
|
while Result.Left<>nil do Result:=Result.Left;
|
|
end;
|
|
|
|
function TCustomAVLTreeMap<TREE_CONSTRAINTS>.FindHighest: PNode;
|
|
begin
|
|
Result:=FRoot;
|
|
if Result<>nil then
|
|
while Result.Right<>nil do Result:=Result.Right;
|
|
end;
|
|
|
|
function TCustomAVLTreeMap<TREE_CONSTRAINTS>.Find(constref AKey: TKey): PNode;
|
|
var
|
|
LComp: SizeInt;
|
|
begin
|
|
Result:=FRoot;
|
|
while (Result<>nil) do
|
|
begin
|
|
LComp:=Compare(AKey,Result.Key);
|
|
if LComp=0 then
|
|
Exit;
|
|
if LComp<0 then
|
|
Result:=Result.Left
|
|
else
|
|
Result:=Result.Right
|
|
end;
|
|
end;
|
|
|
|
function TCustomAVLTreeMap<TREE_CONSTRAINTS>.ContainsKey(constref AKey: TKey; out ANode: PNode): boolean;
|
|
begin
|
|
ANode := Find(AKey);
|
|
Result := Assigned(ANode);
|
|
end;
|
|
|
|
function TCustomAVLTreeMap<TREE_CONSTRAINTS>.ContainsKey(constref AKey: TKey): boolean; overload; inline;
|
|
begin
|
|
Result := Assigned(Find(AKey));
|
|
end;
|
|
|
|
procedure TCustomAVLTreeMap<TREE_CONSTRAINTS>.ConsistencyCheck;
|
|
var
|
|
RealCount: SizeInt;
|
|
begin
|
|
RealCount:=0;
|
|
if FRoot<>nil then begin
|
|
FRoot.ConsistencyCheck(Self);
|
|
RealCount:=FRoot.GetCount;
|
|
end;
|
|
if Count<>RealCount then
|
|
raise EAVLTree.Create('Count<>RealCount');
|
|
end;
|
|
|
|
procedure TCustomAVLTreeMap<TREE_CONSTRAINTS>.WriteTreeNode(AStream: TStream; ANode: PNode);
|
|
var
|
|
b: String;
|
|
IsLeft: boolean;
|
|
LParent: PNode;
|
|
WasLeft: Boolean;
|
|
begin
|
|
if ANode=nil then exit;
|
|
WriteTreeNode(AStream, ANode.Right);
|
|
LParent:=ANode;
|
|
WasLeft:=false;
|
|
b:='';
|
|
while LParent<>nil do begin
|
|
if LParent.Parent=nil then begin
|
|
if LParent=ANode then
|
|
b:='--'+b
|
|
else
|
|
b:=' '+b;
|
|
break;
|
|
end;
|
|
IsLeft:=LParent.Parent.Left=LParent;
|
|
if LParent=ANode then begin
|
|
if IsLeft then
|
|
b:='\-'
|
|
else
|
|
b:='/-';
|
|
end else begin
|
|
if WasLeft=IsLeft then
|
|
b:=' '+b
|
|
else
|
|
b:='| '+b;
|
|
end;
|
|
WasLeft:=IsLeft;
|
|
LParent:=LParent.Parent;
|
|
end;
|
|
b:=b+NodeToReportStr(ANode)+LineEnding;
|
|
WriteStr(AStream, b);
|
|
WriteTreeNode(AStream, ANode.Left);
|
|
end;
|
|
|
|
procedure TCustomAVLTreeMap<TREE_CONSTRAINTS>.WriteReportToStream(AStream: TStream);
|
|
begin
|
|
WriteStr(AStream, '-Start-of-AVL-Tree-------------------'+LineEnding);
|
|
WriteTreeNode(AStream, fRoot);
|
|
WriteStr(AStream, '-End-Of-AVL-Tree---------------------'+LineEnding);
|
|
end;
|
|
|
|
function TCustomAVLTreeMap<TREE_CONSTRAINTS>.NodeToReportStr(ANode: PNode): string;
|
|
begin
|
|
Result:=Format(' Self=%p Parent=%p Balance=%d', [ANode, ANode.Parent, ANode.Balance]);
|
|
end;
|
|
|
|
function TCustomAVLTreeMap<TREE_CONSTRAINTS>.ReportAsString: string;
|
|
var ms: TMemoryStream;
|
|
begin
|
|
Result:='';
|
|
ms:=TMemoryStream.Create;
|
|
try
|
|
WriteReportToStream(ms);
|
|
ms.Position:=0;
|
|
SetLength(Result,ms.Size);
|
|
if Result<>'' then
|
|
ms.Read(Result[1],length(Result));
|
|
finally
|
|
ms.Free;
|
|
end;
|
|
end;
|
|
|
|
{ TIndexedAVLTreeMap<TKey, TValue> }
|
|
|
|
procedure TIndexedAVLTreeMap<TKey, TValue>.RotateRightRight(ANode: PNode);
|
|
var
|
|
LOldRight: PNode;
|
|
begin
|
|
LOldRight:=ANode.Right;
|
|
inherited;
|
|
Inc(LOldRight.Data.Info, (1 + ANode.Data.Info));
|
|
end;
|
|
|
|
procedure TIndexedAVLTreeMap<TKey, TValue>.RotateLeftLeft(ANode: PNode);
|
|
var
|
|
LOldLeft: PNode;
|
|
begin
|
|
LOldLeft:=ANode.Left;
|
|
inherited;
|
|
Dec(ANode.Data.Info, (1 + LOldLeft.Data.Info));
|
|
end;
|
|
|
|
procedure TIndexedAVLTreeMap<TKey, TValue>.RotateRightLeft(ANode: PNode);
|
|
var
|
|
LB, LC: PNode;
|
|
begin
|
|
LB := ANode.Right;
|
|
LC := LB.Left;
|
|
inherited;
|
|
Dec(LB.Data.Info, 1+LC.Info);
|
|
Inc(LC.Data.Info, 1+ANode.Info);
|
|
end;
|
|
|
|
procedure TIndexedAVLTreeMap<TKey, TValue>.RotateLeftRight(ANode: PNode);
|
|
var
|
|
LB, LC: PNode;
|
|
begin
|
|
LB := ANode.Left;
|
|
LC := LB.Right;
|
|
inherited;
|
|
Inc(LC.Data.Info, 1+LB.Info);
|
|
Dec(ANode.Data.Info, 1+LC.Info);
|
|
end;
|
|
|
|
|
|
procedure TIndexedAVLTreeMap<TKey, TValue>.NodeAdded(ANode: PNode);
|
|
var
|
|
LParent, LNode: PNode;
|
|
begin
|
|
FLastNode := nil;
|
|
LNode := ANode;
|
|
repeat
|
|
LParent:=LNode.Parent;
|
|
if (LParent=nil) then break;
|
|
if LParent.Left=LNode then
|
|
Inc(LParent.Data.Info);
|
|
LNode:=LParent;
|
|
until false;
|
|
end;
|
|
|
|
procedure TIndexedAVLTreeMap<TKey, TValue>.DeletingNode(ANode: PNode; AOrigin: boolean);
|
|
var
|
|
LParent: PNode;
|
|
begin
|
|
if not AOrigin then
|
|
Dec(ANode.Data.Info);
|
|
FLastNode := nil;
|
|
repeat
|
|
LParent:=ANode.Parent;
|
|
if (LParent=nil) then exit;
|
|
if LParent.Left=ANode then
|
|
Dec(LParent.Data.Info);
|
|
ANode:=LParent;
|
|
until false;
|
|
end;
|
|
|
|
function TIndexedAVLTreeMap<TKey, TValue>.GetNodeAtIndex(AIndex: SizeInt): PNode;
|
|
begin
|
|
if (AIndex<0) or (AIndex>=Count) then
|
|
raise EIndexedAVLTree.CreateFmt('TIndexedAVLTree: AIndex %d out of bounds 0..%d', [AIndex, Count]);
|
|
|
|
if FLastNode<>nil then begin
|
|
if AIndex=FLastIndex then
|
|
Exit(FLastNode)
|
|
else if AIndex=FLastIndex+1 then begin
|
|
FLastIndex:=AIndex;
|
|
FLastNode:=FLastNode.Successor;
|
|
Exit(FLastNode);
|
|
end else if AIndex=FLastIndex-1 then begin
|
|
FLastIndex:=AIndex;
|
|
FLastNode:=FLastNode.Precessor;
|
|
Exit(FLastNode);
|
|
end;
|
|
end;
|
|
|
|
FLastIndex:=AIndex;
|
|
Result:=FRoot;
|
|
repeat
|
|
if Result.Info>AIndex then
|
|
Result:=Result.Left
|
|
else if Result.Info=AIndex then begin
|
|
FLastNode:=Result;
|
|
Exit;
|
|
end
|
|
else begin
|
|
Dec(AIndex, Result.Info+1);
|
|
Result:=Result.Right;
|
|
end;
|
|
until false;
|
|
end;
|
|
|
|
function TIndexedAVLTreeMap<TKey, TValue>.NodeToIndex(ANode: PNode): SizeInt;
|
|
var
|
|
LNode: PNode;
|
|
LParent: PNode;
|
|
begin
|
|
if ANode=nil then
|
|
Exit(-1);
|
|
|
|
if FLastNode=ANode then
|
|
Exit(FLastIndex);
|
|
|
|
LNode:=ANode;
|
|
Result:=LNode.Info;
|
|
repeat
|
|
LParent:=LNode.Parent;
|
|
if LParent=nil then break;
|
|
if LParent.Right=LNode then
|
|
inc(Result,LParent.Info+1);
|
|
LNode:=LParent;
|
|
until false;
|
|
|
|
FLastNode:=ANode;
|
|
FLastIndex:=Result;
|
|
end;
|
|
|
|
procedure TIndexedAVLTreeMap<TKey, TValue>.ConsistencyCheck;
|
|
var
|
|
LNode: PNode;
|
|
i: SizeInt;
|
|
LeftCount: SizeInt = 0;
|
|
begin
|
|
inherited ConsistencyCheck;
|
|
i:=0;
|
|
for LNode in Self.Nodes do
|
|
begin
|
|
if LNode.Left<>nil then
|
|
LeftCount:=LNode.Left.GetCount
|
|
else
|
|
LeftCount:=0;
|
|
|
|
if LNode.Info<>LeftCount then
|
|
raise EIndexedAVLTree.CreateFmt('LNode.LeftCount=%d<>%d',[LNode.Info,LeftCount]);
|
|
|
|
if GetNodeAtIndex(i)<>LNode then
|
|
raise EIndexedAVLTree.CreateFmt('GetNodeAtIndex(%d)<>%P',[i,LNode]);
|
|
FLastNode:=nil;
|
|
if GetNodeAtIndex(i)<>LNode then
|
|
raise EIndexedAVLTree.CreateFmt('GetNodeAtIndex(%d)<>%P',[i,LNode]);
|
|
|
|
if NodeToIndex(LNode)<>i then
|
|
raise EIndexedAVLTree.CreateFmt('NodeToIndex(%P)<>%d',[LNode,i]);
|
|
FLastNode:=nil;
|
|
if NodeToIndex(LNode)<>i then
|
|
raise EIndexedAVLTree.CreateFmt('NodeToIndex(%P)<>%d',[LNode,i]);
|
|
|
|
inc(i);
|
|
end;
|
|
end;
|
|
|
|
function TIndexedAVLTreeMap<TKey, TValue>.NodeToReportStr(ANode: PNode): string;
|
|
begin
|
|
Result:=Format(' Self=%p Parent=%p Balance=%d Idx=%d Info=%d',
|
|
[ANode,ANode.Parent, ANode.Balance, NodeToIndex(ANode), ANode.Info]);
|
|
end;
|
|
|
|
{ TAVLTree<T> }
|
|
|
|
function TAVLTree<T>.Add(constref AValue: T): PNode;
|
|
begin
|
|
Result := inherited Add(AValue, EmptyRecord);
|
|
end;
|
|
|
|
function TAVLTree<T>.AddNode(ANode: PNode): boolean;
|
|
begin
|
|
Result := inherited AddNode(ANode);
|
|
end;
|
|
|
|
{ TIndexedAVLTree<T> }
|
|
|
|
function TIndexedAVLTree<T>.Add(constref AValue: T): PNode;
|
|
begin
|
|
Result := inherited Add(AValue, EmptyRecord);
|
|
end;
|
|
|
|
function TIndexedAVLTree<T>.AddNode(ANode: PNode): boolean;
|
|
begin
|
|
Result := inherited AddNode(ANode);
|
|
end;
|
|
|
|
{ TSortedSet<T>.TSortedSetEnumerator }
|
|
|
|
function TSortedSet<T>.TSortedSetEnumerator.GetCurrent: T;
|
|
begin
|
|
Result := TTreeEnumerator(FEnumerator).GetCurrent;
|
|
end;
|
|
|
|
constructor TSortedSet<T>.TSortedSetEnumerator.Create(ASet: TCustomSet<T>);
|
|
begin
|
|
TTreeEnumerator(FEnumerator) := TSortedSet<T>(ASet).FInternalTree.Keys.DoGetEnumerator;
|
|
end;
|
|
|
|
{ TSortedSet<T>.TPointersEnumerator }
|
|
|
|
function TSortedSet<T>.TPointersEnumerator.DoMoveNext: boolean;
|
|
begin
|
|
Result := FEnumerator.MoveNext;
|
|
end;
|
|
|
|
function TSortedSet<T>.TPointersEnumerator.DoGetCurrent: PT;
|
|
begin
|
|
Result := FEnumerator.Current;
|
|
end;
|
|
|
|
constructor TSortedSet<T>.TPointersEnumerator.Create(ASortedSet: TSortedSet<T>);
|
|
begin
|
|
FEnumerator := ASortedSet.FInternalTree.Keys.Ptr^.GetEnumerator;
|
|
end;
|
|
|
|
{ TSortedSet<T> }
|
|
|
|
procedure TSortedSet<T>.InternalAVLTreeNotify(ASender: TObject; constref AItem: T; AAction: TCollectionNotification);
|
|
begin
|
|
FOnNotify(Self, AItem, AAction);
|
|
end;
|
|
|
|
function TSortedSet<T>.GetPtrEnumerator: TEnumerator<PT>;
|
|
begin
|
|
Result := TPointersEnumerator.Create(Self);
|
|
end;
|
|
|
|
function TSortedSet<T>.GetCount: SizeInt;
|
|
begin
|
|
Result := FInternalTree.Count;
|
|
end;
|
|
|
|
function TSortedSet<T>.GetCapacity: SizeInt;
|
|
begin
|
|
Result := FInternalTree.Count;
|
|
end;
|
|
|
|
procedure TSortedSet<T>.SetCapacity(AValue: SizeInt);
|
|
begin
|
|
end;
|
|
|
|
function TSortedSet<T>.GetOnNotify: TCollectionNotifyEvent<T>;
|
|
begin
|
|
Result := FInternalTree.OnKeyNotify;
|
|
end;
|
|
|
|
procedure TSortedSet<T>.SetOnNotify(AValue: TCollectionNotifyEvent<T>);
|
|
begin
|
|
FOnNotify := AValue;
|
|
if Assigned(AValue) then
|
|
FInternalTree.OnKeyNotify := InternalAVLTreeNotify
|
|
else
|
|
FInternalTree.OnKeyNotify := nil;
|
|
end;
|
|
|
|
function TSortedSet<T>.GetEnumerator: TCustomSetEnumerator;
|
|
begin
|
|
Result := TSortedSetEnumerator.Create(Self);
|
|
end;
|
|
|
|
constructor TSortedSet<T>.Create;
|
|
begin
|
|
FInternalTree := TAVLTree<T>.Create;
|
|
end;
|
|
|
|
constructor TSortedSet<T>.Create(const AComparer: IComparer<T>);
|
|
begin
|
|
FInternalTree := TAVLTree<T>.Create(AComparer);
|
|
end;
|
|
|
|
destructor TSortedSet<T>.Destroy;
|
|
begin
|
|
FInternalTree.Free;
|
|
end;
|
|
|
|
function TSortedSet<T>.Add(constref AValue: T): Boolean;
|
|
var
|
|
LNodePtr, LParent: TAVLTree<T>.PNode;
|
|
LNode: TAVLTree<T>.TNode;
|
|
LCompare: Integer;
|
|
begin
|
|
LNode.Data.Key := AValue;
|
|
|
|
LCompare := FInternalTree.FindInsertNode(@LNode, LParent);
|
|
|
|
Result := not((LCompare=0) and Assigned(LParent));
|
|
if not Result then
|
|
Exit;
|
|
|
|
LNodePtr := FInternalTree.NewNode;
|
|
LNodePtr^.Data.Key := AValue;
|
|
|
|
case LCompare of
|
|
-1: LParent.Left := LNodePtr;
|
|
1: LParent.Right := LNodePtr;
|
|
end;
|
|
|
|
FInternalTree.InternalAdd(LNodePtr, LParent);
|
|
FInternalTree.NodeNotify(LNodePtr, cnAdded, false);
|
|
end;
|
|
|
|
function TSortedSet<T>.Remove(constref AValue: T): Boolean;
|
|
var
|
|
LNode: TAVLTree<T>.PNode;
|
|
begin
|
|
LNode := FInternalTree.Find(AValue);
|
|
Result := Assigned(LNode);
|
|
if Result then
|
|
FInternalTree.Delete(LNode);
|
|
end;
|
|
|
|
function TSortedSet<T>.Extract(constref AValue: T): T;
|
|
var
|
|
LNode: TAVLTree<T>.PNode;
|
|
begin
|
|
LNode := FInternalTree.Find(AValue);
|
|
if not Assigned(LNode) then
|
|
Exit(Default(T));
|
|
|
|
Result := FInternalTree.ExtractPair(LNode).Key;
|
|
end;
|
|
|
|
procedure TSortedSet<T>.Clear;
|
|
begin
|
|
FInternalTree.Clear;
|
|
end;
|
|
|
|
function TSortedSet<T>.Contains(constref AValue: T): Boolean;
|
|
begin
|
|
Result := FInternalTree.ContainsKey(AValue);
|
|
end;
|
|
|
|
procedure TSortedSet<T>.TrimExcess;
|
|
begin
|
|
end;
|
|
|
|
{ TSortedHashSet<T>.TSortedHashSetEqualityComparer }
|
|
|
|
function TSortedHashSet<T>.TSortedHashSetEqualityComparer.Equals(constref ALeft, ARight: PT): Boolean;
|
|
begin
|
|
if Assigned(FComparer) then
|
|
Result := FComparer.Compare(ALeft^, ARight^) = 0
|
|
else
|
|
Result := FEqualityComparer.Equals(ALeft^, ARight^);
|
|
end;
|
|
|
|
function TSortedHashSet<T>.TSortedHashSetEqualityComparer.GetHashCode(constref AValue: PT): UInt32;
|
|
begin
|
|
Result := FEqualityComparer.GetHashCode(AValue^);
|
|
end;
|
|
|
|
constructor TSortedHashSet<T>.TSortedHashSetEqualityComparer.Create(const AComparer: IComparer<T>);
|
|
begin
|
|
FComparer := AComparer;
|
|
FEqualityComparer := TEqualityComparer<T>.Default;
|
|
end;
|
|
|
|
constructor TSortedHashSet<T>.TSortedHashSetEqualityComparer.Create(const AEqualityComparer: IEqualityComparer<T>);
|
|
begin
|
|
FEqualityComparer := AEqualityComparer;
|
|
end;
|
|
|
|
constructor TSortedHashSet<T>.TSortedHashSetEqualityComparer.Create(const AComparer: IComparer<T>; const AEqualityComparer: IEqualityComparer<T>);
|
|
begin
|
|
FComparer := AComparer;
|
|
FEqualityComparer := AEqualityComparer;
|
|
end;
|
|
|
|
{ TSortedHashSet<T>.TSortedHashSetEnumerator }
|
|
|
|
function TSortedHashSet<T>.TSortedHashSetEnumerator.GetCurrent: T;
|
|
begin
|
|
Result := TTreeEnumerator(FEnumerator).Current;
|
|
end;
|
|
|
|
constructor TSortedHashSet<T>.TSortedHashSetEnumerator.Create(ASet: TCustomSet<T>);
|
|
begin
|
|
FEnumerator := TSortedHashSet<T>(ASet).FInternalTree.Keys.GetEnumerator;
|
|
end;
|
|
|
|
{ TSortedHashSet<T>.TPointersEnumerator }
|
|
|
|
function TSortedHashSet<T>.TPointersEnumerator.DoMoveNext: boolean;
|
|
begin
|
|
Result := FEnumerator.MoveNext;
|
|
end;
|
|
|
|
function TSortedHashSet<T>.TPointersEnumerator.DoGetCurrent: PT;
|
|
begin
|
|
Result := FEnumerator.Current;
|
|
end;
|
|
|
|
constructor TSortedHashSet<T>.TPointersEnumerator.Create(ASortedHashSet: TSortedHashSet<T>);
|
|
begin
|
|
FEnumerator := ASortedHashSet.FInternalTree.Keys.Ptr^.GetEnumerator;
|
|
end;
|
|
|
|
{ TSortedHashSet<T> }
|
|
|
|
procedure TSortedHashSet<T>.InternalDictionaryNotify(ASender: TObject; constref AItem: PT; AAction: TCollectionNotification);
|
|
begin
|
|
FOnNotify(Self, AItem^, AAction);
|
|
end;
|
|
|
|
function TSortedHashSet<T>.GetPtrEnumerator: TEnumerator<PT>;
|
|
begin
|
|
Result := TPointersEnumerator.Create(Self);
|
|
end;
|
|
|
|
function TSortedHashSet<T>.DoGetEnumerator: TEnumerator<T>;
|
|
begin
|
|
Result := GetEnumerator;
|
|
end;
|
|
|
|
function TSortedHashSet<T>.GetCount: SizeInt;
|
|
begin
|
|
Result := FInternalDictionary.Count;
|
|
end;
|
|
|
|
function TSortedHashSet<T>.GetCapacity: SizeInt;
|
|
begin
|
|
Result := FInternalDictionary.Capacity;
|
|
end;
|
|
|
|
procedure TSortedHashSet<T>.SetCapacity(AValue: SizeInt);
|
|
begin
|
|
FInternalDictionary.Capacity := AValue;
|
|
end;
|
|
|
|
function TSortedHashSet<T>.GetOnNotify: TCollectionNotifyEvent<T>;
|
|
begin
|
|
Result := FInternalTree.OnKeyNotify;
|
|
end;
|
|
|
|
procedure TSortedHashSet<T>.SetOnNotify(AValue: TCollectionNotifyEvent<T>);
|
|
begin
|
|
FOnNotify := AValue;
|
|
if Assigned(AValue) then
|
|
FInternalDictionary.OnKeyNotify := InternalDictionaryNotify
|
|
else
|
|
FInternalDictionary.OnKeyNotify := nil;
|
|
end;
|
|
|
|
function TSortedHashSet<T>.GetEnumerator: TCustomSetEnumerator;
|
|
begin
|
|
Result := TSortedHashSetEnumerator.Create(Self);
|
|
end;
|
|
|
|
function TSortedHashSet<T>.Add(constref AValue: T): Boolean;
|
|
var
|
|
LNode: TAVLTree<T>.PNode;
|
|
begin
|
|
Result := not FInternalDictionary.ContainsKey(@AValue);
|
|
if Result then
|
|
begin
|
|
LNode := FInternalTree.Add(AValue);
|
|
FInternalDictionary.Add(@LNode.Data.Key, EmptyRecord);
|
|
end;
|
|
end;
|
|
|
|
function TSortedHashSet<T>.Remove(constref AValue: T): Boolean;
|
|
var
|
|
LIndex: SizeInt;
|
|
begin
|
|
LIndex := FInternalDictionary.FindBucketIndex(@AValue);
|
|
Result := LIndex >= 0;
|
|
if Result then
|
|
begin
|
|
FInternalDictionary.DoRemove(LIndex, cnRemoved);
|
|
FInternalTree.Remove(AValue);
|
|
end;
|
|
end;
|
|
|
|
function TSortedHashSet<T>.Extract(constref AValue: T): T;
|
|
var
|
|
LIndex: SizeInt;
|
|
begin
|
|
LIndex := FInternalDictionary.FindBucketIndex(@AValue);
|
|
if LIndex >= 0 then
|
|
begin
|
|
FInternalDictionary.DoRemove(LIndex, cnExtracted);
|
|
FInternalTree.Remove(AValue);
|
|
Result := AValue;
|
|
end else
|
|
Result := Default(T);
|
|
end;
|
|
|
|
procedure TSortedHashSet<T>.Clear;
|
|
begin
|
|
FInternalDictionary.Clear;
|
|
FInternalTree.Clear;
|
|
end;
|
|
|
|
function TSortedHashSet<T>.Contains(constref AValue: T): Boolean;
|
|
begin
|
|
Result := FInternalDictionary.ContainsKey(@AValue);
|
|
end;
|
|
|
|
constructor TSortedHashSet<T>.Create;
|
|
begin
|
|
FInternalTree := TAVLTree<T>.Create;
|
|
FInternalDictionary := TOpenAddressingLP<PT, TEmptyRecord>.Create(TSortedHashSetEqualityComparer.Create(TEqualityComparer<T>.Default));
|
|
end;
|
|
|
|
constructor TSortedHashSet<T>.Create(const AComparer: IEqualityComparer<T>);
|
|
begin
|
|
Create(TComparer<T>.Default, AComparer);
|
|
end;
|
|
|
|
constructor TSortedHashSet<T>.Create(const AComparer: IComparer<T>);
|
|
begin
|
|
FInternalTree := TAVLTree<T>.Create(AComparer);
|
|
FInternalDictionary := TOpenAddressingLP<PT, TEmptyRecord>.Create(TSortedHashSetEqualityComparer.Create(AComparer));
|
|
end;
|
|
|
|
constructor TSortedHashSet<T>.Create(const AComparer: IComparer<T>; const AEqualityComparer: IEqualityComparer<T>);
|
|
begin
|
|
FInternalTree := TAVLTree<T>.Create(AComparer);
|
|
FInternalDictionary := TOpenAddressingLP<PT, TEmptyRecord>.Create(TSortedHashSetEqualityComparer.Create(AComparer,AEqualityComparer));
|
|
end;
|
|
|
|
destructor TSortedHashSet<T>.Destroy;
|
|
begin
|
|
FInternalDictionary.Free;
|
|
FInternalTree.Free;
|
|
inherited;
|
|
end;
|
|
|
|
procedure TSortedHashSet<T>.TrimExcess;
|
|
begin
|
|
FInternalDictionary.TrimExcess;
|
|
end;
|
|
|
|
end.
|