{ This file is part of the Free Pascal/NewPascal run time library. Copyright (c) 2014 by Maciej Izak (hnb) member of the NewPascal development team (http://newpascal.org) Copyright(c) 2004-2018 DaThoX It contains the generics collections 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 :) **********************************************************************} {$IFNDEF FPC_DOTTEDUNITS} unit Generics.Collections; {$ENDIF} {$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} {$NOTES OFF} {$OVERFLOWCHECKS OFF} {$RANGECHECKS OFF} {$POINTERMATH ON} interface {$IFDEF FPC_DOTTEDUNITS} uses System.RtlConsts, System.Classes, System.SysUtils, System.Generics.MemoryExpanders, System.Generics.Defaults, System.Generics.Helpers, System.Generics.Strings, System.Types, System.Rtti; {$ELSE FPC_DOTTEDUNITS} uses RtlConsts, Classes, SysUtils, Generics.MemoryExpanders, Generics.Defaults, Generics.Helpers, Generics.Strings, Types, Rtti; {$ENDIF FPC_DOTTEDUNITS} {.$define EXTRA_WARNINGS} {.$define ENABLE_METHODS_WITH_TEnumerableWithPointers} type EAVLTree = class(Exception); EIndexedAVLTree = class(EAVLTree); TDuplicates = {$IFDEF FPC_DOTTEDUNITS}System.{$ENDIF}Classes.TDuplicates; // bug #24254 workaround // should be TArray = record class procedure Sort(...) etc. TBinarySearchResult = record FoundIndex, CandidateIndex: SizeInt; CompareResult: SizeInt; end; TCustomArrayHelper = class abstract private type // bug #24282 TComparerBugHack = TComparer; protected class procedure QuickSort(var AValues: array of T; ALeft, ARight: SizeInt; const AComparer: IComparer); virtual; abstract; public class procedure Sort(var AValues: array of T); overload; class procedure Sort(var AValues: array of T; const AComparer: IComparer); overload; class procedure Sort(var AValues: array of T; const AComparer: IComparer; AIndex, ACount: SizeInt); overload; class function BinarySearch(const AValues: array of T; const AItem: T; out ASearchResult: TBinarySearchResult; const AComparer: IComparer; AIndex, ACount: SizeInt): Boolean; virtual; abstract; overload; class function BinarySearch(const AValues: array of T; const AItem: T; out AFoundIndex: SizeInt; const AComparer: IComparer; AIndex, ACount: SizeInt): Boolean; virtual; abstract; overload; class function BinarySearch(const AValues: array of T; const AItem: T; out AFoundIndex: SizeInt; const AComparer: IComparer): Boolean; overload; class function BinarySearch(const AValues: array of T; const AItem: T; out AFoundIndex: SizeInt): Boolean; overload; class function BinarySearch(const AValues: array of T; const AItem: T; out ASearchResult: TBinarySearchResult; const AComparer: IComparer): Boolean; overload; class function BinarySearch(const AValues: array of T; const AItem: T; out ASearchResult: TBinarySearchResult): Boolean; overload; end {$ifdef EXTRA_WARNINGS}experimental{$endif}; // will be renamed to TCustomArray (bug #24254) TArrayHelper = class(TCustomArrayHelper) private type PT = ^T; class procedure QSort(p: PT; n, reasonable: SizeUint; const cmp: IComparer); static; class function Median(p: PT; n: SizeUint; const cmp: IComparer): PT; static; class procedure HeapSort(p: PT; n: SizeUint; const cmp: IComparer); static; class procedure HeapReplacePessimistic(q: PT; nq, id: SizeUint; const item: T; const cmp: IComparer); static; protected class procedure QuickSort(var AValues: array of T; ALeft, ARight: SizeInt; const AComparer: IComparer); override; public class function BinarySearch(const AValues: array of T; const AItem: T; out ASearchResult: TBinarySearchResult; const AComparer: IComparer; AIndex, ACount: SizeInt): Boolean; override; overload; class function BinarySearch(const AValues: array of T; const AItem: T; out AFoundIndex: SizeInt; const AComparer: IComparer; AIndex, ACount: SizeInt): Boolean; override; overload; class function Concat(const Args: array of TArray): TArray; static; class procedure Copy(const aSource: array of T; var aDestination: array of T; aCount: NativeInt); overload; class procedure Copy(const aSource: array of T; var aDestination: array of T; aSourceIndex, aDestIndex, aCount: SizeInt); overload; end {$ifdef EXTRA_WARNINGS}experimental{$endif}; // will be renamed to TArray (bug #24254) TCollectionNotification = (cnAdding, cnAdded, cnDeleting, cnRemoved, cnExtracting, cnExtracted); TCollectionNotifyEvent = procedure(ASender: TObject; const AItem: T; AAction: TCollectionNotification) of object; { TEnumerator } TEnumerator = class abstract protected function DoGetCurrent: T; virtual; abstract; function DoMoveNext: boolean; virtual; public property Current: T read DoGetCurrent; function MoveNext: boolean; end; { TEnumerable } TEnumerable = class abstract public type PT = ^T; protected // no forward generics declarations (needed by TPointersCollection), this should be moved into TEnumerableWithPointers function GetPtrEnumerator: TEnumerator; virtual; abstract; protected function ToArrayImpl(ACount: SizeInt): TArray; overload; // used by descendants protected function DoGetEnumerator: TEnumerator; virtual; abstract; public function GetEnumerator: TEnumerator; inline; function ToArray: TArray; virtual; overload; end; // error: no memory left for TCustomPointersEnumerator version TCustomPointersEnumerator = class abstract(TEnumerator); TCustomPointersCollection = object strict private type TLocalEnumerable = TEnumerable; // compiler has bug for directly usage of TEnumerable protected function Enumerable: TLocalEnumerable; inline; public function GetEnumerator: TEnumerator; end; TEnumerableWithPointers = class(TEnumerable) strict private type TPointersCollection = TCustomPointersCollection; 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 = class abstract(TEnumerableWithPointers) public type PT = ^T; protected type // bug #24282 TArrayHelperBugHack = TArrayHelper; TArrayOfT = array of T; private FOnNotify: TCollectionNotifyEvent; function GetCapacity: SizeInt; inline; protected FLength: SizeInt; FItems: TArrayOfT; function PrepareAddingItem: SizeInt; virtual; function PrepareAddingRange(ACount: SizeInt): SizeInt; virtual; procedure Notify(const 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; override; final; property Count: SizeInt read GetCount; property Capacity: SizeInt read GetCapacity write SetCapacity; property List: TArrayOfT read FItems; property OnNotify: TCollectionNotifyEvent read FOnNotify write FOnNotify; procedure TrimExcess; virtual; abstract; end; TCustomListEnumerator = class abstract(TEnumerator) private FList: TCustomList; FIndex: SizeInt; protected function DoMoveNext: boolean; override; function DoGetCurrent: T; override; function GetCurrent: T; virtual; public constructor Create(AList: TCustomList); end; TCustomListWithPointers = class(TCustomList) public type TPointersEnumerator = class(TCustomPointersEnumerator) protected FList: TCustomListWithPointers; FIndex: SizeInt; function DoMoveNext: boolean; override; function DoGetCurrent: PT; override; public constructor Create(AList: TCustomListWithPointers); end; protected function GetPtrEnumerator: TEnumerator; override; end; TList = class(TCustomListWithPointers) private var FComparer: IComparer; 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; override; public // with this type declaration i found #24285, #24285 type // bug workaround TEnumerator = class(TCustomListEnumerator); TEmptyFunc = reference to function (const L, R: T): Boolean; function GetEnumerator: TEnumerator; reintroduce; protected procedure SetCapacity(AValue: SizeInt); override; procedure SetCount(AValue: SizeInt); procedure InitializeList; virtual; procedure InternalInsert(AIndex: SizeInt; const AValue: T); private function GetItem(AIndex: SizeInt): T; procedure SetItem(AIndex: SizeInt; const AValue: T); public constructor Create; overload; constructor Create(const AComparer: IComparer); overload; constructor Create(ACollection: TEnumerable); overload; constructor Create(aValues : Array of T); overload; {$IFDEF ENABLE_METHODS_WITH_TEnumerableWithPointers} constructor Create(ACollection: TEnumerableWithPointers); overload; {$ENDIF} destructor Destroy; override; function Add(const AValue: T): SizeInt; virtual; procedure AddRange(const AValues: array of T); virtual; overload; procedure AddRange(const AEnumerable: IEnumerable); overload; procedure AddRange(AEnumerable: TEnumerable); overload; {$IFDEF ENABLE_METHODS_WITH_TEnumerableWithPointers} procedure AddRange(AEnumerable: TEnumerableWithPointers); overload; {$ENDIF} procedure Insert(AIndex: SizeInt; const AValue: T); virtual; procedure InsertRange(AIndex: SizeInt; const AValues: array of T); virtual; overload; procedure InsertRange(AIndex: SizeInt; const AEnumerable: IEnumerable); overload; procedure InsertRange(AIndex: SizeInt; const AEnumerable: TEnumerable); overload; {$IFDEF ENABLE_METHODS_WITH_TEnumerableWithPointers} procedure InsertRange(AIndex: SizeInt; const AEnumerable: TEnumerableWithPointers); overload; {$ENDIF} procedure Pack; overload; procedure Pack(const IsEmpty: TEmptyFunc); overload; function Remove(const AValue: T): SizeInt; function RemoveItem(const Value: T; Direction: TDirection): SizeInt; procedure Delete(AIndex: SizeInt); inline; procedure DeleteRange(AIndex, ACount: SizeInt); function ExtractIndex(const AIndex: SizeInt): T; overload; Function ExtractAt(const AIndex: SizeInt): T; inline; function Extract(const 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(const AValue: T): Boolean; inline; function IndexOf(const AValue: T): SizeInt; virtual; function LastIndexOf(const AValue: T): SizeInt; virtual; procedure Reverse; procedure TrimExcess; override; procedure Sort; overload; procedure Sort(const AComparer: IComparer); overload; function BinarySearch(const AItem: T; out AIndex: SizeInt): Boolean; overload; function BinarySearch(const AItem: T; out AIndex: SizeInt; const AComparer: IComparer): 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 = class(TList) private FDuplicates: TDuplicates; FSortStyle: TCollectionSortStyle; function GetSorted: boolean; procedure SetSorted(AValue: boolean); procedure SetSortStyle(AValue: TCollectionSortStyle); protected procedure InitializeList; override; public function Add(const AValue: T): SizeInt; override; overload; procedure AddRange(const AValues: array of T); override; overload; procedure Insert(AIndex: SizeInt; const AValue: T); override; procedure Exchange(AIndex1, AIndex2: SizeInt); override; procedure Move(AIndex, ANewIndex: SizeInt); override; procedure InsertRange(AIndex: SizeInt; const 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 = class private FList: TList; FDuplicates: TDuplicates; FLock: TRTLCriticalSection; public constructor Create; destructor Destroy; override; procedure Add(const AValue: T); procedure Remove(const AValue: T); procedure Clear; function LockList: TList; procedure UnlockList; inline; property Duplicates: TDuplicates read FDuplicates write FDuplicates; end; TQueue = class(TCustomList) public type TPointersEnumerator = class(TCustomPointersEnumerator) protected FQueue: TQueue; FIndex: SizeInt; function DoMoveNext: boolean; override; function DoGetCurrent: PT; override; public constructor Create(AQueue: TQueue); end; protected function PrepareAddingItem: SizeInt; override; function GetPtrEnumerator: TEnumerator; 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; override; public type TEnumerator = class(TCustomListEnumerator) public constructor Create(AQueue: TQueue); end; function GetEnumerator: TEnumerator; reintroduce; private FLow: SizeInt; procedure MoveToFront; protected procedure SetCapacity(AValue: SizeInt); override; function DoRemove(AIndex: SizeInt; ACollectionNotification: TCollectionNotification): T; override; function GetCount: SizeInt; override; public constructor Create(ACollection: TEnumerable); overload; {$IFDEF ENABLE_METHODS_WITH_TEnumerableWithPointers} constructor Create(ACollection: TEnumerableWithPointers); overload; {$ENDIF} destructor Destroy; override; procedure Enqueue(const AValue: T); function Dequeue: T; function Extract: T; function Peek: T; procedure Clear; procedure TrimExcess; override; end; TStack = class(TCustomListWithPointers) 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; override; public type TEnumerator = class(TCustomListEnumerator); function GetEnumerator: TEnumerator; reintroduce; protected function DoRemove(AIndex: SizeInt; ACollectionNotification: TCollectionNotification): T; override; procedure SetCapacity(AValue: SizeInt); override; public constructor Create(ACollection: TEnumerable); overload; {$IFDEF ENABLE_METHODS_WITH_TEnumerableWithPointers} constructor Create(ACollection: TEnumerableWithPointers); overload; {$ENDIF} destructor Destroy; override; procedure Clear; procedure Push(const AValue: T); function Pop: T; inline; function Peek: T; function Extract: T; inline; procedure TrimExcess; override; end; TObjectList = class(TList) private FObjectsOwner: Boolean; protected procedure Notify(const AValue: T; ACollectionNotification: TCollectionNotification); override; public constructor Create(AOwnsObjects: Boolean = True); overload; constructor Create(const AComparer: IComparer; AOwnsObjects: Boolean = True); overload; constructor Create(ACollection: TEnumerable; AOwnsObjects: Boolean = True); overload; {$IFDEF ENABLE_METHODS_WITH_TEnumerableWithPointers} constructor Create(ACollection: TEnumerableWithPointers; AOwnsObjects: Boolean = True); overload; {$ENDIF} property OwnsObjects: Boolean read FObjectsOwner write FObjectsOwner; end; TObjectQueue = class(TQueue) private FObjectsOwner: Boolean; protected procedure Notify(const AValue: T; ACollectionNotification: TCollectionNotification); override; public constructor Create(AOwnsObjects: Boolean = True); overload; constructor Create(ACollection: TEnumerable; AOwnsObjects: Boolean = True); overload; {$IFDEF ENABLE_METHODS_WITH_TEnumerableWithPointers} constructor Create(ACollection: TEnumerableWithPointers; AOwnsObjects: Boolean = True); overload; {$ENDIF} procedure Dequeue; property OwnsObjects: Boolean read FObjectsOwner write FObjectsOwner; end; TObjectStack = class(TStack) private FObjectsOwner: Boolean; protected procedure Notify(const AValue: T; ACollectionNotification: TCollectionNotification); override; public constructor Create(AOwnsObjects: Boolean = True); overload; constructor Create(ACollection: TEnumerable; AOwnsObjects: Boolean = True); overload; {$IFDEF ENABLE_METHODS_WITH_TEnumerableWithPointers} constructor Create(ACollection: TEnumerableWithPointers; AOwnsObjects: Boolean = True); overload; {$ENDIF} function Pop: T; property OwnsObjects: Boolean read FObjectsOwner write FObjectsOwner; end; PObject = ^TObject; {$I inc\generics.dictionariesh.inc} { TCustomHashSet } TCustomSet = class(TEnumerableWithPointers) protected FOnNotify: TCollectionNotifyEvent; public type PT = ^T; protected type TCustomSetEnumerator = class(TEnumerator) protected var FEnumerator: TEnumerator; function DoMoveNext: boolean; override; function DoGetCurrent: T; override; function GetCurrent: T; virtual; abstract; public constructor Create(ASet: TCustomSet); virtual; abstract; destructor Destroy; override; end; protected function DoGetEnumerator: TEnumerator; override; function GetCount: SizeInt; virtual; abstract; function GetCapacity: SizeInt; virtual; abstract; procedure SetCapacity(AValue: SizeInt); virtual; abstract; function GetOnNotify: TCollectionNotifyEvent; virtual; abstract; procedure SetOnNotify(AValue: TCollectionNotifyEvent); virtual; abstract; public constructor Create; virtual; abstract; overload; constructor Create(ACollection: TEnumerable); overload; {$IFDEF ENABLE_METHODS_WITH_TEnumerableWithPointers} constructor Create(ACollection: TEnumerableWithPointers); overload; {$ENDIF} function GetEnumerator: TCustomSetEnumerator; reintroduce; virtual; abstract; function Add(const AValue: T): Boolean; virtual; abstract; function Remove(const AValue: T): Boolean; virtual; abstract; function Extract(const AValue: T): T; virtual; abstract; procedure Clear; virtual; abstract; function Contains(const AValue: T): Boolean; virtual; abstract; function AddRange(const AValues: array of T): Boolean; overload; function AddRange(const AEnumerable: IEnumerable): Boolean; overload; function AddRange(AEnumerable: TEnumerable): Boolean; overload; {$IFDEF ENABLE_METHODS_WITH_TEnumerableWithPointers} function AddRange(AEnumerable: TEnumerableWithPointers): Boolean; overload; {$ENDIF} procedure UnionWith(AHashSet: TCustomSet); procedure IntersectWith(AHashSet: TCustomSet); procedure ExceptWith(AHashSet: TCustomSet); procedure SymmetricExceptWith(AHashSet: TCustomSet); property Count: SizeInt read GetCount; property Capacity: SizeInt read GetCapacity write SetCapacity; procedure TrimExcess; virtual; abstract; property OnNotify: TCollectionNotifyEvent read GetOnNotify write SetOnNotify; end; { THashSet } THashSet = class(TCustomSet) private procedure InternalDictionaryNotify(ASender: TObject; const AItem: T; AAction: TCollectionNotification); protected FInternalDictionary: TOpenAddressingLP; public type THashSetEnumerator = class(TCustomSetEnumerator) protected type TDictionaryEnumerator = TDictionary.TKeyEnumerator; function GetCurrent: T; override; public constructor Create(ASet: TCustomSet); override; end; TPointersEnumerator = class(TCustomPointersEnumerator) protected FEnumerator: TEnumerator; function DoMoveNext: boolean; override; function DoGetCurrent: PT; override; public constructor Create(AHashSet: THashSet); end; protected function GetPtrEnumerator: TEnumerator; override; function GetCount: SizeInt; override; function GetCapacity: SizeInt; override; procedure SetCapacity(AValue: SizeInt); override; function GetOnNotify: TCollectionNotifyEvent; override; procedure SetOnNotify(AValue: TCollectionNotifyEvent); override; public constructor Create; override; overload; constructor Create(const AComparer: IEqualityComparer); virtual; overload; destructor Destroy; override; function GetEnumerator: TCustomSetEnumerator; override; function Add(const AValue: T): Boolean; override; function Remove(const AValue: T): Boolean; override; function Extract(const AValue: T): T; override; procedure Clear; override; function Contains(const AValue: T): Boolean; override; procedure TrimExcess; override; end; TPair = record public Key: TKey; Value: TValue; Info: TInfo; end; TAVLTreeNode = record private type TNodePair = TPair; public type PNode = ^TAVLTreeNode; 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 = class abstract(TEnumerator) 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 = class abstract(TEnumerableWithPointers) private FTree: TTree; function GetCount: SizeInt; inline; protected function GetPtrEnumerator: TEnumerator; override; function DoGetEnumerator: TTreeEnumerator; override; public constructor Create(ATree: TTree); function ToArray: TArray; override; final; property Count: SizeInt read GetCount; end; TAVLTreeEnumerator = class(TCustomTreeEnumerator) protected FLowToHigh: boolean; function DoMoveNext: Boolean; override; public constructor Create(ATree: TObject; ALowToHigh: boolean = true); property LowToHigh: boolean read FLowToHigh; end; TNodeNotifyEvent = procedure(ASender: TObject; ANode: PNode; AAction: TCollectionNotification; ADispose: boolean) of object; TCustomAVLTreeMap = class private type TTree = TCustomAVLTreeMap; public type TNode = TAVLTreeNode; PNode = ^TNode; PPNode = ^PNode; TTreePair = TPair; PKey = ^TKey; PValue = ^TValue; private type // type exist only for generic constraint in TNodeCollection (non functional - PPNode has no sense) TPNodeEnumerator = TAVLTreeEnumerator; private var FDuplicates: TDuplicates; FComparer: IComparer; protected FCount: SizeInt; FRoot: PNode; FKeys: TEnumerable; FValues: TEnumerable; FOnNodeNotify: TNodeNotifyEvent; FOnKeyNotify: TCollectionNotifyEvent; FOnValueNotify: TCollectionNotifyEvent; 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(const 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(const AKey: TKey; ACollectionNotification: TCollectionNotification); inline; procedure ValueNotify(const AValue: TValue; ACollectionNotification: TCollectionNotification); inline; procedure NodeNotify(ANode: PNode; ACollectionNotification: TCollectionNotification; ADispose: boolean); inline; procedure SetValue(var AValue: TValue; const ANewValue: TValue); function GetItem(const AKey: TKey): TValue; procedure SetItem(const AKey: TKey; const AValue: TValue); property Items[Index: TKey]: TValue read GetItem write SetItem; // for reporting procedure WriteStr(AStream: TStream; const AText: string); public type TPairEnumerator = class(TAVLTreeEnumerator) protected function GetCurrent: TTreePair; override; end; TNodeEnumerator = class(TAVLTreeEnumerator) protected function GetCurrent: PNode; override; end; TKeyEnumerator = class(TAVLTreeEnumerator) protected function GetCurrent: TKey; override; end; TPKeyEnumerator = class(TAVLTreeEnumerator) protected function GetCurrent: PKey; override; end; TValueEnumerator = class(TAVLTreeEnumerator) protected function GetCurrent: TValue; override; end; TPValueEnumerator = class(TAVLTreeEnumerator) protected function GetCurrent: PValue; override; end; TNodeCollection = class(TTreeEnumerable) private property Ptr; // PPNode has no sense, so hide enumerator for PPNode end; TKeyCollection = class(TTreeEnumerable); TValueCollection = class(TTreeEnumerable); 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); virtual; overload; function NewNode: PNode; function NewNodeArray(ACount: SizeInt): PNode; overload; procedure NewNodeArray(out AArray: TArray; ACount: SizeInt); overload; procedure DisposeNode(ANode: PNode); procedure DisposeNodeArray(ANode: PNode; ACount: SizeInt); overload; procedure DisposeNodeArray(var AArray: TArray); overload; destructor Destroy; override; function AddNode(ANode: PNode): boolean; overload; inline; function Add(const APair: TTreePair): PNode; overload; inline; function Add(const AKey: TKey; const AValue: TValue): PNode; overload; inline; function Remove(const AKey: TKey; ADisposeNode: boolean = true): boolean; function ExtractPair(const AKey: TKey; ADisposeNode: boolean = true): TTreePair; overload; function ExtractPair(const ANode: PNode; ADispose: boolean = true): TTreePair; overload; function Extract(const AKey: TKey; ADisposeNode: boolean): PNode; function ExtractNode(ANode: PNode; ADispose: boolean): PNode; 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(const AKey: TKey): PNode; function ContainsKey(const AKey: TKey; out ANode: PNode): boolean; overload; inline; function ContainsKey(const 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 read FOnNodeNotify write FOnNodeNotify; property OnKeyNotify: TCollectionNotifyEvent read FOnKeyNotify write FOnKeyNotify; property OnValueNotify: TCollectionNotifyEvent read FOnValueNotify write FOnValueNotify; end; TAVLTreeMap = class(TCustomAVLTreeMap) public property Items; default; end; TIndexedAVLTreeMap = class(TCustomAVLTreeMap) 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 = class(TAVLTreeMap) protected property OnKeyNotify; property OnValueNotify; property Items; public type TItemEnumerator = TKeyEnumerator; public function Add(const AValue: T): PNode; reintroduce; inline; function AddNode(ANode: PNode): boolean; reintroduce; inline; property OnNotify: TCollectionNotifyEvent read FOnKeyNotify write FOnKeyNotify; end; TIndexedAVLTree = class(TIndexedAVLTreeMap) protected property OnKeyNotify; property OnValueNotify; public type TItemEnumerator = TKeyEnumerator; public function Add(const AValue: T): PNode; reintroduce; inline; function AddNode(ANode: PNode): boolean; reintroduce; inline; property OnNotify: TCollectionNotifyEvent read FOnKeyNotify write FOnKeyNotify; end; TSortedSet = class(TCustomSet) private procedure InternalAVLTreeNotify(ASender: TObject; const AItem: T; AAction: TCollectionNotification); protected FInternalTree: TAVLTree; public type TSortedSetEnumerator = class(TCustomSetEnumerator) protected type TTreeEnumerator = TAVLTree.TItemEnumerator; function GetCurrent: T; override; public constructor Create(ASet: TCustomSet); override; end; TPointersEnumerator = class(TCustomPointersEnumerator) protected FEnumerator: TEnumerator; function DoMoveNext: boolean; override; function DoGetCurrent: PT; override; public constructor Create(ASortedSet: TSortedSet); end; protected function GetPtrEnumerator: TEnumerator; override; function GetCount: SizeInt; override; function GetCapacity: SizeInt; override; procedure SetCapacity(AValue: SizeInt); override; function GetOnNotify: TCollectionNotifyEvent; override; procedure SetOnNotify(AValue: TCollectionNotifyEvent); override; public constructor Create; override; overload; constructor Create(const AComparer: IComparer); virtual; overload; destructor Destroy; override; function GetEnumerator: TCustomSetEnumerator; override; function Add(const AValue: T): Boolean; override; function Remove(const AValue: T): Boolean; override; function Extract(const AValue: T): T; override; procedure Clear; override; function Contains(const AValue: T): Boolean; override; procedure TrimExcess; override; end; TSortedHashSet = class(TCustomSet) private procedure InternalDictionaryNotify(ASender: TObject; const AItem: PT; AAction: TCollectionNotification); protected FInternalDictionary: TOpenAddressingLP; FInternalTree: TAVLTree; function DoGetEnumerator: TEnumerator; override; function GetCount: SizeInt; override; function GetCapacity: SizeInt; override; procedure SetCapacity(AValue: SizeInt); override; function GetOnNotify: TCollectionNotifyEvent; override; procedure SetOnNotify(AValue: TCollectionNotifyEvent); override; protected type TSortedHashSetEqualityComparer = class(TInterfacedObject, IEqualityComparer) private FComparer: IComparer; FEqualityComparer: IEqualityComparer; function Equals(const ALeft, ARight: PT): Boolean; function GetHashCode(const AValue: PT): UInt32; public constructor Create(const AComparer: IComparer); overload; constructor Create(const AEqualityComparer: IEqualityComparer); overload; constructor Create(const AComparer: IComparer; const AEqualityComparer: IEqualityComparer); overload; end; public type TSortedHashSetEnumerator = class(TCustomSetEnumerator) protected type TTreeEnumerator = TAVLTree.TItemEnumerator; function GetCurrent: T; override; public constructor Create(ASet: TCustomSet); override; end; TPointersEnumerator = class(TCustomPointersEnumerator) protected FEnumerator: TEnumerator; function DoMoveNext: boolean; override; function DoGetCurrent: PT; override; public constructor Create(ASortedHashSet: TSortedHashSet); end; protected function GetPtrEnumerator: TEnumerator; override; public constructor Create; override; overload; constructor Create(const AComparer: IEqualityComparer); overload; constructor Create(const AComparer: IComparer); overload; constructor Create(const AComparer: IComparer; const AEqualityComparer: IEqualityComparer); overload; destructor Destroy; override; function GetEnumerator: TCustomSetEnumerator; override; function Add(const AValue: T): Boolean; override; function Remove(const AValue: T): Boolean; override; function Extract(const AValue: T): T; override; procedure Clear; override; function Contains(const AValue: T): Boolean; override; procedure TrimExcess; override; end; function InCircularRange(ABottom, AItem, ATop: SizeInt): Boolean; procedure ErrorArgumentOutOfRange; overload; procedure ErrorArgumentOutOfRange(aIndex, aMaxIndex: SizeInt; aListObj: TObject); overload; procedure ErrorArgumentOutOfRange(aIndex, aMaxIndex: SizeInt); overload; var EmptyRecord: TEmptyRecord; implementation procedure ErrorArgumentOutOfRange; begin raise EArgumentOutOfRangeException.Create(SArgumentOutOfRange); end; procedure ErrorArgumentOutOfRange(aIndex, aMaxIndex: SizeInt; aListObj: TObject); overload; begin raise EArgumentOutOfRangeException.Create(ListIndexErrorMsg(aIndex,aMaxIndex,aListObj)); end; procedure ErrorArgumentOutOfRange(aIndex, aMaxIndex: SizeInt); overload; begin raise EArgumentOutOfRangeException.Create(ListIndexErrorMsg(aIndex,aMaxIndex,'')); end; 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 } class function TCustomArrayHelper.BinarySearch(const AValues: array of T; const AItem: T; out AFoundIndex: SizeInt; const AComparer: IComparer): Boolean; begin Result := BinarySearch(AValues, AItem, AFoundIndex, AComparer, Low(AValues), Length(AValues)); end; class function TCustomArrayHelper.BinarySearch(const AValues: array of T; const AItem: T; out AFoundIndex: SizeInt): Boolean; begin Result := BinarySearch(AValues, AItem, AFoundIndex, TComparerBugHack.Default, Low(AValues), Length(AValues)); end; class function TCustomArrayHelper.BinarySearch(const AValues: array of T; const AItem: T; out ASearchResult: TBinarySearchResult; const AComparer: IComparer): Boolean; begin Result := BinarySearch(AValues, AItem, ASearchResult, AComparer, Low(AValues), Length(AValues)); end; class function TCustomArrayHelper.BinarySearch(const AValues: array of T; const AItem: T; out ASearchResult: TBinarySearchResult): Boolean; begin Result := BinarySearch(AValues, AItem, ASearchResult, TComparerBugHack.Default, Low(AValues), Length(AValues)); end; class procedure TCustomArrayHelper.Sort(var AValues: array of T); begin QuickSort(AValues, Low(AValues), High(AValues), TComparerBugHack.Default); end; class procedure TCustomArrayHelper.Sort(var AValues: array of T; const AComparer: IComparer); begin QuickSort(AValues, Low(AValues), High(AValues), AComparer); end; class procedure TCustomArrayHelper.Sort(var AValues: array of T; const AComparer: IComparer; AIndex, ACount: SizeInt); begin if ACount <= 1 then Exit; QuickSort(AValues, AIndex, Pred(AIndex + ACount), AComparer); end; { TArrayHelper } class procedure TArrayHelper.QSort(p: PT; n, reasonable: SizeUint; const cmp: IComparer); const INSERTION_SORT_THRESHOLD = 10; var L, R: SizeInt; pivot, temp: T; begin Prefetch(p); while (n > INSERTION_SORT_THRESHOLD) and (reasonable > 0) do begin { If 'reasonable' reaches zero, the algorithm changes to heapsort } Dec(reasonable); pivot := Median(p, n, cmp)^; R := 0; L := n - 1; repeat while cmp.Compare((p + R)^, pivot) < 0 do inc(R); while cmp.Compare(pivot, (p + L)^) < 0 do dec(L); if R <= L then begin temp := (p + R)^; (p + R)^ := (p + L)^; (p + L)^ := temp; inc(R); dec(L); end; until R > L; { [0 .. L], [R .. n - 1]. Possible edge cases are L = -1 or R = n. Recurse into the smaller half. } if n - R <= L then begin QSort(p + R, n - R, reasonable, cmp); n := L + 1; end else begin QSort(p, L + 1, reasonable, cmp); p := p + R; n := n - R; end; end; { When the partition is small, switch to insertion sort } if (n <= INSERTION_SORT_THRESHOLD) then begin L := 1; while L < n do begin pivot := (P + L)^; R := L - 1; while (R >= 0) and (cmp.compare((p + R)^, pivot) > 0) do begin (p + (R + 1))^ := (p + R)^; Dec(R); end; (p + (R + 1))^ := pivot; Inc(L); end; end else HeapSort(p, n, cmp); end; class function TArrayHelper.Median(p: PT; n: SizeUint; const cmp: IComparer): PT; var a, b, c, temp: PT; begin a := p; b := p + n div 2; c := p + (n - 1); if cmp.Compare(b^, a^) < 0 then begin temp := a; a := b; b := temp; end; if cmp.Compare(c^, b^) < 0 then begin temp := b; b := c; c := temp; end; if cmp.Compare(b^, a^) < 0 then result := a else result := b; end; class procedure TArrayHelper.HeapSort(p: PT; n: SizeUint; const cmp: IComparer); var temp: T; i: SizeInt; begin for i := SizeUint(n - 2) div 2 downto 0 do begin temp := (p + i)^; HeapReplacePessimistic(p, n, i, temp, cmp); end; for i := n - 1 downto 1 do begin temp := (p + i)^; (p + i)^ := p^; HeapReplacePessimistic(p, i, 0, temp, cmp); end; end; { HeapReplacePessimistic replaces q[id] with 'item' by doing something like startId := id; q[id] := item; id := HeapDownThoroughly(q, nq, id); id := HeapUpToId(q, nq, id, startId); Where 'HeapDownThoroughly' sinks the element all the way down, without stopping at the correct position, so it must float up afterwards. See Python's 'heapq' module for explanation why this is an improvement over simple HeapDown. TL;DR: HeapDownThoroughly uses 1 fewer comparison per level, and the item usually ends up close to the bottom, so these savings pay off. Moreover, heap invariant assumed for q[id .. nq - 1] rather than whole q[0 .. nq - 1] which matters when heapifying the array from the end. } class procedure TArrayHelper.HeapReplacePessimistic(q: PT; nq, id: SizeUint; const item: T; const cmp: IComparer); var iChild, iParent, start: SizeUint; begin start := id; repeat iChild := 2 * id + 1; { childs of q[id] are q[2 * id + 1] ... q[2 * id + 2]. } if iChild >= nq then break; if (iChild + 1 < nq) and (cmp.Compare((q + iChild)^, (q + iChild + 1)^) < 0) then iChild := iChild + 1; (q + id)^ := (q + iChild)^; id := iChild; until false; while id > start do begin iParent := SizeUint(id - 1) div 2; if cmp.Compare((q + iParent)^, item) >= 0 then break; (q + id)^ := (q + iParent)^; id := iParent; end; (q + id)^ := item; end; class procedure TArrayHelper.QuickSort(var AValues: array of T; ALeft, ARight: SizeInt; const AComparer: IComparer); var N: SizeInt; begin N := ARight - ALeft + 1; if N > 1 then { Use BSR as a base-2 logarithm } QSort( PT(AValues) + ALeft, N, {$if defined(CPU64)} 2 * BsrQWord(QWord(N)), {$elseif defined(CPU32)} 2 * BsrDWord(LongWord(N)), {$elseif defined(CPU16)} 2 * BsrWord(Word(N)), {$endif} AComparer ); end; class function TArrayHelper.BinarySearch(const AValues: array of T; const AItem: T; out ASearchResult: TBinarySearchResult; const AComparer: IComparer; AIndex, ACount: SizeInt): Boolean; var imin, imax, imid: Int32; begin if Length(AValues) = 0 then begin ASearchResult.CompareResult := 0; ASearchResult.FoundIndex := -1; ASearchResult.CandidateIndex := -1; Exit(False); end; // 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 procedure TArrayHelper.Copy(const aSource: array of T; var aDestination: array of T; aCount: NativeInt); begin Copy(aSource,aDestination,0,0,aCount); end; class procedure TArrayHelper.Copy(const aSource: array of T; var aDestination: array of T; aSourceIndex, aDestIndex, aCount: SizeInt); var I : Integer; begin if (Length(aSource)>0) and (Length(aDestination)>0) and ((@aSource[0]) = (@aDestination[0])) then raise EArgumentException.Create(SErrSameArrays); if (aCount<0) or (aCount>(Length(aSource)-aSourceIndex)) or (aCount>(Length(aDestination)-aDestIndex)) then ErrorArgumentOutOfRange; if IsManagedType(T) then begin // maybe this can be optimized too ? For I:=0 to aCount-1 do aDestination[aDestIndex+i]:=aSource[aSourceIndex+i]; end else Move(Pointer(@aSource[aSourceIndex])^, Pointer(@aDestination[aDestIndex])^, SizeOf(T)*aCount); end; class function TArrayHelper.Concat(const Args: array of TArray): TArray; var TotalLen: SizeInt; CurLen,Dest,i: SizeInt; begin Result:=Nil; TotalLen:=0; for i:=0 to Length(Args)-1 do Inc(TotalLen,Length(Args[i])); SetLength(Result,TotalLen); Dest:=0; for i:=0 to Length(Args)-1 do begin CurLen:=Length(Args[i]); if CurLen>0 then begin Copy(Args[i],Result,0,Dest,CurLen); Inc(Dest,CurLen); end; end; end; class function TArrayHelper.BinarySearch(const AValues: array of T; const AItem: T; out AFoundIndex: SizeInt; const AComparer: IComparer; AIndex, ACount: SizeInt): Boolean; var imin, imax, imid: Int32; LCompare: SizeInt; begin if Length(AValues) = 0 then begin AFoundIndex := -1; Exit(False); end; // 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 AFoundIndex := imin; LCompare := AComparer.Compare(AValues[imin], AItem); Result := (imax = imin) and (LCompare = 0); if not Result and (LCompare < 0) then Inc(AFoundIndex); end; { TEnumerator } function TEnumerator.DoMoveNext: boolean; begin Result:=False; end; function TEnumerator.MoveNext: boolean; begin Exit(DoMoveNext); end; { TEnumerable } function TEnumerable.ToArrayImpl(ACount: SizeInt): TArray; var i: SizeInt; LEnumerator: TEnumerator; begin Result := nil; 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.GetEnumerator: TEnumerator; begin Exit(DoGetEnumerator); end; function TEnumerable.ToArray: TArray; var LEnumerator: TEnumerator; LBuffer: TList; begin LBuffer := TList.Create; try LEnumerator := GetEnumerator; while LEnumerator.MoveNext do LBuffer.Add(LEnumerator.Current); Result := LBuffer.ToArray; finally LBuffer.Free; LEnumerator.Free; end; end; { TCustomPointersCollection } function TCustomPointersCollection.Enumerable: TLocalEnumerable; begin Result := TLocalEnumerable(@Self); end; function TCustomPointersCollection.GetEnumerator: TEnumerator; begin Result := Enumerable.GetPtrEnumerator; end; { TEnumerableWithPointers } function TEnumerableWithPointers.GetPtr: PPointersCollection; begin Result := PPointersCollection(Self); end; { TCustomList } function TCustomList.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.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.ToArray: TArray; begin Result := ToArrayImpl(Count); end; function TCustomList.GetCount: SizeInt; begin Result := FLength; end; procedure TCustomList.Notify(const AValue: T; ACollectionNotification: TCollectionNotification); begin if Assigned(FOnNotify) then FOnNotify(Self, AValue, ACollectionNotification); end; function TCustomList.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.GetCapacity: SizeInt; begin Result := Length(FItems); end; { TCustomListEnumerator } function TCustomListEnumerator.DoMoveNext: boolean; begin Inc(FIndex); Result := (FList.FLength <> 0) and (FIndex < FList.FLength) end; function TCustomListEnumerator.DoGetCurrent: T; begin Result := GetCurrent; end; function TCustomListEnumerator.GetCurrent: T; begin Result := FList.FItems[FIndex]; end; constructor TCustomListEnumerator.Create(AList: TCustomList); begin inherited Create; FIndex := -1; FList := AList; end; { TCustomListWithPointers.TPointersEnumerator } function TCustomListWithPointers.TPointersEnumerator.DoMoveNext: boolean; begin Inc(FIndex); Result := (FList.FLength <> 0) and (FIndex < FList.FLength) end; function TCustomListWithPointers.TPointersEnumerator.DoGetCurrent: PT; begin Result := @FList.FItems[FIndex];; end; constructor TCustomListWithPointers.TPointersEnumerator.Create(AList: TCustomListWithPointers); begin inherited Create; FIndex := -1; FList := AList; end; { TCustomListWithPointers } function TCustomListWithPointers.GetPtrEnumerator: TEnumerator; begin Result := TPointersEnumerator.Create(Self); end; { TList } procedure TList.InitializeList; begin end; constructor TList.Create; begin InitializeList; FComparer := TComparer.Default; end; constructor TList.Create(const AComparer: IComparer); begin InitializeList; FComparer := AComparer; end; constructor TList.Create(ACollection: TEnumerable); var LItem: T; begin Create; for LItem in ACollection do Add(LItem); end; constructor TList.Create(aValues : Array of T); var LItem: T; begin Create; for LItem in aValues do Add(LItem); end; {$IFDEF ENABLE_METHODS_WITH_TEnumerableWithPointers} constructor TList.Create(ACollection: TEnumerableWithPointers); var LItem: PT; begin Create; for LItem in ACollection.Ptr^ do Add(LItem^); end; {$ENDIF} destructor TList.Destroy; begin SetCapacity(0); end; procedure TList.SetCapacity(AValue: SizeInt); begin if AValue < Count then Count := AValue; SetLength(FItems, AValue); end; procedure TList.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.GetItem(AIndex: SizeInt): T; begin if (AIndex < 0) or (AIndex >= Count) then raise EArgumentOutOfRangeException.CreateRes(@SArgumentOutOfRange); Result := FItems[AIndex]; end; procedure TList.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.GetEnumerator: TEnumerator; begin Result := TEnumerator.Create(Self); end; function TList.DoGetEnumerator: {Generics.Collections.}TEnumerator; begin Result := GetEnumerator; end; function TList.Add(const AValue: T): SizeInt; begin Result := PrepareAddingItem; FItems[Result] := AValue; Notify(AValue, cnAdded); end; procedure TList.AddRange(const AValues: array of T); begin InsertRange(Count, AValues); end; procedure TList.AddRange(const AEnumerable: IEnumerable); var LValue: T; begin for LValue in AEnumerable do Add(LValue); end; procedure TList.AddRange(AEnumerable: TEnumerable); var LValue: T; begin for LValue in AEnumerable do Add(LValue); end; {$IFDEF ENABLE_METHODS_WITH_TEnumerableWithPointers} procedure TList.AddRange(AEnumerable: TEnumerableWithPointers); var LValue: PT; begin for LValue in AEnumerable.Ptr^ do Add(LValue^); end; {$ENDIF} procedure TList.InternalInsert(AIndex: SizeInt; const 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.Insert(AIndex: SizeInt; const AValue: T); begin if (AIndex < 0) or (AIndex > Count) then raise EArgumentOutOfRangeException.CreateRes(@SArgumentOutOfRange); InternalInsert(AIndex, AValue); end; procedure TList.InsertRange(AIndex: SizeInt; const 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.InsertRange(AIndex: SizeInt; const AEnumerable: IEnumerable); 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.InsertRange(AIndex: SizeInt; const AEnumerable: TEnumerable); 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.Pack; begin Pack( function(const L, R: T): Boolean begin Result := FComparer.Compare(L, R) = 0; end); end; procedure TList.Pack(const IsEmpty: TEmptyFunc); var I: Integer; begin for I := Count - 1 downto 0 do if IsEmpty(List[I], Default(T)) then DoRemove(I, cnRemoved); end; {$IFDEF ENABLE_METHODS_WITH_TEnumerableWithPointers} procedure TList.InsertRange(AIndex: SizeInt; const AEnumerable: TEnumerableWithPointers); 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.Remove(const AValue: T): SizeInt; begin Result := IndexOf(AValue); if Result >= 0 then DoRemove(Result, cnRemoved); end; function TList.RemoveItem(const Value: T; Direction: TDirection): SizeInt; begin if Direction=TDirection.FromBeginning then Result:=Remove(Value) else begin Result:=LastIndexOf(Value); if Result>=0 then DoRemove(Result, cnRemoved); end; end; procedure TList.Delete(AIndex: SizeInt); begin DoRemove(AIndex, cnRemoved); end; procedure TList.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); LDeleted := nil; 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.ExtractIndex(const AIndex: SizeInt): T; begin Result := DoRemove(AIndex, cnExtracted); end; function TList.ExtractAt(const AIndex: SizeInt): T; begin Result:=ExtractIndex(AIndex); end; function TList.Extract(const AValue: T): T; var LIndex: SizeInt; begin LIndex := IndexOf(AValue); if LIndex < 0 then Exit(Default(T)); Result := DoRemove(LIndex, cnExtracted); end; procedure TList.Exchange(AIndex1, AIndex2: SizeInt); var LTemp: T; begin LTemp := FItems[AIndex1]; FItems[AIndex1] := FItems[AIndex2]; FItems[AIndex2] := LTemp; end; procedure TList.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.First: T; begin Result := Items[0]; end; function TList.Last: T; begin Result := Items[Pred(Count)]; end; procedure TList.Clear; begin SetCount(0); SetCapacity(0); end; procedure TList.TrimExcess; begin SetCapacity(Count); end; function TList.Contains(const AValue: T): Boolean; begin Result := IndexOf(AValue) >= 0; end; function TList.IndexOf(const 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.LastIndexOf(const 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.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.Sort; begin TArrayHelperBugHack.Sort(FItems, FComparer, 0, Count); end; procedure TList.Sort(const AComparer: IComparer); begin TArrayHelperBugHack.Sort(FItems, AComparer, 0, Count); end; function TList.BinarySearch(const AItem: T; out AIndex: SizeInt): Boolean; begin Result := TArrayHelperBugHack.BinarySearch(FItems, AItem, AIndex, FComparer, 0, Count); end; function TList.BinarySearch(const AItem: T; out AIndex: SizeInt; const AComparer: IComparer): Boolean; begin Result := TArrayHelperBugHack.BinarySearch(FItems, AItem, AIndex, AComparer, 0, Count); end; { TSortedList } procedure TSortedList.InitializeList; begin FSortStyle := cssAuto; end; function TSortedList.Add(const 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.Insert(AIndex: SizeInt; const AValue: T); begin if FSortStyle = cssAuto then raise EListError.Create(SSortedListError) else inherited; end; procedure TSortedList.Exchange(AIndex1, AIndex2: SizeInt); begin if FSortStyle = cssAuto then raise EListError.Create(SSortedListError) else inherited; end; procedure TSortedList.Move(AIndex, ANewIndex: SizeInt); begin if FSortStyle = cssAuto then raise EListError.Create(SSortedListError) else inherited; end; procedure TSortedList.AddRange(const AValues: array of T); var i: T; begin for i in AValues do Add(i); end; procedure TSortedList.InsertRange(AIndex: SizeInt; const 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.GetSorted: boolean; begin Result := FSortStyle in [cssAuto, cssUser]; end; procedure TSortedList.SetSorted(AValue: boolean); begin if AValue then SortStyle := cssAuto else SortStyle := cssNone; end; procedure TSortedList.SetSortStyle(AValue: TCollectionSortStyle); begin if FSortStyle = AValue then Exit; if AValue = cssAuto then Sort; FSortStyle := AValue; end; function TSortedList.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 } constructor TThreadList.Create; begin inherited Create; FDuplicates:=dupIgnore; {$ifdef FPC_HAS_FEATURE_THREADING} InitCriticalSection(FLock); {$endif} FList := TList.Create; end; destructor TThreadList.Destroy; begin LockList; try FList.Free; inherited Destroy; finally UnlockList; {$ifdef FPC_HAS_FEATURE_THREADING} DoneCriticalSection(FLock); {$endif} end; end; procedure TThreadList.Add(const 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.Remove(const AValue: T); begin LockList; try FList.Remove(AValue); finally UnlockList; end; end; procedure TThreadList.Clear; begin LockList; try FList.Clear; finally UnlockList; end; end; function TThreadList.LockList: TList; begin Result:=FList; {$ifdef FPC_HAS_FEATURE_THREADING} System.EnterCriticalSection(FLock); {$endif} end; procedure TThreadList.UnlockList; begin {$ifdef FPC_HAS_FEATURE_THREADING} System.LeaveCriticalSection(FLock); {$endif} end; { TQueue.TPointersEnumerator } function TQueue.TPointersEnumerator.DoMoveNext: boolean; begin Inc(FIndex); Result := (FQueue.FLength <> 0) and (FIndex < FQueue.FLength) end; function TQueue.TPointersEnumerator.DoGetCurrent: PT; begin Result := @FQueue.FItems[FIndex]; end; constructor TQueue.TPointersEnumerator.Create(AQueue: TQueue); begin inherited Create; FIndex := Pred(AQueue.FLow); FQueue := AQueue; end; { TQueue.TEnumerator } constructor TQueue.TEnumerator.Create(AQueue: TQueue); begin inherited Create(AQueue); FIndex := Pred(AQueue.FLow); end; { TQueue } function TQueue.PrepareAddingItem: SizeInt; begin repeat result := FLength; if result <= High(FItems) then begin FLength := result + 1; exit; end; if SizeUint(FLow) >= 4 + SizeUint(result) div 4 then // If the empty space at the beginning is comparable to queue size, convert // // .......QQQQQQQQQ // ↑FLow ↑FLength=length(FItems) // // to // // QQQQQQQQQ....... // ↑FLow=0 // // and retry the shortcut above. MoveToFront else exit(inherited); until false; end; function TQueue.GetPtrEnumerator: TEnumerator; begin Result := TPointersenumerator.Create(Self); end; function TQueue.GetEnumerator: TEnumerator; begin Result := TEnumerator.Create(Self); end; function TQueue.DoGetEnumerator: {Generics.Collections.}TEnumerator; begin Result := GetEnumerator; end; function TQueue.DoRemove(AIndex: SizeInt; ACollectionNotification: TCollectionNotification): T; begin if Count = 0 then raise EArgumentOutOfRangeException.CreateRes(@SArgumentOutOfRange); 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.MoveToFront; var i: SizeInt; begin if FLength > FLow then if IsManagedType(T) then for i := 0 to FLength - FLow - 1 do FItems[i] := FItems[FLow + i] else Move(FItems[FLow], FItems[0], (FLength - FLow) * SizeOf(T)); FLength := FLength - FLow; FLow := 0; end; procedure TQueue.SetCapacity(AValue: SizeInt); begin if AValue < Count then raise EArgumentOutOfRangeException.CreateRes(@SArgumentOutOfRange); if AValue = FLength then Exit; MoveToFront; SetLength(FItems, AValue); end; function TQueue.GetCount: SizeInt; begin Result := FLength - FLow; end; constructor TQueue.Create(ACollection: TEnumerable); var LItem: T; begin for LItem in ACollection do Enqueue(LItem); end; {$IFDEF ENABLE_METHODS_WITH_TEnumerableWithPointers} constructor TQueue.Create(ACollection: TEnumerableWithPointers); var LItem: PT; begin for LItem in ACollection.Ptr^ do Enqueue(LItem^); end; {$ENDIF} destructor TQueue.Destroy; begin Clear; end; procedure TQueue.Enqueue(const AValue: T); var LIndex: SizeInt; begin LIndex := PrepareAddingItem; FItems[LIndex] := AValue; Notify(AValue, cnAdded); end; function TQueue.Dequeue: T; begin Result := DoRemove(FLow, cnRemoved); end; function TQueue.Extract: T; begin Result := DoRemove(FLow, cnExtracted); end; function TQueue.Peek: T; begin if (Count = 0) then raise EArgumentOutOfRangeException.CreateRes(@SArgumentOutOfRange); Result := FItems[FLow]; end; procedure TQueue.Clear; begin while Count <> 0 do Dequeue; FLow := 0; FLength := 0; end; procedure TQueue.TrimExcess; begin SetCapacity(Count); end; { TStack } function TStack.GetEnumerator: TEnumerator; begin Result := TEnumerator.Create(Self); end; function TStack.DoGetEnumerator: {Generics.Collections.}TEnumerator; begin Result := GetEnumerator; end; constructor TStack.Create(ACollection: TEnumerable); var LItem: T; begin for LItem in ACollection do Push(LItem); end; {$IFDEF ENABLE_METHODS_WITH_TEnumerableWithPointers} constructor TStack.Create(ACollection: TEnumerableWithPointers); var LItem: PT; begin for LItem in ACollection.Ptr^ do Push(LItem^); end; {$ENDIF} function TStack.DoRemove(AIndex: SizeInt; ACollectionNotification: TCollectionNotification): T; begin if (AIndex < 0) or (Count = 0) then raise EArgumentOutOfRangeException.CreateRes(@SArgumentOutOfRange); Result := FItems[AIndex]; FItems[AIndex] := Default(T); Dec(FLength); Notify(Result, ACollectionNotification); end; destructor TStack.Destroy; begin Clear; end; procedure TStack.Clear; begin while Count <> 0 do Pop; end; procedure TStack.SetCapacity(AValue: SizeInt); begin if AValue < Count then AValue := Count; SetLength(FItems, AValue); end; procedure TStack.Push(const AValue: T); var LIndex: SizeInt; begin LIndex := PrepareAddingItem; FItems[LIndex] := AValue; Notify(AValue, cnAdded); end; function TStack.Pop: T; begin Result := DoRemove(FLength - 1, cnRemoved); end; function TStack.Peek: T; begin if (Count = 0) then raise EArgumentOutOfRangeException.CreateRes(@SArgumentOutOfRange); Result := FItems[FLength - 1]; end; function TStack.Extract: T; begin Result := DoRemove(FLength - 1, cnExtracted); end; procedure TStack.TrimExcess; begin SetCapacity(Count); end; { TObjectList } procedure TObjectList.Notify(const AValue: T; ACollectionNotification: TCollectionNotification); begin inherited Notify(AValue, ACollectionNotification); if FObjectsOwner and (ACollectionNotification = cnRemoved) then TObject(AValue).Free; end; constructor TObjectList.Create(AOwnsObjects: Boolean); begin inherited Create; FObjectsOwner := AOwnsObjects; end; constructor TObjectList.Create(const AComparer: IComparer; AOwnsObjects: Boolean); begin inherited Create(AComparer); FObjectsOwner := AOwnsObjects; end; constructor TObjectList.Create(ACollection: TEnumerable; AOwnsObjects: Boolean); begin inherited Create(ACollection); FObjectsOwner := AOwnsObjects; end; {$IFDEF ENABLE_METHODS_WITH_TEnumerableWithPointers} constructor TObjectList.Create(ACollection: TEnumerableWithPointers; AOwnsObjects: Boolean); begin inherited Create(ACollection); FObjectsOwner := AOwnsObjects; end; {$ENDIF} { TObjectQueue } procedure TObjectQueue.Notify(const AValue: T; ACollectionNotification: TCollectionNotification); begin inherited Notify(AValue, ACollectionNotification); if FObjectsOwner and (ACollectionNotification = cnRemoved) then TObject(AValue).Free; end; constructor TObjectQueue.Create(AOwnsObjects: Boolean); begin inherited Create; FObjectsOwner := AOwnsObjects; end; constructor TObjectQueue.Create(ACollection: TEnumerable; AOwnsObjects: Boolean); begin inherited Create(ACollection); FObjectsOwner := AOwnsObjects; end; {$IFDEF ENABLE_METHODS_WITH_TEnumerableWithPointers} constructor TObjectQueue.Create(ACollection: TEnumerableWithPointers; AOwnsObjects: Boolean); begin inherited Create(ACollection); FObjectsOwner := AOwnsObjects; end; {$ENDIF} procedure TObjectQueue.Dequeue; begin inherited Dequeue; end; { TObjectStack } procedure TObjectStack.Notify(const AValue: T; ACollectionNotification: TCollectionNotification); begin inherited Notify(AValue, ACollectionNotification); if FObjectsOwner and (ACollectionNotification = cnRemoved) then TObject(AValue).Free; end; constructor TObjectStack.Create(AOwnsObjects: Boolean); begin inherited Create; FObjectsOwner := AOwnsObjects; end; constructor TObjectStack.Create(ACollection: TEnumerable; AOwnsObjects: Boolean); begin inherited Create(ACollection); FObjectsOwner := AOwnsObjects; end; {$IFDEF ENABLE_METHODS_WITH_TEnumerableWithPointers} constructor TObjectStack.Create(ACollection: TEnumerableWithPointers; AOwnsObjects: Boolean); begin inherited Create(ACollection); FObjectsOwner := AOwnsObjects; end; {$ENDIF} function TObjectStack.Pop: T; begin Result := inherited Pop; end; {$I inc\generics.dictionaries.inc} { TCustomSet.TCustomSetEnumerator } function TCustomSet.TCustomSetEnumerator.DoMoveNext: boolean; begin Result := FEnumerator.DoMoveNext; end; function TCustomSet.TCustomSetEnumerator.DoGetCurrent: T; begin Result := FEnumerator.DoGetCurrent; end; destructor TCustomSet.TCustomSetEnumerator.Destroy; begin FEnumerator.Free; end; { TCustomSet } function TCustomSet.DoGetEnumerator: {$IFDEF FPC_DOTTEDUNITS}System.{$ENDIF}Generics.Collections.TEnumerator; begin Result := GetEnumerator; end; constructor TCustomSet.Create(ACollection: TEnumerable); var i: T; begin Create; for i in ACollection do Add(i); end; {$IFDEF ENABLE_METHODS_WITH_TEnumerableWithPointers} constructor TCustomSet.Create(ACollection: TEnumerableWithPointers); var i: PT; begin Create; for i in ACollection.Ptr^ do Add(i^); end; {$ENDIF} function TCustomSet.AddRange(const AValues: array of T): Boolean; var i: T; begin Result := True; for i in AValues do Result := Add(i) and Result; end; function TCustomSet.AddRange(const AEnumerable: IEnumerable): Boolean; var i: T; begin Result := True; for i in AEnumerable do Result := Add(i) and Result; end; function TCustomSet.AddRange(AEnumerable: TEnumerable): 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.AddRange(AEnumerable: TEnumerableWithPointers): Boolean; var i: PT; begin Result := True; for i in AEnumerable.Ptr^ do Result := Add(i^) and Result; end; {$ENDIF} procedure TCustomSet.UnionWith(AHashSet: TCustomSet); var i: PT; begin for i in AHashSet.Ptr^ do Add(i^); end; procedure TCustomSet.IntersectWith(AHashSet: TCustomSet); var LList: TList; i: PT; begin LList := TList.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.ExceptWith(AHashSet: TCustomSet); var i: PT; begin for i in AHashSet.Ptr^ do Remove(i^); end; procedure TCustomSet.SymmetricExceptWith(AHashSet: TCustomSet); var LList: TList; i: PT; begin LList := TList.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.THashSetEnumerator } function THashSet.THashSetEnumerator.GetCurrent: T; begin Result := TDictionaryEnumerator(FEnumerator).GetCurrent; end; constructor THashSet.THashSetEnumerator.Create(ASet: TCustomSet); begin TDictionaryEnumerator(FEnumerator) := THashSet(ASet).FInternalDictionary.Keys.DoGetEnumerator; end; { THashSet.TPointersEnumerator } function THashSet.TPointersEnumerator.DoMoveNext: boolean; begin Result := FEnumerator.MoveNext; end; function THashSet.TPointersEnumerator.DoGetCurrent: PT; begin Result := FEnumerator.Current; end; constructor THashSet.TPointersEnumerator.Create(AHashSet: THashSet); begin FEnumerator := AHashSet.FInternalDictionary.Keys.Ptr^.GetEnumerator; end; { THashSet } procedure THashSet.InternalDictionaryNotify(ASender: TObject; const AItem: T; AAction: TCollectionNotification); begin FOnNotify(Self, AItem, AAction); end; function THashSet.GetPtrEnumerator: TEnumerator; begin Result := TPointersEnumerator.Create(Self); end; function THashSet.GetCount: SizeInt; begin Result := FInternalDictionary.Count; end; function THashSet.GetCapacity: SizeInt; begin Result := FInternalDictionary.Capacity; end; procedure THashSet.SetCapacity(AValue: SizeInt); begin FInternalDictionary.Capacity := AValue; end; function THashSet.GetOnNotify: TCollectionNotifyEvent; begin Result := FInternalDictionary.OnKeyNotify; end; procedure THashSet.SetOnNotify(AValue: TCollectionNotifyEvent); begin FOnNotify := AValue; if Assigned(AValue) then FInternalDictionary.OnKeyNotify := InternalDictionaryNotify else FInternalDictionary.OnKeyNotify := nil; end; function THashSet.GetEnumerator: TCustomSetEnumerator; begin Result := THashSetEnumerator.Create(Self); end; constructor THashSet.Create; begin FInternalDictionary := TOpenAddressingLP.Create; end; constructor THashSet.Create(const AComparer: IEqualityComparer); begin FInternalDictionary := TOpenAddressingLP.Create(AComparer); end; destructor THashSet.Destroy; begin FInternalDictionary.Free; end; function THashSet.Add(const AValue: T): Boolean; begin Result := not FInternalDictionary.ContainsKey(AValue); if Result then FInternalDictionary.Add(AValue, EmptyRecord); end; function THashSet.Remove(const AValue: T): Boolean; var LIndex: SizeInt; begin LIndex := FInternalDictionary.FindBucketIndex(AValue); Result := LIndex >= 0; if Result then FInternalDictionary.DoRemove(LIndex, cnRemoved); end; function THashSet.Extract(const 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.Clear; begin FInternalDictionary.Clear; end; function THashSet.Contains(const AValue: T): Boolean; begin Result := FInternalDictionary.ContainsKey(AValue); end; procedure THashSet.TrimExcess; begin FInternalDictionary.TrimExcess; end; { TAVLTreeNode } function TAVLTreeNode.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.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.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.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.GetCount: SizeInt; begin Result:=1; if Assigned(Left) then Inc(Result,Left.GetCount); if Assigned(Right) then Inc(Result,Right.GetCount); end; { TCustomTreeEnumerator } function TCustomTreeEnumerator.DoGetCurrent: T; begin Result := GetCurrent; end; constructor TCustomTreeEnumerator.Create(ATree: TObject); begin TObject(FTree) := ATree; end; { TTreeEnumerable } function TTreeEnumerable.GetCount: SizeInt; begin Result := FTree.Count; end; function TTreeEnumerable.GetPtrEnumerator: TEnumerator; begin Result := TTreePointersEnumerator.Create(FTree); end; constructor TTreeEnumerable.Create( ATree: TTree); begin FTree := ATree; end; function TTreeEnumerable. DoGetEnumerator: TTreeEnumerator; begin Result := TTreeEnumerator.Create(FTree); end; function TTreeEnumerable.ToArray: TArray; begin Result := ToArrayImpl(FTree.Count); end; { TAVLTreeEnumerator } function TAVLTreeEnumerator.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.Create(ATree: TObject; ALowToHigh: boolean); begin inherited Create(ATree); FLowToHigh:=aLowToHigh; end; { TCustomAVLTreeMap.TPairEnumerator } function TCustomAVLTreeMap.TPairEnumerator.GetCurrent: TTreePair; begin Result := TTreePair((@FCurrent.Data)^); end; { TCustomAVLTreeMap.TNodeEnumerator } function TCustomAVLTreeMap.TNodeEnumerator.GetCurrent: PNode; begin Result := FCurrent; end; { TCustomAVLTreeMap.TKeyEnumerator } function TCustomAVLTreeMap.TKeyEnumerator.GetCurrent: TKey; begin Result := FCurrent.Key; end; { TCustomAVLTreeMap.TPKeyEnumerator } function TCustomAVLTreeMap.TPKeyEnumerator.GetCurrent: PKey; begin Result := @FCurrent.Data.Key; end; { TCustomAVLTreeMap.TValueEnumerator } function TCustomAVLTreeMap.TValueEnumerator.GetCurrent: TValue; begin Result := FCurrent.Value; end; { TCustomAVLTreeMap.TValueEnumerator } function TCustomAVLTreeMap.TPValueEnumerator.GetCurrent: PValue; begin Result := @FCurrent.Data.Value; end; { TCustomAVLTreeMap } procedure TCustomAVLTreeMap.NodeAdded(ANode: PNode); begin end; procedure TCustomAVLTreeMap.DeletingNode(ANode: PNode; AOrigin: boolean); begin end; function TCustomAVLTreeMap.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.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.Compare(const ALeft, ARight: TKey): Integer; inline; begin Result := FComparer.Compare(ALeft, ARight); end; function TCustomAVLTreeMap.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.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.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.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.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.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.KeyNotify(const AKey: TKey; ACollectionNotification: TCollectionNotification); begin if Assigned(FOnKeyNotify) then FOnKeyNotify(Self, AKey, ACollectionNotification); end; procedure TCustomAVLTreeMap.ValueNotify(const AValue: TValue; ACollectionNotification: TCollectionNotification); begin if Assigned(FOnValueNotify) then FOnValueNotify(Self, AValue, ACollectionNotification); end; procedure TCustomAVLTreeMap.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.SetValue(var AValue: TValue; const ANewValue: TValue); var LOldValue: TValue; begin LOldValue := AValue; AValue := ANewValue; ValueNotify(LOldValue, cnRemoved); ValueNotify(ANewValue, cnAdded); end; procedure TCustomAVLTreeMap.WriteStr(AStream: TStream; const AText: string); begin if AText='' then exit; AStream.Write(AText[1],Length(AText)); end; function TCustomAVLTreeMap.GetNodeCollection: TNodeCollection; begin if not Assigned(FNodes) then FNodes := TNodeCollection.Create(TTree(Self)); Result := FNodes; end; procedure TCustomAVLTreeMap.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.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.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.GetKeys: TKeyCollection; begin if not Assigned(FKeys) then FKeys := TKeyCollection.Create(TTree(Self)); Result := TKeyCollection(FKeys); end; function TCustomAVLTreeMap.GetValues: TValueCollection; begin if not Assigned(FValues) then FValues := TValueCollection.Create(TTree(Self)); Result := TValueCollection(FValues); end; function TCustomAVLTreeMap.GetItem(const AKey: TKey): TValue; var LNode: PNode; // Need to differentiate with TValue template type... D : {$IFDEF FPC_DOTTEDUNITS}System.{$ENDIF}Rtti.TValue; K : TKey; begin LNode := Find(AKey); if not Assigned(LNode) then begin K:=aKey; {$IFDEF FPC_DOTTEDUNITS}System.{$ENDIF}Rtti.TValue.Make(@K,TypeInfo(TKey),D); raise EAVLTree.CreateFmt(SDictionaryKeyNNNDoesNotExist,[D.ToString]); end; result := LNode.Value; end; procedure TCustomAVLTreeMap.SetItem(const AKey: TKey; const AValue: TValue); begin Find(AKey).Value := AValue; end; constructor TCustomAVLTreeMap.Create; begin FComparer := TComparer.Default; end; constructor TCustomAVLTreeMap.Create(const AComparer: IComparer); begin FComparer := AComparer; end; function TCustomAVLTreeMap.NewNode: PNode; begin Result := AllocMem(SizeOf(TNode)); Initialize(Result^); end; function TCustomAVLTreeMap.NewNodeArray(ACount: SizeInt): PNode; begin Result := AllocMem(ACount * SizeOf(TNode)); Initialize(Result^, ACount); end; procedure TCustomAVLTreeMap.NewNodeArray(out AArray: TArray; ACount: SizeInt); var i: Integer; begin SetLength(AArray, ACount); for i := 0 to ACount-1 do AArray[i] := NewNode; end; procedure TCustomAVLTreeMap.DisposeNode(ANode: PNode); begin Dispose(ANode); end; procedure TCustomAVLTreeMap.DisposeNodeArray(ANode: PNode; ACount: SizeInt); begin Finalize(ANode^, ACount); FreeMem(ANode); end; procedure TCustomAVLTreeMap.DisposeNodeArray(var AArray: TArray); var i: Integer; begin for i := 0 to High(AArray) do Dispose(AArray[i]); AArray := nil; end; destructor TCustomAVLTreeMap.Destroy; begin FKeys.Free; FValues.Free; FNodes.Free; Clear; end; function TCustomAVLTreeMap.AddNode(ANode: PNode): boolean; begin Result := ANode=InternalAdd(ANode, false); end; function TCustomAVLTreeMap.Add(const APair: TTreePair): PNode; begin Result := NewNode; Result.Data.Key := APair.Key; Result.Data.Value := APair.Value; Result := InternalAdd(Result, true); end; function TCustomAVLTreeMap.Add(const AKey: TKey; const AValue: TValue): PNode; begin Result := NewNode; Result.Data.Key := AKey; Result.Data.Value := AValue; Result := InternalAdd(Result, true); end; function TCustomAVLTreeMap.Remove(const 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.ExtractPair(const 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.ExtractPair(const ANode: PNode; ADispose: boolean = true): TTreePair; begin Result.Key := ANode.Key; Result.Value := DoRemove(ANode, cnExtracted, ADispose); end; function TCustomAVLTreeMap.Extract(const 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.ExtractNode(ANode: PNode; ADispose: boolean): PNode; begin DoRemove(ANode, cnExtracted, ADispose); if ADispose then Result := nil else Result := ANode; end; procedure TCustomAVLTreeMap.Delete(ANode: PNode; ADispose: boolean); begin DoRemove(ANode, cnRemoved, ADispose); end; procedure TCustomAVLTreeMap.Clear(ADisposeNodes: Boolean); begin if (FRoot<>nil) and ADisposeNodes then DisposeAllNodes(FRoot); fRoot:=nil; FCount:=0; end; function TCustomAVLTreeMap.GetEnumerator: TPairEnumerator; begin Result := TPairEnumerator.Create(Self, true); end; function TCustomAVLTreeMap.FindLowest: PNode; begin Result:=FRoot; if Result<>nil then while Result.Left<>nil do Result:=Result.Left; end; function TCustomAVLTreeMap.FindHighest: PNode; begin Result:=FRoot; if Result<>nil then while Result.Right<>nil do Result:=Result.Right; end; function TCustomAVLTreeMap.Find(const 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.ContainsKey(const AKey: TKey; out ANode: PNode): boolean; begin ANode := Find(AKey); Result := Assigned(ANode); end; function TCustomAVLTreeMap.ContainsKey(const AKey: TKey): boolean; overload; inline; begin Result := Assigned(Find(AKey)); end; procedure TCustomAVLTreeMap.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.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.WriteReportToStream(AStream: TStream); begin WriteStr(AStream, '-Start-of-AVL-Tree-------------------'+LineEnding); WriteTreeNode(AStream, fRoot); WriteStr(AStream, '-End-Of-AVL-Tree---------------------'+LineEnding); end; function TCustomAVLTreeMap.NodeToReportStr(ANode: PNode): string; begin Result:=Format(' Self=%p Parent=%p Balance=%d', [ANode, ANode.Parent, ANode.Balance]); end; function TCustomAVLTreeMap.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 } procedure TIndexedAVLTreeMap.RotateRightRight(ANode: PNode); var LOldRight: PNode; begin LOldRight:=ANode.Right; inherited; Inc(LOldRight.Data.Info, (1 + ANode.Data.Info)); end; procedure TIndexedAVLTreeMap.RotateLeftLeft(ANode: PNode); var LOldLeft: PNode; begin LOldLeft:=ANode.Left; inherited; Dec(ANode.Data.Info, (1 + LOldLeft.Data.Info)); end; procedure TIndexedAVLTreeMap.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.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.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.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.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.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.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.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 } function TAVLTree.Add(const AValue: T): PNode; begin Result := inherited Add(AValue, EmptyRecord); end; function TAVLTree.AddNode(ANode: PNode): boolean; begin Result := inherited AddNode(ANode); end; { TIndexedAVLTree } function TIndexedAVLTree.Add(const AValue: T): PNode; begin Result := inherited Add(AValue, EmptyRecord); end; function TIndexedAVLTree.AddNode(ANode: PNode): boolean; begin Result := inherited AddNode(ANode); end; { TSortedSet.TSortedSetEnumerator } function TSortedSet.TSortedSetEnumerator.GetCurrent: T; begin Result := TTreeEnumerator(FEnumerator).GetCurrent; end; constructor TSortedSet.TSortedSetEnumerator.Create(ASet: TCustomSet); begin TTreeEnumerator(FEnumerator) := TSortedSet(ASet).FInternalTree.Keys.DoGetEnumerator; end; { TSortedSet.TPointersEnumerator } function TSortedSet.TPointersEnumerator.DoMoveNext: boolean; begin Result := FEnumerator.MoveNext; end; function TSortedSet.TPointersEnumerator.DoGetCurrent: PT; begin Result := FEnumerator.Current; end; constructor TSortedSet.TPointersEnumerator.Create(ASortedSet: TSortedSet); begin FEnumerator := ASortedSet.FInternalTree.Keys.Ptr^.GetEnumerator; end; { TSortedSet } procedure TSortedSet.InternalAVLTreeNotify(ASender: TObject; const AItem: T; AAction: TCollectionNotification); begin FOnNotify(Self, AItem, AAction); end; function TSortedSet.GetPtrEnumerator: TEnumerator; begin Result := TPointersEnumerator.Create(Self); end; function TSortedSet.GetCount: SizeInt; begin Result := FInternalTree.Count; end; function TSortedSet.GetCapacity: SizeInt; begin Result := FInternalTree.Count; end; procedure TSortedSet.SetCapacity(AValue: SizeInt); begin end; function TSortedSet.GetOnNotify: TCollectionNotifyEvent; begin Result := FInternalTree.OnKeyNotify; end; procedure TSortedSet.SetOnNotify(AValue: TCollectionNotifyEvent); begin FOnNotify := AValue; if Assigned(AValue) then FInternalTree.OnKeyNotify := InternalAVLTreeNotify else FInternalTree.OnKeyNotify := nil; end; function TSortedSet.GetEnumerator: TCustomSetEnumerator; begin Result := TSortedSetEnumerator.Create(Self); end; constructor TSortedSet.Create; begin FInternalTree := TAVLTree.Create; end; constructor TSortedSet.Create(const AComparer: IComparer); begin FInternalTree := TAVLTree.Create(AComparer); end; destructor TSortedSet.Destroy; begin FInternalTree.Free; end; function TSortedSet.Add(const AValue: T): Boolean; var LNodePtr, LParent: TAVLTree.PNode; LNode: TAVLTree.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.Remove(const AValue: T): Boolean; var LNode: TAVLTree.PNode; begin LNode := FInternalTree.Find(AValue); Result := Assigned(LNode); if Result then FInternalTree.Delete(LNode); end; function TSortedSet.Extract(const AValue: T): T; var LNode: TAVLTree.PNode; begin LNode := FInternalTree.Find(AValue); if not Assigned(LNode) then Exit(Default(T)); Result := FInternalTree.ExtractPair(LNode).Key; end; procedure TSortedSet.Clear; begin FInternalTree.Clear; end; function TSortedSet.Contains(const AValue: T): Boolean; begin Result := FInternalTree.ContainsKey(AValue); end; procedure TSortedSet.TrimExcess; begin end; { TSortedHashSet.TSortedHashSetEqualityComparer } function TSortedHashSet.TSortedHashSetEqualityComparer.Equals(const ALeft, ARight: PT): Boolean; begin if Assigned(FComparer) then Result := FComparer.Compare(ALeft^, ARight^) = 0 else Result := FEqualityComparer.Equals(ALeft^, ARight^); end; function TSortedHashSet.TSortedHashSetEqualityComparer.GetHashCode(const AValue: PT): UInt32; begin Result := FEqualityComparer.GetHashCode(AValue^); end; constructor TSortedHashSet.TSortedHashSetEqualityComparer.Create(const AComparer: IComparer); begin FComparer := AComparer; FEqualityComparer := TEqualityComparer.Default; end; constructor TSortedHashSet.TSortedHashSetEqualityComparer.Create(const AEqualityComparer: IEqualityComparer); begin FEqualityComparer := AEqualityComparer; end; constructor TSortedHashSet.TSortedHashSetEqualityComparer.Create(const AComparer: IComparer; const AEqualityComparer: IEqualityComparer); begin FComparer := AComparer; FEqualityComparer := AEqualityComparer; end; { TSortedHashSet.TSortedHashSetEnumerator } function TSortedHashSet.TSortedHashSetEnumerator.GetCurrent: T; begin Result := TTreeEnumerator(FEnumerator).Current; end; constructor TSortedHashSet.TSortedHashSetEnumerator.Create(ASet: TCustomSet); begin FEnumerator := TSortedHashSet(ASet).FInternalTree.Keys.GetEnumerator; end; { TSortedHashSet.TPointersEnumerator } function TSortedHashSet.TPointersEnumerator.DoMoveNext: boolean; begin Result := FEnumerator.MoveNext; end; function TSortedHashSet.TPointersEnumerator.DoGetCurrent: PT; begin Result := FEnumerator.Current; end; constructor TSortedHashSet.TPointersEnumerator.Create(ASortedHashSet: TSortedHashSet); begin FEnumerator := ASortedHashSet.FInternalTree.Keys.Ptr^.GetEnumerator; end; { TSortedHashSet } procedure TSortedHashSet.InternalDictionaryNotify(ASender: TObject; const AItem: PT; AAction: TCollectionNotification); begin FOnNotify(Self, AItem^, AAction); end; function TSortedHashSet.GetPtrEnumerator: TEnumerator; begin Result := TPointersEnumerator.Create(Self); end; function TSortedHashSet.DoGetEnumerator: TEnumerator; begin Result := GetEnumerator; end; function TSortedHashSet.GetCount: SizeInt; begin Result := FInternalDictionary.Count; end; function TSortedHashSet.GetCapacity: SizeInt; begin Result := FInternalDictionary.Capacity; end; procedure TSortedHashSet.SetCapacity(AValue: SizeInt); begin FInternalDictionary.Capacity := AValue; end; function TSortedHashSet.GetOnNotify: TCollectionNotifyEvent; begin Result := FInternalTree.OnKeyNotify; end; procedure TSortedHashSet.SetOnNotify(AValue: TCollectionNotifyEvent); begin FOnNotify := AValue; if Assigned(AValue) then FInternalDictionary.OnKeyNotify := InternalDictionaryNotify else FInternalDictionary.OnKeyNotify := nil; end; function TSortedHashSet.GetEnumerator: TCustomSetEnumerator; begin Result := TSortedHashSetEnumerator.Create(Self); end; function TSortedHashSet.Add(const AValue: T): Boolean; var LNode: TAVLTree.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.Remove(const 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.Extract(const 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.Clear; begin FInternalDictionary.Clear; FInternalTree.Clear; end; function TSortedHashSet.Contains(const AValue: T): Boolean; begin Result := FInternalDictionary.ContainsKey(@AValue); end; constructor TSortedHashSet.Create; begin FInternalTree := TAVLTree.Create; FInternalDictionary := TOpenAddressingLP.Create(TSortedHashSetEqualityComparer.Create(TEqualityComparer.Default)); end; constructor TSortedHashSet.Create(const AComparer: IEqualityComparer); begin Create(TComparer.Default, AComparer); end; constructor TSortedHashSet.Create(const AComparer: IComparer); begin FInternalTree := TAVLTree.Create(AComparer); FInternalDictionary := TOpenAddressingLP.Create(TSortedHashSetEqualityComparer.Create(AComparer)); end; constructor TSortedHashSet.Create(const AComparer: IComparer; const AEqualityComparer: IEqualityComparer); begin FInternalTree := TAVLTree.Create(AComparer); FInternalDictionary := TOpenAddressingLP.Create(TSortedHashSetEqualityComparer.Create(AComparer,AEqualityComparer)); end; destructor TSortedHashSet.Destroy; begin FInternalDictionary.Free; FInternalTree.Free; inherited; end; procedure TSortedHashSet.TrimExcess; begin FInternalDictionary.TrimExcess; end; end.