(*
* Copyright (c) 2008-2011, Ciobanu Alexandru
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*     * Redistributions of source code must retain the above copyright
*       notice, this list of conditions and the following disclaimer.
*     * Redistributions in binary form must reproduce the above copyright
*       notice, this list of conditions and the following disclaimer in the
*       documentation and/or other materials provided with the distribution.
*     * Neither the name of the <organization> nor the
*       names of its contributors may be used to endorse or promote products
*       derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ''AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*)
unit Collections.Stacks;
interface
uses SysUtils,
     Generics.Defaults,
     Generics.Collections,
     Collections.Lists,
     Collections.Base;
type
  ///  <summary>The generic <c>stack (LIFO)</c> collection.</summary>
  ///  <remarks>This type uses an internal array to store its values.</remarks>
  TStack<T> = class(TEnexCollection<T>, IStack<T>, IDynamic)
  private type
    {$REGION 'Internal Types'}
    { Generic Stack List Enumerator }
    TEnumerator = class(TEnumerator<T>)
    private
      FVer: NativeInt;
      FStack: TStack<T>;
      FCurrentIndex: NativeInt;
    public
      { Constructor }
      constructor Create(const AStack: TStack<T>);
      { Destructor }
      destructor Destroy(); override;
      function GetCurrent(): T; override;
      function MoveNext(): Boolean; override;
    end;
    {$ENDREGION}
  private var
    FArray: TArray<T>;
    FLength: NativeInt;
    FVer: NativeInt;
  protected
    ///  <summary>Returns the number of elements in the stack.</summary>
    ///  <returns>A positive value specifying the number of elements in the stack.</returns>
    function GetCount(): NativeInt; override;
    ///  <summary>Returns the current capacity.</summary>
    ///  <returns>A positive number that specifies the number of elements that the stack can hold before it
    ///  needs to grow again.</returns>
    ///  <remarks>The value of this method is greater than or equal to the amount of elements in the stack. If this value
    ///  is greater than the number of elements, it means that the stack has some extra capacity to operate upon.</remarks>
    function GetCapacity(): NativeInt;
  public
    ///  <summary>Creates a new instance of this class.</summary>
    ///  <remarks>The default rule set is requested.</remarks>
    constructor Create(); overload;
    ///  <summary>Creates a new instance of this class.</summary>
    ///  <param name="AInitialCapacity">The stack's initial capacity.</param>
    ///  <remarks>The default rule set is requested.</remarks>
    constructor Create(const AInitialCapacity: NativeInt); overload;
    ///  <summary>Creates a new instance of this class.</summary>
    ///  <param name="ACollection">A collection to copy elements from.</param>
    ///  <exception cref="SysUtils|EArgumentNilException"><paramref name="ACollection"/> is <c>nil</c>.</exception>
    ///  <remarks>The default rule set is requested.</remarks>
    constructor Create(const ACollection: IEnumerable<T>); overload;
    ///  <summary>Creates a new instance of this class.</summary>
    ///  <param name="AArray">An array to copy elements from.</param>
    ///  <remarks>The default rule set is requested.</remarks>
    constructor Create(const AArray: array of T); overload;
    ///  <summary>Creates a new instance of this class.</summary>
    ///  <param name="ARules">A rule set describing the elements in the stack.</param>
    constructor Create(const ARules: TRules<T>); overload;
    ///  <summary>Creates a new instance of this class.</summary>
    ///  <param name="ARules">A rule set describing the elements in the stack.</param>
    ///  <param name="AInitialCapacity">The stack's initial capacity.</param>
    constructor Create(const ARules: TRules<T>; const AInitialCapacity: NativeInt); overload;
    ///  <summary>Creates a new instance of this class.</summary>
    ///  <param name="ARules">A rule set describing the elements in the stack.</param>
    ///  <param name="ACollection">A collection to copy elements from.</param>
    ///  <exception cref="SysUtils|EArgumentNilException"><paramref name="ACollection"/> is <c>nil</c>.</exception>
    constructor Create(const ARules: TRules<T>; const ACollection: IEnumerable<T>); overload;
    ///  <summary>Creates a new instance of this class.</summary>
    ///  <param name="ARules">A rule set describing the elements in the stack.</param>
    ///  <param name="AArray">An array to copy elements from.</param>
    constructor Create(const ARules: TRules<T>; const AArray: array of T); overload;
    ///  <summary>Destroys this instance.</summary>
    ///  <remarks>Do not call this method directly; call <c>Free</c> instead.</remarks>
    destructor Destroy(); override;
    ///  <summary>Clears the contents of the stack.</summary>
    procedure Clear();
    ///  <summary>Pushes an element to the top of the stack.</summary>
    ///  <param name="AValue">The value to push.</param>
    procedure Push(const AValue: T);
    ///  <summary>Retrieves the element from the top of the stack.</summary>
    ///  <returns>The value at the top of the stack.</returns>
    ///  <remarks>This method removes the element from the top of the stack.</remarks>
    ///  <exception cref="Collections.Base|ECollectionEmptyException">The stack is empty.</exception>
    function Pop(): T;
    ///  <summary>Reads the element from the top of the stack.</summary>
    ///  <returns>The value at the top of the stack.</returns>
    ///  <remarks>This method does not remove the element from the top of the stack. It merely reads its value.</remarks>
    ///  <exception cref="Collections.Base|ECollectionEmptyException">The stack is empty.</exception>
    function Peek(): T;
    ///  <summary>Removes an element from the stack.</summary>
    ///  <param name="AValue">The value to remove. If there is no such element in the stack, nothing happens.</param>
    procedure Remove(const AValue: T);
    ///  <summary>Checks whether the stack contains a given value.</summary>
    ///  <param name="AValue">The value to check.</param>
    ///  <returns><c>True</c> if the value was found in the stack; <c>False</c> otherwise.</returns>
    function Contains(const AValue: T): Boolean;
    ///  <summary>Specifies the number of elements in the stack.</summary>
    ///  <returns>A positive value specifying the number of elements in the stack.</returns>
    property Count: NativeInt read FLength;
    ///  <summary>Specifies the current capacity.</summary>
    ///  <returns>A positive number that specifies the number of elements that the stack can hold before it
    ///  needs to grow again.</returns>
    ///  <remarks>The value of this property is greater than or equal to the amount of elements in the stack. If this value
    ///  if greater than the number of elements, it means that the stack has some extra capacity to operate upon.</remarks>
    property Capacity: NativeInt read GetCapacity;
    ///  <summary>Removes the excess capacity from the stack.</summary>
    ///  <remarks>This method can be called manually to force the stack to drop the extra capacity it might hold. For example,
    ///  after performing some massive operations on a big list, call this method to ensure that all extra memory held by the
    ///  stack is released.</remarks>
    procedure Shrink();
    ///  <summary>Forces the stack to increase its capacity.</summary>
    ///  <remarks>Call this method to force the stack to increase its capacity ahead of time. Manually adjusting the capacity
    ///  can be useful in certain situations.</remarks>
    procedure Grow();
    ///  <summary>Returns a new enumerator object used to enumerate this stack.</summary>
    ///  <remarks>This method is usually called by compiler-generated code. Its purpose is to create an enumerator
    ///  object that is used to actually traverse the stack.</remarks>
    ///  <returns>An enumerator object.</returns>
    function GetEnumerator(): IEnumerator<T>; override;
    ///  <summary>Copies the values stored in the stack to a given array.</summary>
    ///  <param name="AArray">An array where to copy the contents of the stack.</param>
    ///  <param name="AStartIndex">The index into the array at which the copying begins.</param>
    ///  <remarks>This method assumes that <paramref name="AArray"/> has enough space to hold the contents of the stack.</remarks>
    ///  <exception cref="SysUtils|EArgumentOutOfRangeException"><paramref name="AStartIndex"/> is out of bounds.</exception>
    ///  <exception cref="Collections.Base|EArgumentOutOfSpaceException">The array is not long enough.</exception>
    procedure CopyTo(var AArray: array of T; const AStartIndex: NativeInt); overload; override;
    ///  <summary>Checks whether the stack is empty.</summary>
    ///  <returns><c>True</c> if the stack is empty; <c>False</c> otherwise.</returns>
    ///  <remarks>This method is the recommended way of detecting if the stack is empty.</remarks>
    function Empty(): Boolean; override;
    ///  <summary>Returns the biggest element.</summary>
    ///  <returns>An element from the stack considered to have the biggest value.</returns>
    ///  <exception cref="Collections.Base|ECollectionEmptyException">The stack is empty.</exception>
    function Max(): T; override;
    ///  <summary>Returns the smallest element.</summary>
    ///  <returns>An element from the stack considered to have the smallest value.</returns>
    ///  <exception cref="Collections.Base|ECollectionEmptyException">The stack is empty.</exception>
    function Min(): T; override;
    ///  <summary>Returns the first element.</summary>
    ///  <returns>The first element in the stack.</returns>
    ///  <exception cref="Collections.Base|ECollectionEmptyException">The stack is empty.</exception>
    function First(): T; override;
    ///  <summary>Returns the first element or a default, if the stack is empty.</summary>
    ///  <param name="ADefault">The default value returned if the stack is empty.</param>
    ///  <returns>The first element in the stack if the stack is not empty; otherwise <paramref name="ADefault"/> is returned.</returns>
    function FirstOrDefault(const ADefault: T): T; override;
    ///  <summary>Returns the last element.</summary>
    ///  <returns>The last element in the stack.</returns>
    ///  <exception cref="Collections.Base|ECollectionEmptyException">The stack is empty.</exception>
    function Last(): T; override;
    ///  <summary>Returns the last element or a default if the stack is empty.</summary>
    ///  <param name="ADefault">The default value returned if the stack is empty.</param>
    ///  <returns>The last element in stack if the stack is not empty; otherwise <paramref name="ADefault"/> is returned.</returns>
    function LastOrDefault(const ADefault: T): T; override;
    ///  <summary>Returns the single element stored in the stack.</summary>
    ///  <returns>The element in the stack.</returns>
    ///  <remarks>This method checks whether the stack contains just one element, in which case it is returned.</remarks>
    ///  <exception cref="Collections.Base|ECollectionEmptyException">The stack is empty.</exception>
    ///  <exception cref="Collections.Base|ECollectionNotOneException">There is more than one element in the stack.</exception>
    function Single(): T; override;
    ///  <summary>Returns the single element stored in the stack or a default value.</summary>
    ///  <param name="ADefault">The default value returned if there are less or more elements in the stack.</param>
    ///  <returns>The element in the stack if the condition is satisfied; <paramref name="ADefault"/> is returned otherwise.</returns>
    ///  <remarks>This method checks whether the stack contains just one element, in which case it is returned. Otherwise
    ///  the value in <paramref name="ADefault"/> is returned.</remarks>
    function SingleOrDefault(const ADefault: T): T; override;
    ///  <summary>Aggregates a value based on the stack's elements.</summary>
    ///  <param name="AAggregator">The aggregator method.</param>
    ///  <returns>A value that contains the stack's aggregated value.</returns>
    ///  <remarks>This method returns the first element if the stack only has one element. Otherwise,
    ///  <paramref name="AAggregator"/> is invoked for each two elements (first and second; then the result of the first two
    ///  and the third, and so on). The simplest example of aggregation is the "sum" operation, where you can obtain the sum of all
    ///  elements in the value.</remarks>
    ///  <exception cref="SysUtils|EArgumentNilException"><paramref name="AAggregator"/> is <c>nil</c>.</exception>
    ///  <exception cref="Collections.Base|ECollectionEmptyException">The stack is empty.</exception>
    function Aggregate(const AAggregator: TFunc<T, T, T>): T; override;
    ///  <summary>Aggregates a value based on the stack's elements.</summary>
    ///  <param name="AAggregator">The aggregator method.</param>
    ///  <param name="ADefault">The default value returned if the stack is empty.</param>
    ///  <returns>A value that contains the stack's aggregated value. If the stack is empty, <paramref name="ADefault"/> is returned.</returns>
    ///  <remarks>This method returns the first element if the stack only has one element. Otherwise,
    ///  <paramref name="AAggregator"/> is invoked for each two elements (first and second; then the result of the first two
    ///  and the third, and so on). The simplest example of aggregation is the "sum" operation, where you can obtain the sum of all
    ///  elements in the value.</remarks>
    ///  <exception cref="SysUtils|EArgumentNilException"><paramref name="AAggregator"/> is <c>nil</c>.</exception>
    function AggregateOrDefault(const AAggregator: TFunc<T, T, T>; const ADefault: T): T; override;
    ///  <summary>Returns the element at a given position.</summary>
    ///  <param name="AIndex">The index from which to return the element.</param>
    ///  <returns>The element at the specified position.</returns>
    ///  <exception cref="Collections.Base|ECollectionEmptyException">The stack is empty.</exception>
    ///  <exception cref="SysUtils|EArgumentOutOfRangeException"><paramref name="AIndex"/> is out of bounds.</exception>
    function ElementAt(const AIndex: NativeInt): T; override;
    ///  <summary>Returns the element at a given position.</summary>
    ///  <param name="AIndex">The index from which to return the element.</param>
    ///  <param name="ADefault">The default value returned if the stack is empty.</param>
    ///  <returns>The element at the specified position if the stack is not empty and the position is not out of bounds; otherwise
    ///  the value of <paramref name="ADefault"/> is returned.</returns>
    function ElementAtOrDefault(const AIndex: NativeInt; const ADefault: T): T; override;
    ///  <summary>Checks whether at least one element in the stack satisfies a given predicate.</summary>
    ///  <param name="APredicate">The predicate to check for each element.</param>
    ///  <returns><c>True</c> if at least one element satisfies a given predicate; <c>False</c> otherwise.</returns>
    ///  <remarks>This method traverses the whole stack and checks the value of the predicate for each element. This method
    ///  stops on the first element for which the predicate returns <c>True</c>. The logical equivalent of this operation is "OR".</remarks>
    ///  <exception cref="SysUtils|EArgumentNilException"><paramref name="APredicate"/> is <c>nil</c>.</exception>
    function Any(const APredicate: TFunc<T, Boolean>): Boolean; override;
    ///  <summary>Checks that all elements in the stack satisfy a given predicate.</summary>
    ///  <param name="APredicate">The predicate to check for each element.</param>
    ///  <returns><c>True</c> if all elements satisfy a given predicate; <c>False</c> otherwise.</returns>
    ///  <remarks>This method traverses the whole stack and checks the value of the predicate for each element. This method
    ///  stops on the first element for which the predicate returns <c>False</c>. The logical equivalent of this operation is "AND".</remarks>
    ///  <exception cref="SysUtils|EArgumentNilException"><paramref name="APredicate"/> is <c>nil</c>.</exception>
    function All(const APredicate: TFunc<T, Boolean>): Boolean; override;
    ///  <summary>Checks whether the elements in this stack are equal to the elements in another collection.</summary>
    ///  <param name="ACollection">The collection to compare to.</param>
    ///  <returns><c>True</c> if the collections are equal; <c>False</c> if the collections are different.</returns>
    ///  <remarks>This method checks that each element at position X in this stack is equal to an element at position X in
    ///  the provided collection. If the number of elements in both collections is different, then the collections are considered different.
    ///  Note that the comparison of elements is done using the rule set used by this stack. This means that comparing this collection
    ///  to another one might yeild a different result than comparing the other collection to this one.</remarks>
    ///  <exception cref="SysUtils|EArgumentNilException"><paramref name="ACollection"/> is <c>nil</c>.</exception>
    function EqualsTo(const ACollection: IEnumerable<T>): Boolean; override;
  end;
  ///  <summary>The generic <c>stack (LIFO)</c> collection designed to store objects.</summary>
  ///  <remarks>This type uses an internal array to store its objects.</remarks>
  TObjectStack<T: class> = class(TStack<T>)
  private
    FOwnsObjects: Boolean;
  protected
    ///  <summary>Frees the object that was removed from the collection.</summary>
    ///  <param name="AElement">The object that was removed from the collection.</param>
    procedure HandleElementRemoved(const AElement: T); override;
  public
    ///  <summary>Specifies whether this stack owns the objects stored in it.</summary>
    ///  <returns><c>True</c> if the stack owns its objects; <c>False</c> otherwise.</returns>
    ///  <remarks>This property controls the way the stack controls the life-time of the stored objects.</remarks>
    property OwnsObjects: Boolean read FOwnsObjects write FOwnsObjects;
  end;
