Jelajahi Sumber

first travis with generics.collections.

Ugochukwu Mmaduekwe 7 tahun lalu
induk
melakukan
17a52a091f

+ 4 - 4
.travis.yml

@@ -54,16 +54,16 @@ install:
 script:
 script:
   - curl -L -k https://github.com/Xor-el/HashLib4Pascal/archive/master.zip -o HashLib4Pascal.zip
   - curl -L -k https://github.com/Xor-el/HashLib4Pascal/archive/master.zip -o HashLib4Pascal.zip
   - curl -L -k https://github.com/Xor-el/SimpleBaseLib4Pascal/archive/master.zip -o SimpleBaseLib4Pascal.zip
   - curl -L -k https://github.com/Xor-el/SimpleBaseLib4Pascal/archive/master.zip -o SimpleBaseLib4Pascal.zip
-  - curl -L -k https://github.com/Xor-el/generics.collections/archive/master.zip -o generics.collections.zip
+ # - curl -L -k https://github.com/Xor-el/generics.collections/archive/master.zip -o generics.collections.zip
   - unzip HashLib4Pascal.zip
   - unzip HashLib4Pascal.zip
   - unzip SimpleBaseLib4Pascal.zip
   - unzip SimpleBaseLib4Pascal.zip
-  - unzip generics.collections.zip
+ # - unzip generics.collections.zip
   - lazbuild --add-package-link ./HashLib4Pascal-master/HashLib/src/Packages/FPC/HashLib4PascalPackage.lpk  # Add HashLib4Pascal Package
   - lazbuild --add-package-link ./HashLib4Pascal-master/HashLib/src/Packages/FPC/HashLib4PascalPackage.lpk  # Add HashLib4Pascal Package
   - lazbuild --add-package-link ./SimpleBaseLib4Pascal-master/SimpleBaseLib/src/Packages/FPC/SimpleBaseLib4PascalPackage.lpk  # Add SimpleBaseLib4Pascal Package
   - lazbuild --add-package-link ./SimpleBaseLib4Pascal-master/SimpleBaseLib/src/Packages/FPC/SimpleBaseLib4PascalPackage.lpk  # Add SimpleBaseLib4Pascal Package
-  - lazbuild --add-package-link ./generics.collections-master/packages/generics_collections.lpk  # Add generics.collections Package
+ # - lazbuild --add-package-link ./generics.collections-master/packages/generics_collections.lpk  # Add generics.collections Package
   - lazbuild $LAZ_OPT ./HashLib4Pascal-master/HashLib/src/Packages/FPC/HashLib4PascalPackage.lpk  # Build HashLib4Pascal Package
   - lazbuild $LAZ_OPT ./HashLib4Pascal-master/HashLib/src/Packages/FPC/HashLib4PascalPackage.lpk  # Build HashLib4Pascal Package
   - lazbuild $LAZ_OPT ./SimpleBaseLib4Pascal-master/SimpleBaseLib/src/Packages/FPC/SimpleBaseLib4PascalPackage.lpk  # Build SimpleBaseLib4Pascal Package
   - lazbuild $LAZ_OPT ./SimpleBaseLib4Pascal-master/SimpleBaseLib/src/Packages/FPC/SimpleBaseLib4PascalPackage.lpk  # Build SimpleBaseLib4Pascal Package
-  - lazbuild $LAZ_OPT ./generics.collections-master/packages/generics_collections.lpk  # Build generics.collections Package
+ # - lazbuild $LAZ_OPT ./generics.collections-master/packages/generics_collections.lpk  # Build generics.collections Package
   - lazbuild $LAZ_OPT ./CryptoLib/src/Packages/FPC/CryptoLib4PascalPackage.lpk  # Build CryptoLib4Pascal Package
   - lazbuild $LAZ_OPT ./CryptoLib/src/Packages/FPC/CryptoLib4PascalPackage.lpk  # Build CryptoLib4Pascal Package
   - lazbuild $LAZ_OPT ./CryptoLib.Tests/FreePascal.Tests/CryptoLibConsole.Tests.lpi  # Build CryptoLib4Pascal Test Project
   - lazbuild $LAZ_OPT ./CryptoLib.Tests/FreePascal.Tests/CryptoLibConsole.Tests.lpi  # Build CryptoLib4Pascal Test Project
   - travis_wait 120 $LAZ_ENV ./CryptoLib.Tests/FreePascal.Tests/bin/CryptoLib --all --format=plain  # Run CryptoLib4Pascal TestSuite with timeout of 120 mins
   - travis_wait 120 $LAZ_ENV ./CryptoLib.Tests/FreePascal.Tests/bin/CryptoLib --all --format=plain  # Run CryptoLib4Pascal TestSuite with timeout of 120 mins

+ 4158 - 0
CryptoLib/src/3rdParty/generics.collections/src/generics.collections.pas

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

+ 3304 - 0
CryptoLib/src/3rdParty/generics.collections/src/generics.defaults.pas

@@ -0,0 +1,3304 @@
+{
+    This file is part of the Free Pascal run time library.
+    Copyright (c) 2014 by Maciej Izak (hnb)
+    member of the Free Sparta development team (http://freesparta.com)
+
+    Copyright(c) 2004-2014 DaThoX
+
+    It contains the Free Pascal generics library
+
+    See the file COPYING.FPC, included in this distribution,
+    for details about the copyright.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+
+    Acknowledgment
+
+    Thanks to Sphere 10 Software (http://sphere10.com) for sponsoring
+    many new types and major refactoring of entire library
+
+    Thanks to mORMot (http://synopse.info) project for the best implementations
+    of hashing functions like crc32c and xxHash32 :)
+
+ **********************************************************************}
+
+unit Generics.Defaults;
+
+{$MODE DELPHI}{$H+}
+{$POINTERMATH ON}
+{$MACRO ON}
+{$COPERATORS ON}
+{$HINTS OFF}
+{$WARNINGS OFF}
+{$NOTES OFF}
+{$OVERFLOWCHECKS OFF}
+{$RANGECHECKS OFF}
+
+interface
+
+uses
+  Classes, SysUtils, Generics.Hashes, TypInfo, Variants, Math, Generics.Strings, Generics.Helpers;
+
+type
+  IComparer<T> = interface
+    function Compare(constref Left, Right: T): Integer; overload;
+  end;
+
+  TOnComparison<T> = function(constref Left, Right: T): Integer of object;
+  TComparisonFunc<T> = function(constref Left, Right: T): Integer;
+
+  TComparer<T> = class(TInterfacedObject, IComparer<T>)
+  public
+    class function Default: IComparer<T>; static;
+    function Compare(constref ALeft, ARight: T): Integer; virtual; abstract; overload;
+
+    class function Construct(const AComparison: TOnComparison<T>): IComparer<T>; overload;
+    class function Construct(const AComparison: TComparisonFunc<T>): IComparer<T>; overload;
+  end;
+
+  TDelegatedComparerEvents<T> = class(TComparer<T>)
+  private
+    FComparison: TOnComparison<T>;
+  public
+    function Compare(constref ALeft, ARight: T): Integer; override;
+    constructor Create(AComparison: TOnComparison<T>);
+  end;
+
+  TDelegatedComparerFunc<T> = class(TComparer<T>)
+  private
+    FComparison: TComparisonFunc<T>;
+  public
+    function Compare(constref ALeft, ARight: T): Integer; override;
+    constructor Create(AComparison: TComparisonFunc<T>);
+  end;
+
+  IEqualityComparer<T> = interface
+    function Equals(constref ALeft, ARight: T): Boolean;
+    function GetHashCode(constref AValue: T): UInt32;
+  end;
+
+  IExtendedEqualityComparer<T> = interface(IEqualityComparer<T>)
+    procedure GetHashList(constref AValue: T; AHashList: PUInt32); // for double hashing and more
+  end;
+
+  ShortString1 = string[1];
+  ShortString2 = string[2];
+  ShortString3 = string[3];
+
+  { TAbstractInterface }
+
+  TInterface = class
+  public
+    function QueryInterface(constref {%H-}IID: TGUID;{%H-} out Obj): HResult; {$IFNDEF WINDOWS}cdecl{$ELSE}stdcall{$ENDIF}; virtual;
+    function _AddRef: LongInt; {$IFNDEF WINDOWS}cdecl{$ELSE}stdcall{$ENDIF}; virtual; abstract;
+    function _Release: LongInt; {$IFNDEF WINDOWS}cdecl{$ELSE}stdcall{$ENDIF};  virtual; abstract;
+  end;
+
+  { TRawInterface }
+
+  TRawInterface = class(TInterface)
+  public
+    function _AddRef: LongInt; override;
+    function _Release: LongInt; override;
+  end;
+
+  { TComTypeSizeInterface }
+
+  // INTERNAL USE ONLY!
+  TComTypeSizeInterface = class(TInterface)
+  public
+    // warning ! self as PSpoofInterfacedTypeSizeObject
+    function _AddRef: LongInt; override;
+    // warning ! self as PSpoofInterfacedTypeSizeObject
+    function _Release: LongInt; override;
+  end;
+
+  { TSingletonImplementation }
+
+  TSingletonImplementation = class(TRawInterface, IInterface)
+  public
+    function QueryInterface(constref IID: TGUID; out Obj): HResult; override;
+  end;
+
+  TCompare = class
+  protected
+    // warning ! self as PSpoofInterfacedTypeSizeObject
+    class function _Binary(constref ALeft, ARight): Integer;
+    // warning ! self as PSpoofInterfacedTypeSizeObject
+    class function _DynArray(constref ALeft, ARight: Pointer): Integer;
+  public
+    class function Integer(constref ALeft, ARight: Integer): Integer;
+    class function Int8(constref ALeft, ARight: Int8): Integer;
+    class function Int16(constref ALeft, ARight: Int16): Integer;
+    class function Int32(constref ALeft, ARight: Int32): Integer;
+    class function Int64(constref ALeft, ARight: Int64): Integer;
+    class function UInt8(constref ALeft, ARight: UInt8): Integer;
+    class function UInt16(constref ALeft, ARight: UInt16): Integer;
+    class function UInt32(constref ALeft, ARight: UInt32): Integer;
+    class function UInt64(constref ALeft, ARight: UInt64): Integer;
+    class function Single(constref ALeft, ARight: Single): Integer;
+    class function Double(constref ALeft, ARight: Double): Integer;
+    class function Extended(constref ALeft, ARight: Extended): Integer;
+    class function Currency(constref ALeft, ARight: Currency): Integer;
+    class function Comp(constref ALeft, ARight: Comp): Integer;
+    class function Binary(constref ALeft, ARight; const ASize: SizeInt): Integer;
+    class function DynArray(constref ALeft, ARight: Pointer; const AElementSize: SizeInt): Integer;
+    class function ShortString1(constref ALeft, ARight: ShortString1): Integer;
+    class function ShortString2(constref ALeft, ARight: ShortString2): Integer;
+    class function ShortString3(constref ALeft, ARight: ShortString3): Integer;
+    class function &String(constref ALeft, ARight: string): Integer;
+    class function ShortString(constref ALeft, ARight: ShortString): Integer;
+    class function AnsiString(constref ALeft, ARight: AnsiString): Integer;
+    class function WideString(constref ALeft, ARight: WideString): Integer;
+    class function UnicodeString(constref ALeft, ARight: UnicodeString): Integer;
+    class function Method(constref ALeft, ARight: TMethod): Integer;
+    class function Variant(constref ALeft, ARight: PVariant): Integer;
+    class function Pointer(constref ALeft, ARight: PtrUInt): Integer;
+  end;
+
+  { TEquals }
+
+  TEquals = class
+  protected
+    // warning ! self as PSpoofInterfacedTypeSizeObject
+    class function _Binary(constref ALeft, ARight): Boolean;
+    // warning ! self as PSpoofInterfacedTypeSizeObject
+    class function _DynArray(constref ALeft, ARight: Pointer): Boolean;
+  public
+    class function Integer(constref ALeft, ARight: Integer): Boolean;
+    class function Int8(constref ALeft, ARight: Int8): Boolean;
+    class function Int16(constref ALeft, ARight: Int16): Boolean;
+    class function Int32(constref ALeft, ARight: Int32): Boolean;
+    class function Int64(constref ALeft, ARight: Int64): Boolean;
+    class function UInt8(constref ALeft, ARight: UInt8): Boolean;
+    class function UInt16(constref ALeft, ARight: UInt16): Boolean;
+    class function UInt32(constref ALeft, ARight: UInt32): Boolean;
+    class function UInt64(constref ALeft, ARight: UInt64): Boolean;
+    class function Single(constref ALeft, ARight: Single): Boolean;
+    class function Double(constref ALeft, ARight: Double): Boolean;
+    class function Extended(constref ALeft, ARight: Extended): Boolean;
+    class function Currency(constref ALeft, ARight: Currency): Boolean;
+    class function Comp(constref ALeft, ARight: Comp): Boolean;
+    class function Binary(constref ALeft, ARight; const ASize: SizeInt): Boolean;
+    class function DynArray(constref ALeft, ARight: Pointer; const AElementSize: SizeInt): Boolean;
+    class function &Class(constref ALeft, ARight: TObject): Boolean;
+    class function ShortString1(constref ALeft, ARight: ShortString1): Boolean;
+    class function ShortString2(constref ALeft, ARight: ShortString2): Boolean;
+    class function ShortString3(constref ALeft, ARight: ShortString3): Boolean;
+    class function &String(constref ALeft, ARight: String): Boolean;
+    class function ShortString(constref ALeft, ARight: ShortString): Boolean;
+    class function AnsiString(constref ALeft, ARight: AnsiString): Boolean;
+    class function WideString(constref ALeft, ARight: WideString): Boolean;
+    class function UnicodeString(constref ALeft, ARight: UnicodeString): Boolean;
+    class function Method(constref ALeft, ARight: TMethod): Boolean;
+    class function Variant(constref ALeft, ARight: PVariant): Boolean;
+    class function Pointer(constref ALeft, ARight: PtrUInt): Boolean;
+  end;
+
+  THashServiceClass = class of THashService;
+  TExtendedHashServiceClass = class of TExtendedHashService;
+  THashFactoryClass = class of THashFactory;
+
+  TExtendedHashFactoryClass = class of TExtendedHashFactory;
+
+  { TComparerService }
+
+{$DEFINE STD_RAW_INTERFACE_METHODS :=
+    QueryInterface: @TRawInterface.QueryInterface;
+    _AddRef       : @TRawInterface._AddRef;
+    _Release      : @TRawInterface._Release
+}
+
+{$DEFINE STD_COM_TYPESIZE_INTERFACE_METHODS :=
+    QueryInterface: @TComTypeSizeInterface.QueryInterface;
+    _AddRef       : @TComTypeSizeInterface._AddRef;
+    _Release      : @TComTypeSizeInterface._Release
+}
+
+  TGetHashListOptions = set of (ghloHashListAsInitData);
+
+  THashFactory = class
+  private type
+    PPEqualityComparerVMT = ^PEqualityComparerVMT;
+    PEqualityComparerVMT = ^TEqualityComparerVMT;
+    TEqualityComparerVMT = packed record
+      QueryInterface: CodePointer;
+      _AddRef: CodePointer;
+      _Release: CodePointer;
+      Equals: CodePointer;
+      GetHashCode: CodePointer;
+      __Reserved: Pointer; // initially or TExtendedEqualityComparerVMT compatibility
+                           // (important when ExtendedEqualityComparer is calling Binary method)
+      __ClassRef: THashFactoryClass; // hidden field in VMT. For class ref THashFactoryClass
+    end;
+
+  private
+(***********************************************************************************************************************
+      Hashes
+(**********************************************************************************************************************)
+
+    class function Int8         (constref AValue: Int8         ): UInt32; overload;
+    class function Int16        (constref AValue: Int16        ): UInt32; overload;
+    class function Int32        (constref AValue: Int32        ): UInt32; overload;
+    class function Int64        (constref AValue: Int64        ): UInt32; overload;
+    class function UInt8        (constref AValue: UInt8        ): UInt32; overload;
+    class function UInt16       (constref AValue: UInt16       ): UInt32; overload;
+    class function UInt32       (constref AValue: UInt32       ): UInt32; overload;
+    class function UInt64       (constref AValue: UInt64       ): UInt32; overload;
+    class function Single       (constref AValue: Single       ): UInt32; overload;
+    class function Double       (constref AValue: Double       ): UInt32; overload;
+    class function Extended     (constref AValue: Extended     ): UInt32; overload;
+    class function Currency     (constref AValue: Currency     ): UInt32; overload;
+    class function Comp         (constref AValue: Comp         ): UInt32; overload;
+    // warning ! self as PSpoofInterfacedTypeSizeObject
+    class function Binary       (constref AValue               ): UInt32; overload;
+    // warning ! self as PSpoofInterfacedTypeSizeObject
+    class function DynArray     (constref AValue: Pointer      ): UInt32; overload;
+    class function &Class       (constref AValue: TObject      ): UInt32; overload;
+    class function ShortString1 (constref AValue: ShortString1 ): UInt32; overload;
+    class function ShortString2 (constref AValue: ShortString2 ): UInt32; overload;
+    class function ShortString3 (constref AValue: ShortString3 ): UInt32; overload;
+    class function ShortString  (constref AValue: ShortString   ): UInt32; overload;
+    class function AnsiString   (constref AValue: AnsiString   ): UInt32; overload;
+    class function WideString   (constref AValue: WideString   ): UInt32; overload;
+    class function UnicodeString(constref AValue: UnicodeString): UInt32; overload;
+    class function Method       (constref AValue: TMethod      ): UInt32; overload;
+    class function Variant      (constref AValue: PVariant     ): UInt32; overload;
+    class function Pointer      (constref AValue: Pointer      ): UInt32; overload;
+  public
+    const MAX_HASHLIST_COUNT = 1;
+    const HASH_FUNCTIONS_COUNT = 1;
+    const HASHLIST_COUNT_PER_FUNCTION: array[1..HASH_FUNCTIONS_COUNT] of Integer = (1);
+    const HASH_FUNCTIONS_MASK_SIZE = 1;
+
+    class function GetHashService: THashServiceClass; virtual; abstract;
+    class function GetHashCode(AKey: Pointer; ASize: SizeInt; AInitVal: UInt32 = 0): UInt32; virtual; abstract; reintroduce;
+  end;
+
+  TExtendedHashFactory = class(THashFactory)
+  private type
+    PPExtendedEqualityComparerVMT = ^PExtendedEqualityComparerVMT;
+    PExtendedEqualityComparerVMT = ^TExtendedEqualityComparerVMT;
+    TExtendedEqualityComparerVMT = packed record
+      QueryInterface: CodePointer;
+      _AddRef: CodePointer;
+      _Release: CodePointer;
+      Equals: CodePointer;
+      GetHashCode: CodePointer;
+      GetHashList: CodePointer;
+      __ClassRef: TExtendedHashFactoryClass; // hidden field in VMT. For class ref THashFactoryClass
+    end;
+  private
+(***********************************************************************************************************************
+      Hashes 2
+(**********************************************************************************************************************)
+
+    class procedure Int8         (constref AValue: Int8         ; AHashList: PUInt32); overload;
+    class procedure Int16        (constref AValue: Int16        ; AHashList: PUInt32); overload;
+    class procedure Int32        (constref AValue: Int32        ; AHashList: PUInt32); overload;
+    class procedure Int64        (constref AValue: Int64        ; AHashList: PUInt32); overload;
+    class procedure UInt8        (constref AValue: UInt8        ; AHashList: PUInt32); overload;
+    class procedure UInt16       (constref AValue: UInt16       ; AHashList: PUInt32); overload;
+    class procedure UInt32       (constref AValue: UInt32       ; AHashList: PUInt32); overload;
+    class procedure UInt64       (constref AValue: UInt64       ; AHashList: PUInt32); overload;
+    class procedure Single       (constref AValue: Single       ; AHashList: PUInt32); overload;
+    class procedure Double       (constref AValue: Double       ; AHashList: PUInt32); overload;
+    class procedure Extended     (constref AValue: Extended     ; AHashList: PUInt32); overload;
+    class procedure Currency     (constref AValue: Currency     ; AHashList: PUInt32); overload;
+    class procedure Comp         (constref AValue: Comp         ; AHashList: PUInt32); overload;
+    // warning ! self as PSpoofInterfacedTypeSizeObject
+    class procedure Binary       (constref AValue               ; AHashList: PUInt32); overload;
+    // warning ! self as PSpoofInterfacedTypeSizeObject
+    class procedure DynArray     (constref AValue: Pointer      ; AHashList: PUInt32); overload;
+    class procedure &Class       (constref AValue: TObject      ; AHashList: PUInt32); overload;
+    class procedure ShortString1 (constref AValue: ShortString1 ; AHashList: PUInt32); overload;
+    class procedure ShortString2 (constref AValue: ShortString2 ; AHashList: PUInt32); overload;
+    class procedure ShortString3 (constref AValue: ShortString3 ; AHashList: PUInt32); overload;
+    class procedure ShortString  (constref AValue: ShortString   ; AHashList: PUInt32); overload;
+    class procedure AnsiString   (constref AValue: AnsiString   ; AHashList: PUInt32); overload;
+    class procedure WideString   (constref AValue: WideString   ; AHashList: PUInt32); overload;
+    class procedure UnicodeString(constref AValue: UnicodeString; AHashList: PUInt32); overload;
+    class procedure Method       (constref AValue: TMethod      ; AHashList: PUInt32); overload;
+    class procedure Variant      (constref AValue: PVariant     ; AHashList: PUInt32); overload;
+    class procedure Pointer      (constref AValue: Pointer      ; AHashList: PUInt32); overload;
+  public
+    class procedure GetHashList(AKey: Pointer; ASize: SizeInt; AHashList: PUInt32; AOptions: TGetHashListOptions = []); virtual; abstract;
+  end;
+
+  TComparerService = class abstract
+  private type
+    TSelectMethod = function(ATypeData: PTypeData; ASize: SizeInt): Pointer of object;
+  private
+    class function SelectIntegerEqualityComparer(ATypeData: PTypeData; ASize: SizeInt): Pointer; virtual; abstract;
+    class function SelectFloatEqualityComparer(ATypeData: PTypeData; ASize: SizeInt): Pointer; virtual; abstract;
+    class function SelectShortStringEqualityComparer(ATypeData: PTypeData; ASize: SizeInt): Pointer; virtual; abstract;
+    class function SelectBinaryEqualityComparer(ATypeData: PTypeData; ASize: SizeInt): Pointer; virtual; abstract;
+    class function SelectDynArrayEqualityComparer(ATypeData: PTypeData; ASize: SizeInt): Pointer; virtual; abstract;
+  private type
+    PSpoofInterfacedTypeSizeObject = ^TSpoofInterfacedTypeSizeObject;
+    TSpoofInterfacedTypeSizeObject = record
+      VMT: Pointer;
+      RefCount: LongInt;
+      Size: SizeInt;
+    end;
+
+    PInstance = ^TInstance;
+    TInstance = record
+      class function Create(ASelector: Boolean; AInstance: Pointer): TComparerService.TInstance; static;
+      class function CreateSelector(ASelectorInstance: CodePointer): TComparerService.TInstance; static;
+
+      case Selector: Boolean of
+        false: (Instance: Pointer);
+        true:  (SelectorInstance: CodePointer);
+    end;
+
+    PComparerVMT = ^TComparerVMT;
+    TComparerVMT = packed record
+      QueryInterface: CodePointer;
+      _AddRef: CodePointer;
+      _Release: CodePointer;
+      Compare: CodePointer;
+    end;
+
+    TSelectFunc = function(ATypeData: PTypeData; ASize: SizeInt): Pointer;
+
+  private
+    class function CreateInterface(AVMT: Pointer; ASize: SizeInt): PSpoofInterfacedTypeSizeObject; static;
+
+    class function SelectIntegerComparer(ATypeData: PTypeData; ASize: SizeInt): Pointer; static;
+    class function SelectInt64Comparer(ATypeData: PTypeData; ASize: SizeInt): Pointer; static;
+    class function SelectFloatComparer(ATypeData: PTypeData; ASize: SizeInt): Pointer; static;
+    class function SelectShortStringComparer(ATypeData: PTypeData; ASize: SizeInt): Pointer; static;
+    class function SelectBinaryComparer(ATypeData: PTypeData; ASize: SizeInt): Pointer; static;
+    class function SelectDynArrayComparer(ATypeData: PTypeData; ASize: SizeInt): Pointer; static;
+  private const
+    // IComparer VMT
+    Comparer_Int8_VMT  : TComparerVMT = (STD_RAW_INTERFACE_METHODS; Compare: @TCompare.Int8);
+    Comparer_Int16_VMT : TComparerVMT = (STD_RAW_INTERFACE_METHODS; Compare: @TCompare.Int16 );
+    Comparer_Int32_VMT : TComparerVMT = (STD_RAW_INTERFACE_METHODS; Compare: @TCompare.Int32 );
+    Comparer_Int64_VMT : TComparerVMT = (STD_RAW_INTERFACE_METHODS; Compare: @TCompare.Int64 );
+    Comparer_UInt8_VMT : TComparerVMT = (STD_RAW_INTERFACE_METHODS; Compare: @TCompare.UInt8 );
+    Comparer_UInt16_VMT: TComparerVMT = (STD_RAW_INTERFACE_METHODS; Compare: @TCompare.UInt16);
+    Comparer_UInt32_VMT: TComparerVMT = (STD_RAW_INTERFACE_METHODS; Compare: @TCompare.UInt32);
+    Comparer_UInt64_VMT: TComparerVMT = (STD_RAW_INTERFACE_METHODS; Compare: @TCompare.UInt64);
+
+    Comparer_Single_VMT  : TComparerVMT = (STD_RAW_INTERFACE_METHODS; Compare: @TCompare.Single  );
+    Comparer_Double_VMT  : TComparerVMT = (STD_RAW_INTERFACE_METHODS; Compare: @TCompare.Double  );
+    Comparer_Extended_VMT: TComparerVMT = (STD_RAW_INTERFACE_METHODS; Compare: @TCompare.Extended);
+
+    Comparer_Currency_VMT: TComparerVMT = (STD_RAW_INTERFACE_METHODS; Compare: @TCompare.Currency);
+    Comparer_Comp_VMT    : TComparerVMT = (STD_RAW_INTERFACE_METHODS; Compare: @TCompare.Comp    );
+
+    Comparer_Binary_VMT  : TComparerVMT = (STD_COM_TYPESIZE_INTERFACE_METHODS; Compare: @TCompare._Binary  );
+    Comparer_DynArray_VMT: TComparerVMT = (STD_COM_TYPESIZE_INTERFACE_METHODS; Compare: @TCompare._DynArray);
+
+    Comparer_ShortString1_VMT : TComparerVMT = (STD_RAW_INTERFACE_METHODS; Compare: @TCompare.ShortString1 );
+    Comparer_ShortString2_VMT : TComparerVMT = (STD_RAW_INTERFACE_METHODS; Compare: @TCompare.ShortString2 );
+    Comparer_ShortString3_VMT : TComparerVMT = (STD_RAW_INTERFACE_METHODS; Compare: @TCompare.ShortString3 );
+    Comparer_ShortString_VMT  : TComparerVMT = (STD_RAW_INTERFACE_METHODS; Compare: @TCompare.ShortString  );
+    Comparer_AnsiString_VMT   : TComparerVMT = (STD_RAW_INTERFACE_METHODS; Compare: @TCompare.AnsiString   );
+    Comparer_WideString_VMT   : TComparerVMT = (STD_RAW_INTERFACE_METHODS; Compare: @TCompare.WideString   );
+    Comparer_UnicodeString_VMT: TComparerVMT = (STD_RAW_INTERFACE_METHODS; Compare: @TCompare.UnicodeString);
+
+    Comparer_Method_VMT : TComparerVMT = (STD_RAW_INTERFACE_METHODS; Compare: @TCompare.Method );
+    Comparer_Variant_VMT: TComparerVMT = (STD_RAW_INTERFACE_METHODS; Compare: @TCompare.Variant);
+    Comparer_Pointer_VMT: TComparerVMT = (STD_RAW_INTERFACE_METHODS; Compare: @TCompare.Pointer);
+
+    // Instances
+    Comparer_Int8_Instance  : Pointer = @Comparer_Int8_VMT  ;
+    Comparer_Int16_Instance : Pointer = @Comparer_Int16_VMT ;
+    Comparer_Int32_Instance : Pointer = @Comparer_Int32_VMT ;
+    Comparer_Int64_Instance : Pointer = @Comparer_Int64_VMT ;
+    Comparer_UInt8_Instance : Pointer = @Comparer_UInt8_VMT ;
+    Comparer_UInt16_Instance: Pointer = @Comparer_UInt16_VMT;
+    Comparer_UInt32_Instance: Pointer = @Comparer_UInt32_VMT;
+    Comparer_UInt64_Instance: Pointer = @Comparer_UInt64_VMT;
+
+    Comparer_Single_Instance  : Pointer = @Comparer_Single_VMT  ;
+    Comparer_Double_Instance  : Pointer = @Comparer_Double_VMT  ;
+    Comparer_Extended_Instance: Pointer = @Comparer_Extended_VMT;
+
+    Comparer_Currency_Instance: Pointer = @Comparer_Currency_VMT;
+    Comparer_Comp_Instance    : Pointer = @Comparer_Comp_VMT    ;
+
+    //Comparer_Binary_Instance  : Pointer = @Comparer_Binary_VMT  ;  // dynamic instance
+    //Comparer_DynArray_Instance: Pointer = @Comparer_DynArray_VMT;  // dynamic instance
+
+    Comparer_ShortString1_Instance : Pointer = @Comparer_ShortString1_VMT ;
+    Comparer_ShortString2_Instance : Pointer = @Comparer_ShortString2_VMT ;
+    Comparer_ShortString3_Instance : Pointer = @Comparer_ShortString3_VMT ;
+    Comparer_ShortString_Instance  : Pointer = @Comparer_ShortString_VMT  ;
+    Comparer_AnsiString_Instance   : Pointer = @Comparer_AnsiString_VMT   ;
+    Comparer_WideString_Instance   : Pointer = @Comparer_WideString_VMT   ;
+    Comparer_UnicodeString_Instance: Pointer = @Comparer_UnicodeString_VMT;
+
+    Comparer_Method_Instance : Pointer = @Comparer_Method_VMT ;
+    Comparer_Variant_Instance: Pointer = @Comparer_Variant_VMT;
+    Comparer_Pointer_Instance: Pointer = @Comparer_Pointer_VMT;
+
+    ComparerInstances: array[TTypeKind] of TInstance =
+      (
+        // tkUnknown
+        (Selector: True;  SelectorInstance: @TComparerService.SelectBinaryComparer),
+        // tkInteger
+        (Selector: True;  SelectorInstance: @TComparerService.SelectIntegerComparer),
+        // tkChar
+        (Selector: False; Instance: @Comparer_UInt8_Instance),
+        // tkEnumeration
+        (Selector: True;  SelectorInstance: @TComparerService.SelectIntegerComparer),
+        // tkFloat
+        (Selector: True;  SelectorInstance: @TComparerService.SelectFloatComparer),
+        // tkSet
+        (Selector: True;  SelectorInstance: @TComparerService.SelectBinaryComparer),
+        // tkMethod
+        (Selector: False; Instance: @Comparer_Method_Instance),
+        // tkSString
+        (Selector: True;  SelectorInstance: @TComparerService.SelectShortStringComparer),
+        // tkLString - only internal use / deprecated in compiler
+        (Selector: False; Instance: @Comparer_AnsiString_Instance), // <- unsure
+        // tkAString
+        (Selector: False; Instance: @Comparer_AnsiString_Instance),
+        // tkWString
+        (Selector: False; Instance: @Comparer_WideString_Instance),
+        // tkVariant
+        (Selector: False; Instance: @Comparer_Variant_Instance),
+        // tkArray
+        (Selector: True;  SelectorInstance: @TComparerService.SelectBinaryComparer),
+        // tkRecord
+        (Selector: True;  SelectorInstance: @TComparerService.SelectBinaryComparer),
+        // tkInterface
+        (Selector: False; Instance: @Comparer_Pointer_Instance),
+        // tkClass
+        (Selector: False; Instance: @Comparer_Pointer_Instance),
+        // tkObject
+        (Selector: True;  SelectorInstance: @TComparerService.SelectBinaryComparer),
+        // tkWChar
+        (Selector: False; Instance: @Comparer_UInt16_Instance),
+        // tkBool
+        (Selector: True;  SelectorInstance: @TComparerService.SelectIntegerComparer),
+        // tkInt64
+        (Selector: False;  Instance: @Comparer_Int64_Instance),
+        // tkQWord
+        (Selector: False;  Instance: @Comparer_UInt64_Instance),
+        // tkDynArray
+        (Selector: True;  SelectorInstance: @TComparerService.SelectDynArrayComparer),
+        // tkInterfaceRaw
+        (Selector: False; Instance: @Comparer_Pointer_Instance),
+        // tkProcVar
+        (Selector: False; Instance: @Comparer_Pointer_Instance),
+        // tkUString
+        (Selector: False; Instance: @Comparer_UnicodeString_Instance),
+        // tkUChar - WTF? ... http://bugs.freepascal.org/view.php?id=24609
+        (Selector: False; Instance: @Comparer_UInt16_Instance), // <- unsure maybe Comparer_UInt32_Instance
+        // tkHelper
+        (Selector: False; Instance: @Comparer_Pointer_Instance),
+        // tkFile
+        (Selector: True;  SelectorInstance: @TComparerService.SelectBinaryComparer), // <- unsure what type?
+        // tkClassRef
+        (Selector: False; Instance: @Comparer_Pointer_Instance),
+        // tkPointer
+        (Selector: False; Instance: @Comparer_Pointer_Instance)
+      );
+  public
+    class function LookupComparer(ATypeInfo: PTypeInfo; ASize: SizeInt): Pointer; static;
+  end;
+
+  THashService = class(TComparerService)
+  public
+    class function LookupEqualityComparer(ATypeInfo: PTypeInfo; ASize: SizeInt): Pointer; virtual; abstract;
+  end;
+
+  TExtendedHashService = class(THashService)
+  public
+    class function LookupExtendedEqualityComparer(ATypeInfo: PTypeInfo; ASize: SizeInt): Pointer; virtual; abstract;
+  end;
+
+{$DEFINE HASH_FACTORY := PPEqualityComparerVMT(Self)^.__ClassRef}
+{$DEFINE EXTENDED_HASH_FACTORY := PPExtendedEqualityComparerVMT(Self)^.__ClassRef}
+
+  { THashService }
+
+  THashService<T: THashFactory> = class(THashService)
+  private
+    class function SelectIntegerEqualityComparer(ATypeData: PTypeData; ASize: SizeInt): Pointer; override;
+    class function SelectFloatEqualityComparer(ATypeData: PTypeData; ASize: SizeInt): Pointer; override;
+    class function SelectShortStringEqualityComparer(ATypeData: PTypeData; ASize: SizeInt): Pointer; override;
+    class function SelectBinaryEqualityComparer(ATypeData: PTypeData; ASize: SizeInt): Pointer; override;
+    class function SelectDynArrayEqualityComparer(ATypeData: PTypeData; ASize: SizeInt): Pointer; override;
+  private const
+    // IEqualityComparer VMT templates
+{$WARNINGS OFF}
+    EqualityComparer_Int8_VMT  : THashFactory.TEqualityComparerVMT = (STD_RAW_INTERFACE_METHODS; Equals: @TEquals.Int8  ; GetHashCode: @THashFactory.Int8  );
+    EqualityComparer_Int16_VMT : THashFactory.TEqualityComparerVMT = (STD_RAW_INTERFACE_METHODS; Equals: @TEquals.Int16 ; GetHashCode: @THashFactory.Int16 );
+    EqualityComparer_Int32_VMT : THashFactory.TEqualityComparerVMT = (STD_RAW_INTERFACE_METHODS; Equals: @TEquals.Int32 ; GetHashCode: @THashFactory.Int32 );
+    EqualityComparer_Int64_VMT : THashFactory.TEqualityComparerVMT = (STD_RAW_INTERFACE_METHODS; Equals: @TEquals.Int64 ; GetHashCode: @THashFactory.Int64 );
+    EqualityComparer_UInt8_VMT : THashFactory.TEqualityComparerVMT = (STD_RAW_INTERFACE_METHODS; Equals: @TEquals.UInt8 ; GetHashCode: @THashFactory.UInt8 );
+    EqualityComparer_UInt16_VMT: THashFactory.TEqualityComparerVMT = (STD_RAW_INTERFACE_METHODS; Equals: @TEquals.UInt16; GetHashCode: @THashFactory.UInt16);
+    EqualityComparer_UInt32_VMT: THashFactory.TEqualityComparerVMT = (STD_RAW_INTERFACE_METHODS; Equals: @TEquals.UInt32; GetHashCode: @THashFactory.UInt32);
+    EqualityComparer_UInt64_VMT: THashFactory.TEqualityComparerVMT = (STD_RAW_INTERFACE_METHODS; Equals: @TEquals.UInt64; GetHashCode: @THashFactory.UInt64);
+
+    EqualityComparer_Single_VMT  : THashFactory.TEqualityComparerVMT = (STD_RAW_INTERFACE_METHODS; Equals: @TEquals.Single  ; GetHashCode: @THashFactory.Single  );
+    EqualityComparer_Double_VMT  : THashFactory.TEqualityComparerVMT = (STD_RAW_INTERFACE_METHODS; Equals: @TEquals.Double  ; GetHashCode: @THashFactory.Double  );
+    EqualityComparer_Extended_VMT: THashFactory.TEqualityComparerVMT = (STD_RAW_INTERFACE_METHODS; Equals: @TEquals.Extended; GetHashCode: @THashFactory.Extended);
+
+    EqualityComparer_Currency_VMT: THashFactory.TEqualityComparerVMT = (STD_RAW_INTERFACE_METHODS; Equals: @TEquals.Currency; GetHashCode: @THashFactory.Currency);
+    EqualityComparer_Comp_VMT    : THashFactory.TEqualityComparerVMT = (STD_RAW_INTERFACE_METHODS; Equals: @TEquals.Comp    ; GetHashCode: @THashFactory.Comp    );
+
+    EqualityComparer_Binary_VMT  : THashFactory.TEqualityComparerVMT = (STD_COM_TYPESIZE_INTERFACE_METHODS; Equals: @TEquals._Binary  ; GetHashCode: @THashFactory.Binary  );
+    EqualityComparer_DynArray_VMT: THashFactory.TEqualityComparerVMT = (STD_COM_TYPESIZE_INTERFACE_METHODS; Equals: @TEquals._DynArray; GetHashCode: @THashFactory.DynArray);
+
+    EqualityComparer_Class_VMT: THashFactory.TEqualityComparerVMT = (STD_RAW_INTERFACE_METHODS; Equals: @TEquals.&Class; GetHashCode: @THashFactory.&Class);
+
+    EqualityComparer_ShortString1_VMT : THashFactory.TEqualityComparerVMT = (STD_RAW_INTERFACE_METHODS; Equals: @TEquals.ShortString1 ; GetHashCode: @THashFactory.ShortString1 );
+    EqualityComparer_ShortString2_VMT : THashFactory.TEqualityComparerVMT = (STD_RAW_INTERFACE_METHODS; Equals: @TEquals.ShortString2 ; GetHashCode: @THashFactory.ShortString2 );
+    EqualityComparer_ShortString3_VMT : THashFactory.TEqualityComparerVMT = (STD_RAW_INTERFACE_METHODS; Equals: @TEquals.ShortString3 ; GetHashCode: @THashFactory.ShortString3 );
+    EqualityComparer_ShortString_VMT  : THashFactory.TEqualityComparerVMT = (STD_RAW_INTERFACE_METHODS; Equals: @TEquals.ShortString  ; GetHashCode: @THashFactory.ShortString  );
+    EqualityComparer_AnsiString_VMT   : THashFactory.TEqualityComparerVMT = (STD_RAW_INTERFACE_METHODS; Equals: @TEquals.AnsiString   ; GetHashCode: @THashFactory.AnsiString   );
+    EqualityComparer_WideString_VMT   : THashFactory.TEqualityComparerVMT = (STD_RAW_INTERFACE_METHODS; Equals: @TEquals.WideString   ; GetHashCode: @THashFactory.WideString   );
+    EqualityComparer_UnicodeString_VMT: THashFactory.TEqualityComparerVMT = (STD_RAW_INTERFACE_METHODS; Equals: @TEquals.UnicodeString; GetHashCode: @THashFactory.UnicodeString);
+
+    EqualityComparer_Method_VMT : THashFactory.TEqualityComparerVMT = (STD_RAW_INTERFACE_METHODS; Equals: @TEquals.Method ; GetHashCode: @THashFactory.Method );
+    EqualityComparer_Variant_VMT: THashFactory.TEqualityComparerVMT = (STD_RAW_INTERFACE_METHODS; Equals: @TEquals.Variant; GetHashCode: @THashFactory.Variant);
+    EqualityComparer_Pointer_VMT: THashFactory.TEqualityComparerVMT = (STD_RAW_INTERFACE_METHODS; Equals: @TEquals.Pointer; GetHashCode: @THashFactory.Pointer);
+{.$WARNINGS ON} // do not enable warnings ever in this unit, or you will get many warnings about uninitialized TEqualityComparerVMT fields
+  private class var
+    // IEqualityComparer VMT
+    FEqualityComparer_Int8_VMT  : THashFactory.TEqualityComparerVMT;
+    FEqualityComparer_Int16_VMT : THashFactory.TEqualityComparerVMT;
+    FEqualityComparer_Int32_VMT : THashFactory.TEqualityComparerVMT;
+    FEqualityComparer_Int64_VMT : THashFactory.TEqualityComparerVMT;
+    FEqualityComparer_UInt8_VMT : THashFactory.TEqualityComparerVMT;
+    FEqualityComparer_UInt16_VMT: THashFactory.TEqualityComparerVMT;
+    FEqualityComparer_UInt32_VMT: THashFactory.TEqualityComparerVMT;
+    FEqualityComparer_UInt64_VMT: THashFactory.TEqualityComparerVMT;
+
+    FEqualityComparer_Single_VMT  : THashFactory.TEqualityComparerVMT;
+    FEqualityComparer_Double_VMT  : THashFactory.TEqualityComparerVMT;
+    FEqualityComparer_Extended_VMT: THashFactory.TEqualityComparerVMT;
+
+    FEqualityComparer_Currency_VMT: THashFactory.TEqualityComparerVMT;
+    FEqualityComparer_Comp_VMT    : THashFactory.TEqualityComparerVMT;
+
+    FEqualityComparer_Binary_VMT  : THashFactory.TEqualityComparerVMT;
+    FEqualityComparer_DynArray_VMT: THashFactory.TEqualityComparerVMT;
+
+    FEqualityComparer_Class_VMT: THashFactory.TEqualityComparerVMT;
+
+    FEqualityComparer_ShortString1_VMT : THashFactory.TEqualityComparerVMT;
+    FEqualityComparer_ShortString2_VMT : THashFactory.TEqualityComparerVMT;
+    FEqualityComparer_ShortString3_VMT : THashFactory.TEqualityComparerVMT;
+    FEqualityComparer_ShortString_VMT  : THashFactory.TEqualityComparerVMT;
+    FEqualityComparer_AnsiString_VMT   : THashFactory.TEqualityComparerVMT;
+    FEqualityComparer_WideString_VMT   : THashFactory.TEqualityComparerVMT;
+    FEqualityComparer_UnicodeString_VMT: THashFactory.TEqualityComparerVMT;
+
+    FEqualityComparer_Method_VMT : THashFactory.TEqualityComparerVMT;
+    FEqualityComparer_Variant_VMT: THashFactory.TEqualityComparerVMT;
+    FEqualityComparer_Pointer_VMT: THashFactory.TEqualityComparerVMT;
+
+    FEqualityComparer_Int8_Instance         : Pointer;
+    FEqualityComparer_Int16_Instance        : Pointer;
+    FEqualityComparer_Int32_Instance        : Pointer;
+    FEqualityComparer_Int64_Instance        : Pointer;
+    FEqualityComparer_UInt8_Instance        : Pointer;
+    FEqualityComparer_UInt16_Instance       : Pointer;
+    FEqualityComparer_UInt32_Instance       : Pointer;
+    FEqualityComparer_UInt64_Instance       : Pointer;
+
+    FEqualityComparer_Single_Instance       : Pointer;
+    FEqualityComparer_Double_Instance       : Pointer;
+    FEqualityComparer_Extended_Instance     : Pointer;
+
+    FEqualityComparer_Currency_Instance     : Pointer;
+    FEqualityComparer_Comp_Instance         : Pointer;
+
+    //FEqualityComparer_Binary_Instance     : Pointer;  // dynamic instance
+    //FEqualityComparer_DynArray_Instance   : Pointer;  // dynamic instance
+
+    FEqualityComparer_ShortString1_Instance : Pointer;
+    FEqualityComparer_ShortString2_Instance : Pointer;
+    FEqualityComparer_ShortString3_Instance : Pointer;
+    FEqualityComparer_ShortString_Instance  : Pointer;
+    FEqualityComparer_AnsiString_Instance   : Pointer;
+    FEqualityComparer_WideString_Instance   : Pointer;
+    FEqualityComparer_UnicodeString_Instance: Pointer;
+
+    FEqualityComparer_Method_Instance       : Pointer;
+    FEqualityComparer_Variant_Instance      : Pointer;
+    FEqualityComparer_Pointer_Instance      : Pointer;
+
+
+    FEqualityComparerInstances: array[TTypeKind] of TInstance;
+  private
+    class constructor Create;
+  public
+    class function LookupEqualityComparer(ATypeInfo: PTypeInfo; ASize: SizeInt): Pointer; override;
+  end;
+
+  { TExtendedHashService }
+
+  TExtendedHashService<T: TExtendedHashFactory> = class(TExtendedHashService)
+  private
+    class function SelectIntegerEqualityComparer(ATypeData: PTypeData; ASize: SizeInt): Pointer; override;
+    class function SelectFloatEqualityComparer(ATypeData: PTypeData; ASize: SizeInt): Pointer; override;
+    class function SelectShortStringEqualityComparer(ATypeData: PTypeData; ASize: SizeInt): Pointer; override;
+    class function SelectBinaryEqualityComparer(ATypeData: PTypeData; ASize: SizeInt): Pointer; override;
+    class function SelectDynArrayEqualityComparer(ATypeData: PTypeData; ASize: SizeInt): Pointer; override;
+  private const
+    // IExtendedEqualityComparer VMT templates
+{$WARNINGS OFF}
+    ExtendedEqualityComparer_Int8_VMT  : TExtendedHashFactory.TExtendedEqualityComparerVMT = (STD_RAW_INTERFACE_METHODS; Equals: @TEquals.Int8  ; GetHashCode: @THashFactory.Int8  ; GetHashList: @TExtendedHashFactory.Int8  );
+    ExtendedEqualityComparer_Int16_VMT : TExtendedHashFactory.TExtendedEqualityComparerVMT = (STD_RAW_INTERFACE_METHODS; Equals: @TEquals.Int16 ; GetHashCode: @THashFactory.Int16 ; GetHashList: @TExtendedHashFactory.Int16 );
+    ExtendedEqualityComparer_Int32_VMT : TExtendedHashFactory.TExtendedEqualityComparerVMT = (STD_RAW_INTERFACE_METHODS; Equals: @TEquals.Int32 ; GetHashCode: @THashFactory.Int32 ; GetHashList: @TExtendedHashFactory.Int32 );
+    ExtendedEqualityComparer_Int64_VMT : TExtendedHashFactory.TExtendedEqualityComparerVMT = (STD_RAW_INTERFACE_METHODS; Equals: @TEquals.Int64 ; GetHashCode: @THashFactory.Int64 ; GetHashList: @TExtendedHashFactory.Int64 );
+    ExtendedEqualityComparer_UInt8_VMT : TExtendedHashFactory.TExtendedEqualityComparerVMT = (STD_RAW_INTERFACE_METHODS; Equals: @TEquals.UInt8 ; GetHashCode: @THashFactory.UInt8 ; GetHashList: @TExtendedHashFactory.UInt8 );
+    ExtendedEqualityComparer_UInt16_VMT: TExtendedHashFactory.TExtendedEqualityComparerVMT = (STD_RAW_INTERFACE_METHODS; Equals: @TEquals.UInt16; GetHashCode: @THashFactory.UInt16; GetHashList: @TExtendedHashFactory.UInt16);
+    ExtendedEqualityComparer_UInt32_VMT: TExtendedHashFactory.TExtendedEqualityComparerVMT = (STD_RAW_INTERFACE_METHODS; Equals: @TEquals.UInt32; GetHashCode: @THashFactory.UInt32; GetHashList: @TExtendedHashFactory.UInt32);
+    ExtendedEqualityComparer_UInt64_VMT: TExtendedHashFactory.TExtendedEqualityComparerVMT = (STD_RAW_INTERFACE_METHODS; Equals: @TEquals.UInt64; GetHashCode: @THashFactory.UInt64; GetHashList: @TExtendedHashFactory.UInt64);
+
+    ExtendedEqualityComparer_Single_VMT  : TExtendedHashFactory.TExtendedEqualityComparerVMT = (STD_RAW_INTERFACE_METHODS; Equals: @TEquals.Single  ; GetHashCode: @THashFactory.Single  ; GetHashList: @TExtendedHashFactory.Single  );
+    ExtendedEqualityComparer_Double_VMT  : TExtendedHashFactory.TExtendedEqualityComparerVMT = (STD_RAW_INTERFACE_METHODS; Equals: @TEquals.Double  ; GetHashCode: @THashFactory.Double  ; GetHashList: @TExtendedHashFactory.Double  );
+    ExtendedEqualityComparer_Extended_VMT: TExtendedHashFactory.TExtendedEqualityComparerVMT = (STD_RAW_INTERFACE_METHODS; Equals: @TEquals.Extended; GetHashCode: @THashFactory.Extended; GetHashList: @TExtendedHashFactory.Extended);
+
+    ExtendedEqualityComparer_Currency_VMT: TExtendedHashFactory.TExtendedEqualityComparerVMT = (STD_RAW_INTERFACE_METHODS; Equals: @TEquals.Currency; GetHashCode: @THashFactory.Currency; GetHashList: @TExtendedHashFactory.Currency);
+    ExtendedEqualityComparer_Comp_VMT    : TExtendedHashFactory.TExtendedEqualityComparerVMT = (STD_RAW_INTERFACE_METHODS; Equals: @TEquals.Comp    ; GetHashCode: @THashFactory.Comp    ; GetHashList: @TExtendedHashFactory.Comp    );
+
+    ExtendedEqualityComparer_Binary_VMT  : TExtendedHashFactory.TExtendedEqualityComparerVMT = (STD_COM_TYPESIZE_INTERFACE_METHODS; Equals: @TEquals._Binary  ; GetHashCode: @THashFactory.Binary  ; GetHashList: @TExtendedHashFactory.Binary   );
+    ExtendedEqualityComparer_DynArray_VMT: TExtendedHashFactory.TExtendedEqualityComparerVMT = (STD_COM_TYPESIZE_INTERFACE_METHODS; Equals: @TEquals._DynArray; GetHashCode: @THashFactory.DynArray; GetHashList: @TExtendedHashFactory.DynArray);
+
+    ExtendedEqualityComparer_Class_VMT: TExtendedHashFactory.TExtendedEqualityComparerVMT = (STD_RAW_INTERFACE_METHODS; Equals: @TEquals.&Class; GetHashCode: @THashFactory.&Class; GetHashList: @TExtendedHashFactory.&Class);
+
+    ExtendedEqualityComparer_ShortString1_VMT : TExtendedHashFactory.TExtendedEqualityComparerVMT = (STD_RAW_INTERFACE_METHODS; Equals: @TEquals.ShortString1 ; GetHashCode: @THashFactory.ShortString1 ; GetHashList: @TExtendedHashFactory.ShortString1 );
+    ExtendedEqualityComparer_ShortString2_VMT : TExtendedHashFactory.TExtendedEqualityComparerVMT = (STD_RAW_INTERFACE_METHODS; Equals: @TEquals.ShortString2 ; GetHashCode: @THashFactory.ShortString2 ; GetHashList: @TExtendedHashFactory.ShortString2 );
+    ExtendedEqualityComparer_ShortString3_VMT : TExtendedHashFactory.TExtendedEqualityComparerVMT = (STD_RAW_INTERFACE_METHODS; Equals: @TEquals.ShortString3 ; GetHashCode: @THashFactory.ShortString3 ; GetHashList: @TExtendedHashFactory.ShortString3 );
+    ExtendedEqualityComparer_ShortString_VMT  : TExtendedHashFactory.TExtendedEqualityComparerVMT = (STD_RAW_INTERFACE_METHODS; Equals: @TEquals.ShortString  ; GetHashCode: @THashFactory.ShortString  ; GetHashList: @TExtendedHashFactory.ShortString  );
+    ExtendedEqualityComparer_AnsiString_VMT   : TExtendedHashFactory.TExtendedEqualityComparerVMT = (STD_RAW_INTERFACE_METHODS; Equals: @TEquals.AnsiString   ; GetHashCode: @THashFactory.AnsiString   ; GetHashList: @TExtendedHashFactory.AnsiString   );
+    ExtendedEqualityComparer_WideString_VMT   : TExtendedHashFactory.TExtendedEqualityComparerVMT = (STD_RAW_INTERFACE_METHODS; Equals: @TEquals.WideString   ; GetHashCode: @THashFactory.WideString   ; GetHashList: @TExtendedHashFactory.WideString   );
+    ExtendedEqualityComparer_UnicodeString_VMT: TExtendedHashFactory.TExtendedEqualityComparerVMT = (STD_RAW_INTERFACE_METHODS; Equals: @TEquals.UnicodeString; GetHashCode: @THashFactory.UnicodeString; GetHashList: @TExtendedHashFactory.UnicodeString);
+
+    ExtendedEqualityComparer_Method_VMT : TExtendedHashFactory.TExtendedEqualityComparerVMT = (STD_RAW_INTERFACE_METHODS; Equals: @TEquals.Method ; GetHashCode: @THashFactory.Method ; GetHashList: @TExtendedHashFactory.Method );
+    ExtendedEqualityComparer_Variant_VMT: TExtendedHashFactory.TExtendedEqualityComparerVMT = (STD_RAW_INTERFACE_METHODS; Equals: @TEquals.Variant; GetHashCode: @THashFactory.Variant; GetHashList: @TExtendedHashFactory.Variant);
+    ExtendedEqualityComparer_Pointer_VMT: TExtendedHashFactory.TExtendedEqualityComparerVMT = (STD_RAW_INTERFACE_METHODS; Equals: @TEquals.Pointer; GetHashCode: @THashFactory.Pointer; GetHashList: @TExtendedHashFactory.Pointer);
+{.$WARNINGS ON} // do not enable warnings ever in this unit, or you will get many warnings about uninitialized TEqualityComparerVMT fields
+  private class var
+    // IExtendedEqualityComparer VMT
+    FExtendedEqualityComparer_Int8_VMT  : TExtendedHashFactory.TExtendedEqualityComparerVMT;
+    FExtendedEqualityComparer_Int16_VMT : TExtendedHashFactory.TExtendedEqualityComparerVMT;
+    FExtendedEqualityComparer_Int32_VMT : TExtendedHashFactory.TExtendedEqualityComparerVMT;
+    FExtendedEqualityComparer_Int64_VMT : TExtendedHashFactory.TExtendedEqualityComparerVMT;
+    FExtendedEqualityComparer_UInt8_VMT : TExtendedHashFactory.TExtendedEqualityComparerVMT;
+    FExtendedEqualityComparer_UInt16_VMT: TExtendedHashFactory.TExtendedEqualityComparerVMT;
+    FExtendedEqualityComparer_UInt32_VMT: TExtendedHashFactory.TExtendedEqualityComparerVMT;
+    FExtendedEqualityComparer_UInt64_VMT: TExtendedHashFactory.TExtendedEqualityComparerVMT;
+
+    FExtendedEqualityComparer_Single_VMT  : TExtendedHashFactory.TExtendedEqualityComparerVMT;
+    FExtendedEqualityComparer_Double_VMT  : TExtendedHashFactory.TExtendedEqualityComparerVMT;
+    FExtendedEqualityComparer_Extended_VMT: TExtendedHashFactory.TExtendedEqualityComparerVMT;
+
+    FExtendedEqualityComparer_Currency_VMT: TExtendedHashFactory.TExtendedEqualityComparerVMT;
+    FExtendedEqualityComparer_Comp_VMT    : TExtendedHashFactory.TExtendedEqualityComparerVMT;
+
+    FExtendedEqualityComparer_Binary_VMT  : TExtendedHashFactory.TExtendedEqualityComparerVMT;
+    FExtendedEqualityComparer_DynArray_VMT: TExtendedHashFactory.TExtendedEqualityComparerVMT;
+
+    FExtendedEqualityComparer_Class_VMT: TExtendedHashFactory.TExtendedEqualityComparerVMT;
+
+    FExtendedEqualityComparer_ShortString1_VMT : TExtendedHashFactory.TExtendedEqualityComparerVMT;
+    FExtendedEqualityComparer_ShortString2_VMT : TExtendedHashFactory.TExtendedEqualityComparerVMT;
+    FExtendedEqualityComparer_ShortString3_VMT : TExtendedHashFactory.TExtendedEqualityComparerVMT;
+    FExtendedEqualityComparer_ShortString_VMT  : TExtendedHashFactory.TExtendedEqualityComparerVMT;
+    FExtendedEqualityComparer_AnsiString_VMT   : TExtendedHashFactory.TExtendedEqualityComparerVMT;
+    FExtendedEqualityComparer_WideString_VMT   : TExtendedHashFactory.TExtendedEqualityComparerVMT;
+    FExtendedEqualityComparer_UnicodeString_VMT: TExtendedHashFactory.TExtendedEqualityComparerVMT;
+
+    FExtendedEqualityComparer_Method_VMT : TExtendedHashFactory.TExtendedEqualityComparerVMT;
+    FExtendedEqualityComparer_Variant_VMT: TExtendedHashFactory.TExtendedEqualityComparerVMT;
+    FExtendedEqualityComparer_Pointer_VMT: TExtendedHashFactory.TExtendedEqualityComparerVMT;
+
+    FExtendedEqualityComparer_Int8_Instance         : Pointer;
+    FExtendedEqualityComparer_Int16_Instance        : Pointer;
+    FExtendedEqualityComparer_Int32_Instance        : Pointer;
+    FExtendedEqualityComparer_Int64_Instance        : Pointer;
+    FExtendedEqualityComparer_UInt8_Instance        : Pointer;
+    FExtendedEqualityComparer_UInt16_Instance       : Pointer;
+    FExtendedEqualityComparer_UInt32_Instance       : Pointer;
+    FExtendedEqualityComparer_UInt64_Instance       : Pointer;
+
+    FExtendedEqualityComparer_Single_Instance       : Pointer;
+    FExtendedEqualityComparer_Double_Instance       : Pointer;
+    FExtendedEqualityComparer_Extended_Instance     : Pointer;
+
+    FExtendedEqualityComparer_Currency_Instance     : Pointer;
+    FExtendedEqualityComparer_Comp_Instance         : Pointer;
+
+    //FExtendedEqualityComparer_Binary_Instance     : Pointer;  // dynamic instance
+    //FExtendedEqualityComparer_DynArray_Instance   : Pointer;  // dynamic instance
+
+    FExtendedEqualityComparer_ShortString1_Instance : Pointer;
+    FExtendedEqualityComparer_ShortString2_Instance : Pointer;
+    FExtendedEqualityComparer_ShortString3_Instance : Pointer;
+    FExtendedEqualityComparer_ShortString_Instance  : Pointer;
+    FExtendedEqualityComparer_AnsiString_Instance   : Pointer;
+    FExtendedEqualityComparer_WideString_Instance   : Pointer;
+    FExtendedEqualityComparer_UnicodeString_Instance: Pointer;
+
+    FExtendedEqualityComparer_Method_Instance       : Pointer;
+    FExtendedEqualityComparer_Variant_Instance      : Pointer;
+    FExtendedEqualityComparer_Pointer_Instance      : Pointer;
+
+    // all instances
+    FExtendedEqualityComparerInstances: array[TTypeKind] of TInstance;
+  private
+    class constructor Create;
+  public
+    class function LookupExtendedEqualityComparer(ATypeInfo: PTypeInfo; ASize: SizeInt): Pointer; override;
+  end;
+
+  TOnEqualityComparison<T> = function(constref ALeft, ARight: T): Boolean of object;
+  TEqualityComparisonFunc<T> = function(constref ALeft, ARight: T): Boolean;
+
+  TOnHasher<T> = function(constref AValue: T): UInt32 of object;
+  TOnExtendedHasher<T> = procedure(constref AValue: T; AHashList: PUInt32) of object;
+  THasherFunc<T> = function(constref AValue: T): UInt32;
+  TExtendedHasherFunc<T> = procedure(constref AValue: T; AHashList: PUInt32);
+
+  TEqualityComparer<T> = class(TInterfacedObject, IEqualityComparer<T>)
+  public
+    class function Default: IEqualityComparer<T>; static; overload;
+    class function Default(AHashFactoryClass: THashFactoryClass): IEqualityComparer<T>; static; overload;
+
+    class function Construct(const AEqualityComparison: TOnEqualityComparison<T>;
+      const AHasher: TOnHasher<T>): IEqualityComparer<T>; overload;
+    class function Construct(const AEqualityComparison: TEqualityComparisonFunc<T>;
+      const AHasher: THasherFunc<T>): IEqualityComparer<T>; overload;
+
+    function Equals(constref ALeft, ARight: T): Boolean; virtual; overload; abstract;
+    function GetHashCode(constref AValue: T): UInt32;  virtual; overload; abstract;
+  end;
+
+  { TDelegatedEqualityComparerEvent }
+
+  TDelegatedEqualityComparerEvents<T> = class(TEqualityComparer<T>)
+  private
+    FEqualityComparison: TOnEqualityComparison<T>;
+    FHasher: TOnHasher<T>;
+  public
+    function Equals(constref ALeft, ARight: T): Boolean; override;
+    function GetHashCode(constref AValue: T): UInt32; override;
+
+    constructor Create(const AEqualityComparison: TOnEqualityComparison<T>;
+      const AHasher: TOnHasher<T>);
+  end;
+
+  TDelegatedEqualityComparerFunc<T> = class(TEqualityComparer<T>)
+  private
+    FEqualityComparison: TEqualityComparisonFunc<T>;
+    FHasher: THasherFunc<T>;
+  public
+    function Equals(constref ALeft, ARight: T): Boolean; override;
+    function GetHashCode(constref AValue: T): UInt32; override;
+
+    constructor Create(const AEqualityComparison: TEqualityComparisonFunc<T>;
+      const AHasher: THasherFunc<T>);
+  end;
+
+  { TExtendedEqualityComparer }
+
+  TExtendedEqualityComparer<T> = class(TEqualityComparer<T>, IExtendedEqualityComparer<T>)
+  public
+    class function Default: IExtendedEqualityComparer<T>; static; overload; reintroduce;
+    class function Default(AExtenedHashFactoryClass: TExtendedHashFactoryClass): IExtendedEqualityComparer<T>; static; overload; reintroduce;
+
+    class function Construct(const AEqualityComparison: TOnEqualityComparison<T>;
+       const AHasher: TOnHasher<T>; const AExtendedHasher: TOnExtendedHasher<T>): IExtendedEqualityComparer<T>; overload; reintroduce;
+    class function Construct(const AEqualityComparison: TEqualityComparisonFunc<T>;
+       const AHasher: THasherFunc<T>; const AExtendedHasher: TExtendedHasherFunc<T>): IExtendedEqualityComparer<T>; overload; reintroduce;
+    class function Construct(const AEqualityComparison: TOnEqualityComparison<T>;
+       const AExtendedHasher: TOnExtendedHasher<T>): IExtendedEqualityComparer<T>; overload; reintroduce;
+    class function Construct(const AEqualityComparison: TEqualityComparisonFunc<T>;
+       const AExtendedHasher: TExtendedHasherFunc<T>): IExtendedEqualityComparer<T>; overload; reintroduce;
+
+    procedure GetHashList(constref AValue: T; AHashList: PUInt32); virtual; abstract;
+  end;
+
+  TDelegatedExtendedEqualityComparerEvents<T> = class(TExtendedEqualityComparer<T>)
+  private
+    FEqualityComparison: TOnEqualityComparison<T>;
+    FHasher: TOnHasher<T>;
+    FExtendedHasher: TOnExtendedHasher<T>;
+
+    function GetHashCodeMethod(constref AValue: T): UInt32;
+  public
+    function Equals(constref ALeft, ARight: T): Boolean; override;
+    function GetHashCode(constref AValue: T): UInt32; override;
+    procedure GetHashList(constref AValue: T; AHashList: PUInt32); override;
+
+    constructor Create(const AEqualityComparison: TOnEqualityComparison<T>;
+      const AHasher: TOnHasher<T>; const AExtendedHasher: TOnExtendedHasher<T>); overload;
+    constructor Create(const AEqualityComparison: TOnEqualityComparison<T>;
+      const AExtendedHasher: TOnExtendedHasher<T>); overload;
+  end;
+
+  TDelegatedExtendedEqualityComparerFunc<T> = class(TExtendedEqualityComparer<T>)
+  private
+    FEqualityComparison: TEqualityComparisonFunc<T>;
+    FHasher: THasherFunc<T>;
+    FExtendedHasher: TExtendedHasherFunc<T>;
+  public
+    function Equals(constref ALeft, ARight: T): Boolean; override;
+    function GetHashCode(constref AValue: T): UInt32; override;
+    procedure GetHashList(constref AValue: T; AHashList: PUInt32); override;
+
+    constructor Create(const AEqualityComparison: TEqualityComparisonFunc<T>;
+      const AHasher: THasherFunc<T>; const AExtendedHasher: TExtendedHasherFunc<T>); overload;
+    constructor Create(const AEqualityComparison: TEqualityComparisonFunc<T>;
+      const AExtendedHasher: TExtendedHasherFunc<T>); overload;
+  end;
+
+  { TDelphiHashFactory }
+
+  TDelphiHashFactory = class(THashFactory)
+  public
+    class function GetHashService: THashServiceClass; override;
+    class function GetHashCode(AKey: Pointer; ASize: SizeInt; AInitVal: UInt32 = 0): UInt32; override;
+  end;
+
+  TmORMotHashFactory = class(THashFactory)
+  public
+    class function GetHashService: THashServiceClass; override;
+    class function GetHashCode(AKey: Pointer; ASize: SizeInt; AInitVal: UInt32 = 0): UInt32; override;
+  end;
+
+  { TAdler32HashFactory }
+
+  TAdler32HashFactory = class(THashFactory)
+  public
+    class function GetHashService: THashServiceClass; override;
+    class function GetHashCode(AKey: Pointer; ASize: SizeInt; AInitVal: UInt32 = 0): UInt32; override;
+  end;
+
+  { TSdbmHashFactory }
+
+  TSdbmHashFactory = class(THashFactory)
+  public
+    class function GetHashService: THashServiceClass; override;
+    class function GetHashCode(AKey: Pointer; ASize: SizeInt; AInitVal: UInt32 = 0): UInt32; override;
+  end;
+
+  { TSdbmHashFactory }
+
+  TSimpleChecksumFactory = class(THashFactory)
+  public
+    class function GetHashService: THashServiceClass; override;
+    class function GetHashCode(AKey: Pointer; ASize: SizeInt; AInitVal: UInt32 = 0): UInt32; override;
+  end;
+
+  { TDelphiDoubleHashFactory }
+
+  TDelphiDoubleHashFactory = class(TExtendedHashFactory)
+  public
+    const MAX_HASHLIST_COUNT = 2;
+    const HASH_FUNCTIONS_COUNT = 1;
+    const HASHLIST_COUNT_PER_FUNCTION: array[1..HASH_FUNCTIONS_COUNT] of Integer = (2);
+    const HASH_FUNCTIONS_MASK_SIZE = 1;
+    const HASH_FUNCTIONS_MASK = 1; // 00000001b
+
+    class function GetHashService: THashServiceClass; override;
+    class function GetHashCode(AKey: Pointer; ASize: SizeInt; AInitVal: UInt32 = 0): UInt32; override;
+    class procedure GetHashList(AKey: Pointer; ASize: SizeInt; AHashList: PUInt32; AOptions: TGetHashListOptions = []); override;
+  end;
+
+  TDelphiQuadrupleHashFactory = class(TExtendedHashFactory)
+  public
+    const MAX_HASHLIST_COUNT = 4;
+    const HASH_FUNCTIONS_COUNT = 2;
+    const HASHLIST_COUNT_PER_FUNCTION: array[1..HASH_FUNCTIONS_COUNT] of Integer = (2, 2);
+    const HASH_FUNCTIONS_MASK_SIZE = 2;
+    const HASH_FUNCTIONS_MASK = 3; // 00000011b
+
+    class function GetHashService: THashServiceClass; override;
+    class function GetHashCode(AKey: Pointer; ASize: SizeInt; AInitVal: UInt32 = 0): UInt32; override;
+    class procedure GetHashList(AKey: Pointer; ASize: SizeInt; AHashList: PUInt32; AOptions: TGetHashListOptions = []); override;
+  end;
+
+  TDelphiSixfoldHashFactory = class(TExtendedHashFactory)
+  public
+    const MAX_HASHLIST_COUNT = 6;
+    const HASH_FUNCTIONS_COUNT = 3;
+    const HASHLIST_COUNT_PER_FUNCTION: array[1..HASH_FUNCTIONS_COUNT] of Integer = (2, 2, 2);
+    const HASH_FUNCTIONS_MASK_SIZE = 3;
+    const HASH_FUNCTIONS_MASK = 7; // 00000111b
+
+    class function GetHashService: THashServiceClass; override;
+    class function GetHashCode(AKey: Pointer; ASize: SizeInt; AInitVal: UInt32 = 0): UInt32; override;
+    class procedure GetHashList(AKey: Pointer; ASize: SizeInt; AHashList: PUInt32; AOptions: TGetHashListOptions = []); override;
+  end;
+
+  TDefaultHashFactory = TmORMotHashFactory;
+
+  TDefaultGenericInterface = (giComparer, giEqualityComparer, giExtendedEqualityComparer);
+
+  TCustomComparer<T> = class(TSingletonImplementation, IComparer<T>, IEqualityComparer<T>, IExtendedEqualityComparer<T>)
+  protected
+    function Compare(constref Left, Right: T): Integer; virtual; abstract;
+    function Equals(constref Left, Right: T): Boolean; reintroduce; overload; virtual; abstract;
+    function GetHashCode(constref Value: T): UInt32; reintroduce; overload; virtual; abstract;
+    procedure GetHashList(constref Value: T; AHashList: PUInt32); virtual; abstract;
+  end;
+
+  TOrdinalComparer<T, THashFactory> = class(TCustomComparer<T>)
+  protected class var
+    FComparer: IComparer<T>;
+    FEqualityComparer: IEqualityComparer<T>;
+    FExtendedEqualityComparer: IExtendedEqualityComparer<T>;
+
+    class constructor Create;
+  public
+    class function Ordinal: TCustomComparer<T>; virtual; abstract;
+  end;
+
+  // TGStringComparer will be renamed to TStringComparer -> bug #26030
+  // anyway class var can't be used safely -> bug #24848
+
+  TGStringComparer<T, THashFactory> = class(TOrdinalComparer<T, THashFactory>)
+  private class var
+    FOrdinal: TCustomComparer<T>;
+    class destructor Destroy;
+  public
+    class function Ordinal: TCustomComparer<T>; override;
+  end;
+
+  TGStringComparer<T> = class(TGStringComparer<T, TDelphiQuadrupleHashFactory>);
+  TStringComparer = class(TGStringComparer<string>);
+  TAnsiStringComparer = class(TGStringComparer<AnsiString>);
+  TUnicodeStringComparer = class(TGStringComparer<UnicodeString>);
+
+  { TGOrdinalStringComparer }
+
+  // TGOrdinalStringComparer will be renamed to TOrdinalStringComparer -> bug #26030
+  // anyway class var can't be used safely -> bug #24848
+  TGOrdinalStringComparer<T, THashFactory> = class(TGStringComparer<T, THashFactory>)
+  public
+    function Compare(constref ALeft, ARight: T): Integer; override;
+    function Equals(constref ALeft, ARight: T): Boolean; overload; override;
+    function GetHashCode(constref AValue: T): UInt32; overload; override;
+    procedure GetHashList(constref AValue: T; AHashList: PUInt32); override;
+  end;
+
+  TGOrdinalStringComparer<T> = class(TGOrdinalStringComparer<T, TDelphiQuadrupleHashFactory>);
+  TOrdinalStringComparer = class(TGOrdinalStringComparer<string>);
+
+  TGIStringComparer<T, THashFactory> = class(TOrdinalComparer<T, THashFactory>)
+  private class var
+    FOrdinal: TCustomComparer<T>;
+    class destructor Destroy;
+  public
+    class function Ordinal: TCustomComparer<T>; override;
+  end;
+
+  TGIStringComparer<T> = class(TGIStringComparer<T, TDelphiQuadrupleHashFactory>);
+  TIStringComparer = class(TGIStringComparer<string>);
+  TIAnsiStringComparer = class(TGIStringComparer<AnsiString>);
+  TIUnicodeStringComparer = class(TGIStringComparer<UnicodeString>);
+
+  TGOrdinalIStringComparer<T, THashFactory> = class(TGIStringComparer<T, THashFactory>)
+  public
+    function Compare(constref ALeft, ARight: T): Integer; override;
+    function Equals(constref ALeft, ARight: T): Boolean; overload; override;
+    function GetHashCode(constref AValue: T): UInt32; overload; override;
+    procedure GetHashList(constref AValue: T; AHashList: PUInt32); override;
+  end;
+
+  TGOrdinalIStringComparer<T> = class(TGOrdinalIStringComparer<T, TDelphiQuadrupleHashFactory>);
+  TOrdinalIStringComparer = class(TGOrdinalIStringComparer<string>);
+
+// Delphi version of Bob Jenkins Hash
+function BobJenkinsHash(const AData; ALength, AInitData: Integer): Integer; // same result as HashLittle_Delphi, just different interface
+function BinaryCompare(const ALeft, ARight: Pointer; ASize: PtrUInt): Integer; inline;
+
+function _LookupVtableInfo(AGInterface: TDefaultGenericInterface; ATypeInfo: PTypeInfo; ASize: SizeInt): Pointer; inline;
+function _LookupVtableInfoEx(AGInterface: TDefaultGenericInterface; ATypeInfo: PTypeInfo; ASize: SizeInt;
+  AFactory: THashFactoryClass): Pointer;
+
+implementation
+
+{ TComparer<T> }
+
+class function TComparer<T>.Default: IComparer<T>;
+begin
+  Result := _LookupVtableInfo(giComparer, TypeInfo(T), SizeOf(T));
+end;
+
+class function TComparer<T>.Construct(const AComparison: TOnComparison<T>): IComparer<T>;
+begin
+  Result := TDelegatedComparerEvents<T>.Create(AComparison);
+end;
+
+class function TComparer<T>.Construct(const AComparison: TComparisonFunc<T>): IComparer<T>;
+begin
+  Result := TDelegatedComparerFunc<T>.Create(AComparison);
+end;
+
+function TDelegatedComparerEvents<T>.Compare(constref ALeft, ARight: T): Integer;
+begin
+  Result := FComparison(ALeft, ARight);
+end;
+
+constructor TDelegatedComparerEvents<T>.Create(AComparison: TOnComparison<T>);
+begin
+  FComparison := AComparison;
+end;
+
+function TDelegatedComparerFunc<T>.Compare(constref ALeft, ARight: T): Integer;
+begin
+  Result := FComparison(ALeft, ARight);
+end;
+
+constructor TDelegatedComparerFunc<T>.Create(AComparison: TComparisonFunc<T>);
+begin
+  FComparison := AComparison;
+end;
+
+{ TInterface }
+
+function TInterface.QueryInterface(constref IID: TGUID; out Obj): HResult;
+begin
+  Result := E_NOINTERFACE;
+end;
+
+{ TRawInterface }
+
+function TRawInterface._AddRef: LongInt;
+begin
+  Result := -1;
+end;
+
+function TRawInterface._Release: LongInt;
+begin
+  Result := -1;
+end;
+
+{ TComTypeSizeInterface }
+
+function TComTypeSizeInterface._AddRef: LongInt;
+var
+  _self: TComparerService.PSpoofInterfacedTypeSizeObject absolute Self;
+begin
+  Result := InterLockedIncrement(_self.RefCount);
+end;
+
+function TComTypeSizeInterface._Release: LongInt;
+var
+  _self: TComparerService.PSpoofInterfacedTypeSizeObject absolute Self;
+begin
+  Result := InterLockedDecrement(_self.RefCount);
+  if _self.RefCount = 0 then
+    Dispose(_self);
+end;
+
+{ TSingletonImplementation }
+
+function TSingletonImplementation.QueryInterface(constref IID: TGUID; out Obj): HResult;
+begin
+  if GetInterface(IID, Obj) then
+    Result := S_OK
+  else
+    Result := E_NOINTERFACE;
+end;
+
+{ TCompare }
+
+(***********************************************************************************************************************
+  Comparers
+(**********************************************************************************************************************)
+
+{-----------------------------------------------------------------------------------------------------------------------
+  Comparers Int8 - Int32 and UInt8 - UInt32
+{----------------------------------------------------------------------------------------------------------------------}
+
+class function TCompare.Integer(constref ALeft, ARight: Integer): Integer;
+begin
+  Result := Math.CompareValue(ALeft, ARight);
+end;
+
+class function TCompare.Int8(constref ALeft, ARight: Int8): Integer;
+begin
+  Result := ALeft - ARight;
+end;
+
+class function TCompare.Int16(constref ALeft, ARight: Int16): Integer;
+begin
+  Result := ALeft - ARight;
+end;
+
+class function TCompare.Int32(constref ALeft, ARight: Int32): Integer;
+begin
+  if ALeft > ARight then
+    Exit(1)
+  else if ALeft < ARight then
+    Exit(-1)
+  else
+    Exit(0);
+end;
+
+class function TCompare.Int64(constref ALeft, ARight: Int64): Integer;
+begin
+  if ALeft > ARight then
+    Exit(1)
+  else if ALeft < ARight then
+    Exit(-1)
+  else
+    Exit(0);
+end;
+
+class function TCompare.UInt8(constref ALeft, ARight: UInt8): Integer;
+begin
+  Result := System.Integer(ALeft) - System.Integer(ARight);
+end;
+
+class function TCompare.UInt16(constref ALeft, ARight: UInt16): Integer;
+begin
+  Result := System.Integer(ALeft) - System.Integer(ARight);
+end;
+
+class function TCompare.UInt32(constref ALeft, ARight: UInt32): Integer;
+begin
+  if ALeft > ARight then
+    Exit(1)
+  else if ALeft < ARight then
+    Exit(-1)
+  else
+    Exit(0);
+end;
+
+class function TCompare.UInt64(constref ALeft, ARight: UInt64): Integer;
+begin
+  if ALeft > ARight then
+    Exit(1)
+  else if ALeft < ARight then
+    Exit(-1)
+  else
+    Exit(0);
+end;
+
+{-----------------------------------------------------------------------------------------------------------------------
+  Comparers for Float types
+{----------------------------------------------------------------------------------------------------------------------}
+
+class function TCompare.Single(constref ALeft, ARight: Single): Integer;
+begin
+  if ALeft > ARight then
+    Exit(1)
+  else if ALeft < ARight then
+    Exit(-1)
+  else
+    Exit(0);
+end;
+
+class function TCompare.Double(constref ALeft, ARight: Double): Integer;
+begin
+  if ALeft > ARight then
+    Exit(1)
+  else if ALeft < ARight then
+    Exit(-1)
+  else
+    Exit(0);
+end;
+
+class function TCompare.Extended(constref ALeft, ARight: Extended): Integer;
+begin
+  if ALeft > ARight then
+    Exit(1)
+  else if ALeft < ARight then
+    Exit(-1)
+  else
+    Exit(0);
+end;
+
+{-----------------------------------------------------------------------------------------------------------------------
+  Comparers for other number types
+{----------------------------------------------------------------------------------------------------------------------}
+
+class function TCompare.Currency(constref ALeft, ARight: Currency): Integer;
+begin
+  if ALeft > ARight then
+    Exit(1)
+  else if ALeft < ARight then
+    Exit(-1)
+  else
+    Exit(0);
+end;
+
+class function TCompare.Comp(constref ALeft, ARight: Comp): Integer;
+begin
+  if ALeft > ARight then
+    Exit(1)
+  else if ALeft < ARight then
+    Exit(-1)
+  else
+    Exit(0);
+end;
+
+{-----------------------------------------------------------------------------------------------------------------------
+  Comparers for binary data (records etc) and dynamics arrays
+{----------------------------------------------------------------------------------------------------------------------}
+
+class function TCompare._Binary(constref ALeft, ARight): Integer;
+var
+  _self: TComparerService.PSpoofInterfacedTypeSizeObject absolute Self;
+begin
+  Result := CompareMemRange(@ALeft, @ARight, _self.Size);
+end;
+
+class function TCompare._DynArray(constref ALeft, ARight: Pointer): Integer;
+var
+  _self: TComparerService.PSpoofInterfacedTypeSizeObject absolute Self;
+  LLength, LLeftLength, LRightLength: Integer;
+begin
+  LLeftLength := DynArraySize(ALeft);
+  LRightLength := DynArraySize(ARight);
+  if LLeftLength > LRightLength then
+    LLength := LRightLength
+  else
+    LLength := LLeftLength;
+
+  Result := CompareMemRange(ALeft, ARight, LLength * _self.Size);
+
+  if Result = 0 then
+    Result := LLeftLength - LRightLength;
+end;
+
+class function TCompare.Binary(constref ALeft, ARight; const ASize: SizeInt): Integer;
+begin
+  Result := CompareMemRange(@ALeft, @ARight, ASize);
+end;
+
+class function TCompare.DynArray(constref ALeft, ARight: Pointer; const AElementSize: SizeInt): Integer;
+var
+  LLength, LLeftLength, LRightLength: Integer;
+begin
+  LLeftLength := DynArraySize(ALeft);
+  LRightLength := DynArraySize(ARight);
+  if LLeftLength > LRightLength then
+    LLength := LRightLength
+  else
+    LLength := LLeftLength;
+
+  Result := CompareMemRange(ALeft, ARight, LLength * AElementSize);
+
+  if Result = 0 then
+    Result := LLeftLength - LRightLength;
+end;
+
+{-----------------------------------------------------------------------------------------------------------------------
+  Comparers for string types
+{----------------------------------------------------------------------------------------------------------------------}
+
+class function TCompare.ShortString1(constref ALeft, ARight: ShortString1): Integer;
+begin
+  if ALeft > ARight then
+    Exit(1)
+  else if ALeft < ARight then
+    Exit(-1)
+  else
+    Exit(0);
+end;
+
+class function TCompare.ShortString2(constref ALeft, ARight: ShortString2): Integer;
+begin
+  if ALeft > ARight then
+    Exit(1)
+  else if ALeft < ARight then
+    Exit(-1)
+  else
+    Exit(0);
+end;
+
+class function TCompare.ShortString3(constref ALeft, ARight: ShortString3): Integer;
+begin
+  if ALeft > ARight then
+    Exit(1)
+  else if ALeft < ARight then
+    Exit(-1)
+  else
+    Exit(0);
+end;
+
+class function TCompare.ShortString(constref ALeft, ARight: ShortString): Integer;
+begin
+  if ALeft > ARight then
+    Exit(1)
+  else if ALeft < ARight then
+    Exit(-1)
+  else
+    Exit(0);
+end;
+
+class function TCompare.&String(constref ALeft, ARight: String): Integer;
+begin
+  Result := CompareStr(ALeft, ARight);
+end;
+
+class function TCompare.AnsiString(constref ALeft, ARight: AnsiString): Integer;
+begin
+  Result := AnsiCompareStr(ALeft, ARight);
+end;
+
+class function TCompare.WideString(constref ALeft, ARight: WideString): Integer;
+begin
+  Result := WideCompareStr(ALeft, ARight);
+end;
+
+class function TCompare.UnicodeString(constref ALeft, ARight: UnicodeString): Integer;
+begin
+  Result := UnicodeCompareStr(ALeft, ARight);
+end;
+
+{-----------------------------------------------------------------------------------------------------------------------
+  Comparers for Delegates
+{----------------------------------------------------------------------------------------------------------------------}
+
+class function TCompare.Method(constref ALeft, ARight: TMethod): Integer;
+begin
+  Result := CompareMemRange(@ALeft, @ARight, SizeOf(System.TMethod));
+end;
+
+{-----------------------------------------------------------------------------------------------------------------------
+  Comparers for Variant
+{----------------------------------------------------------------------------------------------------------------------}
+
+class function TCompare.Variant(constref ALeft, ARight: PVariant): Integer;
+var
+  LLeftString, LRightString: string;
+begin
+  try
+    case VarCompareValue(ALeft^, ARight^) of
+      vrGreaterThan:
+        Exit(1);
+      vrLessThan:
+        Exit(-1);
+      vrEqual:
+        Exit(0);
+      vrNotEqual:
+        if VarIsEmpty(ALeft^) or VarIsNull(ALeft^) then
+          Exit(1)
+        else
+          Exit(-1);
+    end;
+  except
+    try
+      LLeftString := ALeft^;
+      LRightString := ARight^;
+      Result := CompareStr(LLeftString, LRightString);
+    except
+      Result := CompareMemRange(ALeft, ARight, SizeOf(System.Variant));
+    end;
+  end;
+end;
+
+{-----------------------------------------------------------------------------------------------------------------------
+  Comparers for Pointer
+{----------------------------------------------------------------------------------------------------------------------}
+
+class function TCompare.Pointer(constref ALeft, ARight: PtrUInt): Integer;
+begin
+  if ALeft > ARight then
+    Exit(1)
+  else if ALeft < ARight then
+    Exit(-1)
+  else
+    Exit(0);
+end;
+
+{ TEquals }
+
+(***********************************************************************************************************************
+  Equality Comparers
+(**********************************************************************************************************************)
+
+{-----------------------------------------------------------------------------------------------------------------------
+  Equality Comparers Int8 - Int32 and UInt8 - UInt32
+{----------------------------------------------------------------------------------------------------------------------}
+
+class function TEquals.Integer(constref ALeft, ARight: Integer): Boolean;
+begin
+  Result := ALeft = ARight;
+end;
+
+class function TEquals.Int8(constref ALeft, ARight: Int8): Boolean;
+begin
+  Result := ALeft = ARight;
+end;
+
+class function TEquals.Int16(constref ALeft, ARight: Int16): Boolean;
+begin
+  Result := ALeft = ARight;
+end;
+
+class function TEquals.Int32(constref ALeft, ARight: Int32): Boolean;
+begin
+  Result := ALeft = ARight;
+end;
+
+class function TEquals.Int64(constref ALeft, ARight: Int64): Boolean;
+begin
+  Result := ALeft = ARight;
+end;
+
+class function TEquals.UInt8(constref ALeft, ARight: UInt8): Boolean;
+begin
+  Result := ALeft = ARight;
+end;
+
+class function TEquals.UInt16(constref ALeft, ARight: UInt16): Boolean;
+begin
+  Result := ALeft = ARight;
+end;
+
+class function TEquals.UInt32(constref ALeft, ARight: UInt32): Boolean;
+begin
+  Result := ALeft = ARight;
+end;
+
+class function TEquals.UInt64(constref ALeft, ARight: UInt64): Boolean;
+begin
+  Result := ALeft = ARight;
+end;
+
+{-----------------------------------------------------------------------------------------------------------------------
+  Equality Comparers for Float types
+{----------------------------------------------------------------------------------------------------------------------}
+
+class function TEquals.Single(constref ALeft, ARight: Single): Boolean;
+begin
+  Result := ALeft = ARight;
+end;
+
+class function TEquals.Double(constref ALeft, ARight: Double): Boolean;
+begin
+  Result := ALeft = ARight;
+end;
+
+class function TEquals.Extended(constref ALeft, ARight: Extended): Boolean;
+begin
+  Result := ALeft = ARight;
+end;
+
+{-----------------------------------------------------------------------------------------------------------------------
+  Equality Comparers for other number types
+{----------------------------------------------------------------------------------------------------------------------}
+
+class function TEquals.Currency(constref ALeft, ARight: Currency): Boolean;
+begin
+  Result := ALeft = ARight;
+end;
+
+class function TEquals.Comp(constref ALeft, ARight: Comp): Boolean;
+begin
+  Result := ALeft = ARight;
+end;
+
+{-----------------------------------------------------------------------------------------------------------------------
+  Equality Comparers for binary data (records etc) and dynamics arrays
+{----------------------------------------------------------------------------------------------------------------------}
+
+class function TEquals._Binary(constref ALeft, ARight): Boolean;
+var
+  _self: TComparerService.PSpoofInterfacedTypeSizeObject absolute Self;
+begin
+  Result := CompareMem(@ALeft, @ARight, _self.Size);
+end;
+
+class function TEquals._DynArray(constref ALeft, ARight: Pointer): Boolean;
+var
+  _self: TComparerService.PSpoofInterfacedTypeSizeObject absolute Self;
+  LLength: Integer;
+begin
+  LLength := DynArraySize(ALeft);
+  if LLength <> DynArraySize(ARight) then
+    Exit(False);
+
+  Result := CompareMem(ALeft, ARight, LLength * _self.Size);
+end;
+
+class function TEquals.Binary(constref ALeft, ARight; const ASize: SizeInt): Boolean;
+begin
+  Result := CompareMem(@ALeft, @ARight, ASize);
+end;
+
+class function TEquals.DynArray(constref ALeft, ARight: Pointer; const AElementSize: SizeInt): Boolean;
+var
+  LLength: Integer;
+begin
+  LLength := DynArraySize(ALeft);
+  if LLength <> DynArraySize(ARight) then
+    Exit(False);
+
+  Result := CompareMem(ALeft, ARight, LLength * AElementSize);
+end;
+
+{-----------------------------------------------------------------------------------------------------------------------
+  Equality Comparers for classes
+{----------------------------------------------------------------------------------------------------------------------}
+
+class function TEquals.&class(constref ALeft, ARight: TObject): Boolean;
+begin
+  if ALeft <> nil then
+    Exit(ALeft.Equals(ARight))
+  else
+    Exit(ARight = nil);
+end;
+
+{-----------------------------------------------------------------------------------------------------------------------
+  Equality Comparers for string types
+{----------------------------------------------------------------------------------------------------------------------}
+
+class function TEquals.ShortString1(constref ALeft, ARight: ShortString1): Boolean;
+begin
+  Result := ALeft = ARight;
+end;
+
+class function TEquals.ShortString2(constref ALeft, ARight: ShortString2): Boolean;
+begin
+  Result := ALeft = ARight;
+end;
+
+class function TEquals.ShortString3(constref ALeft, ARight: ShortString3): Boolean;
+begin
+  Result := ALeft = ARight;
+end;
+
+class function TEquals.&String(constref ALeft, ARight: String): Boolean;
+begin
+  Result := ALeft = ARight;
+end;
+
+class function TEquals.ShortString(constref ALeft, ARight: ShortString): Boolean;
+begin
+  Result := ALeft = ARight;
+end;
+
+class function TEquals.AnsiString(constref ALeft, ARight: AnsiString): Boolean;
+begin
+  Result := ALeft = ARight;
+end;
+
+class function TEquals.WideString(constref ALeft, ARight: WideString): Boolean;
+begin
+  Result := ALeft = ARight;
+end;
+
+class function TEquals.UnicodeString(constref ALeft, ARight: UnicodeString): Boolean;
+begin
+  Result := ALeft = ARight;
+end;
+
+{-----------------------------------------------------------------------------------------------------------------------
+  Equality Comparers for Delegates
+{----------------------------------------------------------------------------------------------------------------------}
+
+class function TEquals.Method(constref ALeft, ARight: TMethod): Boolean;
+begin
+  Result := (ALeft.Code = ARight.Code) and (ALeft.Data = ARight.Data);
+end;
+
+{-----------------------------------------------------------------------------------------------------------------------
+  Equality Comparers for Variant
+{----------------------------------------------------------------------------------------------------------------------}
+
+class function TEquals.Variant(constref ALeft, ARight: PVariant): Boolean;
+begin
+  Result := VarCompareValue(ALeft^, ARight^) = vrEqual;
+end;
+
+{-----------------------------------------------------------------------------------------------------------------------
+  Equality Comparers for Pointer
+{----------------------------------------------------------------------------------------------------------------------}
+
+class function TEquals.Pointer(constref ALeft, ARight: PtrUInt): Boolean;
+begin
+  Result := ALeft = ARight;
+end;
+
+(***********************************************************************************************************************
+  Hashes
+(**********************************************************************************************************************)
+
+{-----------------------------------------------------------------------------------------------------------------------
+  GetHashCode Int8 - Int32 and UInt8 - UInt32
+{----------------------------------------------------------------------------------------------------------------------}
+
+class function THashFactory.Int8(constref AValue: Int8): UInt32;
+begin
+  Result := HASH_FACTORY.GetHashCode(@AValue, SizeOf(System.Int8), 0);
+end;
+
+class function THashFactory.Int16(constref AValue: Int16): UInt32;
+begin
+  Result := HASH_FACTORY.GetHashCode(@AValue, SizeOf(System.Int16), 0);
+end;
+
+class function THashFactory.Int32(constref AValue: Int32): UInt32;
+begin
+  Result := HASH_FACTORY.GetHashCode(@AValue, SizeOf(System.Int32), 0);
+end;
+
+class function THashFactory.Int64(constref AValue: Int64): UInt32;
+begin
+  Result := HASH_FACTORY.GetHashCode(@AValue, SizeOf(System.Int64), 0);
+end;
+
+class function THashFactory.UInt8(constref AValue: UInt8): UInt32;
+begin
+  Result := HASH_FACTORY.GetHashCode(@AValue, SizeOf(System.UInt8), 0);
+end;
+
+class function THashFactory.UInt16(constref AValue: UInt16): UInt32;
+begin
+  Result := HASH_FACTORY.GetHashCode(@AValue, SizeOf(System.UInt16), 0);
+end;
+
+class function THashFactory.UInt32(constref AValue: UInt32): UInt32;
+begin
+  Result := HASH_FACTORY.GetHashCode(@AValue, SizeOf(System.UInt32), 0);
+end;
+
+class function THashFactory.UInt64(constref AValue: UInt64): UInt32;
+begin
+  Result := HASH_FACTORY.GetHashCode(@AValue, SizeOf(System.UInt64), 0);
+end;
+
+{-----------------------------------------------------------------------------------------------------------------------
+  GetHashCode for Float types
+{----------------------------------------------------------------------------------------------------------------------}
+
+class function THashFactory.Single(constref AValue: Single): UInt32;
+var
+  LMantissa: Float;
+  LExponent: Integer;
+begin
+  Frexp(AValue, LMantissa, LExponent);
+
+  if LMantissa = 0 then
+    LMantissa := Abs(LMantissa);
+
+  Result := HASH_FACTORY.GetHashCode(@LMantissa, SizeOf(Math.Float), 0);
+  Result := HASH_FACTORY.GetHashCode(@LExponent, SizeOf(System.Integer), Result);
+end;
+
+class function THashFactory.Double(constref AValue: Double): UInt32;
+var
+  LMantissa: Float;
+  LExponent: Integer;
+begin
+  Frexp(AValue, LMantissa, LExponent);
+
+  if LMantissa = 0 then
+    LMantissa := Abs(LMantissa);
+
+  Result := HASH_FACTORY.GetHashCode(@LMantissa, SizeOf(Math.Float), 0);
+  Result := HASH_FACTORY.GetHashCode(@LExponent, SizeOf(System.Integer), Result);
+end;
+
+class function THashFactory.Extended(constref AValue: Extended): UInt32;
+var
+  LMantissa: Float;
+  LExponent: Integer;
+begin
+  Frexp(AValue, LMantissa, LExponent);
+
+  if LMantissa = 0 then
+    LMantissa := Abs(LMantissa);
+
+  Result := HASH_FACTORY.GetHashCode(@LMantissa, SizeOf(Math.Float), 0);
+  Result := HASH_FACTORY.GetHashCode(@LExponent, SizeOf(System.Integer), Result);
+end;
+
+{-----------------------------------------------------------------------------------------------------------------------
+  GetHashCode for other number types
+{----------------------------------------------------------------------------------------------------------------------}
+
+class function THashFactory.Currency(constref AValue: Currency): UInt32;
+begin
+  Result := HASH_FACTORY.GetHashCode(@AValue, SizeOf(System.Int64), 0);
+end;
+
+class function THashFactory.Comp(constref AValue: Comp): UInt32;
+begin
+  Result := HASH_FACTORY.GetHashCode(@AValue, SizeOf(System.Int64), 0);
+end;
+
+{-----------------------------------------------------------------------------------------------------------------------
+  GetHashCode for binary data (records etc) and dynamics arrays
+{----------------------------------------------------------------------------------------------------------------------}
+
+class function THashFactory.Binary(constref AValue): UInt32;
+var
+  _self: TComparerService.PSpoofInterfacedTypeSizeObject absolute Self;
+begin
+  Result := HASH_FACTORY.GetHashCode(@AValue, _self.Size, 0);
+end;
+
+class function THashFactory.DynArray(constref AValue: Pointer): UInt32;
+var
+  _self: TComparerService.PSpoofInterfacedTypeSizeObject absolute Self;
+begin
+  Result := HASH_FACTORY.GetHashCode(AValue, DynArraySize(AValue) * _self.Size, 0);
+end;
+
+{-----------------------------------------------------------------------------------------------------------------------
+  GetHashCode for classes
+{----------------------------------------------------------------------------------------------------------------------}
+
+class function THashFactory.&Class(constref AValue: TObject): UInt32;
+begin
+  if AValue = nil then
+    Exit($2A);
+
+  Result := AValue.GetHashCode;
+end;
+
+{-----------------------------------------------------------------------------------------------------------------------
+  GetHashCode for string types
+{----------------------------------------------------------------------------------------------------------------------}
+
+class function THashFactory.ShortString1(constref AValue: ShortString1): UInt32;
+begin
+  Result := HASH_FACTORY.GetHashCode(@AValue[1], Length(AValue), 0);
+end;
+
+class function THashFactory.ShortString2(constref AValue: ShortString2): UInt32;
+begin
+  Result := HASH_FACTORY.GetHashCode(@AValue[1], Length(AValue), 0);
+end;
+
+class function THashFactory.ShortString3(constref AValue: ShortString3): UInt32;
+begin
+  Result := HASH_FACTORY.GetHashCode(@AValue[1], Length(AValue), 0);
+end;
+
+class function THashFactory.ShortString(constref AValue: ShortString): UInt32;
+begin
+  Result := HASH_FACTORY.GetHashCode(@AValue[1], Length(AValue), 0);
+end;
+
+class function THashFactory.AnsiString(constref AValue: AnsiString): UInt32;
+begin
+  Result := HASH_FACTORY.GetHashCode(@AValue[1], Length(AValue) * SizeOf(System.AnsiChar), 0);
+end;
+
+class function THashFactory.WideString(constref AValue: WideString): UInt32;
+begin
+  Result := HASH_FACTORY.GetHashCode(@AValue[1], Length(AValue) * SizeOf(System.WideChar), 0);
+end;
+
+class function THashFactory.UnicodeString(constref AValue: UnicodeString): UInt32;
+begin
+  Result := HASH_FACTORY.GetHashCode(@AValue[1], Length(AValue) * SizeOf(System.UnicodeChar), 0);
+end;
+
+{-----------------------------------------------------------------------------------------------------------------------
+  GetHashCode for Delegates
+{----------------------------------------------------------------------------------------------------------------------}
+
+class function THashFactory.Method(constref AValue: TMethod): UInt32;
+begin
+  Result := HASH_FACTORY.GetHashCode(@AValue, SizeOf(System.TMethod), 0);
+end;
+
+{-----------------------------------------------------------------------------------------------------------------------
+  GetHashCode for Variant
+{----------------------------------------------------------------------------------------------------------------------}
+
+class function THashFactory.Variant(constref AValue: PVariant): UInt32;
+begin
+  try
+    Result := HASH_FACTORY.UnicodeString(AValue^);
+  except
+    Result := HASH_FACTORY.GetHashCode(AValue, SizeOf(System.Variant), 0);
+  end;
+end;
+
+{-----------------------------------------------------------------------------------------------------------------------
+  GetHashCode for Pointer
+{----------------------------------------------------------------------------------------------------------------------}
+
+class function THashFactory.Pointer(constref AValue: Pointer): UInt32;
+begin
+  Result := HASH_FACTORY.GetHashCode(@AValue, SizeOf(System.Pointer), 0);
+end;
+
+{ TExtendedHashFactory }
+
+(***********************************************************************************************************************
+  Hashes 2
+(**********************************************************************************************************************)
+
+{-----------------------------------------------------------------------------------------------------------------------
+  GetHashCode Int8 - Int32 and UInt8 - UInt32
+{----------------------------------------------------------------------------------------------------------------------}
+
+class procedure TExtendedHashFactory.Int8(constref AValue: Int8; AHashList: PUInt32);
+begin
+  EXTENDED_HASH_FACTORY.GetHashList(@AValue, SizeOf(System.Int8), AHashList, []);
+end;
+
+class procedure TExtendedHashFactory.Int16(constref AValue: Int16; AHashList: PUInt32);
+begin
+  EXTENDED_HASH_FACTORY.GetHashList(@AValue, SizeOf(System.Int16), AHashList, []);
+end;
+
+class procedure TExtendedHashFactory.Int32(constref AValue: Int32; AHashList: PUInt32);
+begin
+  EXTENDED_HASH_FACTORY.GetHashList(@AValue, SizeOf(System.Int32), AHashList, []);
+end;
+
+class procedure TExtendedHashFactory.Int64(constref AValue: Int64; AHashList: PUInt32);
+begin
+  EXTENDED_HASH_FACTORY.GetHashList(@AValue, SizeOf(System.Int64), AHashList, []);
+end;
+
+class procedure TExtendedHashFactory.UInt8(constref AValue: UInt8; AHashList: PUInt32);
+begin
+  EXTENDED_HASH_FACTORY.GetHashList(@AValue, SizeOf(System.UInt8), AHashList, []);
+end;
+
+class procedure TExtendedHashFactory.UInt16(constref AValue: UInt16; AHashList: PUInt32);
+begin
+  EXTENDED_HASH_FACTORY.GetHashList(@AValue, SizeOf(System.UInt16), AHashList, []);
+end;
+
+class procedure TExtendedHashFactory.UInt32(constref AValue: UInt32; AHashList: PUInt32);
+begin
+  EXTENDED_HASH_FACTORY.GetHashList(@AValue, SizeOf(System.UInt32), AHashList, []);
+end;
+
+class procedure TExtendedHashFactory.UInt64(constref AValue: UInt64; AHashList: PUInt32);
+begin
+  EXTENDED_HASH_FACTORY.GetHashList(@AValue, SizeOf(System.UInt64), AHashList, []);
+end;
+
+{-----------------------------------------------------------------------------------------------------------------------
+  GetHashCode for Float types
+{----------------------------------------------------------------------------------------------------------------------}
+
+class procedure TExtendedHashFactory.Single(constref AValue: Single; AHashList: PUInt32);
+var
+  LMantissa: Float;
+  LExponent: Integer;
+begin
+  Frexp(AValue, LMantissa, LExponent);
+
+  if LMantissa = 0 then
+    LMantissa := Abs(LMantissa);
+
+  EXTENDED_HASH_FACTORY.GetHashList(@LMantissa, SizeOf(Math.Float), AHashList, []);
+  EXTENDED_HASH_FACTORY.GetHashList(@LExponent, SizeOf(System.Integer), AHashList, [ghloHashListAsInitData]);
+end;
+
+class procedure TExtendedHashFactory.Double(constref AValue: Double; AHashList: PUInt32);
+var
+  LMantissa: Float;
+  LExponent: Integer;
+begin
+  Frexp(AValue, LMantissa, LExponent);
+
+  if LMantissa = 0 then
+    LMantissa := Abs(LMantissa);
+
+  EXTENDED_HASH_FACTORY.GetHashList(@LMantissa, SizeOf(Math.Float), AHashList, []);
+  EXTENDED_HASH_FACTORY.GetHashList(@LExponent, SizeOf(System.Integer), AHashList, [ghloHashListAsInitData]);
+end;
+
+class procedure TExtendedHashFactory.Extended(constref AValue: Extended; AHashList: PUInt32);
+var
+  LMantissa: Float;
+  LExponent: Integer;
+begin
+  Frexp(AValue, LMantissa, LExponent);
+
+  if LMantissa = 0 then
+    LMantissa := Abs(LMantissa);
+
+  EXTENDED_HASH_FACTORY.GetHashList(@LMantissa, SizeOf(Math.Float), AHashList, []);
+  EXTENDED_HASH_FACTORY.GetHashList(@LExponent, SizeOf(System.Integer), AHashList, [ghloHashListAsInitData]);
+end;
+
+{-----------------------------------------------------------------------------------------------------------------------
+  GetHashCode for other number types
+{----------------------------------------------------------------------------------------------------------------------}
+
+class procedure TExtendedHashFactory.Currency(constref AValue: Currency; AHashList: PUInt32);
+begin
+  EXTENDED_HASH_FACTORY.GetHashList(@AValue, SizeOf(System.Int64), AHashList, []);
+end;
+
+class procedure TExtendedHashFactory.Comp(constref AValue: Comp; AHashList: PUInt32);
+begin
+  EXTENDED_HASH_FACTORY.GetHashList(@AValue, SizeOf(System.Int64), AHashList, []);
+end;
+
+{-----------------------------------------------------------------------------------------------------------------------
+  GetHashCode for binary data (records etc) and dynamics arrays
+{----------------------------------------------------------------------------------------------------------------------}
+
+class procedure TExtendedHashFactory.Binary(constref AValue; AHashList: PUInt32);
+var
+  _self: TComparerService.PSpoofInterfacedTypeSizeObject absolute Self;
+begin
+  EXTENDED_HASH_FACTORY.GetHashList(@AValue, _self.Size, AHashList, []);
+end;
+
+class procedure TExtendedHashFactory.DynArray(constref AValue: Pointer; AHashList: PUInt32);
+var
+  _self: TComparerService.PSpoofInterfacedTypeSizeObject absolute Self;
+begin
+  EXTENDED_HASH_FACTORY.GetHashList(AValue, DynArraySize(AValue) * _self.Size, AHashList, []);
+end;
+
+{-----------------------------------------------------------------------------------------------------------------------
+  GetHashCode for classes
+{----------------------------------------------------------------------------------------------------------------------}
+
+class procedure TExtendedHashFactory.&Class(constref AValue: TObject; AHashList: PUInt32);
+var
+  LValue: PtrInt;
+begin
+  if AValue = nil then
+  begin
+    LValue := $2A;
+    EXTENDED_HASH_FACTORY.GetHashList(@LValue, SizeOf(LValue), AHashList, []);
+    Exit;
+  end;
+
+  LValue := AValue.GetHashCode;
+  EXTENDED_HASH_FACTORY.GetHashList(@LValue, SizeOf(LValue), AHashList, []);
+end;
+
+{-----------------------------------------------------------------------------------------------------------------------
+  GetHashCode for string types
+{----------------------------------------------------------------------------------------------------------------------}
+
+class procedure TExtendedHashFactory.ShortString1(constref AValue: ShortString1; AHashList: PUInt32);
+begin
+  EXTENDED_HASH_FACTORY.GetHashList(@AValue[1], Length(AValue), AHashList, []);
+end;
+
+class procedure TExtendedHashFactory.ShortString2(constref AValue: ShortString2; AHashList: PUInt32);
+begin
+  EXTENDED_HASH_FACTORY.GetHashList(@AValue[1], Length(AValue), AHashList, []);
+end;
+
+class procedure TExtendedHashFactory.ShortString3(constref AValue: ShortString3; AHashList: PUInt32);
+begin
+  EXTENDED_HASH_FACTORY.GetHashList(@AValue[1], Length(AValue), AHashList, []);
+end;
+
+class procedure TExtendedHashFactory.ShortString(constref AValue: ShortString; AHashList: PUInt32);
+begin
+  EXTENDED_HASH_FACTORY.GetHashList(@AValue[1], Length(AValue), AHashList, []);
+end;
+
+class procedure TExtendedHashFactory.AnsiString(constref AValue: AnsiString; AHashList: PUInt32);
+begin
+  EXTENDED_HASH_FACTORY.GetHashList(@AValue[1], Length(AValue) * SizeOf(System.AnsiChar), AHashList, []);
+end;
+
+class procedure TExtendedHashFactory.WideString(constref AValue: WideString; AHashList: PUInt32);
+begin
+  EXTENDED_HASH_FACTORY.GetHashList(@AValue[1], Length(AValue) * SizeOf(System.WideChar), AHashList, []);
+end;
+
+class procedure TExtendedHashFactory.UnicodeString(constref AValue: UnicodeString; AHashList: PUInt32);
+begin
+  EXTENDED_HASH_FACTORY.GetHashList(@AValue[1], Length(AValue) * SizeOf(System.UnicodeChar), AHashList, []);
+end;
+
+{-----------------------------------------------------------------------------------------------------------------------
+  GetHashCode for Delegates
+{----------------------------------------------------------------------------------------------------------------------}
+
+class procedure TExtendedHashFactory.Method(constref AValue: TMethod; AHashList: PUInt32);
+begin
+  EXTENDED_HASH_FACTORY.GetHashList(@AValue, SizeOf(System.TMethod), AHashList, []);
+end;
+
+{-----------------------------------------------------------------------------------------------------------------------
+  GetHashCode for Variant
+{----------------------------------------------------------------------------------------------------------------------}
+
+class procedure TExtendedHashFactory.Variant(constref AValue: PVariant; AHashList: PUInt32);
+begin
+  try
+    EXTENDED_HASH_FACTORY.UnicodeString(AValue^, AHashList);
+  except
+    EXTENDED_HASH_FACTORY.GetHashList(AValue, SizeOf(System.Variant), AHashList, []);
+  end;
+end;
+
+{-----------------------------------------------------------------------------------------------------------------------
+  GetHashCode for Pointer
+{----------------------------------------------------------------------------------------------------------------------}
+
+class procedure TExtendedHashFactory.Pointer(constref AValue: Pointer; AHashList: PUInt32);
+begin
+  EXTENDED_HASH_FACTORY.GetHashList(@AValue, SizeOf(System.Pointer), AHashList, []);
+end;
+
+{ TComparerService }
+
+class function TComparerService.CreateInterface(AVMT: Pointer; ASize: SizeInt): PSpoofInterfacedTypeSizeObject;
+begin
+    Result := New(PSpoofInterfacedTypeSizeObject);
+    Result.VMT      := AVMT;
+    Result.RefCount := 0;
+    Result.Size     := ASize;
+end;
+
+class function TComparerService.SelectIntegerComparer(ATypeData: PTypeData; ASize: SizeInt): Pointer;
+begin
+  case ATypeData.OrdType of
+    otSByte:
+      Exit(@Comparer_Int8_Instance);
+    otUByte:
+      Exit(@Comparer_UInt8_Instance);
+    otSWord:
+      Exit(@Comparer_Int16_Instance);
+    otUWord:
+      Exit(@Comparer_UInt16_Instance);
+    otSLong:
+      Exit(@Comparer_Int32_Instance);
+    otULong:
+      Exit(@Comparer_UInt32_Instance);
+  else
+    System.Error(reRangeError);
+    Exit(nil);
+  end;
+end;
+
+class function TComparerService.SelectInt64Comparer(ATypeData: PTypeData; ASize: SizeInt): Pointer;
+begin
+  if ATypeData.MaxInt64Value > ATypeData.MinInt64Value then
+    Exit(@Comparer_Int64_Instance)
+  else
+    Exit(@Comparer_UInt64_Instance);
+end;
+
+class function TComparerService.SelectFloatComparer(ATypeData: PTypeData;
+  ASize: SizeInt): Pointer;
+begin
+  case ATypeData.FloatType of
+    ftSingle:
+      Exit(@Comparer_Single_Instance);
+    ftDouble:
+      Exit(@Comparer_Double_Instance);
+    ftExtended:
+      Exit(@Comparer_Extended_Instance);
+    ftComp:
+      Exit(@Comparer_Comp_Instance);
+    ftCurr:
+      Exit(@Comparer_Currency_Instance);
+  else
+    System.Error(reRangeError);
+    Exit(nil);
+  end;
+end;
+
+class function TComparerService.SelectShortStringComparer(ATypeData: PTypeData;
+  ASize: SizeInt): Pointer;
+begin
+  case ASize of
+    2: Exit(@Comparer_ShortString1_Instance);
+    3: Exit(@Comparer_ShortString2_Instance);
+    4: Exit(@Comparer_ShortString3_Instance);
+  else
+    Exit(@Comparer_ShortString_Instance);
+  end;
+end;
+
+class function TComparerService.SelectBinaryComparer(ATypeData: PTypeData;
+  ASize: SizeInt): Pointer;
+begin
+  case ASize of
+    1: Exit(@Comparer_UInt8_Instance);
+    2: Exit(@Comparer_UInt16_Instance);
+    4: Exit(@Comparer_UInt32_Instance);
+{$IFDEF CPU64}
+    8: Exit(@Comparer_UInt64_Instance)
+{$ENDIF}
+  else
+    Result := CreateInterface(@Comparer_Binary_VMT, ASize);
+  end;
+end;
+
+class function TComparerService.SelectDynArrayComparer(ATypeData: PTypeData; ASize: SizeInt): Pointer;
+begin
+  Result := CreateInterface(@Comparer_DynArray_VMT, ATypeData.elSize);
+end;
+
+class function TComparerService.LookupComparer(ATypeInfo: PTypeInfo; ASize: SizeInt): Pointer;
+var
+  LInstance: PInstance;
+begin
+  if ATypeInfo = nil then
+    Exit(SelectBinaryComparer(GetTypeData(ATypeInfo), ASize))
+  else
+  begin
+    LInstance := @ComparerInstances[ATypeInfo.Kind];
+    Result := LInstance.Instance;
+    if LInstance.Selector then
+      Result := TSelectFunc(Result)(GetTypeData(ATypeInfo), ASize);
+  end;
+end;
+
+{ TComparerService.TInstance }
+
+class function TComparerService.TInstance.Create(ASelector: Boolean;
+  AInstance: Pointer): TComparerService.TInstance;
+begin
+  Result.Selector := ASelector;
+  Result.Instance := AInstance;
+end;
+
+class function TComparerService.TInstance.CreateSelector(ASelectorInstance: CodePointer): TComparerService.TInstance;
+begin
+  Result.Selector := True;
+  Result.SelectorInstance := ASelectorInstance;
+end;
+
+{ THashService }
+
+class function THashService<T>.SelectIntegerEqualityComparer(ATypeData: PTypeData; ASize: SizeInt): Pointer;
+begin
+  case ATypeData.OrdType of
+    otSByte:
+      Exit(@FEqualityComparer_Int8_Instance);
+    otUByte:
+      Exit(@FEqualityComparer_UInt8_Instance);
+    otSWord:
+      Exit(@FEqualityComparer_Int16_Instance);
+    otUWord:
+      Exit(@FEqualityComparer_UInt16_Instance);
+    otSLong:
+      Exit(@FEqualityComparer_Int32_Instance);
+    otULong:
+      Exit(@FEqualityComparer_UInt32_Instance);
+  else
+    System.Error(reRangeError);
+    Exit(nil);
+  end;
+end;
+
+class function THashService<T>.SelectFloatEqualityComparer(ATypeData: PTypeData;
+  ASize: SizeInt): Pointer;
+begin
+  case ATypeData.FloatType of
+    ftSingle:
+      Exit(@FEqualityComparer_Single_Instance);
+    ftDouble:
+      Exit(@FEqualityComparer_Double_Instance);
+    ftExtended:
+      Exit(@FEqualityComparer_Extended_Instance);
+    ftComp:
+      Exit(@FEqualityComparer_Comp_Instance);
+    ftCurr:
+      Exit(@FEqualityComparer_Currency_Instance);
+  else
+    System.Error(reRangeError);
+    Exit(nil);
+  end;
+end;
+
+class function THashService<T>.SelectShortStringEqualityComparer(
+  ATypeData: PTypeData; ASize: SizeInt): Pointer;
+begin
+  case ASize of
+    2: Exit(@FEqualityComparer_ShortString1_Instance);
+    3: Exit(@FEqualityComparer_ShortString2_Instance);
+    4: Exit(@FEqualityComparer_ShortString3_Instance);
+  else
+    Exit(@FEqualityComparer_ShortString_Instance);
+  end
+end;
+
+class function THashService<T>.SelectBinaryEqualityComparer(ATypeData: PTypeData;
+  ASize: SizeInt): Pointer;
+begin
+  case ASize of
+    1: Exit(@FEqualityComparer_UInt8_Instance);
+    2: Exit(@FEqualityComparer_UInt16_Instance);
+    4: Exit(@FEqualityComparer_UInt32_Instance);
+{$IFDEF CPU64}
+    8: Exit(@FEqualityComparer_UInt64_Instance)
+{$ENDIF}
+  else
+    Result := CreateInterface(@FEqualityComparer_Binary_VMT, ASize);
+  end;
+end;
+
+class function THashService<T>.SelectDynArrayEqualityComparer(
+  ATypeData: PTypeData; ASize: SizeInt): Pointer;
+begin
+  Result := CreateInterface(@FEqualityComparer_DynArray_VMT, ATypeData.elSize);
+end;
+
+class function THashService<T>.LookupEqualityComparer(ATypeInfo: PTypeInfo;
+  ASize: SizeInt): Pointer;
+var
+  LInstance: PInstance;
+  LSelectMethod: TSelectMethod;
+begin
+  if ATypeInfo = nil then
+    Exit(SelectBinaryEqualityComparer(GetTypeData(ATypeInfo), ASize))
+  else
+  begin
+    LInstance := @FEqualityComparerInstances[ATypeInfo.Kind];
+    Result := LInstance.Instance;
+    if LInstance.Selector then
+    begin
+      TMethod(LSelectMethod).Code := LInstance.SelectorInstance;
+      TMethod(LSelectMethod).Data := Self;
+      Result := LSelectMethod(GetTypeData(ATypeInfo), ASize);
+    end;
+  end;
+end;
+
+class constructor THashService<T>.Create;
+begin
+  FEqualityComparer_Int8_VMT          := EqualityComparer_Int8_VMT         ;
+  FEqualityComparer_Int16_VMT         := EqualityComparer_Int16_VMT        ;
+  FEqualityComparer_Int32_VMT         := EqualityComparer_Int32_VMT        ;
+  FEqualityComparer_Int64_VMT         := EqualityComparer_Int64_VMT        ;
+  FEqualityComparer_UInt8_VMT         := EqualityComparer_UInt8_VMT        ;
+  FEqualityComparer_UInt16_VMT        := EqualityComparer_UInt16_VMT       ;
+  FEqualityComparer_UInt32_VMT        := EqualityComparer_UInt32_VMT       ;
+  FEqualityComparer_UInt64_VMT        := EqualityComparer_UInt64_VMT       ;
+  FEqualityComparer_Single_VMT        := EqualityComparer_Single_VMT       ;
+  FEqualityComparer_Double_VMT        := EqualityComparer_Double_VMT       ;
+  FEqualityComparer_Extended_VMT      := EqualityComparer_Extended_VMT     ;
+  FEqualityComparer_Currency_VMT      := EqualityComparer_Currency_VMT     ;
+  FEqualityComparer_Comp_VMT          := EqualityComparer_Comp_VMT         ;
+  FEqualityComparer_Binary_VMT        := EqualityComparer_Binary_VMT       ;
+  FEqualityComparer_DynArray_VMT      := EqualityComparer_DynArray_VMT     ;
+  FEqualityComparer_Class_VMT         := EqualityComparer_Class_VMT        ;
+  FEqualityComparer_ShortString1_VMT  := EqualityComparer_ShortString1_VMT ;
+  FEqualityComparer_ShortString2_VMT  := EqualityComparer_ShortString2_VMT ;
+  FEqualityComparer_ShortString3_VMT  := EqualityComparer_ShortString3_VMT ;
+  FEqualityComparer_ShortString_VMT   := EqualityComparer_ShortString_VMT  ;
+  FEqualityComparer_AnsiString_VMT    := EqualityComparer_AnsiString_VMT   ;
+  FEqualityComparer_WideString_VMT    := EqualityComparer_WideString_VMT   ;
+  FEqualityComparer_UnicodeString_VMT := EqualityComparer_UnicodeString_VMT;
+  FEqualityComparer_Method_VMT        := EqualityComparer_Method_VMT       ;
+  FEqualityComparer_Variant_VMT       := EqualityComparer_Variant_VMT      ;
+  FEqualityComparer_Pointer_VMT       := EqualityComparer_Pointer_VMT      ;
+
+  /////
+  FEqualityComparer_Int8_VMT.__ClassRef          := THashFactoryClass(T.ClassType);
+  FEqualityComparer_Int16_VMT.__ClassRef         := THashFactoryClass(T.ClassType);
+  FEqualityComparer_Int32_VMT.__ClassRef         := THashFactoryClass(T.ClassType);
+  FEqualityComparer_Int64_VMT.__ClassRef         := THashFactoryClass(T.ClassType);
+  FEqualityComparer_UInt8_VMT.__ClassRef         := THashFactoryClass(T.ClassType);
+  FEqualityComparer_UInt16_VMT.__ClassRef        := THashFactoryClass(T.ClassType);
+  FEqualityComparer_UInt32_VMT.__ClassRef        := THashFactoryClass(T.ClassType);
+  FEqualityComparer_UInt64_VMT.__ClassRef        := THashFactoryClass(T.ClassType);
+  FEqualityComparer_Single_VMT.__ClassRef        := THashFactoryClass(T.ClassType);
+  FEqualityComparer_Double_VMT.__ClassRef        := THashFactoryClass(T.ClassType);
+  FEqualityComparer_Extended_VMT.__ClassRef      := THashFactoryClass(T.ClassType);
+  FEqualityComparer_Currency_VMT.__ClassRef      := THashFactoryClass(T.ClassType);
+  FEqualityComparer_Comp_VMT.__ClassRef          := THashFactoryClass(T.ClassType);
+  FEqualityComparer_Binary_VMT.__ClassRef        := THashFactoryClass(T.ClassType);
+  FEqualityComparer_DynArray_VMT.__ClassRef      := THashFactoryClass(T.ClassType);
+  FEqualityComparer_Class_VMT.__ClassRef         := THashFactoryClass(T.ClassType);
+  FEqualityComparer_ShortString1_VMT.__ClassRef  := THashFactoryClass(T.ClassType);
+  FEqualityComparer_ShortString2_VMT.__ClassRef  := THashFactoryClass(T.ClassType);
+  FEqualityComparer_ShortString3_VMT.__ClassRef  := THashFactoryClass(T.ClassType);
+  FEqualityComparer_ShortString_VMT.__ClassRef   := THashFactoryClass(T.ClassType);
+  FEqualityComparer_AnsiString_VMT.__ClassRef    := THashFactoryClass(T.ClassType);
+  FEqualityComparer_WideString_VMT.__ClassRef    := THashFactoryClass(T.ClassType);
+  FEqualityComparer_UnicodeString_VMT.__ClassRef := THashFactoryClass(T.ClassType);
+  FEqualityComparer_Method_VMT.__ClassRef        := THashFactoryClass(T.ClassType);
+  FEqualityComparer_Variant_VMT.__ClassRef       := THashFactoryClass(T.ClassType);
+  FEqualityComparer_Pointer_VMT.__ClassRef       := THashFactoryClass(T.ClassType);
+
+  ///////
+  FEqualityComparer_Int8_Instance          := @FEqualityComparer_Int8_VMT         ;
+  FEqualityComparer_Int16_Instance         := @FEqualityComparer_Int16_VMT        ;
+  FEqualityComparer_Int32_Instance         := @FEqualityComparer_Int32_VMT        ;
+  FEqualityComparer_Int64_Instance         := @FEqualityComparer_Int64_VMT        ;
+  FEqualityComparer_UInt8_Instance         := @FEqualityComparer_UInt8_VMT        ;
+  FEqualityComparer_UInt16_Instance        := @FEqualityComparer_UInt16_VMT       ;
+  FEqualityComparer_UInt32_Instance        := @FEqualityComparer_UInt32_VMT       ;
+  FEqualityComparer_UInt64_Instance        := @FEqualityComparer_UInt64_VMT       ;
+  FEqualityComparer_Single_Instance        := @FEqualityComparer_Single_VMT       ;
+  FEqualityComparer_Double_Instance        := @FEqualityComparer_Double_VMT       ;
+  FEqualityComparer_Extended_Instance      := @FEqualityComparer_Extended_VMT     ;
+  FEqualityComparer_Currency_Instance      := @FEqualityComparer_Currency_VMT     ;
+  FEqualityComparer_Comp_Instance          := @FEqualityComparer_Comp_VMT         ;
+  //FEqualityComparer_Binary_Instance        := @FEqualityComparer_Binary_VMT       ;  // dynamic instance
+  //FEqualityComparer_DynArray_Instance      := @FEqualityComparer_DynArray_VMT     ;  // dynamic instance
+  FEqualityComparer_ShortString1_Instance  := @FEqualityComparer_ShortString1_VMT ;
+  FEqualityComparer_ShortString2_Instance  := @FEqualityComparer_ShortString2_VMT ;
+  FEqualityComparer_ShortString3_Instance  := @FEqualityComparer_ShortString3_VMT ;
+  FEqualityComparer_ShortString_Instance   := @FEqualityComparer_ShortString_VMT  ;
+  FEqualityComparer_AnsiString_Instance    := @FEqualityComparer_AnsiString_VMT   ;
+  FEqualityComparer_WideString_Instance    := @FEqualityComparer_WideString_VMT   ;
+  FEqualityComparer_UnicodeString_Instance := @FEqualityComparer_UnicodeString_VMT;
+  FEqualityComparer_Method_Instance        := @FEqualityComparer_Method_VMT       ;
+  FEqualityComparer_Variant_Instance       := @FEqualityComparer_Variant_VMT      ;
+  FEqualityComparer_Pointer_Instance       := @FEqualityComparer_Pointer_VMT      ;
+
+  //////
+  FEqualityComparerInstances[tkUnknown]      := TInstance.CreateSelector(TMethod(TSelectMethod(THashService<T>.SelectBinaryEqualityComparer)).Code);
+  FEqualityComparerInstances[tkInteger]      := TInstance.CreateSelector(TMethod(TSelectMethod(THashService<T>.SelectIntegerEqualityComparer)).Code);
+  FEqualityComparerInstances[tkChar]         := TInstance.Create(False, @FEqualityComparer_UInt8_Instance);
+  FEqualityComparerInstances[tkEnumeration]  := TInstance.CreateSelector(TMethod(TSelectMethod(THashService<T>.SelectIntegerEqualityComparer)).Code);
+  FEqualityComparerInstances[tkFloat]        := TInstance.CreateSelector(TMethod(TSelectMethod(THashService<T>.SelectFloatEqualityComparer)).Code);
+  FEqualityComparerInstances[tkSet]          := TInstance.CreateSelector(TMethod(TSelectMethod(THashService<T>.SelectBinaryEqualityComparer)).Code);
+  FEqualityComparerInstances[tkMethod]       := TInstance.Create(False, @FEqualityComparer_Method_Instance);
+  FEqualityComparerInstances[tkSString]      := TInstance.CreateSelector(TMethod(TSelectMethod(THashService<T>.SelectShortStringEqualityComparer)).Code);
+  FEqualityComparerInstances[tkLString]      := TInstance.Create(False, @FEqualityComparer_AnsiString_Instance);
+  FEqualityComparerInstances[tkAString]      := TInstance.Create(False, @FEqualityComparer_AnsiString_Instance);
+  FEqualityComparerInstances[tkWString]      := TInstance.Create(False, @FEqualityComparer_WideString_Instance);
+  FEqualityComparerInstances[tkVariant]      := TInstance.Create(False, @FEqualityComparer_Variant_Instance);
+  FEqualityComparerInstances[tkArray]        := TInstance.CreateSelector(TMethod(TSelectMethod(THashService<T>.SelectBinaryEqualityComparer)).Code);
+  FEqualityComparerInstances[tkRecord]       := TInstance.CreateSelector(TMethod(TSelectMethod(THashService<T>.SelectBinaryEqualityComparer)).Code);
+  FEqualityComparerInstances[tkInterface]    := TInstance.Create(False, @FEqualityComparer_Pointer_Instance);
+  FEqualityComparerInstances[tkClass]        := TInstance.Create(False, @FEqualityComparer_Pointer_Instance);
+  FEqualityComparerInstances[tkObject]       := TInstance.CreateSelector(TMethod(TSelectMethod(THashService<T>.SelectBinaryEqualityComparer)).Code);
+  FEqualityComparerInstances[tkWChar]        := TInstance.Create(False, @FEqualityComparer_UInt16_Instance);
+  FEqualityComparerInstances[tkBool]         := TInstance.CreateSelector(TMethod(TSelectMethod(THashService<T>.SelectIntegerEqualityComparer)).Code);
+  FEqualityComparerInstances[tkInt64]        := TInstance.Create(False, @FEqualityComparer_Int64_Instance);
+  FEqualityComparerInstances[tkQWord]        := TInstance.Create(False, @FEqualityComparer_UInt64_Instance);
+  FEqualityComparerInstances[tkDynArray]     := TInstance.CreateSelector(TMethod(TSelectMethod(THashService<T>.SelectDynArrayEqualityComparer)).Code);
+  FEqualityComparerInstances[tkInterfaceRaw] := TInstance.Create(False, @FEqualityComparer_Pointer_Instance);
+  FEqualityComparerInstances[tkProcVar]      := TInstance.Create(False, @FEqualityComparer_Pointer_Instance);
+  FEqualityComparerInstances[tkUString]      := TInstance.Create(False, @FEqualityComparer_UnicodeString_Instance);
+  FEqualityComparerInstances[tkUChar]        := TInstance.Create(False, @FEqualityComparer_UInt16_Instance);
+  FEqualityComparerInstances[tkHelper]       := TInstance.Create(False, @FEqualityComparer_Pointer_Instance);
+  FEqualityComparerInstances[tkFile]         := TInstance.CreateSelector(TMethod(TSelectMethod(THashService<T>.SelectBinaryEqualityComparer)).Code);
+  FEqualityComparerInstances[tkClassRef]     := TInstance.Create(False, @FEqualityComparer_Pointer_Instance);
+  FEqualityComparerInstances[tkPointer]      := TInstance.Create(False, @FEqualityComparer_Pointer_Instance)
+end;
+
+{ TExtendedHashService }
+
+class function TExtendedHashService<T>.SelectIntegerEqualityComparer(ATypeData: PTypeData; ASize: SizeInt): Pointer;
+begin
+  case ATypeData.OrdType of
+    otSByte:
+      Exit(@FExtendedEqualityComparer_Int8_Instance);
+    otUByte:
+      Exit(@FExtendedEqualityComparer_UInt8_Instance);
+    otSWord:
+      Exit(@FExtendedEqualityComparer_Int16_Instance);
+    otUWord:
+      Exit(@FExtendedEqualityComparer_UInt16_Instance);
+    otSLong:
+      Exit(@FExtendedEqualityComparer_Int32_Instance);
+    otULong:
+      Exit(@FExtendedEqualityComparer_UInt32_Instance);
+  else
+    System.Error(reRangeError);
+    Exit(nil);
+  end;
+end;
+
+class function TExtendedHashService<T>.SelectFloatEqualityComparer(ATypeData: PTypeData; ASize: SizeInt): Pointer;
+begin
+  case ATypeData.FloatType of
+    ftSingle:
+      Exit(@FExtendedEqualityComparer_Single_Instance);
+    ftDouble:
+      Exit(@FExtendedEqualityComparer_Double_Instance);
+    ftExtended:
+      Exit(@FExtendedEqualityComparer_Extended_Instance);
+    ftComp:
+      Exit(@FExtendedEqualityComparer_Comp_Instance);
+    ftCurr:
+      Exit(@FExtendedEqualityComparer_Currency_Instance);
+  else
+    System.Error(reRangeError);
+    Exit(nil);
+  end;
+end;
+
+class function TExtendedHashService<T>.SelectShortStringEqualityComparer(ATypeData: PTypeData;
+  ASize: SizeInt): Pointer;
+begin
+  case ASize of
+    2: Exit(@FExtendedEqualityComparer_ShortString1_Instance);
+    3: Exit(@FExtendedEqualityComparer_ShortString2_Instance);
+    4: Exit(@FExtendedEqualityComparer_ShortString3_Instance);
+  else
+    Exit(@FExtendedEqualityComparer_ShortString_Instance);
+  end
+end;
+
+class function TExtendedHashService<T>.SelectBinaryEqualityComparer(ATypeData: PTypeData;
+  ASize: SizeInt): Pointer;
+begin
+  case ASize of
+    1: Exit(@FExtendedEqualityComparer_UInt8_Instance);
+    2: Exit(@FExtendedEqualityComparer_UInt16_Instance);
+    4: Exit(@FExtendedEqualityComparer_UInt32_Instance);
+{$IFDEF CPU64}
+    8: Exit(@FExtendedEqualityComparer_UInt64_Instance)
+{$ENDIF}
+  else
+    Result := CreateInterface(@FExtendedEqualityComparer_Binary_VMT, ASize);
+  end;
+end;
+
+class function TExtendedHashService<T>.SelectDynArrayEqualityComparer(
+  ATypeData: PTypeData; ASize: SizeInt): Pointer;
+begin
+  Result := CreateInterface(@FExtendedEqualityComparer_DynArray_VMT, ATypeData.elSize);
+end;
+
+class function TExtendedHashService<T>.LookupExtendedEqualityComparer(
+  ATypeInfo: PTypeInfo; ASize: SizeInt): Pointer;
+var
+  LInstance: PInstance;
+  LSelectMethod: TSelectMethod;
+begin
+  if ATypeInfo = nil then
+    Exit(SelectBinaryEqualityComparer(GetTypeData(ATypeInfo), ASize))
+  else
+  begin
+    LInstance := @FExtendedEqualityComparerInstances[ATypeInfo.Kind];
+    Result := LInstance.Instance;
+    if LInstance.Selector then
+    begin
+      TMethod(LSelectMethod).Code := LInstance.SelectorInstance;
+      TMethod(LSelectMethod).Data := Self;
+      Result := LSelectMethod(GetTypeData(ATypeInfo), ASize);
+    end;
+  end;
+end;
+
+class constructor TExtendedHashService<T>.Create;
+begin
+  FExtendedEqualityComparer_Int8_VMT          := ExtendedEqualityComparer_Int8_VMT         ;
+  FExtendedEqualityComparer_Int16_VMT         := ExtendedEqualityComparer_Int16_VMT        ;
+  FExtendedEqualityComparer_Int32_VMT         := ExtendedEqualityComparer_Int32_VMT        ;
+  FExtendedEqualityComparer_Int64_VMT         := ExtendedEqualityComparer_Int64_VMT        ;
+  FExtendedEqualityComparer_UInt8_VMT         := ExtendedEqualityComparer_UInt8_VMT        ;
+  FExtendedEqualityComparer_UInt16_VMT        := ExtendedEqualityComparer_UInt16_VMT       ;
+  FExtendedEqualityComparer_UInt32_VMT        := ExtendedEqualityComparer_UInt32_VMT       ;
+  FExtendedEqualityComparer_UInt64_VMT        := ExtendedEqualityComparer_UInt64_VMT       ;
+  FExtendedEqualityComparer_Single_VMT        := ExtendedEqualityComparer_Single_VMT       ;
+  FExtendedEqualityComparer_Double_VMT        := ExtendedEqualityComparer_Double_VMT       ;
+  FExtendedEqualityComparer_Extended_VMT      := ExtendedEqualityComparer_Extended_VMT     ;
+  FExtendedEqualityComparer_Currency_VMT      := ExtendedEqualityComparer_Currency_VMT     ;
+  FExtendedEqualityComparer_Comp_VMT          := ExtendedEqualityComparer_Comp_VMT         ;
+  FExtendedEqualityComparer_Binary_VMT        := ExtendedEqualityComparer_Binary_VMT       ;
+  FExtendedEqualityComparer_DynArray_VMT      := ExtendedEqualityComparer_DynArray_VMT     ;
+  FExtendedEqualityComparer_Class_VMT         := ExtendedEqualityComparer_Class_VMT        ;
+  FExtendedEqualityComparer_ShortString1_VMT  := ExtendedEqualityComparer_ShortString1_VMT ;
+  FExtendedEqualityComparer_ShortString2_VMT  := ExtendedEqualityComparer_ShortString2_VMT ;
+  FExtendedEqualityComparer_ShortString3_VMT  := ExtendedEqualityComparer_ShortString3_VMT ;
+  FExtendedEqualityComparer_ShortString_VMT   := ExtendedEqualityComparer_ShortString_VMT  ;
+  FExtendedEqualityComparer_AnsiString_VMT    := ExtendedEqualityComparer_AnsiString_VMT   ;
+  FExtendedEqualityComparer_WideString_VMT    := ExtendedEqualityComparer_WideString_VMT   ;
+  FExtendedEqualityComparer_UnicodeString_VMT := ExtendedEqualityComparer_UnicodeString_VMT;
+  FExtendedEqualityComparer_Method_VMT        := ExtendedEqualityComparer_Method_VMT       ;
+  FExtendedEqualityComparer_Variant_VMT       := ExtendedEqualityComparer_Variant_VMT      ;
+  FExtendedEqualityComparer_Pointer_VMT       := ExtendedEqualityComparer_Pointer_VMT      ;
+
+  /////
+  FExtendedEqualityComparer_Int8_VMT.__ClassRef          := TExtendedHashFactoryClass(T.ClassType);
+  FExtendedEqualityComparer_Int16_VMT.__ClassRef         := TExtendedHashFactoryClass(T.ClassType);
+  FExtendedEqualityComparer_Int32_VMT.__ClassRef         := TExtendedHashFactoryClass(T.ClassType);
+  FExtendedEqualityComparer_Int64_VMT.__ClassRef         := TExtendedHashFactoryClass(T.ClassType);
+  FExtendedEqualityComparer_UInt8_VMT.__ClassRef         := TExtendedHashFactoryClass(T.ClassType);
+  FExtendedEqualityComparer_UInt16_VMT.__ClassRef        := TExtendedHashFactoryClass(T.ClassType);
+  FExtendedEqualityComparer_UInt32_VMT.__ClassRef        := TExtendedHashFactoryClass(T.ClassType);
+  FExtendedEqualityComparer_UInt64_VMT.__ClassRef        := TExtendedHashFactoryClass(T.ClassType);
+  FExtendedEqualityComparer_Single_VMT.__ClassRef        := TExtendedHashFactoryClass(T.ClassType);
+  FExtendedEqualityComparer_Double_VMT.__ClassRef        := TExtendedHashFactoryClass(T.ClassType);
+  FExtendedEqualityComparer_Extended_VMT.__ClassRef      := TExtendedHashFactoryClass(T.ClassType);
+  FExtendedEqualityComparer_Currency_VMT.__ClassRef      := TExtendedHashFactoryClass(T.ClassType);
+  FExtendedEqualityComparer_Comp_VMT.__ClassRef          := TExtendedHashFactoryClass(T.ClassType);
+  FExtendedEqualityComparer_Binary_VMT.__ClassRef        := TExtendedHashFactoryClass(T.ClassType);
+  FExtendedEqualityComparer_DynArray_VMT.__ClassRef      := TExtendedHashFactoryClass(T.ClassType);
+  FExtendedEqualityComparer_Class_VMT.__ClassRef         := TExtendedHashFactoryClass(T.ClassType);
+  FExtendedEqualityComparer_ShortString1_VMT.__ClassRef  := TExtendedHashFactoryClass(T.ClassType);
+  FExtendedEqualityComparer_ShortString2_VMT.__ClassRef  := TExtendedHashFactoryClass(T.ClassType);
+  FExtendedEqualityComparer_ShortString3_VMT.__ClassRef  := TExtendedHashFactoryClass(T.ClassType);
+  FExtendedEqualityComparer_ShortString_VMT.__ClassRef   := TExtendedHashFactoryClass(T.ClassType);
+  FExtendedEqualityComparer_AnsiString_VMT.__ClassRef    := TExtendedHashFactoryClass(T.ClassType);
+  FExtendedEqualityComparer_WideString_VMT.__ClassRef    := TExtendedHashFactoryClass(T.ClassType);
+  FExtendedEqualityComparer_UnicodeString_VMT.__ClassRef := TExtendedHashFactoryClass(T.ClassType);
+  FExtendedEqualityComparer_Method_VMT.__ClassRef        := TExtendedHashFactoryClass(T.ClassType);
+  FExtendedEqualityComparer_Variant_VMT.__ClassRef       := TExtendedHashFactoryClass(T.ClassType);
+  FExtendedEqualityComparer_Pointer_VMT.__ClassRef       := TExtendedHashFactoryClass(T.ClassType);
+
+  ///////
+  FExtendedEqualityComparer_Int8_Instance          := @FExtendedEqualityComparer_Int8_VMT         ;
+  FExtendedEqualityComparer_Int16_Instance         := @FExtendedEqualityComparer_Int16_VMT        ;
+  FExtendedEqualityComparer_Int32_Instance         := @FExtendedEqualityComparer_Int32_VMT        ;
+  FExtendedEqualityComparer_Int64_Instance         := @FExtendedEqualityComparer_Int64_VMT        ;
+  FExtendedEqualityComparer_UInt8_Instance         := @FExtendedEqualityComparer_UInt8_VMT        ;
+  FExtendedEqualityComparer_UInt16_Instance        := @FExtendedEqualityComparer_UInt16_VMT       ;
+  FExtendedEqualityComparer_UInt32_Instance        := @FExtendedEqualityComparer_UInt32_VMT       ;
+  FExtendedEqualityComparer_UInt64_Instance        := @FExtendedEqualityComparer_UInt64_VMT       ;
+  FExtendedEqualityComparer_Single_Instance        := @FExtendedEqualityComparer_Single_VMT       ;
+  FExtendedEqualityComparer_Double_Instance        := @FExtendedEqualityComparer_Double_VMT       ;
+  FExtendedEqualityComparer_Extended_Instance      := @FExtendedEqualityComparer_Extended_VMT     ;
+  FExtendedEqualityComparer_Currency_Instance      := @FExtendedEqualityComparer_Currency_VMT     ;
+  FExtendedEqualityComparer_Comp_Instance          := @FExtendedEqualityComparer_Comp_VMT         ;
+  //FExtendedEqualityComparer_Binary_Instance        := @FExtendedEqualityComparer_Binary_VMT       ;  // dynamic instance
+  //FExtendedEqualityComparer_DynArray_Instance      := @FExtendedEqualityComparer_DynArray_VMT     ;  // dynamic instance
+  FExtendedEqualityComparer_ShortString1_Instance  := @FExtendedEqualityComparer_ShortString1_VMT ;
+  FExtendedEqualityComparer_ShortString2_Instance  := @FExtendedEqualityComparer_ShortString2_VMT ;
+  FExtendedEqualityComparer_ShortString3_Instance  := @FExtendedEqualityComparer_ShortString3_VMT ;
+  FExtendedEqualityComparer_ShortString_Instance   := @FExtendedEqualityComparer_ShortString_VMT  ;
+  FExtendedEqualityComparer_AnsiString_Instance    := @FExtendedEqualityComparer_AnsiString_VMT   ;
+  FExtendedEqualityComparer_WideString_Instance    := @FExtendedEqualityComparer_WideString_VMT   ;
+  FExtendedEqualityComparer_UnicodeString_Instance := @FExtendedEqualityComparer_UnicodeString_VMT;
+  FExtendedEqualityComparer_Method_Instance        := @FExtendedEqualityComparer_Method_VMT       ;
+  FExtendedEqualityComparer_Variant_Instance       := @FExtendedEqualityComparer_Variant_VMT      ;
+  FExtendedEqualityComparer_Pointer_Instance       := @FExtendedEqualityComparer_Pointer_VMT      ;
+
+  //////
+  FExtendedEqualityComparerInstances[tkUnknown]      := TInstance.CreateSelector(TMethod(TSelectMethod(TExtendedHashService<T>.SelectBinaryEqualityComparer)).Code);
+  FExtendedEqualityComparerInstances[tkInteger]      := TInstance.CreateSelector(TMethod(TSelectMethod(TExtendedHashService<T>.SelectIntegerEqualityComparer)).Code);
+  FExtendedEqualityComparerInstances[tkChar]         := TInstance.Create(False, @FExtendedEqualityComparer_UInt8_Instance);
+  FExtendedEqualityComparerInstances[tkEnumeration]  := TInstance.CreateSelector(TMethod(TSelectMethod(TExtendedHashService<T>.SelectIntegerEqualityComparer)).Code);
+  FExtendedEqualityComparerInstances[tkFloat]        := TInstance.CreateSelector(TMethod(TSelectMethod(TExtendedHashService<T>.SelectFloatEqualityComparer)).Code);
+  FExtendedEqualityComparerInstances[tkSet]          := TInstance.CreateSelector(TMethod(TSelectMethod(TExtendedHashService<T>.SelectBinaryEqualityComparer)).Code);
+  FExtendedEqualityComparerInstances[tkMethod]       := TInstance.Create(False, @FExtendedEqualityComparer_Method_Instance);
+  FExtendedEqualityComparerInstances[tkSString]      := TInstance.CreateSelector(TMethod(TSelectMethod(TExtendedHashService<T>.SelectShortStringEqualityComparer)).Code);
+  FExtendedEqualityComparerInstances[tkLString]      := TInstance.Create(False, @FExtendedEqualityComparer_AnsiString_Instance);
+  FExtendedEqualityComparerInstances[tkAString]      := TInstance.Create(False, @FExtendedEqualityComparer_AnsiString_Instance);
+  FExtendedEqualityComparerInstances[tkWString]      := TInstance.Create(False, @FExtendedEqualityComparer_WideString_Instance);
+  FExtendedEqualityComparerInstances[tkVariant]      := TInstance.Create(False, @FExtendedEqualityComparer_Variant_Instance);
+  FExtendedEqualityComparerInstances[tkArray]        := TInstance.CreateSelector(TMethod(TSelectMethod(TExtendedHashService<T>.SelectBinaryEqualityComparer)).Code);
+  FExtendedEqualityComparerInstances[tkRecord]       := TInstance.CreateSelector(TMethod(TSelectMethod(TExtendedHashService<T>.SelectBinaryEqualityComparer)).Code);
+  FExtendedEqualityComparerInstances[tkInterface]    := TInstance.Create(False, @FExtendedEqualityComparer_Pointer_Instance);
+  FExtendedEqualityComparerInstances[tkClass]        := TInstance.Create(False, @FExtendedEqualityComparer_Pointer_Instance);
+  FExtendedEqualityComparerInstances[tkObject]       := TInstance.CreateSelector(TMethod(TSelectMethod(TExtendedHashService<T>.SelectBinaryEqualityComparer)).Code);
+  FExtendedEqualityComparerInstances[tkWChar]        := TInstance.Create(False, @FExtendedEqualityComparer_UInt16_Instance);
+  FExtendedEqualityComparerInstances[tkBool]         := TInstance.CreateSelector(TMethod(TSelectMethod(TExtendedHashService<T>.SelectIntegerEqualityComparer)).Code);
+  FExtendedEqualityComparerInstances[tkInt64]        := TInstance.Create(False, @FExtendedEqualityComparer_Int64_Instance);
+  FExtendedEqualityComparerInstances[tkQWord]        := TInstance.Create(False, @FExtendedEqualityComparer_UInt64_Instance);
+  FExtendedEqualityComparerInstances[tkDynArray]     := TInstance.CreateSelector(TMethod(TSelectMethod(TExtendedHashService<T>.SelectDynArrayEqualityComparer)).Code);
+  FExtendedEqualityComparerInstances[tkInterfaceRaw] := TInstance.Create(False, @FExtendedEqualityComparer_Pointer_Instance);
+  FExtendedEqualityComparerInstances[tkProcVar]      := TInstance.Create(False, @FExtendedEqualityComparer_Pointer_Instance);
+  FExtendedEqualityComparerInstances[tkUString]      := TInstance.Create(False, @FExtendedEqualityComparer_UnicodeString_Instance);
+  FExtendedEqualityComparerInstances[tkUChar]        := TInstance.Create(False, @FExtendedEqualityComparer_UInt16_Instance);
+  FExtendedEqualityComparerInstances[tkHelper]       := TInstance.Create(False, @FExtendedEqualityComparer_Pointer_Instance);
+  FExtendedEqualityComparerInstances[tkFile]         := TInstance.CreateSelector(TMethod(TSelectMethod(TExtendedHashService<T>.SelectBinaryEqualityComparer)).Code);
+  FExtendedEqualityComparerInstances[tkClassRef]     := TInstance.Create(False, @FExtendedEqualityComparer_Pointer_Instance);
+  FExtendedEqualityComparerInstances[tkPointer]      := TInstance.Create(False, @FExtendedEqualityComparer_Pointer_Instance);
+end;
+
+{ TEqualityComparer<T> }
+
+class function TEqualityComparer<T>.Default: IEqualityComparer<T>;
+begin
+  Result := _LookupVtableInfo(giEqualityComparer, TypeInfo(T), SizeOf(T));
+end;
+
+class function TEqualityComparer<T>.Default(AHashFactoryClass: THashFactoryClass): IEqualityComparer<T>;
+begin
+  if AHashFactoryClass.InheritsFrom(TExtendedHashFactory) then
+    Result := _LookupVtableInfoEx(giExtendedEqualityComparer, TypeInfo(T), SizeOf(T), AHashFactoryClass)
+  else if AHashFactoryClass.InheritsFrom(THashFactory) then
+    Result := _LookupVtableInfoEx(giEqualityComparer, TypeInfo(T), SizeOf(T), AHashFactoryClass);
+end;
+
+class function  TEqualityComparer<T>.Construct(const AEqualityComparison: TOnEqualityComparison<T>;
+  const AHasher: TOnHasher<T>): IEqualityComparer<T>;
+begin
+  Result := TDelegatedEqualityComparerEvents<T>.Create(AEqualityComparison, AHasher);
+end;
+
+class function  TEqualityComparer<T>.Construct(const AEqualityComparison: TEqualityComparisonFunc<T>;
+  const AHasher: THasherFunc<T>): IEqualityComparer<T>;
+begin
+  Result := TDelegatedEqualityComparerFunc<T>.Create(AEqualityComparison, AHasher);
+end;
+
+{ TDelegatedEqualityComparerEvents<T> }
+
+function TDelegatedEqualityComparerEvents<T>.Equals(constref ALeft, ARight: T): Boolean;
+begin
+  Result := FEqualityComparison(ALeft, ARight);
+end;
+
+function TDelegatedEqualityComparerEvents<T>.GetHashCode(constref AValue: T): UInt32;
+begin
+  Result := FHasher(AValue);
+end;
+
+constructor TDelegatedEqualityComparerEvents<T>.Create(const AEqualityComparison: TOnEqualityComparison<T>;
+  const AHasher: TOnHasher<T>);
+begin
+  FEqualityComparison := AEqualityComparison;
+  FHasher := AHasher;
+end;
+
+{ TDelegatedEqualityComparerFunc<T> }
+
+function TDelegatedEqualityComparerFunc<T>.Equals(constref ALeft, ARight: T): Boolean;
+begin
+  Result := FEqualityComparison(ALeft, ARight);
+end;
+
+function TDelegatedEqualityComparerFunc<T>.GetHashCode(constref AValue: T): UInt32;
+begin
+  Result := FHasher(AValue);
+end;
+
+constructor TDelegatedEqualityComparerFunc<T>.Create(const AEqualityComparison: TEqualityComparisonFunc<T>;
+  const AHasher: THasherFunc<T>);
+begin
+  FEqualityComparison := AEqualityComparison;
+  FHasher := AHasher;
+end;
+
+{ TDelegatedExtendedEqualityComparerEvents<T> }
+
+function TDelegatedExtendedEqualityComparerEvents<T>.GetHashCodeMethod(constref AValue: T): UInt32;
+var
+  LHashList: array[0..1] of Int32;
+  LHashListParams: array[0..3] of Int16 absolute LHashList;
+begin
+  LHashListParams[0] := -1;
+  FExtendedHasher(AValue, @LHashList[0]);
+  Result := LHashList[1];
+end;
+
+function TDelegatedExtendedEqualityComparerEvents<T>.Equals(constref ALeft, ARight: T): Boolean;
+begin
+  Result := FEqualityComparison(ALeft, ARight);
+end;
+
+function TDelegatedExtendedEqualityComparerEvents<T>.GetHashCode(constref AValue: T): UInt32;
+begin
+  Result := FHasher(AValue);
+end;
+
+procedure TDelegatedExtendedEqualityComparerEvents<T>.GetHashList(constref AValue: T; AHashList: PUInt32);
+begin
+  FExtendedHasher(AValue, AHashList);
+end;
+
+constructor TDelegatedExtendedEqualityComparerEvents<T>.Create(const AEqualityComparison: TOnEqualityComparison<T>;
+  const AHasher: TOnHasher<T>; const AExtendedHasher: TOnExtendedHasher<T>);
+begin
+  FEqualityComparison := AEqualityComparison;
+  FHasher := AHasher;
+  FExtendedHasher := AExtendedHasher;
+end;
+
+constructor TDelegatedExtendedEqualityComparerEvents<T>.Create(const AEqualityComparison: TOnEqualityComparison<T>;
+  const AExtendedHasher: TOnExtendedHasher<T>);
+begin
+  Create(AEqualityComparison, GetHashCodeMethod, AExtendedHasher);
+end;
+
+{ TDelegatedExtendedEqualityComparerFunc<T> }
+
+function TDelegatedExtendedEqualityComparerFunc<T>.Equals(constref ALeft, ARight: T): Boolean;
+begin
+  Result := FEqualityComparison(ALeft, ARight);
+end;
+
+function TDelegatedExtendedEqualityComparerFunc<T>.GetHashCode(constref AValue: T): UInt32;
+var
+  LHashList: array[0..1] of Int32;
+  LHashListParams: array[0..3] of Int16 absolute LHashList;
+begin
+  if not Assigned(FHasher) then
+  begin
+    LHashListParams[0] := -1;
+    FExtendedHasher(AValue, @LHashList[0]);
+    Result := LHashList[1];
+  end
+  else
+    Result := FHasher(AValue);
+end;
+
+procedure TDelegatedExtendedEqualityComparerFunc<T>.GetHashList(constref AValue: T; AHashList: PUInt32);
+begin
+  FExtendedHasher(AValue, AHashList);
+end;
+
+constructor TDelegatedExtendedEqualityComparerFunc<T>.Create(const AEqualityComparison: TEqualityComparisonFunc<T>;
+  const AHasher: THasherFunc<T>; const AExtendedHasher: TExtendedHasherFunc<T>);
+begin
+  FEqualityComparison := AEqualityComparison;
+  FHasher := AHasher;
+  FExtendedHasher := AExtendedHasher;
+end;
+
+constructor TDelegatedExtendedEqualityComparerFunc<T>.Create(const AEqualityComparison: TEqualityComparisonFunc<T>;
+  const AExtendedHasher: TExtendedHasherFunc<T>);
+begin
+  Create(AEqualityComparison, nil, AExtendedHasher);
+end;
+
+{ TExtendedEqualityComparer<T> }
+
+class function TExtendedEqualityComparer<T>.Default: IExtendedEqualityComparer<T>;
+begin
+  Result := _LookupVtableInfo(giExtendedEqualityComparer, TypeInfo(T), SizeOf(T));
+end;
+
+class function TExtendedEqualityComparer<T>.Default(
+  AExtenedHashFactoryClass: TExtendedHashFactoryClass
+  ): IExtendedEqualityComparer<T>;
+begin
+  Result := _LookupVtableInfoEx(giExtendedEqualityComparer, TypeInfo(T), SizeOf(T), AExtenedHashFactoryClass);
+end;
+
+class function TExtendedEqualityComparer<T>.Construct(
+  const AEqualityComparison: TOnEqualityComparison<T>; const AHasher: TOnHasher<T>;
+  const AExtendedHasher: TOnExtendedHasher<T>): IExtendedEqualityComparer<T>;
+begin
+  Result := TDelegatedExtendedEqualityComparerEvents<T>.Create(AEqualityComparison, AHasher, AExtendedHasher);
+end;
+
+class function TExtendedEqualityComparer<T>.Construct(
+  const AEqualityComparison: TEqualityComparisonFunc<T>; const AHasher: THasherFunc<T>;
+  const AExtendedHasher: TExtendedHasherFunc<T>): IExtendedEqualityComparer<T>;
+begin
+  Result := TDelegatedExtendedEqualityComparerFunc<T>.Create(AEqualityComparison, AHasher, AExtendedHasher);
+end;
+
+class function TExtendedEqualityComparer<T>.Construct(
+  const AEqualityComparison: TOnEqualityComparison<T>;
+  const AExtendedHasher: TOnExtendedHasher<T>): IExtendedEqualityComparer<T>;
+begin
+  Result := TDelegatedExtendedEqualityComparerEvents<T>.Create(AEqualityComparison, AExtendedHasher);
+end;
+
+class function TExtendedEqualityComparer<T>.Construct(
+  const AEqualityComparison: TEqualityComparisonFunc<T>;
+  const AExtendedHasher: TExtendedHasherFunc<T>): IExtendedEqualityComparer<T>;
+begin
+  Result := TDelegatedExtendedEqualityComparerFunc<T>.Create(AEqualityComparison, AExtendedHasher);
+end;
+
+{ TDelphiHashFactory }
+
+class function TDelphiHashFactory.GetHashService: THashServiceClass;
+begin
+  Result := THashService<TDelphiHashFactory>;
+end;
+
+class function TDelphiHashFactory.GetHashCode(AKey: Pointer; ASize: SizeInt; AInitVal: UInt32): UInt32;
+begin
+  Result := DelphiHashLittle(AKey, ASize, AInitVal);
+end;
+
+{ TmORMotHashFactory }
+
+class function TmORMotHashFactory.GetHashService: THashServiceClass;
+begin
+  Result := THashService<TmORMotHashFactory>;
+end;
+
+class function TmORMotHashFactory.GetHashCode(AKey: Pointer; ASize: SizeInt; AInitVal: UInt32): UInt32;
+begin
+  Result := mORMotHasher(AInitVal, AKey, ASize);
+end;
+
+{ TAdler32HashFactory }
+
+class function TAdler32HashFactory.GetHashService: THashServiceClass;
+begin
+  Result := THashService<TAdler32HashFactory>;
+end;
+
+class function TAdler32HashFactory.GetHashCode(AKey: Pointer; ASize: SizeInt;
+  AInitVal: UInt32): UInt32;
+begin
+  Result := Adler32(AKey, ASize);
+end;
+
+{ TSdbmHashFactory }
+
+class function TSdbmHashFactory.GetHashService: THashServiceClass;
+begin
+  Result := THashService<TSdbmHashFactory>;
+end;
+
+class function TSdbmHashFactory.GetHashCode(AKey: Pointer; ASize: SizeInt;
+  AInitVal: UInt32): UInt32;
+begin
+  Result := sdbm(AKey, ASize);
+end;
+
+{ TSimpleChecksumFactory }
+
+class function TSimpleChecksumFactory.GetHashService: THashServiceClass;
+begin
+  Result := THashService<TSimpleChecksumFactory>;
+end;
+
+class function TSimpleChecksumFactory.GetHashCode(AKey: Pointer; ASize: SizeInt;
+  AInitVal: UInt32): UInt32;
+begin
+  Result := SimpleChecksumHash(AKey, ASize);
+end;
+
+{ TDelphiDoubleHashFactory }
+
+class function TDelphiDoubleHashFactory.GetHashService: THashServiceClass;
+begin
+  Result := TExtendedHashService<TDelphiDoubleHashFactory>;
+end;
+
+class function TDelphiDoubleHashFactory.GetHashCode(AKey: Pointer; ASize: SizeInt; AInitVal: UInt32): UInt32;
+begin
+  Result := DelphiHashLittle(AKey, ASize, AInitVal);
+end;
+
+class procedure TDelphiDoubleHashFactory.GetHashList(AKey: Pointer; ASize: SizeInt; AHashList: PUInt32;
+        AOptions: TGetHashListOptions);
+var
+  LHash: UInt32;
+  AHashListParams: PUInt16 absolute AHashList;
+begin
+{$WARNINGS OFF}
+  case AHashListParams[0] of
+    -2:
+      begin
+        if not (ghloHashListAsInitData in AOptions) then
+          AHashList[1] := 0;
+        LHash := 0;
+        DelphiHashLittle2(AKey, ASize, LHash, AHashList[1]);
+        Exit;
+      end;
+    -1:
+      begin
+        if not (ghloHashListAsInitData in AOptions) then
+          AHashList[1] := 0;
+        LHash := 0;
+        DelphiHashLittle2(AKey, ASize, AHashList[1], LHash);
+        Exit;
+      end;
+    0: Exit;
+    1:
+      begin
+        if not (ghloHashListAsInitData in AOptions) then
+          AHashList[1] := 0;
+        LHash := 0;
+        DelphiHashLittle2(AKey, ASize, AHashList[1], LHash);
+        Exit;
+      end;
+    2:
+      begin
+        if not (ghloHashListAsInitData in AOptions) then
+        begin
+          AHashList[1] := 0;
+          AHashList[2] := 0;
+        end;
+        DelphiHashLittle2(AKey, ASize, AHashList[1], AHashList[2]);
+        Exit;
+      end;
+  else
+    raise EArgumentOutOfRangeException.CreateRes(@SArgumentOutOfRange);
+  end;
+{.$WARNINGS ON} // do not enable warnings ever in this unit, or you will get many warnings about uninitialized TEqualityComparerVMT fields
+end;
+
+{ TDelphiQuadrupleHashFactory }
+
+class function TDelphiQuadrupleHashFactory.GetHashService: THashServiceClass;
+begin
+  Result := TExtendedHashService<TDelphiQuadrupleHashFactory>;
+end;
+
+class function TDelphiQuadrupleHashFactory.GetHashCode(AKey: Pointer; ASize: SizeInt; AInitVal: UInt32): UInt32;
+begin
+  Result := DelphiHashLittle(AKey, ASize, AInitVal);
+end;
+
+class procedure TDelphiQuadrupleHashFactory.GetHashList(AKey: Pointer; ASize: SizeInt; AHashList: PUInt32;
+        AOptions: TGetHashListOptions);
+var
+  LHash: UInt32;
+  AHashListParams: PInt16 absolute AHashList;
+begin
+  case AHashListParams[0] of
+    -4:
+      begin
+        if not (ghloHashListAsInitData in AOptions) then
+          AHashList[1] := 1988;
+        LHash := 2004;
+        DelphiHashLittle2(AKey, ASize, LHash, AHashList[1]);
+        Exit;
+      end;
+    -3:
+      begin
+        if not (ghloHashListAsInitData in AOptions) then
+          AHashList[1] := 2004;
+        LHash := 1988;
+        DelphiHashLittle2(AKey, ASize, AHashList[1], LHash);
+        Exit;
+      end;
+    -2:
+      begin
+        if not (ghloHashListAsInitData in AOptions) then
+          AHashList[1] := 0;
+        LHash := 0;
+        DelphiHashLittle2(AKey, ASize, LHash, AHashList[1]);
+        Exit;
+      end;
+    -1:
+      begin
+        if not (ghloHashListAsInitData in AOptions) then
+          AHashList[1] := 0;
+        LHash := 0;
+        DelphiHashLittle2(AKey, ASize, AHashList[1], LHash);
+        Exit;
+      end;
+    0: Exit;
+    1:
+      begin
+        if not (ghloHashListAsInitData in AOptions) then
+          AHashList[1] := 0;
+        LHash := 0;
+        DelphiHashLittle2(AKey, ASize, AHashList[1], LHash);
+        Exit;
+      end;
+    2:
+      begin
+        case AHashListParams[1] of
+          0, 1:
+            begin
+              if not (ghloHashListAsInitData in AOptions) then
+              begin
+                AHashList[1] := 0;
+                AHashList[2] := 0;
+              end;
+              DelphiHashLittle2(AKey, ASize, AHashList[1], AHashList[2]);
+              Exit;
+            end;
+          2:
+            begin
+              if not (ghloHashListAsInitData in AOptions) then
+              begin
+                AHashList[1] := 2004;
+                AHashList[2] := 1988;
+              end;
+              DelphiHashLittle2(AKey, ASize, AHashList[1], AHashList[2]);
+              Exit;
+            end;
+        else
+          raise EArgumentOutOfRangeException.CreateRes(@SArgumentOutOfRange);
+        end;
+      end;
+    4:
+      case AHashListParams[1] of
+        1:
+          begin
+            if not (ghloHashListAsInitData in AOptions) then
+            begin
+              AHashList[1] := 0;
+              AHashList[2] := 0;
+            end;
+            DelphiHashLittle2(AKey, ASize, AHashList[1], AHashList[2]);
+            Exit;
+          end;
+        2:
+          begin
+            if not (ghloHashListAsInitData in AOptions) then
+            begin
+              AHashList[3] := 2004;
+              AHashList[4] := 1988;
+            end;
+            DelphiHashLittle2(AKey, ASize, AHashList[3], AHashList[4]);
+            Exit;
+          end;
+      else
+        raise EArgumentOutOfRangeException.CreateRes(@SArgumentOutOfRange);
+      end;
+  else
+    raise EArgumentOutOfRangeException.CreateRes(@SArgumentOutOfRange);
+  end;
+end;
+
+{ TDelphiSixfoldHashFactory }
+
+class function TDelphiSixfoldHashFactory.GetHashService: THashServiceClass;
+begin
+  Result := TExtendedHashService<TDelphiSixfoldHashFactory>;
+end;
+
+class function TDelphiSixfoldHashFactory.GetHashCode(AKey: Pointer; ASize: SizeInt; AInitVal: UInt32): UInt32;
+begin
+  Result := DelphiHashLittle(AKey, ASize, AInitVal);
+end;
+
+class procedure TDelphiSixfoldHashFactory.GetHashList(AKey: Pointer; ASize: SizeInt; AHashList: PUInt32;
+        AOptions: TGetHashListOptions);
+var
+  LHash: UInt32;
+  AHashListParams: PInt16 absolute AHashList;
+begin
+  case AHashListParams[0] of
+    -6:
+      begin
+        if not (ghloHashListAsInitData in AOptions) then
+          AHashList[1] := 2;
+        LHash := 1;
+        DelphiHashLittle2(AKey, ASize, LHash, AHashList[1]);
+        Exit;
+      end;
+    -5:
+      begin
+        if not (ghloHashListAsInitData in AOptions) then
+          AHashList[1] := 1;
+        LHash := 2;
+        DelphiHashLittle2(AKey, ASize, AHashList[1], LHash);
+        Exit;
+      end;
+    -4:
+      begin
+        if not (ghloHashListAsInitData in AOptions) then
+          AHashList[1] := 1988;
+        LHash := 2004;
+        DelphiHashLittle2(AKey, ASize, LHash, AHashList[1]);
+        Exit;
+      end;
+    -3:
+      begin
+        if not (ghloHashListAsInitData in AOptions) then
+          AHashList[1] := 2004;
+        LHash := 1988;
+        DelphiHashLittle2(AKey, ASize, AHashList[1], LHash);
+        Exit;
+      end;
+    -2:
+      begin
+        if not (ghloHashListAsInitData in AOptions) then
+          AHashList[1] := 0;
+        LHash := 0;
+        DelphiHashLittle2(AKey, ASize, LHash, AHashList[1]);
+        Exit;
+      end;
+    -1:
+      begin
+        if not (ghloHashListAsInitData in AOptions) then
+          AHashList[1] := 0;
+        LHash := 0;
+        DelphiHashLittle2(AKey, ASize, AHashList[1], LHash);
+        Exit;
+      end;
+    0: Exit;
+    1:
+      begin
+        if not (ghloHashListAsInitData in AOptions) then
+          AHashList[1] := 0;
+        LHash := 0;
+        DelphiHashLittle2(AKey, ASize, AHashList[1], LHash);
+        Exit;
+      end;
+    2:
+      begin
+        case AHashListParams[1] of
+          0, 1:
+            begin
+              if not (ghloHashListAsInitData in AOptions) then
+              begin
+                AHashList[1] := 0;
+                AHashList[2] := 0;
+              end;
+              DelphiHashLittle2(AKey, ASize, AHashList[1], AHashList[2]);
+              Exit;
+            end;
+          2:
+            begin
+              if not (ghloHashListAsInitData in AOptions) then
+              begin
+                AHashList[1] := 2004;
+                AHashList[2] := 1988;
+              end;
+              DelphiHashLittle2(AKey, ASize, AHashList[1], AHashList[2]);
+              Exit;
+            end;
+        else
+          raise EArgumentOutOfRangeException.CreateRes(@SArgumentOutOfRange);
+        end;
+      end;
+    6:
+      case AHashListParams[1] of
+        1:
+          begin
+            if not (ghloHashListAsInitData in AOptions) then
+            begin
+              AHashList[1] := 0;
+              AHashList[2] := 0;
+            end;
+            DelphiHashLittle2(AKey, ASize, AHashList[1], AHashList[2]);
+            Exit;
+          end;
+        2:
+          begin
+            if not (ghloHashListAsInitData in AOptions) then
+            begin
+              AHashList[3] := 2004;
+              AHashList[4] := 1988;
+            end;
+            DelphiHashLittle2(AKey, ASize, AHashList[3], AHashList[4]);
+            Exit;
+          end;
+        3:
+          begin
+            if not (ghloHashListAsInitData in AOptions) then
+            begin
+              AHashList[5] := 1;
+              AHashList[6] := 2;
+            end;
+            DelphiHashLittle2(AKey, ASize, AHashList[5], AHashList[6]);
+            Exit;
+          end;
+      else
+        raise EArgumentOutOfRangeException.CreateRes(@SArgumentOutOfRange);
+      end;
+  else
+    raise EArgumentOutOfRangeException.CreateRes(@SArgumentOutOfRange);
+  end;
+end;
+
+{ TOrdinalComparer<T, THashFactory> }
+
+class constructor TOrdinalComparer<T, THashFactory>.Create;
+begin
+  if THashFactory.InheritsFrom(TExtendedHashService) then
+  begin
+    FExtendedEqualityComparer := TExtendedEqualityComparer<T>.Default(TExtendedHashFactoryClass(THashFactory));
+    FEqualityComparer := IEqualityComparer<T>(FExtendedEqualityComparer);
+  end
+  else
+    FEqualityComparer := TEqualityComparer<T>.Default(THashFactory);
+  FComparer := TComparer<T>.Default;
+end;
+
+{ TGStringComparer<T, THashFactory> }
+
+class destructor TGStringComparer<T, THashFactory>.Destroy;
+begin
+  if Assigned(FOrdinal) then
+    FOrdinal.Free;
+end;
+
+class function TGStringComparer<T, THashFactory>.Ordinal: TCustomComparer<T>;
+begin
+  if not Assigned(FOrdinal) then
+    FOrdinal := TGOrdinalStringComparer<T, THashFactory>.Create;
+  Result := FOrdinal;
+end;
+
+{ TGOrdinalStringComparer<T, THashFactory> }
+
+function TGOrdinalStringComparer<T, THashFactory>.Compare(constref ALeft, ARight: T): Integer;
+begin
+  Result := FComparer.Compare(ALeft, ARight);
+end;
+
+function TGOrdinalStringComparer<T, THashFactory>.Equals(constref ALeft, ARight: T): Boolean;
+begin
+  Result := FEqualityComparer.Equals(ALeft, ARight);
+end;
+
+function TGOrdinalStringComparer<T, THashFactory>.GetHashCode(constref AValue: T): UInt32;
+begin
+  Result := FEqualityComparer.GetHashCode(AValue);
+end;
+
+procedure TGOrdinalStringComparer<T, THashFactory>.GetHashList(constref AValue: T; AHashList: PUInt32);
+begin
+  FExtendedEqualityComparer.GetHashList(AValue, AHashList);
+end;
+
+{ TGIStringComparer<T, THashFactory> }
+
+class destructor TGIStringComparer<T, THashFactory>.Destroy;
+begin
+  if Assigned(FOrdinal) then
+    FOrdinal.Free;
+end;
+
+class function TGIStringComparer<T, THashFactory>.Ordinal: TCustomComparer<T>;
+begin
+  if not Assigned(FOrdinal) then
+    FOrdinal := TGOrdinalIStringComparer<T, THashFactory>.Create;
+  Result := FOrdinal;
+end;
+
+{ TGOrdinalIStringComparer<T, THashFactory> }
+
+function TGOrdinalIStringComparer<T, THashFactory>.Compare(constref ALeft, ARight: T): Integer;
+begin
+  Result := FComparer.Compare(ALeft.ToLower, ARight.ToLower);
+end;
+
+function TGOrdinalIStringComparer<T, THashFactory>.Equals(constref ALeft, ARight: T): Boolean;
+begin
+  Result := FEqualityComparer.Equals(ALeft.ToLower, ARight.ToLower);
+end;
+
+function TGOrdinalIStringComparer<T, THashFactory>.GetHashCode(constref AValue: T): UInt32;
+begin
+  Result := FEqualityComparer.GetHashCode(AValue.ToLower);
+end;
+
+procedure TGOrdinalIStringComparer<T, THashFactory>.GetHashList(constref AValue: T; AHashList: PUInt32);
+begin
+  FExtendedEqualityComparer.GetHashList(AValue.ToLower, AHashList);
+end;
+
+function BobJenkinsHash(const AData; ALength, AInitData: Integer): Integer;
+begin
+  Result := DelphiHashLittle(@AData, ALength, AInitData);
+end;
+
+function BinaryCompare(const ALeft, ARight: Pointer; ASize: PtrUInt): Integer;
+begin
+  Result := CompareMemRange(ALeft, ARight, ASize);
+end;
+
+function _LookupVtableInfo(AGInterface: TDefaultGenericInterface; ATypeInfo: PTypeInfo; ASize: SizeInt): Pointer;
+begin
+  Result := _LookupVtableInfoEx(AGInterface, ATypeInfo, ASize, nil);
+end;
+
+function _LookupVtableInfoEx(AGInterface: TDefaultGenericInterface; ATypeInfo: PTypeInfo; ASize: SizeInt;
+  AFactory: THashFactoryClass): Pointer;
+begin
+  case AGInterface of
+    giComparer:
+        Exit(
+          TComparerService.LookupComparer(ATypeInfo, ASize));
+    giEqualityComparer:
+      begin
+        if AFactory = nil then
+          AFactory := TDefaultHashFactory;
+
+        Exit(
+          AFactory.GetHashService.LookupEqualityComparer(ATypeInfo, ASize));
+      end;
+    giExtendedEqualityComparer:
+      begin
+        if AFactory = nil then
+          AFactory := TDelphiDoubleHashFactory;
+
+        Exit(
+          TExtendedHashServiceClass(AFactory.GetHashService).LookupExtendedEqualityComparer(ATypeInfo, ASize));
+      end;
+  else
+    System.Error(reRangeError);
+    Exit(nil);
+  end;
+end;
+
+end.
+

+ 1589 - 0
CryptoLib/src/3rdParty/generics.collections/src/generics.hashes.pas

@@ -0,0 +1,1589 @@
+{
+    This file is part of the Free Pascal run time library.
+    Copyright (c) 2014 by Maciej Izak (hnb)
+    member of the Free Sparta development team (http://freesparta.com)
+
+    Copyright(c) 2004-2014 DaThoX
+
+    It contains the Free Pascal generics library
+
+    See the file COPYING.FPC, included in this distribution,
+    for details about the copyright.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+
+    Acknowledgment
+
+    Thanks to Sphere 10 Software (http://sphere10.com) for sponsoring
+    many new types and major refactoring of entire library.
+
+    Thanks to mORMot (http://synopse.info) project for the best implementations
+    of hashing functions like crc32c and xxHash32 :)
+
+ **********************************************************************}
+
+unit Generics.Hashes;
+
+{$MODE DELPHI}{$H+}
+{$POINTERMATH ON}
+{$MACRO ON}
+{$COPERATORS ON}
+{$OVERFLOWCHECKS OFF}
+{$RANGECHECKS OFF}
+
+interface
+
+uses
+  Classes, SysUtils;
+
+// Original version of Bob Jenkins Hash
+// http://burtleburtle.net/bob/c/lookup3.c
+function HashWord(
+  AKey: PLongWord;                   //* the key, an array of uint32_t values */
+  ALength: SizeInt;                  //* the length of the key, in uint32_ts */
+  AInitVal: UInt32): UInt32;         //* the previous hash, or an arbitrary value */
+procedure HashWord2 (
+  AKey: PLongWord;                   //* the key, an array of uint32_t values */
+  ALength: SizeInt;                  //* the length of the key, in uint32_ts */
+  var APrimaryHashAndInitVal: UInt32;                  //* IN: seed OUT: primary hash value */
+  var ASecondaryHashAndInitVal: UInt32);               //* IN: more seed OUT: secondary hash value */
+
+function HashLittle(AKey: Pointer; ALength: SizeInt; AInitVal: UInt32): UInt32;
+procedure HashLittle2(
+  AKey: Pointer;        //* the key to hash */
+  ALength: SizeInt;     //* length of the key */
+  var APrimaryHashAndInitVal: UInt32;                  //* IN: primary initval, OUT: primary hash */
+  var ASecondaryHashAndInitVal: UInt32);               //* IN: secondary initval, OUT: secondary hash */
+
+function DelphiHashLittle(AKey: Pointer; ALength: SizeInt; AInitVal: UInt32): Int32;
+procedure DelphiHashLittle2(AKey: Pointer; ALength: SizeInt; var APrimaryHashAndInitVal, ASecondaryHashAndInitVal: UInt32);
+
+// hash function from fstl
+function SimpleChecksumHash(AKey: Pointer; ALength: SizeInt): UInt32;
+
+// some other hashes
+// http://stackoverflow.com/questions/14409466/simple-hash-functions
+// http://www.partow.net/programming/hashfunctions/
+// http://en.wikipedia.org/wiki/List_of_hash_functions
+// http://www.cse.yorku.ca/~oz/hash.html
+
+// https://code.google.com/p/hedgewars/source/browse/hedgewars/adler32.pas
+function Adler32(AKey: Pointer; ALength: SizeInt): UInt32;
+function sdbm(AKey: Pointer; ALength: SizeInt): UInt32;
+function xxHash32(crc: cardinal; P: Pointer; len: integer): cardinal;
+
+type
+  THasher = function(crc: cardinal; buf: Pointer; len: cardinal): cardinal;
+
+var
+  crc32c: THasher;
+  mORMotHasher: THasher;
+
+implementation
+
+function SimpleChecksumHash(AKey: Pointer; ALength: SizeInt): UInt32;
+var
+  i: Integer;
+  ABuffer: PUInt8 absolute AKey;
+begin
+  Result := 0;
+  for i := 0 to ALength - 1 do
+     Inc(Result,ABuffer[i]);
+end;
+
+function Adler32(AKey: Pointer; ALength: SizeInt): UInt32;
+const
+  MOD_ADLER = 65521;
+var
+  ABuffer: PUInt8 absolute AKey;
+  a: UInt32 = 1;
+  b: UInt32 = 0;
+  n: Integer;
+begin
+  for n := 0 to ALength -1 do
+  begin
+    a := (a + ABuffer[n]) mod MOD_ADLER;
+    b := (b + a) mod MOD_ADLER;
+  end;
+  Result := (b shl 16) or a;
+end;
+
+function sdbm(AKey: Pointer; ALength: SizeInt): UInt32;
+var
+  c: PUInt8 absolute AKey;
+  i: Integer;
+begin
+  Result := 0;
+  c := AKey;
+  for i := 0 to ALength - 1 do
+  begin
+    Result := c^ + (Result shl 6) + (Result shl 16) {%H-}- Result;
+    Inc(c);
+  end;
+end;
+
+{ BobJenkinsHash }
+
+{$define mix_abc :=
+  a -= c;  a := a xor (((c)shl(4)) or ((c)shr(32-(4))));  c += b;
+  b -= a;  b := b xor (((a)shl(6)) or ((a)shr(32-(6))));  a += c;
+  c -= b;  c := c xor (((b)shl(8)) or ((b)shr(32-(8))));  b += a;
+  a -= c;  a := a xor (((c)shl(16)) or ((c)shr(32-(16))));  c += b;
+  b -= a;  b := b xor (((a)shl(19)) or ((a)shr(32-(19))));  a += c;
+  c -= b;  c := c xor (((b)shl(4)) or ((b)shr(32-(4))));  b += a
+}
+
+{$define final_abc :=
+  c := c xor b; c -= (((b)shl(14)) or ((b)shr(32-(14))));
+  a := a xor c; a -= (((c)shl(11)) or ((c)shr(32-(11))));
+  b := b xor a; b -= (((a)shl(25)) or ((a)shr(32-(25))));
+  c := c xor b; c -= (((b)shl(16)) or ((b)shr(32-(16))));
+  a := a xor c; a -= (((c)shl(4)) or ((c)shr(32-(4))));
+  b := b xor a; b -= (((a)shl(14)) or ((a)shr(32-(14))));
+  c := c xor b; c -= (((b)shl(24)) or ((b)shr(32-(24))))
+}
+
+function HashWord(
+  AKey: PLongWord;                   //* the key, an array of uint32_t values */
+  ALength: SizeInt;               //* the length of the key, in uint32_ts */
+  AInitVal: UInt32): UInt32;         //* the previous hash, or an arbitrary value */
+var
+  a,b,c: UInt32;
+label
+  Case0, Case1, Case2, Case3;
+begin
+  //* Set up the internal state */
+  a := $DEADBEEF + (UInt32(ALength) shl 2) + AInitVal;
+  b := a;
+  c := b;
+
+  //*------------------------------------------------- handle most of the key */
+  while ALength > 3 do
+  begin
+    a += AKey[0];
+    b += AKey[1];
+    c += AKey[2];
+    mix_abc;
+    ALength -= 3;
+    AKey += 3;
+  end;
+
+  //*------------------------------------------- handle the last 3 uint32_t's */
+  case ALength of //* all the case statements fall through */
+    3: goto Case3;
+    2: goto Case2;
+    1: goto Case1;
+    0: goto Case0;
+  end;
+  Case3: c+=AKey[2];
+  Case2: b+=AKey[1];
+  Case1: a+=AKey[0];
+    final_abc;
+  Case0:     //* case 0: nothing left to add */
+  //*------------------------------------------------------ report the result */
+  Result := c;
+end;
+
+procedure HashWord2 (
+AKey: PLongWord;                   //* the key, an array of uint32_t values */
+ALength: SizeInt;               //* the length of the key, in uint32_ts */
+var APrimaryHashAndInitVal: UInt32;                      //* IN: seed OUT: primary hash value */
+var ASecondaryHashAndInitVal: UInt32);               //* IN: more seed OUT: secondary hash value */
+var
+  a,b,c: UInt32;
+label
+  Case0, Case1, Case2, Case3;
+begin
+  //* Set up the internal state */
+  a := $deadbeef + (UInt32(ALength shl 2)) + APrimaryHashAndInitVal;
+  b := a;
+  c := b;
+  c += ASecondaryHashAndInitVal;
+
+  //*------------------------------------------------- handle most of the key */
+  while ALength > 3 do
+  begin
+    a += AKey[0];
+    b += AKey[1];
+    c += AKey[2];
+    mix_abc;
+    ALength -= 3;
+    AKey += 3;
+  end;
+
+  //*------------------------------------------- handle the last 3 uint32_t's */
+  case ALength of                     //* all the case statements fall through */
+    3: goto Case3;
+    2: goto Case2;
+    1: goto Case1;
+    0: goto Case0;
+  end;
+  Case3: c+=AKey[2];
+  Case2: b+=AKey[1];
+  Case1: a+=AKey[0];
+    final_abc;
+  Case0:     //* case 0: nothing left to add */
+  //*------------------------------------------------------ report the result */
+  APrimaryHashAndInitVal := c;
+  ASecondaryHashAndInitVal := b;
+end;
+
+function HashLittle(AKey: Pointer; ALength: SizeInt; AInitVal: UInt32): UInt32;
+var
+  a, b, c: UInt32;
+  u: record case byte of
+    0: (ptr: Pointer);
+    1: (i: PtrUint);
+  end absolute AKey;
+
+  k32: ^UInt32 absolute AKey;
+  k16: ^UInt16 absolute AKey;
+  k8: ^UInt8 absolute AKey;
+
+label _10, _8, _6, _4, _2;
+label Case12, Case11, Case10, Case9, Case8, Case7, Case6, Case5, Case4, Case3, Case2, Case1;
+
+begin
+  a := $DEADBEEF + UInt32(ALength) + AInitVal;
+  b := a;
+  c := b;
+
+{$IFDEF ENDIAN_LITTLE}
+  if (u.i and $3) = 0 then
+  begin
+    while (ALength > 12) do
+    begin
+      a += k32[0];
+      b += k32[1];
+      c += k32[2];
+      mix_abc;
+      ALength -= 12;
+      k32 += 3;
+    end;
+
+    case ALength of
+      12: begin c += k32[2]; b += k32[1]; a += k32[0]; end;
+      11: begin c += k32[2] and $ffffff; b += k32[1]; a += k32[0]; end;
+      10: begin c += k32[2] and $ffff; b += k32[1]; a += k32[0]; end;
+      9 : begin c += k32[2] and $ff; b += k32[1]; a += k32[0]; end;
+      8 : begin b += k32[1]; a += k32[0]; end;
+      7 : begin b += k32[1] and $ffffff; a += k32[0]; end;
+      6 : begin b += k32[1] and $ffff; a += k32[0]; end;
+      5 : begin b += k32[1] and $ff; a += k32[0]; end;
+      4 : begin a += k32[0]; end;
+      3 : begin a += k32[0] and $ffffff; end;
+      2 : begin a += k32[0] and $ffff; end;
+      1 : begin a += k32[0] and $ff; end;
+      0 : Exit(c);              // zero length strings require no mixing
+    end
+  end
+  else
+  if (u.i and $1) = 0 then
+  begin
+    while (ALength > 12) do
+    begin
+      a += k16[0] + (UInt32(k16[1]) shl 16);
+      b += k16[2] + (UInt32(k16[3]) shl 16);
+      c += k16[4] + (UInt32(k16[5]) shl 16);
+      mix_abc;
+      ALength -= 12;
+      k16 += 6;
+    end;
+
+    case ALength of
+      12:
+        begin
+          c+=k16[4]+((UInt32(k16[5])) shl 16);
+          b+=k16[2]+((UInt32(k16[3])) shl 16);
+          a+=k16[0]+((UInt32(k16[1])) shl 16);
+        end;
+      11:
+        begin
+          c+=(UInt32(k8[10])) shl 16;     //* fall through */
+          goto _10;
+        end;
+      10:
+        begin _10:
+          c+=k16[4];
+          b+=k16[2]+((UInt32(k16[3])) shl 16);
+          a+=k16[0]+((UInt32(k16[1])) shl 16);
+        end;
+      9 :
+        begin
+          c+=k8[8];                      //* fall through */
+          goto _8;
+        end;
+      8 :
+        begin _8:
+          b+=k16[2]+((UInt32(k16[3])) shl 16);
+          a+=k16[0]+((UInt32(k16[1])) shl 16);
+        end;
+      7 :
+        begin
+          b+=(UInt32(k8[6])) shl 16;      //* fall through */
+          goto _6;
+        end;
+      6 :
+        begin _6:
+          b+=k16[2];
+          a+=k16[0]+((UInt32(k16[1])) shl 16);
+        end;
+      5 :
+        begin
+          b+=k8[4];                      //* fall through */
+          goto _4;
+        end;
+      4 :
+        begin _4:
+          a+=k16[0]+((UInt32(k16[1])) shl 16);
+        end;
+      3 :
+        begin
+          a+=(UInt32(k8[2])) shl 16;      //* fall through */
+          goto _2;
+        end;
+      2 :
+        begin _2:
+          a+=k16[0];
+        end;
+      1 :
+        begin
+          a+=k8[0];
+        end;
+      0 : Exit(c);                     //* zero length requires no mixing */
+    end;
+  end
+  else
+{$ENDIF}
+  begin
+    while ALength > 12 do
+    begin
+      a += k8[0];
+      a += (UInt32(k8[1])) shl 8;
+      a += (UInt32(k8[2])) shl 16;
+      a += (UInt32(k8[3])) shl 24;
+      b += k8[4];
+      b += (UInt32(k8[5])) shl 8;
+      b += (UInt32(k8[6])) shl 16;
+      b += (UInt32(k8[7])) shl 24;
+      c += k8[8];
+      c += (UInt32(k8[9])) shl 8;
+      c += (UInt32(k8[10])) shl 16;
+      c += (UInt32(k8[11])) shl 24;
+      mix_abc;
+      ALength -= 12;
+      k8 += 12;
+    end;
+
+    case ALength of
+      12: goto Case12;
+      11: goto Case11;
+      10: goto Case10;
+      9 : goto Case9;
+      8 : goto Case8;
+      7 : goto Case7;
+      6 : goto Case6;
+      5 : goto Case5;
+      4 : goto Case4;
+      3 : goto Case3;
+      2 : goto Case2;
+      1 : goto Case1;
+      0 : Exit(c);
+    end;
+
+    Case12: c+=(UInt32(k8[11])) shl 24;
+    Case11: c+=(UInt32(k8[10])) shl 16;
+    Case10: c+=(UInt32(k8[9])) shl 8;
+    Case9: c+=k8[8];
+    Case8: b+=(UInt32(k8[7])) shl 24;
+    Case7: b+=(UInt32(k8[6])) shl 16;
+    Case6: b+=(UInt32(k8[5])) shl 8;
+    Case5: b+=k8[4];
+    Case4: a+=(UInt32(k8[3])) shl 24;
+    Case3: a+=(UInt32(k8[2])) shl 16;
+    Case2: a+=(UInt32(k8[1])) shl 8;
+    Case1: a+=k8[0];
+  end;
+
+  final_abc;
+  Result := c;
+end;
+
+(*
+ * hashlittle2: return 2 32-bit hash values
+ *
+ * This is identical to hashlittle(), except it returns two 32-bit hash
+ * values instead of just one.  This is good enough for hash table
+ * lookup with 2^^64 buckets, or if you want a second hash if you're not
+ * happy with the first, or if you want a probably-unique 64-bit ID for
+ * the key.  *pc is better mixed than *pb, so use *pc first.  If you want
+ * a 64-bit value do something like "*pc + (((uint64_t)*pb)<<32)".
+ *)
+procedure HashLittle2(
+  AKey: Pointer;        //* the key to hash */
+  ALength: SizeInt;    //* length of the key */
+  var APrimaryHashAndInitVal: UInt32;                      //* IN: primary initval, OUT: primary hash */
+  var ASecondaryHashAndInitVal: UInt32);               //* IN: secondary initval, OUT: secondary hash */
+var
+  a,b,c: UInt32;
+  u: record case byte of
+    0: (ptr: Pointer);
+    1: (i: PtrUint);
+  end absolute AKey;
+
+  k32: ^UInt32 absolute AKey;
+  k16: ^UInt16 absolute AKey;
+  k8: ^UInt8 absolute AKey;
+
+label _10, _8, _6, _4, _2;
+label Case12, Case11, Case10, Case9, Case8, Case7, Case6, Case5, Case4, Case3, Case2, Case1;
+
+begin
+  //* Set up the internal state */
+  a := $DEADBEEF + UInt32(ALength) + APrimaryHashAndInitVal;
+  b := a;
+  c := b;
+  c += ASecondaryHashAndInitVal;
+
+{$IFDEF ENDIAN_LITTLE}
+  if (u.i and $3) = 0 then
+  begin
+    while (ALength > 12) do
+    begin
+      a += k32[0];
+      b += k32[1];
+      c += k32[2];
+      mix_abc;
+      ALength -= 12;
+      k32 += 3;
+    end;
+
+    case ALength of
+      12: begin c += k32[2]; b += k32[1]; a += k32[0]; end;
+      11: begin c += k32[2] and $ffffff; b += k32[1]; a += k32[0]; end;
+      10: begin c += k32[2] and $ffff; b += k32[1]; a += k32[0]; end;
+      9 : begin c += k32[2] and $ff; b += k32[1]; a += k32[0]; end;
+      8 : begin b += k32[1]; a += k32[0]; end;
+      7 : begin b += k32[1] and $ffffff; a += k32[0]; end;
+      6 : begin b += k32[1] and $ffff; a += k32[0]; end;
+      5 : begin b += k32[1] and $ff; a += k32[0]; end;
+      4 : begin a += k32[0]; end;
+      3 : begin a += k32[0] and $ffffff; end;
+      2 : begin a += k32[0] and $ffff; end;
+      1 : begin a += k32[0] and $ff; end;
+      0 :
+        begin
+          APrimaryHashAndInitVal := c;
+          ASecondaryHashAndInitVal := b;
+          Exit;              // zero length strings require no mixing
+        end;
+    end
+  end
+  else
+  if (u.i and $1) = 0 then
+  begin
+    while (ALength > 12) do
+    begin
+      a += k16[0] + (UInt32(k16[1]) shl 16);
+      b += k16[2] + (UInt32(k16[3]) shl 16);
+      c += k16[4] + (UInt32(k16[5]) shl 16);
+      mix_abc;
+      ALength -= 12;
+      k16 += 6;
+    end;
+
+    case ALength of
+      12:
+        begin
+          c+=k16[4]+((UInt32(k16[5])) shl 16);
+          b+=k16[2]+((UInt32(k16[3])) shl 16);
+          a+=k16[0]+((UInt32(k16[1])) shl 16);
+        end;
+      11:
+        begin
+          c+=(UInt32(k8[10])) shl 16;     //* fall through */
+          goto _10;
+        end;
+      10:
+        begin _10:
+          c+=k16[4];
+          b+=k16[2]+((UInt32(k16[3])) shl 16);
+          a+=k16[0]+((UInt32(k16[1])) shl 16);
+        end;
+      9 :
+        begin
+          c+=k8[8];                      //* fall through */
+          goto _8;
+        end;
+      8 :
+        begin _8:
+          b+=k16[2]+((UInt32(k16[3])) shl 16);
+          a+=k16[0]+((UInt32(k16[1])) shl 16);
+        end;
+      7 :
+        begin
+          b+=(UInt32(k8[6])) shl 16;      //* fall through */
+          goto _6;
+        end;
+      6 :
+        begin _6:
+          b+=k16[2];
+          a+=k16[0]+((UInt32(k16[1])) shl 16);
+        end;
+      5 :
+        begin
+          b+=k8[4];                      //* fall through */
+          goto _4;
+        end;
+      4 :
+        begin _4:
+          a+=k16[0]+((UInt32(k16[1])) shl 16);
+        end;
+      3 :
+        begin
+          a+=(UInt32(k8[2])) shl 16;      //* fall through */
+          goto _2;
+        end;
+      2 :
+        begin _2:
+          a+=k16[0];
+        end;
+      1 :
+        begin
+          a+=k8[0];
+        end;
+      0 :
+        begin
+          APrimaryHashAndInitVal := c;
+          ASecondaryHashAndInitVal := b;
+          Exit;              // zero length strings require no mixing
+        end;
+    end;
+  end
+  else
+{$ENDIF}
+  begin
+    while ALength > 12 do
+    begin
+      a += k8[0];
+      a += (UInt32(k8[1])) shl 8;
+      a += (UInt32(k8[2])) shl 16;
+      a += (UInt32(k8[3])) shl 24;
+      b += k8[4];
+      b += (UInt32(k8[5])) shl 8;
+      b += (UInt32(k8[6])) shl 16;
+      b += (UInt32(k8[7])) shl 24;
+      c += k8[8];
+      c += (UInt32(k8[9])) shl 8;
+      c += (UInt32(k8[10])) shl 16;
+      c += (UInt32(k8[11])) shl 24;
+      mix_abc;
+      ALength -= 12;
+      k8 += 12;
+    end;
+
+    case ALength of
+      12: goto Case12;
+      11: goto Case11;
+      10: goto Case10;
+      9 : goto Case9;
+      8 : goto Case8;
+      7 : goto Case7;
+      6 : goto Case6;
+      5 : goto Case5;
+      4 : goto Case4;
+      3 : goto Case3;
+      2 : goto Case2;
+      1 : goto Case1;
+      0 :
+        begin
+          APrimaryHashAndInitVal := c;
+          ASecondaryHashAndInitVal := b;
+          Exit;              // zero length strings require no mixing
+        end;
+    end;
+
+    Case12: c+=(UInt32(k8[11])) shl 24;
+    Case11: c+=(UInt32(k8[10])) shl 16;
+    Case10: c+=(UInt32(k8[9])) shl 8;
+    Case9: c+=k8[8];
+    Case8: b+=(UInt32(k8[7])) shl 24;
+    Case7: b+=(UInt32(k8[6])) shl 16;
+    Case6: b+=(UInt32(k8[5])) shl 8;
+    Case5: b+=k8[4];
+    Case4: a+=(UInt32(k8[3])) shl 24;
+    Case3: a+=(UInt32(k8[2])) shl 16;
+    Case2: a+=(UInt32(k8[1])) shl 8;
+    Case1: a+=k8[0];
+  end;
+
+  final_abc;
+  APrimaryHashAndInitVal := c;
+  ASecondaryHashAndInitVal := b;
+end;
+
+procedure DelphiHashLittle2(AKey: Pointer; ALength: SizeInt; var APrimaryHashAndInitVal, ASecondaryHashAndInitVal: UInt32);
+var
+  a,b,c: UInt32;
+  u: record case byte of
+    0: (ptr: Pointer);
+    1: (i: PtrUint);
+  end absolute AKey;
+
+  k32: ^UInt32 absolute AKey;
+  k16: ^UInt16 absolute AKey;
+  k8: ^UInt8 absolute AKey;
+
+label _10, _8, _6, _4, _2;
+label Case12, Case11, Case10, Case9, Case8, Case7, Case6, Case5, Case4, Case3, Case2, Case1;
+
+begin
+  //* Set up the internal state */
+  a := $DEADBEEF + UInt32(ALength shl 2) + APrimaryHashAndInitVal; // delphi version bug? original version don't have "shl 2"
+  b := a;
+  c := b;
+  c += ASecondaryHashAndInitVal;
+
+{$IFDEF ENDIAN_LITTLE}
+  if (u.i and $3) = 0 then
+  begin
+    while (ALength > 12) do
+    begin
+      a += k32[0];
+      b += k32[1];
+      c += k32[2];
+      mix_abc;
+      ALength -= 12;
+      k32 += 3;
+    end;
+
+    case ALength of
+      12: begin c += k32[2]; b += k32[1]; a += k32[0]; end;
+      11: begin c += k32[2] and $ffffff; b += k32[1]; a += k32[0]; end;
+      10: begin c += k32[2] and $ffff; b += k32[1]; a += k32[0]; end;
+      9 : begin c += k32[2] and $ff; b += k32[1]; a += k32[0]; end;
+      8 : begin b += k32[1]; a += k32[0]; end;
+      7 : begin b += k32[1] and $ffffff; a += k32[0]; end;
+      6 : begin b += k32[1] and $ffff; a += k32[0]; end;
+      5 : begin b += k32[1] and $ff; a += k32[0]; end;
+      4 : begin a += k32[0]; end;
+      3 : begin a += k32[0] and $ffffff; end;
+      2 : begin a += k32[0] and $ffff; end;
+      1 : begin a += k32[0] and $ff; end;
+      0 :
+        begin
+          APrimaryHashAndInitVal := c;
+          ASecondaryHashAndInitVal := b;
+          Exit;              // zero length strings require no mixing
+        end;
+    end
+  end
+  else
+  if (u.i and $1) = 0 then
+  begin
+    while (ALength > 12) do
+    begin
+      a += k16[0] + (UInt32(k16[1]) shl 16);
+      b += k16[2] + (UInt32(k16[3]) shl 16);
+      c += k16[4] + (UInt32(k16[5]) shl 16);
+      mix_abc;
+      ALength -= 12;
+      k16 += 6;
+    end;
+
+    case ALength of
+      12:
+        begin
+          c+=k16[4]+((UInt32(k16[5])) shl 16);
+          b+=k16[2]+((UInt32(k16[3])) shl 16);
+          a+=k16[0]+((UInt32(k16[1])) shl 16);
+        end;
+      11:
+        begin
+          c+=(UInt32(k8[10])) shl 16;     //* fall through */
+          goto _10;
+        end;
+      10:
+        begin _10:
+          c+=k16[4];
+          b+=k16[2]+((UInt32(k16[3])) shl 16);
+          a+=k16[0]+((UInt32(k16[1])) shl 16);
+        end;
+      9 :
+        begin
+          c+=k8[8];                      //* fall through */
+          goto _8;
+        end;
+      8 :
+        begin _8:
+          b+=k16[2]+((UInt32(k16[3])) shl 16);
+          a+=k16[0]+((UInt32(k16[1])) shl 16);
+        end;
+      7 :
+        begin
+          b+=(UInt32(k8[6])) shl 16;      //* fall through */
+          goto _6;
+        end;
+      6 :
+        begin _6:
+          b+=k16[2];
+          a+=k16[0]+((UInt32(k16[1])) shl 16);
+        end;
+      5 :
+        begin
+          b+=k8[4];                      //* fall through */
+          goto _4;
+        end;
+      4 :
+        begin _4:
+          a+=k16[0]+((UInt32(k16[1])) shl 16);
+        end;
+      3 :
+        begin
+          a+=(UInt32(k8[2])) shl 16;      //* fall through */
+          goto _2;
+        end;
+      2 :
+        begin _2:
+          a+=k16[0];
+        end;
+      1 :
+        begin
+          a+=k8[0];
+        end;
+      0 :
+        begin
+          APrimaryHashAndInitVal := c;
+          ASecondaryHashAndInitVal := b;
+          Exit;              // zero length strings require no mixing
+        end;
+    end;
+  end
+  else
+{$ENDIF}
+  begin
+    while ALength > 12 do
+    begin
+      a += k8[0];
+      a += (UInt32(k8[1])) shl 8;
+      a += (UInt32(k8[2])) shl 16;
+      a += (UInt32(k8[3])) shl 24;
+      b += k8[4];
+      b += (UInt32(k8[5])) shl 8;
+      b += (UInt32(k8[6])) shl 16;
+      b += (UInt32(k8[7])) shl 24;
+      c += k8[8];
+      c += (UInt32(k8[9])) shl 8;
+      c += (UInt32(k8[10])) shl 16;
+      c += (UInt32(k8[11])) shl 24;
+      mix_abc;
+      ALength -= 12;
+      k8 += 12;
+    end;
+
+    case ALength of
+      12: goto Case12;
+      11: goto Case11;
+      10: goto Case10;
+      9 : goto Case9;
+      8 : goto Case8;
+      7 : goto Case7;
+      6 : goto Case6;
+      5 : goto Case5;
+      4 : goto Case4;
+      3 : goto Case3;
+      2 : goto Case2;
+      1 : goto Case1;
+      0 :
+        begin
+          APrimaryHashAndInitVal := c;
+          ASecondaryHashAndInitVal := b;
+          Exit;              // zero length strings require no mixing
+        end;
+    end;
+
+    Case12: c+=(UInt32(k8[11])) shl 24;
+    Case11: c+=(UInt32(k8[10])) shl 16;
+    Case10: c+=(UInt32(k8[9])) shl 8;
+    Case9: c+=k8[8];
+    Case8: b+=(UInt32(k8[7])) shl 24;
+    Case7: b+=(UInt32(k8[6])) shl 16;
+    Case6: b+=(UInt32(k8[5])) shl 8;
+    Case5: b+=k8[4];
+    Case4: a+=(UInt32(k8[3])) shl 24;
+    Case3: a+=(UInt32(k8[2])) shl 16;
+    Case2: a+=(UInt32(k8[1])) shl 8;
+    Case1: a+=k8[0];
+  end;
+
+  final_abc;
+  APrimaryHashAndInitVal := c;
+  ASecondaryHashAndInitVal := b;
+end;
+
+function DelphiHashLittle(AKey: Pointer; ALength: SizeInt; AInitVal: UInt32): Int32;
+var
+  a, b, c: UInt32;
+  u: record case byte of
+    0: (ptr: Pointer);
+    1: (i: PtrUint);
+  end absolute AKey;
+
+  k32: ^UInt32 absolute AKey;
+  //k16: ^UInt16 absolute AKey;
+  k8: ^UInt8 absolute AKey;
+
+label Case12, Case11, Case10, Case9, Case8, Case7, Case6, Case5, Case4, Case3, Case2, Case1;
+
+begin
+  a := $DEADBEEF + UInt32(ALength shl 2) + AInitVal; // delphi version bug? original version don't have "shl 2"
+  b := a;
+  c := b;
+
+{.$IFDEF ENDIAN_LITTLE} // Delphi version don't care
+  if (u.i and $3) = 0 then
+  begin
+    while (ALength > 12) do
+    begin
+      a += k32[0];
+      b += k32[1];
+      c += k32[2];
+      mix_abc;
+      ALength -= 12;
+      k32 += 3;
+    end;
+
+    case ALength of
+      12: begin c += k32[2]; b += k32[1]; a += k32[0]; end;
+      11: begin c += k32[2] and $ffffff; b += k32[1]; a += k32[0]; end;
+      10: begin c += k32[2] and $ffff; b += k32[1]; a += k32[0]; end;
+      9 : begin c += k32[2] and $ff; b += k32[1]; a += k32[0]; end;
+      8 : begin b += k32[1]; a += k32[0]; end;
+      7 : begin b += k32[1] and $ffffff; a += k32[0]; end;
+      6 : begin b += k32[1] and $ffff; a += k32[0]; end;
+      5 : begin b += k32[1] and $ff; a += k32[0]; end;
+      4 : begin a += k32[0]; end;
+      3 : begin a += k32[0] and $ffffff; end;
+      2 : begin a += k32[0] and $ffff; end;
+      1 : begin a += k32[0] and $ff; end;
+      0 : Exit(c);              // zero length strings require no mixing
+    end
+  end
+  else
+{.$ENDIF}
+  begin
+    while ALength > 12 do
+    begin
+      a += k8[0];
+      a += (UInt32(k8[1])) shl 8;
+      a += (UInt32(k8[2])) shl 16;
+      a += (UInt32(k8[3])) shl 24;
+      b += k8[4];
+      b += (UInt32(k8[5])) shl 8;
+      b += (UInt32(k8[6])) shl 16;
+      b += (UInt32(k8[7])) shl 24;
+      c += k8[8];
+      c += (UInt32(k8[9])) shl 8;
+      c += (UInt32(k8[10])) shl 16;
+      c += (UInt32(k8[11])) shl 24;
+      mix_abc;
+      ALength -= 12;
+      k8 += 12;
+    end;
+
+    case ALength of
+      12: goto Case12;
+      11: goto Case11;
+      10: goto Case10;
+      9 : goto Case9;
+      8 : goto Case8;
+      7 : goto Case7;
+      6 : goto Case6;
+      5 : goto Case5;
+      4 : goto Case4;
+      3 : goto Case3;
+      2 : goto Case2;
+      1 : goto Case1;
+      0 : Exit(c);
+    end;
+
+    Case12: c+=(UInt32(k8[11])) shl 24;
+    Case11: c+=(UInt32(k8[10])) shl 16;
+    Case10: c+=(UInt32(k8[9])) shl 8;
+    Case9: c+=k8[8];
+    Case8: b+=(UInt32(k8[7])) shl 24;
+    Case7: b+=(UInt32(k8[6])) shl 16;
+    Case6: b+=(UInt32(k8[5])) shl 8;
+    Case5: b+=k8[4];
+    Case4: a+=(UInt32(k8[3])) shl 24;
+    Case3: a+=(UInt32(k8[2])) shl 16;
+    Case2: a+=(UInt32(k8[1])) shl 8;
+    Case1: a+=k8[0];
+  end;
+
+  final_abc;
+  Result := Int32(c);
+end;
+
+{$ifdef CPU64}
+  {$define PUREPASCAL}
+  {$ifdef CPUX64}
+    {$define CPUINTEL}
+    {$ASMMODE INTEL}
+  {$endif CPUX64}
+{$else}
+  {$ifdef CPUX86}
+    {$ifndef FPC_PIC}
+      {$define CPUINTEL}
+      {$ASMMODE INTEL}
+    {$else}
+      { Assembler code uses references to static
+        variables with are not PIC ready }
+      {$define PUREPASCAL}
+    {$endif}
+  {$else CPUX86}
+  {$define PUREPASCAL}
+  {$endif}
+{$endif CPU64}
+
+{$ifdef CPUARM} // circumvent FPC issue on ARM
+function ToByte(value: cardinal): cardinal; inline;
+begin
+  result := value and $ff;
+end;
+{$else}
+type ToByte = byte;
+{$endif}
+
+{$ifdef CPUINTEL} // use optimized x86/x64 asm versions for xxHash32
+
+{$ifdef CPUX86}
+function xxHash32(crc: cardinal; P: Pointer; len: integer): cardinal;
+asm
+        xchg    edx, ecx
+        push    ebp
+        push    edi
+        lea     ebp, [ecx+edx]
+        push    esi
+        push    ebx
+        sub     esp, 8
+        cmp     edx, 15
+        mov     ebx, eax
+        mov     dword ptr [esp], edx
+        lea     eax, [ebx+165667B1H]
+        jbe     @2
+        lea     eax, [ebp-10H]
+        lea     edi, [ebx+24234428H]
+        lea     esi, [ebx-7A143589H]
+        mov     dword ptr [esp+4H], ebp
+        mov     edx, eax
+        lea     eax, [ebx+61C8864FH]
+        mov     ebp, edx
+@1:     mov     edx, dword ptr [ecx]
+        imul    edx, edx, -2048144777
+        add     edi, edx
+        rol     edi, 13
+        imul    edi, edi, -1640531535
+        mov     edx, dword ptr [ecx+4]
+        imul    edx, edx, -2048144777
+        add     esi, edx
+        rol     esi, 13
+        imul    esi, esi, -1640531535
+        mov     edx, dword ptr [ecx+8]
+        imul    edx, edx, -2048144777
+        add     ebx, edx
+        rol     ebx, 13
+        imul    ebx, ebx, -1640531535
+        mov     edx, dword ptr [ecx+12]
+        lea     ecx, [ecx+16]
+        imul    edx, edx, -2048144777
+        add     eax, edx
+        rol     eax, 13
+        imul    eax, eax, -1640531535
+        cmp     ebp, ecx
+        jnc     @1
+        rol     edi, 1
+        rol     esi, 7
+        rol     ebx, 12
+        add     esi, edi
+        mov     ebp, dword ptr [esp+4H]
+        ror     eax, 14
+        add     ebx, esi
+        add     eax, ebx
+@2:     lea     esi, [ecx+4H]
+        add     eax, dword ptr [esp]
+        cmp     ebp, esi
+        jc      @4
+        mov     ebx, esi
+        nop
+@3:     imul    edx, dword ptr [ebx-4H], -1028477379
+        add     ebx, 4
+        add     eax, edx
+        ror     eax, 15
+        imul    eax, eax, 668265263
+        cmp     ebp, ebx
+        jnc     @3
+        lea     edx, [ebp-4H]
+        sub     edx, ecx
+        mov     ecx, edx
+        and     ecx, 0FFFFFFFCH
+        add     ecx, esi
+@4:     cmp     ebp, ecx
+        jbe     @6
+@5:     movzx   edx, byte ptr [ecx]
+        add     ecx, 1
+        imul    edx, edx, 374761393
+        add     eax, edx
+        rol     eax, 11
+        imul    eax, eax, -1640531535
+        cmp     ebp, ecx
+        jnz     @5
+        nop
+@6:     mov     edx, eax
+        add     esp, 8
+        shr     edx, 15
+        xor     eax, edx
+        imul    eax, eax, -2048144777
+        pop     ebx
+        pop     esi
+        mov     edx, eax
+        shr     edx, 13
+        xor     eax, edx
+        imul    eax, eax, -1028477379
+        pop     edi
+        pop     ebp
+        mov     edx, eax
+        shr     edx, 16
+        xor     eax, edx
+end;
+{$endif CPUX86}
+
+{$ifdef CPUX64}
+function xxHash32(crc: cardinal; P: Pointer; len: integer): cardinal;
+asm
+        {$ifndef WIN64} // crc=rdi P=rsi len=rdx
+        mov     r8, rdi
+        mov     rcx, rsi
+        {$else} // crc=r8 P=rcx len=rdx
+        mov     r10, r8
+        mov     r8, rcx
+        mov     rcx, rdx
+        mov     rdx, r10
+        push    rsi   // Win64 expects those registers to be preserved
+        push    rdi
+        {$endif}
+        // P=r8 len=rcx crc=rdx
+        push    rbx
+        lea     r10, [rcx+rdx]
+        cmp     rdx, 15
+        lea     eax, [r8+165667B1H]
+        jbe     @2
+        lea     rsi, [r10-10H]
+        lea     ebx, [r8+24234428H]
+        lea     edi, [r8-7A143589H]
+        lea     eax, [r8+61C8864FH]
+@1:     imul    r9d, dword ptr [rcx], -2048144777
+        add     rcx, 16
+        imul    r11d, dword ptr [rcx-0CH], -2048144777
+        add     ebx, r9d
+        lea     r9d, [r11+rdi]
+        rol     ebx, 13
+        rol     r9d, 13
+        imul    ebx, ebx, -1640531535
+        imul    edi, r9d, -1640531535
+        imul    r9d, dword ptr [rcx-8H], -2048144777
+        add     r8d, r9d
+        imul    r9d, dword ptr [rcx-4H], -2048144777
+        rol     r8d, 13
+        imul    r8d, r8d, -1640531535
+        add     eax, r9d
+        rol     eax, 13
+        imul    eax, eax, -1640531535
+        cmp     rsi, rcx
+        jnc     @1
+        rol     edi, 7
+        rol     ebx, 1
+        rol     r8d, 12
+        mov     r9d, edi
+        ror     eax, 14
+        add     r9d, ebx
+        add     r8d, r9d
+        add     eax, r8d
+@2:     lea     r9, [rcx+4H]
+        add     eax, edx
+        cmp     r10, r9
+        jc      @4
+        mov     r8, r9
+@3:     imul    edx, dword ptr [r8-4H], -1028477379
+        add     r8, 4
+        add     eax, edx
+        ror     eax, 15
+        imul    eax, eax, 668265263
+        cmp     r10, r8
+        jnc     @3
+        lea     rdx, [r10-4H]
+        sub     rdx, rcx
+        mov     rcx, rdx
+        and     rcx, 0FFFFFFFFFFFFFFFCH
+        add     rcx, r9
+@4:     cmp     r10, rcx
+        jbe     @6
+@5:     movzx   edx, byte ptr [rcx]
+        add     rcx, 1
+        imul    edx, edx, 374761393
+        add     eax, edx
+        rol     eax, 11
+        imul    eax, eax, -1640531535
+        cmp     r10, rcx
+        jnz     @5
+@6:     mov     edx, eax
+        shr     edx, 15
+        xor     eax, edx
+        imul    eax, eax, -2048144777
+        mov     edx, eax
+        shr     edx, 13
+        xor     eax, edx
+        imul    eax, eax, -1028477379
+        mov     edx, eax
+        shr     edx, 16
+        xor     eax, edx
+        pop     rbx
+        {$ifdef WIN64}
+        pop     rdi
+        pop     rsi
+        {$endif}
+end;
+{$endif CPUX64}
+
+{$else not CPUINTEL}
+const
+  PRIME32_1 = 2654435761;
+  PRIME32_2 = 2246822519;
+  PRIME32_3 = 3266489917;
+  PRIME32_4 = 668265263;
+  PRIME32_5 = 374761393;
+
+// RolDWord is an intrinsic function under FPC :)
+function Rol13(value: cardinal): cardinal; inline;
+begin
+  result := RolDWord(value, 13);
+end;
+
+function xxHash32(crc: cardinal; P: Pointer; len: integer): cardinal;
+var c1, c2, c3, c4: cardinal;
+    PLimit, PEnd: PAnsiChar;
+begin
+  PEnd := P + len;
+  if len >= 16 then begin
+    PLimit := PEnd - 16;
+    c3 := crc;
+    c2 := c3 + PRIME32_2;
+    c1 := c2 + PRIME32_1;
+    c4 := c3 - PRIME32_1;
+    repeat
+      c1 := PRIME32_1 * Rol13(c1 + PRIME32_2 * PCardinal(P)^);
+      c2 := PRIME32_1 * Rol13(c2 + PRIME32_2 * PCardinal(P+4)^);
+      c3 := PRIME32_1 * Rol13(c3 + PRIME32_2 * PCardinal(P+8)^);
+      c4 := PRIME32_1 * Rol13(c4 + PRIME32_2 * PCardinal(P+12)^);
+      inc(P, 16);
+    until not (P <= PLimit);
+    result := RolDWord(c1, 1) + RolDWord(c2, 7) + RolDWord(c3, 12) + RolDWord(c4, 18);
+  end else
+    result := crc + PRIME32_5;
+  inc(result, len);
+  while P <= PEnd - 4 do begin
+    inc(result, PCardinal(P)^ * PRIME32_3);
+    result := RolDWord(result, 17) * PRIME32_4;
+    inc(P, 4);
+  end;
+  while P < PEnd do begin
+    inc(result, PByte(P)^ * PRIME32_5);
+    result := RolDWord(result, 11) * PRIME32_1;
+    inc(P);
+  end;
+  result := result xor (result shr 15);
+  result := result * PRIME32_2;
+  result := result xor (result shr 13);
+  result := result * PRIME32_3;
+  result := result xor (result shr 16);
+end;
+{$endif CPUINTEL}
+
+{$ifdef CPUINTEL}
+
+type
+ TRegisters = record
+   eax,ebx,ecx,edx: cardinal;
+ end;
+
+{$ifdef CPU64}
+procedure GetCPUID(Param: Cardinal; var Registers: TRegisters); nostackframe; assembler;
+asm
+        {$ifdef win64}
+        mov     eax, ecx
+        mov     r9, rdx
+        {$else}
+        mov     eax, edi
+        mov     r9, rsi
+        {$endif win64}
+        mov     r10, rbx // preserve rbx
+        xor     ebx, ebx
+        xor     ecx, ecx
+        xor     edx, edx
+        cpuid
+        mov     TRegisters(r9).&eax, eax
+        mov     TRegisters(r9).&ebx, ebx
+        mov     TRegisters(r9).&ecx, ecx
+        mov     TRegisters(r9).&edx, edx
+        mov     rbx, r10
+end;
+
+function crc32csse42(crc: cardinal; buf: Pointer; len: cardinal): cardinal; nostackframe; assembler;
+asm // ecx=crc, rdx=buf, r8=len (Linux: edi,rsi,rdx)
+        {$ifdef win64}
+        mov     eax, ecx
+        {$else}
+        mov     eax, edi
+        mov     r8, rdx
+        mov     rdx, rsi
+        {$endif win64}
+        not     eax
+        test    rdx, rdx
+        jz      @0
+        test    r8, r8
+        jz      @0
+@7:     test    dl, 7
+        jz      @8 // align to 8 bytes boundary
+        crc32   eax, byte ptr[rdx]
+        inc     rdx
+        dec     r8
+        jz      @0
+        test    dl, 7
+        jnz     @7
+@8:     mov     rcx, r8
+        shr     r8, 3
+        jz      @2
+@1:
+        crc32   rax, qword [rdx] // hash 8 bytes per loop
+        dec     r8
+        lea     rdx, [rdx + 8]
+        jnz     @1
+@2:     and     ecx, 7
+        jz      @0
+        cmp     ecx, 4
+        jb      @4
+        crc32   eax, dword ptr[rdx]
+        sub     ecx, 4
+        lea     rdx, [rdx + 4]
+        jz      @0
+@4:     crc32   eax, byte ptr[rdx]
+        dec     ecx
+        jz      @0
+        crc32   eax, byte ptr[rdx + 1]
+        dec     ecx
+        jz      @0
+        crc32   eax, byte ptr[rdx + 2]
+@0:     not     eax
+end;
+{$endif CPU64}
+
+{$ifdef CPUX86}
+procedure GetCPUID(Param: Cardinal; var Registers: TRegisters);
+asm
+        push    esi
+        push    edi
+        mov     esi, edx
+        mov     edi, eax
+        pushfd
+        pop     eax
+        mov     edx, eax
+        xor     eax, $200000
+        push    eax
+        popfd
+        pushfd
+        pop     eax
+        xor     eax, edx
+        jz      @nocpuid
+        push    ebx
+        mov     eax, edi
+        xor     ecx, ecx
+        cpuid
+        mov     TRegisters(esi).&eax, eax
+        mov     TRegisters(esi).&ebx, ebx
+        mov     TRegisters(esi).&ecx, ecx
+        mov     TRegisters(esi).&edx, edx
+        pop     ebx
+@nocpuid:
+        pop     edi
+        pop     esi
+end;
+
+function crc32csse42(crc: cardinal; buf: Pointer; len: cardinal): cardinal;
+asm // eax=crc, edx=buf, ecx=len
+        not     eax
+        test    ecx, ecx
+        jz      @0
+        test    edx, edx
+        jz      @0
+@3:     test    edx, 3
+        jz      @8 // align to 4 bytes boundary
+        crc32   eax, byte ptr[edx]
+        inc     edx
+        dec     ecx
+        jz      @0
+        test    edx, 3
+        jnz     @3
+@8:     push    ecx
+        shr     ecx, 3
+        jz      @2
+@1:
+        crc32   eax, dword ptr[edx]
+        crc32   eax, dword ptr[edx + 4]
+        dec     ecx
+        lea     edx, [edx + 8]
+        jnz     @1
+@2:     pop     ecx
+        and     ecx, 7
+        jz      @0
+        cmp     ecx, 4
+        jb      @4
+        crc32   eax, dword ptr[edx]
+        sub     ecx, 4
+        lea     edx, [edx + 4]
+        jz      @0
+@4:
+        crc32   eax, byte ptr[edx]
+        dec     ecx
+        jz      @0
+        crc32   eax, byte ptr[edx + 1]
+        dec     ecx
+        jz      @0
+        crc32   eax, byte ptr[edx + 2]
+@0:     not     eax
+end;
+{$endif CPUX86}
+
+type
+  /// the potential features, retrieved from an Intel CPU
+  // - see https://en.wikipedia.org/wiki/CPUID#EAX.3D1:_Processor_Info_and_Feature_Bits
+  TIntelCpuFeature =
+   ( { in EDX }
+   cfFPU, cfVME, cfDE, cfPSE, cfTSC, cfMSR, cfPAE, cfMCE,
+   cfCX8, cfAPIC, cf_d10, cfSEP, cfMTRR, cfPGE, cfMCA, cfCMOV,
+   cfPAT, cfPSE36, cfPSN, cfCLFSH, cf_d20, cfDS, cfACPI, cfMMX,
+   cfFXSR, cfSSE, cfSSE2, cfSS, cfHTT, cfTM, cfIA64, cfPBE,
+   { in ECX }
+   cfSSE3, cfCLMUL, cfDS64, cfMON, cfDSCPL, cfVMX, cfSMX, cfEST,
+   cfTM2, cfSSSE3, cfCID, cfSDBG, cfFMA, cfCX16, cfXTPR, cfPDCM,
+   cf_c16, cfPCID, cfDCA, cfSSE41, cfSSE42, cfX2A, cfMOVBE, cfPOPCNT,
+   cfTSC2, cfAESNI, cfXS, cfOSXS, cfAVX, cfF16C, cfRAND, cfHYP,
+   { extended features in EBX, ECX }
+   cfFSGS, cf_b01, cfSGX, cfBMI1, cfHLE, cfAVX2, cf_b06, cfSMEP,
+   cfBMI2, cfERMS, cfINVPCID, cfRTM, cfPQM, cf_b13, cfMPX, cfPQE,
+   cfAVX512F, cfAVX512DQ, cfRDSEED, cfADX, cfSMAP, cfAVX512IFMA, cfPCOMMIT, cfCLFLUSH,
+   cfCLWB, cfIPT, cfAVX512PF, cfAVX512ER, cfAVX512CD, cfSHA, cfAVX512BW, cfAVX512VL,
+   cfPREFW1, cfAVX512VBMI, cfUMIP, cfPKU, cfOSPKE, cf_c05, cf_c06, cf_c07,
+   cf_c08, cf_c09, cf_c10, cf_c11, cf_c12, cf_c13, cfAVX512VPC, cf_c15,
+   cf_cc16, cf_c17, cf_c18, cf_c19, cf_c20, cf_c21, cfRDPID, cf_c23,
+   cf_c24, cf_c25, cf_c26, cf_c27, cf_c28, cf_c29, cfSGXLC, cf_c31,
+   cf_d0, cf_d1, cfAVX512NNI, cfAVX512MAS, cf_d4, cf_d5, cf_d6, cf_d7);
+
+  /// all features, as retrieved from an Intel CPU
+  TIntelCpuFeatures = set of TIntelCpuFeature;
+
+var
+  /// the available CPU features, as recognized at program startup
+  CpuFeatures: TIntelCpuFeatures;
+
+procedure TestIntelCpuFeatures;
+var regs: TRegisters;
+begin
+  regs.edx := 0;
+  regs.ecx := 0;
+  GetCPUID(1,regs);
+  PIntegerArray(@CpuFeatures)^[0] := regs.edx;
+  PIntegerArray(@CpuFeatures)^[1] := regs.ecx;
+  GetCPUID(7,regs);
+  PIntegerArray(@CpuFeatures)^[2] := regs.ebx;
+  PIntegerArray(@CpuFeatures)^[3] := regs.ecx;
+  PByte(@PIntegerArray(@CpuFeatures)^[4])^ := regs.edx;
+//  assert(sizeof(CpuFeatures)=4*4+1);
+  {$ifdef Darwin}
+  {$ifdef CPU64}
+  // SSE42 asm does not (yet) work on Darwin x64 ...
+  Exclude(CpuFeatures, cfSSE42);
+  {$endif}
+  {$endif}
+end;
+{$endif CPUINTEL}
+
+var
+  crc32ctab: array[0..{$ifdef PUREPASCAL}3{$else}7{$endif},byte] of cardinal;
+
+function crc32cfast(crc: cardinal; buf: Pointer; len: cardinal): cardinal;
+{$ifdef PUREPASCAL}
+begin
+  result := not crc;
+  if (buf<>nil) and (len>0) then begin
+    repeat
+      if PtrUInt(buf) and 3=0 then // align to 4 bytes boundary
+        break;
+      result := crc32ctab[0,ToByte(result xor cardinal(buf^))] xor (result shr 8);
+      dec(len);
+      inc(buf);
+    until len=0;
+    while len>=4 do begin
+      result := result xor PCardinal(buf)^;
+      inc(buf,4);
+      result := crc32ctab[3,ToByte(result)] xor
+                crc32ctab[2,ToByte(result shr 8)] xor
+                crc32ctab[1,ToByte(result shr 16)] xor
+                crc32ctab[0,result shr 24];
+      dec(len,4);
+    end;
+    while len>0 do begin
+      result := crc32ctab[0,ToByte(result xor cardinal(buf^))] xor (result shr 8);
+      dec(len);
+      inc(buf);
+    end;
+  end;
+  result := not result;
+end;
+{$else}
+// adapted from fast Aleksandr Sharahov version
+asm
+        test    edx, edx
+        jz      @ret
+        neg     ecx
+        jz      @ret
+        not     eax
+        push    ebx
+@head:  test    dl, 3
+        jz      @aligned
+        movzx   ebx, byte[edx]
+        inc     edx
+        xor     bl, al
+        shr     eax, 8
+        xor     eax, dword ptr[ebx * 4 + crc32ctab]
+        inc     ecx
+        jnz     @head
+        pop     ebx
+        not     eax
+        ret
+@ret:   rep     ret
+@aligned:
+        sub     edx, ecx
+        add     ecx, 8
+        jg      @bodydone
+        push    esi
+        push    edi
+        mov     edi, edx
+        mov     edx, eax
+@bodyloop:
+        mov     ebx, [edi + ecx - 4]
+        xor     edx, [edi + ecx - 8]
+        movzx   esi, bl
+        mov     eax, dword ptr[esi * 4 + crc32ctab + 1024 * 3]
+        movzx   esi, bh
+        xor     eax, dword ptr[esi * 4 + crc32ctab + 1024 * 2]
+        shr     ebx, 16
+        movzx   esi, bl
+        xor     eax, dword ptr[esi * 4 + crc32ctab + 1024 * 1]
+        movzx   esi, bh
+        xor     eax, dword ptr[esi * 4 + crc32ctab + 1024 * 0]
+        movzx   esi, dl
+        xor     eax, dword ptr[esi * 4 + crc32ctab + 1024 * 7]
+        movzx   esi, dh
+        xor     eax, dword ptr[esi * 4 + crc32ctab + 1024 * 6]
+        shr     edx, 16
+        movzx   esi, dl
+        xor     eax, dword ptr[esi * 4 + crc32ctab + 1024 * 5]
+        movzx   esi, dh
+        xor     eax, dword ptr[esi * 4 + crc32ctab + 1024 * 4]
+        add     ecx, 8
+        jg      @done
+        mov     ebx, [edi + ecx - 4]
+        xor     eax, [edi + ecx - 8]
+        movzx   esi, bl
+        mov     edx, dword ptr[esi * 4 + crc32ctab + 1024 * 3]
+        movzx   esi, bh
+        xor     edx, dword ptr[esi * 4 + crc32ctab + 1024 * 2]
+        shr     ebx, 16
+        movzx   esi, bl
+        xor     edx, dword ptr[esi * 4 + crc32ctab + 1024 * 1]
+        movzx   esi, bh
+        xor     edx, dword ptr[esi * 4 + crc32ctab + 1024 * 0]
+        movzx   esi, al
+        xor     edx, dword ptr[esi * 4 + crc32ctab + 1024 * 7]
+        movzx   esi, ah
+        xor     edx, dword ptr[esi * 4 + crc32ctab + 1024 * 6]
+        shr     eax, 16
+        movzx   esi, al
+        xor     edx, dword ptr[esi * 4 + crc32ctab + 1024 * 5]
+        movzx   esi, ah
+        xor     edx, dword ptr[esi * 4 + crc32ctab + 1024 * 4]
+        add     ecx, 8
+        jle     @bodyloop
+        mov     eax, edx
+@done:  mov     edx, edi
+        pop     edi
+        pop     esi
+@bodydone:
+        sub     ecx, 8
+        jl      @tail
+        pop     ebx
+        not     eax
+        ret
+@tail:  movzx   ebx, byte[edx + ecx]
+        xor     bl, al
+        shr     eax, 8
+        xor     eax, dword ptr[ebx * 4 + crc32ctab]
+        inc     ecx
+        jnz     @tail
+        pop     ebx
+        not     eax
+end;
+{$endif PUREPASCAL}
+
+procedure InitializeCrc32ctab;
+var
+  i, n: integer;
+  crc: cardinal;
+begin
+  // initialize tables for crc32cfast() and SymmetricEncrypt/FillRandom
+  for i := 0 to 255 do begin
+    crc := i;
+    for n := 1 to 8 do
+      if (crc and 1)<>0 then // polynom is not the same as with zlib's crc32()
+        crc := (crc shr 1) xor $82f63b78 else
+        crc := crc shr 1;
+    crc32ctab[0,i] := crc;
+  end;
+  for i := 0 to 255 do begin
+    crc := crc32ctab[0,i];
+    for n := 1 to high(crc32ctab) do begin
+      crc := (crc shr 8) xor crc32ctab[0,ToByte(crc)];
+      crc32ctab[n,i] := crc;
+    end;
+  end;
+end;
+
+begin
+  {$ifdef CPUINTEL}
+  TestIntelCpuFeatures;
+  if cfSSE42 in CpuFeatures then
+  begin
+    crc32c := @crc32csse42;
+    mORMotHasher := @crc32csse42;
+  end
+  else
+  {$endif CPUINTEL}
+  begin
+    InitializeCrc32ctab;
+    crc32c := @crc32cfast;
+    mORMotHasher := @xxHash32;
+  end;
+end.
+

+ 146 - 0
CryptoLib/src/3rdParty/generics.collections/src/generics.helpers.pas

@@ -0,0 +1,146 @@
+{
+    This file is part of the Free Pascal run time library.
+    Copyright (c) 2014 by Maciej Izak (hnb)
+    member of the Free Sparta development team (http://freesparta.com)
+
+    Copyright(c) 2004-2014 DaThoX
+
+    It contains the Free Pascal generics library
+
+    See the file COPYING.FPC, included in this distribution,
+    for details about the copyright.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+
+ **********************************************************************}
+
+unit Generics.Helpers;
+
+{$MODE DELPHI}{$H+}
+{$MODESWITCH TYPEHELPERS}
+{$OVERFLOWCHECKS OFF}
+{$RANGECHECKS OFF}
+
+interface
+
+uses
+  Classes, SysUtils;
+
+type
+  { TValueAnsiStringHelper }
+
+  TValueAnsiStringHelper = record helper for AnsiString
+    function ToLower: AnsiString; inline;
+  end;
+
+  { TValuewideStringHelper }
+
+  TValueWideStringHelper = record helper for WideString
+    function ToLower: WideString; inline;
+  end;
+
+  { TValueUnicodeStringHelper }
+
+  TValueUnicodeStringHelper = record helper for UnicodeString
+    function ToLower: UnicodeString; inline;
+  end;
+
+  { TValueShortStringHelper }
+
+  TValueShortStringHelper = record helper for ShortString
+    function ToLower: ShortString; inline;
+  end;
+
+  { TValueUTF8StringHelper }
+
+  TValueUTF8StringHelper = record helper for UTF8String
+    function ToLower: UTF8String; inline;
+  end;
+
+  { TValueRawByteStringHelper }
+
+  TValueRawByteStringHelper = record helper for RawByteString
+    function ToLower: RawByteString; inline;
+  end;
+
+  { TValueUInt32Helper }
+
+  TValueUInt32Helper = record helper for UInt32
+    class function GetSignMask: UInt32; static; inline;
+    class function GetSizedSignMask(ABits: Byte): UInt32; static; inline;
+    class function GetBitsLength: Byte; static; inline;
+
+    const
+      SIZED_SIGN_MASK: array[1..32] of UInt32 = (
+        $80000000, $C0000000, $E0000000, $F0000000, $F8000000, $FC000000, $FE000000, $FF000000,
+        $FF800000, $FFC00000, $FFE00000, $FFF00000, $FFF80000, $FFFC0000, $FFFE0000, $FFFF0000,
+        $FFFF8000, $FFFFC000, $FFFFE000, $FFFFF000, $FFFFF800, $FFFFFC00, $FFFFFE00, $FFFFFF00,
+        $FFFFFF80, $FFFFFFC0, $FFFFFFE0, $FFFFFFF0, $FFFFFFF8, $FFFFFFFC, $FFFFFFFE, $FFFFFFFF);
+      BITS_LENGTH = 32;
+  end;
+
+implementation
+
+{ TRawDataStringHelper }
+
+function TValueAnsiStringHelper.ToLower: AnsiString;
+begin
+  Result := LowerCase(Self);
+end;
+
+{ TValueWideStringHelper }
+
+function TValueWideStringHelper.ToLower: WideString;
+begin
+  Result := LowerCase(Self);
+end;
+
+{ TValueUnicodeStringHelper }
+
+function TValueUnicodeStringHelper.ToLower: UnicodeString;
+begin
+  Result := LowerCase(Self);
+end;
+
+{ TValueShortStringHelper }
+
+function TValueShortStringHelper.ToLower: ShortString;
+begin
+  Result := LowerCase(Self);
+end;
+
+{ TValueUTF8StringHelper }
+
+function TValueUTF8StringHelper.ToLower: UTF8String;
+begin
+  Result := LowerCase(Self);
+end;
+
+{ TValueRawByteStringHelper }
+
+function TValueRawByteStringHelper.ToLower: RawByteString;
+begin
+  Result := LowerCase(Self);
+end;
+
+{ TValueUInt32Helper }
+
+class function TValueUInt32Helper.GetSignMask: UInt32;
+begin
+  Result := $80000000;
+end;
+
+class function TValueUInt32Helper.GetSizedSignMask(ABits: Byte): UInt32;
+begin
+  Result := SIZED_SIGN_MASK[ABits];
+end;
+
+class function TValueUInt32Helper.GetBitsLength: Byte;
+begin
+  Result := BITS_LENGTH;
+end;
+
+end.
+

+ 227 - 0
CryptoLib/src/3rdParty/generics.collections/src/generics.memoryexpanders.pas

@@ -0,0 +1,227 @@
+{
+    This file is part of the Free Pascal run time library.
+    Copyright (c) 2014 by Maciej Izak (hnb)
+    member of the Free Sparta development team (http://freesparta.com)
+
+    Copyright(c) 2004-2014 DaThoX
+
+    It contains the Free Pascal generics library
+
+    See the file COPYING.FPC, included in this distribution,
+    for details about the copyright.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+
+ **********************************************************************}
+
+unit Generics.MemoryExpanders;
+// Memory expanders
+
+{$mode delphi}
+{$MACRO ON}
+{$OVERFLOWCHECKS OFF}
+{$RANGECHECKS OFF}
+{.$WARN 5024 OFF}
+{.$WARN 4079 OFF}
+
+interface
+
+uses
+  Classes, SysUtils;
+
+type
+  TProbeSequence = class
+  public
+  end;
+
+  { TLinearProbing }
+
+  TLinearProbing = class(TProbeSequence)
+  public
+    class function Probe(I, Hash: UInt32): UInt32; static; inline;
+
+    const MAX_LOAD_FACTOR = 1;
+    const DEFAULT_LOAD_FACTOR = 0.75;
+  end;
+
+  { TQuadraticProbing }
+
+  TQuadraticProbing = class(TProbeSequence)
+  public
+    class function Probe(I, Hash: UInt32): UInt32; static; inline;
+
+    const MAX_LOAD_FACTOR = 0.5;
+    const DEFAULT_LOAD_FACTOR = 0.5;
+  end;
+
+  { TDoubleHashing }
+
+  TDoubleHashing = class(TProbeSequence)
+  public
+    class function Probe(I, Hash1: UInt32; Hash2: UInt32 = 1): UInt32; static; inline;
+
+    const MAX_LOAD_FACTOR = 1;
+    const DEFAULT_LOAD_FACTOR = 0.85;
+  end;
+
+const
+  // http://stackoverflow.com/questions/757059/position-of-least-significant-bit-that-is-set
+  // MultiplyDeBruijnBitPosition[uint32(((numberInt32 and -numberInt32) * $077CB531)) shr 27]
+  MultiplyDeBruijnBitPosition: array[0..31] of Int32 =
+  (
+    0, 1, 28, 2, 29, 14, 24, 3, 30, 22, 20, 15, 25, 17, 4, 8,
+    31, 27, 13, 23, 21, 19, 16, 7, 26, 12, 18, 6, 11, 5, 10, 9
+  );
+
+  // http://primes.utm.edu/lists/2small/0bit.html
+  // http://www.math.niu.edu/~rusin/known-math/98/pi_x
+  // http://oeis.org/A014234/
+  PrimaryNumbersJustLessThanPowerOfTwo: array[0..31] of UInt32 =
+  (
+    0, 1, 3, 7, 13, 31, 61, 127, 251, 509, 1021, 2039, 4093, 8191, 16381, 32749, 65521, 131071,
+    262139, 524287, 1048573, 2097143, 4194301, 8388593, 16777213, 33554393, 67108859,
+    134217689, 268435399, 536870909, 1073741789, 2147483647
+  );
+
+  // http://oeis.org/A014210
+  // http://oeis.org/A203074
+  PrimaryNumbersJustBiggerThanPowerOfTwo: array[0..31] of UInt32 = (
+    2,3,5,11,17,37,67,131,257,521,1031,2053,4099,
+    8209,16411,32771,65537,131101,262147,524309,
+    1048583,2097169,4194319,8388617,16777259,33554467,
+    67108879,134217757,268435459,536870923,1073741827,
+    2147483659);
+
+  // Fibonacci numbers
+  FibonacciNumbers: array[0..44] of UInt32 = (
+    {0,1,1,2,3,}0,5,8,13,21,34,55,89,144,233,377,610,987,
+    1597,2584,4181,6765,10946,17711,28657,46368,75025,
+    121393,196418,317811,514229,832040,1346269,
+    2178309,3524578,5702887,9227465,14930352,24157817,
+    39088169, 63245986, 102334155, 165580141, 267914296,
+    433494437, 701408733, 1134903170, 1836311903, 2971215073,
+    {! not fib number - this is memory limit} 4294967295);
+
+  // Largest prime not exceeding Fibonacci(n)
+  // http://oeis.org/A138184/list
+  // http://www.numberempire.com/primenumbers.php
+  PrimaryNumbersJustLessThanFibonacciNumbers: array[0..44] of UInt32 = (
+    {! not correlated to fib number. For empty table} 0,
+    5,7,13,19,31,53,89,139,233,373,607,983,1597,
+    2579,4177,6763,10939,17707,28657,46351,75017,
+    121379,196387,317797,514229,832003,1346249,
+    2178283,3524569,5702867,9227443,14930341,24157811,
+    39088157,63245971,102334123,165580123,267914279,
+    433494437,701408717,1134903127,1836311879,2971215073,
+    {! not correlated to fib number - this is prime memory limit} 4294967291);
+
+  // Smallest prime >= n-th Fibonacci number.
+  // http://oeis.org/A138185
+  PrimaryNumbersJustBiggerThanFibonacciNumbers: array[0..44] of UInt32 = (
+    {! not correlated to fib number. For empty table} 0,
+    5,11,13,23,37,59,89,149,233,379,613,
+    991,1597,2591,4201,6779,10949,17713,28657,46381,
+    75029,121403,196429,317827,514229,832063,1346273,
+    2178313,3524603,5702897,9227479,14930387,24157823,
+    39088193,63245989,102334157,165580147,267914303,
+    433494437,701408753,1134903179,1836311951,2971215073,
+    {! not correlated to fib number - this is prime memory limit} 4294967291);
+
+type
+
+  { TCuckooHashingCfg }
+
+  TCuckooHashingCfg = class
+  public
+    const D = 2;
+    const MAX_LOAD_FACTOR = 0.5;
+
+    class function LoadFactor(M: Integer): Integer; virtual;
+  end;
+
+  TStdCuckooHashingCfg = class(TCuckooHashingCfg)
+  public
+    const MAX_LOOP = 1000;
+  end;
+
+  TDeamortizedCuckooHashingCfg = class(TCuckooHashingCfg)
+  public
+    const L = 5;
+  end;
+
+  TDeamortizedCuckooHashingCfg_D2 = TDeamortizedCuckooHashingCfg;
+
+  { TDeamortizedCuckooHashingCfg_D4 }
+
+  TDeamortizedCuckooHashingCfg_D4 = class(TDeamortizedCuckooHashingCfg)
+  public
+    const D = 4;
+    const L = 20;
+    const MAX_LOAD_FACTOR = 0.9;
+
+    class function LoadFactor(M: Integer): Integer; override;
+  end;
+
+  { TDeamortizedCuckooHashingCfg_D6 }
+
+  TDeamortizedCuckooHashingCfg_D6 = class(TDeamortizedCuckooHashingCfg)
+  public
+    const D = 6;
+    const L = 170;
+    const MAX_LOAD_FACTOR = 0.99;
+
+    class function LoadFactor(M: Integer): Integer; override;
+  end;
+
+  TL5CuckooHashingCfg = class(TCuckooHashingCfg)
+  public
+  end;
+
+implementation
+
+{ TDeamortizedCuckooHashingCfg_D6 }
+
+class function TDeamortizedCuckooHashingCfg_D6.LoadFactor(M: Integer): Integer;
+begin
+  Result:=Pred(Round(MAX_LOAD_FACTOR*M));
+end;
+
+{ TDeamortizedCuckooHashingCfg_D4 }
+
+class function TDeamortizedCuckooHashingCfg_D4.LoadFactor(M: Integer): Integer;
+begin
+  Result:=Pred(Round(MAX_LOAD_FACTOR*M));
+end;
+
+{ TCuckooHashingCfg }
+
+class function TCuckooHashingCfg.LoadFactor(M: Integer): Integer;
+begin
+  Result := Pred(M shr 1);
+end;
+
+{ TLinearProbing }
+
+class function TLinearProbing.Probe(I, Hash: UInt32): UInt32;
+begin
+  Result := (Hash + I)
+end;
+
+{ TQuadraticProbing }
+
+class function TQuadraticProbing.Probe(I, Hash: UInt32): UInt32;
+begin
+  Result := (Hash + Sqr(I));
+end;
+
+{ TDoubleHashingNoMod }
+
+class function TDoubleHashing.Probe(I, Hash1: UInt32; Hash2: UInt32): UInt32;
+begin
+  Result := Hash1 + I * Hash2;
+end;
+
+end.
+

+ 37 - 0
CryptoLib/src/3rdParty/generics.collections/src/generics.strings.pas

@@ -0,0 +1,37 @@
+{
+    This file is part of the Free Pascal run time library.
+    Copyright (c) 2014 by Maciej Izak (hnb)
+    member of the Free Sparta development team (http://freesparta.com)
+
+    Copyright(c) 2004-2014 DaThoX
+
+    It contains the Free Pascal generics library
+
+    See the file COPYING.FPC, included in this distribution,
+    for details about the copyright.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+
+ **********************************************************************}
+
+unit Generics.Strings;
+
+{$mode objfpc}{$H+}
+
+interface
+
+resourcestring
+  SArgumentOutOfRange = 'Argument out of range';
+  SArgumentNilNode = 'Node is nil';
+  SDuplicatesNotAllowed = 'Duplicates not allowed in dictionary';
+  SCollectionInconsistency = 'Collection inconsistency';
+  SCollectionDuplicate = 'Collection does not allow duplicates';
+  SDictionaryKeyDoesNotExist = 'Dictionary key does not exist';
+  SItemNotFound = 'Item not found';
+
+implementation
+
+end.
+

+ 2269 - 0
CryptoLib/src/3rdParty/generics.collections/src/inc/generics.dictionaries.inc

@@ -0,0 +1,2269 @@
+{%MainUnit generics.collections.pas}
+
+{
+    This file is part of the Free Pascal run time library.
+    Copyright (c) 2014 by Maciej Izak (hnb)
+    member of the Free Sparta development team (http://freesparta.com)
+
+    Copyright(c) 2004-2014 DaThoX
+
+    It contains the Free Pascal generics library
+
+    See the file COPYING.FPC, included in this distribution,
+    for details about the copyright.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+
+    Acknowledgment
+
+    Thanks to Sphere 10 Software (http://sphere10.com) for sponsoring
+    many new types and major refactoring of entire library
+
+    Thanks to mORMot (http://synopse.info) project for the best implementations
+    of hashing functions like crc32c and xxHash32 :)
+
+ **********************************************************************}
+
+{ TPair<TKey,TValue> }
+
+class function TPair<TKey, TValue>.Create(AKey: TKey;
+  AValue: TValue): TPair<TKey, TValue>;
+begin
+  Result.Key := AKey;
+  Result.Value := AValue;
+end;
+
+{ TCustomDictionary<CUSTOM_DICTIONARY_CONSTRAINTS> }
+
+procedure TCustomDictionary<CUSTOM_DICTIONARY_CONSTRAINTS>.PairNotify(constref APair: TDictionaryPair;
+  ACollectionNotification: TCollectionNotification);
+begin
+  KeyNotify(APair.Key, ACollectionNotification);
+  ValueNotify(APair.Value, ACollectionNotification);
+end;
+
+procedure TCustomDictionary<CUSTOM_DICTIONARY_CONSTRAINTS>.KeyNotify(constref AKey: TKey;
+  ACollectionNotification: TCollectionNotification);
+begin
+  if Assigned(FOnKeyNotify) then
+    FOnKeyNotify(Self, AKey, ACollectionNotification);
+end;
+
+procedure TCustomDictionary<CUSTOM_DICTIONARY_CONSTRAINTS>.SetValue(var AValue: TValue; constref ANewValue: TValue);
+var
+  LOldValue: TValue;
+begin
+  LOldValue := AValue;
+  AValue := ANewValue;
+
+  ValueNotify(LOldValue, cnRemoved);
+  ValueNotify(ANewValue, cnAdded);
+end;
+
+procedure TCustomDictionary<CUSTOM_DICTIONARY_CONSTRAINTS>.ValueNotify(constref AValue: TValue;
+  ACollectionNotification: TCollectionNotification);
+begin
+  if Assigned(FOnValueNotify) then
+    FOnValueNotify(Self, AValue, ACollectionNotification);
+end;
+
+constructor TCustomDictionary<CUSTOM_DICTIONARY_CONSTRAINTS>.Create;
+begin
+  Create(0);
+end;
+
+constructor TCustomDictionary<CUSTOM_DICTIONARY_CONSTRAINTS>.Create(ACapacity: SizeInt); overload;
+begin
+  Create(ACapacity, TEqualityComparer<TKey>.Default(THashFactory));
+end;
+
+constructor TCustomDictionary<CUSTOM_DICTIONARY_CONSTRAINTS>.Create(ACapacity: SizeInt;
+  const AComparer: IEqualityComparer<TKey>);
+begin
+  FEqualityComparer := AComparer;
+  SetCapacity(ACapacity);
+end;
+
+constructor TCustomDictionary<CUSTOM_DICTIONARY_CONSTRAINTS>.Create(const AComparer: IEqualityComparer<TKey>);
+begin
+  Create(0, AComparer);
+end;
+
+constructor TCustomDictionary<CUSTOM_DICTIONARY_CONSTRAINTS>.Create(ACollection: TEnumerable<TDictionaryPair>);
+begin
+  Create(ACollection, TEqualityComparer<TKey>.Default(THashFactory));
+end;
+
+{$IFDEF ENABLE_METHODS_WITH_TEnumerableWithPointers}
+constructor TCustomDictionary<CUSTOM_DICTIONARY_CONSTRAINTS>.Create(ACollection: TEnumerableWithPointers<TDictionaryPair>);
+begin
+  Create(ACollection, TEqualityComparer<TKey>.Default(THashFactory));
+end;
+{$ENDIF}
+
+constructor TCustomDictionary<CUSTOM_DICTIONARY_CONSTRAINTS>.Create(ACollection: TEnumerable<TDictionaryPair>;
+  const AComparer: IEqualityComparer<TKey>); overload;
+var
+  LItem: TDictionaryPair;
+begin
+  Create(AComparer);
+  for LItem in ACollection do
+    Add(LItem);
+end;
+
+{$IFDEF ENABLE_METHODS_WITH_TEnumerableWithPointers}
+constructor TCustomDictionary<CUSTOM_DICTIONARY_CONSTRAINTS>.Create(ACollection: TEnumerableWithPointers<TDictionaryPair>;
+  const AComparer: IEqualityComparer<TKey>); overload;
+var
+  LItem: PDictionaryPair;
+begin
+  Create(AComparer);
+  for LItem in ACollection.Ptr^ do
+    Add(LItem^);
+end;
+{$ENDIF}
+
+destructor TCustomDictionary<CUSTOM_DICTIONARY_CONSTRAINTS>.Destroy;
+begin
+  Clear;
+  FKeys.Free;
+  FValues.Free;
+  inherited;
+end;
+
+function TCustomDictionary<CUSTOM_DICTIONARY_CONSTRAINTS>.ToArray(ACount: SizeInt): TArray<TDictionaryPair>;
+var
+  i: SizeInt;
+  LEnumerator: TEnumerator<TDictionaryPair>;
+begin
+  SetLength(Result, ACount);
+  LEnumerator := DoGetEnumerator;
+
+  i := 0;
+  while LEnumerator.MoveNext do
+  begin
+    Result[i] := LEnumerator.Current;
+    Inc(i);
+  end;
+  LEnumerator.Free;
+end;
+
+function TCustomDictionary<CUSTOM_DICTIONARY_CONSTRAINTS>.ToArray: TArray<TDictionaryPair>;
+begin
+  Result := ToArray(Count);
+end;
+
+{ TCustomDictionaryEnumerator<T, CUSTOM_DICTIONARY_CONSTRAINTS> }
+
+constructor TCustomDictionaryEnumerator<T, CUSTOM_DICTIONARY_CONSTRAINTS>.Create(
+  ADictionary: TCustomDictionary<CUSTOM_DICTIONARY_CONSTRAINTS>);
+begin
+  inherited Create;
+  FIndex := -1;
+  FDictionary := ADictionary;
+end;
+
+function TCustomDictionaryEnumerator<T, CUSTOM_DICTIONARY_CONSTRAINTS>.DoGetCurrent: T;
+begin
+  Result := GetCurrent;
+end;
+
+{ TDictionaryEnumerable<TDictionaryEnumerator, TDictionaryPointersEnumerator, T, CUSTOM_DICTIONARY_CONSTRAINTS> }
+
+function TDictionaryEnumerable<TDictionaryEnumerator, TDictionaryPointersEnumerator, T, CUSTOM_DICTIONARY_CONSTRAINTS>.GetPtrEnumerator: TEnumerator<PT>;
+begin
+  Result := TDictionaryPointersEnumerator.Create(FDictionary);
+end;
+
+constructor TDictionaryEnumerable<TDictionaryEnumerator, TDictionaryPointersEnumerator, T, CUSTOM_DICTIONARY_CONSTRAINTS>.Create(
+  ADictionary: TCustomDictionary<CUSTOM_DICTIONARY_CONSTRAINTS>);
+begin
+  FDictionary := ADictionary;
+end;
+
+function TDictionaryEnumerable<TDictionaryEnumerator, TDictionaryPointersEnumerator, T, CUSTOM_DICTIONARY_CONSTRAINTS>.
+  DoGetEnumerator: TDictionaryEnumerator;
+begin
+  Result := TDictionaryEnumerator(TDictionaryEnumerator.NewInstance);
+  TCustomDictionaryEnumerator<T, CUSTOM_DICTIONARY_CONSTRAINTS>(Result).Create(FDictionary);
+end;
+
+function TDictionaryEnumerable<TDictionaryEnumerator, TDictionaryPointersEnumerator, T, CUSTOM_DICTIONARY_CONSTRAINTS>.GetCount: SizeInt;
+begin
+  Result := TCustomDictionary<CUSTOM_DICTIONARY_CONSTRAINTS>(FDictionary).Count;
+end;
+
+function TDictionaryEnumerable<TDictionaryEnumerator, TDictionaryPointersEnumerator, T, CUSTOM_DICTIONARY_CONSTRAINTS>.ToArray: TArray<T>;
+begin
+  Result := ToArrayImpl(FDictionary.Count);
+end;
+
+{ TOpenAddressingEnumerator<T, DICTIONARY_CONSTRAINTS> }
+
+function TOpenAddressingEnumerator<T, OPEN_ADDRESSING_CONSTRAINTS>.DoMoveNext: Boolean;
+var
+  LLength: SizeInt;
+begin
+  Inc(FIndex);
+
+  LLength := Length(TOpenAddressing<OPEN_ADDRESSING_CONSTRAINTS>(FDictionary).FItems);
+
+  if FIndex >= LLength then
+    Exit(False);
+
+  // maybe related to bug #24098
+  // compiler error for (TDictionary<DICTIONARY_CONSTRAINTS>(FDictionary).FItems[FIndex].Hash and UInt32.GetSignMask) = 0
+  while ((TOpenAddressing<OPEN_ADDRESSING_CONSTRAINTS>(FDictionary).FItems[FIndex].Hash) and UInt32.GetSignMask) = 0 do
+  begin
+    Inc(FIndex);
+    if FIndex = LLength then
+      Exit(False);
+  end;
+
+  Result := True;
+end;
+
+{ TOpenAddressingPointersEnumerator<TItem, PDictionaryPair> }
+
+function TOpenAddressingPointersEnumerator<TItem, PDictionaryPair>.DoMoveNext: boolean;
+var
+  LLength: SizeInt;
+begin
+  Inc(FIndex);
+
+  LLength := Length(FItems^);
+
+  if FIndex >= LLength then
+    Exit(False);
+
+  // maybe related to bug #24098
+  // compiler error for (TDictionary<DICTIONARY_CONSTRAINTS>(FDictionary).FItems[FIndex].Hash and UInt32.GetSignMask) = 0
+  while (FItems^[FIndex].Hash and UInt32.GetSignMask) = 0 do
+  begin
+    Inc(FIndex);
+    if FIndex = LLength then
+      Exit(False);
+  end;
+
+  Result := True;
+end;
+
+function TOpenAddressingPointersEnumerator<TItem, PDictionaryPair>.DoGetCurrent: PDictionaryPair;
+begin
+  Result := GetCurrent;
+end;
+
+function TOpenAddressingPointersEnumerator<TItem, PDictionaryPair>.GetCurrent: PDictionaryPair;
+begin
+  Result := @FItems^[FIndex].Pair;
+end;
+
+constructor TOpenAddressingPointersEnumerator<TItem, PDictionaryPair>.Create(var AItems);
+begin
+  FIndex := -1;
+  FItems := @AItems;
+end;
+
+{ TOpenAddressingPointersCollection<TPointersEnumerator, TItem, PDictionaryPair> }
+
+function TOpenAddressingPointersCollection<TPointersEnumerator, TItem, PDictionaryPair>.Items: PArray;
+begin
+  Result := PArray(@((@Self)^));
+end;
+
+function TOpenAddressingPointersCollection<TPointersEnumerator, TItem, PDictionaryPair>.GetCount: SizeInt;
+begin
+  Result := PSizeInt(PByte(@((@Self)^))-SizeOf(SizeInt))^;
+end;
+
+function TOpenAddressingPointersCollection<TPointersEnumerator, TItem, PDictionaryPair>.GetEnumerator: TPointersEnumerator;
+begin
+  Result := TPointersEnumerator(TPointersEnumerator.NewInstance);
+  TPointersEnumerator(Result).Create(Items^);
+end;
+
+function TOpenAddressingPointersCollection<TPointersEnumerator, TItem, PDictionaryPair>.ToArray: TArray<PDictionaryPair>;
+{begin
+  Result := ToArrayImpl(FList.Count);
+end;}
+var
+  i: SizeInt;
+  LEnumerator: TPointersEnumerator;
+begin
+  SetLength(Result, GetCount);
+
+  try
+    LEnumerator := GetEnumerator;
+
+    i := 0;
+    while LEnumerator.MoveNext do
+    begin
+      Result[i] := LEnumerator.Current;
+      Inc(i);
+    end;
+  finally
+    LEnumerator.Free;
+  end;
+end;
+
+{ TOpenAddressing<OPEN_ADDRESSING_CONSTRAINTS> }
+
+constructor TOpenAddressing<OPEN_ADDRESSING_CONSTRAINTS>.Create(ACapacity: SizeInt;
+  const AComparer: IEqualityComparer<TKey>);
+begin
+  inherited Create(ACapacity, AComparer);
+
+  FMaxLoadFactor := TProbeSequence.DEFAULT_LOAD_FACTOR;
+end;
+
+function TOpenAddressing<OPEN_ADDRESSING_CONSTRAINTS>.GetKeys: TKeyCollection;
+begin
+  if not Assigned(FKeys) then
+    FKeys := TKeyCollection.Create(Self);
+  Result := TKeyCollection(FKeys);
+end;
+
+function TOpenAddressing<OPEN_ADDRESSING_CONSTRAINTS>.GetValues: TValueCollection;
+begin
+  if not Assigned(FValues) then
+    FValues := TValueCollection.Create(Self);
+  Result := TValueCollection(FValues);
+end;
+
+function TOpenAddressing<OPEN_ADDRESSING_CONSTRAINTS>.FindBucketIndex(constref AKey: TKey): SizeInt;
+var
+  LHash: UInt32;
+begin
+  Result := FindBucketIndex(FItems, AKey, LHash);
+end;
+
+procedure TOpenAddressing<OPEN_ADDRESSING_CONSTRAINTS>.PrepareAddingItem;
+begin
+  if RealItemsLength > FItemsThreshold then
+    Rehash(Length(FItems) shl 1)
+  else if FItemsThreshold = 0 then
+  begin
+    SetLength(FItems, 8);
+    UpdateItemsThreshold(8);
+  end
+  else if FItemsLength = $40000001 then // High(TIndex) ... Error: Type mismatch
+    OutOfMemoryError;
+end;
+
+procedure TOpenAddressing<OPEN_ADDRESSING_CONSTRAINTS>.UpdateItemsThreshold(ASize: SizeInt);
+begin
+  if ASize = $40000000 then
+    FItemsThreshold := $40000001
+  else
+    FItemsThreshold := Pred(Round(ASize * FMaxLoadFactor));
+end;
+
+procedure TOpenAddressing<OPEN_ADDRESSING_CONSTRAINTS>.AddItem(var AItem: TItem; constref AKey: TKey;
+  constref AValue: TValue; const AHash: UInt32);
+begin
+  AItem.Hash := AHash;
+  AItem.Pair.Key := AKey;
+  AItem.Pair.Value := AValue;
+
+  // ! very important. FItemsLength must be increased after above code (because constref has meaning)
+  Inc(FItemsLength);
+
+  PairNotify(AItem.Pair, cnAdded);
+end;
+
+function TOpenAddressing<OPEN_ADDRESSING_CONSTRAINTS>.GetPointers: PPointersCollection;
+begin
+  Result := PPointersCollection(@FItems);
+end;
+
+procedure TOpenAddressing<OPEN_ADDRESSING_CONSTRAINTS>.Add(constref AKey: TKey; constref AValue: TValue);
+begin
+  DoAdd(AKey, AValue);
+end;
+
+procedure TOpenAddressing<OPEN_ADDRESSING_CONSTRAINTS>.Add(constref APair: TPair<TKey, TValue>);
+begin
+  DoAdd(APair.Key, APair.Value);
+end;
+
+function TOpenAddressing<OPEN_ADDRESSING_CONSTRAINTS>.DoAdd(constref AKey: TKey; constref AValue: TValue): SizeInt;
+var
+  LHash: UInt32;
+begin
+  PrepareAddingItem;
+
+  Result := FindBucketIndex(FItems, AKey, LHash);
+  if Result >= 0 then
+    raise EListError.CreateRes(@SDuplicatesNotAllowed);
+
+  Result := not Result;
+  AddItem(FItems[Result], AKey, AValue, LHash);
+end;
+
+function TOpenAddressing<OPEN_ADDRESSING_CONSTRAINTS>.DoRemove(AIndex: SizeInt;
+  ACollectionNotification: TCollectionNotification): TValue;
+var
+  LItem: PItem;
+  LPair: TPair<TKey, TValue>;
+begin
+  LItem := @FItems[AIndex];
+  LItem.Hash := 0;
+  Result := LItem.Pair.Value;
+  LPair := LItem.Pair;
+  LItem.Pair := Default(TPair<TKey, TValue>);
+  Dec(FItemsLength);
+  PairNotify(LPair, ACollectionNotification);
+end;
+
+procedure TOpenAddressing<OPEN_ADDRESSING_CONSTRAINTS>.Remove(constref AKey: TKey);
+var
+  LIndex: SizeInt;
+begin
+  LIndex := FindBucketIndex(AKey);
+  if LIndex < 0 then
+    Exit;
+
+  DoRemove(LIndex, cnRemoved);
+end;
+
+function TOpenAddressing<OPEN_ADDRESSING_CONSTRAINTS>.ExtractPair(constref AKey: TKey): TPair<TKey, TValue>;
+var
+  LIndex: SizeInt;
+begin
+  LIndex := FindBucketIndex(AKey);
+  if LIndex < 0 then
+    Exit(Default(TPair<TKey, TValue>));
+
+  Result.Key := AKey;
+  Result.Value := DoRemove(LIndex, cnExtracted);
+end;
+
+procedure TOpenAddressing<OPEN_ADDRESSING_CONSTRAINTS>.Clear;
+var
+  LItem: PItem;
+  i: SizeInt;
+  LOldItems: array of TItem;
+begin
+  FItemsLength := 0;
+  FItemsThreshold := 0;
+  // ClearTombstones;
+  LOldItems := FItems;
+  FItems := nil;
+
+  for i := 0 to High(LOldItems) do
+  begin
+    LItem := @LOldItems[i];
+    if (LItem.Hash and UInt32.GetSignMask = 0) then
+      Continue;
+
+    PairNotify(LItem.Pair, cnRemoved);
+  end;
+end;
+
+function TOpenAddressing<OPEN_ADDRESSING_CONSTRAINTS>.RealItemsLength: SizeInt;
+begin
+  Result := FItemsLength;
+end;
+
+function TOpenAddressing<OPEN_ADDRESSING_CONSTRAINTS>.Rehash(ASizePow2: SizeInt; AForce: Boolean): Boolean;
+var
+  LNewItems: TArray<TItem>;
+  LHash: UInt32;
+  LIndex: SizeInt;
+  i: SizeInt;
+  LItem, LNewItem: PItem;
+begin
+  if (ASizePow2 = Length(FItems)) and not AForce then
+    Exit(False);
+  if ASizePow2 < 0 then
+    OutOfMemoryError;
+
+  SetLength(LNewItems, ASizePow2);
+  UpdateItemsThreshold(ASizePow2);
+
+  for i := 0 to High(FItems) do
+  begin
+    LItem := @FItems[i];
+
+    if (LItem.Hash and UInt32.GetSignMask) <> 0 then
+    begin
+      LIndex := FindBucketIndex(LNewItems, LItem.Pair.Key, LHash);
+      LIndex := not LIndex;
+
+      LNewItem := @LNewItems[LIndex];
+      LNewItem.Hash := LHash;
+      LNewItem.Pair := LItem.Pair;
+    end;
+  end;
+
+  FItems := LNewItems;
+  Result := True;
+end;
+
+function TOpenAddressing<OPEN_ADDRESSING_CONSTRAINTS>.DoGetEnumerator: TEnumerator<TDictionaryPair>;
+begin
+  Result := GetEnumerator;
+end;
+
+procedure TOpenAddressing<OPEN_ADDRESSING_CONSTRAINTS>.SetCapacity(ACapacity: SizeInt);
+begin
+  if ACapacity < FItemsLength then
+    raise EArgumentOutOfRangeException.CreateRes(@SArgumentOutOfRange);
+
+  Resize(ACapacity);
+end;
+
+procedure TOpenAddressing<OPEN_ADDRESSING_CONSTRAINTS>.SetMaxLoadFactor(AValue: single);
+var
+  LItemsLength: SizeInt;
+begin
+  if (AValue > TProbeSequence.MAX_LOAD_FACTOR) or (AValue <= 0) then
+    raise EArgumentOutOfRangeException.CreateRes(@SArgumentOutOfRange);
+
+  FMaxLoadFactor := AValue;
+
+  repeat
+    LItemsLength := Length(FItems);
+    UpdateItemsThreshold(LItemsLength);
+    if RealItemsLength > FItemsThreshold then
+      Rehash(LItemsLength shl 1);
+  until RealItemsLength <= FItemsThreshold;
+end;
+
+function TOpenAddressing<OPEN_ADDRESSING_CONSTRAINTS>.GetLoadFactor: single;
+begin
+  Result := FItemsLength / Length(FItems);
+end;
+
+function TOpenAddressing<OPEN_ADDRESSING_CONSTRAINTS>.GetCapacity: SizeInt;
+begin
+  Result := Length(FItems);
+end;
+
+procedure TOpenAddressing<OPEN_ADDRESSING_CONSTRAINTS>.Resize(ANewSize: SizeInt);
+var
+  LNewSize: SizeInt;
+begin
+  if ANewSize < 0 then
+    raise EArgumentOutOfRangeException.CreateRes(@SArgumentOutOfRange);
+
+  LNewSize := 0;
+  if ANewSize > 0 then
+  begin
+    LNewSize := 8;
+    while LNewSize < ANewSize do
+      LNewSize := LNewSize shl 1;
+  end;
+
+  Rehash(LNewSize);
+end;
+
+function TOpenAddressing<OPEN_ADDRESSING_CONSTRAINTS>.GetEnumerator: TPairEnumerator;
+begin
+  Result := TPairEnumerator.Create(Self);
+end;
+
+function TOpenAddressing<OPEN_ADDRESSING_CONSTRAINTS>.GetItem(const AKey: TKey): TValue;
+var
+  LIndex: SizeInt;
+begin
+  LIndex := FindBucketIndex(AKey);
+  if LIndex < 0 then
+    raise EListError.CreateRes(@SDictionaryKeyDoesNotExist);
+  Result := FItems[LIndex].Pair.Value;
+end;
+
+procedure TOpenAddressing<OPEN_ADDRESSING_CONSTRAINTS>.TrimExcess;
+begin
+  SetCapacity(Succ(FItemsLength));
+end;
+
+procedure TOpenAddressing<OPEN_ADDRESSING_CONSTRAINTS>.SetItem(const AKey: TKey; const AValue: TValue);
+var
+  LIndex: SizeInt;
+begin
+  LIndex := FindBucketIndex(AKey);
+  if LIndex < 0 then
+    raise EListError.CreateRes(@SItemNotFound);
+
+  SetValue(FItems[LIndex].Pair.Value, AValue);
+end;
+
+function TOpenAddressing<OPEN_ADDRESSING_CONSTRAINTS>.TryGetValue(constref AKey: TKey; out AValue: TValue): Boolean;
+var
+  LIndex: SizeInt;
+begin
+  LIndex := FindBucketIndex(AKey);
+  Result := LIndex >= 0;
+
+  if Result then
+    AValue := FItems[LIndex].Pair.Value
+  else
+    AValue := Default(TValue);
+end;
+
+procedure TOpenAddressing<OPEN_ADDRESSING_CONSTRAINTS>.AddOrSetValue(constref AKey: TKey; constref AValue: TValue);
+var
+  LIndex: SizeInt;
+  LHash: UInt32;
+begin
+  LIndex := FindBucketIndex(FItems, AKey, LHash);
+
+  if LIndex < 0 then
+    DoAdd(AKey, AValue)
+  else
+    SetValue(FItems[LIndex].Pair.Value, AValue);
+end;
+
+function TOpenAddressing<OPEN_ADDRESSING_CONSTRAINTS>.ContainsKey(constref AKey: TKey): Boolean;
+var
+  LIndex: SizeInt;
+begin
+  LIndex := FindBucketIndex(AKey);
+  Result := LIndex >= 0;
+end;
+
+function TOpenAddressing<OPEN_ADDRESSING_CONSTRAINTS>.ContainsValue(constref AValue: TValue): Boolean;
+begin
+  Result := ContainsValue(AValue, TEqualityComparer<TValue>.Default(THashFactory));
+end;
+
+function TOpenAddressing<OPEN_ADDRESSING_CONSTRAINTS>.ContainsValue(constref AValue: TValue;
+  const AEqualityComparer: IEqualityComparer<TValue>): Boolean;
+var
+  i: SizeInt;
+  LItem: PItem;
+begin
+  if Length(FItems) = 0 then
+    Exit(False);
+
+  for i := 0 to High(FItems) do
+  begin
+    LItem := @FItems[i];
+    if (LItem.Hash and UInt32.GetSignMask) = 0 then
+      Continue;
+
+    if AEqualityComparer.Equals(AValue, LItem.Pair.Value) then
+      Exit(True);
+  end;
+  Result := False;
+end;
+
+procedure TOpenAddressing<OPEN_ADDRESSING_CONSTRAINTS>.GetMemoryLayout(
+  const AOnGetMemoryLayoutKeyPosition: TOnGetMemoryLayoutKeyPosition);
+var
+  i: SizeInt;
+begin
+  for i := 0 to High(FItems) do
+    if (FItems[i].Hash and UInt32.GetSignMask) <> 0 then
+      AOnGetMemoryLayoutKeyPosition(Self, i);
+end;
+
+{ TOpenAddressing<OPEN_ADDRESSING_CONSTRAINTS>.TPairEnumerator }
+
+function TOpenAddressing<OPEN_ADDRESSING_CONSTRAINTS>.TPairEnumerator.GetCurrent: TPair<TKey, TValue>;
+begin
+  Result := TOpenAddressing<OPEN_ADDRESSING_CONSTRAINTS>(FDictionary).FItems[FIndex].Pair;
+end;
+
+{ TOpenAddressing<OPEN_ADDRESSING_CONSTRAINTS>.TValueEnumerator }
+
+function TOpenAddressing<OPEN_ADDRESSING_CONSTRAINTS>.TValueEnumerator.GetCurrent: TValue;
+begin
+  Result := TOpenAddressing<OPEN_ADDRESSING_CONSTRAINTS>(FDictionary).FItems[FIndex].Pair.Value;
+end;
+
+{ TOpenAddressing<OPEN_ADDRESSING_CONSTRAINTS>.TPValueEnumerator }
+
+function TOpenAddressing<OPEN_ADDRESSING_CONSTRAINTS>.TPValueEnumerator.GetCurrent: PValue;
+begin
+  Result := @(TOpenAddressing<OPEN_ADDRESSING_CONSTRAINTS>(FDictionary).FItems[FIndex].Pair.Value);
+end;
+
+{ TOpenAddressing<OPEN_ADDRESSING_CONSTRAINTS>.TKeyEnumerator }
+
+function TOpenAddressing<OPEN_ADDRESSING_CONSTRAINTS>.TKeyEnumerator.GetCurrent: TKey;
+begin
+  Result := TOpenAddressing<OPEN_ADDRESSING_CONSTRAINTS>(FDictionary).FItems[FIndex].Pair.Key;
+end;
+
+{ TOpenAddressing<OPEN_ADDRESSING_CONSTRAINTS>.TPKeyEnumerator }
+
+function TOpenAddressing<OPEN_ADDRESSING_CONSTRAINTS>.TPKeyEnumerator.GetCurrent: PKey;
+begin
+  Result := @(TOpenAddressing<OPEN_ADDRESSING_CONSTRAINTS>(FDictionary).FItems[FIndex].Pair.Key);
+end;
+
+{ TOpenAddressingLP<DICTIONARY_CONSTRAINTS> }
+
+procedure TOpenAddressingLP<OPEN_ADDRESSING_CONSTRAINTS>.NotifyIndexChange(AFrom, ATo: SizeInt);
+begin
+end;
+
+function TOpenAddressingLP<OPEN_ADDRESSING_CONSTRAINTS>.DoRemove(AIndex: SizeInt;
+  ACollectionNotification: TCollectionNotification): TValue;
+var
+  LItem: PItem;
+  LPair: TPair<TKey, TValue>;
+  LLengthMask: SizeInt;
+  i, LIndex, LGapIndex: SizeInt;
+  LHash, LBucket: UInt32;
+begin
+  LItem := @FItems[AIndex];
+  LPair := LItem.Pair;
+
+  // try fill gap
+  LHash := LItem.Hash;
+  LItem.Hash := 0; // prevents an infinite searching loop
+  LLengthMask := Length(FItems) - 1;
+  i := Succ(AIndex - (LHash and LLengthMask));
+  LGapIndex := AIndex;
+  repeat
+    LIndex := TProbeSequence.Probe(i, LHash) and LLengthMask;
+    LItem := @FItems[LIndex];
+
+    // Empty position
+    if (LItem.Hash and UInt32.GetSignMask) = 0 then
+      Break; // breaking bad!
+
+    LBucket := LItem.Hash and LLengthMask;
+    if not InCircularRange(LGapIndex, LBucket, LIndex) then
+    begin
+      NotifyIndexChange(LIndex, LGapIndex);
+      FItems[LGapIndex] := LItem^;
+      LItem.Hash := 0; // new gap
+      LGapIndex := LIndex;
+    end;
+    Inc(i);
+  until false;
+
+  LItem := @FItems[LGapIndex];
+  LItem.Hash := 0;
+  LItem.Pair := Default(TPair<TKey, TValue>);
+  Dec(FItemsLength);
+
+  Result := LPair.Value;
+  PairNotify(LPair, ACollectionNotification);
+end;
+
+function TOpenAddressingLP<OPEN_ADDRESSING_CONSTRAINTS>.FindBucketIndex(constref AItems: TArray<TItem>;
+  constref AKey: TKey; out AHash: UInt32): SizeInt;
+var
+  LItem: {TOpenAddressing<OPEN_ADDRESSING_CONSTRAINTS>.}_TItem; // for workaround Lazarus bug #25613
+  LLengthMask: SizeInt;
+  i, m: SizeInt;
+  LHash: UInt32;
+begin
+  m := Length(AItems);
+  LLengthMask := m - 1;
+
+  LHash := FEqualityComparer.GetHashCode(AKey);
+
+  i := 0;
+  AHash := LHash or UInt32.GetSignMask;
+
+  if m = 0 then
+    Exit(-1);
+
+  Result := AHash and LLengthMask;
+
+  repeat
+    LItem := _TItem(AItems[Result]);
+
+    // Empty position
+    if (LItem.Hash and UInt32.GetSignMask) = 0 then
+      Exit(not Result); // insert!
+
+    // Same position?
+    if LItem.Hash = AHash then
+      if FEqualityComparer.Equals(AKey, LItem.Pair.Key) then
+        Exit;
+
+    Inc(i);
+
+    Result := TProbeSequence.Probe(i, AHash) and LLengthMask;
+
+  until false;
+end;
+
+{ TOpenAddressingTombstones<OPEN_ADDRESSING_CONSTRAINTS> }
+
+function TOpenAddressingTombstones<OPEN_ADDRESSING_CONSTRAINTS>.Rehash(ASizePow2: SizeInt; AForce: Boolean): Boolean;
+begin
+  if inherited then
+    FTombstonesCount := 0;
+end;
+
+function TOpenAddressingTombstones<OPEN_ADDRESSING_CONSTRAINTS>.RealItemsLength: SizeInt;
+begin
+  Result := FItemsLength + FTombstonesCount
+end;
+
+procedure TOpenAddressingTombstones<OPEN_ADDRESSING_CONSTRAINTS>.ClearTombstones;
+begin
+  Rehash(Length(FItems), True);
+end;
+
+procedure TOpenAddressingTombstones<OPEN_ADDRESSING_CONSTRAINTS>.Clear;
+begin
+  FTombstonesCount := 0;
+  inherited;
+end;
+
+function TOpenAddressingTombstones<OPEN_ADDRESSING_CONSTRAINTS>.DoRemove(AIndex: SizeInt;
+  ACollectionNotification: TCollectionNotification): TValue;
+begin
+  Result := inherited;
+
+  FItems[AIndex].Hash := 1;
+  Inc(FTombstonesCount);
+end;
+
+function TOpenAddressingTombstones<OPEN_ADDRESSING_CONSTRAINTS>.DoAdd(constref AKey: TKey;
+  constref AValue: TValue): SizeInt;
+var
+  LHash: UInt32;
+begin
+  PrepareAddingItem;
+
+  Result := FindBucketIndexOrTombstone(FItems, AKey, LHash);
+  if Result >= 0 then
+    raise EListError.CreateRes(@SDuplicatesNotAllowed);
+
+  Result := not Result;
+  // Can't ovverride because we lost info about old hash
+  if FItems[Result].Hash <> 0 then
+    Dec(FTombstonesCount);
+
+  AddItem(FItems[Result], AKey, AValue, LHash);
+end;
+
+{ TOpenAddressingSH<OPEN_ADDRESSING_CONSTRAINTS> }
+
+function TOpenAddressingSH<OPEN_ADDRESSING_CONSTRAINTS>.FindBucketIndex(constref AItems: TArray<TItem>;
+  constref AKey: TKey; out AHash: UInt32): SizeInt;
+var
+  LItem: {TOpenAddressing<OPEN_ADDRESSING_CONSTRAINTS>.}_TItem; // for workaround Lazarus bug #25613
+  LLengthMask: SizeInt;
+  i, m: SizeInt;
+  LHash: UInt32;
+begin
+  m := Length(AItems);
+  LLengthMask := m - 1;
+
+  LHash := FEqualityComparer.GetHashCode(AKey);
+
+  i := 0;
+  AHash := LHash or UInt32.GetSignMask;
+
+  if m = 0 then
+    Exit(-1);
+
+  Result := AHash and LLengthMask;
+
+  repeat
+    LItem := _TItem(AItems[Result]);
+    // Empty position
+    if LItem.Hash = 0 then
+      Exit(not Result); // insert!
+
+    // Same position?
+    if LItem.Hash = AHash then
+      if FEqualityComparer.Equals(AKey, LItem.Pair.Key) then
+        Exit;
+
+    Inc(i);
+
+    Result := TProbeSequence.Probe(i, AHash) and LLengthMask;
+
+  until false;
+end;
+
+function TOpenAddressingSH<OPEN_ADDRESSING_CONSTRAINTS>.FindBucketIndexOrTombstone(constref AItems: TArray<TItem>;
+  constref AKey: TKey; out AHash: UInt32): SizeInt;
+var
+  LItem: {TOpenAddressing<OPEN_ADDRESSING_CONSTRAINTS>.}_TItem; // for workaround Lazarus bug #25613
+  LLengthMask: SizeInt;
+  i, m: SizeInt;
+  LHash: UInt32;
+begin
+  m := Length(AItems);
+  LLengthMask := m - 1;
+
+  LHash := FEqualityComparer.GetHashCode(AKey);
+
+  i := 0;
+  AHash := LHash or UInt32.GetSignMask;
+
+  if m = 0 then
+    Exit(-1);
+
+  Result := AHash and LLengthMask;
+
+  repeat
+    LItem := _TItem(AItems[Result]);
+
+    // Empty position or tombstone
+    if LItem.Hash and UInt32.GetSignMask = 0 then
+      Exit(not Result); // insert!
+
+    // Same position?
+    if LItem.Hash = AHash then
+      if FEqualityComparer.Equals(AKey, LItem.Pair.Key) then
+        Exit;
+
+    Inc(i);
+
+    Result := TProbeSequence.Probe(i, AHash) and LLengthMask;
+
+  until false;
+end;
+
+{ TOpenAddressingQP<OPEN_ADDRESSING_CONSTRAINTS> }
+
+procedure TOpenAddressingQP<OPEN_ADDRESSING_CONSTRAINTS>.UpdateItemsThreshold(ASize: SizeInt);
+begin
+  if ASize = $40000000 then
+    FItemsThreshold := $40000001
+  else
+    begin
+      FPrimaryNumberAsSizeApproximation := PrimaryNumbersJustLessThanPowerOfTwo[
+        MultiplyDeBruijnBitPosition[UInt32(((ASize and -ASize) * $077CB531)) shr 27]];
+
+      FItemsThreshold := Pred(Round(FPrimaryNumberAsSizeApproximation * FMaxLoadFactor));
+    end;
+end;
+
+function TOpenAddressingQP<OPEN_ADDRESSING_CONSTRAINTS>.FindBucketIndex(constref AItems: TArray<TItem>;
+  constref AKey: TKey; out AHash: UInt32): SizeInt;
+var
+  LItem: {TOpenAddressing<OPEN_ADDRESSING_CONSTRAINTS>.}_TItem; // for workaround Lazarus bug #25613
+  i: SizeInt;
+  LHash: UInt32;
+begin
+  LHash := FEqualityComparer.GetHashCode(AKey);
+
+  i := 0;
+  AHash := LHash or UInt32.GetSignMask;
+
+  if Length(AItems) = 0 then
+    Exit(-1);
+
+  for i := 0 to FPrimaryNumberAsSizeApproximation - 1 do
+  begin
+    Result := TProbeSequence.Probe(i, AHash) mod FPrimaryNumberAsSizeApproximation;
+    LItem := _TItem(AItems[Result]);
+
+    // Empty position
+    if LItem.Hash = 0 then
+      Exit(not Result); // insert!
+
+    // Same position?
+    if LItem.Hash = AHash then
+      if FEqualityComparer.Equals(AKey, LItem.Pair.Key) then
+        Exit;
+  end;
+
+  Result := -1;
+end;
+
+
+function TOpenAddressingQP<OPEN_ADDRESSING_CONSTRAINTS>.FindBucketIndexOrTombstone(constref AItems: TArray<TItem>;
+  constref AKey: TKey; out AHash: UInt32): SizeInt;
+var
+  LItem: {TOpenAddressing<OPEN_ADDRESSING_CONSTRAINTS>.}_TItem; // for workaround Lazarus bug #25613
+  i: SizeInt;
+  LHash: UInt32;
+begin
+  LHash := FEqualityComparer.GetHashCode(AKey);
+
+  i := 0;
+  AHash := LHash or UInt32.GetSignMask;
+
+  if Length(AItems) = 0 then
+    Exit(-1);
+
+  for i := 0 to FPrimaryNumberAsSizeApproximation - 1 do
+  begin
+    Result := TProbeSequence.Probe(i, AHash) mod FPrimaryNumberAsSizeApproximation;
+    LItem := _TItem(AItems[Result]);
+
+    // Empty position or tombstone
+    if LItem.Hash and UInt32.GetSignMask = 0 then
+      Exit(not Result); // insert!
+
+    // Same position?
+    if LItem.Hash = AHash then
+      if FEqualityComparer.Equals(AKey, LItem.Pair.Key) then
+        Exit;
+  end;
+
+  Result := -1;
+end;
+
+{ TOpenAddressingDH<OPEN_ADDRESSING_CONSTRAINTS> }
+
+constructor TOpenAddressingDH<OPEN_ADDRESSING_CONSTRAINTS>.Create(ACapacity: SizeInt;
+  const AComparer: IEqualityComparer<TKey>);
+begin
+end;
+
+constructor TOpenAddressingDH<OPEN_ADDRESSING_CONSTRAINTS>.Create(const AComparer: IEqualityComparer<TKey>);
+begin
+end;
+
+constructor TOpenAddressingDH<OPEN_ADDRESSING_CONSTRAINTS>.Create(ACollection: TEnumerable<TDictionaryPair>;
+  const AComparer: IEqualityComparer<TKey>);
+begin
+end;
+
+{$IFDEF ENABLE_METHODS_WITH_TEnumerableWithPointers}
+constructor TOpenAddressingDH<OPEN_ADDRESSING_CONSTRAINTS>.Create(ACollection: TEnumerableWithPointers<TDictionaryPair>;
+  const AComparer: IEqualityComparer<TKey>);
+begin
+end;
+{$ENDIF}
+
+constructor TOpenAddressingDH<OPEN_ADDRESSING_CONSTRAINTS>.Create(ACapacity: SizeInt);
+begin
+  Create(ACapacity, TExtendedEqualityComparer<TKey>.Default(THashFactory));
+end;
+
+constructor TOpenAddressingDH<OPEN_ADDRESSING_CONSTRAINTS>.Create(ACollection: TEnumerable<TDictionaryPair>);
+begin
+  Create(ACollection, TExtendedEqualityComparer<TKey>.Default(THashFactory));
+end;
+
+{$IFDEF ENABLE_METHODS_WITH_TEnumerableWithPointers}
+constructor TOpenAddressingDH<OPEN_ADDRESSING_CONSTRAINTS>.Create(ACollection: TEnumerableWithPointers<TDictionaryPair>);
+begin
+  Create(ACollection, TExtendedEqualityComparer<TKey>.Default(THashFactory));
+end;
+{$ENDIF}
+
+constructor TOpenAddressingDH<OPEN_ADDRESSING_CONSTRAINTS>.Create(ACapacity: SizeInt;
+  const AComparer: IExtendedEqualityComparer<TKey>);
+begin
+  FMaxLoadFactor := TProbeSequence.DEFAULT_LOAD_FACTOR;
+  FEqualityComparer := AComparer;
+  SetCapacity(ACapacity);
+end;
+
+constructor TOpenAddressingDH<OPEN_ADDRESSING_CONSTRAINTS>.Create(const AComparer: IExtendedEqualityComparer<TKey>);
+begin
+  Create(0, AComparer);
+end;
+
+constructor TOpenAddressingDH<OPEN_ADDRESSING_CONSTRAINTS>.Create(ACollection: TEnumerable<TDictionaryPair>;
+  const AComparer: IExtendedEqualityComparer<TKey>);
+var
+  LItem: TDictionaryPair;
+begin
+  Create(AComparer);
+  for LItem in ACollection do
+    Add(LItem);
+end;
+
+{$IFDEF ENABLE_METHODS_WITH_TEnumerableWithPointers}
+constructor TOpenAddressingDH<OPEN_ADDRESSING_CONSTRAINTS>.Create(ACollection: TEnumerableWithPointers<TDictionaryPair>;
+  const AComparer: IExtendedEqualityComparer<TKey>);
+var
+  LItem: PDictionaryPair;
+begin
+  Create(AComparer);
+  for LItem in ACollection.Ptr^ do
+    Add(LItem^);
+end;
+{$ENDIF}
+
+procedure TOpenAddressingDH<OPEN_ADDRESSING_CONSTRAINTS>.UpdateItemsThreshold(ASize: SizeInt);
+begin
+  inherited;
+  R :=
+    PrimaryNumbersJustLessThanPowerOfTwo[
+      MultiplyDeBruijnBitPosition[UInt32(((ASize and -ASize) * $077CB531)) shr 27]]
+end;
+
+function TOpenAddressingDH<OPEN_ADDRESSING_CONSTRAINTS>.FindBucketIndex(constref AItems: TArray<TItem>;
+  constref AKey: TKey; out AHash: UInt32): SizeInt;
+var
+  LItem: {TOpenAddressing<OPEN_ADDRESSING_CONSTRAINTS>.}_TItem; // for workaround Lazarus bug #25613
+  LLengthMask: SizeInt;
+  i, m: SizeInt;
+  LHash: array[-1..1] of UInt32;
+  LHash1: UInt32 absolute LHash[0];
+  LHash2: UInt32 absolute LHash[1];
+begin
+  m := Length(AItems);
+  LLengthMask := m - 1;
+  LHash[-1] := 2; // number of hashes
+
+  IExtendedEqualityComparer<TKey>(FEqualityComparer).GetHashList(AKey, @LHash[-1]);
+
+  i := 0;
+  AHash := LHash1 or UInt32.GetSignMask;
+
+  if m = 0 then
+    Exit(-1);
+
+  Result := LHash1 and LLengthMask;
+  // second hash function must be special
+  LHash2 := (R - (LHash2 mod R)) or 1;
+
+  repeat
+    LItem := _TItem(AItems[Result]);
+
+    // Empty position
+    if LItem.Hash = 0 then
+      Exit(not Result);
+
+    // Same position?
+    if LItem.Hash = AHash then
+      if FEqualityComparer.Equals(AKey, LItem.Pair.Key) then
+        Exit;
+
+    Inc(i);
+
+    Result := TProbeSequence.Probe(i, AHash, LHash2) and LLengthMask;
+  until false;
+end;
+
+function TOpenAddressingDH<OPEN_ADDRESSING_CONSTRAINTS>.FindBucketIndexOrTombstone(constref AItems: TArray<TItem>;
+  constref AKey: TKey; out AHash: UInt32): SizeInt;
+var
+  LItem: {TOpenAddressing<OPEN_ADDRESSING_CONSTRAINTS>.}_TItem; // for workaround Lazarus bug #25613
+  LLengthMask: SizeInt;
+  i, m: SizeInt;
+  LHash: array[-1..1] of UInt32;
+  LHash1: UInt32 absolute LHash[0];
+  LHash2: UInt32 absolute LHash[1];
+begin
+  m := Length(AItems);
+  LLengthMask := m - 1;
+  LHash[-1] := 2; // number of hashes
+
+  IExtendedEqualityComparer<TKey>(FEqualityComparer).GetHashList(AKey, @LHash[-1]);
+
+  i := 0;
+  AHash := LHash1 or UInt32.GetSignMask;
+
+  if m = 0 then
+    Exit(-1);
+
+  Result := LHash1 and LLengthMask;
+  // second hash function must be special
+  LHash2 := (R - (LHash2 mod R)) or 1;
+
+  repeat
+    LItem := _TItem(AItems[Result]);
+
+    // Empty position or tombstone
+    if LItem.Hash and UInt32.GetSignMask = 0 then
+      Exit(not Result);
+
+    // Same position?
+    if LItem.Hash = AHash then
+      if FEqualityComparer.Equals(AKey, LItem.Pair.Key) then
+        Exit;
+
+    Inc(i);
+
+    Result := TProbeSequence.Probe(i, AHash, LHash2) and LLengthMask;
+  until false;
+end;
+
+{ TDeamortizedDArrayCuckooMapEnumerator<T, CUCKOO_CONSTRAINTS> }
+
+constructor TDeamortizedDArrayCuckooMapEnumerator<T, CUCKOO_CONSTRAINTS>.Create(
+  ADictionary: TCustomDictionary<CUSTOM_DICTIONARY_CONSTRAINTS>);
+begin
+  inherited;
+  if ADictionary.Count = 0 then
+    FMainIndex := TCuckooCfg.D
+  else
+    FMainIndex := 0;
+end;
+
+function TDeamortizedDArrayCuckooMapEnumerator<T, CUCKOO_CONSTRAINTS>.DoMoveNext: Boolean;
+var
+  LLength: SizeInt;
+  LArray: TItemsArray;
+begin
+  Inc(FIndex);
+
+  if (FMainIndex = TCuckooCfg.D) then // queue
+  begin
+    LLength := Length(TDeamortizedDArrayCuckooMap<CUCKOO_CONSTRAINTS>(FDictionary).FQueue.FItems);
+    if FIndex >= LLength then
+      Exit(False);
+
+    while ((TDeamortizedDArrayCuckooMap<CUCKOO_CONSTRAINTS>(FDictionary).FQueue.FItems[FIndex].Hash)
+      and UInt32.GetSignMask) = 0 do
+    begin
+      Inc(FIndex);
+      if FIndex = LLength then
+        Exit(False);
+    end;
+  end
+  else // d-array
+  begin
+    LArray := TItemsArray(TDeamortizedDArrayCuckooMap<CUCKOO_CONSTRAINTS>(FDictionary).FItems[FMainIndex]);
+    LLength := Length(LArray);
+    if FIndex >= LLength then
+    begin
+      Inc(FMainIndex);
+      FIndex := -1;
+      Exit(DoMoveNext);
+    end;
+
+    while ((LArray[FIndex].Hash) and UInt32.GetSignMask) = 0 do
+    begin
+      Inc(FIndex);
+      if FIndex = LLength then
+      begin
+        Inc(FMainIndex);
+        FIndex := -1;
+        Exit(DoMoveNext);
+      end;
+    end;
+  end;
+
+  Result := True;
+end;
+
+{ TDeamortizedDArrayPointersEnumerator<TCuckooCfg, TItemsArray, TItemsDArray, TQueueDictionary, PDictionaryPair> }
+
+function TDeamortizedDArrayPointersEnumerator<TCuckooCfg, TItemsArray, TItemsDArray, TQueueDictionary, PDictionaryPair>.DoMoveNext: boolean;
+var
+  LLength: SizeInt;
+  LArray: TItemsArray;
+begin
+  Inc(FIndex);
+
+  if (FMainIndex = TCuckooCfg.D) then // queue
+  begin
+    LLength := Length(FQueue.FItems);
+    if FIndex >= LLength then
+      Exit(False);
+
+    while ((FQueue.FItems[FIndex].Hash)
+      and UInt32.GetSignMask) = 0 do
+    begin
+      Inc(FIndex);
+      if FIndex = LLength then
+        Exit(False);
+    end;
+  end
+  else // d-array
+  begin
+    LArray := FItems^[FMainIndex];
+    LLength := Length(LArray);
+    if FIndex >= LLength then
+    begin
+      Inc(FMainIndex);
+      FIndex := -1;
+      Exit(DoMoveNext);
+    end;
+
+    while (((LArray[FIndex]).Hash) and UInt32.GetSignMask) = 0 do
+    begin
+      Inc(FIndex);
+      if FIndex = LLength then
+      begin
+        Inc(FMainIndex);
+        FIndex := -1;
+        Exit(DoMoveNext);
+      end;
+    end;
+  end;
+
+  Result := True;
+end;
+
+function TDeamortizedDArrayPointersEnumerator<TCuckooCfg, TItemsArray, TItemsDArray, TQueueDictionary, PDictionaryPair>.DoGetCurrent: PDictionaryPair;
+begin
+  Result := GetCurrent;
+end;
+
+function TDeamortizedDArrayPointersEnumerator<TCuckooCfg, TItemsArray, TItemsDArray, TQueueDictionary, PDictionaryPair>.GetCurrent: PDictionaryPair;
+begin
+  if FMainIndex = TCuckooCfg.D then
+    Result := @(FQueue.FItems[FIndex].Pair.Value.Pair)
+  else
+    Result := @((FItems^[FMainIndex])[FIndex].Pair);
+end;
+
+constructor TDeamortizedDArrayPointersEnumerator<TCuckooCfg, TItemsArray, TItemsDArray, TQueueDictionary, PDictionaryPair>.Create(var AItems; AQueue: TQueueDictionary; ACount: SizeInt);
+begin
+  FIndex := -1;
+  if ACount = 0 then
+    FMainIndex := TCuckooCfg.D
+  else
+    FMainIndex := 0;
+  FQueue := AQueue;
+  FItems := @AItems;
+end;
+
+{ TDeamortizedDArrayPointersCollection<TPointersEnumerator, TItem, TQueueDictionary, PDictionaryPair> }
+
+function TDeamortizedDArrayPointersCollection<TPointersEnumerator, TItemsDArray, TQueueDictionary, PDictionaryPair>.Items: PArray;
+begin
+  Result := PArray(@((@Self)^));
+end;
+
+function TDeamortizedDArrayPointersCollection<TPointersEnumerator, TItemsDArray, TQueueDictionary, PDictionaryPair>.GetCount: SizeInt;
+begin
+  Result := SizeInt((@PByte(@((@Self)^))[-SizeOf(SizeInt)])^);
+end;
+
+function TDeamortizedDArrayPointersCollection<TPointersEnumerator, TItemsDArray, TQueueDictionary, PDictionaryPair>.GetQueue: TQueueDictionary;
+begin
+  Result := TQueueDictionary((@PByte(@((@Self)^))[SizeOf(TItemsDArray)])^);
+end;
+
+function TDeamortizedDArrayPointersCollection<TPointersEnumerator, TItemsDArray, TQueueDictionary, PDictionaryPair>.GetEnumerator: TPointersEnumerator;
+begin
+  Result := TPointersEnumerator(TPointersEnumerator.NewInstance);
+  TPointersEnumerator(Result).Create(Items^, GetQueue, GetCount);
+end;
+
+function TDeamortizedDArrayPointersCollection<TPointersEnumerator, TItemsDArray, TQueueDictionary, PDictionaryPair>.ToArray: TArray<PDictionaryPair>;
+{begin
+  Result := ToArrayImpl(FList.Count);
+end;}
+var
+  i: SizeInt;
+  LEnumerator: TPointersEnumerator;
+begin
+  SetLength(Result, GetCount);
+
+  try
+    LEnumerator := GetEnumerator;
+
+    i := 0;
+    while LEnumerator.MoveNext do
+    begin
+      Result[i] := LEnumerator.Current;
+      Inc(i);
+    end;
+  finally
+    LEnumerator.Free;
+  end;
+end;
+
+{ TDeamortizedDArrayCuckooMap<CUCKOO_CONSTRAINTS> }
+
+function TDeamortizedDArrayCuckooMap<CUCKOO_CONSTRAINTS>.TQueueDictionary.Rehash(ASizePow2: SizeInt;
+  AForce: boolean): Boolean;
+var
+  FOldIdx: array of TKey;
+  i: SizeInt;
+begin
+  SetLength(FOldIdx, FIdx.Count);
+  for i := 0 to FIdx.Count - 1 do
+    FOldIdx[i] := FItems[FIdx[i]].Pair.Key;
+
+  Result := inherited Rehash(ASizePow2, AForce);
+
+  for i := 0 to FIdx.Count - 1 do
+    FIdx[i] := FindBucketIndex(FOldIdx[i]);
+end;
+
+procedure TDeamortizedDArrayCuckooMap<CUCKOO_CONSTRAINTS>.TQueueDictionary.NotifyIndexChange(AFrom, ATo: SizeInt);
+var
+  i: SizeInt;
+begin
+  // notify change position
+  for i := 0 to FIdx.Count-1 do
+    if FIdx[i] = AFrom then
+    begin
+      FIdx[i] := ATo;
+      Exit;
+    end;
+end;
+
+procedure TDeamortizedDArrayCuckooMap<CUCKOO_CONSTRAINTS>.TQueueDictionary.InsertIntoBack(AItem: Pointer);
+//var
+//  LItem: TDeamortizedDArrayCuckooMap<CUCKOO_CONSTRAINTS>.PItem; absolute AItem; !!! bug #25917
+var
+  LItem: TQueueDictionary.PValue absolute AItem;
+  LIndex: SizeInt;
+begin
+  LIndex := DoAdd(LItem.Pair.Key, LItem^);
+  FIdx.Insert(0, LIndex);
+end;
+
+procedure TDeamortizedDArrayCuckooMap<CUCKOO_CONSTRAINTS>.TQueueDictionary.InsertIntoHead(AItem: Pointer);
+//var
+//  LItem: TDeamortizedDArrayCuckooMap<CUCKOO_CONSTRAINTS>.PItem absolute AItem; !!! bug #25917
+var
+  LItem: TQueueDictionary.PValue absolute AItem;
+  LIndex: SizeInt;
+begin
+  LIndex := DoAdd(LItem.Pair.Key, LItem^);
+  FIdx.Add(LIndex);
+end;
+
+function TDeamortizedDArrayCuckooMap<CUCKOO_CONSTRAINTS>.TQueueDictionary.IsEmpty: Boolean;
+begin
+  Result := FIdx.Count = 0;
+end;
+
+function TDeamortizedDArrayCuckooMap<CUCKOO_CONSTRAINTS>.TQueueDictionary.Pop: Pointer;
+var
+  AIndex: SizeInt;
+  //LResult: TDeamortizedDArrayCuckooMap<CUCKOO_CONSTRAINTS>.TItem; !!!bug #25917
+begin
+  AIndex := FIdx.DoRemove(FIdx.Count - 1, cnExtracted);
+
+  Result := New(TQueueDictionary.PValue);
+  TQueueDictionary.PValue(Result)^ := DoRemove(AIndex, cnExtracted);
+end;
+
+constructor TDeamortizedDArrayCuckooMap<CUCKOO_CONSTRAINTS>.TQueueDictionary.Create(ACapacity: SizeInt;
+  const AComparer: IEqualityComparer<TKey>);
+begin
+  FIdx := TList<UInt32>.Create;
+  inherited Create(ACapacity, AComparer);
+end;
+
+destructor TDeamortizedDArrayCuckooMap<CUCKOO_CONSTRAINTS>.TQueueDictionary.Destroy;
+begin
+  FIdx.Free;
+end;
+
+function TDeamortizedDArrayCuckooMap<CUCKOO_CONSTRAINTS>.GetQueueCount: SizeInt;
+begin
+  Result := FQueue.Count;
+end;
+
+constructor TDeamortizedDArrayCuckooMap<CUCKOO_CONSTRAINTS>.Create(ACapacity: SizeInt;
+  const AComparer: IEqualityComparer<TKey>);
+begin
+end;
+
+constructor TDeamortizedDArrayCuckooMap<CUCKOO_CONSTRAINTS>.Create(const AComparer: IEqualityComparer<TKey>);
+begin
+end;
+
+constructor TDeamortizedDArrayCuckooMap<CUCKOO_CONSTRAINTS>.Create(ACollection: TEnumerable<TDictionaryPair>;
+  const AComparer: IEqualityComparer<TKey>);
+begin
+end;
+
+{$IFDEF ENABLE_METHODS_WITH_TEnumerableWithPointers}
+constructor TDeamortizedDArrayCuckooMap<CUCKOO_CONSTRAINTS>.Create(ACollection: TEnumerableWithPointers<TDictionaryPair>;
+  const AComparer: IEqualityComparer<TKey>);
+begin
+end;
+{$ENDIF}
+
+constructor TDeamortizedDArrayCuckooMap<CUCKOO_CONSTRAINTS>.Create;
+begin
+  Create(0);
+end;
+
+constructor TDeamortizedDArrayCuckooMap<CUCKOO_CONSTRAINTS>.Create(ACapacity: SizeInt);
+begin
+  Create(ACapacity, TExtendedEqualityComparer<TKey>.Default(THashFactory));
+end;
+
+constructor TDeamortizedDArrayCuckooMap<CUCKOO_CONSTRAINTS>.Create(ACollection: TEnumerable<TDictionaryPair>);
+begin
+  Create(ACollection, TExtendedEqualityComparer<TKey>.Default(THashFactory));
+end;
+
+{$IFDEF ENABLE_METHODS_WITH_TEnumerableWithPointers}
+constructor TDeamortizedDArrayCuckooMap<CUCKOO_CONSTRAINTS>.Create(ACollection: TEnumerableWithPointers<TDictionaryPair>);
+begin
+  Create(ACollection, TExtendedEqualityComparer<TKey>.Default(THashFactory));
+end;
+{$ENDIF}
+
+constructor TDeamortizedDArrayCuckooMap<CUCKOO_CONSTRAINTS>.Create(ACapacity: SizeInt;
+  const AComparer: IExtendedEqualityComparer<TKey>);
+begin
+  FMaxLoadFactor := TCuckooCfg.MAX_LOAD_FACTOR;
+  FQueue := TQueueDictionary.Create;
+  FCDM   := TCDM.Create;
+
+  // to do - check constraint consts
+
+  if TCuckooCfg.D > THashFactory.MAX_HASHLIST_COUNT then
+    raise EArgumentOutOfRangeException.CreateRes(@SArgumentOutOfRange);
+
+  // should be moved to class constructor, but bug #24848
+  CUCKOO_SIGN := UInt32.GetSizedSignMask(THashFactory.HASH_FUNCTIONS_MASK_SIZE + 1);
+  CUCKOO_INDEX_SIZE := UInt32.GetBitsLength - (THashFactory.HASH_FUNCTIONS_MASK_SIZE + 1);
+  CUCKOO_HASH_SIGN := THashFactory.HASH_FUNCTIONS_MASK shl CUCKOO_INDEX_SIZE;
+
+  FEqualityComparer := AComparer;
+  SetCapacity(ACapacity);
+end;
+
+constructor TDeamortizedDArrayCuckooMap<CUCKOO_CONSTRAINTS>.Create(const AComparer: IExtendedEqualityComparer<TKey>);
+begin
+  Create(0, AComparer);
+end;
+
+constructor TDeamortizedDArrayCuckooMap<CUCKOO_CONSTRAINTS>.Create(ACollection: TEnumerable<TDictionaryPair>;
+  const AComparer: IExtendedEqualityComparer<TKey>);
+var
+  LItem: TDictionaryPair;
+begin
+  Create(AComparer);
+  for LItem in ACollection do
+    Add(LItem);
+end;
+
+{$IFDEF ENABLE_METHODS_WITH_TEnumerableWithPointers}
+constructor TDeamortizedDArrayCuckooMap<CUCKOO_CONSTRAINTS>.Create(ACollection: TEnumerableWithPointers<TDictionaryPair>;
+  const AComparer: IExtendedEqualityComparer<TKey>);
+var
+  LItem: PDictionaryPair;
+begin
+  Create(AComparer);
+  for LItem in ACollection.Ptr^ do
+    Add(LItem^);
+end;
+{$ENDIF}
+
+destructor TDeamortizedDArrayCuckooMap<CUCKOO_CONSTRAINTS>.Destroy;
+begin
+  inherited;
+  FQueue.Free;
+  FCDM.Free;
+end;
+
+function TDeamortizedDArrayCuckooMap<CUCKOO_CONSTRAINTS>.GetKeys: TKeyCollection;
+begin
+  if not Assigned(FKeys) then
+    FKeys := TKeyCollection.Create(Self);
+  Result := TKeyCollection(FKeys);
+end;
+
+function TDeamortizedDArrayCuckooMap<CUCKOO_CONSTRAINTS>.GetValues: TValueCollection;
+begin
+  if not Assigned(FValues) then
+    FValues := TValueCollection.Create(Self);
+  Result := TValueCollection(FValues);
+end;
+
+function TDeamortizedDArrayCuckooMap<CUCKOO_CONSTRAINTS>.GetPointers: PPointersCollection;
+begin
+  Result := PPointersCollection(@FItems);
+end;
+
+function TDeamortizedDArrayCuckooMap<CUCKOO_CONSTRAINTS>.Lookup(constref AKey: TKey;
+  var AHashListOrIndex: PUInt32): SizeInt;
+begin
+  Result := Lookup(FItems, AKey, AHashListOrIndex);
+end;
+
+function TDeamortizedDArrayCuckooMap<CUCKOO_CONSTRAINTS>.Lookup(constref AItems: TItemsDArray; constref AKey: TKey;
+  var AHashListOrIndex: PUInt32): SizeInt;
+var
+  LLengthMask: SizeInt;
+  i, j, k: SizeInt;
+  AHashList: PUInt32 absolute AHashListOrIndex;
+  AHashListParams: PUInt16 absolute AHashListOrIndex;
+  AIndex: PtrInt absolute AHashListOrIndex;
+  // LBloomFilter: UInt32; // to rethink. now is useless
+begin
+  if Length(AItems[0]) = 0 then
+    Exit(LR_NIL);
+
+  LLengthMask := Length(AItems[0]) - 1;
+  AHashListParams[0] := TCuckooCfg.D; // number of hashes
+
+  i := 1; // ineks iteracji iteracji haszy
+  k := 1; // indeks iteracji haszy
+  // LBloomFilter := 0;
+  repeat
+    AHashListParams[1] := i; // iteration
+    IExtendedEqualityComparer<TKey>(FEqualityComparer).GetHashList(AKey, AHashList);
+    for j := 0 to THashFactory.HASHLIST_COUNT_PER_FUNCTION[i] - 1 do
+    begin
+      AHashList[k] := AHashList[k] or CUCKOO_SIGN;
+      // LBloomFilter := LBloomFilter or AHashList[k];
+
+      with AItems[k-1][AHashList[k] and LLengthMask] do
+        if (Hash and UInt32.GetSignMask) <> 0 then
+          if (AHashList[k] = Hash or CUCKOO_SIGN) and FEqualityComparer.Equals(AKey, Pair.Key) then
+            Exit(k-1);
+
+      Inc(k);
+    end;
+    Inc(i);
+  until k > TCuckooCfg.D;
+
+  i := FQueue.FindBucketIndex(AKey);
+  if i >= 0 then
+  begin
+    AIndex := i;
+    Exit(LR_QUEUE);
+  end;
+
+{  LBloomFilter := not LBloomFilter;
+  for i := 0 to FDicQueueList.Count - 1 do
+    // with FQueue[i] do
+    if LBloomFilter and FQueue[i].Hash = 0 then
+      for j := 1 to TCuckooCfg.D do
+        if (FQueue[i].Hash or CUCKOO_SIGN = AHashList[j]) then
+          if FEqualityComparer.Equals(AKey, FQueue[i].Pair.Key) then
+          begin
+            AIndex := i;
+            Exit(LR_QUEUE);
+          end;     }
+
+  Result := LR_NIL;
+end;
+
+procedure TDeamortizedDArrayCuckooMap<CUCKOO_CONSTRAINTS>.PrepareAddingItem;
+var
+  i: SizeInt;
+begin
+  if FItemsLength > FItemsThreshold then
+    Rehash(Length(FItems[0]) shl 1)
+  else if FItemsThreshold = 0 then
+  begin
+    for i := 0 to TCuckooCfg.D - 1 do
+      SetLength(FItems[i], 4);
+    UpdateItemsThreshold(4);
+  end
+  else if FItemsLength = $40000001 then // High(TIndex) ... Error: Type mismatch
+    OutOfMemoryError;
+end;
+
+procedure TDeamortizedDArrayCuckooMap<CUCKOO_CONSTRAINTS>.UpdateItemsThreshold(ASize: SizeInt);
+var
+  LLength: SizeInt;
+begin
+  LLength := ASize*TCuckooCfg.D;
+  if LLength = $40000000 then
+    FItemsThreshold := $40000001
+  else
+    FItemsThreshold := Pred(Round(LLength * FMaxLoadFactor));
+end;
+
+procedure TDeamortizedDArrayCuckooMap<CUCKOO_CONSTRAINTS>.AddItem(constref AItems: TItemsDArray; constref AKey: TKey;
+  constref AValue: TValue; const AHashList: PUInt32);
+var
+  LNewItem: TItem;
+  LPNewItem: PItem;
+  y: boolean = false;
+  b: UInt32;
+  LIndex: UInt32;
+  i, LLengthMask: SizeInt;
+  LTempItem: TItem;
+  LHashList: array[0..1] of UInt32;
+  LHashListParams: array[0..3] of UInt16 absolute LHashList;
+begin
+  LLengthMask := Length(AItems[0]) - 1;
+
+  LNewItem.Pair.Key := AKey;
+  LNewItem.Pair.Value := AValue;
+  // by concept already sign bit is set
+  LNewItem.Hash := ((not CUCKOO_HASH_SIGN) and AHashList[1]) or UInt32.GetSignMask; // start at array [0]
+  FQueue.InsertIntoBack(@LNewItem);
+
+  for i := 0 to TCuckooCfg.L - 1 do
+  begin
+    if not y then
+      if FQueue.IsEmpty then
+        Exit
+      else
+      begin
+        LPNewItem := FQueue.Pop; // bug #25917 workaround
+        LNewItem := LPNewItem^;
+        Dispose(LPNewItem);
+        b := (LNewItem.Hash and CUCKOO_HASH_SIGN) shr CUCKOO_INDEX_SIZE;
+        y := true;
+      end;
+    LIndex := LNewItem.Hash and LLengthMask;
+    if (AItems[b][LIndex].Hash and UInt32.GetSignMask) = 0 then // insert!
+    begin
+      AItems[b][LIndex] := LNewItem;
+      FCDM.Clear;
+      y := false;
+    end
+    else
+    begin
+      if FCDM.ContainsKey(LNewItem.Pair.Key) then // found second cycle
+      begin
+        FQueue.InsertIntoBack(@LNewItem);
+        FCDM.Clear;
+        y := false;
+      end
+      else
+      begin
+        LTempItem := AItems[b][LIndex];
+        AItems[b][LIndex] := LNewItem;
+        LNewItem.Hash := LNewItem.Hash or CUCKOO_SIGN;
+        FCDM.AddOrSetValue(LNewItem.Pair.Key, EmptyRecord);
+
+        LNewItem := LTempItem;
+        b := b + 1;
+        if b >= TCuckooCfg.D then
+          b := 0;
+        LHashListParams[0] := -Succ(b);
+        IExtendedEqualityComparer<TKey>(FEqualityComparer).GetHashList(LNewItem.Pair.Key, @LHashList[0]);
+        LNewItem.Hash := (LHashList[1] and not CUCKOO_SIGN) or (b shl CUCKOO_INDEX_SIZE) or UInt32.GetSignMask;
+        //  y := True; // always true in this place
+      end;
+    end;
+  end;
+  if y then
+    FQueue.InsertIntoHead(@LNewItem);
+end;
+
+procedure TDeamortizedDArrayCuckooMap<CUCKOO_CONSTRAINTS>.DoAdd(const AKey: TKey; const AValue: TValue;
+  const AHashList: PUInt32);
+begin
+  AddItem(FItems, AKey, AValue, AHashList);
+  Inc(FItemsLength);
+  KeyNotify(AKey, cnAdded);
+  ValueNotify(AValue, cnAdded);
+end;
+
+procedure TDeamortizedDArrayCuckooMap<CUCKOO_CONSTRAINTS>.Add(constref AKey: TKey; constref AValue: TValue);
+var
+  LHashList: array[0..TCuckooCfg.D] of UInt32;
+  LHashListOrIndex: PUint32;
+begin
+  PrepareAddingItem;
+  LHashListOrIndex := @LHashList[0];
+  if Lookup(AKey, LHashListOrIndex) <> LR_NIL then
+    raise EListError.CreateRes(@SDuplicatesNotAllowed);
+
+  DoAdd(AKey, AValue, LHashListOrIndex);
+end;
+
+procedure TDeamortizedDArrayCuckooMap<CUCKOO_CONSTRAINTS>.Add(constref APair: TPair<TKey, TValue>);
+begin
+  Add(APair.Key, APair.Value);
+end;
+
+function TDeamortizedDArrayCuckooMap<CUCKOO_CONSTRAINTS>.DoRemove(const AHashListOrIndex: PUInt32;
+  ALookupResult: SizeInt; ACollectionNotification: TCollectionNotification): TValue;
+var
+  LItem: PItem;
+  LIndex: UInt32;
+  LQueueIndex: SizeInt absolute AHashListOrIndex;
+  LPair: TPair<TKey, TValue>;
+begin
+  case ALookupResult of
+    LR_QUEUE:
+      LPair := FQueue.FItems[LQueueIndex].Pair.Value.Pair;
+    LR_NIL:
+      raise ERangeError.Create(SItemNotFound);
+  else
+    LIndex := AHashListOrIndex[ALookupResult + 1] and (Length(FItems[0]) - 1);
+    LItem := @FItems[ALookupResult][LIndex];
+    LItem.Hash := 0;
+    LPair := LItem.Pair;
+    LItem.Pair := Default(TPair<TKey, TValue>);
+  end;
+
+  Result := LPair.Value;
+  Dec(FItemsLength);
+  if ALookupResult = LR_QUEUE then
+  begin
+    FQueue.FIdx.Remove(LQueueIndex);
+    FQueue.DoRemove(LQueueIndex, cnRemoved);
+  end;
+
+  FCDM.Remove(LPair.Key); // item can exist in CDM
+
+  PairNotify(LPair, ACollectionNotification);
+end;
+
+procedure TDeamortizedDArrayCuckooMap<CUCKOO_CONSTRAINTS>.Remove(constref AKey: TKey);
+var
+  LHashList: array[0..TCuckooCfg.D] of UInt32;
+  LHashListOrIndex: PUint32;
+  LLookupResult: SizeInt;
+begin
+  LHashListOrIndex := @LHashList[0];
+  LLookupResult := Lookup(AKey, LHashListOrIndex);
+  if LLookupResult = LR_NIL then
+    Exit;
+
+  DoRemove(LHashListOrIndex, LLookupResult, cnRemoved);
+end;
+
+function TDeamortizedDArrayCuckooMap<CUCKOO_CONSTRAINTS>.ExtractPair(constref AKey: TKey): TPair<TKey, TValue>;
+var
+  LHashList: array[0..TCuckooCfg.D] of UInt32;
+  LHashListOrIndex: PUint32;
+  LLookupResult: SizeInt;
+begin
+  LHashListOrIndex := @LHashList[0];
+  LLookupResult := Lookup(AKey, LHashListOrIndex);
+  if LLookupResult = LR_NIL then
+    Exit(Default(TPair<TKey, TValue>));
+
+  Result.Key := AKey;
+  Result.Value := DoRemove(LHashListOrIndex, LLookupResult, cnExtracted);
+end;
+
+procedure TDeamortizedDArrayCuckooMap<CUCKOO_CONSTRAINTS>.Clear;
+var
+  LItem: PItem;
+  i, j: SizeInt;
+  LOldItems: TItemsDArray;
+  LOldQueueItems: TQueueDictionary.TItemsArray;
+  LQueueItem: TQueueDictionary._TItem;
+begin
+  FItemsLength := 0;
+  FItemsThreshold := 0;
+  LOldItems := FItems;
+  for i := 0 to TCuckooCfg.D - 1 do
+    FItems[i] := nil;
+
+  for i := 0 to TCuckooCfg.D - 1 do
+  begin
+    for j := 0 to High(LOldItems[0]) do
+    begin
+      LItem := @LOldItems[i][j];
+      if (LItem.Hash and UInt32.GetSignMask <> 0) then
+        PairNotify(LItem.Pair, cnRemoved);
+    end;
+  end;
+
+  FCDM.Clear;
+
+  // queue
+  FQueue.FItemsLength := 0;
+  FQueue.FItemsThreshold := 0;
+  LOldQueueItems := FQueue.FItems;
+  FQueue.FItems := nil;
+
+  for i := 0 to High(LOldQueueItems) do
+  begin
+    LQueueItem := TQueueDictionary._TItem(LOldQueueItems[i]);
+    if (LQueueItem.Hash and UInt32.GetSignMask = 0) then
+      Continue;
+
+    PairNotify(LQueueItem.Pair.Value.Pair, cnRemoved);
+  end;
+end;
+
+procedure TDeamortizedDArrayCuckooMap<CUCKOO_CONSTRAINTS>.Rehash(ASizePow2: SizeInt);
+var
+  LNewItems: TItemsDArray;
+  i, j: SizeInt;
+  LItem: PItem;
+  LOldQueue: TQueueDictionary;
+var
+  LHashList: array[0..1] of UInt32;
+  LHashListParams: array[0..3] of Int16 absolute LHashList;
+begin
+  if ASizePow2 = Length(FItems[0]) then
+    Exit;
+  if ASizePow2 < 0 then
+    OutOfMemoryError;
+
+  for i := 0 to TCuckooCfg.D - 1 do
+    SetLength(LNewItems[i], ASizePow2);
+
+  LHashListParams[0] := -1;
+
+  // opportunity to clear the queue
+  LOldQueue := FQueue;
+  FCDM.Clear;
+  FQueue := TQueueDictionary.Create;
+  for i := 0 to LOldQueue.FIdx.Count - 1 do
+  begin
+   LItem := @LOldQueue.FItems[LOldQueue.FIdx[i]].Pair.Value;
+   LHashList[1] := FEqualityComparer.GetHashCode(LItem.Pair.Key);
+   AddItem(LNewItems, LItem.Pair.Key, LItem.Pair.Value, @LHashList[0]);
+  end;
+  LOldQueue.Free;
+
+  // copy the old elements
+  for i := 0 to TCuckooCfg.D - 1 do
+    for j := 0 to High(FItems[0]) do
+    begin
+      LItem := @FItems[i][j];
+      if (LItem.Hash and UInt32.GetSignMask) = 0 then
+        Continue;
+
+      // small optimization. most of items exist in table 0
+      if LItem.Hash and CUCKOO_HASH_SIGN = 0 then
+      begin
+        LHashList[1] := LItem.Hash;
+        AddItem(LNewItems, LItem.Pair.Key, LItem.Pair.Value, @LHashList[0]);
+      end
+      else
+      begin
+        LHashList[1] := FEqualityComparer.GetHashCode(LItem.Pair.Key);
+        AddItem(LNewItems, LItem.Pair.Key, LItem.Pair.Value, @LHashList[0]);
+      end;
+    end;
+
+  FItems := LNewItems;
+  UpdateItemsThreshold(ASizePow2);
+end;
+
+function TDeamortizedDArrayCuckooMap<CUCKOO_CONSTRAINTS>.DoGetEnumerator: TEnumerator<TDictionaryPair>;
+begin
+  Result := GetEnumerator;
+end;
+
+procedure TDeamortizedDArrayCuckooMap<CUCKOO_CONSTRAINTS>.SetCapacity(ACapacity: SizeInt);
+begin
+  if ACapacity < FItemsLength then
+    raise EArgumentOutOfRangeException.CreateRes(@SArgumentOutOfRange);
+
+  Resize(ACapacity);
+end;
+
+procedure TDeamortizedDArrayCuckooMap<CUCKOO_CONSTRAINTS>.SetMaxLoadFactor(AValue: single);
+var
+  LItemsLength: SizeInt;
+begin
+  if (AValue > TCuckooCfg.MAX_LOAD_FACTOR) or (AValue <= 0) then
+    raise EArgumentOutOfRangeException.CreateRes(@SArgumentOutOfRange);
+
+  FMaxLoadFactor := AValue;
+
+  repeat
+    LItemsLength := Length(FItems[0]);
+    UpdateItemsThreshold(LItemsLength);
+    if FItemsLength > FItemsThreshold then
+      Rehash(LItemsLength shl 1);
+  until FItemsLength <= FItemsThreshold;
+end;
+
+function TDeamortizedDArrayCuckooMap<CUCKOO_CONSTRAINTS>.GetLoadFactor: single;
+begin
+  Result := FItemsLength / (Length(FItems[0]) * TCuckooCfg.D);
+end;
+
+function TDeamortizedDArrayCuckooMap<CUCKOO_CONSTRAINTS>.GetCapacity: SizeInt;
+begin
+  Result := Length(FItems[0]) * TCuckooCfg.D;
+end;
+
+procedure TDeamortizedDArrayCuckooMap<CUCKOO_CONSTRAINTS>.Resize(ANewSize: SizeInt);
+var
+  LNewSize: SizeInt;
+begin
+  if ANewSize < 0 then
+    raise EArgumentOutOfRangeException.CreateRes(@SArgumentOutOfRange);
+
+  LNewSize := 0;
+  if ANewSize > 0 then
+  begin
+    LNewSize := 4;
+    while LNewSize * TCuckooCfg.D < ANewSize do
+      LNewSize := LNewSize shl 1;
+  end;
+
+  Rehash(LNewSize);
+end;
+
+function TDeamortizedDArrayCuckooMap<CUCKOO_CONSTRAINTS>.GetEnumerator: TPairEnumerator;
+begin
+  Result := TPairEnumerator.Create(Self);
+end;
+
+function TDeamortizedDArrayCuckooMap<CUCKOO_CONSTRAINTS>.GetItem(const AKey: TKey): TValue;
+var
+  LHashList: array[0..TCuckooCfg.D] of UInt32;
+  LHashListOrIndex: PUint32;
+  LLookupResult: SizeInt;
+  LIndex: UInt32;
+begin
+  LHashListOrIndex := @LHashList[0];
+  LLookupResult := Lookup(AKey, LHashListOrIndex);
+
+  case LLookupResult of
+    LR_QUEUE:
+      Result := FQueue.FItems[PtrInt(LHashListOrIndex)].Pair.Value.Pair.Value;
+    LR_NIL:
+      raise EListError.CreateRes(@SDictionaryKeyDoesNotExist);
+  else
+    LIndex := LHashListOrIndex[LLookupResult + 1] and (Length(FItems[0]) - 1);
+    Result := FItems[LLookupResult][LIndex].Pair.Value;
+  end;
+end;
+
+procedure TDeamortizedDArrayCuckooMap<CUCKOO_CONSTRAINTS>.TrimExcess;
+begin
+  SetCapacity(Succ(FItemsLength));
+  FQueue.TrimExcess;
+  FQueue.FIdx.TrimExcess;
+end;
+
+procedure TDeamortizedDArrayCuckooMap<CUCKOO_CONSTRAINTS>.SetItem(constref AValue: TValue;
+  const AHashListOrIndex: PUInt32; ALookupResult: SizeInt);
+var
+  LIndex: UInt32;
+begin
+  case ALookupResult of
+    LR_QUEUE:
+      SetValue(FQueue.FItems[PtrInt(AHashListOrIndex)].Pair.Value.Pair.Value, AValue);
+    LR_NIL:
+      raise EListError.CreateRes(@SItemNotFound);
+  else
+    LIndex := AHashListOrIndex[ALookupResult + 1] and (Length(FItems[0]) - 1);
+    SetValue(FItems[ALookupResult][LIndex].Pair.Value, AValue);
+  end;
+end;
+
+procedure TDeamortizedDArrayCuckooMap<CUCKOO_CONSTRAINTS>.SetItem(const AKey: TKey; const AValue: TValue);
+var
+  LHashList: array[0..TCuckooCfg.D] of UInt32;
+  LHashListOrIndex: PUint32;
+  LLookupResult: SizeInt;
+begin
+  LHashListOrIndex := @LHashList[0];
+  LLookupResult := Lookup(AKey, LHashListOrIndex);
+
+  SetItem(AValue, LHashListOrIndex, LLookupResult);
+end;
+
+function TDeamortizedDArrayCuckooMap<CUCKOO_CONSTRAINTS>.TryGetValue(constref AKey: TKey; out AValue: TValue): Boolean;
+var
+  LHashList: array[0..TCuckooCfg.D] of UInt32;
+  LHashListOrIndex: PUint32;
+  LLookupResult: SizeInt;
+  LIndex: UInt32;
+begin
+  LHashListOrIndex := @LHashList[0];
+  LLookupResult := Lookup(AKey, LHashListOrIndex);
+
+  Result := LLookupResult <> LR_NIL;
+
+  case LLookupResult of
+    LR_QUEUE:
+      AValue := FQueue.FItems[PtrInt(LHashListOrIndex)].Pair.Value.Pair.Value;
+    LR_NIL:
+      AValue := Default(TValue);
+  else
+    LIndex := LHashListOrIndex[LLookupResult + 1] and (Length(FItems[0]) - 1);
+    AValue := FItems[LLookupResult][LIndex].Pair.Value;
+  end;
+end;
+
+procedure TDeamortizedDArrayCuckooMap<CUCKOO_CONSTRAINTS>.AddOrSetValue(constref AKey: TKey; constref AValue: TValue);
+var
+  LHashList: array[0..TCuckooCfg.D] of UInt32;
+  LHashListOrIndex: PUint32;
+  LLookupResult: SizeInt;
+begin
+  LHashListOrIndex := @LHashList[0];
+  LLookupResult := Lookup(AKey, LHashListOrIndex);
+
+  if LLookupResult = LR_NIL then
+    Add(AKey, AValue)
+    // more optimal version for AddOrSetValue has some bug : see Test_CuckooD2_Notification
+    //begin
+    //  PrepareAddingItem;
+    //  DoAdd(AKey, AValue, LHashListOrIndex);
+    //end
+  else
+    SetItem(AValue, LHashListOrIndex, LLookupResult);
+end;
+
+function TDeamortizedDArrayCuckooMap<CUCKOO_CONSTRAINTS>.ContainsKey(constref AKey: TKey): Boolean;
+var
+  LHashList: array[0..TCuckooCfg.D] of UInt32;
+  LHashListOrIndex: PUint32;
+begin
+  LHashListOrIndex := @LHashList[0];
+  Result := Lookup(AKey, LHashListOrIndex) <> LR_NIL;
+end;
+
+function TDeamortizedDArrayCuckooMap<CUCKOO_CONSTRAINTS>.ContainsValue(constref AValue: TValue): Boolean;
+begin
+  Result := ContainsValue(AValue, TEqualityComparer<TValue>.Default(THashFactory));
+end;
+
+function TDeamortizedDArrayCuckooMap<CUCKOO_CONSTRAINTS>.ContainsValue(constref AValue: TValue;
+  const AEqualityComparer: IEqualityComparer<TValue>): Boolean;
+var
+  i, j: SizeInt;
+  LItem: PItem;
+begin
+  if Length(FItems[0]) = 0 then
+    Exit(False);
+
+  for i := 0 to TCuckooCfg.D - 1 do
+    for j := 0 to High(FItems[0]) do
+    begin
+      LItem := @FItems[i][j];
+      if (LItem.Hash and UInt32.GetSignMask) = 0 then
+        Continue;
+
+      if AEqualityComparer.Equals(AValue, LItem.Pair.Value) then
+        Exit(True);
+    end;
+  Result := False;
+end;
+
+procedure TDeamortizedDArrayCuckooMap<CUCKOO_CONSTRAINTS>.GetMemoryLayout(
+  const AOnGetMemoryLayoutKeyPosition: TOnGetMemoryLayoutKeyPosition);
+var
+  i, j, k: SizeInt;
+begin
+  k := 0;
+  for i := 0 to TCuckooCfg.D - 1 do
+    for j := 0 to High(FItems[0]) do
+    begin
+      if FItems[i][j].Hash and UInt32.GetSignMask <> 0 then
+        AOnGetMemoryLayoutKeyPosition(Self, k);
+      inc(k);
+    end;
+end;
+
+{ TDeamortizedDArrayCuckooMap<CUCKOO_CONSTRAINTS>.TPairEnumerator }
+
+function TDeamortizedDArrayCuckooMap<CUCKOO_CONSTRAINTS>.TPairEnumerator.GetCurrent: TPair<TKey, TValue>;
+begin
+  if FMainIndex = TCuckooCfg.D then
+    Result := TDeamortizedDArrayCuckooMap<CUCKOO_CONSTRAINTS>(FDictionary).FQueue.FItems[FIndex].Pair.Value.Pair
+  else
+    Result := TDeamortizedDArrayCuckooMap<CUCKOO_CONSTRAINTS>(FDictionary).FItems[FMainIndex][FIndex].Pair;
+end;
+
+{ TDeamortizedDArrayCuckooMap<CUCKOO_CONSTRAINTS>.TValueEnumerator }
+
+function TDeamortizedDArrayCuckooMap<CUCKOO_CONSTRAINTS>.TValueEnumerator.GetCurrent: TValue;
+begin
+  if FMainIndex = TCuckooCfg.D then
+    Result := TDeamortizedDArrayCuckooMap<CUCKOO_CONSTRAINTS>(FDictionary).FQueue.FItems[FIndex].Pair.Value.Pair.Value
+  else
+    Result := TDeamortizedDArrayCuckooMap<CUCKOO_CONSTRAINTS>(FDictionary).FItems[FMainIndex][FIndex].Pair.Value;
+end;
+
+{ TDeamortizedDArrayCuckooMap<CUCKOO_CONSTRAINTS>.TPValueEnumerator }
+
+function TDeamortizedDArrayCuckooMap<CUCKOO_CONSTRAINTS>.TPValueEnumerator.GetCurrent: PValue;
+begin
+  if FMainIndex = TCuckooCfg.D then
+    Result := @(TDeamortizedDArrayCuckooMap<CUCKOO_CONSTRAINTS>(FDictionary).FQueue.FItems[FIndex].Pair.Value.Pair.Value)
+  else
+    Result := @(TDeamortizedDArrayCuckooMap<CUCKOO_CONSTRAINTS>(FDictionary).FItems[FMainIndex][FIndex].Pair.Value);
+end;
+
+{ TDeamortizedDArrayCuckooMap<CUCKOO_CONSTRAINTS>.TKeyEnumerator }
+
+function TDeamortizedDArrayCuckooMap<CUCKOO_CONSTRAINTS>.TKeyEnumerator.GetCurrent: TKey;
+begin
+  if FMainIndex = TCuckooCfg.D then
+    Result := TDeamortizedDArrayCuckooMap<CUCKOO_CONSTRAINTS>(FDictionary).FQueue.FItems[FIndex].Pair.Value.Pair.Key
+  else
+    Result := TDeamortizedDArrayCuckooMap<CUCKOO_CONSTRAINTS>(FDictionary).FItems[FMainIndex][FIndex].Pair.Key;
+end;
+
+{ TDeamortizedDArrayCuckooMap<CUCKOO_CONSTRAINTS>.TPKeyEnumerator }
+
+function TDeamortizedDArrayCuckooMap<CUCKOO_CONSTRAINTS>.TPKeyEnumerator.GetCurrent: TKey;
+begin
+  if FMainIndex = TCuckooCfg.D then
+    Result := @(TDeamortizedDArrayCuckooMap<CUCKOO_CONSTRAINTS>(FDictionary).FQueue.FItems[FIndex].Pair.Value.Pair.Key)
+  else
+    Result := @(TDeamortizedDArrayCuckooMap<CUCKOO_CONSTRAINTS>(FDictionary).FItems[FMainIndex][FIndex].Pair.Key);
+end;
+
+{ TObjectDictionary<DICTIONARY_CONSTRAINTS> }
+
+procedure TObjectDeamortizedDArrayCuckooMap<CUCKOO_CONSTRAINTS>.KeyNotify(
+  constref AKey: TKey; ACollectionNotification: TCollectionNotification);
+begin
+  inherited;
+
+  if (doOwnsKeys in FOwnerships) and (ACollectionNotification = cnRemoved) then
+    TObject((@AKey)^).Free;
+end;
+
+procedure TObjectDeamortizedDArrayCuckooMap<CUCKOO_CONSTRAINTS>.ValueNotify(constref AValue: TValue;
+  ACollectionNotification: TCollectionNotification);
+begin
+  inherited;
+
+  if (doOwnsValues in FOwnerships) and (ACollectionNotification = cnRemoved) then
+    TObject((@AValue)^).Free;
+end;
+
+constructor TObjectDeamortizedDArrayCuckooMap<CUCKOO_CONSTRAINTS>.Create(
+  AOwnerships: TDictionaryOwnerships);
+begin
+  Create(AOwnerships, 0);
+end;
+
+constructor TObjectDeamortizedDArrayCuckooMap<CUCKOO_CONSTRAINTS>.Create(
+  AOwnerships: TDictionaryOwnerships; ACapacity: SizeInt);
+begin
+  inherited Create(ACapacity);
+
+  FOwnerships := AOwnerships;
+end;
+
+constructor TObjectDeamortizedDArrayCuckooMap<CUCKOO_CONSTRAINTS>.Create(
+  AOwnerships: TDictionaryOwnerships; const AComparer: IExtendedEqualityComparer<TKey>);
+begin
+  inherited Create(AComparer);
+
+  FOwnerships := AOwnerships;
+end;
+
+constructor TObjectDeamortizedDArrayCuckooMap<CUCKOO_CONSTRAINTS>.Create(
+  AOwnerships: TDictionaryOwnerships; ACapacity: SizeInt; const AComparer: IExtendedEqualityComparer<TKey>);
+begin
+  inherited Create(ACapacity, AComparer);
+
+  FOwnerships := AOwnerships;
+end;
+
+procedure TObjectOpenAddressingLP<OPEN_ADDRESSING_CONSTRAINTS>.KeyNotify(
+  constref AKey: TKey; ACollectionNotification: TCollectionNotification);
+begin
+  inherited;
+
+  if (doOwnsKeys in FOwnerships) and (ACollectionNotification = cnRemoved) then
+    TObject((@AKey)^).Free;
+end;
+
+procedure TObjectOpenAddressingLP<OPEN_ADDRESSING_CONSTRAINTS>.ValueNotify(
+  constref AValue: TValue; ACollectionNotification: TCollectionNotification);
+begin
+  inherited;
+
+  if (doOwnsValues in FOwnerships) and (ACollectionNotification = cnRemoved) then
+    TObject((@AValue)^).Free;
+end;
+
+constructor TObjectOpenAddressingLP<OPEN_ADDRESSING_CONSTRAINTS>.Create(AOwnerships: TDictionaryOwnerships);
+begin
+  Create(AOwnerships, 0);
+end;
+
+constructor TObjectOpenAddressingLP<OPEN_ADDRESSING_CONSTRAINTS>.Create(AOwnerships: TDictionaryOwnerships;
+  ACapacity: SizeInt);
+begin
+  inherited Create(ACapacity);
+
+  FOwnerships := AOwnerships;
+end;
+
+constructor TObjectOpenAddressingLP<OPEN_ADDRESSING_CONSTRAINTS>.Create(AOwnerships: TDictionaryOwnerships;
+  const AComparer: IEqualityComparer<TKey>);
+begin
+  inherited Create(AComparer);
+
+  FOwnerships := AOwnerships;
+end;
+
+constructor TObjectOpenAddressingLP<OPEN_ADDRESSING_CONSTRAINTS>.Create(AOwnerships: TDictionaryOwnerships;
+  ACapacity: SizeInt; const AComparer: IEqualityComparer<TKey>);
+begin
+  inherited Create(ACapacity, AComparer);
+
+  FOwnerships := AOwnerships;
+end;

+ 655 - 0
CryptoLib/src/3rdParty/generics.collections/src/inc/generics.dictionariesh.inc

@@ -0,0 +1,655 @@
+{%MainUnit generics.collections.pas}
+
+{
+    This file is part of the Free Pascal run time library.
+    Copyright (c) 2014 by Maciej Izak (hnb)
+    member of the Free Sparta development team (http://freesparta.com)
+
+    Copyright(c) 2004-2014 DaThoX
+
+    It contains the Free Pascal generics library
+
+    See the file COPYING.FPC, included in this distribution,
+    for details about the copyright.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+
+    Acknowledgment
+
+    Thanks to Sphere 10 Software (http://sphere10.com) for sponsoring
+    many new types and major refactoring of entire library
+
+    Thanks to mORMot (http://synopse.info) project for the best implementations
+    of hashing functions like crc32c and xxHash32 :)
+
+ **********************************************************************}
+
+{$WARNINGS OFF}
+type
+  TEmptyRecord = record // special record for Dictionary TValue (Dictionary as Set)
+  end;
+
+  { TPair }
+
+  TPair<TKey, TValue> = record
+  public
+    Key: TKey;
+    Value: TValue;
+    class function Create(AKey: TKey; AValue: TValue): TPair<TKey, TValue>; static;
+  end;
+
+  { TCustomDictionary }
+
+  // bug #24283 and #24097 (forward declaration) - should be:
+  // TCustomDictionary<CUSTOM_DICTIONARY_CONSTRAINTS> = class(TEnumerable<TPair<TKey, TValue> >);
+  TCustomDictionary<CUSTOM_DICTIONARY_CONSTRAINTS> = class abstract
+  public type
+    //  workaround... no generics types in generics types
+    TDictionaryPair = TPair<TKey, TValue>;
+    PDictionaryPair = ^TDictionaryPair;
+    PKey = ^TKey;
+    PValue = ^TValue;
+    THashFactoryClass = THashFactory;
+  protected
+    FEqualityComparer: IEqualityComparer<TKey>;
+    FKeys: TEnumerable<TKey>;
+    FValues: TEnumerable<TValue>;
+    FMaxLoadFactor: single;
+  protected
+    procedure SetCapacity(ACapacity: SizeInt); virtual; abstract;
+    // bug #24283. workaround for this class because can't inherit from TEnumerable
+    function DoGetEnumerator: TEnumerator<TDictionaryPair>; virtual; abstract; {override;}
+
+    procedure SetMaxLoadFactor(AValue: single); virtual; abstract;
+    function GetLoadFactor: single; virtual; abstract;
+    function GetCapacity: SizeInt; virtual; abstract;
+  public
+    property MaxLoadFactor: single read FMaxLoadFactor write SetMaxLoadFactor;
+    property LoadFactor: single read GetLoadFactor;
+    property Capacity: SizeInt read GetCapacity write SetCapacity;
+
+    procedure Clear; virtual; abstract;
+    procedure Add(constref APair: TPair<TKey, TValue>); virtual; abstract;
+  strict private // bug #24283. workaround for this class because can't inherit from TEnumerable
+    function ToArray(ACount: SizeInt): TArray<TDictionaryPair>; overload;
+  public
+    function ToArray: TArray<TDictionaryPair>; virtual; final; {override; final; // bug #24283} overload;
+
+    constructor Create; virtual; overload;
+    constructor Create(ACapacity: SizeInt); virtual; overload;
+    constructor Create(ACapacity: SizeInt; const AComparer: IEqualityComparer<TKey>); virtual; overload;
+    constructor Create(const AComparer: IEqualityComparer<TKey>); overload;
+    constructor Create(ACollection: TEnumerable<TDictionaryPair>); virtual; overload;
+    constructor Create(ACollection: TEnumerable<TDictionaryPair>; const AComparer: IEqualityComparer<TKey>); virtual; overload;
+    {$IFDEF ENABLE_METHODS_WITH_TEnumerableWithPointers}
+    constructor Create(ACollection: TEnumerableWithPointers<TDictionaryPair>); virtual; overload;
+    constructor Create(ACollection: TEnumerableWithPointers<TDictionaryPair>; const AComparer: IEqualityComparer<TKey>); virtual; overload;
+    {$ENDIF}
+
+    destructor Destroy; override;
+  private
+    FOnKeyNotify: TCollectionNotifyEvent<TKey>;
+    FOnValueNotify: TCollectionNotifyEvent<TValue>;
+  protected
+    procedure UpdateItemsThreshold(ASize: SizeInt); virtual; abstract;
+
+    procedure KeyNotify(constref AKey: TKey; ACollectionNotification: TCollectionNotification); virtual;
+    procedure ValueNotify(constref AValue: TValue; ACollectionNotification: TCollectionNotification); virtual;
+    procedure PairNotify(constref APair: TDictionaryPair; ACollectionNotification: TCollectionNotification); inline;
+    procedure SetValue(var AValue: TValue; constref ANewValue: TValue);
+  public
+    property OnKeyNotify: TCollectionNotifyEvent<TKey> read FOnKeyNotify write FOnKeyNotify;
+    property OnValueNotify: TCollectionNotifyEvent<TValue> read FOnValueNotify write FOnValueNotify;
+  protected // FItemsLength must be declared at the end of TCustomDictionary
+    FItemsLength: SizeInt;
+  public
+    property Count: SizeInt read FItemsLength;
+  end;
+
+  { TCustomDictionaryEnumerator }
+
+  TCustomDictionaryEnumerator<T, CUSTOM_DICTIONARY_CONSTRAINTS> = class abstract(TEnumerator< T >)
+  private
+    FDictionary: TCustomDictionary<CUSTOM_DICTIONARY_CONSTRAINTS>;
+    FIndex: SizeInt;
+  protected
+    function DoGetCurrent: T; override;
+    function GetCurrent: T; virtual; abstract;
+  public
+    constructor Create(ADictionary: TCustomDictionary<CUSTOM_DICTIONARY_CONSTRAINTS>);
+  end;
+
+  { TDictionaryEnumerable }
+
+  TDictionaryEnumerable<TDictionaryEnumerator: TObject; TDictionaryPointersEnumerator, // ... inherits from TCustomDictionaryEnumerator. workaround...
+    T, CUSTOM_DICTIONARY_CONSTRAINTS> = class abstract(TEnumerableWithPointers<T>)
+  private
+    FDictionary: TCustomDictionary<CUSTOM_DICTIONARY_CONSTRAINTS>;
+    function GetCount: SizeInt;
+  protected
+    function GetPtrEnumerator: TEnumerator<PT>; override;
+    function DoGetEnumerator: TDictionaryEnumerator; override;
+  public
+    constructor Create(ADictionary: TCustomDictionary<CUSTOM_DICTIONARY_CONSTRAINTS>);
+    function ToArray: TArray<T>; override; final;
+    property Count: SizeInt read GetCount;
+  end;
+
+  // more info : http://en.wikipedia.org/wiki/Open_addressing
+
+  { TOpenAddressingEnumerator }
+
+  TOpenAddressingEnumerator<T, OPEN_ADDRESSING_CONSTRAINTS> = class abstract(TCustomDictionaryEnumerator<T, CUSTOM_DICTIONARY_CONSTRAINTS>)
+  protected
+    function DoMoveNext: Boolean; override;
+  end;
+
+  TOpenAddressingPointersEnumerator<TItem, PDictionaryPair> = class abstract(TEnumerator<PDictionaryPair>)
+  private var
+    FItems: ^TArray<TItem>;
+    FIndex: SizeInt;
+  protected
+    function DoMoveNext: boolean; override;
+    function DoGetCurrent: PDictionaryPair; override;
+    function GetCurrent: PDictionaryPair; virtual;
+  public
+    constructor Create(var AItems);
+  end;
+
+  TOpenAddressingPointersCollection<TPointersEnumerator, TItem, PDictionaryPair> = record
+  private type
+    PArray = ^TArray<TItem>;
+    function Items: PArray; inline;
+    function GetCount: SizeInt; inline;
+  public
+    function GetEnumerator: TPointersEnumerator;
+    function ToArray: TArray<PDictionaryPair>;
+    property Count: SizeInt read GetCount;
+  end;
+
+  TOnGetMemoryLayoutKeyPosition = procedure(Sender: TObject; AKeyPos: UInt32) of object;
+
+  TOpenAddressing<OPEN_ADDRESSING_CONSTRAINTS> = class abstract(TCustomDictionary<CUSTOM_DICTIONARY_CONSTRAINTS>)
+  private type
+    PItem = ^TItem;
+    TItem = record
+      Hash: UInt32;
+      Pair: TPair<TKey, TValue>;
+    end;
+
+    TItemsArray = array of TItem;
+    TPointersEnumerator = class(TOpenAddressingPointersEnumerator<TItem, PDictionaryPair>);
+    TPointersCollection = TOpenAddressingPointersCollection<TPointersEnumerator, TItem, PDictionaryPair>;
+  public type
+    PPointersCollection = ^TPointersCollection;
+  private var // FItems must be declared as first field 
+    FItems: TItemsArray;
+    FItemsThreshold: SizeInt;
+
+    procedure Resize(ANewSize: SizeInt);
+    procedure PrepareAddingItem;
+  protected
+    function RealItemsLength: SizeInt; virtual;
+    function Rehash(ASizePow2: SizeInt; AForce: Boolean = False): boolean; virtual;
+    function FindBucketIndex(constref AKey: TKey): SizeInt; overload; inline;
+    function FindBucketIndex(constref AItems: TArray<TItem>; constref AKey: TKey; out AHash: UInt32): SizeInt; virtual; abstract; overload;
+  public
+    type
+      // Enumerators
+      TPairEnumerator = class(TOpenAddressingEnumerator<TDictionaryPair, OPEN_ADDRESSING_CONSTRAINTS>)
+      protected
+        function GetCurrent: TPair<TKey,TValue>; override;
+      end;
+
+      TValueEnumerator = class(TOpenAddressingEnumerator<TValue, OPEN_ADDRESSING_CONSTRAINTS>)
+      protected
+        function GetCurrent: TValue; override;
+      end;
+
+      TPValueEnumerator = class(TOpenAddressingEnumerator<PValue, OPEN_ADDRESSING_CONSTRAINTS>)
+      protected
+        function GetCurrent: PValue; override;
+      end;
+
+      TKeyEnumerator = class(TOpenAddressingEnumerator<TKey, OPEN_ADDRESSING_CONSTRAINTS>)
+      protected
+        function GetCurrent: TKey; override;
+      end;
+
+      TPKeyEnumerator = class(TOpenAddressingEnumerator<PKey, OPEN_ADDRESSING_CONSTRAINTS>)
+      protected
+        function GetCurrent: PKey; override;
+      end;
+
+      // Collections
+      TValueCollection = class(TDictionaryEnumerable<TValueEnumerator, TPValueEnumerator, TValue, CUSTOM_DICTIONARY_CONSTRAINTS>);
+
+      TKeyCollection = class(TDictionaryEnumerable<TKeyEnumerator, TPKeyEnumerator, TKey, CUSTOM_DICTIONARY_CONSTRAINTS>);
+
+    // bug #24283 - workaround related to lack of DoGetEnumerator
+    function GetEnumerator: TPairEnumerator; reintroduce;
+  private
+    function GetKeys: TKeyCollection;
+    function GetValues: TValueCollection;
+    function GetPointers: PPointersCollection; inline;
+  private
+    function GetItem(const AKey: TKey): TValue; inline;
+    procedure SetItem(const AKey: TKey; const AValue: TValue); inline;
+    procedure AddItem(var AItem: TItem; constref AKey: TKey; constref AValue: TValue; const AHash: UInt32); inline;
+  protected
+     // useful for using dictionary as array
+    function DoRemove(AIndex: SizeInt; ACollectionNotification: TCollectionNotification): TValue; virtual;
+    function DoAdd(constref AKey: TKey; constref AValue: TValue): SizeInt; virtual;
+
+    procedure UpdateItemsThreshold(ASize: SizeInt); override;
+
+    procedure SetCapacity(ACapacity: SizeInt); override;
+    // bug #24283 - can't descadent from TEnumerable
+    function DoGetEnumerator: TEnumerator<TDictionaryPair>; override;
+    procedure SetMaxLoadFactor(AValue: single); override;
+    function GetLoadFactor: single; override;
+    function GetCapacity: SizeInt; override;
+  public
+    // many constructors because bug #25607
+    constructor Create(ACapacity: SizeInt; const AComparer: IEqualityComparer<TKey>); override; overload;
+
+    procedure Add(constref APair: TPair<TKey, TValue>); override; overload;
+    procedure Add(constref AKey: TKey; constref AValue: TValue); overload; inline;
+    procedure Remove(constref AKey: TKey);
+    function ExtractPair(constref AKey: TKey): TPair<TKey, TValue>;
+    procedure Clear; override;
+    procedure TrimExcess;
+    function TryGetValue(constref AKey: TKey; out AValue: TValue): Boolean;
+    procedure AddOrSetValue(constref AKey: TKey; constref AValue: TValue);
+    function ContainsKey(constref AKey: TKey): Boolean; inline;
+    function ContainsValue(constref AValue: TValue): Boolean; overload;
+    function ContainsValue(constref AValue: TValue; const AEqualityComparer: IEqualityComparer<TValue>): Boolean; virtual; overload;
+
+    property Items[Index: TKey]: TValue read GetItem write SetItem; default;
+    property Keys: TKeyCollection read GetKeys;
+    property Values: TValueCollection read GetValues;
+    property Ptr: PPointersCollection read GetPointers;
+
+    procedure GetMemoryLayout(const AOnGetMemoryLayoutKeyPosition: TOnGetMemoryLayoutKeyPosition);
+  end;
+
+  TOpenAddressingLP<OPEN_ADDRESSING_CONSTRAINTS> = class(TOpenAddressing<OPEN_ADDRESSING_CONSTRAINTS>)
+  private type // for workaround Lazarus bug #25613
+    _TItem = record
+      Hash: UInt32;
+      Pair: TPair<TKey, TValue>;
+    end;
+  protected
+    procedure NotifyIndexChange(AFrom, ATo: SizeInt); virtual;
+    function DoRemove(AIndex: SizeInt; ACollectionNotification: TCollectionNotification): TValue; override;
+    function FindBucketIndex(constref AItems: TArray<TItem>; constref AKey: TKey; out AHash: UInt32): SizeInt; override; overload;
+  end;
+
+  // More info and TODO
+  // https://github.com/OpenHFT/UntitledCollectionsProject/wiki/Tombstones-purge-from-hashtable:-theory-and-practice
+
+  TOpenAddressingTombstones<OPEN_ADDRESSING_CONSTRAINTS> = class abstract(TOpenAddressing<OPEN_ADDRESSING_CONSTRAINTS>)
+  private
+    FTombstonesCount: SizeInt;
+  protected
+    function Rehash(ASizePow2: SizeInt; AForce: Boolean = False): boolean; override;
+    function RealItemsLength: SizeInt; override;
+
+    function FindBucketIndexOrTombstone(constref AItems: TArray<TItem>; constref AKey: TKey;
+      out AHash: UInt32): SizeInt; virtual; abstract;
+
+    function DoRemove(AIndex: SizeInt; ACollectionNotification: TCollectionNotification): TValue; override;
+    function DoAdd(constref AKey: TKey; constref AValue: TValue): SizeInt; override;
+  public
+    property TombstonesCount: SizeInt read FTombstonesCount;
+    procedure ClearTombstones; virtual;
+    procedure Clear; override;
+  end;
+
+  TOpenAddressingSH<OPEN_ADDRESSING_CONSTRAINTS> = class(TOpenAddressingTombstones<OPEN_ADDRESSING_CONSTRAINTS>)
+  private type // for workaround Lazarus bug #25613
+    _TItem = record
+      Hash: UInt32;
+      Pair: TPair<TKey, TValue>;
+    end;
+  protected
+    function FindBucketIndex(constref AItems: TArray<TItem>; constref AKey: TKey;
+      out AHash: UInt32): SizeInt; override; overload;
+    function FindBucketIndexOrTombstone(constref AItems: TArray<TItem>; constref AKey: TKey;
+      out AHash: UInt32): SizeInt; override;
+  end;
+
+  TOpenAddressingQP<OPEN_ADDRESSING_CONSTRAINTS> = class(TOpenAddressingSH<OPEN_ADDRESSING_CONSTRAINTS>)
+  private
+    FPrimaryNumberAsSizeApproximation: SizeInt;
+  protected
+    procedure UpdateItemsThreshold(ASize: SizeInt); override;
+    function FindBucketIndex(constref AItems: TArray<TItem>;
+      constref AKey: TKey; out AHash: UInt32): SizeInt; override; overload;
+    function FindBucketIndexOrTombstone(constref AItems: TArray<TItem>;
+      constref AKey: TKey; out AHash: UInt32): SizeInt; override;
+  end;
+
+  TOpenAddressingDH<OPEN_ADDRESSING_CONSTRAINTS> = class(TOpenAddressingTombstones<OPEN_ADDRESSING_CONSTRAINTS>)
+  private type // for workaround Lazarus bug #25613
+    _TItem = record
+      Hash: UInt32;
+      Pair: TPair<TKey, TValue>;
+    end;
+  private
+    R: UInt32;
+  protected
+    procedure UpdateItemsThreshold(ASize: SizeInt); override;
+    function FindBucketIndex(constref AItems: TArray<TItem>; constref AKey: TKey;
+      out AHash: UInt32): SizeInt; override; overload;
+    function FindBucketIndexOrTombstone(constref AItems: TArray<TItem>; constref AKey: TKey;
+      out AHash: UInt32): SizeInt; override;
+  strict protected
+    constructor Create(ACapacity: SizeInt; const AComparer: IEqualityComparer<TKey>); override; overload;
+    constructor Create(const AComparer: IEqualityComparer<TKey>); reintroduce; overload;
+    constructor Create(ACollection: TEnumerable<TDictionaryPair>; const AComparer: IEqualityComparer<TKey>); override; overload;
+    {$IFDEF ENABLE_METHODS_WITH_TEnumerableWithPointers}
+    constructor Create(ACollection: TEnumerableWithPointers<TDictionaryPair>; const AComparer: IEqualityComparer<TKey>); override; overload;
+    {$ENDIF}
+  public // bug #26181 (redundancy of constructors)
+    constructor Create(ACapacity: SizeInt); override; overload;
+    constructor Create(ACollection: TEnumerable<TDictionaryPair>); override; overload;
+    {$IFDEF ENABLE_METHODS_WITH_TEnumerableWithPointers}
+    constructor Create(ACollection: TEnumerableWithPointers<TDictionaryPair>); override; overload;
+    {$ENDIF}
+    constructor Create(ACapacity: SizeInt; const AComparer: IExtendedEqualityComparer<TKey>); virtual; overload;
+    constructor Create(const AComparer: IExtendedEqualityComparer<TKey>); overload;
+    constructor Create(ACollection: TEnumerable<TDictionaryPair>; const AComparer: IExtendedEqualityComparer<TKey>); virtual; overload;
+    {$IFDEF ENABLE_METHODS_WITH_TEnumerableWithPointers}
+    constructor Create(ACollection: TEnumerableWithPointers<TDictionaryPair>; const AComparer: IExtendedEqualityComparer<TKey>); virtual; overload;
+    {$ENDIF}
+  end;
+
+  TDeamortizedDArrayCuckooMapEnumerator<T, CUCKOO_CONSTRAINTS> = class abstract(TCustomDictionaryEnumerator<T, CUSTOM_DICTIONARY_CONSTRAINTS>)
+  private type // for workaround Lazarus bug #25613
+    TItem = record
+      Hash: UInt32;
+      Pair: TPair<TKey, TValue>;
+    end;
+    TItemsArray = array of TItem;
+  private
+    FMainIndex: SizeInt;
+  protected
+    function DoMoveNext: Boolean; override;
+  public
+    constructor Create(ADictionary: TCustomDictionary<CUSTOM_DICTIONARY_CONSTRAINTS>);
+  end;
+
+  TDeamortizedDArrayPointersEnumerator<TCuckooCfg, TItemsArray, TItemsDArray, TQueueDictionary, PDictionaryPair> = class abstract(TEnumerator<PDictionaryPair>)
+  private var // FItems must be declared as first field and FQueue as second
+    FItems: ^TItemsDArray;
+    FQueue: TQueueDictionary;
+    FIndex: SizeInt;
+    FMainIndex: SizeInt;
+  protected
+    function DoMoveNext: boolean; override;
+    function DoGetCurrent: PDictionaryPair; override;
+    function GetCurrent: PDictionaryPair; virtual;
+  public
+    constructor Create(var AItems; AQueue: TQueueDictionary; ACount: SizeInt);
+  end;
+
+  TDeamortizedDArrayPointersCollection<TPointersEnumerator, TItemsDArray, TQueueDictionary, PDictionaryPair> = record
+  private type
+    PArray = ^TItemsDArray;
+    function Items: PArray; inline;
+    function GetCount: SizeInt; inline;
+    function GetQueue: TQueueDictionary; inline;
+  public
+    function GetEnumerator: TPointersEnumerator;
+    function ToArray: TArray<PDictionaryPair>;
+    property Count: SizeInt read GetCount;
+  end;
+
+  // more info :
+  // http://arxiv.org/abs/0903.0391
+
+  TDeamortizedDArrayCuckooMap<CUCKOO_CONSTRAINTS> = class(TCustomDictionary<CUSTOM_DICTIONARY_CONSTRAINTS>)
+  private const // Lookup Result
+    LR_NIL = -1;
+    LR_QUEUE = -2;
+  private type
+    PItem = ^TItem;
+    TItem = record
+      Hash: UInt32;
+      Pair: TPair<TKey, TValue>;
+    end;
+    TValueForQueue = TItem;
+
+    TQueueDictionary = class(TOpenAddressingLP<TKey, TValueForQueue, TDefaultHashFactory, TLinearProbing>)
+    private type // for workaround Lazarus bug #25613
+      _TItem = record
+        Hash: UInt32;
+        Pair: TPair<TKey, TValueForQueue>;
+      end;
+    private
+      FIdx: TList<UInt32>; // list to keep order
+    protected
+      procedure NotifyIndexChange(AFrom, ATo: SizeInt); override;
+      function Rehash(ASizePow2: SizeInt; AForce: Boolean = False): Boolean; override;
+    public
+      procedure InsertIntoBack(AItem: Pointer);
+      procedure InsertIntoHead(AItem: Pointer);
+      function IsEmpty: Boolean;
+      function Pop: Pointer;
+      constructor Create(ACapacity: SizeInt; const AComparer: IEqualityComparer<TKey>); override; overload;
+      destructor Destroy; override;
+    end;
+
+    // cycle-detection mechanism class
+    TCDM = class(TOpenAddressingSH<TKey, TEmptyRecord, TDefaultHashFactory, TLinearProbing>);
+    TItemsArray = array of TItem;
+    TItemsDArray = array[0..Pred(TCuckooCfg.D)] of TItemsArray;
+    TPointersEnumerator = class(TDeamortizedDArrayPointersEnumerator<TCuckooCfg, TItemsArray, TItemsDArray, TQueueDictionary, PDictionaryPair>);
+    TPointersCollection = TDeamortizedDArrayPointersCollection<TPointersEnumerator, TItemsDArray, TQueueDictionary, PDictionaryPair>;
+  public type
+    PPointersCollection = ^TPointersCollection;
+  private var
+    FItems: TItemsDArray;
+    FQueue: TQueueDictionary;  // probably can be optimized - hash TItem give information from TItem.Hash for cuckoo ...
+      // currently is kept in "TQueueDictionary = class(TOpenAddressingSH<TKey, TItem, ...>"
+
+    FCDM: TCDM; // cycle-detection mechanism
+    FItemsThreshold: SizeInt;
+  // sadly there is bug #24848 for class var ...
+  {class} var
+    CUCKOO_SIGN, CUCKOO_INDEX_SIZE, CUCKOO_HASH_SIGN: UInt32;
+    // CUCKOO_MAX_ITEMS_LENGTH: <- to do : calc max length for items based on CUCKOO sign
+    // maybe some CDM bloom filter?
+
+    procedure Resize(ANewSize: SizeInt);
+    procedure Rehash(ASizePow2: SizeInt);
+    procedure PrepareAddingItem;
+  protected
+    procedure UpdateItemsThreshold(ASize: SizeInt); override;
+    function Lookup(constref AKey: TKey; var AHashListOrIndex: PUInt32): SizeInt; inline; overload;
+    function Lookup(constref AItems: TItemsDArray; constref AKey: TKey; var AHashListOrIndex: PUInt32): SizeInt; virtual; overload;
+  public
+    type
+      // Enumerators
+      TPairEnumerator = class(TDeamortizedDArrayCuckooMapEnumerator<TDictionaryPair, CUCKOO_CONSTRAINTS>)
+      protected
+        function GetCurrent: TPair<TKey,TValue>; override;
+      end;
+
+      TValueEnumerator = class(TDeamortizedDArrayCuckooMapEnumerator<TValue, CUCKOO_CONSTRAINTS>)
+      protected
+        function GetCurrent: TValue; override;
+      end;
+
+      TPValueEnumerator = class(TDeamortizedDArrayCuckooMapEnumerator<PValue, CUCKOO_CONSTRAINTS>)
+      protected
+        function GetCurrent: PValue; override;
+      end;
+
+      TKeyEnumerator = class(TDeamortizedDArrayCuckooMapEnumerator<TKey, CUCKOO_CONSTRAINTS>)
+      protected
+        function GetCurrent: TKey; override;
+      end;
+
+      TPKeyEnumerator = class(TDeamortizedDArrayCuckooMapEnumerator<PKey, CUCKOO_CONSTRAINTS>)
+      protected
+        function GetCurrent: PKey; override;
+      end;
+
+      // Collections
+      TValueCollection = class(TDictionaryEnumerable<TValueEnumerator, TPValueEnumerator, TValue, CUSTOM_DICTIONARY_CONSTRAINTS>);
+
+      TKeyCollection = class(TDictionaryEnumerable<TKeyEnumerator, TPKeyEnumerator, TKey, CUSTOM_DICTIONARY_CONSTRAINTS>);
+
+    // bug #24283 - workaround related to lack of DoGetEnumerator
+    function GetEnumerator: TPairEnumerator; reintroduce;
+  private
+    function GetKeys: TKeyCollection;
+    function GetValues: TValueCollection;
+    function GetPointers: PPointersCollection; inline;
+  private
+    function GetItem(const AKey: TKey): TValue; inline;
+    procedure SetItem(const AKey: TKey; const AValue: TValue); overload; inline;
+    procedure SetItem(constref AValue: TValue; const AHashListOrIndex: PUInt32; ALookupResult: SizeInt); overload;
+
+    procedure AddItem(constref AItems: TItemsDArray; constref AKey: TKey; constref AValue: TValue; const AHashList: PUInt32); overload;
+    procedure DoAdd(const AKey: TKey; const AValue: TValue; const AHashList: PUInt32); overload; inline;
+    function DoRemove(const AHashListOrIndex: PUInt32; ALookupResult: SizeInt;
+      ACollectionNotification: TCollectionNotification): TValue;
+
+    function GetQueueCount: SizeInt;
+  protected
+    procedure SetCapacity(ACapacity: SizeInt); override;
+    // bug #24283 - can't descadent from TEnumerable
+    function DoGetEnumerator: TEnumerator<TDictionaryPair>; override;
+    procedure SetMaxLoadFactor(AValue: single); override;
+    function GetLoadFactor: single; override;
+    function GetCapacity: SizeInt; override;
+  strict protected // bug #26181
+    constructor Create(ACapacity: SizeInt; const AComparer: IEqualityComparer<TKey>); override; overload;
+    constructor Create(const AComparer: IEqualityComparer<TKey>); reintroduce; overload;
+    constructor Create(ACollection: TEnumerable<TDictionaryPair>; const AComparer: IEqualityComparer<TKey>); override; overload;
+    {$IFDEF ENABLE_METHODS_WITH_TEnumerableWithPointers}
+    constructor Create(ACollection: TEnumerableWithPointers<TDictionaryPair>; const AComparer: IEqualityComparer<TKey>); override; overload;
+    {$ENDIF}
+  public
+    // TODO: function TryFlushQueue(ACount: SizeInt): SizeInt;
+
+    constructor Create; override; overload;
+    constructor Create(ACapacity: SizeInt); override; overload;
+    constructor Create(ACollection: TEnumerable<TDictionaryPair>); override; overload;
+    {$IFDEF ENABLE_METHODS_WITH_TEnumerableWithPointers}
+    constructor Create(ACollection: TEnumerableWithPointers<TDictionaryPair>); override; overload;
+    {$ENDIF}
+    constructor Create(ACapacity: SizeInt; const AComparer: IExtendedEqualityComparer<TKey>); virtual; overload;
+    constructor Create(const AComparer: IExtendedEqualityComparer<TKey>); overload;
+    constructor Create(ACollection: TEnumerable<TDictionaryPair>; const AComparer: IExtendedEqualityComparer<TKey>); virtual; overload;
+    {$IFDEF ENABLE_METHODS_WITH_TEnumerableWithPointers}
+    constructor Create(ACollection: TEnumerableWithPointers<TDictionaryPair>; const AComparer: IExtendedEqualityComparer<TKey>); virtual; overload;
+    {$ENDIF}
+    destructor Destroy; override;
+
+    procedure Add(constref APair: TPair<TKey, TValue>); override; overload;
+    procedure Add(constref AKey: TKey; constref AValue: TValue); overload;
+    procedure Remove(constref AKey: TKey);
+    function ExtractPair(constref AKey: TKey): TPair<TKey, TValue>;
+    procedure Clear; override;
+    procedure TrimExcess;
+    function TryGetValue(constref AKey: TKey; out AValue: TValue): Boolean;
+    procedure AddOrSetValue(constref AKey: TKey; constref AValue: TValue);
+    function ContainsKey(constref AKey: TKey): Boolean; inline;
+    function ContainsValue(constref AValue: TValue): Boolean; overload;
+    function ContainsValue(constref AValue: TValue; const AEqualityComparer: IEqualityComparer<TValue>): Boolean; virtual; overload;
+
+    property Items[Index: TKey]: TValue read GetItem write SetItem; default;
+    property Keys: TKeyCollection read GetKeys;
+    property Values: TValueCollection read GetValues;
+    property Ptr: PPointersCollection read GetPointers;
+
+    property QueueCount: SizeInt read GetQueueCount;
+    procedure GetMemoryLayout(const AOnGetMemoryLayoutKeyPosition: TOnGetMemoryLayoutKeyPosition);
+  end;
+
+  TDictionaryOwnerships = set of (doOwnsKeys, doOwnsValues);
+
+  TObjectDeamortizedDArrayCuckooMap<CUCKOO_CONSTRAINTS> = class(TDeamortizedDArrayCuckooMap<CUCKOO_CONSTRAINTS>)
+  private
+    FOwnerships: TDictionaryOwnerships;
+  protected
+    procedure KeyNotify(constref AKey: TKey; ACollectionNotification: TCollectionNotification); override;
+    procedure ValueNotify(constref AValue: TValue; ACollectionNotification: TCollectionNotification); override;
+  public
+    // can't be as "Create(AOwnerships: TDictionaryOwnerships; ACapacity: SizeInt = 0)"
+    // because bug #25607
+    constructor Create(AOwnerships: TDictionaryOwnerships); overload;
+    constructor Create(AOwnerships: TDictionaryOwnerships; ACapacity: SizeInt); overload;
+    constructor Create(AOwnerships: TDictionaryOwnerships;
+      const AComparer: IExtendedEqualityComparer<TKey>); overload;
+    constructor Create(AOwnerships: TDictionaryOwnerships; ACapacity: SizeInt;
+      const AComparer: IExtendedEqualityComparer<TKey>); overload;
+  end;
+
+  TObjectOpenAddressingLP<OPEN_ADDRESSING_CONSTRAINTS> = class(TOpenAddressingLP<OPEN_ADDRESSING_CONSTRAINTS>)
+  private
+    FOwnerships: TDictionaryOwnerships;
+  protected
+    procedure KeyNotify(constref AKey: TKey; ACollectionNotification: TCollectionNotification); override;
+    procedure ValueNotify(constref AValue: TValue; ACollectionNotification: TCollectionNotification); override;
+  public
+    // can't be as "Create(AOwnerships: TDictionaryOwnerships; ACapacity: SizeInt = 0)"
+    // because bug #25607
+    constructor Create(AOwnerships: TDictionaryOwnerships); overload;
+    constructor Create(AOwnerships: TDictionaryOwnerships; ACapacity: SizeInt); overload;
+    constructor Create(AOwnerships: TDictionaryOwnerships;
+      const AComparer: IEqualityComparer<TKey>); overload;
+    constructor Create(AOwnerships: TDictionaryOwnerships; ACapacity: SizeInt;
+      const AComparer: IEqualityComparer<TKey>); overload;
+  end;
+
+  // useful generics overloads
+  TOpenAddressingLP<TKey, TValue, THashFactory> = class(TOpenAddressingLP<TKey, TValue, THashFactory, TLinearProbing>);
+  TOpenAddressingLP<TKey, TValue>  = class(TOpenAddressingLP<TKey, TValue, TDefaultHashFactory, TLinearProbing>);
+
+  TObjectOpenAddressingLP<TKey, TValue, THashFactory> = class(TObjectOpenAddressingLP<TKey, TValue, THashFactory, TLinearProbing>);
+  TObjectOpenAddressingLP<TKey, TValue> = class(TObjectOpenAddressingLP<TKey, TValue, TDefaultHashFactory, TLinearProbing>);
+
+  // Linear Probing with Tombstones (LPT)
+  TOpenAddressingLPT<TKey, TValue, THashFactory> = class(TOpenAddressingSH<TKey, TValue, THashFactory, TLinearProbing>);
+  TOpenAddressingLPT<TKey, TValue> = class(TOpenAddressingSH<TKey, TValue, TDefaultHashFactory, TLinearProbing>);
+
+  TOpenAddressingQP<TKey, TValue, THashFactory> = class(TOpenAddressingQP<TKey, TValue, THashFactory, TQuadraticProbing>);
+  TOpenAddressingQP<TKey, TValue> = class(TOpenAddressingQP<TKey, TValue, TDefaultHashFactory, TQuadraticProbing>);
+
+  TOpenAddressingDH<TKey, TValue, THashFactory> = class(TOpenAddressingDH<TKey, TValue, THashFactory, TDoubleHashing>);
+  TOpenAddressingDH<TKey, TValue> = class(TOpenAddressingDH<TKey, TValue, TDelphiDoubleHashFactory, TDoubleHashing>);
+
+  TCuckooD2<TKey, TValue, THashFactory> = class(TDeamortizedDArrayCuckooMap<TKey, TValue, THashFactory, TDeamortizedCuckooHashingCfg_D2>);
+  TCuckooD2<TKey, TValue> = class(TDeamortizedDArrayCuckooMap<TKey, TValue, TDelphiDoubleHashFactory, TDeamortizedCuckooHashingCfg_D2>);
+
+  TCuckooD4<TKey, TValue, THashFactory> = class(TDeamortizedDArrayCuckooMap<TKey, TValue, THashFactory, TDeamortizedCuckooHashingCfg_D4>);
+  TCuckooD4<TKey, TValue> = class(TDeamortizedDArrayCuckooMap<TKey, TValue, TDelphiQuadrupleHashFactory, TDeamortizedCuckooHashingCfg_D4>);
+
+  TCuckooD6<TKey, TValue, THashFactory> = class(TDeamortizedDArrayCuckooMap<TKey, TValue, THashFactory, TDeamortizedCuckooHashingCfg_D6>);
+  TCuckooD6<TKey, TValue> = class(TDeamortizedDArrayCuckooMap<TKey, TValue, TDelphiSixfoldHashFactory, TDeamortizedCuckooHashingCfg_D6>);
+
+  TObjectCuckooD2<TKey, TValue, THashFactory> = class(TObjectDeamortizedDArrayCuckooMap<TKey, TValue, THashFactory, TDeamortizedCuckooHashingCfg_D2>);
+  TObjectCuckooD2<TKey, TValue> = class(TObjectDeamortizedDArrayCuckooMap<TKey, TValue, TDelphiDoubleHashFactory, TDeamortizedCuckooHashingCfg_D2>);
+
+  TObjectCuckooD4<TKey, TValue, THashFactory> = class(TObjectDeamortizedDArrayCuckooMap<TKey, TValue, THashFactory, TDeamortizedCuckooHashingCfg_D4>);
+  TObjectCuckooD4<TKey, TValue> = class(TObjectDeamortizedDArrayCuckooMap<TKey, TValue, TDelphiQuadrupleHashFactory, TDeamortizedCuckooHashingCfg_D4>);
+
+  TObjectCuckooD6<TKey, TValue, THashFactory> = class(TObjectDeamortizedDArrayCuckooMap<TKey, TValue, THashFactory, TDeamortizedCuckooHashingCfg_D6>);
+  TObjectCuckooD6<TKey, TValue> = class(TObjectDeamortizedDArrayCuckooMap<TKey, TValue, TDelphiSixfoldHashFactory, TDeamortizedCuckooHashingCfg_D6>);
+
+  // for normal programmers to normal use =)
+  TDictionary<TKey, TValue> = class(TOpenAddressingLP<TKey, TValue>);
+  TObjectDictionary<TKey, TValue> = class(TObjectOpenAddressingLP<TKey, TValue>);
+
+  TFastHashMap<TKey, TValue> = class(TCuckooD2<TKey, TValue>);
+  TFastObjectHashMap<TKey, TValue> = class(TObjectCuckooD2<TKey, TValue>);
+
+  THashMap<TKey, TValue> = class(TCuckooD4<TKey, TValue>);
+  TObjectHashMap<TKey, TValue> = class(TObjectCuckooD4<TKey, TValue>);

+ 6 - 6
CryptoLib/src/Math/ClpBigInteger.pas

@@ -2618,7 +2618,7 @@ begin
   begin
   begin
 
 
     x0 := UInt32(x[n - 1]);
     x0 := UInt32(x[n - 1]);
-    t := UInt32(x0 * mDash);
+    t := UInt32(UInt64(x0) * mDash);
 
 
     carry := t * UInt32(m[n - 1]) + x0;
     carry := t * UInt32(m[n - 1]) + x0;
 
 
@@ -2807,7 +2807,7 @@ begin
 
 
   carry := xi * y0;
   carry := xi * y0;
 
 
-  t := UInt32(UInt32(carry) * mDash);
+  t := UInt32(UInt64(UInt32(carry)) * mDash);
 
 
   prod2 := t * UInt32(m[n - 1]);
   prod2 := t * UInt32(m[n - 1]);
   carry := carry + UInt32(prod2);
   carry := carry + UInt32(prod2);
@@ -2839,7 +2839,7 @@ begin
 
 
     prod1 := xi * y0;
     prod1 := xi * y0;
     carry := (prod1 and UIMASK) + a0;
     carry := (prod1 and UIMASK) + a0;
-    t := UInt32(UInt32(carry) * mDash);
+    t := UInt32(UInt64(UInt32(carry)) * mDash);
 
 
     prod2 := t * UInt32(m[n - 1]);
     prod2 := t * UInt32(m[n - 1]);
     carry := carry + UInt32(prod2);
     carry := carry + UInt32(prod2);
@@ -3304,7 +3304,7 @@ begin
 
 
   if (QuickPow2Check()) then
   if (QuickPow2Check()) then
   begin
   begin
-    powOf2 := Int64(exp) * (BitLength - 1);
+    powOf2 := Int64(exp) * (Int64(BitLength) - 1);
     if (powOf2 > System.High(Int32)) then
     if (powOf2 > System.High(Int32)) then
     begin
     begin
       raise EArithmeticCryptoLibException.CreateRes(@SResultTooLarge);
       raise EArithmeticCryptoLibException.CreateRes(@SResultTooLarge);
@@ -4011,7 +4011,7 @@ begin
 
 
   carry := x0 * x0;
   carry := x0 * x0;
 
 
-  t := UInt32(UInt32(carry) * mDash);
+  t := UInt32(UInt64(UInt32(carry)) * mDash);
 
 
   prod2 := t * UInt32(m[n - 1]);
   prod2 := t * UInt32(m[n - 1]);
   carry := carry + UInt32(prod2);
   carry := carry + UInt32(prod2);
@@ -4040,7 +4040,7 @@ begin
   while i >= 0 do
   while i >= 0 do
   begin
   begin
     a0 := UInt32(a[n]);
     a0 := UInt32(a[n]);
-    t := UInt32(a0 * mDash);
+    t := UInt32(UInt64(a0) * mDash);
 
 
     carry := t * UInt32(m[n - 1]) + a0;
     carry := t * UInt32(m[n - 1]) + a0;
 
 

+ 40 - 12
CryptoLib/src/Packages/FPC/CryptoLib4PascalPackage.lpk

@@ -9,8 +9,8 @@
       <Version Value="11"/>
       <Version Value="11"/>
       <PathDelim Value="\"/>
       <PathDelim Value="\"/>
       <SearchPaths>
       <SearchPaths>
-        <IncludeFiles Value="..\..\Include"/>
-        <OtherUnitFiles Value="..\..\Asn1;..\..\Asn1\CryptoPro;..\..\Asn1\Nist;..\..\Asn1\Oiw;..\..\Asn1\Pkcs;..\..\Asn1\RossStandart;..\..\Asn1\Sec;..\..\Asn1\TeleTrust;..\..\Asn1\X9;..\..\Crypto;..\..\Crypto\Generators;..\..\Crypto\Parameters;..\..\Crypto\Prng;..\..\Crypto\Signers;..\..\Interfaces;..\..\Math;..\..\Math\EC;..\..\Math\EC\Abc;..\..\Math\EC\Endo;..\..\Math\EC\Multiplier;..\..\Math\Field;..\..\Math\Raw;..\..\Security;..\..\Utils;..\..\Utils\Collections;..\..\Utils\Encoders;..\..\Utils\Helpers;..\..\Utils\IO;..\..\Utils\Randoms;..\..\Utils\Rng;..\..\Crypto\Modes;..\..\Crypto\Paddings;..\..\Crypto\Engines;..\..\Crypto\Parsers;..\..\Crypto\Agreement;..\..\Crypto\Macs;..\..\Asn1\Misc;..\..\Asn1\Iana"/>
+        <IncludeFiles Value="..\..\Include;..\..\3rdParty\generics.collections\src\inc"/>
+        <OtherUnitFiles Value="..\..\Asn1;..\..\Asn1\CryptoPro;..\..\Asn1\Nist;..\..\Asn1\Oiw;..\..\Asn1\Pkcs;..\..\Asn1\RossStandart;..\..\Asn1\Sec;..\..\Asn1\TeleTrust;..\..\Asn1\X9;..\..\Crypto;..\..\Crypto\Generators;..\..\Crypto\Parameters;..\..\Crypto\Prng;..\..\Crypto\Signers;..\..\Interfaces;..\..\Math;..\..\Math\EC;..\..\Math\EC\Abc;..\..\Math\EC\Endo;..\..\Math\EC\Multiplier;..\..\Math\Field;..\..\Math\Raw;..\..\Security;..\..\Utils;..\..\Utils\Collections;..\..\Utils\Encoders;..\..\Utils\Helpers;..\..\Utils\IO;..\..\Utils\Randoms;..\..\Utils\Rng;..\..\Crypto\Modes;..\..\Crypto\Paddings;..\..\Crypto\Engines;..\..\Crypto\Parsers;..\..\Crypto\Agreement;..\..\Crypto\Macs;..\..\Asn1\Misc;..\..\Asn1\Iana;..\..\3rdParty\generics.collections\src"/>
         <UnitOutputDirectory Value="lib\$(TargetCPU)-$(TargetOS)"/>
         <UnitOutputDirectory Value="lib\$(TargetCPU)-$(TargetOS)"/>
       </SearchPaths>
       </SearchPaths>
       <CodeGeneration>
       <CodeGeneration>
@@ -24,8 +24,8 @@
 
 
  Acknowledgements: 
  Acknowledgements: 
 Thanks to Sphere 10 Software (http://www.sphere10.com/) for sponsoring the development of this library "/>
 Thanks to Sphere 10 Software (http://www.sphere10.com/) for sponsoring the development of this library "/>
-    <Version Major="2"/>
-    <Files Count="368">
+    <Version Major="2" Minor="1"/>
+    <Files Count="376">
       <Item1>
       <Item1>
         <Filename Value="..\..\Asn1\ClpAsn1Encodable.pas"/>
         <Filename Value="..\..\Asn1\ClpAsn1Encodable.pas"/>
         <UnitName Value="ClpAsn1Encodable"/>
         <UnitName Value="ClpAsn1Encodable"/>
@@ -1499,23 +1499,51 @@ Thanks to Sphere 10 Software (http://www.sphere10.com/) for sponsoring the devel
         <Filename Value="..\..\Interfaces\ClpIHMac.pas"/>
         <Filename Value="..\..\Interfaces\ClpIHMac.pas"/>
         <UnitName Value="ClpIHMac"/>
         <UnitName Value="ClpIHMac"/>
       </Item368>
       </Item368>
+      <Item369>
+        <Filename Value="..\..\3rdParty\generics.collections\src\generics.collections.pas"/>
+        <UnitName Value="Generics.Collections"/>
+      </Item369>
+      <Item370>
+        <Filename Value="..\..\3rdParty\generics.collections\src\generics.defaults.pas"/>
+        <UnitName Value="Generics.Defaults"/>
+      </Item370>
+      <Item371>
+        <Filename Value="..\..\3rdParty\generics.collections\src\generics.hashes.pas"/>
+        <UnitName Value="Generics.Hashes"/>
+      </Item371>
+      <Item372>
+        <Filename Value="..\..\3rdParty\generics.collections\src\generics.helpers.pas"/>
+        <UnitName Value="Generics.Helpers"/>
+      </Item372>
+      <Item373>
+        <Filename Value="..\..\3rdParty\generics.collections\src\generics.memoryexpanders.pas"/>
+        <UnitName Value="Generics.MemoryExpanders"/>
+      </Item373>
+      <Item374>
+        <Filename Value="..\..\3rdParty\generics.collections\src\generics.strings.pas"/>
+        <UnitName Value="Generics.Strings"/>
+      </Item374>
+      <Item375>
+        <Filename Value="..\..\3rdParty\generics.collections\src\inc\generics.dictionaries.inc"/>
+        <Type Value="Include"/>
+      </Item375>
+      <Item376>
+        <Filename Value="..\..\3rdParty\generics.collections\src\inc\generics.dictionariesh.inc"/>
+        <Type Value="Include"/>
+      </Item376>
     </Files>
     </Files>
-    <RequiredPkgs Count="4">
+    <RequiredPkgs Count="3">
       <Item1>
       <Item1>
-        <PackageName Value="generics_collections"/>
+        <PackageName Value="HashLib4PascalPackage"/>
+        <MinVersion Major="2" Minor="4" Valid="True"/>
       </Item1>
       </Item1>
       <Item2>
       <Item2>
         <PackageName Value="SimpleBaseLib4PascalPackage"/>
         <PackageName Value="SimpleBaseLib4PascalPackage"/>
         <MinVersion Major="1" Minor="2" Valid="True"/>
         <MinVersion Major="1" Minor="2" Valid="True"/>
       </Item2>
       </Item2>
       <Item3>
       <Item3>
-        <PackageName Value="HashLib4PascalPackage"/>
-        <MaxVersion Major="2" Minor="3"/>
-        <MinVersion Major="2" Minor="4" Valid="True"/>
-      </Item3>
-      <Item4>
         <PackageName Value="FCL"/>
         <PackageName Value="FCL"/>
-      </Item4>
+      </Item3>
     </RequiredPkgs>
     </RequiredPkgs>
     <UsageOptions>
     <UsageOptions>
       <UnitPath Value="$(PkgOutDir)"/>
       <UnitPath Value="$(PkgOutDir)"/>

+ 3 - 1
CryptoLib/src/Packages/FPC/CryptoLib4PascalPackage.pas

@@ -121,7 +121,9 @@ uses
   ClpISO10126d2Padding, ClpIISO10126d2Padding, ClpPkcs5S2ParametersGenerator, 
   ClpISO10126d2Padding, ClpIISO10126d2Padding, ClpPkcs5S2ParametersGenerator, 
   ClpIPkcs5S2ParametersGenerator, ClpIPbeParametersGenerator, 
   ClpIPkcs5S2ParametersGenerator, ClpIPbeParametersGenerator, 
   ClpPbeParametersGenerator, ClpHMac, ClpMiscObjectIdentifiers, 
   ClpPbeParametersGenerator, ClpHMac, ClpMiscObjectIdentifiers, 
-  ClpIanaObjectIdentifiers, ClpMacUtilities, ClpIMac, ClpIHMac;
+  ClpIanaObjectIdentifiers, ClpMacUtilities, ClpIMac, ClpIHMac, 
+  Generics.Collections, Generics.Defaults, Generics.Hashes, Generics.Helpers, 
+  Generics.MemoryExpanders, Generics.Strings;
 
 
 implementation
 implementation