type
  ///  <summary>The generic <c>stack (LIFO)</c> collection.</summary>
  ///  <remarks>This type uses a linked list to store its values.</remarks>
  TLinkedStack<T> = class(TEnexCollection<T>, IStack<T>)
  private type
    {$REGION 'Internal Types'}
    PEntry = ^TEntry;
    TEntry = record
      FPrev, FNext: PEntry;
      FValue: T;
    end;
    TEnumerator = class(TEnumerator<T>)
    private
      FVer: NativeInt;
      FStack: TLinkedStack<T>;
      FCurrentEntry: PEntry;
      FValue: T;
    public
      { Constructor }
      constructor Create(const AStack: TLinkedStack<T>);
      { Destructor }
      destructor Destroy(); override;
      function GetCurrent(): T; override;
      function MoveNext(): Boolean; override;
    end;
    {$ENDREGION}
  private var
    FFirst, FLast, FFirstFree: PEntry;
    FCount, FFreeCount: NativeInt;
    FVer: NativeInt;
    { Caching }
    function NeedEntry(const AValue: T): PEntry;
    procedure ReleaseEntry(const AEntry: PEntry);
  protected
    ///  <summary>Returns the number of elements in the stack.</summary>
    ///  <returns>A positive value specifying the number of elements in the stack.</returns>
    function GetCount(): NativeInt; override;
  public
    ///  <summary>Creates a new instance of this class.</summary>
    ///  <remarks>The default rule set is requested.</remarks>
    constructor Create(); overload;
    ///  <summary>Creates a new instance of this class.</summary>
    ///  <param name="ACollection">A collection to copy elements from.</param>
    ///  <exception cref="SysUtils|EArgumentNilException"><paramref name="ACollection"/> is <c>nil</c>.</exception>
    ///  <remarks>The default rule set is requested.</remarks>
    constructor Create(const ACollection: IEnumerable<T>); overload;
    ///  <summary>Creates a new instance of this class.</summary>
    ///  <param name="AArray">An array to copy elements from.</param>
    ///  <remarks>The default rule set is requested.</remarks>
    constructor Create(const AArray: array of T); overload;
    ///  <summary>Creates a new instance of this class.</summary>
    ///  <param name="ARules">A rule set describing the elements in the stack.</param>
    constructor Create(const ARules: TRules<T>); overload;
    ///  <summary>Creates a new instance of this class.</summary>
    ///  <param name="ACollection">A collection to copy elements from.</param>
    ///  <param name="ARules">A rule set describing the elements in the stack.</param>
    ///  <exception cref="SysUtils|EArgumentNilException"><paramref name="ACollection"/> is <c>nil</c>.</exception>
    constructor Create(const ARules: TRules<T>; const ACollection: IEnumerable<T>); overload;
    ///  <summary>Creates a new instance of this class.</summary>
    ///  <param name="ARules">A rule set describing the elements in the stack.</param>
    ///  <param name="AArray">An array to copy elements from.</param>
    constructor Create(const ARules: TRules<T>; const AArray: array of T); overload;
    ///  <summary>Destroys this instance.</summary>
    ///  <remarks>Do not call this method directly; call <c>Free</c> instead</remarks>
    destructor Destroy(); override;
    ///  <summary>Clears the contents of the stack.</summary>
    procedure Clear();
    ///  <summary>Pushes an element to the top of the stack.</summary>
    ///  <param name="AValue">The value to push.</param>
    procedure Push(const AValue: T);
    ///  <summary>Retrieves the element from the top of the stack.</summary>
    ///  <returns>The value at the top of the stack.</returns>
    ///  <remarks>This method removes the element from the top of the stack.</remarks>
    ///  <exception cref="Collections.Base|ECollectionEmptyException">The stack is empty.</exception>
    function Pop(): T;
    ///  <summary>Reads the element from the top of the stack.</summary>
    ///  <returns>The value at the top of the stack.</returns>
    ///  <remarks>This method does not remove the element from the top of the stack. It merely reads its value.</remarks>
    ///  <exception cref="Collections.Base|ECollectionEmptyException">The stack is empty.</exception>
    function Peek(): T;
    ///  <summary>Removes an element from the stack.</summary>
    ///  <param name="AValue">The value to remove. If there is no such element in the stack, nothing happens.</param>
    procedure Remove(const AValue: T);
    ///  <summary>Checks whether the stack contains a given value.</summary>
    ///  <param name="AValue">The value to check.</param>
    ///  <returns><c>True</c> if the value was found in the stack; <c>False</c> otherwise.</returns>
    function Contains(const AValue: T): Boolean;
    ///  <summary>Specifies the number of elements in the stack.</summary>
    ///  <returns>A positive value specifying the number of elements in the stack.</returns>
    property Count: NativeInt read FCount;
    ///  <summary>Returns a new enumerator object used to enumerate this stack.</summary>
    ///  <remarks>This method is usually called by compiler-generated code. Its purpose is to create an enumerator
    ///  object that is used to actually traverse the stack.</remarks>
    ///  <returns>An enumerator object.</returns>
    function GetEnumerator(): IEnumerator<T>; override;
    ///  <summary>Copies the values stored in the stack to a given array.</summary>
    ///  <param name="AArray">An array where to copy the contents of the stack.</param>
    ///  <param name="AStartIndex">The index into the array at which the copying begins.</param>
    ///  <remarks>This method assumes that <paramref name="AArray"/> has enough space to hold the contents of the stack.</remarks>
    ///  <exception cref="SysUtils|EArgumentOutOfRangeException"><paramref name="AStartIndex"/> is out of bounds.</exception>
    ///  <exception cref="Collections.Base|EArgumentOutOfSpaceException">The array is not long enough.</exception>
    procedure CopyTo(var AArray: array of T; const AStartIndex: NativeInt); overload; override;
    ///  <summary>Checks whether the stack is empty.</summary>
    ///  <returns><c>True</c> if the stack is empty; <c>False</c> otherwise.</returns>
    ///  <remarks>This method is the recommended way of detecting if the stack is empty.</remarks>
    function Empty(): Boolean; override;
    ///  <summary>Returns the biggest element.</summary>
    ///  <returns>An element from the stack considered to have the biggest value.</returns>
    ///  <exception cref="Collections.Base|ECollectionEmptyException">The stack is empty.</exception>
    function Max(): T; override;
    ///  <summary>Returns the smallest element.</summary>
    ///  <returns>An element from the stack considered to have the smallest value.</returns>
    ///  <exception cref="Collections.Base|ECollectionEmptyException">The stack is empty.</exception>
    function Min(): T; override;
    ///  <summary>Returns the first element.</summary>
    ///  <returns>The first element in the stack.</returns>
    ///  <exception cref="Collections.Base|ECollectionEmptyException">The stack is empty.</exception>
    function First(): T; override;
    ///  <summary>Returns the first element or a default if the stack is empty.</summary>
    ///  <param name="ADefault">The default value returned if the stack is empty.</param>
    ///  <returns>The first element in stack if the stack is not empty; otherwise <paramref name="ADefault"/> is returned.</returns>
    function FirstOrDefault(const ADefault: T): T; override;
    ///  <summary>Returns the last element.</summary>
    ///  <returns>The last element in the stack.</returns>
    ///  <exception cref="Collections.Base|ECollectionEmptyException">The stack is empty.</exception>
    function Last(): T; override;
    ///  <summary>Returns the last element or a default, if the stack is empty.</summary>
    ///  <param name="ADefault">The default value returned if the stack is empty.</param>
    ///  <returns>The last element in the stack if the stack is not empty; otherwise <paramref name="ADefault"/> is returned.</returns>
    function LastOrDefault(const ADefault: T): T; override;
    ///  <summary>Returns the single element stored in the stack.</summary>
    ///  <returns>The element in the stack.</returns>
    ///  <remarks>This method checks whether the stack contains just one element, in which case it is returned.</remarks>
    ///  <exception cref="Collections.Base|ECollectionEmptyException">The stack is empty.</exception>
    ///  <exception cref="Collections.Base|ECollectionNotOneException">There is more than one element in the stack.</exception>
    function Single(): T; override;
    ///  <summary>Returns the single element stored in the stack, or a default value.</summary>
    ///  <param name="ADefault">The default value returned if there are less or more elements in the stack.</param>
    ///  <returns>The element in the stack if the condition is satisfied; <paramref name="ADefault"/> is returned otherwise.</returns>
    ///  <remarks>This method checks if the stack contains just one element, in which case it is returned. Otherwise
    ///  the value in <paramref name="ADefault"/> is returned.</remarks>
    function SingleOrDefault(const ADefault: T): T; override;
    ///  <summary>Aggregates a value based on the stack's elements.</summary>
    ///  <param name="AAggregator">The aggregator method.</param>
    ///  <returns>A value that contains the stack's aggregated value.</returns>
    ///  <remarks>This method returns the first element if the stack only has one element. Otherwise,
    ///  <paramref name="AAggregator"/> is invoked for each two elements (first and second; then the result of the first two
    ///  and the third, and so on). The simplest example of aggregation is the "sum" operation, where you can obtain the sum of all
    ///  elements in the value.</remarks>
    ///  <exception cref="SysUtils|EArgumentNilException"><paramref name="AAggregator"/> is <c>nil</c>.</exception>
    ///  <exception cref="Collections.Base|ECollectionEmptyException">The stack is empty.</exception>
    function Aggregate(const AAggregator: TFunc<T, T, T>): T; override;
    ///  <summary>Aggregates a value based on the stack's elements.</summary>
    ///  <param name="AAggregator">The aggregator method.</param>
    ///  <param name="ADefault">The default value returned if the stack is empty.</param>
    ///  <returns>A value that contains the stack's aggregated value. If the stack is empty, <paramref name="ADefault"/> is returned.</returns>
    ///  <remarks>This method returns the first element if the stack only has one element. Otherwise,
    ///  <paramref name="AAggregator"/> is invoked for each two elements (first and second; then the result of the first two
    ///  and the third, and so on). The simplest example of aggregation is the "sum" operation, where you can obtain the sum of all
    ///  elements in the value.</remarks>
    ///  <exception cref="SysUtils|EArgumentNilException"><paramref name="AAggregator"/> is <c>nil</c>.</exception>
    function AggregateOrDefault(const AAggregator: TFunc<T, T, T>; const ADefault: T): T; override;
    ///  <summary>Returns the element at a given position.</summary>
    ///  <param name="AIndex">The index from which to return the element.</param>
    ///  <returns>The element at the specified position.</returns>
    ///  <exception cref="Collections.Base|ECollectionEmptyException">The stack is empty.</exception>
    ///  <exception cref="SysUtils|EArgumentOutOfRangeException"><paramref name="AIndex"/> is out of bounds.</exception>
    function ElementAt(const AIndex: NativeInt): T; override;
    ///  <summary>Returns the element at a given position.</summary>
    ///  <param name="AIndex">The index from which to return the element.</param>
    ///  <param name="ADefault">The default value returned if the stack is empty.</param>
    ///  <returns>The element at the specified position if the stack is not empty and the position is not out of bounds; otherwise
    ///  the value of <paramref name="ADefault"/> is returned.</returns>
    function ElementAtOrDefault(const AIndex: NativeInt; const ADefault: T): T; override;
    ///  <summary>Checks whether at least one element in the stack satisfies a given predicate.</summary>
    ///  <param name="APredicate">The predicate to check for each element.</param>
    ///  <returns><c>True</c> if at least one element satisfies a given predicate; <c>False</c> otherwise.</returns>
    ///  <remarks>This method traverses the whole stack and checks the value of the predicate for each element. This method
    ///  stops on the first element for which the predicate returns <c>True</c>. The logical equivalent of this operation is "OR".</remarks>
    ///  <exception cref="SysUtils|EArgumentNilException"><paramref name="APredicate"/> is <c>nil</c>.</exception>
    function Any(const APredicate: TFunc<T, Boolean>): Boolean; override;
    ///  <summary>Checks that all elements in the stack satisfy a given predicate.</summary>
    ///  <param name="APredicate">The predicate to check for each element.</param>
    ///  <returns><c>True</c> if all elements satisfy a given predicate; <c>False</c> otherwise.</returns>
    ///  <remarks>This method traverses the whole stack and checks the value of the predicate for each element. This method
    ///  stops on the first element for which the predicate returns <c>False</c>. The logical equivalent of this operation is "AND".</remarks>
    ///  <exception cref="SysUtils|EArgumentNilException"><paramref name="APredicate"/> is <c>nil</c>.</exception>
    function All(const APredicate: TFunc<T, Boolean>): Boolean; override;
    ///  <summary>Checks whether the elements in this stack are equal to the elements in another collection.</summary>
    ///  <param name="ACollection">The collection to compare to.</param>
    ///  <returns><c>True</c> if the collections are equal; <c>False</c> if the collections are different.</returns>
    ///  <remarks>This method checks that each element at position X in this stack is equal to an element at position X in
    ///  the provided collection. If the number of elements in both collections is different, then the collections are considered different.
    ///  Note that the comparison of elements is done using the rule set used by this stack. This means that comparing this collection
    ///  to another one might yeild a different result than comparing the other collection to this one.</remarks>
    ///  <exception cref="SysUtils|EArgumentNilException"><paramref name="ACollection"/> is <c>nil</c>.</exception>
    function EqualsTo(const ACollection: IEnumerable<T>): Boolean; override;
  end;
  ///  <summary>The generic <c>stack (LIFO)</c> collection designed to store objects.</summary>
  ///  <remarks>This type uses a linked list to store its objects.</remarks>
  TObjectLinkedStack<T: class> = class(TLinkedStack<T>)
  private
    FOwnsObjects: Boolean;
  protected
    ///  <summary>Frees the object that was removed from the collection.</summary>
    ///  <param name="AElement">The object that was removed from the collection.</param>
    procedure HandleElementRemoved(const AElement: T); override;
  public
    ///  <summary>Specifies whether this stack owns the objects stored in it.</summary>
    ///  <returns><c>True</c> if the stack owns its objects; <c>False</c> otherwise.</returns>
    ///  <remarks>This property specifies the way the stack controls the life-time of the stored objects.</remarks>
    property OwnsObjects: Boolean read FOwnsObjects write FOwnsObjects;
  end;
implementation
{ TStack<T> }
function TStack<T>.Aggregate(const AAggregator: TFunc<T, T, T>): T;
var
  I: NativeInt;
begin
  { Check arguments }
  if not Assigned(AAggregator) then
    ExceptionHelper.Throw_ArgumentNilError('AAggregator');
  if FLength = 0 then
    ExceptionHelper.Throw_CollectionEmptyError();
  { Select the first element as comparison base }
  Result := FArray[0];
  { Iterate over the last N - 1 elements }
  for I := 1 to FLength - 1 do
  begin
    { Aggregate a value }
    Result := AAggregator(Result, FArray[I]);
  end;
end;
function TStack<T>.AggregateOrDefault(const AAggregator: TFunc<T, T, T>; const ADefault: T): T;
var
  I: NativeInt;
begin
  { Check arguments }
  if not Assigned(AAggregator) then
    ExceptionHelper.Throw_ArgumentNilError('AAggregator');
  if FLength = 0 then
    Exit(ADefault);
  { Select the first element as comparison base }
  Result := FArray[0];
  { Iterate over the last N - 1 elements }
  for I := 1 to FLength - 1 do
  begin
    { Aggregate a value }
    Result := AAggregator(Result, FArray[I]);
  end;
end;
function TStack<T>.All(const APredicate: TFunc<T, Boolean>): Boolean;
var
  I: NativeInt;
begin
  if not Assigned(APredicate) then
    ExceptionHelper.Throw_ArgumentNilError('APredicate');
  if FLength > 0 then
    for I := 0 to FLength - 1 do
      if not APredicate(FArray[I]) then
        Exit(false);
  Result := true;
end;
function TStack<T>.Any(const APredicate: TFunc<T, Boolean>): Boolean;
var
  I: NativeInt;
begin
  if not Assigned(APredicate) then
    ExceptionHelper.Throw_ArgumentNilError('APredicate');
  if FLength > 0 then
    for I := 0 to FLength - 1 do
      if APredicate(FArray[I]) then
        Exit(true);
  Result := false;
end;
procedure TStack<T>.Clear;
var
  I: NativeInt;
begin
  for I := 0 to FLength - 1 do
    NotifyElementRemoved(FArray[I]);
  { Simply reset all to default }
  SetLength(FArray, CDefaultSize);
  FLength := 0;
  Inc(FVer);
end;
function TStack<T>.Contains(const AValue: T): Boolean;
var
  I: NativeInt;
begin
  { Defaults }
  Result := False;
  if (FLength = 0) then Exit;
  for I := 0 to FLength - 1 do
  begin
    if ElementsAreEqual(FArray[I], AValue) then
    begin
      Result := True;
      Exit;
    end;
  end;
end;
procedure TStack<T>.CopyTo(var AArray: array of T; const AStartIndex: NativeInt);
var
  I: NativeInt;
begin
  { Check for indexes }
  if (AStartIndex >= Length(AArray)) or (AStartIndex < 0) then
    ExceptionHelper.Throw_ArgumentOutOfRangeError('AStartIndex');
  if (Length(AArray) - AStartIndex) < FLength then
     ExceptionHelper.Throw_ArgumentOutOfSpaceError('AArray');
  { Copy all elements safely }
  for I := 0 to FLength - 1 do
    AArray[AStartIndex + I] := FArray[I];
end;
constructor TStack<T>.Create(const ARules: TRules<T>; const ACollection: IEnumerable<T>);
var
  LValue: T;
begin
  { Call upper constructor }
  Create(ARules, CDefaultSize);
  { Initialize instance }
  if not Assigned(ACollection) then
     ExceptionHelper.Throw_ArgumentNilError('ACollection');
  { Try to copy the given Enumerable }
  for LValue in ACollection do
    Push(LValue);
end;
constructor TStack<T>.Create;
begin
  Create(TRules<T>.Default);
end;
constructor TStack<T>.Create(const AInitialCapacity: NativeInt);
begin
  Create(TRules<T>.Default, AInitialCapacity);
end;
constructor TStack<T>.Create(const ACollection: IEnumerable<T>);
begin
  Create(TRules<T>.Default, ACollection);
end;
constructor TStack<T>.Create(const ARules: TRules<T>);
begin
  { Call upper constructor }
  Create(ARules, CDefaultSize);
end;
constructor TStack<T>.Create(const ARules: TRules<T>; const AInitialCapacity: NativeInt);
begin
  { Initialize instance }
  inherited Create(ARules);
  FLength := 0;
  FVer := 0;
  SetLength(FArray, AInitialCapacity);
end;
destructor TStack<T>.Destroy;
begin
  { Some clean-up }
  Clear();
  inherited;
end;
function TStack<T>.ElementAt(const AIndex: NativeInt): T;
begin
  if (AIndex >= FLength) or (AIndex < 0) then
    ExceptionHelper.Throw_ArgumentOutOfRangeError('AIndex');
  Result := FArray[AIndex];
end;
function TStack<T>.ElementAtOrDefault(const AIndex: NativeInt; const ADefault: T): T;
begin
  { Check range }
  if (AIndex >= FLength) then
     Result := ADefault
  else
     Result := FArray[AIndex];
end;
function TStack<T>.Empty: Boolean;
begin
  Result := (FLength = 0);
end;
function TStack<T>.EqualsTo(const ACollection: IEnumerable<T>): Boolean;
var
  LValue: T;
  I: NativeInt;
begin
  I := 0;
  for LValue in ACollection do
  begin
    if I >= FLength then
      Exit(false);
    if not ElementsAreEqual(FArray[I], LValue) then
      Exit(false);
    Inc(I);
  end;
  if I < FLength then
    Exit(false);
  Result := true;
end;
function TStack<T>.First: T;
begin
  { Check length }
  if FLength = 0 then
    ExceptionHelper.Throw_CollectionEmptyError();
  Result := FArray[0];
end;
function TStack<T>.FirstOrDefault(const ADefault: T): T;
begin
  { Check length }
  if FLength = 0 then
    Result := ADefault
  else
    Result := FArray[0];
end;
function TStack<T>.GetCapacity: NativeInt;
begin
  Result := Length(FArray);
end;
function TStack<T>.GetCount: NativeInt;
begin
  { Use the variable }
  Result := FLength;
end;
function TStack<T>.GetEnumerator: IEnumerator<T>;
begin
  Result := TEnumerator.Create(Self);
end;
procedure TStack<T>.Grow;
var
  LNewCapacity: NativeInt;
begin
  if Capacity = 0 then
    LNewCapacity := CDefaultSize
  else
    LNewCapacity := Capacity * 2;
  SetLength(FArray, LNewCapacity);
end;
function TStack<T>.Last: T;
begin
  { Check length }
  if FLength = 0 then
    ExceptionHelper.Throw_CollectionEmptyError();
  Result := FArray[FLength - 1];
end;
function TStack<T>.LastOrDefault(const ADefault: T): T;
begin
  { Check length }
  if FLength = 0 then
    Result := ADefault
  else
    Result := FArray[FLength - 1];
end;
function TStack<T>.Max: T;
var
  I: NativeInt;
begin
  { Check length }
  if FLength = 0 then
    ExceptionHelper.Throw_CollectionEmptyError();
  { Default one }
  Result := FArray[0];
  for I := 1 to FLength - 1 do
    if CompareElements(FArray[I], Result) > 0 then
      Result := FArray[I];
end;
function TStack<T>.Min: T;
var
  I: NativeInt;
begin
  { Check length }
  if FLength = 0 then
    ExceptionHelper.Throw_CollectionEmptyError();
  { Default one }
  Result := FArray[0];
  for I := 1 to FLength - 1 do
    if CompareElements(FArray[I], Result) < 0 then
      Result := FArray[I];
end;
function TStack<T>.Peek: T;
begin
  if FLength > 0 then
     Result := FArray[FLength - 1]
  else
     ExceptionHelper.Throw_CollectionEmptyError();
end;
function TStack<T>.Pop: T;
begin
  if FLength > 0 then
  begin
    Result := FArray[FLength - 1];
    Dec(FLength);
    Inc(FVer);
  end else
    ExceptionHelper.Throw_CollectionEmptyError();
end;
procedure TStack<T>.Push(const AValue: T);
begin
  { Ensure enough capacity }
  if (FLength >= Capacity) then
    Grow();
  { Add the element to the stack and increase the index }
  FArray[FLength] := AValue;
  Inc(FLength);
  Inc(FVer);
end;
procedure TStack<T>.Remove(const AValue: T);
var
  I, LFoundIndex: NativeInt;
begin
  { Defaults }
  if (FLength = 0) then Exit;
  LFoundIndex := -1;
  for I := 0 to FLength - 1 do
  begin
    if ElementsAreEqual(FArray[I], AValue) then
    begin
      LFoundIndex := I;
      Break;
    end;
  end;
  if (LFoundIndex > -1) then
  begin
    { Move the list }
    if FLength > 1 then
      for I := LFoundIndex to FLength - 2 do
        FArray[I] := FArray[I + 1];
    Dec(FLength);
    Inc(FVer);
  end;
end;
procedure TStack<T>.Shrink;
begin
  { Cut the capacity if required }
  if FLength < Capacity then
    SetLength(FArray, FLength);
end;
function TStack<T>.Single: T;
begin
  { Check length }
  if FLength = 0 then
    ExceptionHelper.Throw_CollectionEmptyError()
  else if FLength > 1 then
    ExceptionHelper.Throw_CollectionHasMoreThanOneElement()
  else
    Result := FArray[0];
end;
function TStack<T>.SingleOrDefault(const ADefault: T): T;
begin
  { Check length }
  if FLength = 0 then
    Result := ADefault
  else if FLength > 1 then
    ExceptionHelper.Throw_CollectionHasMoreThanOneElement()
  else
    Result := FArray[0];
end;
constructor TStack<T>.Create(const AArray: array of T);
begin
  Create(TRules<T>.Default, AArray);
end;
constructor TStack<T>.Create(const ARules: TRules<T>; const AArray: array of T);
var
  I: NativeInt;
begin
  { Call upper constructor }
  Create(ARules, CDefaultSize);
  { Copy array }
  for I := 0 to Length(AArray) - 1 do
  begin
    Push(AArray[I]);
  end;
end;
{ TStack<T>.TEnumerator }
constructor TStack<T>.TEnumerator.Create(const AStack: TStack<T>);
begin
  { Initialize }
  FStack := AStack;
  KeepObjectAlive(FStack);
  FCurrentIndex := 0;
  FVer := AStack.FVer;
end;
destructor TStack<T>.TEnumerator.Destroy;
begin
  ReleaseObject(FStack);
  inherited;
end;
function TStack<T>.TEnumerator.GetCurrent: T;
begin
  if FVer <> FStack.FVer then
     ExceptionHelper.Throw_CollectionChangedError();
  if FCurrentIndex > 0 then
    Result := FStack.FArray[FCurrentIndex - 1]
  else
    Result := default(T);
end;
function TStack<T>.TEnumerator.MoveNext: Boolean;
begin
  if FVer <> FStack.FVer then
     ExceptionHelper.Throw_CollectionChangedError();
  Result := FCurrentIndex < FStack.FLength;
  Inc(FCurrentIndex);
end;
{ TObjectStack<T> }
procedure TObjectStack<T>.HandleElementRemoved(const AElement: T);
begin
  if FOwnsObjects then
    TObject(AElement).Free;
end;
{ TLinkedStack<T> }
function TLinkedStack<T>.Aggregate(const AAggregator: TFunc<T, T, T>): T;
var
  LCurrent: PEntry;
begin
  { Check arguments }
  if not Assigned(AAggregator) then
    ExceptionHelper.Throw_ArgumentNilError('AAggregator');
  if not Assigned(FFirst) then
    ExceptionHelper.Throw_CollectionEmptyError();
  { Select the first element as comparison base }
  Result := FFirst^.FValue;
  LCurrent := FFirst^.FNext;
  { Iterate over the last N - 1 elements }
  while Assigned(LCurrent) do
  begin
    Result := AAggregator(Result, LCurrent^.FValue);
    LCurrent := LCurrent^.FNext;
  end;
end;
function TLinkedStack<T>.AggregateOrDefault(const AAggregator: TFunc<T, T, T>; const ADefault: T): T;
var
  LCurrent: PEntry;
begin
  { Check arguments }
  if not Assigned(AAggregator) then
    ExceptionHelper.Throw_ArgumentNilError('AAggregator');
  { Select the first element as comparison base }
  if not Assigned(FFirst) then
    Exit(ADefault);
  Result := FFirst^.FValue;
  LCurrent := FFirst^.FNext;
  { Iterate over the last N - 1 elements }
  while Assigned(LCurrent) do
  begin
    Result := AAggregator(Result, LCurrent^.FValue);
    LCurrent := LCurrent^.FNext;
  end;
end;
function TLinkedStack<T>.All(const APredicate: TFunc<T, Boolean>): Boolean;
var
  LCurrent: PEntry;
begin
  if not Assigned(APredicate) then
    ExceptionHelper.Throw_ArgumentNilError('APredicate');
  LCurrent := FFirst;
  while Assigned(LCurrent) do
  begin
    if not APredicate(LCurrent^.FValue) then
      Exit(false);
    LCurrent := LCurrent^.FNext;
  end;
  Result := true;
end;
function TLinkedStack<T>.Any(const APredicate: TFunc<T, Boolean>): Boolean;
var
  LCurrent: PEntry;
begin
  if not Assigned(APredicate) then
    ExceptionHelper.Throw_ArgumentNilError('APredicate');
  LCurrent := FFirst;
  while Assigned(LCurrent) do
  begin
    if APredicate(LCurrent^.FValue) then
      Exit(true);
    LCurrent := LCurrent^.FNext;
  end;
  Result := false;
end;
procedure TLinkedStack<T>.Clear;
var
  LCurrent, LNext: PEntry;
begin
  LCurrent := FFirst;
  while Assigned(LCurrent) do
  begin
    NotifyElementRemoved(LCurrent^.FValue);
    { Release}
    LNext := LCurrent^.FNext;
    ReleaseEntry(LCurrent);
    LCurrent := LNext;
  end;
  FFirst := nil;
  FLast := nil;
  FCount := 0;
  Inc(FVer);
end;
function TLinkedStack<T>.Contains(const AValue: T): Boolean;
var
  LCurrent: PEntry;
begin
  LCurrent := FFirst;
  Result := False;
  while Assigned(LCurrent) do
  begin
    if ElementsAreEqual(AValue, LCurrent^.FValue) then
      Exit(True);
    LCurrent := LCurrent^.FNext;
  end;
end;
procedure TLinkedStack<T>.CopyTo(var AArray: array of T; const AStartIndex: NativeInt);
var
  X: NativeInt;
  LCurrent: PEntry;
begin
  { Check for indexes }
  if (AStartIndex >= Length(AArray)) or (AStartIndex < 0) then
    ExceptionHelper.Throw_ArgumentOutOfRangeError('AStartIndex');
  if (Length(AArray) - AStartIndex) < FCount then
     ExceptionHelper.Throw_ArgumentOutOfSpaceError('AArray');
  X := AStartIndex;
  LCurrent := FFirst;
  while Assigned(LCurrent) do
  begin
    AArray[X] := LCurrent^.FValue;
    Inc(X);
    LCurrent := LCurrent^.FNext;
  end;
end;
constructor TLinkedStack<T>.Create(const ARules: TRules<T>; const ACollection: IEnumerable<T>);
var
  LValue: T;
begin
  { Call upper constructor }
  Create(ARules);
  { Initialize instance }
  if not Assigned(ACollection) then
     ExceptionHelper.Throw_ArgumentNilError('ACollection');
  { Try to copy the given Enumerable }
  for LValue in ACollection do
    Push(LValue);
end;
constructor TLinkedStack<T>.Create;
begin
  Create(TRules<T>.Default);
end;
constructor TLinkedStack<T>.Create(const ACollection: IEnumerable<T>);
begin
  Create(TRules<T>.Default, ACollection);
end;
constructor TLinkedStack<T>.Create(const ARules: TRules<T>);
begin
  { Initialize instance }
  inherited Create(ARules);
  FFirst := nil;
  FLast := nil;
  FFirstFree := nil;
  FFreeCount := 0;
  FCount := 0;
  FVer := 0;
end;
destructor TLinkedStack<T>.Destroy;
var
  LNext: PEntry;
begin
  { Some cleanup }
  Clear();
  { Clear the cached entries too }
  if FFreeCount > 0 then
    while Assigned(FFirstFree) do
    begin
      LNext := FFirstFree^.FNext;
      { Delphi doesn finalize this }
      FFirstFree^.FValue := default(T);
      FreeMem(FFirstFree);
      FFirstFree := LNext;
    end;
  inherited;
end;
function TLinkedStack<T>.ElementAt(const AIndex: NativeInt): T;
var
  LCurrent: PEntry;
  LIndex: NativeInt;
begin
  { Check range }
  if (AIndex >= FCount) or (AIndex < 0) then
    ExceptionHelper.Throw_ArgumentOutOfRangeError('AIndex');
  LCurrent := FFirst;
  LIndex := 0;
  while Assigned(LCurrent) do
  begin
    if LIndex = AIndex then
      Exit(LCurrent^.FValue);
    LCurrent := LCurrent^.FNext;
    Inc(LIndex);
  end;
  { Should never happen }
  ExceptionHelper.Throw_ArgumentOutOfRangeError('AIndex');
end;
function TLinkedStack<T>.ElementAtOrDefault(const AIndex: NativeInt; const ADefault: T): T;
var
  LCurrent: PEntry;
  LIndex: NativeInt;
begin
  { Check range }
  if AIndex < 0 then
    ExceptionHelper.Throw_ArgumentOutOfRangeError('AIndex');
  if AIndex >= FCount then
    Exit(ADefault);
  LCurrent := FFirst;
  LIndex := 0;
  while Assigned(LCurrent) do
  begin
    if LIndex = AIndex then
      Exit(LCurrent^.FValue);
    LCurrent := LCurrent^.FNext;
    Inc(LIndex);
  end;
  { Should never happen }
  Result := ADefault;
end;
function TLinkedStack<T>.Empty: Boolean;
begin
  { Call the one from the list }
  Result := not Assigned(FFirst);
end;
function TLinkedStack<T>.EqualsTo(const ACollection: IEnumerable<T>): Boolean;
var
  LValue: T;
  LCurrent: PEntry;
begin
  LCurrent := FFirst;
  for LValue in ACollection do
  begin
    if not Assigned(LCurrent) then
      Exit(false);
    if not ElementsAreEqual(LCurrent^.FValue, LValue) then
      Exit(false);
    LCurrent := LCurrent^.FNext;
  end;
  Result := not Assigned(LCurrent);
end;
function TLinkedStack<T>.First: T;
begin
  if not Assigned(FFirst) then
    ExceptionHelper.Throw_CollectionEmptyError();
  Result := FFirst^.FValue;
end;
function TLinkedStack<T>.FirstOrDefault(const ADefault: T): T;
begin
  if not Assigned(FFirst) then
    Result := ADefault
  else
    Result := FFirst^.FValue;
end;
function TLinkedStack<T>.GetCount: NativeInt;
begin
  { Use the variable }
  Result := FCount;
end;
function TLinkedStack<T>.GetEnumerator: IEnumerator<T>;
begin
  Result := TEnumerator.Create(Self);
end;
function TLinkedStack<T>.Last: T;
begin
  if not Assigned(FLast) then
    ExceptionHelper.Throw_CollectionEmptyError();
  Result := FLast^.FValue;
end;
function TLinkedStack<T>.LastOrDefault(const ADefault: T): T;
begin
  if not Assigned(FLast) then
    Result := ADefault
  else
    Result := FLast^.FValue;
end;
function TLinkedStack<T>.Max: T;
var
  LCurrent: PEntry;
begin
  if not Assigned(FLast) then
    ExceptionHelper.Throw_CollectionEmptyError();
  Result := FFirst^.FValue;
  LCurrent := FFirst^.FNext;
  while Assigned(LCurrent) do
  begin
    if CompareElements(LCurrent^.FValue, Result) > 0 then
      Result := LCurrent^.FValue;
    LCurrent := LCurrent^.FNext;
  end;
end;
function TLinkedStack<T>.Min: T;
var
  LCurrent: PEntry;
begin
  if not Assigned(FLast) then
    ExceptionHelper.Throw_CollectionEmptyError();
  Result := FFirst^.FValue;
  LCurrent := FFirst^.FNext;
  while Assigned(LCurrent) do
  begin
    if CompareElements(LCurrent^.FValue, Result) < 0 then
      Result := LCurrent^.FValue;
    LCurrent := LCurrent^.FNext;
  end;
end;
function TLinkedStack<T>.NeedEntry(const AValue: T): PEntry;
begin
  if FFreeCount > 0 then
  begin
    Result := FFirstFree;
    FFirstFree := FFirstFree^.FNext;
    Dec(FFreeCount);
  end else
    Result := AllocMem(SizeOf(TEntry));
  { Initialize the node }
  Result^.FValue := AValue;
end;
function TLinkedStack<T>.Peek: T;
begin
  if not Assigned(FLast) then
    ExceptionHelper.Throw_CollectionEmptyError();
  Result := FLast^.FValue;
end;
function TLinkedStack<T>.Pop: T;
var
  LEntry: PEntry;
begin
  if not Assigned(FLast) then
    ExceptionHelper.Throw_CollectionEmptyError();
  LEntry := FLast;
  Result := LEntry^.FValue;
  FLast := LEntry^.FPrev;
  if FFirst = LEntry then
    FFirst := FLast;
  ReleaseEntry(LEntry);
  Inc(FVer);
  Dec(FCount);
end;
procedure TLinkedStack<T>.Push(const AValue: T);
var
  LNew: PEntry;
begin
  LNew := NeedEntry(AValue);
  LNew^.FPrev := FLast;
  LNew^.FNext := nil;
  if Assigned(FLast) then
    FLast^.FNext := LNew;
  FLast := LNew;
  if not Assigned(FFirst) then
    FFirst := LNew;
  Inc(FVer);
  Inc(FCount);
end;
procedure TLinkedStack<T>.ReleaseEntry(const AEntry: PEntry);
begin
  if FFreeCount = CDefaultSize then
  begin
    { Delphi doesn finalize this }
    AEntry^.FValue := default(T);
    FreeMem(AEntry);
  end else begin
    { Place the entry into the cache }
    AEntry^.FNext := FFirstFree;
    FFirstFree := AEntry;
    Inc(FFreeCount);
  end;
end;
procedure TLinkedStack<T>.Remove(const AValue: T);
var
  LCurrent: PEntry;
begin
  LCurrent := FFirst;
  while Assigned(LCurrent) do
  begin
    if ElementsAreEqual(AValue, LCurrent^.FValue) then
    begin
      { Remove the node }
      if Assigned(LCurrent^.FPrev) then
        LCurrent^.FPrev^.FNext := LCurrent^.FNext;
      if Assigned(LCurrent^.FNext) then
        LCurrent^.FNext^.FPrev := LCurrent^.FPrev;
      if FFirst = LCurrent then
        FFirst := LCurrent^.FNext;
      if FLast = LCurrent then
        FLast := LCurrent^.FPrev;
      ReleaseEntry(LCurrent);
      Inc(FVer);
      Dec(FCount);
      Exit;
    end;
    LCurrent := LCurrent^.FNext;
  end;
end;
function TLinkedStack<T>.Single: T;
begin
  { Check length }
  if not Assigned(FFirst) then
    ExceptionHelper.Throw_CollectionEmptyError()
  else if FFirst <> FLast then
    ExceptionHelper.Throw_CollectionHasMoreThanOneElement()
  else
    Result := FFirst^.FValue;
end;
function TLinkedStack<T>.SingleOrDefault(const ADefault: T): T;
begin
  { Check length }
  if not Assigned(FFirst) then
    Result := ADefault
  else if FFirst <> FLast then
    ExceptionHelper.Throw_CollectionHasMoreThanOneElement()
  else
    Result := FFirst^.FValue;
end;
constructor TLinkedStack<T>.Create(const AArray: array of T);
begin
  Create(TRules<T>.Default, AArray);
end;
constructor TLinkedStack<T>.Create(const ARules: TRules<T>; const AArray: array of T);
var
  I: NativeInt;
begin
  { Call upper constructor }
  Create(ARules);
  { Copy array }
  for I := 0 to Length(AArray) - 1 do
  begin
    Push(AArray[I]);
  end;
end;
{ TLinkedStack<T>.TEnumerator }
constructor TLinkedStack<T>.TEnumerator.Create(const AStack: TLinkedStack<T>);
begin
  FStack := AStack;
  KeepObjectAlive(FStack);
  FVer := AStack.FVer;
  FCurrentEntry := AStack.FFirst;
end;
destructor TLinkedStack<T>.TEnumerator.Destroy;
begin
  ReleaseObject(FStack);
  inherited;
end;
function TLinkedStack<T>.TEnumerator.GetCurrent: T;
begin
  if FVer <> FStack.FVer then
    ExceptionHelper.Throw_CollectionChangedError();
  Result := FValue;
end;
function TLinkedStack<T>.TEnumerator.MoveNext: Boolean;
begin
  if FVer <> FStack.FVer then
    ExceptionHelper.Throw_CollectionChangedError();
  Result := Assigned(FCurrentEntry);
  if Result then
  begin
    FValue := FCurrentEntry^.FValue;
    FCurrentEntry := FCurrentEntry^.FNext;
  end;
end;
{ TObjectLinkedStack<T> }
procedure TObjectLinkedStack<T>.HandleElementRemoved(const AElement: T);
begin
  if FOwnsObjects then
    TObject(AElement).Free;
end;
end.