unit ASObjects;

{ **************************************************************************** }
{ Rejbrand AlgoSim Kernel object data types                                    }
{ Copyright © 2017-2020 Andreas Rejbrand                                       }
{ https://english.rejbrand.se/                                                 }
{ **************************************************************************** }

{$WARN SYMBOL_PLATFORM OFF}
{$WARN DUPLICATE_CTOR_DTOR OFF}

interface

uses
  ASStrFcns, SysUtils, Types, Classes, ASNum, ASPixmap, ASSounds, ASKernelDefs,
  Generics.Defaults, Generics.Collections, ASTable, ASColors, Math, UITypes,
  GenHelpers;

type
  TAlgosimObjectClassFlag = (asoObjectContainer, asoValueContainer,
    asoOrderedContainer, asoPlanarContainer, asoComplex);
  TAlgosimObjectClassFlags = set of TAlgosimObjectClassFlag;

  TAlgosimObjectClassData = record
    ClassTypeName: string;
    ClassFlags: TAlgosimObjectClassFlags;
    ExportExts: TArray<string>;
    constructor Create(const AClassTypeName: string;
      AClassFlags: TAlgosimObjectClassFlags = [];
      const AExportExts: string = '');
  end;

  AlgosimObjectAttribute = class(TCustomAttribute)
    Data: TAlgosimObjectClassData;
    constructor Create(const AClassTypeName: string;
      AClassFlags: TAlgosimObjectClassFlags = [];
      const AExportExts: string = '');
  end;

  TAlgosimObject = class;
  TAlgosimNumericEntity = class;
  TAlgosimNumericArray = class;
  TAlgosimNumber = class;
  TAlgosimArray = class;
  TAlgosimVector = class;
  TAlgosimMatrix = class;
  TAlgosimSet = class;
  TAlgosimBinaryData = class;
  TAlgosimStructure = class;
  TAlgosimStructureType = class;

  TAlgosimObjectClass = class of TAlgosimObject;
  TAlgosimNumericEntityClass = class of TAlgosimNumericEntity;
  TAlgosimNumericArrayClass = class of TAlgosimNumericArray;
  TAlgosimNumberClass = class of TAlgosimNumber;
  TAlgosimArrayClass = class of TAlgosimArray;
  TAlgosimVectorClass = class of TAlgosimVector;
  TAlgosimMatrixClass = class of TAlgosimMatrix;
  TAlgosimSetClass = class of TAlgosimSet;
  TAlgosimBinaryDataClass = class of TAlgosimBinaryData;
  TAlgosimStructureClass = class of TAlgosimStructure;
  TAlgosimStructureTypeClass = class of TAlgosimStructureType;

  /// <summary>An AlgoSim object predicate.</summary>
  /// <param name="AObject">The object. The function MUST consider it read-only
  ///  and will NOT gain ownership of it.</param>
  TASOPredicate = reference to function(AObject: TAlgosimObject): Boolean;

  /// <summary>An AlgoSim object function.</summary>
  /// <param name="AObject">The object. The function MUST consider it read-only
  ///  and will NOT gain ownership of it.</param>
  /// <returns>A new object. The caller gains ownership of the object.</returns>
  TASOFunction = reference to function(AObject: TAlgosimObject): TAlgosimObject;

  /// <summary>An AlgoSim object accumulator.</summary>
  /// <param name="CurVal">The accumulated value so far. The function MUST
  ///  consider it read-only and will NOT gain ownership of it.</param>
  /// <param name="NewVal">The new value to add to the accumulated value. The
  ///  function MUST consider it read-only and will NOT gain ownership of it.
  ///  </param>
  /// <returns>The new accumulated value. The caller gains ownership of the
  ///  object.</returns>
  TASOAccumulator = reference to function(CurVal, NewVal: TAlgosimObject): TAlgosimObject;

  TSubscriptKind = (skIndexObject, skIdentifier, skFirst, skLast, skRandom,
    skRowIndex, skColIndex, skMainDiagonal, skSuperdiagonal, skSubdiagonal,
    skAntidiagonal);

  TSubscript = record
    Kind: TSubscriptKind;
    Ident: string;
    Obj: TAlgosimObject;
    Ordinal: Integer;
    constructor Create(const AIdent: string); overload;
    constructor Create(const AIndex: TAlgosimObject); overload;
    constructor Create(const AKind: TSubscriptKind; const AOrd: Integer = 0); overload;
    function ToString: string;
  end;

  TSortClass = (
    SORTCLASS_NULL,
    SORTCLASS_BOOLEAN,
    SORTCLASS_NUMBER,
    SORTCLASS_TEXT,
    SORTCLASS_VECTOR,
    SORTCLASS_MATRIX,
    SORTCLASS_TABLE,
    SORTCLASS_COLOR,
    SORTCLASS_PIXMAP,
    SORTCLASS_SOUND,
    SORTCLASS_ARRAY,
    SORTCLASS_SET,
    SORTCLASS_STRUCTURE,
    SORTCLASS_BINARYDATA,
    SORTCLASS_SUCCESS,
    SORTCLASS_CONTROL);

  PAlgosimObject = ^TAlgosimObject;

  [AlgosimObject('object')]
  TAlgosimObject = class abstract
  public const
    EllipsisSymbol = #$FDE0 {noncharacter};
    EllipsisSymbol_HORIZONTAL_ELLIPSIS = #$2026;
    EllipsisSymbol_VERTICAL_ELLIPSIS = #$22EE;
    EllipsisSymbol_MIDLINE_HORIZONTAL_ELLIPSIS = #$22EF;
    EllipsisSymbol_DOWN_RIGHT_DIAGONAL_ELLIPSIS = #$22F1;
  private

    /// <summary>Returns the sum of all numbers contanied in the object. Raises
    ///  an exception if the object contains other kinds of objects than
    ///  numbers. The empty sum is 0.</summary>
    /// <returns>The desired sum as an AlgoSim object. The caller gains
    ///  ownership of this object.</returns>
    /// <exception cref="EAlgosimObjectException">Raised if the operation is not
    ///  supported.</exception>
    function N_NumberSum: TAlgosimNumber;

    /// <summary>Returns the sum of all vectors contanied in the object. Raises
    ///  an exception if the object contains other kinds of objects than
    ///  vectors, if the vectors are of different dimensions, or if the object
    //   contains no vectors.</summary>
    /// <returns>The desired sum as an AlgoSim object. The caller gains
    ///  ownership of this object.</returns>
    /// <exception cref="EAlgosimObjectException">Raised if the operation is not
    ///  supported.</exception>
    function N_VectorSum: TAlgosimVector;

    /// <summary>Returns the sum of all matrices contanied in the object. Raises
    ///  an exception if the object contains other kinds of objects than
    ///  matrices, if the matrices are of different sizes, or if the object
    //   contains no matrices.</summary>
    /// <returns>The desired sum as an AlgoSim object. The caller gains
    ///  ownership of this object.</returns>
    /// <exception cref="EAlgosimObjectException">Raised if the operation is not
    ///  supported.</exception>
    function N_MatrixSum: TAlgosimMatrix;

    /// <summary>Returns the product of all numbers contanied in the object.
    ///  Raises an exception if the object contains other kinds of objects than
    ///  numbers. The empty product is 1.</summary>
    /// <returns>The desired product as an AlgoSim object. The caller gains
    ///  ownership of this object.</returns>
    /// <exception cref="EAlgosimObjectException">Raised if the operation is not
    ///  supported.</exception>
    function N_NumberProduct: TAlgosimNumber;

    /// <summary>Returns the product of all matrices contanied in the object.
    ///  Raises an exception if the object contains other kinds of objects than
    ///  matrices, if the matrices are not square and of the same size, or if
    ///  the object contains no matrices.</summary>
    /// <returns>The desired product as an AlgoSim object. The caller gains
    ///  ownership of this object.</returns>
    /// <exception cref="EAlgosimObjectException">Raised if the operation is not
    ///  supported.</exception>
    function N_MatrixProduct: TAlgosimMatrix;

  protected

    procedure NoCopyConstr(AFrom: TAlgosimObject);
    function GetTypeName: string; virtual;
    function GetElement(Index: Integer): TAlgosimObject; virtual;
    function GetElementCount: Integer; virtual;
    procedure SetElement(Index: Integer; AValue: TAlgosimObject); virtual;
    function GetCapacity: Integer; virtual;
    procedure SetCapacity(const Value: Integer); virtual;
    function GetValue(Index: Integer): TAlgosimObject; virtual;
    function GetValueCount: Integer; virtual;
    procedure SetValue(Index: Integer; AValue: TAlgosimObject); virtual;
    function GetPlanarExtent: TSize; virtual;
    procedure SetPlanarExtent(const Value: TSize); virtual;
    function GetValueFromPoint(const APoint: TPoint): TAlgosimObject; virtual;
    procedure SetValueFromPoint(const APoint: TPoint; AValue: TAlgosimObject); virtual;
    procedure AddNumbersToArray(AArray: TAlgosimArray; ALevel: Integer = 0); virtual;
    procedure AddMembersToArray(AArray: TAlgosimArray;
      APredicate: TASOPredicate; ALevel: Integer); overload;
    procedure AddMembersToArray(AArray: TAlgosimArray;
      APredicate: TASOPredicate); overload;
    function GetIsComplex: Boolean; virtual;
    function GetMaxLen: Integer; virtual;
    procedure SetMaxLen(AValue: Integer); virtual;
    class function GetPhysIndex0(AIndex, ALength: Integer): Integer; static;
    class function GetPhysIndex1(AIndex, ALength: Integer): Integer; static;
    class function GetPhysIndex2D0(const AIndex: TPoint; const ASize: TSize): TPoint; static;
    function GetMemorySize: UInt64; virtual;

  public

    constructor Create; overload; virtual;

    function ASOClassType: TAlgosimObjectClass; inline;

    class var _ASOClassData: TDictionary<TAlgosimObjectClass, TAlgosimObjectClassData>;
    class constructor ClassCreate;
    class destructor ClassDestroy;

    class function EqualityComparison(const Left, Right: TAlgosimObject): Boolean; static; inline;
    class function Hasher(const Value: TAlgosimObject): Integer; static; inline;
    class function Comparer: IComparer<TAlgosimObject>; static;
    class function ValEqualityComparer: IEqualityComparer<TAlgosimObject>; static;
    class function RefEqualityComparer: IEqualityComparer<TAlgosimObject>; static;

    /// <summary>The copy constructor.</summary>
    constructor Create(AObject: TAlgosimObject); overload; virtual;

    class function ClassData: TAlgosimObjectClassData; inline;
    class function ClassFlags: TAlgosimObjectClassFlags; inline;

    /// <summary>A rough estimate of the memory size of the object, or 0 if not
    ///  implemented for this type of object.</summary>
    property MemorySize: UInt64 read GetMemorySize;

    /// <summary>Returns a textual representation of the data, if reasonable.
    ///  If so, the returned string is as accurate as possible. If not, a
    ///  textual summary of the contents is returned.</summary>
    function ToString: string; override;

    function ToInputString: string; virtual;

    function ToPreviewString: string; virtual;

    function ToSpeech: string; virtual;

    /// <summary>Returns a pretty-printed single-line representation of the
    ///  data.</summary>
    function GetAsSingleLineText(const AOptions: TFormatOptions): string; virtual;

    /// <summary>Returns a pretty-printed multi-line representation of the
    ///  data.</summary>
    function GetAsMultilineText(const AOptions: TFormatOptions): string; virtual;

    /// <summary>Returns a pretty-printed, possibly multi-line representation of the
    ///  data, with optional annotations helping the user to understand the
    ///  value.</summary>
    function ExplainedOutput(const AOptions: TFormatOptions): string; virtual;

    /// <summary>Invokes the object, if applicable.</summary>
    procedure Invoke; virtual;

    /// <summary>Saves the data contained in the object in a file.</summary>
    /// <param name="AFileName">The file name of the created file.</param>
    procedure SaveToFile(const AFileName: string); overload; virtual;

    procedure SaveToFile(const AFileName: string; AOptions: TAlgosimStructure;
      AContext: TObject); overload; virtual;

    procedure SaveToFile(ADlgOwner: TComponent;
      const ADefFileName: string = ''); overload; virtual;

    /// <summary>Loads the object from a file.</summary>
    class function LoadFromFile(const AFileName: string;
      AEncoding: TEncoding = nil; const AParams: string = ''): TAlgosimObject; virtual;

    /// <summary>Copies the data contained in the object to the clipboard.</summary>
    procedure CopyToClipboard; virtual;

    /// <summary>The name of the AlgoSim data type of the object. Notice that
    ///  two objects of the same Delphi class might have different AlgoSim
    ///  data type names (e.g., typed structures).</summary>
    property TypeName: string read GetTypeName;

    /// <summary>The name of the AlgoSim data type of the object. This name
    ///  is potentially less informative than the <c>TypeName</c>; in particular,
    ///  two AlgoSim objects of the same Delphi class have the same
    ///  <c>ClassTypeName</c>.</summary>
    class function ClassTypeName: string; inline;

    /// <summary>Returns a clone of the current object, that is, a new AlgoSim
    ///  object of the same type with the same content as the current object.
    ///  </summary>
    /// <returns>A clone of the current object. The caller gains ownership of
    ///  this new object.</returns>
    function Clone: TAlgosimObject;

    /// <summary>Returns a reference to a subscripted object.</summary>
    /// <param name="ASubscript">The subscript to look up. Its objects are
    ///  not modified by the function.</param>
    /// <returns>If successful, a reference to the subscripted object. The
    ///  caller does not gain ownership of it.</returns>
    /// <exception cref="EAlgosimObjectException">Raised if the subscripted
    ///  object doesn't exist or can only be accessed by value.</exception>
    function GetSubscriptedRef(ASubscript: TSubscript): TAlgosimObject;

    /// <summary>Tries to return a reference to a subscripted object.</summary>
    /// <param name="ASubscript">The subscript to look up. Its objects are
    ///  not modified by the function.</param>
    /// <param name="AValue">If successful, becomes a reference to the
    ///  subscripted object. The caller does not gain ownership of it.</param>
    /// <returns>True iff a reference to the subscripted object could be
    ///  obtained.</returns>
    function TryGetSubscriptedRef(ASubscript: TSubscript;
      out AValue: TAlgosimObject): Boolean; virtual;

    /// <summary>Returns a copy of a subscripted object.</summary>
    /// <param name="ASubscript">The subscript to look up. Its objects are
    ///  not modified by the function.</param>
    /// <returns>If successful, a copy of the subscripted object. The caller
    ///  gains ownership of it.</returns>
    /// <exception cref="EAlgosimObjectException">Raised if the subscripted
    ///  object doesn't exist.</exception>
    function GetSubscriptedValue(ASubscript: TSubscript): TAlgosimObject;

    /// <summary>Tries to return a copy of a subscripted object.</summary>
    /// <param name="ASubscript">The subscript to look up. Its objects are
    ///  not modified by the function.</param>
    /// <param name="AValue">If successful, is set to point to a copy of the
    ///  subscripted object. The caller gains ownership of the object.</param>
    /// <returns>True iff a copy of the subscripted object could be obtained.
    ///  </returns>
    function TryGetSubscriptedValue(ASubscript: TSubscript;
      out AValue: TAlgosimObject): Boolean; virtual;

    /// <summary>Sets a subscripted member of the object.</summary>
    /// <param name="ASubscript">The subscript to look up. Its objects are not
    ///  modified by the function.</param>
    /// <param name="AValue">The value to assign to the subscripted member.
    ///  The current object gains ownership of this; hence, the caller
    ///  loses ownership of it.</param>
    /// <exception cref="EAlgosimObjectException">Raised if the operation is not
    ///  supported.</exception>
    procedure SetSubscript(ASubscript: TSubscript;
      AValue: TAlgosimObject); virtual;

    /// <summary>Returns an AlgoSim array containing all numbers found within
    ///  the object.</summary>
    /// <returns>The desired AlgoSim array. The caller gains ownership of this
    ///  object.</returns>
    function GetNumbers: TAlgosimArray;

    /// <summary>Returns the unary minus of the object, if applicable.</summary>
    /// <returns>The unary minus as an AlgoSim object. The caller gains
    ///  ownership of this object.</returns>
    /// <exception cref="EAlgosimObjectException">Raised if the operation is not
    ///  supported.</exception>
    function UnaryMinus: TAlgosimObject; virtual;

    /// <summary>Returns the sum of all elements in the container, if
    ///  applicable. The elements may be numbers, vectors, or matrices. If the
    ///  container is empty and the elements are of unknown type, the elements
    ///  are assumed to be numbers and the empty sum 0 is returned.</summary>
    /// <returns>The desired sum as an AlgoSim object. The caller gains
    ///  ownership of this object.</returns>
    /// <exception cref="EAlgosimObjectException">Raised if the operation is not
    ///  supported.</exception>
    function N_sum: TAlgosimNumericEntity; virtual;

    /// <summary>Returns the product of all elements in the container, if
    ///  applicable. The elements may be numbers or matrices. If the container
    ///  is empty and the elements are of unknown type, the elements are assumed
    ///  to be numbers and the empty product 1 is returned.</summary>
    /// <returns>The desired product as an AlgoSim object. The caller gains
    ///  ownership of this object.</returns>
    /// <exception cref="EAlgosimObjectException">Raised if the operation is not
    ///  supported.</exception>
    function N_product: TAlgosimNumericEntity; virtual;

    /// <summary>Returns the arithmetic mean of all elements in the container,
    ///  if applicable. The elements may be numbers, vectors, or matrices. The
    ///  container must not be empty, or an exception is raised.</summary>
    /// <returns>The desired mean as an AlgoSim object. The caller gains
    ///  ownership of this object.</returns>
    /// <exception cref="EAlgosimObjectException">Raised if the operation is not
    ///  supported.</exception>
    function N_ArithmeticMean: TAlgosimNumericEntity; virtual;

    /// <summary>Returns the geometric mean of all elements in the container,
    ///  if applicable. The elements must be numbers. The container must not
    ///  be empty, or an exception is raised.</summary>
    /// <returns>The desired mean as an AlgoSim object. The caller gains
    ///  ownership of this object.</returns>
    /// <exception cref="EAlgosimObjectException">Raised if the operation is not
    ///  supported.</exception>
    function N_GeometricMean: TAlgosimNumber; virtual;

    /// <summary>Returns the harmonic mean of all elements in the container,
    ///  if applicable. The elements must be numbers. The container must not
    ///  be empty, or an exception is raised.</summary>
    /// <returns>The desired mean as an AlgoSim object. The caller gains
    ///  ownership of this object.</returns>
    /// <exception cref="EAlgosimObjectException">Raised if the operation is not
    ///  supported.</exception>
    function N_HarmonicMean: TAlgosimNumber; virtual;

    /// <summary>Returns the largest number of all elements in the container,
    ///  if applicable. The elements must be real numbers. The container must
    ///  not be empty, or an exception is raised.</summary>
    /// <returns>The desired maximum as an AlgoSim object. The caller gains
    ///  ownership of this object.</returns>
    /// <exception cref="EAlgosimObjectException">Raised if the operation is not
    ///  supported.</exception>
    function N_max: TAlgosimNumber; virtual;

    /// <summary>Returns the smallest number of all elements in the container,
    ///  if applicable. The elements must be real numbers. The container must
    ///  not be empty, or an exception is raised.</summary>
    /// <returns>The desired minimum as an AlgoSim object. The caller gains
    ///  ownership of this object.</returns>
    /// <exception cref="EAlgosimObjectException">Raised if the operation is not
    ///  supported.</exception>
    function N_min: TAlgosimNumber; virtual;

    /// <summary>Returns the 1-based index of the first occurrence of a given
    ///  value in the current object, which has to be an indexed container.
    ///  </summary>
    /// <param name="AObj">The value to search for. This value is not modified
    ///  by the function.</param>
    /// <returns>The index of the first occurrence of the value, or ASO NULL if
    ///  it is not present.</returns>
    /// <exception cref="EAlgosimObjectException">Raised if the operation is not
    ///  supported by this type of object.</exception>
    /// <remarks>The current object is not modified.</remarks>
    function IndexOfValue(AObj: TAlgosimObject): TAlgosimObject; virtual;

    function IndexOfValueEps(AObj: TAlgosimObject;
      const AEpsilon: TASR = 0): TAlgosimObject; virtual;

    function Contains(AObj: TAlgosimObject): Boolean; inline;
    function ContainsEps(AObj: TAlgosimObject; const AEpsilon: TASR = 0): Boolean; inline;

    /// <summary>Returns an AlgoSim array containing the 1-based indices of the
    ///  subscripted members that equal a given value.</summary>
    /// <param name="AObj">The value to compare against. This object is not
    ///  modified by the function.</param>
    /// <returns>An AlgoSim array containing the indices of the matching
    ///  members. The caller gains ownership of this object.</returns>
    function IndicesOfValue(AObj: TAlgosimObject): TAlgosimArray; virtual;

    function IndicesOfValueEps(AObj: TAlgosimObject;
      const AEpsilon: TASR = 0): TAlgosimArray; virtual;

    function CountValue(AObj: TAlgosimObject): Integer;
    function CountValueEps(AObj: TAlgosimObject; const AEpsilon: TASR = 0): Integer;

    /// <summary>Returns an AlgoSim array containing the 1-based indices of the
    ///  subscripted members that satisfy a given predicate.</summary>
    /// <param name="APredicate">The predicate to use when testing the
    ///  subscripted members.</param>
    /// <returns>An AlgoSim array containing the indices of the matching
    ///  members. The caller gains ownership of this object.</returns>
    function IndicesOf(APredicate: TASOPredicate): TAlgosimArray; virtual;

    /// <summary>Returns the count of the subscripted members that satisfy
    ///  a given predicate.</summary>
    /// <param name="APredicate">The predicate to use when testing the
    ///  subscripted members.</param>
    /// <returns>The count of matching members.</returns>
    function Count(APredicate: TASOPredicate): Integer; virtual;

    /// <summary>Tests whether a given predicate is satisfied by all subscripted
    ///  members of the object.</summary>
    /// <param name="APredicate">The predicate to use when testing the
    ///  subscripted members.</param>
    /// <returns>True iff all subscripted members satisfy the predicate.
    ///  </returns>
    function ForAll(APredicate: TASOPredicate): Boolean; virtual;

    /// <summary>Tests whether there exists a subscripted member that satisfies
    ///  a given predicate.</summary>
    /// <param name="APredicate">The predicate to use when testing the
    ///  subscripted members.</param>
    /// <returns>True iff there exists a subscripted member that satisfies the
    ///  predicate.</returns>
    function Exists(APredicate: TASOPredicate): Boolean; virtual;

    /// <summary>Tests whether there exists exactly one subscripted member that
    ///  satisfies a given predicate.</summary>
    /// <param name="APredicate">The predicate to use when testing the
    ///  subscripted members.</param>
    /// <returns>True iff there exists a unique subscripted member that
    ///  satisfies the predicate.</returns>
    function ExistsUnique(APredicate: TASOPredicate): Boolean; virtual;

    /// <summary>Returns an AlgoSim object of the same type as the current
    ///  object, consisting of all subscripted members of the current object
    ///  that satisfy a given predicate.</summary>
    /// <param name="APredicate">The predicate to use when testing the
    ///  subscripted members.</param>
    /// <returns>A new AlgoSim object containing (copies of) the matching
    ///  elements.</returns>
    /// <exception cref="EAlgosimObjectException">Raised if the operation is not
    ///  supported by this type of object.</exception>
    /// <remarks>The current object is not modified.</remarks>
    function Filter(APredicate: TASOPredicate): TAlgosimObject; virtual;

    /// <summary>Returns an array consisting of all subscripted members of the
    ///  current object that satisfy a given predicate.</summary>
    /// <param name="APredicate">The predicate to use when testing the
    ///  subscripted members.</param>
    /// <returns>A new AlgoSim array containing (copies of) the matching
    ///  elements.</returns>
    /// <exception cref="EAlgosimObjectException">Raised if the operation is not
    ///  supported by this type of object.</exception>
    /// <remarks>The current object is not modified.</remarks>
    function Pick(APredicate: TASOPredicate; ALevel: Integer = 1): TAlgosimArray;

    /// <summary>Returns an array consisting of all recursively contained
    ///  members of the current object that satisfy a given predicate.</summary>
    /// <param name="APredicate">The predicate to use when testing the
    ///  members.</param>
    /// <returns>A new AlgoSim array containing (copies of) the matching
    ///  elements.</returns>
    /// <exception cref="EAlgosimObjectException">Raised if the operation is not
    ///  supported by this type of object.</exception>
    /// <remarks>The current object is not modified.</remarks>
    function PickRecursive(APredicate: TASOPredicate): TAlgosimArray; virtual;

    /// <summary>Applies a function to each member in the container, or to each
    ///  member satisfying a specified predicate, if applicable.</summary>
    /// <param name="AFunction">The function to apply to the members.</param>
    /// <param name="APredicate">A predicate used to test the members; only
    ///  members satisfying the predicate will be touched. If <c>nil</c>, the
    ///  behaviour is as if the predicate is identically <c>True</c>.</param>
    /// <param name="ALevel">The level at which the function is applied. If
    ///  <c>ALevel = 1</c> then the function is applied to the members of the
    ///  object, if <c>ALevel = 2</c> then the function is applied to the
    ///  members of the object's members, and so on.</param>
    /// <exception cref="EAlgosimObjectException">Raised if the operation is not
    ///  supported by this type of object.</exception>
    procedure Apply(AFunction: TASOFunction; ACondition: TASOPredicate = nil;
      ALevel: Integer = 1); virtual;

    /// <summary>Replaces each member satisfying the given predicate with
    ///  (copies) of the given object.</summary>
    /// <param name="APredicate">The predicate to use when testing members. If
    ///  <c>nil</c>, the behaviour is as if the predicate is identically
    ///  <c>True</c>.</param>
    /// <param name="ANewValue">The value to replace matching members with.
    ///  This object isn't modified.</param>
    /// <exception cref="EAlgosimObjectException">Raised if the operation is not
    ///  supported by this type of object.</exception>
    procedure Replace(APredicate: TASOPredicate; ANewValue: TAlgosimObject;
      ALevel: Integer = 1); overload; virtual;

    /// <summary>Replaces each member equal to a given object with a copy of a
    ///  different object.</summary>
    /// <param name="AOldValue">The value to compare against. This object isn't
    ///  modified.</param>
    /// <param name="ANewValue">The value to replace matching members with. This
    ///  object isn't modified.</param>
    /// <exception cref="EAlgosimObjectException">Raised if the operation is not
    ///  supported by this type of object.</exception>
    procedure Replace(AOldValue, ANewValue: TAlgosimObject;
      ALevel: Integer = 1); overload;

    procedure Replace(ANewValue: TAlgosimObject; ALevel: Integer = 1); overload;

    procedure RemoveIf(APredicate: TASOPredicate; ALevel: Integer = 1);

    procedure RemoveAll(AOldValue: TAlgosimObject; ALevel: Integer = 1);

    /// <summary>Accumulates the values in the container based on a user-
    ///  specified accumulation function and a user-defined start value.</summary>
    /// <param name="AInitialValue">The initial value for the accumulation.
    ///  This value is not modified by the function.</param>
    /// <param name="AFunction">The accumulation function to use.</param>
    /// <returns>The accumulated value. The caller gains ownership of this
    ///  object.</returns>
    function Accumulate(AInitialValue: TAlgosimObject;
      AFunction: TASOAccumulator): TAlgosimObject; virtual;

    function AccumulateStepsList(AInitialValue: TAlgosimObject;
      AFunction: TASOAccumulator): TAlgosimArray; virtual;

    function AccumulateSteps(AInitialValue: TAlgosimObject;
      AFunction: TASOAccumulator): TAlgosimObject; virtual;

    /// <summary>Removes the first member(s) in the container, if applicable.</summary>
    procedure RemoveFirst(N: Integer = 1); virtual;

    /// <summary><c>True</c> iff the object is an 'object container', that is,
    ///  if it is made up of a collection of other AlgoSim objects and also
    ///  supports the 'container' interface.</summary>
    /// <remarks>Must not raise (noexcept).</remarks>
    class function IsObjectContainer: Boolean; inline;

    /// <summary><c>True</c> iff the object is a 'container' and the elements
    ///  have a well-defined order.</summary>
    /// <remarks>Must not raise (noexcept).</remarks>
    class function IsOrderedContainer: Boolean; inline;

    /// <summary>The AlgoSim objects that this object contains.
    ///  This property can be used iff the object is an object container, that
    ///  is, iff <c>IsObjectContainer</c> is <c>True</c>.</summary>
    /// <remarks>On reading, the obtained object SHOULD be considered read-only.
    ///  On writing, the current object gains ownership of the given object,
    ///  even if the operation raises an exception. Any previous member at the
    ///  same index is automatically freed. The property is using 1-based
    ///  indices.</remarks>
    property Elements[Index: Integer]: TAlgosimObject read GetElement write SetElement;

    /// <summary>Returns the number of contained elements in <c>Elements</c>.
    ///  This property can be used iff the object is an object container, that
    ///  is, iff <c>IsObjectContainer</c> is <c>True</c>.</summary>
    property ElementCount: Integer read GetElementCount;

    /// <summary>Adds an element to this container.
    ///  This property can be used iff the object is an object container, that
    ///  is, iff <c>IsObjectContainer</c> is <c>True</c>.</summary>
    /// <returns>Some type of containers will only conditionally add the element.
    ///  If it is not added, it is freed. These containers will return True iff
    ///  the element is actually added. The typical example is a set told to add
    ///  an already existing element.</returns>
    /// <remarks>The current object gains ownership of the given object,
    ///  even if the operation raises an exception or returns False.</remarks>
    function AddElement(const AElement: TAlgosimObject): Boolean; virtual;

    function ElementsAre(AASOMetaclass: TAlgosimObjectClass): Boolean;

    function HasElementOfClass(AASOMetaclass: TAlgosimObjectClass): Boolean;

    /// <summary>For container-like objects that support this concept, reads or
    ///  sets the capacity. For other types of objects, this value is not used.
    ///  </summary>
    property Capacity: Integer read GetCapacity write SetCapacity;

    /// <summary>For container-like objects that support the copcept of a
    ///  'capacity', sets the capacity to the actual count of elements. For
    ///  other types of objects, this method has no effect.</summary>
    procedure TrimExcess; virtual;

    /// <summary><c>True</c> iff the object supports the property and considers
    ///  itself to be of a complex type (as opposed to a real type).</summary>
    /// <remarks>For instance, a real number, vector, or matrix has
    //   <c>IsComplex = False</c> while a complex number, vector, or matrix has
    ///  <c>IsComplex = True</c>. A container has <c>IsComplex = True</c> iff
    ///  any of its members has <c>IsComplex = True</c>.</remarks>
    property IsComplex: Boolean read GetIsComplex;

    /// <summary><c>True</c> iff the object is a 'value container', that is, if
    ///  it naturally is made up of a collection of values that easily can be
    ///  converted into AlgoSim objects and also supports the 'value container'
    ///  interface.</summary>
    /// <remarks>Must not raise (noexcept).</remarks>
    class function IsValueContainer: Boolean; inline;

    /// <summary><c>True</c> iff the object is an object or a value container.
    ///  </summary>
    /// <remarks>Must not raise (noexcept).</remarks>
    class function IsContainer: Boolean; inline;

    /// <summary>The AlgoSim objects that this object implicitly contains.
    ///  This property can be used iff the object is an object or a value
    ///  container, that is, iff <c>IsContainer</c> is <c>True</c>.</summary>
    /// <remarks>On reading, the caller gains ownership of the given object and
    ///  SHOULD free it when it no longer needs it.
    ///  On writing, the current object gains ownership of the given object,
    ///  even if the operation raises an exception. The property is using
    ///  1-based indices.</remarks>
    property Values[Index: Integer]: TAlgosimObject read GetValue write SetValue;

    /// <summary>Returns the number of values in <c>Values</c>.
    ///  This property can be used iff the object is an object or a value
    ///  container, that is, iff <c>IsContainer</c> is <c>True</c>.</summary>
    property ValueCount: Integer read GetValueCount;

    /// <summary><c>True</c> iff the object is a container or value container
    ///  that is able to index its values using points (x, y) of integers
    ///  (e.g., a matrix, a table, or a pixmap).</summary>
    /// <remarks>Must not raise (noexcept).</remarks>
    class function IsPlanarContainer: Boolean; inline;

    /// <summary><c>True</c> iff the object is a value container that is able
    ///  to index its values using points (x, y) of integers (e.g., a matrix,
    ///  a table, or a pixmap).</summary>
    /// <remarks>Must not raise (noexcept).</remarks>
    class function IsPlanarValueContainer: Boolean; inline;

    /// <summary>The AlgoSim objects that this object implicitly contains.
    ///  This property can be used iff the object is a planar value container,
    ///  that is, iff <c>IsPlanarValueContainer</c> is <c>True</c>.</summary>
    /// <remarks>On reading, the caller gains ownership of the given object and
    ///  SHOULD free it when it no longer needs it. On writing, the current
    ///  object gains ownership of the given object, even if the operation
    ///  raises an exception. The indices are 1-based.</remarks>
    property ValueFromPoint[const Index: TPoint]: TAlgosimObject read GetValueFromPoint write SetValueFromPoint;

    /// <summary>The width and height of the object, if applicable.
    ///  For objects that don't have a planar extent, 1×1 is returned.</summary>
    property PlanarExtent: TSize read GetPlanarExtent write SetPlanarExtent;

    /// <summary>Returns an AlgoSim object of the same type as the current
    ///  object, containing (copies of) the first <c>N</c> members of the
    ///  current object, if applicable.</summary>
    /// <returns>A new AlgoSim object. The caller gains ownership of this.
    ///  </returns>
    /// <exception cref="EAlgosimObjectException">Raised if the operation is not
    ///  supported by this type of object.</exception>
    /// <remarks>The current object is not modified.</remarks>
    function First(N: Integer): TAlgosimObject; virtual;

    /// <summary>Returns an AlgoSim object of the same type as the current
    ///  object, containing (copies of) the last <c>N</c> members of the
    ///  current object, if applicable.</summary>
    /// <returns>A new AlgoSim object. The caller gains ownership of this.
    ///  </returns>
    /// <exception cref="EAlgosimObjectException">Raised if the operation is not
    ///  supported by this type of object.</exception>
    /// <remarks>The current object is not modified.</remarks>
    function Last(N: Integer): TAlgosimObject; virtual;

    /// <summary>Returns an AlgoSim object of the same type as the current
    ///  object, containing (copies of) the members of the current object
    ///  with indices in [A, B], if applicable. The indices are 1-based.</summary>
    /// <returns>A new AlgoSim object. The caller gains ownership of this.
    ///  </returns>
    /// <exception cref="EAlgosimObjectException">Raised if the operation is not
    ///  supported by this type of object.</exception>
    /// <remarks>The current object is not modified.</remarks>
    function Part(A, B: Integer): TAlgosimObject; overload; virtual;

    /// <summary>Returns an AlgoSim object of the same type as the current
    ///  object, containing (copies of) the members of the current object
    ///  with indices in [A, \infty), if applicable. The indices are 1-based.
    ///  </summary>
    /// <returns>A new AlgoSim object. The caller gains ownership of this.
    ///  </returns>
    /// <exception cref="EAlgosimObjectException">Raised if the operation is not
    ///  supported by this type of object.</exception>
    /// <remarks>The current object is not modified.</remarks>
    function Part(A: Integer): TAlgosimObject; overload; virtual;

    /// <summary>Returns an AlgoSim object of the same type as the current
    ///  object, containing (copies of) the members of the current object
    ///  with the specified indices, if applicable. The indices are 1-based.
    ///  </summary>
    /// <returns>A new AlgoSim object. The caller gains ownership of this.
    ///  </returns>
    /// <exception cref="EAlgosimObjectException">Raised if the operation is not
    ///  supported by this type of object or if any index is out of bounds.
    ///  </exception>
    /// <remarks>The current object is not modified.</remarks>
    function Part(const ARanges: array of TRange): TAlgosimObject; overload; virtual;

    /// <summary>Returns an AlgoSim object of the same type as the current
    ///  object, containing (copies of) the members of the current object
    ///  with the specified indices, if applicable. The indices are 1-based.
    ///  </summary>
    /// <returns>A new AlgoSim object. The caller gains ownership of this.
    ///  </returns>
    /// <exception cref="EAlgosimObjectException">Raised if the operation is not
    ///  supported by this type of object or if any index is out of bounds.
    ///  </exception>
    /// <remarks>The current object is not modified.</remarks>
    function Part(const AIndices: array of Integer): TAlgosimObject; overload; virtual;

    /// <summary>Returns an AlgoSim object of the same type as the current
    ///  planar container object, containing (copies of) the members of the
    ///  current object with the specified indices, if applicable. The indices
    ///  are 1-based.</summary>
    /// <returns>A new AlgoSim object. The caller gains ownership of this.
    ///  </returns>
    /// <exception cref="EAlgosimObjectException">Raised if the operation is not
    ///  supported by this type of object or if any index is out of bounds.
    ///  </exception>
    /// <remarks>The current object is not modified.</remarks>
    function Part2d(const ARangesX, ARangesY: array of TRange): TAlgosimObject; overload; virtual;

    /// <summary>Returns an AlgoSim object of the same type as the current
    ///  planar container object, containing (copies of) the members of the
    ///  current object with the specified indices, if applicable. The indices
    ///  are 1-based.</summary>
    /// <returns>A new AlgoSim object. The caller gains ownership of this.
    ///  </returns>
    /// <exception cref="EAlgosimObjectException">Raised if the operation is not
    ///  supported by this type of object or if any index is out of bounds.
    ///  </exception>
    /// <remarks>The current object is not modified.</remarks>
    function Part2d(const AIndicesX, AIndicesY: array of Integer): TAlgosimObject; overload; virtual;

    function Row(const AIndex: Integer): TAlgosimObject; inline;
    function Column(const AIndex: Integer): TAlgosimObject; inline;

    function Rows: TAlgosimArray;
    function Columns: TAlgosimArray;

    function Random: TAlgosimObject; virtual;

    /// <summary>Compares two AlgoSim objects for (strict) equality of their
    ///  data.</summary>
    /// <param name="Obj">The object to compare to.</param>
    /// <returns>True iff the data of the current object is identical to the
    ///  data of <c>Obj</c>.</returns>
    function Equals(Obj: TObject): Boolean; override;

    function GetHashCode: Integer; override;

    class function SortClass: TSortClass; virtual;
    class function SortClassCompare(const Left, Right: TAlgosimObject): Integer; virtual;
    class function SortClassSameObject(const Left, Right: TAlgosimObject;
      const AEpsilon: TASR = 0): Boolean; virtual;
    function SortClassGetHashCode: Integer; virtual;

    procedure Sort; overload; virtual;
    procedure Sort(AComparer: IComparer<TAlgosimObject>); overload; virtual;
    procedure Sort(AComparer: IComparer<TASR>); overload; virtual;
    procedure Sort(AComparer: IComparer<TASC>); overload; virtual;

    procedure Shuffle; virtual;
    procedure Reverse; virtual;

    function RemoveDuplicates: TAlgosimObject; virtual;
    function RemoveDuplicatesEps(const Epsilon: TASR = 0): TAlgosimObject; virtual;
    function RemoveAdjacentDuplicates: TAlgosimObject; virtual;
    function RemoveAdjacentDuplicatesEps(const Epsilon: TASR = 0): TAlgosimObject; virtual;

    function Frequencies: TAlgosimArray; virtual;
    function FrequenciesEps(const Epsilon: TASR = 0): TAlgosimArray; virtual;
    function CollapseSequences: TAlgosimArray; virtual;
    function CollapseSequencesEps(const Epsilon: TASR = 0): TAlgosimArray; virtual;

    function RotLeft(N: Integer): TAlgosimObject; virtual;
    function RotRight(N: Integer): TAlgosimObject; virtual;

    procedure Append(AElement: TAlgosimObject); virtual;
    procedure ExtendWith(AElement: TAlgosimObject); virtual;
    procedure Insert(AIndex: Integer; AElement: TAlgosimObject); virtual;
    procedure Remove(const AIndices: array of Integer); overload; virtual;
    procedure Remove(const ARanges: array of TRange); overload;
    procedure Truncate(ANewLength: Integer); virtual;
    procedure Swap(Index1, Index2: Integer); virtual;

    function WithSpecificValues(AValues: TAlgosimArray): TAlgosimObject; virtual;

    /// <summary>Creates an integer with the same information as
    ///  the current object, if possible.</summary>
    /// <exception cref="EAlgosimObjectException">Raised if the operation is not
    ///  supported by this type of object.</exception>
    /// <remarks>The current object is not modified.</remarks>
    function ToInteger: TASI; virtual;

    /// <summary>Creates a rational number with the same information as
    ///  the current object, if possible.</summary>
    /// <exception cref="EAlgosimObjectException">Raised if the operation is not
    ///  supported by this type of object.</exception>
    /// <remarks>The current object is not modified.</remarks>
    function ToRationalNumber: TRationalNumber; virtual;

    /// <summary>Creates a real number with the same information as
    ///  the current object, if possible.</summary>
    /// <exception cref="EAlgosimObjectException">Raised if the operation is not
    ///  supported by this type of object.</exception>
    /// <remarks>The current object is not modified.</remarks>
    function ToRealNumber: TASR; virtual;

    /// <summary>Creates a complex number with the same information as
    ///  the current object, if possible.</summary>
    /// <exception cref="EAlgosimObjectException">Raised if the operation is not
    ///  supported by this type of object.</exception>
    /// <remarks>The current object is not modified.</remarks>
    function ToComplexNumber: TASC; virtual;

    /// <summary>Creates a number object with the same information as
    ///  the current object, if possible.</summary>
    /// <exception cref="EAlgosimObjectException">Raised if the operation is not
    ///  supported by this type of object.</exception>
    /// <remarks>The current object is not modified.</remarks>
    function ToNumber: TAlgosimObject; virtual;

    /// <summary>Creates a real vector with the same information as
    ///  the current object, if possible.</summary>
    /// <exception cref="EAlgosimObjectException">Raised if the operation is not
    ///  supported by this type of object.</exception>
    /// <remarks>The current object is not modified.</remarks>
    function ToRealVector: TRealVector; virtual;

    /// <summary>Creates a complex vector with the same information as
    ///  the current object, if possible.</summary>
    /// <exception cref="EAlgosimObjectException">Raised if the operation is not
    ///  supported by this type of object.</exception>
    /// <remarks>The current object is not modified.</remarks>
    function ToComplexVector: TComplexVector; virtual;

    /// <summary>Creates a vector object with the same information as
    ///  the current object, if possible.</summary>
    /// <exception cref="EAlgosimObjectException">Raised if the operation is not
    ///  supported by this type of object.</exception>
    /// <remarks>The current object is not modified.</remarks>
    function ToVector: TAlgosimVector; virtual;

    /// <summary>Creates a real matrix with the same information as
    ///  the current object, if possible.</summary>
    /// <exception cref="EAlgosimObjectException">Raised if the operation is not
    ///  supported by this type of object.</exception>
    /// <remarks>The current object is not modified.</remarks>
    function ToRealMatrix: TRealMatrix; virtual;

    /// <summary>Creates a complex matrix with the same information as
    ///  the current object, if possible.</summary>
    /// <exception cref="EAlgosimObjectException">Raised if the operation is not
    ///  supported by this type of object.</exception>
    /// <remarks>The current object is not modified.</remarks>
    function ToComplexMatrix: TComplexMatrix; virtual;

    /// <summary>Creates a matrix object with the same information as
    ///  the current object, if possible.</summary>
    /// <exception cref="EAlgosimObjectException">Raised if the operation is not
    ///  supported by this type of object.</exception>
    /// <remarks>The current object is not modified.</remarks>
    function ToMatrix: TAlgosimMatrix; virtual;

    // The following As* methods work like the corresponding To* methods
    // but return a reference to the original data instead of copying it to a
    // new buffer, if possible. Hence, changing the returned values of the As*
    // methods might change the original objects. (But if that isn't a problem,
    // the As* methods might return faster, since they don't need to copy a
    // potentially large amount of data.)
    function AsRealVector: TRealVector; virtual;
    function AsComplexVector: TComplexVector; virtual;
    function AsVector: TAlgosimVector; inline;
    function AsRealMatrix: TRealMatrix; virtual;
    function AsComplexMatrix: TComplexMatrix; virtual;
    function AsMatrix: TAlgosimMatrix;

    function ToCharacter: Char; virtual;

    /// <summary>Creates a boolean value with the same information as
    ///  the current object, if possible.</summary>
    /// <exception cref="EAlgosimObjectException">Raised if the operation is not
    ///  supported by this type of object.</exception>
    /// <remarks>The current object is not modified.</remarks>
    function ToBoolean: Boolean; virtual;

    /// <summary>Creates an AlgoSim array object with the same information as
    ///  the current object, if possible.</summary>
    /// <exception cref="EAlgosimObjectException">Raised if the operation is not
    ///  supported by this type of object.</exception>
    /// <remarks>The current object is not modified.</remarks>
    function ToList: TAlgosimArray; virtual;

    /// <summary>Creates an AlgoSim table object with the same information as
    ///  the current object, if possible.</summary>
    /// <exception cref="EAlgosimObjectException">Raised if the operation is not
    ///  supported by this type of object.</exception>
    /// <remarks>The current object is not modified.</remarks>
    function ToTable: TASTable; virtual;

    /// <summary>Creates an AlgoSim set object with the same information as the
    ///  current object, if possible.</summary>
    /// <exception cref="EAlgosimObjectException">Raised if the operation is not
    ///  supported by this type of object.</exception>
    /// <remarks>The current object is not modified.</remarks>
    function ToSet: TAlgosimSet; virtual;

    /// <summary>Creates an AlgoSim binary object with the same information as
    ///  the current object, if possible.</summary>
    /// <exception cref="EAlgosimObjectException">Raised if the operation is not
    ///  supported by this type of object.</exception>
    /// <remarks>The current object is not modified.</remarks>
    function ToBinaryObject: TAlgosimBinaryData; virtual;

    /// <summary>Tries to convert the object's value to a TASI integer.</summary>
    /// <param name="ASI">If the function returns <c>true</c>, this value is set
    ///  to the number represented by the object's value.</param>
    /// <returns>Returns <c>true</c> iff the object is a number that can be
    ///  exactly represented as a TASI.</returns>
    function TryToASI(out ASI: TASI): Boolean; virtual;

    /// <summary>Tries to convert the object's value to a rational number.</summary>
    /// <param name="R">If the function returns <c>true</c>, this value is set
    ///  to the number represented by the object's value.</param>
    /// <returns>Returns <c>true</c> iff the object is a number that can be
    ///  exactly represented as a rational number.</returns>
    function TryToRat(out R: TRationalNumber): Boolean; virtual;

    /// <summary>Tries to convert the object's value to a 32-bit signed
    ///  integer.</summary>
    /// <param name="Int">If the function returns <c>true</c>, this value is set
    ///  to the number represented by the object's value.</param>
    /// <returns>Returns <c>true</c> iff the object is a number that can be
    ///  exactly represented as a 32-bit signed integer.</returns>
    function TryToInt32(out Int: Int32): Boolean; virtual;

    /// <summary>Tries to convert the object's value to a 64-bit signed
    ///  integer.</summary>
    /// <param name="Int">If the function returns <c>true</c>, this value is set
    ///  to the number represented by the object's value.</param>
    /// <returns>Returns <c>true</c> iff the object is a number that can be
    ///  exactly represented as a 64-bit signed integer.</returns>
    function TryToInt64(out Int: Int64): Boolean; virtual;

    /// <summary>Tries to convert the object's value to a floating-point number.
    ///  </summary>
    /// <param name="Int">If the function returns <c>true</c>, this value is set
    ///  to the number represented by the object's value.</param>
    /// <returns>Returns <c>true</c> iff the object is a number that can be
    ///  represented as a floating-point number.</returns>
    function TryToASR(out Val: TASR): Boolean; virtual;

    /// <summary>Tries to convert the object's value to complex number.
    ///  </summary>
    /// <param name="Int">If the function returns <c>true</c>, this value is set
    ///  to the number represented by the object's value.</param>
    /// <returns>Returns <c>true</c> iff the object is a number that can be
    ///  represented as a floating-point complex number.</returns>
    function TryToASC(out Val: TASC): Boolean; virtual;

    function IsASI: Boolean; inline;
    function IsRat: Boolean; inline;
    function IsInt32: Boolean; inline;
    function IsInt64: Boolean; inline;
    function IsASR: Boolean; inline;
    function IsASC: Boolean; inline;

    function ToASI: TASI; inline;
    function ToRat: TRationalNumber; inline;
    function ToInt32: Int32; inline;
    function ToInt64: Int64; inline;
    function ToASR: TASR; inline;
    function ToASC: TASC; inline;

    /// <summary>If possible, returns a pointer (and an associated byte length
    ///  to the raw, binary data of the object.</summary>
    /// <param name="Buf">If the function returns <c>true</c>, receives the
    ///  pointer to the raw, binary data of the object.</param>
    /// <param name="Len">If the function returns <c>true</c>, receives the
    ///  length of the binary data of the object.</param>
    /// <returns><c>True</c> iff it is possible to obtain a read-only pointer
    ///  to the binary data of the object.</returns>
    /// <remarks>The binary data pointed to by <c>Buf</c> must be considered
    ///  read-only by the caller; it must not be modified in any way. The
    ///  typical reason why this function returns <c>false</c> is that the
    ///  object's data isn't stored as a contiguous sequence of bytes in memory.
    ///  </remarks>
    function GetBinaryData(var Buf: PByte; var Len: UInt64): Boolean; virtual;

    /// <summary>Sets the binary data of the object using a provided buffer,
    ///  if supported by the object type.</summary>
    /// <param name="Buf">The buffer to copy bytes from. This is not modified
    ///  by the procedure.</param>
    /// <param name="Len">The size of the buffer, in bytes.</param>
    /// <exception cref="EAlgosimObjectException">Raised if the object doesn't
    ///  support having its data set from a byte buffer or if the byte buffer
    ///  is invalid for the object type (either in size or in content).
    ///  </exception>
    procedure SetBinaryData(const Buf: PByte; const Len: UInt64); overload; virtual;
    procedure SetBinaryData(const AData: array of Byte); overload;

    function ToColor: TRGB; virtual;
    function ToPixel: TASPixel; virtual;

    property MaxLen: Integer read GetMaxLen write SetMaxLen;

  end;

  [AlgosimObject('null')]
  TAlgosimNullObject = class(TAlgosimObject)
  public
    constructor Create(AObject: TAlgosimObject); override;
    function ToString: string; override;
    function GetAsSingleLineText(const AOptions: TFormatOptions): string; override;
    function ToBoolean: Boolean; override;
    class function SortClassSameObject(const Left, Right: TAlgosimObject;
      const AEpsilon: TASR = 0): Boolean; override;
    function Equals(Obj: TObject): Boolean; override;
    function ToInputString: string; override;
  end;

  TNormType = (ntEuclidean, ntFrobenius, ntPNorm, ntMaxNorm, ntSumNorm, ntKNorm,
    ntMaxColSum, ntMaxRowSum, ntSpectral);

  TAlgosimInteger = class;
  TAlgosimRationalNumber = class;
  TAlgosimRealNumber = class;
  TAlgosimComplexNumber = class;

  [AlgosimObject('numeric entity')]
  TAlgosimNumericEntity = class abstract(TAlgosimObject)
  strict private
    var
      FFormatCode: UInt64;
    // 00000000 00000000 00000000 00000000
    //          LLLLLLLL      fFF gGGGGGGG
    // 00000000 00000000 00000000 00000000
    // SSSSSSSS eEBBBBBB dDDDDDDD DDDDDDDD
    const
      FCM_DIGITS   = $0000000000007FFF;
      FCM_DIGITSO  = $0000000000008000;
      FCM_BASE     = $00000000003F0000;
      FCM_EXPFMT   = $0000000000400000;
      FCM_EXPFMTO  = $0000000000800000;
      FCM_STYLE    = $00000000FF000000;
      FCM_GROUP    = $0000007F00000000;
      FCM_GROUPO   = $0000008000000000;
      FCM_NUMFMT   = $0000030000000000;
      FCM_NUMFMTO  = $0000040000000000;
      FCM_MINLEN   = $00FF000000000000;
    function GetNumDigits: Integer; inline;
    procedure SetNumDigits(const Value: Integer); inline;
    function GetNumDigitsOverride: Boolean; inline;
    procedure SetNumDigitsOverride(const Value: Boolean); inline;
    function GetStyle: TFormatStyle; inline;
    procedure SetStyle(const Value: TFormatStyle); inline;
    function GetNumberBase: Integer; inline;
    procedure SetNumberBase(Value: Integer); inline;
    function GetDigitGrouping: Integer; inline;
    procedure SetDigitGrouping(Value: Integer); inline;
    function GetDigitGroupingOverride: Boolean; inline;
    procedure SetDigitGroupingOverride(Value: Boolean); inline;
    function GetNumberFormat: TNumberFormat; inline;
    procedure SetNumberFormat(Value: TNumberFormat); inline;
    function GetNumberFormatOverride: Boolean; inline;
    procedure SetNumberFormatOverride(Value: Boolean); inline;
    function GetPrettyExp: Boolean; inline;
    procedure SetPrettyExp(Value: Boolean); inline;
    function GetPrettyExpOverride: Boolean; inline;
    procedure SetPrettyExpOverride(Value: Boolean); inline;
    function GetMinLength: Integer; inline;
    procedure SetMinLength(Value: Integer); inline;
  protected
    function ApplyOptions(const AOptions: TFormatOptions): TFormatOptions;
  public
    property FormatCode: UInt64 read FFormatCode write FFormatCode;
    property NumDigits: Integer read GetNumDigits write SetNumDigits;
    property NumDigitsOverride: Boolean read GetNumDigitsOverride write SetNumDigitsOverride;
    property Style: TFormatStyle read GetStyle write SetStyle;
    property NumberBase: Integer read GetNumberBase write SetNumberBase;
    property DigitGrouping: Integer read GetDigitGrouping write SetDigitGrouping;
    property DigitGroupingOverride: Boolean read GetDigitGroupingOverride write SetDigitGroupingOverride;
    property NumberFormat: TNumberFormat read GetNumberFormat write SetNumberFormat;
    property NumberFormatOverride: Boolean read GetNumberFormatOverride write SetNumberFormatOverride;
    property PrettyExp: Boolean read GetPrettyExp write SetPrettyExp;
    property PrettyExpOverride: Boolean read GetPrettyExpOverride write SetPrettyExpOverride;
    property MinLength: Integer read GetMinLength write SetMinLength;

    function RealPart: TAlgosimNumericEntity; virtual;
    function ImaginaryPart: TAlgosimNumericEntity; virtual;
    function Abs: TAlgosimNumericEntity; virtual;
    function Square: TAlgosimNumericEntity; virtual;
    function Norm(AType: TNormType = ntEuclidean; AParam: Integer = 2;
      AYieldProc: TObjProc = nil): TASR; virtual;
    function NormSquared: TAlgosimRealNumber; virtual;
    function Inverse: TAlgosimNumericEntity; virtual;
    function Transpose: TAlgosimMatrix; virtual;
    function ConjugateTranspose: TAlgosimMatrix; virtual;

    function ScaledBy(const AFactor: TASR): TAlgosimNumericEntity; virtual;

    function IsPositive(const Eps: Double = 0): Boolean; virtual;
    function IsNonNegative(const Eps: Double = 0): Boolean; virtual;
    function IsNegative(const Eps: Double = 0): Boolean; virtual;
    function IsNonPositive(const Eps: Double = 0): Boolean; virtual;
    function IsZero(const Eps: Double = 0): Boolean; virtual;
    function IsNonZero(const Eps: Double = 0): Boolean; virtual;

    procedure Defuzz(const Eps: Double = 1E-8); virtual;
  end;

  TSDDKind = (
    sddLt,      // x < a
    sddLeq,     // x <= a
    sddGt,      // x > a
    sddGeq,     // x >= a
    sddBii,     // a <= x <= b
    sddBei,     // a < x <= b
    sddBie,     // a <= x < b
    sddBee      // a < x < b
    );

  TSimpleDomainDescription = record
    a, b: TASR;
    Kind: TSDDKind;
    Complement: Boolean;
    function Contains(const x: TASR): Boolean;
  end;

  TSDD = TSimpleDomainDescription;

  [AlgosimObject('number')]
  TAlgosimNumber = class abstract(TAlgosimNumericEntity)
  protected
    procedure AddNumbersToArray(AArray: TAlgosimArray; ALevel: Integer = 0); override;

    function AddASR(ASR: TAlgosimRealNumber): TAlgosimNumber; virtual; abstract;
    function AddASC(ASC: TAlgosimComplexNumber): TAlgosimNumber; virtual; abstract;
    function AddASI(ASI: TAlgosimInteger): TAlgosimNumber; virtual; abstract;
    function AddRat(R: TAlgosimRationalNumber): TAlgosimNumber; virtual; abstract;
    function AddTo(ANum: TAlgosimNumber): TAlgosimNumber; virtual; abstract;

    function SubtractASR(ASR: TAlgosimRealNumber): TAlgosimNumber; virtual; abstract;
    function SubtractASC(ASC: TAlgosimComplexNumber): TAlgosimNumber; virtual; abstract;
    function SubtractASI(ASI: TAlgosimInteger): TAlgosimNumber; virtual; abstract;
    function SubtractRat(R: TAlgosimRationalNumber): TAlgosimNumber; virtual; abstract;
    function SubtractFrom(ANum: TAlgosimNumber): TAlgosimNumber; virtual; abstract;

    function MultiplyASR(ASR: TAlgosimRealNumber): TAlgosimNumber; virtual; abstract;
    function MultiplyASC(ASC: TAlgosimComplexNumber): TAlgosimNumber; virtual; abstract;
    function MultiplyASI(ASI: TAlgosimInteger): TAlgosimNumber; virtual; abstract;
    function MultiplyRat(R: TAlgosimRationalNumber): TAlgosimNumber; virtual; abstract;
    function MultiplyTo(ANum: TAlgosimNumber): TAlgosimNumber; virtual; abstract;

    function DivideASR(ASR: TAlgosimRealNumber): TAlgosimNumber; virtual; abstract;
    function DivideASC(ASC: TAlgosimComplexNumber): TAlgosimNumber; virtual; abstract;
    function DivideASI(ASI: TAlgosimInteger): TAlgosimNumber; virtual; abstract;
    function DivideRat(R: TAlgosimRationalNumber): TAlgosimNumber; virtual; abstract;
    function DivideBy(ANum: TAlgosimNumber): TAlgosimNumber; virtual; abstract;

    function RaiseToASR(ASR: TAlgosimRealNumber): TAlgosimNumber; virtual; abstract;
    function RaiseToASC(ASC: TAlgosimComplexNumber): TAlgosimNumber; virtual; abstract;
    function RaiseToASI(ASI: TAlgosimInteger): TAlgosimNumber; virtual; abstract;
    function RaiseToRat(R: TAlgosimRationalNumber): TAlgosimNumber; virtual; abstract;
    function RaiseTo(ANum: TAlgosimNumber): TAlgosimNumber; virtual; abstract;

    function GetMaxLen: Integer; override;
    procedure SetMaxLen(AValue: Integer); override;

  public
    function Conjugate: TAlgosimNumber; virtual; abstract;
    function Argument: TAlgosimRealNumber; virtual; abstract;

    function ComputeFunction(const ARealDomain: TSDD; ARealFcn: TRealFunction;
      AComplexFcn: TComplexFunction): TAlgosimNumber; overload; virtual; abstract;
    function ComputeFunction(ARealFcn: TRealFunction;
      AComplexFcn: TComplexFunction): TAlgosimNumber; overload; virtual; abstract;

    procedure Increase(AAmount: TASI = 1); virtual; abstract;

    class function Add(Left, Right: TAlgosimNumber): TAlgosimNumber;
    class function Subtract(Left, Right: TAlgosimNumber): TAlgosimNumber;
    class function Multiply(Left, Right: TAlgosimNumber): TAlgosimNumber;
    class function Divide(Left, Right: TAlgosimNumber): TAlgosimNumber;
    class function Power(Left, Right: TAlgosimNumber): TAlgosimNumber;

    class function LessThan(Left, Right: TAlgosimNumber): Boolean;
    class function LessThanOrEqualTo(Left, Right: TAlgosimNumber): Boolean;
    class function GreaterThan(Left, Right: TAlgosimNumber): Boolean;
    class function GreaterThanOrEqualTo(Left, Right: TAlgosimNumber): Boolean;

    function Equals(Obj: TObject): Boolean; override;
    function SortClassGetHashCode: Integer; override;

    class function SortClass: TSortClass; override;
    class function SortClassCompare(const Left, Right: TAlgosimObject): Integer; override;
    class function SortClassSameObject(const Left, Right: TAlgosimObject;
      const AEpsilon: TASR = 0): Boolean; override;

    function AsMemberOfSimplestField: TAlgosimNumber; virtual; abstract;

    function ToColor: TRGB; override;
  end;

  [AlgosimObject('integer')]
  TAlgosimInteger = class(TAlgosimNumber)
  protected
    FValue: TASI;

    function AddASR(ASR: TAlgosimRealNumber): TAlgosimNumber; override;
    function AddASC(ASC: TAlgosimComplexNumber): TAlgosimNumber; override;
    function AddASI(ASI: TAlgosimInteger): TAlgosimNumber; override;
    function AddRat(R: TAlgosimRationalNumber): TAlgosimNumber; override;
    function AddTo(ANum: TAlgosimNumber): TAlgosimNumber; override;

    function SubtractASR(ASR: TAlgosimRealNumber): TAlgosimNumber; override;
    function SubtractASC(ASC: TAlgosimComplexNumber): TAlgosimNumber; override;
    function SubtractASI(ASI: TAlgosimInteger): TAlgosimNumber; override;
    function SubtractRat(R: TAlgosimRationalNumber): TAlgosimNumber; override;
    function SubtractFrom(ANum: TAlgosimNumber): TAlgosimNumber; override;

    function MultiplyASR(ASR: TAlgosimRealNumber): TAlgosimNumber; override;
    function MultiplyASC(ASC: TAlgosimComplexNumber): TAlgosimNumber; override;
    function MultiplyASI(ASI: TAlgosimInteger): TAlgosimNumber; override;
    function MultiplyRat(R: TAlgosimRationalNumber): TAlgosimNumber; override;
    function MultiplyTo(ANum: TAlgosimNumber): TAlgosimNumber; override;

    function DivideASR(ASR: TAlgosimRealNumber): TAlgosimNumber; override;
    function DivideASC(ASC: TAlgosimComplexNumber): TAlgosimNumber; override;
    function DivideASI(ASI: TAlgosimInteger): TAlgosimNumber; override;
    function DivideRat(R: TAlgosimRationalNumber): TAlgosimNumber; override;
    function DivideBy(ANum: TAlgosimNumber): TAlgosimNumber; override;

    function RaiseToASR(ASR: TAlgosimRealNumber): TAlgosimNumber; override;
    function RaiseToASC(ASC: TAlgosimComplexNumber): TAlgosimNumber; override;
    function RaiseToASI(ASI: TAlgosimInteger): TAlgosimNumber; override;
    function RaiseToRat(R: TAlgosimRationalNumber): TAlgosimNumber; override;
    function RaiseTo(ANum: TAlgosimNumber): TAlgosimNumber; override;

    function GetMemorySize: UInt64; override;

  public
    constructor Create(AObject: TAlgosimObject); override;
    constructor CreateWithValue(const AValue: TASI);
    function ToString: string; override;
    function GetAsSingleLineText(const AOptions: TFormatOptions): string; override;
    function ExplainedOutput(const AOptions: TFormatOptions): string; override;
    function AsMemberOfSimplestField: TAlgosimNumber; override;
    property Value: TASI read FValue write FValue;
    function UnaryMinus: TAlgosimObject; override;
    function Conjugate: TAlgosimNumber; override;
    function Argument: TAlgosimRealNumber; override;
    function RealPart: TAlgosimNumericEntity; override;
    function ImaginaryPart: TAlgosimNumericEntity; override;
    function Abs: TAlgosimNumericEntity; override;
    function Square: TAlgosimNumericEntity; override;
    function Norm(AType: TNormType = ntEuclidean; AParam: Integer = 2;
      AYieldProc: TObjProc = nil): TASR; override;
    function NormSquared: TAlgosimRealNumber; override;
    function Inverse: TAlgosimNumericEntity; override;
    function ScaledBy(const AFactor: TASR): TAlgosimNumericEntity; override;
    function IsPositive(const Eps: Double = 0): Boolean; override;
    function IsNonNegative(const Eps: Double = 0): Boolean; override;
    function IsNegative(const Eps: Double = 0): Boolean; override;
    function IsNonPositive(const Eps: Double = 0): Boolean; override;
    function IsZero(const Eps: Double = 0): Boolean; override;
    function IsNonZero(const Eps: Double = 0): Boolean; override;
    function ComputeFunction(const ARealDomain: TSDD; ARealFcn: TRealFunction;
      AComplexFcn: TComplexFunction): TAlgosimNumber; override;
    function ComputeFunction(ARealFcn: TRealFunction;
      AComplexFcn: TComplexFunction): TAlgosimNumber; override;
    function ToRealNumber: TASR; override;
    function ToComplexNumber: TASC; override;
    function ToRealVector: TRealVector; override;
    function ToComplexVector: TComplexVector; override;
    function ToRealMatrix: TRealMatrix; override;
    function ToComplexMatrix: TComplexMatrix; override;
    function ToBoolean: Boolean; override;
    function TryToASI(out ASI: Int64): Boolean; override;
    function TryToRat(out R: TRationalNumber): Boolean; override;
    function TryToInt32(out Int: Integer): Boolean; override;
    function TryToInt64(out Int: Int64): Boolean; override;
    function TryToASR(out Val: TASR): Boolean; override;
    function TryToASC(out Val: TASC): Boolean; override;
    procedure Increase(AAmount: TASI = 1); override;

    function Equals(Obj: TObject): Boolean; override;

    function GetBinaryData(var Buf: PByte; var Len: UInt64): Boolean; override;
    procedure SetBinaryData(const Buf: PByte; const Len: UInt64); override;
  end;

  [AlgosimObject('rational number')]
  TAlgosimRationalNumber = class(TAlgosimNumber)
  protected
    FValue: TRationalNumber;

    function AddASR(ASR: TAlgosimRealNumber): TAlgosimNumber; override;
    function AddASC(ASC: TAlgosimComplexNumber): TAlgosimNumber; override;
    function AddASI(ASI: TAlgosimInteger): TAlgosimNumber; override;
    function AddRat(R: TAlgosimRationalNumber): TAlgosimNumber; override;
    function AddTo(ANum: TAlgosimNumber): TAlgosimNumber; override;

    function SubtractASR(ASR: TAlgosimRealNumber): TAlgosimNumber; override;
    function SubtractASC(ASC: TAlgosimComplexNumber): TAlgosimNumber; override;
    function SubtractASI(ASI: TAlgosimInteger): TAlgosimNumber; override;
    function SubtractRat(R: TAlgosimRationalNumber): TAlgosimNumber; override;
    function SubtractFrom(ANum: TAlgosimNumber): TAlgosimNumber; override;

    function MultiplyASR(ASR: TAlgosimRealNumber): TAlgosimNumber; override;
    function MultiplyASC(ASC: TAlgosimComplexNumber): TAlgosimNumber; override;
    function MultiplyASI(ASI: TAlgosimInteger): TAlgosimNumber; override;
    function MultiplyRat(R: TAlgosimRationalNumber): TAlgosimNumber; override;
    function MultiplyTo(ANum: TAlgosimNumber): TAlgosimNumber; override;

    function DivideASR(ASR: TAlgosimRealNumber): TAlgosimNumber; override;
    function DivideASC(ASC: TAlgosimComplexNumber): TAlgosimNumber; override;
    function DivideASI(ASI: TAlgosimInteger): TAlgosimNumber; override;
    function DivideRat(R: TAlgosimRationalNumber): TAlgosimNumber; override;
    function DivideBy(ANum: TAlgosimNumber): TAlgosimNumber; override;

    function RaiseToASR(ASR: TAlgosimRealNumber): TAlgosimNumber; override;
    function RaiseToASC(ASC: TAlgosimComplexNumber): TAlgosimNumber; override;
    function RaiseToASI(ASI: TAlgosimInteger): TAlgosimNumber; override;
    function RaiseToRat(R: TAlgosimRationalNumber): TAlgosimNumber; override;
    function RaiseTo(ANum: TAlgosimNumber): TAlgosimNumber; override;

    function GetMemorySize: UInt64; override;

  public
    constructor Create(AObject: TAlgosimObject); override;
    constructor CreateWithValue(const AValue: TRationalNumber);
    function ToString: string; override;
    function GetAsSingleLineText(const AOptions: TFormatOptions): string; override;
    function AsMemberOfSimplestField: TAlgosimNumber; override;
    function ExplainedOutput(const AOptions: TFormatOptions): string; override;
    property Value: TRationalNumber read FValue write FValue;
    function UnaryMinus: TAlgosimObject; override;
    function Conjugate: TAlgosimNumber; override;
    function Argument: TAlgosimRealNumber; override;
    function RealPart: TAlgosimNumericEntity; override;
    function ImaginaryPart: TAlgosimNumericEntity; override;
    function Abs: TAlgosimNumericEntity; override;
    function Square: TAlgosimNumericEntity; override;
    function Norm(AType: TNormType = ntEuclidean; AParam: Integer = 2;
      AYieldProc: TObjProc = nil): TASR; override;
    function NormSquared: TAlgosimRealNumber; override;
    function Inverse: TAlgosimNumericEntity; override;
    function ScaledBy(const AFactor: TASR): TAlgosimNumericEntity; override;
    function IsPositive(const Eps: Double = 0): Boolean; override;
    function IsNonNegative(const Eps: Double = 0): Boolean; override;
    function IsNegative(const Eps: Double = 0): Boolean; override;
    function IsNonPositive(const Eps: Double = 0): Boolean; override;
    function IsZero(const Eps: Double = 0): Boolean; override;
    function IsNonZero(const Eps: Double = 0): Boolean; override;
    function ComputeFunction(const ARealDomain: TSDD; ARealFcn: TRealFunction;
      AComplexFcn: TComplexFunction): TAlgosimNumber; override;
    function ComputeFunction(ARealFcn: TRealFunction;
      AComplexFcn: TComplexFunction): TAlgosimNumber; override;
    function ToRealNumber: TASR; override;
    function ToComplexNumber: TASC; override;
    function ToRealVector: TRealVector; override;
    function ToComplexVector: TComplexVector; override;
    function ToRealMatrix: TRealMatrix; override;
    function ToComplexMatrix: TComplexMatrix; override;
    function ToBoolean: Boolean; override;
    function TryToASI(out ASI: Int64): Boolean; override;
    function TryToRat(out R: TRationalNumber): Boolean; override;
    function TryToInt32(out Int: Integer): Boolean; override;
    function TryToInt64(out Int: Int64): Boolean; override;
    function TryToASR(out Val: TASR): Boolean; override;
    function TryToASC(out Val: TASC): Boolean; override;
    procedure Increase(AAmount: TASI = 1); override;

    function GetBinaryData(var Buf: PByte; var Len: UInt64): Boolean; override;
    procedure SetBinaryData(const Buf: PByte; const Len: UInt64); override;
  end;

  [AlgosimObject('real number')]
  TAlgosimRealNumber = class(TAlgosimNumber)
  protected

    FValue: TASR;

    function AddASR(ASR: TAlgosimRealNumber): TAlgosimNumber; override;
    function AddASC(ASC: TAlgosimComplexNumber): TAlgosimNumber; override;
    function AddASI(ASI: TAlgosimInteger): TAlgosimNumber; override;
    function AddRat(R: TAlgosimRationalNumber): TAlgosimNumber; override;
    function AddTo(ANum: TAlgosimNumber): TAlgosimNumber; override;

    function SubtractASR(ASR: TAlgosimRealNumber): TAlgosimNumber; override;
    function SubtractASC(ASC: TAlgosimComplexNumber): TAlgosimNumber; override;
    function SubtractASI(ASI: TAlgosimInteger): TAlgosimNumber; override;
    function SubtractRat(R: TAlgosimRationalNumber): TAlgosimNumber; override;
    function SubtractFrom(ANum: TAlgosimNumber): TAlgosimNumber; override;

    function MultiplyASR(ASR: TAlgosimRealNumber): TAlgosimNumber; override;
    function MultiplyASC(ASC: TAlgosimComplexNumber): TAlgosimNumber; override;
    function MultiplyASI(ASI: TAlgosimInteger): TAlgosimNumber; override;
    function MultiplyRat(R: TAlgosimRationalNumber): TAlgosimNumber; override;
    function MultiplyTo(ANum: TAlgosimNumber): TAlgosimNumber; override;

    function DivideASR(ASR: TAlgosimRealNumber): TAlgosimNumber; override;
    function DivideASC(ASC: TAlgosimComplexNumber): TAlgosimNumber; override;
    function DivideASI(ASI: TAlgosimInteger): TAlgosimNumber; override;
    function DivideRat(R: TAlgosimRationalNumber): TAlgosimNumber; override;
    function DivideBy(ANum: TAlgosimNumber): TAlgosimNumber; override;

    function RaiseToASR(ASR: TAlgosimRealNumber): TAlgosimNumber; override;
    function RaiseToASC(ASC: TAlgosimComplexNumber): TAlgosimNumber; override;
    function RaiseToASI(ASI: TAlgosimInteger): TAlgosimNumber; override;
    function RaiseToRat(R: TAlgosimRationalNumber): TAlgosimNumber; override;
    function RaiseTo(ANum: TAlgosimNumber): TAlgosimNumber; override;

    function GetMemorySize: UInt64; override;

  public
    constructor Create(AObject: TAlgosimObject); override;
    constructor CreateWithValue(const AValue: TASR);
    function ToString: string; override;
    function ToSpeech: string; override;
    function GetAsSingleLineText(const AOptions: TFormatOptions): string; override;
    function AsMemberOfSimplestField: TAlgosimNumber; override;
    function ExplainedOutput(const AOptions: TFormatOptions): string; override;
    property Value: TASR read FValue write FValue;
    function UnaryMinus: TAlgosimObject; override;
    function Conjugate: TAlgosimNumber; override;
    function Argument: TAlgosimRealNumber; override;
    function RealPart: TAlgosimNumericEntity; override;
    function ImaginaryPart: TAlgosimNumericEntity; override;
    function Abs: TAlgosimNumericEntity; override;
    function Square: TAlgosimNumericEntity; override;
    function Norm(AType: TNormType = ntEuclidean; AParam: Integer = 2;
      AYieldProc: TObjProc = nil): TASR; override;
    function NormSquared: TAlgosimRealNumber; override;
    function Inverse: TAlgosimNumericEntity; override;
    function ScaledBy(const AFactor: TASR): TAlgosimNumericEntity; override;
    function IsPositive(const Eps: Double = 0): Boolean; override;
    function IsNonNegative(const Eps: Double = 0): Boolean; override;
    function IsNegative(const Eps: Double = 0): Boolean; override;
    function IsNonPositive(const Eps: Double = 0): Boolean; override;
    function IsZero(const Eps: Double = 0): Boolean; override;
    function IsNonZero(const Eps: Double = 0): Boolean; override;
    procedure Defuzz(const Eps: Double = 1E-8); override;
    function ComputeFunction(const ARealDomain: TSDD; ARealFcn: TRealFunction;
      AComplexFcn: TComplexFunction): TAlgosimNumber; override;
    function ComputeFunction(ARealFcn: TRealFunction;
      AComplexFcn: TComplexFunction): TAlgosimNumber; override;
    function ToRealNumber: TASR; override;
    function ToComplexNumber: TASC; override;
    function ToRealVector: TRealVector; override;
    function ToComplexVector: TComplexVector; override;
    function ToRealMatrix: TRealMatrix; override;
    function ToComplexMatrix: TComplexMatrix; override;
    function ToBoolean: Boolean; override;
    function TryToASI(out ASI: Int64): Boolean; override;
    function TryToRat(out R: TRationalNumber): Boolean; override;
    function TryToInt32(out Int: Integer): Boolean; override;
    function TryToInt64(out Int: Int64): Boolean; override;
    function TryToASR(out Val: TASR): Boolean; override;
    function TryToASC(out Val: TASC): Boolean; override;
    procedure Increase(AAmount: TASI = 1); override;

    function GetBinaryData(var Buf: PByte; var Len: UInt64): Boolean; override;
    procedure SetBinaryData(const Buf: PByte; const Len: UInt64); override;
  end;

  [AlgosimObject('complex number', [asoComplex])]
  TAlgosimComplexNumber = class(TAlgosimNumber)
  protected

    FValue: TASC;

    function AddASR(ASR: TAlgosimRealNumber): TAlgosimNumber; override;
    function AddASC(ASC: TAlgosimComplexNumber): TAlgosimNumber; override;
    function AddASI(ASI: TAlgosimInteger): TAlgosimNumber; override;
    function AddRat(R: TAlgosimRationalNumber): TAlgosimNumber; override;
    function AddTo(ANum: TAlgosimNumber): TAlgosimNumber; override;

    function SubtractASR(ASR: TAlgosimRealNumber): TAlgosimNumber; override;
    function SubtractASC(ASC: TAlgosimComplexNumber): TAlgosimNumber; override;
    function SubtractASI(ASI: TAlgosimInteger): TAlgosimNumber; override;
    function SubtractRat(R: TAlgosimRationalNumber): TAlgosimNumber; override;
    function SubtractFrom(ANum: TAlgosimNumber): TAlgosimNumber; override;

    function MultiplyASR(ASR: TAlgosimRealNumber): TAlgosimNumber; override;
    function MultiplyASC(ASC: TAlgosimComplexNumber): TAlgosimNumber; override;
    function MultiplyASI(ASI: TAlgosimInteger): TAlgosimNumber; override;
    function MultiplyRat(R: TAlgosimRationalNumber): TAlgosimNumber; override;
    function MultiplyTo(ANum: TAlgosimNumber): TAlgosimNumber; override;

    function DivideASR(ASR: TAlgosimRealNumber): TAlgosimNumber; override;
    function DivideASC(ASC: TAlgosimComplexNumber): TAlgosimNumber; override;
    function DivideASI(ASI: TAlgosimInteger): TAlgosimNumber; override;
    function DivideRat(R: TAlgosimRationalNumber): TAlgosimNumber; override;
    function DivideBy(ANum: TAlgosimNumber): TAlgosimNumber; override;

    function RaiseToASR(ASR: TAlgosimRealNumber): TAlgosimNumber; override;
    function RaiseToASC(ASC: TAlgosimComplexNumber): TAlgosimNumber; override;
    function RaiseToASI(ASI: TAlgosimInteger): TAlgosimNumber; override;
    function RaiseToRat(R: TAlgosimRationalNumber): TAlgosimNumber; override;
    function RaiseTo(ANum: TAlgosimNumber): TAlgosimNumber; override;

    function GetMemorySize: UInt64; override;

  public
    constructor Create(AObject: TAlgosimObject); override;
    constructor CreateWithValue(const AValue: TASC);
    function ToString: string; override;
    function GetAsSingleLineText(const AOptions: TFormatOptions): string; override;
    function AsMemberOfSimplestField: TAlgosimNumber; override;
    property Value: TASC read FValue write FValue;
    function UnaryMinus: TAlgosimObject; override;
    function Conjugate: TAlgosimNumber; override;
    function Argument: TAlgosimRealNumber; override;
    function RealPart: TAlgosimNumericEntity; override;
    function ImaginaryPart: TAlgosimNumericEntity; override;
    function Abs: TAlgosimNumericEntity; override;
    function Square: TAlgosimNumericEntity; override;
    function Norm(AType: TNormType = ntEuclidean; AParam: Integer = 2;
      AYieldProc: TObjProc = nil): TASR; override;
    function NormSquared: TAlgosimRealNumber; override;
    function Inverse: TAlgosimNumericEntity; override;
    function ScaledBy(const AFactor: TASR): TAlgosimNumericEntity; override;
    function IsZero(const Eps: Double = 0): Boolean; override;
    function IsNonZero(const Eps: Double = 0): Boolean; override;
    procedure Defuzz(const Eps: Double = 1E-8); override;
    function ComputeFunction(const ARealDomain: TSDD; ARealFcn: TRealFunction;
      AComplexFcn: TComplexFunction): TAlgosimNumber; override;
    function ComputeFunction(ARealFcn: TRealFunction;
      AComplexFcn: TComplexFunction): TAlgosimNumber; override;
    function ToRealNumber: TASR; override;
    function ToComplexNumber: TASC; override;
    function ToRealVector: TRealVector; override;
    function ToComplexVector: TComplexVector; override;
    function ToRealMatrix: TRealMatrix; override;
    function ToComplexMatrix: TComplexMatrix; override;
    function ToBoolean: Boolean; override;
    function TryToASI(out ASI: Int64): Boolean; override;
    function TryToRat(out R: TRationalNumber): Boolean; override;
    function TryToInt32(out Int: Integer): Boolean; override;
    function TryToInt64(out Int: Int64): Boolean; override;
    function TryToASR(out Val: TASR): Boolean; override;
    function TryToASC(out Val: TASC): Boolean; override;
    procedure Increase(AAmount: TASI = 1); override;

    function SortClassGetHashCode: Integer; override;
    function GetBinaryData(var Buf: PByte; var Len: UInt64): Boolean; override;
    procedure SetBinaryData(const Buf: PByte; const Len: UInt64); override;
  end;

  [AlgosimObject(
    'string',
    [asoValueContainer, asoOrderedContainer],
    'txt,log,ini,url,reg,inf,xml,xsd,xsl,svg,rss,atom,xul,wml,xhtml,html,htm,' +
    'asp,php,inc,css,pas,dpr,c,h,cpp,cs,java,js,vba,py,pl,bat,cmd,ps1'
  )]
  TAlgosimString = class(TAlgosimObject)
  strict private
    function NumFix(const AStr: string): string;
  protected
    FValue: string;
    FMaxLen: Integer;
    function GetValue(Index: Integer): TAlgosimObject; override;
    function GetValueCount: Integer; override;
    procedure SetValue(Index: Integer; AValue: TAlgosimObject); override;
    procedure AddNumbersToArray(AArray: TAlgosimArray; ALevel: Integer = 0); override;
    function GetMaxLen: Integer; override;
    procedure SetMaxLen(AValue: Integer); override;
    function GetMemorySize: UInt64; override;
  public
    constructor Create(AObject: TAlgosimObject); override;
    constructor CreateWithValue(const AValue: string);
    function ToString: string; override;
    function GetAsSingleLineText(const AOptions: TFormatOptions): string; override;
    function GetAsMultilineText(const AOptions: TFormatOptions): string; override;
    function ToRealNumber: TASR; override;
    function ToComplexNumber: TASC; override;
    function ToNumber: TAlgosimObject; override;
    function ToCharacter: char; override;
    function IndexOfValue(AObj: TAlgosimObject): TAlgosimObject; override;
    function First(N: Integer): TAlgosimObject; overload; override;
    function Last(N: Integer): TAlgosimObject; overload; override;
    function Part(A, B: Integer): TAlgosimObject; overload; override;
    function Part(A: Integer): TAlgosimObject; overload; override;
    function Part(const ARanges: array of TRange): TAlgosimObject; overload; override;
    function Part(const AIndices: array of Integer): TAlgosimObject; overload; override;
    function TryToASC(out Val: TASC): Boolean; override;
    function TryToASR(out Val: TASR): Boolean; override;
    function TryToASI(out ASI: TASI): Boolean; override;
    function TryToRat(out R: TRationalNumber): Boolean; override;
    function TryToInt32(out Int: Integer): Boolean; override;
    function TryToInt64(out Int: Int64): Boolean; override;
    function ToRealVector: TRealVector; override;
    function ToComplexVector: TComplexVector; override;
    function ToVector: TAlgosimVector; override;
    function ToRealMatrix: TRealMatrix; override;
    function ToComplexMatrix: TComplexMatrix; override;
    function ToTable: TASTable; override;
    property Value: string read FValue write FValue;
    procedure Append(AElement: TAlgosimObject); override;
    procedure ExtendWith(AElement: TAlgosimObject); override;
    procedure Insert(AIndex: Integer; AElement: TAlgosimObject); override;
    procedure Truncate(ANewLength: Integer); override;
    procedure Remove(const AIndices: array of Integer); override;
    procedure Swap(Index1, Index2: Integer); override;
    procedure Shuffle; override;
    procedure Reverse; override;
    function RemoveDuplicates: TAlgosimObject; override;
    function RemoveAdjacentDuplicates: TAlgosimObject; override;
    function Count(APredicate: TASOPredicate): Integer; override;
    function ForAll(APredicate: TASOPredicate): Boolean; override;
    function Exists(APredicate: TASOPredicate): Boolean; override;
    function Filter(APredicate: TASOPredicate): TAlgosimObject; override;
    procedure Apply(AFunction: TASOFunction; ACondition: TASOPredicate = nil;
      ALevel: Integer = 1); override;
    procedure Replace(APredicate: TASOPredicate; ANewValue: TAlgosimObject;
      ALevel: Integer = 1); override;
    function Accumulate(AInitialValue: TAlgosimObject;
      AFunction: TASOAccumulator): TAlgosimObject; override;
    function AccumulateStepsList(AInitialValue: TAlgosimObject;
      AFunction: TASOAccumulator): TAlgosimArray; override;
    procedure RemoveFirst(N: Integer = 1); override;
    function RotLeft(N: Integer): TAlgosimObject; override;
    function RotRight(N: Integer): TAlgosimObject; override;
    function ToBoolean: Boolean; override;
    function Equals(Obj: TObject): Boolean; override;
    function SortClassGetHashCode: Integer; override;
    class function SortClass: TSortClass; override;
    class function SortClassCompare(const Left, Right: TAlgosimObject): Integer; override;
    class function SortClassSameObject(const Left, Right: TAlgosimObject;
      const AEpsilon: TASR = 0): Boolean; override;
    class function LoadFromFile(const AFileName: string;
      AEncoding: TEncoding = nil; const AParams: string = ''): TAlgosimObject; override;
    class function Concat(const Args: array of TAlgosimString): TAlgosimString;
    function GetBinaryData(var Buf: PByte; var Len: UInt64): Boolean; override;
    procedure SetBinaryData(const Buf: PByte; const Len: UInt64); override;
    function ToColor: TRGB; override;
  end;

  [AlgosimObject('boolean')]
  TAlgosimBoolean = class(TAlgosimObject)
  public const
    BoolStrs: array[Boolean] of string = ('false', 'true');
  protected var
    FValue: Boolean;
    FTrueSymbol,
    FFalseSymbol: string;
    procedure AddNumbersToArray(AArray: TAlgosimArray; ALevel: Integer = 0); override;
    function GetMemorySize: UInt64; override;
  public
    constructor Create; override;
    constructor Create(AObject: TAlgosimObject); override;
    constructor CreateWithValue(const AValue: Boolean);
    function ToString: string; override;
    function GetAsSingleLineText(const AOptions: TFormatOptions): string; override;
    function TryToASI(out ASI: Int64): Boolean; override;
    function ToRealNumber: TASR; override;
    function ToComplexNumber: TASC; override;
    function ToNumber: TAlgosimObject; override;
    function ToBoolean: Boolean; override;
    procedure SetBinaryData(const Buf: PByte; const Len: UInt64); override;
    property Value: Boolean read FValue write FValue;
    property TrueSymbol: string read FTrueSymbol write FTrueSymbol;
    property FalseSymbol: string read FFalseSymbol write FFalseSymbol;
    function Equals(Obj: TObject): Boolean; override;
    function SortClassGetHashCode: Integer; override;
    class function SortClass: TSortClass; override;
    class function SortClassCompare(const Left, Right: TAlgosimObject): Integer; override;
    class function SortClassSameObject(const Left, Right: TAlgosimObject;
      const AEpsilon: TASR = 0): Boolean; override;

    function GetBinaryData(var Buf: PByte; var Len: UInt64): Boolean; override;
  end;

  [AlgosimObject('success indication')]
  TAlgosimSuccessIndication = class(TAlgosimObject)
  public
  const
    SuccessStr = 'success';
    constructor Create(AObject: TAlgosimObject); override;
    function ToString: string; override;
    function GetAsSingleLineText(const AOptions: TFormatOptions): string;
      override;
    function ToBoolean: Boolean; override;
    class function CreateWithValue(ASuccess: Boolean): TAlgosimObject;
    class function SortClass: TSortClass; override;
    class function SortClassSameObject(const Left: TAlgosimObject;
      const Right: TAlgosimObject; const AEpsilon: Extended = 0): Boolean;
      override;
    function Equals(Obj: TObject): Boolean; override;
    function ToInputString: string; override;
  end;

  [AlgosimObject('control flow object')]
  TAlgosimControlFlowObject = class(TAlgosimObject)
  public
    constructor Create(AObject: TAlgosimObject); override;
    function GetAsSingleLineText(const AOptions: TFormatOptions): string;
      override;
    function ToString: string; override;
    class function SortClass: TSortClass; override;
    class function SortClassSameObject(const Left: TAlgosimObject;
      const Right: TAlgosimObject; const AEpsilon: Extended = 0): Boolean;
      override;
    function Equals(Obj: TObject): Boolean; override;
  end;

  [AlgosimObject('failure object')]
  TAlgosimFailure = class(TAlgosimControlFlowObject)
  strict protected var
    FFailureReason: string;
    FFailureSource: TList<TClass>;
  public
    function ToString: string; override;
    constructor Create; override;
    constructor Create(AObject: TAlgosimObject); override;
    destructor Destroy; override;
    property FailureReason: string read FFailureReason write FFailureReason;
    property Source: TList<TClass> read FFailureSource;
    function ToInputString: string; override;
  end;

  TAlgosimSyntaxError = class(TAlgosimFailure)
  public
    function ToString: string; override;
  end;

  TAlgosimParserError = class(TAlgosimFailure)
    function ToString: string; override;
  end;

  [AlgosimObject('break object')]
  TAlgosimBreak = class(TAlgosimControlFlowObject)
  strict protected var
    FDepth: Integer;
  public
    constructor Create; override;
    constructor CreateWithValue(ADepth: Integer);
    function ToString: string; override;
    property Depth: Integer read FDepth;
    function Consume: Boolean;
  end;

  [AlgosimObject('continue object')]
  TAlgosimContinue = class(TAlgosimControlFlowObject)
  public
    function ToString: string; override;
  end;

  PAlgosimReference = ^TAlgosimReference;

  [AlgosimObject('reference')]
  TAlgosimReference = class(TAlgosimObject)
  strict private
    FGUID: TGUID;
    FSubrefs: TObjectDictionary<string, TAlgosimReference>;
  public
    constructor Create; override;
    constructor Create(AObject: TAlgosimObject); override;
    constructor CreateWithValue(const AGUID: TGUID);
    destructor Destroy; override;
    function Equals(Obj: TObject): Boolean; override;
    function ToString: string; override;
    property GUID: TGUID read FGUID;
    procedure AddSubref(const AName: string; ARef: TAlgosimReference); overload;
    procedure AddSubref(const AName: string; const ARef: TGUID); overload;
    function TryGetSubscriptedRef(ASubscript: TSubscript;
      out AValue: TAlgosimObject): Boolean; override;
    function TryGetSubscriptedValue(ASubscript: TSubscript;
      out AValue: TAlgosimObject): Boolean; override;
    procedure SaveToFile(const AFileName: string; AOptions: TAlgosimStructure;
      AContext: TObject); overload; override;
  end;

  [AlgosimObject('numeric array')]
  TAlgosimNumericArray = class abstract(TAlgosimNumericEntity)
  public const
    ParenLeftUpper = #$239B;
    ParenLeftExtension = #$239C;
    ParenLeftLower = #$239D;
    ParenRightUpper = #$239E;
    ParenRightExtension = #$239F;
    ParenRightLower = #$23A0;
  end;

  [AlgosimObject('vector')]
  TAlgosimVector = class abstract(TAlgosimNumericArray)
  protected
    FMaxLen: Integer;
    function GetDimension: Integer; virtual; abstract;
    procedure SetDimension(const Value: Integer); virtual; abstract;
    function GetElementAsStringFmt(Index: Integer; const AOptions: TFormatOptions): string; virtual; abstract;
    function GetElementAsString(Index: Integer): string; virtual; abstract;
    function GetValueCount: Integer; override;
    function GetPlanarExtent: TSize; override;
    procedure SetPlanarExtent(const Value: TSize); override;
    function GetMaxLen: Integer; override;
    procedure SetMaxLen(AValue: Integer); override;
  public
    constructor Create; override;
    function ToString: string; override;
    function ToSpeech: string; override;
    function GetAsSingleLineText(const AOptions: TFormatOptions): string; override;
    function GetAsMultilineText(const AOptions: TFormatOptions): string; override;
    property Dimension: Integer read GetDimension write SetDimension;

    function Normalized: TAlgosimVector; virtual; abstract;
    function NormalizedIfNonzero: TAlgosimVector; virtual; abstract;

    function WithSpecificValues(AValues: TAlgosimArray): TAlgosimObject; override;

    class function Add(Left, Right: TAlgosimVector): TAlgosimVector; overload;
    class function Subtract(Left, Right: TAlgosimVector): TAlgosimVector; overload;
    class function Add(Left: TAlgosimVector; Right: TAlgosimNumber): TAlgosimVector; overload;
    class function Subtract(Left: TAlgosimVector; Right: TAlgosimNumber): TAlgosimVector; overload;
    class function InnerProduct(Left, Right: TAlgosimVector): TAlgosimNumber;
    class function Multiply(Left: TAlgosimVector; Right: TAlgosimNumber): TAlgosimVector;
    class function Divide(Left: TAlgosimVector; Right: TAlgosimNumber): TAlgosimVector;
    class function CrossProduct(Left, Right: TAlgosimVector): TAlgosimVector;
    class function Angle(Left, Right: TAlgosimVector): TAlgosimNumber;
    class function Concat(const Args: array of TAlgosimVector): TAlgosimVector;

    function Equals(Obj: TObject): Boolean; override;
    class function SortClass: TSortClass; override;
    class function SortClassCompare(const Left, Right: TAlgosimObject): Integer; override;
    class function SortClassSameObject(const Left, Right: TAlgosimObject;
      const AEpsilon: TASR = 0): Boolean; override;
  end;

  [AlgosimObject('real vector', [asoValueContainer, asoOrderedContainer])]
  TAlgosimRealVector = class(TAlgosimVector)
  protected
    FValue: TRealVector;
    function GetElementAsString(Index: Integer): string; override;
    function GetElementAsStringFmt(Index: Integer;
      const AOptions: TFormatOptions): string; override;
    function GetDimension: Integer; override;
    procedure SetDimension(const Value: Integer); override;
    function GetValue(Index: Integer): TAlgosimObject; override;
    procedure AddNumbersToArray(AArray: TAlgosimArray; ALevel: Integer = 0); override;
    procedure SetValue(Index: Integer; AValue: TAlgosimObject); override;
    function GetValueFromPoint(const APoint: TPoint): TAlgosimObject; override;
    procedure SetValueFromPoint(const APoint: TPoint; AValue: TAlgosimObject); override;
    function GetMemorySize: UInt64; override;
  public
    constructor Create(AObject: TAlgosimObject); override;
    constructor CreateWithValue(const AValue: TRealVector);

    property Value: TRealVector read FValue write FValue;

    function N_sum: TAlgosimNumericEntity; override;
    function N_product: TAlgosimNumericEntity; override;
    function N_GeometricMean: TAlgosimNumber; override;
    function N_HarmonicMean: TAlgosimNumber; override;
    function N_max: TAlgosimNumber; override;
    function N_min: TAlgosimNumber; override;

    function UnaryMinus: TAlgosimObject; override;
    function RealPart: TAlgosimNumericEntity; override;
    function ImaginaryPart: TAlgosimNumericEntity; override;
    function Abs: TAlgosimNumericEntity; override;
    function Square: TAlgosimNumericEntity; override;
    function Norm(AType: TNormType = ntEuclidean; AParam: Integer = 2;
      AYieldProc: TObjProc = nil): TASR; override;
    function NormSquared: TAlgosimRealNumber; override;
    function ScaledBy(const AFactor: TASR): TAlgosimNumericEntity; override;
    function IsPositive(const Eps: Double = 0): Boolean; override;
    function IsNonNegative(const Eps: Double = 0): Boolean; override;
    function IsNegative(const Eps: Double = 0): Boolean; override;
    function IsNonPositive(const Eps: Double = 0): Boolean; override;
    function IsZero(const Eps: Double = 0): Boolean; override;
    function IsNonZero(const Eps: Double = 0): Boolean; override;
    function Transpose: TAlgosimMatrix; override;
    function ConjugateTranspose: TAlgosimMatrix; override;

    procedure Defuzz(const Eps: Double = 1E-8); override;

    function ToComplexNumber: TASC; override;
    function ToRealVector: TRealVector; override;
    function ToComplexVector: TComplexVector; override;
    function ToRealMatrix: TRealMatrix; override;
    function ToComplexMatrix: TComplexMatrix; override;

    function AsRealVector: TRealVector; override;

    function Normalized: TAlgosimVector; override;
    function NormalizedIfNonzero: TAlgosimVector; override;

    function First(N: Integer): TAlgosimObject; overload; override;
    function Last(N: Integer): TAlgosimObject; overload; override;
    function Part(A, B: Integer): TAlgosimObject; overload; override;
    function Part(A: Integer): TAlgosimObject; overload; override;
    function Part(const ARanges: array of TRange): TAlgosimObject; overload; override;
    function Part(const AIndices: array of Integer): TAlgosimObject; overload; override;

    procedure Append(AElement: TAlgosimObject); override;
    procedure ExtendWith(AElement: TAlgosimObject); override;
    procedure Insert(AIndex: Integer; AElement: TAlgosimObject); override;
    procedure Remove(const AIndices: array of Integer); override;
    procedure Truncate(ANewLength: Integer); override;
    procedure Swap(Index1, Index2: Integer); override;

    procedure Sort; overload; override;
    procedure Sort(AComparer: IComparer<TAlgosimObject>); overload; override;
    procedure Sort(AComparer: IComparer<TASR>); overload; override;
    procedure Sort(AComparer: IComparer<TASC>); overload; override;

    procedure Shuffle; override;
    procedure Reverse; override;

    function RemoveDuplicates: TAlgosimObject; override;
    function RemoveDuplicatesEps(const Epsilon: TASR = 0): TAlgosimObject; override;
    function RemoveAdjacentDuplicates: TAlgosimObject; override;
    function RemoveAdjacentDuplicatesEps(const Epsilon: TASR = 0): TAlgosimObject; override;

    function Frequencies: TAlgosimArray; override;
    function FrequenciesEps(const Epsilon: TASR = 0): TAlgosimArray; override;
    function CollapseSequences: TAlgosimArray; override;
    function CollapseSequencesEps(const Epsilon: TASR = 0): TAlgosimArray; override;

    function IndexOfValue(AObj: TAlgosimObject): TAlgosimObject; override;
    function IndexOfValueEps(AObj: TAlgosimObject;
      const AEpsilon: TASR = 0): TAlgosimObject; override;
    function IndicesOfValue(AObj: TAlgosimObject): TAlgosimArray; override;
    function IndicesOfValueEps(AObj: TAlgosimObject;
      const AEpsilon: TASR = 0): TAlgosimArray; override;
    function IndicesOf(APredicate: TASOPredicate): TAlgosimArray; override;
    function Count(APredicate: TASOPredicate): Integer; override;
    function ForAll(APredicate: TASOPredicate): Boolean; override;
    function Exists(APredicate: TASOPredicate): Boolean; override;
    function Filter(APredicate: TASOPredicate): TAlgosimObject; override;
    procedure Apply(AFunction: TASOFunction; ACondition: TASOPredicate = nil;
      ALevel: Integer = 1); override;
    procedure Replace(APredicate: TASOPredicate; ANewValue: TAlgosimObject;
      ALevel: Integer = 1); override;
    function Accumulate(AInitialValue: TAlgosimObject;
      AFunction: TASOAccumulator): TAlgosimObject; override;
    function AccumulateStepsList(AInitialValue: TAlgosimObject;
      AFunction: TASOAccumulator): TAlgosimArray; override;
    procedure RemoveFirst(N: Integer = 1); override;

    function RotLeft(N: Integer): TAlgosimObject; override;
    function RotRight(N: Integer): TAlgosimObject; override;

    function SortClassGetHashCode: Integer; override;

    function GetBinaryData(var Buf: PByte; var Len: UInt64): Boolean; override;
  end;

  [AlgosimObject('complex vector', [asoValueContainer, asoOrderedContainer, asoComplex])]
  TAlgosimComplexVector = class(TAlgosimVector)
  protected
    FValue: TComplexVector;
    function GetElementAsString(Index: Integer): string; override;
    function GetElementAsStringFmt(Index: Integer;
      const AOptions: TFormatOptions): string; override;
    function GetDimension: Integer; override;
    procedure SetDimension(const Value: Integer); override;
    function GetValue(Index: Integer): TAlgosimObject; override;
    procedure AddNumbersToArray(AArray: TAlgosimArray; ALevel: Integer = 0); override;
    procedure SetValue(Index: Integer; AValue: TAlgosimObject); override;
    function GetValueFromPoint(const APoint: TPoint): TAlgosimObject; override;
    procedure SetValueFromPoint(const APoint: TPoint; AValue: TAlgosimObject); override;
    function GetMemorySize: UInt64; override;
  public
    constructor Create(AObject: TAlgosimObject); override;
    constructor CreateWithValue(const AValue: TComplexVector);

    property Value: TComplexVector read FValue write FValue;

    function N_sum: TAlgosimNumericEntity; override;
    function N_product: TAlgosimNumericEntity; override;
    function N_GeometricMean: TAlgosimNumber; override;
    function N_HarmonicMean: TAlgosimNumber; override;

    function UnaryMinus: TAlgosimObject; override;
    function RealPart: TAlgosimNumericEntity; override;
    function ImaginaryPart: TAlgosimNumericEntity; override;
    function Abs: TAlgosimNumericEntity; override;
    function Square: TAlgosimNumericEntity; override;
    function Norm(AType: TNormType = ntEuclidean; AParam: Integer = 2;
      AYieldProc: TObjProc = nil): TASR; override;
    function NormSquared: TAlgosimRealNumber; override;
    function ScaledBy(const AFactor: TASR): TAlgosimNumericEntity; override;

    function IsZero(const Eps: Double = 0): Boolean; override;
    function IsNonZero(const Eps: Double = 0): Boolean; override;

    function Transpose: TAlgosimMatrix; override;
    function ConjugateTranspose: TAlgosimMatrix; override;

    procedure Defuzz(const Eps: Double = 1E-8); override;

    function ToRealVector: TRealVector; override;
    function ToComplexVector: TComplexVector; override;
    function ToRealMatrix: TRealMatrix; override;
    function ToComplexMatrix: TComplexMatrix; override;

    function AsComplexVector: TComplexVector; override;

    function Normalized: TAlgosimVector; override;
    function NormalizedIfNonzero: TAlgosimVector; override;

    function First(N: Integer): TAlgosimObject; overload; override;
    function Last(N: Integer): TAlgosimObject; overload; override;
    function Part(A, B: Integer): TAlgosimObject; overload; override;
    function Part(A: Integer): TAlgosimObject; overload; override;
    function Part(const ARanges: array of TRange): TAlgosimObject; overload; override;
    function Part(const AIndices: array of Integer): TAlgosimObject; overload; override;

    procedure Append(AElement: TAlgosimObject); override;
    procedure ExtendWith(AElement: TAlgosimObject); override;
    procedure Insert(AIndex: Integer; AElement: TAlgosimObject); override;
    procedure Remove(const AIndices: array of integer); override;
    procedure Truncate(ANewLength: Integer); override;
    procedure Swap(Index1, Index2: Integer); override;

    // procedure Sort; overload; override; missing because there is no
    // 'natural' way to compare complex numbers
    procedure Sort(AComparer: IComparer<TAlgosimObject>); overload; override;
    procedure Sort(AComparer: IComparer<TASR>); overload; override;
    procedure Sort(AComparer: IComparer<TASC>); overload; override;

    procedure Shuffle; override;
    procedure Reverse; override;

    function RemoveDuplicates: TAlgosimObject; override;
    function RemoveDuplicatesEps(const Epsilon: TASR = 0): TAlgosimObject; override;
    function RemoveAdjacentDuplicates: TAlgosimObject; override;
    function RemoveAdjacentDuplicatesEps(const Epsilon: TASR = 0): TAlgosimObject; override;

    function Frequencies: TAlgosimArray; override;
    function FrequenciesEps(const Epsilon: TASR = 0): TAlgosimArray; override;
    function CollapseSequences: TAlgosimArray; override;
    function CollapseSequencesEps(const Epsilon: TASR = 0): TAlgosimArray; override;

    function IndexOfValue(AObj: TAlgosimObject): TAlgosimObject; override;
    function IndexOfValueEps(AObj: TAlgosimObject;
      const AEpsilon: TASR = 0): TAlgosimObject; override;
    function IndicesOfValue(AObj: TAlgosimObject): TAlgosimArray; override;
    function IndicesOfValueEps(AObj: TAlgosimObject;
      const AEpsilon: TASR = 0): TAlgosimArray; override;
    function IndicesOf(APredicate: TASOPredicate): TAlgosimArray; override;
    function Count(APredicate: TASOPredicate): Integer; override;
    function ForAll(APredicate: TASOPredicate): Boolean; override;
    function Exists(APredicate: TASOPredicate): Boolean; override;
    function Filter(APredicate: TASOPredicate): TAlgosimObject; override;
    procedure Apply(AFunction: TASOFunction; ACondition: TASOPredicate = nil;
      ALevel: Integer = 1); override;
    procedure Replace(APredicate: TASOPredicate; ANewValue: TAlgosimObject;
      ALevel: Integer = 1); override;
    function Accumulate(AInitialValue: TAlgosimObject;
      AFunction: TASOAccumulator): TAlgosimObject; override;
    function AccumulateStepsList(AInitialValue: TAlgosimObject;
      AFunction: TASOAccumulator): TAlgosimArray; override;
    procedure RemoveFirst(N: Integer = 1); override;

    function RotLeft(N: Integer): TAlgosimObject; override;
    function RotRight(N: Integer): TAlgosimObject; override;

    function SortClassGetHashCode: Integer; override;

    function GetBinaryData(var Buf: PByte; var Len: UInt64): Boolean; override;
  end;

  [AlgosimObject('matrix')]
  TAlgosimMatrix = class abstract(TAlgosimNumericArray)
  protected
    FMaxLen: Integer;
    function GetDimension: TMatrixSize; virtual; abstract;
    procedure SetDimension(const Value: TMatrixSize); virtual; abstract;
    function GetElementAsStringFmt(Y, X: Integer;
      const AOptions: TFormatOptions): string; virtual; abstract;
    function GetElementAsString(Y, X: Integer): string; virtual; abstract;
    function GetValueCount: Integer; override;
    function GetPlanarExtent: TSize; override;
    procedure SetPlanarExtent(const Value: TSize); override;
    function GetMaxLen: Integer; override;
    procedure SetMaxLen(AValue: Integer); override;
  public
    function ToString: string; override;
    function ToSpeech: string; override;
    function GetAsSingleLineText(const AOptions: TFormatOptions): string; override;
    function GetAsMultilineText(const AOptions: TFormatOptions): string; override;
    property Dimension: TMatrixSize read GetDimension write SetDimension;

    function HermitianSquare: TAlgosimMatrix; virtual; abstract;
    function Modulus: TAlgosimMatrix; virtual; abstract;
    function Determinant: TAlgosimNumber; virtual; abstract;
    function Trace: TAlgosimNumber; virtual; abstract;
    function Rank: Integer; virtual; abstract;
    function Nullity: Integer; virtual; abstract;
    function ConditionNumber(p: Integer = 2): TASR; virtual; abstract;
    function IsSingular: Boolean; virtual; abstract;
    function DeletedAbsoluteRowSum(Index: Integer): TASR; virtual; abstract;
    function RowEchelonForm: TAlgosimMatrix; virtual; abstract;
    function ReducedRowEchelonForm: TAlgosimMatrix; virtual; abstract;
    function NumZeroRows(const AEpsilon: TASR = 0): Integer; virtual; abstract;
    function NumTrailingZeroRows(const AEpsilon: TASR = 0): Integer; virtual; abstract;
    function GramSchmidt: TAlgosimMatrix; virtual; abstract;
    function ColumnSpaceBasis: TAlgosimMatrix; virtual; abstract;
    function SimilarHessenberg: TAlgosimMatrix; virtual; abstract;
    function Eigenvalues: TComplexVector; virtual; abstract;
    function Eigenvectors: TAlgosimArray; virtual; abstract;
    function SpectralRadius: TASR; virtual; abstract;
    function SingularValues: TRealVector; virtual; abstract;
    function Vectorization: TAlgosimVector; virtual; abstract;
    function Minor(Row, Col: Integer): TAlgosimNumber; virtual; abstract;
    function Cofactor(Row, Col: Integer): TAlgosimNumber; virtual; abstract;
    function CofactorMatrix: TAlgosimMatrix; virtual; abstract;
    function AdjugateMatrix: TAlgosimMatrix; virtual; abstract;

    function MainDiagonal: TAlgosimVector; virtual; abstract;
    function Subdiagonal: TAlgosimVector; virtual; abstract;
    function Superdiagonal: TAlgosimVector; virtual; abstract;
    function Antidiagonal: TAlgosimVector; virtual; abstract;

    function WithSpecificValues(AValues: TAlgosimArray): TAlgosimObject; override;

    class function Add(Left, Right: TAlgosimMatrix): TAlgosimMatrix; overload;
    class function Subtract(Left, Right: TAlgosimMatrix): TAlgosimMatrix; overload;
    class function Add(Left: TAlgosimMatrix; Right: TAlgosimNumber): TAlgosimMatrix; overload;
    class function Subtract(Left: TAlgosimMatrix; Right: TAlgosimNumber): TAlgosimMatrix; overload;
    class function Multiply(Left, Right: TAlgosimMatrix): TAlgosimMatrix; overload;
    class function Multiply(Left: TAlgosimMatrix; Right: TAlgosimVector): TAlgosimVector; overload;
    class function Multiply(Left: TAlgosimMatrix; Right: TAlgosimNumber): TAlgosimMatrix; overload;
    class function Divide(Left: TAlgosimMatrix; Right: TAlgosimNumber): TAlgosimMatrix;
    function Power(AExp: Integer): TAlgosimMatrix; virtual; abstract;

    function Equals(Obj: TObject): Boolean; override;
    class function SortClass: TSortClass; override;
    class function SortClassCompare(const Left, Right: TAlgosimObject): Integer; override;
    class function SortClassSameObject(const Left, Right: TAlgosimObject;
      const AEpsilon: TASR = 0): Boolean; override;
  end;

  [AlgosimObject('real matrix', [asoValueContainer, asoOrderedContainer, asoPlanarContainer])]
  TAlgosimRealMatrix = class(TAlgosimMatrix)
  protected
    FValue: TRealMatrix;
    function GetElementAsString(Y, X: Integer): string; override;
    function GetElementAsStringFmt(Y, X: Integer;
      const AOptions: TFormatOptions): string; override;
    function GetDimension: TMatrixSize; override;
    procedure SetDimension(const Value: TMatrixSize); override;
    function GetValue(Index: Integer): TAlgosimObject; override;
    function GetValueFromPoint(const APoint: TPoint): TAlgosimObject; override;
    procedure AddNumbersToArray(AArray: TAlgosimArray; ALevel: Integer = 0); override;
    procedure SetValue(Index: Integer; AValue: TAlgosimObject); override;
    procedure SetValueFromPoint(const APoint: TPoint; AValue: TAlgosimObject); override;
    function GetMemorySize: UInt64; override;
  public
    constructor Create(AObject: TAlgosimObject); override;
    constructor CreateWithValue(const AValue: TRealMatrix);

    property Value: TRealMatrix read FValue write FValue;

    function N_sum: TAlgosimNumericEntity; override;
    function N_product: TAlgosimNumericEntity; override;
    function N_GeometricMean: TAlgosimNumber; override;
    function N_HarmonicMean: TAlgosimNumber; override;
    function N_max: TAlgosimNumber; override;
    function N_min: TAlgosimNumber; override;

    function UnaryMinus: TAlgosimObject; override;
    function Power(AExp: Integer): TAlgosimMatrix; override;
    function Transpose: TAlgosimMatrix; override;
    function ConjugateTranspose: TAlgosimMatrix; override;
    function HermitianSquare: TAlgosimMatrix; override;
    function Modulus: TAlgosimMatrix; override;
    function Determinant: TAlgosimNumber; override;
    function Trace: TAlgosimNumber; override;
    function Rank: Integer; override;
    function Nullity: Integer; override;
    function ConditionNumber(p: Integer = 2): TASR; override;
    function IsSingular: Boolean; override;
    function DeletedAbsoluteRowSum(Index: Integer): TASR; override;
    function RowEchelonForm: TAlgosimMatrix; override;
    function ReducedRowEchelonForm: TAlgosimMatrix; override;
    function NumZeroRows(const AEpsilon: TASR = 0): Integer; override;
    function NumTrailingZeroRows(const AEpsilon: TASR = 0): Integer; override;
    function GramSchmidt: TAlgosimMatrix; override;
    function ColumnSpaceBasis: TAlgosimMatrix; override;
    function SimilarHessenberg: TAlgosimMatrix; override;
    function Eigenvalues: TComplexVector; override;
    function Eigenvectors: TAlgosimArray; override;
    function SpectralRadius: TASR; override;
    function SingularValues: TRealVector; override;
    function Vectorization: TAlgosimVector; override;
    function Minor(Row, Col: Integer): TAlgosimNumber; override;
    function Cofactor(Row, Col: Integer): TAlgosimNumber; override;
    function CofactorMatrix: TAlgosimMatrix; override;
    function AdjugateMatrix: TAlgosimMatrix; override;
    function RealPart: TAlgosimNumericEntity; override;
    function ImaginaryPart: TAlgosimNumericEntity; override;
    function Abs: TAlgosimNumericEntity; override;
    function Square: TAlgosimNumericEntity; override;
    function Norm(AType: TNormType = ntEuclidean; AParam: Integer = 2;
      AYieldProc: TObjProc = nil): TASR; override;
    function NormSquared: TAlgosimRealNumber; override;
    function Inverse: TAlgosimNumericEntity; override;
    function ScaledBy(const AFactor: TASR): TAlgosimNumericEntity; override;
    function IsPositive(const Eps: Double = 0): Boolean; override;
    function IsNonNegative(const Eps: Double = 0): Boolean; override;
    function IsNegative(const Eps: Double = 0): Boolean; override;
    function IsNonPositive(const Eps: Double = 0): Boolean; override;
    function IsZero(const Eps: Double = 0): Boolean; override;
    function IsNonZero(const Eps: Double = 0): Boolean; override;

    procedure Defuzz(const Eps: Double = 1E-8); override;

    function TryGetSubscriptedValue(ASubscript: TSubscript;
      out AValue: TAlgosimObject): Boolean; override;
    procedure SetSubscript(ASubscript: TSubscript;
      AValue: TAlgosimObject); override;

    function ToRealVector: TRealVector; override;
    function ToComplexVector: TComplexVector; override;
    function ToRealMatrix: TRealMatrix; override;
    function ToComplexMatrix: TComplexMatrix; override;

    function AsRealVector: TRealVector; override;
    function AsRealMatrix: TRealMatrix; override;

    function Part2d(const AIndicesX, AIndicesY: array of Integer): TAlgosimObject; overload; override;

    procedure Sort; overload; override;
    procedure Sort(AComparer: IComparer<TAlgosimObject>); overload; override;
    procedure Sort(AComparer: IComparer<TASR>); overload; override;
    procedure Sort(AComparer: IComparer<TASC>); overload; override;

    procedure Shuffle; override;
    procedure Reverse; override;

    function Frequencies: TAlgosimArray; override;
    function FrequenciesEps(const Epsilon: TASR = 0): TAlgosimArray; override;

    function IndexOfValue(AObj: TAlgosimObject): TAlgosimObject; override;
    function IndexOfValueEps(AObj: TAlgosimObject;
      const AEpsilon: TASR = 0): TAlgosimObject; override;
    function IndicesOfValue(AObj: TAlgosimObject): TAlgosimArray; override;
    function IndicesOfValueEps(AObj: TAlgosimObject;
      const AEpsilon: TASR = 0): TAlgosimArray; override;
    function IndicesOf(APredicate: TASOPredicate): TAlgosimArray; override;
    function Count(APredicate: TASOPredicate): Integer; override;
    function ForAll(APredicate: TASOPredicate): Boolean; override;
    function Exists(APredicate: TASOPredicate): Boolean; override;
    procedure Apply(AFunction: TASOFunction; ACondition: TASOPredicate = nil;
      ALevel: Integer = 1); override;
    procedure Replace(APredicate: TASOPredicate; ANewValue: TAlgosimObject;
      ALevel: Integer = 1); override;
    function Accumulate(AInitialValue: TAlgosimObject;
      AFunction: TASOAccumulator): TAlgosimObject; override;
    function AccumulateStepsList(AInitialValue: TAlgosimObject;
      AFunction: TASOAccumulator): TAlgosimArray; override;

    function GetBinaryData(var Buf: PByte; var Len: UInt64): Boolean; override;

    function SortClassGetHashCode: Integer; override;

    function MainDiagonal: TAlgosimVector; override;
    function Subdiagonal: TAlgosimVector; override;
    function Superdiagonal: TAlgosimVector; override;
    function Antidiagonal: TAlgosimVector; override;
  end;

  [AlgosimObject('complex matrix', [asoValueContainer, asoOrderedContainer, asoPlanarContainer, asoComplex])]
  TAlgosimComplexMatrix = class(TAlgosimMatrix)
  protected
    FValue: TComplexMatrix;
    function GetElementAsString(Y, X: Integer): string; override;
    function GetElementAsStringFmt(Y, X: Integer;
      const AOptions: TFormatOptions): string; override;
    function GetDimension: TMatrixSize; override;
    procedure SetDimension(const Value: TMatrixSize); override;
    function GetValue(Index: Integer): TAlgosimObject; override;
    function GetValueFromPoint(const APoint: TPoint): TAlgosimObject; override;
    procedure AddNumbersToArray(AArray: TAlgosimArray; ALevel: Integer = 0); override;
    procedure SetValue(Index: Integer; AValue: TAlgosimObject); override;
    procedure SetValueFromPoint(const APoint: TPoint; AValue: TAlgosimObject); override;
    function GetMemorySize: UInt64; override;
  public
    constructor Create(AObject: TAlgosimObject); override;
    constructor CreateWithValue(const AValue: TComplexMatrix);

    property Value: TComplexMatrix read FValue write FValue;

    function N_sum: TAlgosimNumericEntity; override;
    function N_product: TAlgosimNumericEntity; override;
    function N_GeometricMean: TAlgosimNumber; override;
    function N_HarmonicMean: TAlgosimNumber; override;

    function UnaryMinus: TAlgosimObject; override;
    function Power(AExp: Integer): TAlgosimMatrix; override;
    function Transpose: TAlgosimMatrix; override;
    function ConjugateTranspose: TAlgosimMatrix; override;
    function HermitianSquare: TAlgosimMatrix; override;
    function Modulus: TAlgosimMatrix; override;
    function Determinant: TAlgosimNumber; override;
    function Trace: TAlgosimNumber; override;
    function Rank: Integer; override;
    function Nullity: Integer; override;
    function ConditionNumber(p: Integer = 2): TASR; override;
    function IsSingular: Boolean; override;
    function DeletedAbsoluteRowSum(Index: Integer): TASR; override;
    function RowEchelonForm: TAlgosimMatrix; override;
    function ReducedRowEchelonForm: TAlgosimMatrix; override;
    function NumZeroRows(const AEpsilon: TASR = 0): Integer; override;
    function NumTrailingZeroRows(const AEpsilon: TASR = 0): Integer; override;
    function GramSchmidt: TAlgosimMatrix; override;
    function ColumnSpaceBasis: TAlgosimMatrix; override;
    function SimilarHessenberg: TAlgosimMatrix; override;
    function Eigenvalues: TComplexVector; override;
    function Eigenvectors: TAlgosimArray; override;
    function SpectralRadius: TASR; override;
    function SingularValues: TRealVector; override;
    function Vectorization: TAlgosimVector; override;
    function Minor(Row, Col: Integer): TAlgosimNumber; override;
    function Cofactor(Row, Col: Integer): TAlgosimNumber; override;
    function CofactorMatrix: TAlgosimMatrix; override;
    function AdjugateMatrix: TAlgosimMatrix; override;
    function RealPart: TAlgosimNumericEntity; override;
    function ImaginaryPart: TAlgosimNumericEntity; override;
    function Abs: TAlgosimNumericEntity; override;
    function Square: TAlgosimNumericEntity; override;
    function Norm(AType: TNormType = ntEuclidean; AParam: Integer = 2;
      AYieldProc: TObjProc = nil): TASR; override;
    function NormSquared: TAlgosimRealNumber; override;
    function Inverse: TAlgosimNumericEntity; override;
    function ScaledBy(const AFactor: TASR): TAlgosimNumericEntity; override;
    function IsZero(const Eps: Double = 0): Boolean; override;
    function IsNonZero(const Eps: Double = 0): Boolean; override;

    procedure Defuzz(const Eps: Double = 1E-8); override;

    function TryGetSubscriptedValue(ASubscript: TSubscript;
      out AValue: TAlgosimObject): Boolean; override;
    procedure SetSubscript(ASubscript: TSubscript;
      AValue: TAlgosimObject); override;

    function ToRealVector: TRealVector; override;
    function ToComplexVector: TComplexVector; override;
    function ToRealMatrix: TRealMatrix; override;
    function ToComplexMatrix: TComplexMatrix; override;

    function AsComplexVector: TComplexVector; override;
    function AsComplexMatrix: TComplexMatrix; override;

    function Part2d(const AIndicesX, AIndicesY: array of integer): TAlgosimObject; overload; override;

    // procedure Sort; overload; override; missing because there is no
    // 'natural' way to compare complex numbers
    procedure Sort(AComparer: IComparer<TAlgosimObject>); overload; override;
    procedure Sort(AComparer: IComparer<TASR>); overload; override;
    procedure Sort(AComparer: IComparer<TASC>); overload; override;

    procedure Shuffle; override;
    procedure Reverse; override;

    function Frequencies: TAlgosimArray; override;
    function FrequenciesEps(const Epsilon: TASR = 0): TAlgosimArray; override;

    function IndexOfValue(AObj: TAlgosimObject): TAlgosimObject; override;
    function IndexOfValueEps(AObj: TAlgosimObject;
      const AEpsilon: TASR = 0): TAlgosimObject; override;
    function IndicesOfValue(AObj: TAlgosimObject): TAlgosimArray; override;
    function IndicesOfValueEps(AObj: TAlgosimObject;
      const AEpsilon: TASR = 0): TAlgosimArray; override;
    function IndicesOf(APredicate: TASOPredicate): TAlgosimArray; override;
    function Count(APredicate: TASOPredicate): Integer; override;
    function ForAll(APredicate: TASOPredicate): Boolean; override;
    function Exists(APredicate: TASOPredicate): Boolean; override;
    procedure Apply(AFunction: TASOFunction; ACondition: TASOPredicate = nil;
      ALevel: Integer = 1); override;
    procedure Replace(APredicate: TASOPredicate; ANewValue: TAlgosimObject;
      ALevel: Integer = 1); override;
    function Accumulate(AInitialValue: TAlgosimObject;
      AFunction: TASOAccumulator): TAlgosimObject; override;
    function AccumulateStepsList(AInitialValue: TAlgosimObject;
      AFunction: TASOAccumulator): TAlgosimArray; override;

    function GetBinaryData(var Buf: PByte; var Len: UInt64): Boolean; override;

    function SortClassGetHashCode: Integer; override;

    function MainDiagonal: TAlgosimVector; override;
    function Subdiagonal: TAlgosimVector; override;
    function Superdiagonal: TAlgosimVector; override;
    function Antidiagonal: TAlgosimVector; override;
  end;

  [AlgosimObject('pixmap', [asoValueContainer, asoOrderedContainer, asoPlanarContainer],
    'bmp,png,gif,jpg')]
  TAlgosimPixmap = class(TAlgosimObject)
  protected
    FValue: TASPixmap;
    function GetValue(Index: Integer): TAlgosimObject; override;
    function GetValueCount: Integer; override;
    function GetValueFromPoint(const APoint: TPoint): TAlgosimObject; override;
    function GetPlanarExtent: TSize; override;
    procedure SetPlanarExtent(const Value: TSize); override;
    procedure SetValue(Index: Integer; AValue: TAlgosimObject); override;
    procedure SetValueFromPoint(const APoint: TPoint; AValue: TAlgosimObject); override;
    procedure AddNumbersToArray(AArray: TAlgosimArray; ALevel: Integer = 0); override;
    function GetMemorySize: UInt64; override;
  public
    constructor Create(AObject: TAlgosimObject); override;
    constructor CreateWithValue(const AValue: TASPixmap);
    function GetAsSingleLineText(const AOptions: TFormatOptions): string; override;
    procedure SaveToFile(const AFileName: string); override;
    procedure SaveToFile(ADlgOwner: TComponent;
      const ADefFileName: string = ''); override;
    class function LoadFromFile(const AFileName: string;
      AEncoding: TEncoding = nil; const AParams: string = ''): TAlgosimObject; override;
    procedure CopyToClipboard; override;
    function ToString: string; override;
    property Value: TASPixmap read FValue write FValue;

    function ToRealMatrix: TRealMatrix; override;

    function Frequencies: TAlgosimArray; override;

    function Count(APredicate: TASOPredicate): Integer; override;
    function ForAll(APredicate: TASOPredicate): Boolean; override;
    function Exists(APredicate: TASOPredicate): Boolean; override;
    procedure Apply(AFunction: TASOFunction; ACondition: TASOPredicate = nil;
      ALevel: Integer = 1); override;
    procedure Replace(APredicate: TASOPredicate; ANewValue: TAlgosimObject;
      ALevel: Integer = 1); override;
    function Accumulate(AInitialValue: TAlgosimObject;
      AFunction: TASOAccumulator): TAlgosimObject; override;
    function AccumulateStepsList(AInitialValue: TAlgosimObject;
      AFunction: TASOAccumulator): TAlgosimArray; override;

    function WithSpecificValues(AValues: TAlgosimArray): TAlgosimObject; override;

    procedure Shuffle; override;
    procedure Reverse; override;

    function GetBinaryData(var Buf: PByte; var Len: UInt64): Boolean; override;
    function Equals(Obj: TObject): Boolean; override;
    function SortClassGetHashCode: Integer; override;
    class function SortClass: TSortClass; override;
    class function SortClassCompare(const Left, Right: TAlgosimObject): Integer; override;
    class function SortClassSameObject(const Left, Right: TAlgosimObject;
      const AEpsilon: TASR = 0): Boolean; override;
  end;

  [AlgosimObject('sound', [], 'wav')]
  TAlgosimSound = class(TAlgosimObject)
  protected
    FValue: TASSound;
    function GetMemorySize: UInt64; override;
  public
    constructor Create(AObject: TAlgosimObject); override;
    constructor CreateWithValue(const AValue: TASSound);
    function GetAsSingleLineText(const AOptions: TFormatOptions): string; override;
    procedure SaveToFile(const AFileName: string); override;
    class function LoadFromFile(const AFileName: string;
      AEncoding: TEncoding = nil; const AParams: string = ''): TAlgosimObject; override;
    procedure Invoke; override;
    function ToString: string; override;
    procedure CopyToClipboard; override;
    property Value: TASSound read FValue write FValue;
    function ToRealVector: TRealVector; override;
    procedure Reverse; override;

    function GetBinaryData(var Buf: PByte; var Len: UInt64): Boolean; override;
    function Equals(Obj: TObject): Boolean; override;
    function SortClassGetHashCode: Integer; override;
    class function SortClass: TSortClass; override;
    class function SortClassCompare(const Left, Right: TAlgosimObject): Integer; override;
    class function SortClassSameObject(const Left, Right: TAlgosimObject;
      const AEpsilon: TASR = 0): Boolean; override;
  end;

  [AlgosimObject('array', [asoObjectContainer, asoOrderedContainer])]
  TAlgosimArray = class(TAlgosimObject)
  protected
    FElements: TArray<TAlgosimObject>;
    FActualLength: Integer;
    FOwnsObjects: Boolean; // only used when destroying
    FMaxLen: Integer;
    function GetElement(Index: Integer): TAlgosimObject; override;
    function GetElementCount: Integer; override;
    procedure SetElement(Index: Integer; Value: TAlgosimObject); override;
    procedure Grow;
    function GetCapacity: Integer; override;
    procedure SetCapacity(const Value: Integer); override;
    procedure SetElementCount(const AValue: Integer);
    function GetMaxLen: Integer; override;
    procedure SetMaxLen(AValue: Integer); override;
    function Is2D(out Size: TSize): Boolean;
    function GetMemorySize: UInt64; override;
  public
    destructor Destroy; override;
    constructor Create; override;
    constructor CreateUninitialized(Size: Integer);
    constructor Create(AObject: TAlgosimObject); override;
    constructor CreateWithValue(const AElements: array of TAlgosimObject); overload;
    constructor CreateWithValue(const AElements: array of Integer); overload;
    constructor CreateWithValue(const AElements: array of TASI); overload;
    constructor CreateWithValue(const AElements: array of TASR); overload;
    constructor CreateWithValue(const AElements: array of TASC); overload;
    constructor CreateWithValue(const AElements: array of Boolean); overload;
    constructor CreateWithValue(const AElements: array of string); overload;
    constructor CreateWithValue(const AElements: array of Char); overload;
    function ToString: string; override;
    function ToSpeech: string; override;
    function ToStringArray: TArray<string>;
    function ToRealMatrix: TRealMatrix; override;
    function ToComplexMatrix: TComplexMatrix; override;
    function ToTable: TASTable; override;
    property OwnsObjects: Boolean read FOwnsObjects write FOwnsObjects;
    function GetAsSingleLineText(const AOptions: TFormatOptions): string; override;
    function GetAsMultilineText(const AOptions: TFormatOptions): string; override;
    function Equals(Obj: TObject): Boolean; override;
    function SortClassGetHashCode: Integer; override;
    function AddElement(const AElement: TAlgosimObject): Boolean; override;
    property Elements[Index: Integer]: TAlgosimObject read GetElement
      write SetElement; default;
    property ElementCount: Integer read FActualLength write SetElementCount;
    procedure TrimExcess; override;
    procedure Append(AElement: TAlgosimObject); override;
    procedure ExtendWith(AElement: TAlgosimObject); override;
    class function Concat(const Args: array of TAlgosimArray): TAlgosimArray;
    function Add(const AElement: TAlgosimObject): Integer;
    procedure Insert(AIndex: Integer; AElement: TAlgosimObject); override;
    procedure Remove(const AIndices: array of Integer); override;
    procedure Clear;
    procedure Truncate(ANewLength: Integer); override;
    procedure Sort; overload; override;
    procedure Sort(AComparer: IComparer<TAlgosimObject>); overload; override;
    procedure Sort(AComparer: IComparer<TASR>); overload; override;
    procedure Sort(AComparer: IComparer<TASC>); overload; override;
    procedure Shuffle; override;
    procedure Reverse; override;
    function RemoveDuplicates: TAlgosimObject; override;
    function RemoveDuplicatesEps(const Epsilon: TASR = 0): TAlgosimObject; override;
    function RemoveAdjacentDuplicates: TAlgosimObject; override;
    function RemoveAdjacentDuplicatesEps(const Epsilon: TASR = 0): TAlgosimObject; override;
    function Frequencies: TAlgosimArray; override;
    function FrequenciesEps(const Epsilon: TASR = 0): TAlgosimArray; override;
    function CollapseSequences: TAlgosimArray; override;
    function CollapseSequencesEps(const Epsilon: TASR = 0): TAlgosimArray; override;
    procedure Swap(Index1, Index2: Integer); override;
    procedure Group(AGroupSize: Integer);
    function Transpose: TAlgosimArray;
    function Zip(const AArrays: array of TAlgosimArray): TAlgosimArray;
    function RotLeft(N: Integer): TAlgosimObject; override;
    function RotRight(N: Integer): TAlgosimObject; override;
    function AsArray: TArray<TAlgosimObject>;
    function AccumulateSteps(AInitialValue: TAlgosimObject;
      AFunction: TASOAccumulator): TAlgosimObject; override;
    procedure RemoveFirst(N: Integer = 1); override;
    function Release(Index: Integer): TAlgosimObject;
    function ExplainedOutput(const AOptions: TFormatOptions): string; override;
    class function SortClass: TSortClass; override;
    class function SortClassCompare(const Left, Right: TAlgosimObject): Integer; override;
    class function SortClassSameObject(const Left, Right: TAlgosimObject;
      const AEpsilon: TASR = 0): Boolean; override;
  end;

  [AlgosimObject('assignment list', [asoObjectContainer, asoOrderedContainer])]
  TAlgosimAssignmentList = class(TAlgosimArray)
  end;

  [AlgosimObject('structure', [asoObjectContainer, asoOrderedContainer])]
  TAlgosimStructure = class(TAlgosimObject)
  public type
    TMemberRef = record
      Name: string;
      Value: TAlgosimObject;
      constructor Create(const AName: string; AValue: TAlgosimObject);
    end;
  protected type
    TMemberObj = class
      Name: string;
      Value: TAlgosimObject;
      constructor Create(const AName: string; AValue: TAlgosimObject); overload;
      constructor Create(const AMemberRef: TMemberRef); overload;
      function Ref: TMemberRef;
      destructor Destroy; override;
    end;
  var
    FMembers: TObjectList<TMemberObj>;
    procedure RequestTypeChange; virtual;
    function GetMember(Index: Integer): TMemberRef;
    function GetMemberCount: Integer; inline;
    function GetValue(const AName: string): TAlgosimObject; reintroduce;
    function GetValueByIndex(Index: Integer): TAlgosimObject; inline;
    procedure SetMember(Index: Integer; const AMember: TMemberRef);
    procedure SetValue(const AName: string; const AValue: TAlgosimObject); reintroduce;
    procedure SetValueByIndex(AIndex: Integer; const AValue: TAlgosimObject);
    function GetPrefixedMultilineText(const APrefix: string;
      const AOptions: TFormatOptions): string; virtual;
    function GetElement(Index: Integer): TAlgosimObject; override;
    function GetElementCount: Integer; override;
    procedure SetElement(Index: Integer; AValue: TAlgosimObject); override;
    function GetMemorySize: UInt64; override;
  public
    constructor Create; override;
    constructor Create(AObject: TAlgosimObject); override;
    constructor CreateWithValue(const ANames: array of string;
      const AValues: array of TAlgosimObject); overload;
    constructor CreateWithValue(const AMembers: array of TMemberRef); overload;
    destructor Destroy; override;
    function GetAsSingleLineText(const AOptions: TFormatOptions): string; override;
    function GetAsMultilineText(const AOptions: TFormatOptions): string; override;
    function Equals(Obj: TObject): Boolean; override;
    function SortClassGetHashCode: Integer; override;
    function AddElement(const AElement: TAlgosimObject): Boolean; override;
    function ToString: string; override;
    function ToSpeech: string; override;
    property Members[Index: Integer]: TMemberRef read GetMember write SetMember;
    property MemberCount: Integer read GetMemberCount;
    property Values[const AName: string]: TAlgosimObject read GetValue
      write SetValue; default;
    property Values[Index: Integer]: TAlgosimObject read GetValueByIndex
      write SetValueByIndex; default;
    procedure Release(Index: Integer); overload;
    procedure Release(const AName: string); overload;
    function TryGetSubscriptedRef(ASubscript: TSubscript;
      out AValue: TAlgosimObject): Boolean; override;
    function TryGetSubscriptedValue(ASubscript: TSubscript;
      out AValue: TAlgosimObject): Boolean; override;
    procedure SetSubscript(ASubscript: TSubscript;
      AValue: TAlgosimObject); override;
    function Filter(APredicate: TASOPredicate): TAlgosimObject; override;
    procedure Clear;
    procedure Delete(AIndex: Integer); overload;
    procedure Delete(const AName: string); overload;
    function HasMember(const AName: string): Boolean; inline;
    function HasIntegerMember(const AName: string; out AValue: TASI): Boolean; overload;
    function HasIntegerMember(const AName: string; out AValue: Integer): Boolean; overload;
    function IndexOfName(const AName: string): Integer;
    function Add(const AName: string; const AValue: TAlgosimObject;
      ASkipUniquenessCheck: Boolean = False): Integer;
    procedure Insert(Index: Integer; const AName: string;
      const AValue: TAlgosimObject); reintroduce;
    procedure Truncate(ANewLength: Integer); override;
    procedure RenameMember(const AOldName, ANewName: string);
    class function IsValidMemberName(const AName: string): Boolean; static; inline;
    function ToStructType(const AName: string): TAlgosimStructureType;
    function ValidateAgainst(AStructureType: TAlgosimStructure): Boolean;
    function ToColor: TRGB; override;
    class function SortClass: TSortClass; override;
    class function SortClassCompare(const Left, Right: TAlgosimObject): Integer; override;
    class function SortClassSameObject(const Left, Right: TAlgosimObject;
      const AEpsilon: TASR = 0): Boolean; override;
  end;

  TAlgosimTypedStructure = class;

  [AlgosimObject('structure type', [asoObjectContainer, asoOrderedContainer])]
  TAlgosimStructureType = class(TAlgosimStructure)
  strict private
    FName: string;
  public
    constructor Create(AObject: TAlgosimObject); override;
    property Name: string read FName write FName;
    function New(const AValues: array of TAlgosimObject): TAlgosimTypedStructure; overload;
    function New(const AMembers: array of TAlgosimStructure.TMemberRef): TAlgosimTypedStructure; overload;
    function New: TAlgosimTypedStructure; overload;
  end;

  [AlgosimObject('typed structure', [asoObjectContainer, asoOrderedContainer])]
  TAlgosimTypedStructure = class(TAlgosimStructure)
  protected
    FStructureTypeName: string;
    FConstructed: Boolean;
    function GetTypeName: string; override;
    procedure RequestTypeChange; override;
  public
    constructor Create(AObject: TAlgosimObject); override;
    constructor Create(AStructureType: TAlgosimStructureType;
      const AValues: array of TAlgosimObject); overload;
    constructor Create(AStructureType: TAlgosimStructureType;
      const AMembers: array of TAlgosimStructure.TMemberRef); overload;
    procedure AfterConstruction; override;
    property StructureTypeName: string read FStructureTypeName
      write FStructureTypeName;
  end;

  [AlgosimObject('set', [asoObjectContainer])]
  TAlgosimSet = class(TAlgosimObject)
  strict private
    const MAX_EPS_CARD = 10000;
    procedure OnDictChanged(Sender: TObject; const Item: TAlgosimObject;
      Action: TCollectionNotification);
  protected
    FValue: TObjectDictionary<TAlgosimObject, Pointer>;
    FValueArray: TArray<TAlgosimObject>;
    function GetElementCount: Integer; override;
    function GetElement(Index: Integer): TAlgosimObject; override;
    function GetMemorySize: UInt64; override;
  public
    constructor Create; override;
    constructor Create(AObject: TAlgosimObject); override;
    function GetAsSingleLineText(const AOptions: TFormatOptions): string; override;
    procedure Clear;
    destructor Destroy; override;
    function AddElement(const AElement: TAlgosimObject): Boolean; override;
    class function SortClass: TSortClass; override;
    function Equals(Obj: TObject): Boolean; override;
    class function SortClassCompare(const Left: TAlgosimObject;
      const Right: TAlgosimObject): Integer; override;
    function SortClassGetHashCode: Integer; override;
    class function SortClassSameObject(const Left: TAlgosimObject;
      const Right: TAlgosimObject; const AEpsilon: Extended = 0): Boolean;
      override;
    function ToString: string; override;
    function ToSpeech: string; override;
    property Cardinality: Integer read GetElementCount;
    function ToArray: TArray<TAlgosimObject>;
    class function Union(U, V: TAlgosimSet): TAlgosimSet; static;
    class function Intersection(U, V: TAlgosimSet): TAlgosimSet; static;
    class function Difference(U, V: TAlgosimSet): TAlgosimSet; static;
    class function SymmetricDifference(U, V: TAlgosimSet): TAlgosimSet; static;
    class function ElementOf(x: TAlgosimObject; U: TAlgosimSet): Boolean; static;
    class function Subset(U, V: TAlgosimSet): Boolean; static;
    class function ProperSubset(U, V: TAlgosimSet): Boolean; static;
    class function CartesianProduct(U, V: TAlgosimSet): TAlgosimSet; overload; static;
    class function CartesianProduct(const ASets: array of TAlgosimSet): TAlgosimSet; overload; static;
  end;

  [AlgosimObject('table', [asoValueContainer, asoOrderedContainer, asoPlanarContainer])]
  TAlgosimTable = class(TAlgosimObject)
  protected
    FValue: TASTable;
    FAlignment: TAlignment;
    FColSpacing: Integer;
    function GetValue(Index: Integer): TAlgosimObject; override;
    function GetValueCount: Integer; override;
    function GetValueFromPoint(const APoint: TPoint): TAlgosimObject; override;
    function GetPlanarExtent: TSize; override;
    procedure SetPlanarExtent(const Value: TSize); override;
    procedure SetValue(Index: Integer; AValue: TAlgosimObject); override;
    procedure SetValueFromPoint(const APoint: TPoint; AValue: TAlgosimObject); override;
  public
    constructor Create; override;
    constructor Create(AObject: TAlgosimObject); override;
    constructor CreateWithValue(const AValue: TASTable);
    procedure SaveToFile(const AFileName: string); override;
    function Equals(Obj: TObject): Boolean; override;
    function SortClassGetHashCode: Integer; override;
    destructor Destroy; override;
    property Value: TASTable read FValue write FValue;
    property Alignment: TAlignment read FAlignment write FAlignment;
    property ColSpacing: Integer read FColSpacing write FColSpacing;

    function ToString: string; override;
    function GetAsSingleLineText(const AOptions: TFormatOptions): string; override;
    function GetAsMultilineText(const AOptions: TFormatOptions): string; override;

    function Count(APredicate: TASOPredicate): Integer; override;
    function ForAll(APredicate: TASOPredicate): Boolean; override;
    function Exists(APredicate: TASOPredicate): Boolean; override;
    procedure Apply(AFunction: TASOFunction; ACondition: TASOPredicate = nil;
      ALevel: Integer = 1); override;
    procedure Replace(APredicate: TASOPredicate; ANewValue: TAlgosimObject;
      ALevel: Integer = 1); override;
    function Accumulate(AInitialValue: TAlgosimObject;
      AFunction: TASOAccumulator): TAlgosimObject; override;
    function AccumulateStepsList(AInitialValue: TAlgosimObject;
      AFunction: TASOAccumulator): TAlgosimArray; override;

    function WithSpecificValues(AValues: TAlgosimArray): TAlgosimObject; override;

    function ToRealMatrix: TRealMatrix; override;
    function ToComplexMatrix: TComplexMatrix; override;
    function ToMatrix: TAlgosimMatrix; override;
    function ToTable: TASTable; override;

    class function SortClass: TSortClass; override;
    class function SortClassCompare(const Left, Right: TAlgosimObject): Integer; override;
    class function SortClassSameObject(const Left, Right: TAlgosimObject;
      const AEpsilon: TASR = 0): Boolean; override;
  end;

  [AlgosimObject('color')]
  TAlgosimColor = class(TAlgosimObject)
  strict private
    function GetAsPixel: TASPixel; inline;
    procedure SetAsPixel(const APixel: TASPixel); inline;
  protected
    FData: packed record
      FValue: TRGB;
      FAlpha: Double;
    end;
    function GetMemorySize: UInt64; override;
  public
    constructor Create(AObject: TAlgosimObject); overload; override;
    constructor CreateWithValue(const AValue: TRGB; const AAlpha: Double = 1); overload;
    constructor CreateWithValue(const AValue: THSV; const AAlpha: Double = 1); overload;
    constructor CreateWithValue(const AValue: THSL; const AAlpha: Double = 1); overload;
    constructor CreateWithValue(const AValue: TColor; const AAlpha: Double = 1); overload;
    function ToString: string; override;
    function ToSpeech: string; override;
    function GetAsSingleLineText(const AOptions: TFormatOptions): string; override;
    function ToInputString: string; override;
    function ExplainedOutput(const AOptions: TFormatOptions): string; override;
    function Equals(Obj: TObject): Boolean; override;
    function SortClassGetHashCode: Integer; override;
    function TryToInt32(out Int: Integer): Boolean; override;
    function TryToASI(out ASI: TASI): Boolean; override;
    function ToColor: TRGB; override;
    function ToPixel: TASPixel; override;
    constructor Create; overload; override;
    property Value: TRGB read FData.FValue write FData.FValue;
    property Alpha: Double read FData.FAlpha write FData.FAlpha;
    property AsPixel: TASPixel read GetAsPixel write SetAsPixel;
    function GetBinaryData(var Buf: PByte; var Len: UInt64): Boolean; override;
    procedure SetBinaryData(const Buf: PByte; const Len: UInt64); override;
    class function SortClass: TSortClass; override;
    class function SortClassCompare(const Left, Right: TAlgosimObject): Integer; override;
    class function SortClassSameObject(const Left, Right: TAlgosimObject;
      const AEpsilon: TASR = 0): Boolean; override;
  end;

  [AlgosimObject('binary data', [asoValueContainer, asoOrderedContainer])]
  TAlgosimBinaryData = class(TAlgosimObject)
  strict private
    function GetDataLength: NativeInt;
    procedure SetDataLength(const Value: NativeInt);
    function GetDataPtr: PByte;
  protected
    FData: TArray<Byte>;
    function GetValue(Index: Integer): TAlgosimObject; override;
    function GetValueCount: Integer; override;
    procedure SetValue(Index: Integer; AValue: TAlgosimObject); override;
    function ToASO<T: TAlgosimObject>: T;
    function GetMemorySize: UInt64; override;
  public
    constructor Create(AObject: TAlgosimObject); override;
    constructor CreateWithValue(const ABytes: array of Byte); overload;
    function ToString: string; override;
    function ToSpeech: string; override;
    function GetAsSingleLineText(const AOptions: TFormatOptions): string; override;
    function GetAsMultilineText(const AOptions: TFormatOptions): string; override;
    function Equals(Obj: TObject): Boolean; override;
    function SortClassGetHashCode: Integer; override;
    procedure SaveToFile(const AFileName: string); override;
    class function LoadFromFile(const AFileName: string;
      AEncoding: TEncoding = nil; const AParams: string = ''): TAlgosimObject; override;
    function ToInteger: TASI; override;
    function ToRationalNumber: TRationalNumber; override;
    function ToRealNumber: TASR; override;
    function ToComplexNumber: TASC; override;
    function TryToASI(out ASI: Int64): Boolean; override;
    function ToBoolean: Boolean; override;
    property Data: PByte read GetDataPtr;
    property DataLength: NativeInt read GetDataLength write SetDataLength;
    procedure Truncate(ANewLength: Integer); override;

    function GetBinaryData(var Buf: PByte; var Len: UInt64): Boolean; override;
    procedure SetBinaryData(const Buf: PByte; const Len: UInt64); override;

    class function SortClass: TSortClass; override;
    class function SortClassCompare(const Left, Right: TAlgosimObject): Integer; override;
    class function SortClassSameObject(const Left, Right: TAlgosimObject;
      const AEpsilon: TASR = 0): Boolean; override;
  end;

function CompareASO(const Left, Right: TAlgosimObject): Integer;
function SameASO(const Left, Right: TAlgosimObject;
  const AEpsilon: TASR = 0): Boolean; inline;


type
  TASOSignal = (success, failure, null, _break, _continue);

function ASO(const Value: TASR): TAlgosimRealNumber; overload; inline;
function ASO(const Value: TASR; AFormatStyle: TFormatStyle): TAlgosimRealNumber; overload; inline;
function ASO(const Value: TASC): TAlgosimComplexNumber; overload; inline;
function ASO(const Value: string): TAlgosimString; overload; inline;
function ASO(const Value: Boolean): TAlgosimBoolean; overload; inline;
function ASO(const Value: TRealVector): TAlgosimRealVector; overload; inline;
function ASO(const Value: TComplexVector): TAlgosimComplexVector; overload; inline;
function ASO(const Value: TRealMatrix): TAlgosimRealMatrix; overload; inline;
function ASO(const Value: TComplexMatrix): TAlgosimComplexMatrix; overload; inline;
function ASO(const Value: TASPixmap): TAlgosimPixmap; overload; inline;
function ASO(const Value: TASSound): TAlgosimSound; overload; inline;
function ASO(const Value: TASTable): TAlgosimTable; overload; inline;
function ASO(const Value: TRGB): TAlgosimColor; overload; inline;
function ASO(const Value: THSV): TAlgosimColor; overload; inline;
function ASO(const Value: THSL): TAlgosimColor; overload; inline;
function ASO(const Value: TASPixel): TAlgosimColor; overload; inline;
function ASO(const Value: Exception): TAlgosimFailure; overload;
function ASOColor(const Value: TColor): TAlgosimColor; inline;
function ASO(const Elements: array of TAlgosimObject): TAlgosimArray; overload;
function ASO(const Names: array of string;
  const Values: array of TAlgosimObject): TAlgosimStructure; overload;
function ASO(const Members: array of TAlgosimStructure.TMemberRef): TAlgosimStructure; overload;
function ASO(const Value: TASOSignal; const AReason: string = ''): TAlgosimObject; overload;
function ASOInt(const Value: TASI): TAlgosimInteger; overload; inline;
function ASOInt(const Value: TASI; AFormatStyle: TFormatStyle): TAlgosimInteger; overload; inline;
function ASOInt(const Value: TASI; ABase: Integer): TAlgosimInteger; overload; inline;
function ASORat(const Value: TRationalNumber): TAlgosimRationalNumber; overload; inline;
function ASORat(const Value: TRationalNumber; AFormatStyle: TFormatStyle): TAlgosimRationalNumber; overload; inline;
function ASO(const AGUID: TGUID): TAlgosimReference; overload; inline;
function ASOBreak(ADepth: Integer = 1): TAlgosimBreak;
function ASOContinue: TAlgosimContinue;

function sm(const AName: string; AValue: TAlgosimObject): TAlgosimStructure.TMemberRef; inline;

function IsNonNull(AObject: TAlgosimObject): Boolean; inline;
function IsControl(AObject: TAlgosimObject): Boolean; inline;
function IsFailure(AObject: TAlgosimObject): Boolean; inline;
function IsCharacter(AObject: TAlgosimObject): Boolean; inline;
function IsTypedStructure(AObject: TAlgosimObject;
  AType: TAlgosimStructureType): Boolean; overload; inline;

const
  NormTypes: array[TNormType] of string = ('Euclidean norm', 'Frobenius norm',
    'p-norm', 'max norm', 'sum norm', 'k-norm', 'max column sum norm',
    'max row sum norm', 'spectral norm');

  ShortNormTypes: array[TNormType] of string = ('Euclidean', 'Frobenius',
    'p', 'max', 'sum', 'k', 'max column sum', 'max row sum', 'spectral');

type
  TNormTypeHelper = record helper for TNormType
    function ToString: string;
    function ToShortString: string;
    class function FromString(const AStr: string): TNormType; static;
  end;

const
  NumArrClasses: array[{2d}Boolean, {cplx}Boolean] of TAlgosimNumericArrayClass =
    (
      (TAlgosimRealVector, TAlgosimComplexVector),
      (TAlgosimRealMatrix, TAlgosimComplexMatrix)
    );

function NumArrayClass(AMatrix, AComplex: Boolean): TAlgosimNumericArrayClass; inline;

procedure MakeComplex(var AObj: TAlgosimObject);

type
  TASOArrSpec = record
    class function TrySpec<T: TAlgosimObject>(
      const AArgs: TArray<TAlgosimObject>; out ASpec: TArray<T>): Boolean; static;
  end;

var
  Debug_DisableASOHashing: Boolean;

function LoadResObject(const AName: string): TAlgosimObject;

implementation

uses
  Windows, StrUtils, Character, Graphics, Clipbrd, ASFormatters, ASStructs,
  Rtti, IOUtils, TextEncodings, System.Hash, Dialogs, ASExecutionContext;

function CapCap(ACapacity: Integer): Integer; inline;
begin
  if ACapacity <= 1024 then
    Result := ACapacity
  else
    Result := 1024;
end;

{$IFOPT Q+}
  {$DEFINE OverflowCheckingWasEnabled}
  {$Q-}
{$ELSE}
  {$UNDEF OverflowCheckingWasEnabled}
{$ENDIF}

function CombineHashes(const Values: array of Integer): Integer;
var
  Value: Integer;
begin
  Result := 17;
  for Value in Values do
    Result := 37 * Result + Value;
end;

{$IFDEF OverflowCheckingWasEnabled}
  {$Q+}
  {$UNDEF OverflowCheckingWasEnabled}
{$ENDIF}

function CompareASO(const Left, Right: TAlgosimObject): Integer;
begin
  Result := CompareValue(Ord(Left.SortClass), Ord(Right.SortClass));
  if Result = 0 then
    Result := Left.SortClassCompare(Left, Right);
end;

function SameASO(const Left, Right: TAlgosimObject; const AEpsilon: TASR): Boolean;
begin
  Result := (Left.SortClass = Right.SortClass) and
    Left.SortClassSameObject(Left, Right, AEpsilon);
end;

function NumArrayClass(AMatrix, AComplex: Boolean): TAlgosimNumericArrayClass;
begin
  Result := NumArrClasses[AMatrix, AComplex];
end;

procedure MakeComplex(var AObj: TAlgosimObject);
var
  OldObj: TAlgosimObject;
begin

  if not (AObj is TAlgosimNumericEntity) or AObj.IsComplex then
    Exit;

  OldObj := AObj;

  if AObj is TAlgosimNumber then
  begin
    AObj := ASO(OldObj.ToASC);
    OldObj.Free;
  end

  else if AObj is TAlgosimVector then
  begin
    AObj := ASO(OldObj.AsComplexVector);
    OldObj.Free;
  end

  else if AObj is TAlgosimMatrix then
  begin
    AObj := ASO(OldObj.AsComplexMatrix);
    OldObj.Free;
  end;

end;

function PadStr(const AStr: string; const ALen: Integer;
  const AAlign: TAlignment = taCenter): string;
var
  PadLen: Integer;
begin
  Result := AStr;
  PadLen := Max(ALen - AStr.Length, 0);
  case AAlign of
    taLeftJustify:
      Result := AStr + StringOfChar(#32, PadLen);
    taRightJustify:
      Result := StringOfChar(#32, PadLen) + AStr;
    taCenter:
      Result := StringOfChar(#32, PadLen div 2) + AStr + StringOfChar(#32, PadLen - PadLen div 2);
  end;
end;

{ TAlgosimObject }

constructor TAlgosimObject.Create;
begin

end;

function TAlgosimObject.Accumulate(AInitialValue: TAlgosimObject;
  AFunction: TASOAccumulator): TAlgosimObject;
var
  i: Integer;
  NewVal: TAlgosimObject;
begin
  Result := AInitialValue.Clone;
  try
    if IsObjectContainer then
      for i := 1 to ElementCount do
        TObjReplacer<TAlgosimObject>.Replace(Result, AFunction(Result, Elements[i]))
    else if IsValueContainer then
      for i := 1 to ValueCount do
      begin
        NewVal := Values[i];
        try
          TObjReplacer<TAlgosimObject>.Replace(Result, AFunction(Result, NewVal));
        finally
          NewVal.Free;
        end;
      end
    else
      raise EAlgosimObjectException.CreateFmt(SCannotAccumulateElements,
        [ClassTypeName]);
  except
    Result.Free;
    raise;
  end;
end;

function TAlgosimObject.AccumulateSteps(AInitialValue: TAlgosimObject;
  AFunction: TASOAccumulator): TAlgosimObject;
var
  List: TAlgosimArray;
begin
  List := AccumulateStepsList(AInitialValue, AFunction);
  try
    Result := WithSpecificValues(List);
  finally
    List.Free;
  end;
end;

function TAlgosimObject.AccumulateStepsList(AInitialValue: TAlgosimObject;
  AFunction: TASOAccumulator): TAlgosimArray;
var
  i: Integer;
  Val: TAlgosimObject;
begin
  Result := TAlgosimArray.Create;
  try
    if IsObjectContainer then
    begin
      Result.Capacity := ElementCount;
      for i := 1 to ElementCount do
      begin
        AInitialValue := AFunction(AInitialValue, Elements[i]);
        Result.Add(AInitialValue);
      end;
    end
    else if IsValueContainer then
    begin
      Result.Capacity := ValueCount;
      for i := 1 to ValueCount do
      begin
        Val := Values[i];
        try
          AInitialValue := AFunction(AInitialValue, Val);
          Result.Add(AInitialValue);
        finally
          Val.Free;
        end;
      end;
    end
    else
      raise EAlgosimObjectException.CreateFmt(SCannotAccumulateElements,
        [ClassTypeName]);
  except
    Result.Free;
    raise;
  end;
end;

function TAlgosimObject.AddElement(const AElement: TAlgosimObject): Boolean;
begin
  AElement.Free;
  raise EAlgosimObjectException.CreateFmt(SObjectNoContainer, [ClassTypeName]);
end;

procedure TAlgosimObject.AddMembersToArray(AArray: TAlgosimArray;
  APredicate: TASOPredicate; ALevel: Integer);
var
  i: Integer;
  obj, objref: TAlgosimObject;
begin

  if ALevel = 1 then
  begin
    if IsObjectContainer then
    begin
      for i := 1 to ElementCount do
        if not Assigned(APredicate) or APredicate(Elements[i]) then
          AArray.Add(Elements[i].Clone);
    end
    else if IsValueContainer then
    begin
      for i := 1 to ValueCount do
      begin
        obj := Values[i];
        try
          if not Assigned(APredicate) or APredicate(Obj) then
          begin
            objref := obj;
            obj := nil;
            AArray.Add(objref);
          end;
        finally
          obj.Free;
        end;
      end;
    end;
  end
  else if (ALevel > 1) and IsObjectContainer then
    for i := 1 to ElementCount do
      Elements[i].AddMembersToArray(AArray, APredicate, ALevel - 1);

end;

procedure TAlgosimObject.AddMembersToArray(AArray: TAlgosimArray;
  APredicate: TASOPredicate);
var
  i: Integer;
  obj, objref: TAlgosimObject;
begin

  if IsObjectContainer then
  begin
    for i := 1 to ElementCount do
    begin
      if not Assigned(APredicate) or APredicate(Elements[i]) then
        AArray.Add(Elements[i].Clone);
      Elements[i].AddMembersToArray(AArray, APredicate);
    end;
  end
  else if IsValueContainer and not (Self is TAlgosimString) then
  begin
    for i := 1 to ValueCount do
    begin
      obj := Values[i];
      try
        if not Assigned(APredicate) or APredicate(Obj) then
        begin
          objref := obj;
          obj := nil;
          AArray.Add(objref);
        end;
      finally
        obj.Free;
      end;
    end;
  end;

end;

procedure TAlgosimObject.AddNumbersToArray(AArray: TAlgosimArray;
  ALevel: Integer);
var
  i: Integer;
  obj: TAlgosimObject;
begin

  if ALevel > 64 then
    raise Exception.Create('TAlgosimObject.AddNumbersToArray: Too deep recursion.');

  if IsObjectContainer then
    for i := 1 to ElementCount do
      Elements[i].AddNumbersToArray(AArray, ALevel + 1)
  else if IsValueContainer then
    for i := 1 to ValueCount do
    begin
      obj := Values[i];
      try
        obj.AddNumbersToArray(AArray, ALevel + 1)
      finally
        obj.Free;
      end;
    end;

end;

procedure TAlgosimObject.Append(AElement: TAlgosimObject);
begin
  AElement.Free;
  raise EAlgosimObjectException.CreateFmt(SObjectNoContainer, [ClassTypeName]);
end;

procedure TAlgosimObject.Apply(AFunction: TASOFunction;
  ACondition: TASOPredicate; ALevel: Integer);
var
  i: Integer;
begin
  if IsObjectContainer then
  begin
    if ALevel > 1 then
      for i := 1 to ElementCount do
        Elements[i].Apply(AFunction, ACondition, ALevel - 1)
    else
      for i := 1 to ElementCount do
        if not Assigned(ACondition) or ACondition(Elements[i]) then
          Elements[i] := AFunction(Elements[i]);
  end
  else
    raise EAlgosimObjectException.CreateFmt(SCannotApply, [ClassTypeName]);
end;

function TAlgosimObject.AsComplexMatrix: TComplexMatrix;
begin
  Result := ToComplexMatrix;
end;

function TAlgosimObject.AsComplexVector: TComplexVector;
begin
  Result := ToComplexVector;
end;

function TAlgosimObject.AsMatrix: TAlgosimMatrix;
begin
  if IsComplex then
    Result := ASO(AsComplexMatrix)
  else
    Result := ASO(AsRealMatrix);
end;

function TAlgosimObject.ASOClassType: TAlgosimObjectClass;
begin
  Result := TAlgosimObjectClass(ClassType);
end;

function TAlgosimObject.AsRealMatrix: TRealMatrix;
begin
  Result := ToRealMatrix;
end;

function TAlgosimObject.AsRealVector: TRealVector;
begin
  Result := ToRealVector;
end;

function TAlgosimObject.AsVector: TAlgosimVector;
begin
  if IsComplex then
    Result := ASO(AsComplexVector)
  else
    Result := ASO(AsRealVector);
end;

procedure TAlgosimObject.ExtendWith(AElement: TAlgosimObject);
begin
  AElement.Free;
  raise EAlgosimObjectException.CreateFmt(SObjectNoContainer, [ClassTypeName]);
end;

function TAlgosimObject.Clone: TAlgosimObject;
begin
  Result := TAlgosimObjectClass(ClassType).Create(Self);
end;

procedure TAlgosimObject.CopyToClipboard;
begin
  Clipboard.AsText := ToString;
end;

function TAlgosimObject.Count(APredicate: TASOPredicate): Integer;
var
  i: Integer;
  obj: TAlgosimObject;
begin
  if IsObjectContainer then
  begin
    Result := 0;
    for i := 1 to ElementCount do
      if APredicate(Elements[i]) then
        Inc(Result);
  end
  else if IsValueContainer then
  begin
    Result := 0;
    for i := 1 to ValueCount do
    begin
      obj := Values[i];
      try
        if APredicate(obj) then
          Inc(Result);
      finally
        obj.Free;
      end;
    end;
  end
  else
    raise EAlgosimObjectException.CreateFmt(SCannotCountElements,
      [ClassTypeName]);
end;

function TAlgosimObject.CountValue(AObj: TAlgosimObject): Integer;
var
  i: Integer;
  obj: TAlgosimObject;
begin
  if IsObjectContainer then
  begin
    Result := 0;
    for i := 1 to ElementCount do
      if Elements[i].Equals(AObj) then
        Inc(Result);
  end
  else if IsValueContainer then
  begin
    Result := 0;
    for i := 1 to ValueCount do
    begin
      obj := Values[i];
      try
        if obj.Equals(AObj) then
          Inc(Result);
      finally
        obj.Free;
      end;
    end;
  end
  else
    raise EAlgosimObjectException.CreateFmt(SCannotCountElements,
      [ClassTypeName]);
end;

function TAlgosimObject.CountValueEps(AObj: TAlgosimObject;
  const AEpsilon: TASR): Integer;
var
  i: Integer;
  obj: TAlgosimObject;
begin
  if IsObjectContainer then
  begin
    Result := 0;
    for i := 1 to ElementCount do
      if SameASO(Elements[i], AObj, AEpsilon) then
        Inc(Result);
  end
  else if IsValueContainer then
  begin
    Result := 0;
    for i := 1 to ValueCount do
    begin
      obj := Values[i];
      try
        if SameASO(obj, AObj, AEpsilon) then
          Inc(Result);
      finally
        obj.Free;
      end;
    end;
  end
  else
    raise EAlgosimObjectException.CreateFmt(SCannotCountElements,
      [ClassTypeName]);
end;

function TAlgosimObject.Columns: TAlgosimArray;
var
  i: Integer;
begin
  Result := TAlgosimArray.Create;
  try
    for i := 1 to PlanarExtent.cx do
      Result.Add(Column(i));
  except
    Result.Free;
    raise;
  end;
end;

class function TAlgosimObject.Comparer: IComparer<TAlgosimObject>;
begin
  Result := TComparer<TAlgosimObject>.Construct(CompareASO);
end;

function TAlgosimObject.Contains(AObj: TAlgosimObject): Boolean;
var
  Idx: TAlgosimObject;
begin
  Idx := IndexOfValue(AObj);
  try
    Result := IsNonNull(Idx);
  finally
    Idx.Free;
  end;
end;

function TAlgosimObject.ContainsEps(AObj: TAlgosimObject;
  const AEpsilon: TASR): Boolean;
var
  Idx: TAlgosimObject;
begin
  Idx := IndexOfValueEps(AObj, AEpsilon);
  try
    Result := IsNonNull(Idx);
  finally
    Idx.Free;
  end;
end;

function TAlgosimObject.CollapseSequences: TAlgosimArray;
begin
  raise EAlgosimObjectException.CreateFmt(SCannotConstructFreqAdj, [ClassTypeName]);
end;

function TAlgosimObject.CollapseSequencesEps(const Epsilon: TASR): TAlgosimArray;
begin
  raise EAlgosimObjectException.CreateFmt(SCannotConstructFreqAdj, [ClassTypeName]);
end;

function TAlgosimObject.Column(const AIndex: Integer): TAlgosimObject;
begin
  Result := GetSubscriptedValue(TSubscript.Create(skColIndex, AIndex));
end;

constructor TAlgosimObject.Create(AObject: TAlgosimObject);
begin
  raise EAlgosimObjectException.CreateFmt(SNoCopyConstructor, [ClassTypeName]);
end;

function TAlgosimObject.ElementsAre(
  AASOMetaclass: TAlgosimObjectClass): Boolean;
var
  i: Integer;
begin
  for i := 1 to ElementCount do
    if not (Elements[i] is AASOMetaclass) then
      Exit(False);
  Result := True;
end;

class function TAlgosimObject.ValEqualityComparer: IEqualityComparer<TAlgosimObject>;
begin
  Result := TEqualityComparer<TAlgosimObject>.Construct(
    TAlgosimObject.EqualityComparison,
    TAlgosimObject.Hasher
  );
end;

class function TAlgosimObject.EqualityComparison(const Left,
  Right: TAlgosimObject): Boolean;
begin
  Result := Left.Equals(Right);
end;

function TAlgosimObject.Equals(Obj: TObject): Boolean;
begin
  Result := False;
end;

function TAlgosimObject.Exists(APredicate: TASOPredicate): Boolean;
var
  i: Integer;
  obj: TAlgosimObject;
begin
  if IsObjectContainer then
  begin
    for i := 1 to ElementCount do
      if APredicate(Elements[i]) then
        Exit(True);
    Result := False;
  end
  else if IsValueContainer then
  begin
    for i := 1 to ValueCount do
    begin
      obj := Values[i];
      try
        if APredicate(obj) then
          Exit(True);
      finally
        obj.Free;
      end;
    end;
    Result := False;
  end
  else
    raise EAlgosimObjectException.CreateFmt(SCannotCountElements,
      [ClassTypeName]);
end;

function TAlgosimObject.ExistsUnique(APredicate: TASOPredicate): Boolean;
var
  i, c: Integer;
  obj: TAlgosimObject;
begin
  c := 0;
  if IsObjectContainer then
  begin
    for i := 1 to ElementCount do
      if APredicate(Elements[i]) then
      begin
        Inc(c);
        if c > 1 then
          Exit(False);
      end;
    Result := c = 1;
  end
  else if IsValueContainer then
  begin
    for i := 1 to ValueCount do
    begin
      obj := Values[i];
      try
        if APredicate(obj) then
        begin
          Inc(c);
          if c > 1 then
            Exit(False);
        end;
      finally
        obj.Free;
      end;
    end;
    Result := c = 1;
  end
  else
    raise EAlgosimObjectException.CreateFmt(SCannotCountElements,
      [ClassTypeName]);
end;

function TAlgosimObject.ExplainedOutput(const AOptions: TFormatOptions): string;
begin
  Result := GetAsMultilineText(AOptions);
end;

function TAlgosimObject.Filter(APredicate: TASOPredicate): TAlgosimObject;
var
  i: Integer;
begin
  if IsObjectContainer then
  begin
    Result := TAlgosimObjectClass(ClassType).Create;
    try
      Result.Capacity := ElementCount;
      for i := 1 to ElementCount do
        if APredicate(Elements[i]) then
          Result.AddElement(Elements[i].Clone);
      Result.TrimExcess;
    except
      Result.Free;
      raise;
    end;
  end
  else
    raise EAlgosimObjectException.CreateFmt(SCannotFilter, [ClassTypeName]);
end;

function TAlgosimObject.First(N: Integer): TAlgosimObject;
begin
  Result := Part(1, N);
end;

function TAlgosimObject.ForAll(APredicate: TASOPredicate): Boolean;
var
  i: Integer;
  obj: TAlgosimObject;
begin
  if IsObjectContainer then
  begin
    for i := 1 to ElementCount do
      if not APredicate(Elements[i]) then
        Exit(False);
    Result := True;
  end
  else if IsValueContainer then
  begin
    for i := 1 to ValueCount do
    begin
      obj := Values[i];
      try
        if not APredicate(obj) then
          Exit(False);
      finally
        obj.Free;
      end;
    end;
    Result := True;
  end
  else
    raise EAlgosimObjectException.CreateFmt(SCannotCountElements,
      [ClassTypeName]);
end;

function TAlgosimObject.Frequencies: TAlgosimArray;
begin
  raise EAlgosimObjectException.CreateFmt(SCannotConstructFreq, [ClassTypeName]);
end;

function TAlgosimObject.FrequenciesEps(const Epsilon: TASR): TAlgosimArray;
begin
  raise EAlgosimObjectException.CreateFmt(SCannotConstructFreq, [ClassTypeName]);
end;

function TAlgosimObject.GetAsMultilineText(const AOptions: TFormatOptions): string;
begin
  Result := GetAsSingleLineText(AOptions);
end;

function TAlgosimObject.GetAsSingleLineText(const AOptions: TFormatOptions): string;
begin
  Result := '';
end;

function TAlgosimObject.GetBinaryData(var Buf: PByte; var Len: UInt64): Boolean;
begin
  Buf := nil;
  Len := 0;
  Result := False;
end;

function TAlgosimObject.GetElement(Index: Integer): TAlgosimObject;
begin
  raise EAlgosimObjectException.CreateFmt(SObjectNoContainer, [ClassTypeName]);
end;

function TAlgosimObject.GetElementCount: Integer;
begin
  raise EAlgosimObjectException.CreateFmt(SObjectNoContainer, [ClassTypeName]);
end;

function TAlgosimObject.GetHashCode: Integer;
begin
  Result := CombineHashes([Ord(SortClass), SortClassGetHashCode]);
end;

function TAlgosimObject.SortClassGetHashCode: Integer;
begin
  Result := 0;
end;

class function TAlgosimObject.Hasher(const Value: TAlgosimObject): Integer;
begin
  Result := Value.GetHashCode;
end;

procedure TAlgosimObject.SetElement(Index: Integer; AValue: TAlgosimObject);
begin
  AValue.Free; // since we own it
  raise EAlgosimObjectException.CreateFmt(SObjectNoContainer, [ClassTypeName]);
end;

procedure TAlgosimObject.SetMaxLen(AValue: Integer);
begin
  raise EAlgosimObjectException.CreateFmt(SNoMaxLen, [ClassTypeName]);
end;

procedure TAlgosimObject.SetPlanarExtent(const Value: TSize);
begin
  raise EAlgosimObjectException.CreateFmt(SObjectNoPlanarValueContainer, [ClassTypeName]);
end;

function TAlgosimObject.GetNumbers: TAlgosimArray;
begin
  Result := TAlgosimArray.Create;
  try
    AddNumbersToArray(Result);
  except
    Result.Free;
    raise;
  end;
end;

class function TAlgosimObject.GetPhysIndex0(AIndex, ALength: Integer): Integer;
begin

  if not InRange(Abs(AIndex), 1, ALength) then
    raise EAlgosimObjectException.CreateFmt(SIndexOutOfBounds, [AIndex]);

  if AIndex > 0 then
    Result := AIndex - 1
  else
    Result := ALength + AIndex;

end;

class function TAlgosimObject.GetPhysIndex1(AIndex, ALength: Integer): Integer;
begin

  if not InRange(Abs(AIndex), 1, ALength) then
    raise EAlgosimObjectException.CreateFmt(SIndexOutOfBounds, [AIndex]);

  if AIndex > 0 then
    Result := AIndex
  else
    Result := ALength + 1 + AIndex;

end;

class function TAlgosimObject.GetPhysIndex2D0(const AIndex: TPoint;
  const ASize: TSize): TPoint;
begin

  if not InRange(Abs(AIndex.X), 1, ASize.cx) or not InRange(Abs(AIndex.Y), 1, ASize.cy) then
    raise EAlgosimObjectException.CreateFmt(SIndexOutOfBounds2D, [AIndex.Y, AIndex.X]);

  if AIndex.X > 0 then
    Result.X := AIndex.X - 1
  else
    Result.X := ASize.cx + AIndex.X;

  if AIndex.Y > 0 then
    Result.Y := AIndex.Y - 1
  else
    Result.Y := ASize.cy + AIndex.Y;

end;

function TAlgosimObject.GetPlanarExtent: TSize;
begin
  Result.cx := 1;
  Result.cy := 1;
end;

function TAlgosimObject.TryGetSubscriptedValue(
  ASubscript: TSubscript; out AValue: TAlgosimObject): Boolean;

  function SubscriptIsIntPair(out Idx1, Idx2: Integer): Boolean;
  begin
    Result := (ASubscript.Obj is TAlgosimArray) and
      (TAlgosimArray(ASubscript.Obj).ElementCount = 2) and
      (TAlgosimArray(ASubscript.Obj).ElementsAre(TAlgosimNumber)) and
      TAlgosimArray(ASubscript.Obj).Elements[1].TryToInt32(Idx1) and
      TAlgosimArray(ASubscript.Obj).Elements[2].TryToInt32(Idx2);
  end;

var
  IntIdx, IntIdx2: Integer;

begin

  AValue := nil;

  case ASubscript.Kind of
    skIndexObject:
      if (ASubscript.Obj is TAlgosimNumber) and ASubscript.Obj.TryToInt32(IntIdx) and IsContainer then
        AValue := Values[IntIdx]
      else if SubscriptIsIntPair(IntIdx, IntIdx2) and IsPlanarValueContainer then
        AValue := ValueFromPoint[
          Point(
            IntIdx2,
            IntIdx
          )];
    skFirst:
      AValue := Values[1];
    skLast:
      AValue := Values[-1];
    skRandom:
      AValue := Values[1 + System.Random(ValueCount)];
  end;

  Result := Assigned(AValue);

end;

function TAlgosimObject.TryToASI(out ASI: TASI): Boolean;
var
  obj: TAlgosimObject;
begin

  if IsObjectContainer and (ElementCount = 1) then
    Result := Elements[1].TryToASI(ASI)

  else if IsValueContainer and (ValueCount = 1) then
  begin
    obj := Values[1];
    try
      Result := obj.TryToASI(ASI);
    finally
      obj.Free;
    end;
  end

  else
    Result := False;

end;

function TAlgosimObject.TryToRat(out R: TRationalNumber): Boolean;
var
  obj: TAlgosimObject;
begin

  if IsObjectContainer and (ElementCount = 1) then
    Result := Elements[1].TryToRat(R)

  else if IsValueContainer and (ValueCount = 1) then
  begin
    obj := Values[1];
    try
      Result := obj.TryToRat(R)
    finally
      obj.Free;
    end;
  end

  else
    Result := False;

end;

function TAlgosimObject.TryToASR(out Val: TASR): Boolean;
var
  obj: TAlgosimObject;
begin

  if IsObjectContainer and (ElementCount = 1) then
    Result := Elements[1].TryToASR(Val)

  else if IsValueContainer and (ValueCount = 1) then
  begin
    obj := Values[1];
    try
      Result := obj.TryToASR(Val);
    finally
      obj.Free;
    end;
  end

  else
    Result := False;

end;

function TAlgosimObject.TryToASC(out Val: TASC): Boolean;
var
  obj: TAlgosimObject;
begin

  if IsObjectContainer and (ElementCount = 1) then
    Result := Elements[1].TryToASC(Val)

  else if IsValueContainer and (ValueCount = 1) then
  begin
    obj := Values[1];
    try
      Result := obj.TryToASC(Val);
    finally
      obj.Free;
    end;
  end

  else
    Result := False;

end;

function TAlgosimObject.TryToInt32(out Int: Int32): Boolean;
var
  obj: TAlgosimObject;
begin

  if IsObjectContainer and (ElementCount = 1) then
    Result := Elements[1].TryToInt32(Int)

  else if IsValueContainer and (ValueCount = 1) then
  begin
    obj := Values[1];
    try
      Result := obj.TryToInt32(Int);
    finally
      obj.Free;
    end;
  end

  else
    Result := False;

end;

function TAlgosimObject.TryToInt64(out Int: Int64): Boolean;
var
  obj: TAlgosimObject;
begin

  if IsObjectContainer and (ElementCount = 1) then
    Result := Elements[1].TryToInt64(Int)

  else if IsValueContainer and (ValueCount = 1) then
  begin
    obj := Values[1];
    try
      Result := obj.TryToInt64(Int);
    finally
      obj.Free;
    end;
  end

  else
    Result := False;

end;

function TAlgosimObject.GetSubscriptedValue(
  ASubscript: TSubscript): TAlgosimObject;
begin
  if not TryGetSubscriptedValue(ASubscript, Result) then
    raise EAlgosimObjectException.Create(SUnsupportedSubscriptOp);
end;

function TAlgosimObject.TryGetSubscriptedRef(
  ASubscript: TSubscript; out AValue: TAlgosimObject): Boolean;
var
  IntIdx: Integer;
begin

  AValue := nil;

  if IsObjectContainer then
    case ASubscript.Kind of
      skIndexObject:
        if (ASubscript.Obj is TAlgosimNumber) and ASubscript.Obj.TryToInt32(IntIdx) then
          AValue := Elements[IntIdx];
      skFirst:
        AValue := Elements[1];
      skLast:
        AValue := Elements[ElementCount];
      skRandom:
        AValue := Elements[1 + System.Random(ElementCount)];
    end;

  Result := Assigned(AValue);

end;

function TAlgosimObject.GetSubscriptedRef(
  ASubscript: TSubscript): TAlgosimObject;
begin
  if not TryGetSubscriptedRef(ASubscript, Result) then
    raise EAlgosimObjectException.Create(SUnsupportedSubscriptOp);
end;

function TAlgosimObject.GetTypeName: string;
begin
  Result := ClassTypeName;
end;

function TAlgosimObject.GetCapacity: Integer;
begin
  Result := -1;
end;

class constructor TAlgosimObject.ClassCreate;
var
  Context: TRttiContext;
  &Type: TRttiType;
  HasAttribute: Boolean;
  Attribute: TCustomAttribute;
begin

  _ASOClassData := TDictionary<TAlgosimObjectClass, TAlgosimObjectClassData>.Create;

  Context := TRttiContext.Create;
  try
    for &Type in Context.GetTypes do
      if &Type.IsInstance and (TRttiInstanceType(&Type).MetaclassType.InheritsFrom(TAlgosimObject)) then
      begin
        HasAttribute := False;
        for Attribute in &Type.GetAttributes do
          if Attribute is AlgosimObjectAttribute then
          begin
            _ASOClassData.Add
              (
                TAlgosimObjectClass(TRttiInstanceType(&Type).MetaclassType),
                AlgosimObjectAttribute(Attribute).Data
              );
            HasAttribute := True;
            Break;
          end;
        if not HasAttribute then
        begin
          _ASOClassData.Add
            (
              TAlgosimObjectClass(TRttiInstanceType(&Type).MetaclassType),
              TAlgosimObjectClassData.Create(
                TRttiInstanceType(&Type).MetaclassType.ClassName,
                []
              )
            );
        end;
      end
  finally
    Context.Free;
  end;

end;

class function TAlgosimObject.ClassData: TAlgosimObjectClassData;
begin
  Result := _ASOClassData[Self];
end;

class destructor TAlgosimObject.ClassDestroy;
begin
  FreeAndNil(_ASOClassData);
end;

class function TAlgosimObject.ClassFlags: TAlgosimObjectClassFlags;
begin
  Result := ClassData.ClassFlags;
end;

class function TAlgosimObject.SortClass: TSortClass;
begin
  Result := SORTCLASS_NULL;
end;

class function TAlgosimObject.SortClassCompare(const Left,
  Right: TAlgosimObject): Integer;
begin
  Result := 0;
end;

class function TAlgosimObject.SortClassSameObject(const Left,
  Right: TAlgosimObject; const AEpsilon: TASR): Boolean;
begin
  Result := False;
end;

class function TAlgosimObject.ClassTypeName: string;
begin
  Result := ClassData.ClassTypeName;
end;

function TAlgosimObject.GetValue(Index: Integer): TAlgosimObject;
begin
  Result := Elements[Index].Clone;
end;

function TAlgosimObject.GetValueCount: Integer;
begin
  Result := ElementCount;
end;

procedure TAlgosimObject.SetValue(Index: Integer; AValue: TAlgosimObject);
begin
  Elements[Index] := AValue;
end;

function TAlgosimObject.GetValueFromPoint(const APoint: TPoint): TAlgosimObject;
begin
  raise EAlgosimObjectException.CreateFmt(SObjectNoPlanarValueContainer, [ClassTypeName]);
end;

function TAlgosimObject.HasElementOfClass(
  AASOMetaclass: TAlgosimObjectClass): Boolean;
var
  i: Integer;
begin
  for i := 1 to ElementCount do
    if Elements[i] is AASOMetaclass then
      Exit(True);
  Result := False;
end;

procedure TAlgosimObject.SetValueFromPoint(const APoint: TPoint; AValue: TAlgosimObject);
begin
  AValue.Free; // since we own it
  raise EAlgosimObjectException.CreateFmt(SObjectNoPlanarValueContainer, [ClassTypeName]);
end;

procedure TAlgosimObject.Shuffle;
begin
  raise EAlgosimObjectException.CreateFmt(SCannotShuffle, [ClassTypeName]);
end;

procedure TAlgosimObject.Sort;
begin
  raise EAlgosimObjectException.CreateFmt(SCannotSort, [ClassTypeName]);
end;

procedure TAlgosimObject.Sort(AComparer: IComparer<TASR>);
begin
  raise EAlgosimObjectException.CreateFmt(SCannotSort, [ClassTypeName]);
end;

procedure TAlgosimObject.Sort(AComparer: IComparer<TASC>);
begin
  raise EAlgosimObjectException.CreateFmt(SCannotSort, [ClassTypeName]);
end;

procedure TAlgosimObject.Sort(AComparer: IComparer<TAlgosimObject>);
begin
  raise EAlgosimObjectException.CreateFmt(SCannotSort, [ClassTypeName]);
end;

function TAlgosimObject.IndexOfValue(AObj: TAlgosimObject): TAlgosimObject;
var
  i: Integer;
  size: TSize;
  y: Integer;
  x: Integer;
  obj: TAlgosimObject;
begin

  if IsObjectContainer then
  begin
    for i := 1 to ElementCount do
      if Elements[i].Equals(AObj) then
        Exit(ASOInt(i));
    Result := ASO(null);
  end

  else if IsValueContainer then
  begin

    if IsPlanarValueContainer then
    begin
      size := GetPlanarExtent;
      for y := 1 to size.cy do
        for x := 1 to size.cx do
        begin
          obj := GetValueFromPoint(Point(x, y));
          try
            if obj.Equals(AObj) then
              Exit(TAlgosimArray.CreateWithValue([y, x]));
          finally
            FreeAndNil(obj);
          end;
        end;
      Result := ASO(null);
    end
    else
    begin
      for i := 1 to ValueCount do
      begin
        obj := Values[i];
        try
          if obj.Equals(AObj) then
            Exit(ASOInt(i));
        finally
          FreeAndNil(obj);
        end;
      end;
      Result := ASO(null);
    end;

  end

  else
    raise EAlgosimObjectException.CreateFmt(SObjectNoContainer, [ClassTypeName]);

end;

function TAlgosimObject.IndexOfValueEps(AObj: TAlgosimObject;
  const AEpsilon: TASR): TAlgosimObject;
var
  i: Integer;
  size: TSize;
  y: Integer;
  x: Integer;
  obj: TAlgosimObject;
begin

  if IsObjectContainer then
  begin
    for i := 1 to ElementCount do
      if SameASO(Elements[i], AObj, AEpsilon) then
        Exit(ASOInt(i));
    Result := ASO(null);
  end

  else if IsValueContainer then
  begin

    if IsPlanarValueContainer then
    begin
      size := GetPlanarExtent;
      for y := 1 to size.cy do
        for x := 1 to size.cx do
        begin
          obj := GetValueFromPoint(Point(x, y));
          try
            if SameASO(obj, AObj, AEpsilon) then
              Exit(TAlgosimArray.CreateWithValue([y, x]));
          finally
            FreeAndNil(obj);
          end;
        end;
      Result := ASO(null);
    end
    else
    begin
      for i := 1 to ValueCount do
      begin
        obj := Values[i];
        try
          if SameASO(obj, AObj, AEpsilon) then
            Exit(ASOInt(i));
        finally
          FreeAndNil(obj);
        end;
      end;
      Result := ASO(null);
    end;

  end

  else
    raise EAlgosimObjectException.CreateFmt(SObjectNoContainer, [ClassTypeName]);

end;

function TAlgosimObject.IndicesOf(APredicate: TASOPredicate): TAlgosimArray;
var
  i: Integer;
  size: TSize;
  y: Integer;
  x: Integer;
  obj: TAlgosimObject;
begin

  Result := TAlgosimArray.Create;
  try

    if IsObjectContainer and IsOrderedContainer then
    begin
      Result.Capacity := CapCap(ElementCount);
      for i := 1 to ElementCount do
        if APredicate(Elements[i]) then
          Result.Add(ASOInt(i));
      Result.TrimExcess;
    end

    else if IsValueContainer then
    begin

      Result.Capacity := CapCap(ValueCount);

      if IsPlanarValueContainer then
      begin
        size := GetPlanarExtent;
        for y := 1 to size.cy do
          for x := 1 to size.cx do
          begin
            obj := GetValueFromPoint(Point(x, y));
            try
              if APredicate(obj) then
                Result.Add(TAlgosimArray.CreateWithValue([y, x]));
            finally
              FreeAndNil(obj);
            end;
          end;
      end
      else
      begin
        for i := 1 to ValueCount do
        begin
          obj := Values[i];
          try
            if APredicate(obj) then
              Result.Add(ASOInt(i));
          finally
            FreeAndNil(obj);
          end;
        end;
      end;

      Result.TrimExcess;

    end

    else
      raise EAlgosimObjectException.CreateFmt(SObjectNoContainer, [ClassTypeName]);

  except
    Result.Free;
    raise;
  end;

end;

function TAlgosimObject.IndicesOfValue(AObj: TAlgosimObject): TAlgosimArray;
var
  i: Integer;
  size: TSize;
  y: Integer;
  x: Integer;
  obj: TAlgosimObject;
begin

  Result := TAlgosimArray.Create;
  try

    if IsObjectContainer and IsOrderedContainer then
    begin
      Result.Capacity := CapCap(ElementCount);
      for i := 1 to ElementCount do
        if Elements[i].Equals(AObj) then
          Result.Add(ASOInt(i));
      Result.TrimExcess;
    end

    else if IsValueContainer then
    begin

      Result.Capacity := CapCap(ValueCount);

      if IsPlanarValueContainer then
      begin
        size := GetPlanarExtent;
        for y := 1 to size.cy do
          for x := 1 to size.cx do
          begin
            obj := GetValueFromPoint(Point(x, y));
            try
              if obj.Equals(AObj) then
                Result.Add(TAlgosimArray.CreateWithValue([y, x]));
            finally
              FreeAndNil(obj);
            end;
          end;
      end
      else
      begin
        for i := 1 to ValueCount do
        begin
          obj := Values[i];
          try
            if obj.Equals(AObj) then
              Result.Add(ASOInt(i));
          finally
            FreeAndNil(obj);
          end;
        end;
      end;

      Result.TrimExcess;

    end

    else
      raise EAlgosimObjectException.CreateFmt(SObjectNoContainer, [ClassTypeName]);

  except
    Result.Free;
    raise;
  end;

end;

function TAlgosimObject.IndicesOfValueEps(AObj: TAlgosimObject;
  const AEpsilon: TASR): TAlgosimArray;
var
  i: Integer;
  size: TSize;
  y: Integer;
  x: Integer;
  obj: TAlgosimObject;
begin

  Result := TAlgosimArray.Create;
  try

    if IsObjectContainer and IsOrderedContainer then
    begin
      Result.Capacity := CapCap(ElementCount);
      for i := 1 to ElementCount do
        if SameASO(Elements[i], AObj, AEpsilon) then
          Result.Add(ASOInt(i));
      Result.TrimExcess;
    end

    else if IsValueContainer then
    begin

      Result.Capacity := CapCap(ValueCount);

      if IsPlanarValueContainer then
      begin
        size := GetPlanarExtent;
        for y := 1 to size.cy do
          for x := 1 to size.cx do
          begin
            obj := GetValueFromPoint(Point(x, y));
            try
              if SameASO(obj, AObj, AEpsilon) then
                Result.Add(TAlgosimArray.CreateWithValue([y, x]));
            finally
              FreeAndNil(obj);
            end;
          end;
      end
      else
      begin
        for i := 1 to ValueCount do
        begin
          obj := Values[i];
          try
            if SameASO(obj, AObj, AEpsilon) then
              Result.Add(ASOInt(i));
          finally
            FreeAndNil(obj);
          end;
        end;
      end;

      Result.TrimExcess;

    end

    else
      raise EAlgosimObjectException.CreateFmt(SObjectNoContainer, [ClassTypeName]);

  except
    Result.Free;
    raise;
  end;

end;

procedure TAlgosimObject.Insert(AIndex: Integer; AElement: TAlgosimObject);
begin
  AElement.Free;
  raise EAlgosimObjectException.CreateFmt(SObjectNoContainer, [ClassTypeName]);
end;

procedure TAlgosimObject.Invoke;
begin

end;

function TAlgosimObject.IsASI: Boolean;
var
  dummy: TASI;
begin
  Result := TryToASI(dummy);
end;

function TAlgosimObject.IsRat: Boolean;
var
  dummy: TRationalNumber;
begin
  Result := TryToRat(dummy);
end;

function TAlgosimObject.IsASR: Boolean;
var
  dummy: TASR;
begin
  Result := TryToASR(dummy);
end;

class function TAlgosimObject.IsContainer: Boolean;
begin
  Result := IsObjectContainer or IsValueContainer;
end;

function TAlgosimObject.IsASC: Boolean;
var
  dummy: TASC;
begin
  Result := TryToASC(dummy);
end;

function TAlgosimObject.IsInt32: Boolean;
var
  dummy: Int32;
begin
  Result := TryToInt32(dummy);
end;

function TAlgosimObject.IsInt64: Boolean;
var
  dummy: Int64;
begin
  Result := TryToInt64(dummy);
end;

function TAlgosimObject.Last(N: Integer): TAlgosimObject;
begin
  Result := Part(ValueCount - N + 1, ValueCount);
end;

class function TAlgosimObject.LoadFromFile(const AFileName: string;
  AEncoding: TEncoding; const AParams: string): TAlgosimObject;
var
  ext: string;
  classpair: TPair<TAlgosimObjectClass, TAlgosimObjectClassData>;
  TextRep: TAlgosimString;
begin

  if Self = TAlgosimObject then
  begin
    ext := ExtractFileExt(AFileName).TrimLeft(['.']);
    for classpair in _ASOClassData do
      if IndexText(ext, classpair.Value.ExportExts) <> -1 then
        Exit(classpair.Key.LoadFromFile(AFileName, AEncoding, AParams));
    raise EAlgosimObjectException.CreateFmt(SUnknownFileExt, [ext]);
  end;

  // The following makes sense in child classes of TAlgosimObject
  // and serves as a default file loader from text-based data.

  if AEncoding = nil then
    AEncoding := TEncoding.UTF8;

  TextRep := ASO(TFile.ReadAllText(AFileName, AEncoding));
  try
    Result := Self.Create(TextRep);
  finally
    TextRep.Free;
  end;

end;

function TAlgosimObject.GetIsComplex: Boolean;
var
  i: Integer;
begin

  if IsObjectContainer then
  begin
    for i := 1 to ElementCount do
      if Elements[i].IsComplex then
        Exit(True);
    Result := False;
  end

  else
    Result := asoComplex in ClassFlags;

end;

function TAlgosimObject.GetMaxLen: Integer;
begin
  raise EAlgosimObjectException.CreateFmt(SNoMaxLen, [ClassTypeName]);
end;

function TAlgosimObject.GetMemorySize: UInt64;
begin
  Result := 0;
end;

class function TAlgosimObject.IsObjectContainer: Boolean;
begin
  Result := asoObjectContainer in ClassFlags
end;

class function TAlgosimObject.IsOrderedContainer: Boolean;
begin
  Result := asoOrderedContainer in ClassFlags;
end;

class function TAlgosimObject.IsPlanarContainer: Boolean;
begin
  Result := asoPlanarContainer in ClassFlags;
end;

class function TAlgosimObject.IsPlanarValueContainer: Boolean;
begin
  Result := IsValueContainer and IsPlanarContainer;
end;

class function TAlgosimObject.IsValueContainer: Boolean;
begin
  Result := asoValueContainer in ClassFlags;
end;

procedure TAlgosimObject.NoCopyConstr(AFrom: TAlgosimObject);
begin
  raise EAlgosimObjectException.CreateFmt(SObjectTypeHasNoCopyConstrForType,
    [Self.TypeName, AFrom.TypeName]
  );
end;

procedure TAlgosimObject.SaveToFile(const AFileName: string);
var
  Stream: TStringStream;
begin
  Stream := TStringStream.Create('', TEncoding.UTF8);
  try
    Stream.WriteString(ToString);
    Stream.SaveToFile(AFileName);
  finally
    Stream.Free;
  end;
end;

procedure TAlgosimObject.SetBinaryData(const Buf: PByte; const Len: UInt64);
begin
  raise EAlgosimObjectException.CreateFmt(SSetFromBlob, [ClassTypeName]);
end;

procedure TAlgosimObject.SaveToFile(ADlgOwner: TComponent;
  const ADefFileName: string);
var
  dlg: TFileSaveDialog;
begin

  dlg := TFileSaveDialog.Create(ADlgOwner);
  try
    if Length(ClassData.ExportExts) > 0 then
    begin
      with dlg.FileTypes.Add do
      begin
        FileMask := '*.' + ClassData.ExportExts[0];
        DisplayName := TxtSentenceCase(ClassData.ExportExts[0]) + ' files';
      end;
      dlg.DefaultExtension := ClassData.ExportExts[0];
    end
    else
      with dlg.FileTypes.Add do
      begin
        FileMask := '*.*';
        DisplayName := 'All files';
      end;
    dlg.FileName := ADefFileName;
    if dlg.Execute then
      SaveToFile(dlg.FileName);
  finally
    dlg.Free;
  end;

end;

procedure TAlgosimObject.SaveToFile(const AFileName: string;
  AOptions: TAlgosimStructure; AContext: TObject);
begin
  SaveToFile(AFileName);
end;

procedure TAlgosimObject.SetBinaryData(const AData: array of Byte);
begin
//  SetBinaryData(Pointer(AData), Length(AData));
  if Length(AData) <> 0 then
    SetBinaryData(@AData[0], Length(AData))
  else
    SetBinaryData(nil, 0)
end;

procedure TAlgosimObject.SetCapacity(const Value: Integer);
begin

end;

procedure TAlgosimObject.SetSubscript(ASubscript: TSubscript;
  AValue: TAlgosimObject);

  function SubscriptIsIntPair(out Idx1, Idx2: Integer): Boolean;
  begin
    Result := (ASubscript.Obj is TAlgosimArray) and
      (TAlgosimArray(ASubscript.Obj).ElementCount = 2) and
      (TAlgosimArray(ASubscript.Obj).ElementsAre(TAlgosimNumber)) and
      TAlgosimArray(ASubscript.Obj).Elements[1].TryToInt32(Idx1) and
      TAlgosimArray(ASubscript.Obj).Elements[2].TryToInt32(Idx2);
  end;

  procedure DoFail;
  begin
    AValue.Free;
    raise EAlgosimObjectException.Create(SUnsupportedSubscriptOp);
  end;

var
  IntIdx, IntIdx2: Integer;

begin

  case ASubscript.Kind of
    skIndexObject:
      begin
        if (ASubscript.Obj is TAlgosimNumber) and ASubscript.Obj.TryToInt32(IntIdx) then
          Values[IntIdx] := AValue
        else if SubscriptIsIntPair(IntIdx, IntIdx2) and IsPlanarValueContainer then
        begin
          ValueFromPoint[
            Point(
              IntIdx2,
              IntIdx
            )] := AValue;
        end
        else
          DoFail;
      end;
    skFirst:
      Values[1] := AValue;
    skLast:
      Values[-1] := AValue;
    skRandom:
      Values[1 + System.Random(ValueCount)] := AValue;
  else
    DoFail;
  end;

end;

function TAlgosimObject.N_sum: TAlgosimNumericEntity;
var
  e: TAlgosimObject;
begin

  if IsObjectContainer then
  begin

    if ElementCount = 0 then
      Exit(ASOInt(0));

    e := Elements[1];

    if e is TAlgosimNumber then
      Result := N_NumberSum

    else if e is TAlgosimVector then
      Result := N_VectorSum

    else if e is TAlgosimMatrix then
      Result := N_MatrixSum

    else
      raise EAlgosimObjectException.Create(SCannotComputeSumOfContainer);

  end

  else if IsValueContainer then
    Result := N_NumberSum

  else
    raise EAlgosimObjectException.Create(SCannotComputeSumOfObject);

end;

procedure TAlgosimObject.Swap(Index1, Index2: Integer);
begin
  raise EAlgosimObjectException.CreateFmt(SObjectNoContainer, [ClassTypeName]);
end;

function TAlgosimObject.N_ArithmeticMean: TAlgosimNumericEntity;
begin

  if IsContainer then
    if ValueCount > 0 then
      with N_sum do
        try
          Result := ScaledBy(1/Self.ValueCount) {"Self" required}
        finally
          Free;
        end
    else
      raise EAlgosimObjectException.Create(SCannotComputeAvgEmptyList)
  else
    raise EAlgosimObjectException.CreateFmt(SCannotComputeAvg, [ClassTypeName]);

end;

function TAlgosimObject.N_GeometricMean: TAlgosimNumber;
var
  val: TAlgosimObject;
  Rval: TASR;
  Cval: TASC;
  i: Integer;
  p: TASR;
begin

  Rval := 1;
  Cval := 1;

  if IsContainer then
  begin
    if ValueCount > 0 then
    begin
      p := 1 / ValueCount;
      if IsObjectContainer then
      begin
        for i := 1 to ElementCount do
          if Elements[i] is TAlgosimNumber then
            if Elements[i].IsComplex then
              Cval := Cval * ASnum.cpow(Elements[i].ToASC, p)
            else
              Rval := Rval * ASnum.pow(Elements[i].ToASR, p)
          else
            raise EAlgosimObjectException.Create(SCannotComputeAvgNonSpecific)
      end
      else
      begin
        for i := 1 to ValueCount do
        begin
          val := Values[i];
          try
            if val is TAlgosimNumber then
              if val.IsComplex then
                Cval := Cval * ASnum.cpow(val.ToASC, p)
              else
                Rval := Rval * ASnum.pow(val.ToASR, p)
            else
              raise EAlgosimObjectException.Create(SCannotComputeAvgNonSpecific)
          finally
            val.Free;
          end;
        end
      end;
    end
    else
      raise EAlgosimObjectException.Create(SCannotComputeAvgEmptyList)
  end
  else
    raise EAlgosimObjectException.CreateFmt(SCannotComputeAvg, [ClassTypeName]);

  if Cval = 1 then
    Result := ASO(Rval)
  else
    Result := ASO(Rval * Cval);

end;

function TAlgosimObject.N_HarmonicMean: TAlgosimNumber;
var
  val: TAlgosimObject;
  Rval: TASR;
  Cval: TASC;
  i: Integer;
begin

  Rval := 0;
  Cval := 0;

  if IsContainer then
  begin
    if ValueCount > 0 then
    begin
      if IsObjectContainer then
      begin
        for i := 1 to ElementCount do
          if Elements[i] is TAlgosimNumber then
            if Elements[i].IsComplex then
              Cval := Cval + Elements[i].ToASC.Inverse
            else
              Rval := Rval + 1 / Elements[i].ToASR
          else
            raise EAlgosimObjectException.Create(SCannotComputeAvgNonSpecific)
      end
      else
      begin
        for i := 1 to ValueCount do
        begin
          val := Values[i];
          try
            if val is TAlgosimNumber then
              if val.IsComplex then
                Cval := Cval + val.ToASC.Inverse
              else
                Rval := Rval + 1 / val.ToASR
            else
              raise EAlgosimObjectException.Create(SCannotComputeAvgNonSpecific)
          finally
            val.Free;
          end;
        end
      end;
    end
    else
      raise EAlgosimObjectException.Create(SCannotComputeAvgEmptyList)
  end
  else
    raise EAlgosimObjectException.CreateFmt(SCannotComputeAvg, [ClassTypeName]);

  if Cval = 0 then
    Result := ASO(ValueCount / Rval)
  else
    Result := ASO(ValueCount / (Rval + Cval));

end;

function TAlgosimObject.N_MatrixProduct: TAlgosimMatrix;
var
  len: Integer;
  e: TAlgosimObject;
  S: TMatrixSize;
  R: TRealMatrix;
  C: TComplexMatrix;
  i: Integer;
  OnlyReal: Boolean;
begin

  if IsObjectContainer then
  begin

    len := ElementCount;
    if len = 0 then
      raise EAlgosimObjectException.Create(SCannotComputeProductOfUnknownType);

    e := Elements[1];
    if not (e is TAlgosimMatrix) then
      raise EAlgosimObjectException.Create(SCannotComputeProductOfContainer);

    S := TAlgosimMatrix(e).Dimension;

    if not S.IsSquare then
      raise EAlgosimObjectException.Create(SCannotComputeProductOfContainer);

    R := IdentityMatrix(S.Rows);
    C := ComplexIdentityMatrix(S.Rows);

    OnlyReal := true;

    for i := 1 to len do
    begin

      e := Elements[i];

      if not (e is TAlgosimMatrix) then
        raise EAlgosimObjectException.Create(SCannotComputeProductOfContainer);

      if OnlyReal then
      begin
        if e.IsComplex then
        begin
          C := R * e.AsComplexMatrix;
          OnlyReal := false;
        end
        else
          R := R * e.AsRealMatrix
      end

      else
        C := C * e.AsComplexMatrix;

    end;

    if OnlyReal then
      Exit(ASO(R))
    else
      Exit(ASO(C));

  end
  else
    raise EAlgosimObjectException.Create(SCannotComputeProductOfObject);

end;

function TAlgosimObject.N_MatrixSum: TAlgosimMatrix;
var
  len: Integer;
  size: TMatrixSize;
  e: TAlgosimObject;
  R: TRealMatrix;
  C: TComplexMatrix;
  i: Integer;
begin

  if IsObjectContainer then
  begin

    len := ElementCount;
    if len = 0 then
      raise EAlgosimObjectException.Create(SCannotComputeSumOfUnknownType);

    e := Elements[1];
    if not (e is TAlgosimMatrix) then
      raise EAlgosimObjectException.Create(SCannotComputeSumOfContainer);

    size := TAlgosimMatrix(e).Dimension;

    R := ZeroMatrix(size);
    C := ComplexZeroMatrix(size);

    for i := 1 to len do
    begin
      e := Elements[i];
      if not (e is TAlgosimMatrix) then
        raise EAlgosimObjectException.Create(SCannotComputeSumOfContainer);
      if e.IsComplex then
        C := C + e.AsComplexMatrix
      else
        R := R + e.AsRealMatrix
    end;

    if C = ComplexZeroMatrix(size) then
      Exit(ASO(R))
    else
      Exit(ASO(R + C));

  end

  else
    raise EAlgosimObjectException.Create(SCannotComputeSumOfObject);

end;

function TAlgosimObject.N_max: TAlgosimNumber;
var
  val: TAlgosimObject;
  i: Integer;
  AllInts: Boolean;
  MaxX: TASR;
  MaxN: TASI;
begin

  MaxX := 0; // to make compiler happy
  MaxN := 0;

  if IsContainer then
  begin
    if ValueCount > 0 then
    begin
      if IsObjectContainer then
      begin
        AllInts := True;
        if Elements[1] is TAlgosimNumber then
        begin
          if Elements[1] is TAlgosimInteger then
            MaxN := Elements[1].ToASI
          else
          begin
            MaxX := Elements[1].ToASR;
            AllInts := False;
          end;
        end
        else
          raise EAlgosimObjectException.Create(SCannotFindMaxNonSpecific);
        for i := 2 to ElementCount do
        begin
          if Elements[i] is TAlgosimNumber then
          begin
            if AllInts then
            begin
              if Elements[i] is TAlgosimInteger then
                MaxN := Math.Max(MaxN, Elements[i].ToASI)
              else
              begin
                MaxX := Math.Max(MaxN, Elements[i].ToASR);
                AllInts := False;
              end;
            end
            else
              MaxX := Math.Max(MaxX, Elements[i].ToASR)
          end
          else
            raise EAlgosimObjectException.Create(SCannotFindMaxNonSpecific)
        end;
        if AllInts then
          Result := ASOInt(MaxN)
        else
          Result := ASO(MaxX);
      end
      else
      begin
        val := Values[1];
        try
          if val is TAlgosimNumber then
            MaxX := val.ToASR
          else
            raise EAlgosimObjectException.Create(SCannotFindMaxNonSpecific);
        finally
          val.Free;
        end;
        for i := 2 to ValueCount do
        begin
          val := Values[i];
          try
            if val is TAlgosimNumber then
              MaxX := Math.Max(MaxX, val.ToASR)
            else
              raise EAlgosimObjectException.Create(SCannotFindMaxNonSpecific)
          finally
            val.Free;
          end;
        end;
        Result := ASO(MaxX);
      end;
    end
    else
      raise EAlgosimObjectException.Create(SCannotFindMaximumEmpty)
  end
  else
    raise EAlgosimObjectException.CreateFmt(SCannotFindMax, [ClassTypeName]);

end;

function TAlgosimObject.N_min: TAlgosimNumber;
var
  val: TAlgosimObject;
  i: Integer;
  AllInts: Boolean;
  MinX: TASR;
  MinN: TASI;
begin

  MinX := 0; // to make compiler happy
  MinN := 0;

  if IsContainer then
  begin
    if ValueCount > 0 then
    begin
      if IsObjectContainer then
      begin
        AllInts := True;
        if Elements[1] is TAlgosimNumber then
        begin
          if Elements[1] is TAlgosimInteger then
            MinN := Elements[1].ToASI
          else
          begin
            MinX := Elements[1].ToASR;
            AllInts := False;
          end;
        end
        else
          raise EAlgosimObjectException.Create(SCannotFindMinNonSpecific);
        for i := 2 to ElementCount do
        begin
          if Elements[i] is TAlgosimNumber then
          begin
            if AllInts then
            begin
              if Elements[i] is TAlgosimInteger then
                MinN := Math.Min(MinN, Elements[i].ToASI)
              else
              begin
                MinX := Math.Min(MinN, Elements[i].ToASR);
                AllInts := False;
              end;
            end
            else
              MinX := Math.Min(MinX, Elements[i].ToASR)
          end
          else
            raise EAlgosimObjectException.Create(SCannotFindMinNonSpecific)
        end;
        if AllInts then
          Result := ASOInt(MinN)
        else
          Result := ASO(MinX);
      end
      else
      begin
        val := Values[1];
        try
          if val is TAlgosimNumber then
            MinX := val.ToASR
          else
            raise EAlgosimObjectException.Create(SCannotFindMinNonSpecific);
        finally
          val.Free;
        end;
        for i := 2 to ValueCount do
        begin
          val := Values[i];
          try
            if val is TAlgosimNumber then
              MinX := Math.Min(MinX, val.ToASR)
            else
              raise EAlgosimObjectException.Create(SCannotFindMinNonSpecific)
          finally
            val.Free;
          end;
        end;
        Result := ASO(MinX);
      end;
    end
    else
      raise EAlgosimObjectException.Create(SCannotFindMinimumEmpty)
  end
  else
    raise EAlgosimObjectException.CreateFmt(SCannotFindMin, [ClassTypeName]);


end;

function TAlgosimObject.N_NumberProduct: TAlgosimNumber;
var
  len: Integer;
  e: TAlgosimObject;
  N: TASI;
  R: TASR;
  C: TASC;
  i: Integer;
  AllInts: Boolean;
begin

  if IsObjectContainer then
  begin

    len := ElementCount;
    if len = 0 then
      Exit(ASOInt(1));

    AllInts := True;
    N := 1;
    R := 1;
    C := 1;

    for i := 1 to len do
    begin
      e := Elements[i];
      if not (e is TAlgosimNumber) then
        raise EAlgosimObjectException.Create(SCannotComputeProductOfContainer);
      if AllInts then
      begin
        if e is TAlgosimInteger then
          N := N * TAlgosimInteger(e).Value
        else
          AllInts := False;
      end;
      if e.IsComplex then
        C := C * e.ToASC
      else
        R := R * e.ToASR;
    end;

    if AllInts and SameValue(N, R) then
      Exit(ASOInt(N))
    else if C = 1 then
      Exit(ASO(R))
    else
      Exit(ASO(R * C));

  end

  else if IsValueContainer then
  begin

    len := ValueCount;
    if len = 0 then
      Exit(ASOInt(1));

    AllInts := True;
    N := 1;
    R := 1;
    C := 1;

    for i := 1 to len do
    begin
      e := Values[i];
      try
        if not (e is TAlgosimNumber) then
          raise EAlgosimObjectException.Create(SCannotComputeProductOfContainer);
        if AllInts then
        begin
          if e is TAlgosimInteger then
            N := N * TAlgosimInteger(e).Value
          else
            AllInts := False;
        end;
        if e.IsComplex then
          C := C * e.ToASC
        else
          R := R * e.ToASR;
      finally
        e.Free;
      end;
    end;

    if AllInts and SameValue(N, R) then
      Exit(ASOInt(N))
    else if C = 1 then
      Exit(ASO(R))
    else
      Exit(ASO(R * C));

  end

  else
    raise EAlgosimObjectException.Create(SCannotComputeProductOfObject);

end;

function TAlgosimObject.N_NumberSum: TAlgosimNumber;
var
  len: Integer;
  e: TAlgosimObject;
  N: TASI;
  R: TASR;
  C: TASC;
  i: Integer;
  AllInts: Boolean;
begin

  if IsObjectContainer then
  begin

    len := ElementCount;
    if len = 0 then
      Exit(ASOInt(0));

    AllInts := True;
    N := 0;
    R := 0;
    C := 0;

    for i := 1 to len do
    begin
      e := Elements[i];
      if not (e is TAlgosimNumber) then
        raise EAlgosimObjectException.Create(SCannotComputeSumOfContainer);
      if AllInts then
      begin
        if e is TAlgosimInteger then
          N := N + TAlgosimInteger(e).Value
        else
          AllInts := False;
      end;
      if e.IsComplex then
        C := C + e.ToASC
      else
        R := R + e.ToASR;
    end;

    if AllInts and SameValue(N, R) then
      Exit(ASOInt(N))
    else if C = 0 then
      Exit(ASO(R))
    else
      Exit(ASO(R + C));

  end

  else if IsValueContainer then
  begin

    len := ValueCount;
    if len = 0 then
      Exit(ASOInt(0));

    AllInts := True;
    N := 0;
    R := 0;
    C := 0;

    for i := 1 to len do
    begin
      e := Values[i];
      try
        if not (e is TAlgosimNumber) then
          raise EAlgosimObjectException.Create(SCannotComputeSumOfContainer);
        if AllInts then
        begin
          if e is TAlgosimInteger then
            N := N + TAlgosimInteger(e).Value
          else
            AllInts := False;
        end;
        if e.IsComplex then
          C := C + e.ToASC
        else
          R := R + e.ToASR;
      finally
        e.Free;
      end;
    end;

    if AllInts and SameValue(N, R) then
      Exit(ASOInt(N))
    else if C = 0 then
      Exit(ASO(R))
    else
      Exit(ASO(R + C));

  end

  else
    raise EAlgosimObjectException.Create(SCannotComputeSumOfObject);

end;

function TAlgosimObject.N_VectorSum: TAlgosimVector;
var
  len, dim: Integer;
  e: TAlgosimObject;
  R: TRealVector;
  C: TComplexVector;
  i: Integer;
begin

  if IsObjectContainer then
  begin

    len := ElementCount;
    if len = 0 then
      raise EAlgosimObjectException.Create(SCannotComputeSumOfUnknownType);

    e := Elements[1];
    if not (e is TAlgosimVector) then
      raise EAlgosimObjectException.Create(SCannotComputeSumOfContainer);

    dim := TAlgosimVector(e).Dimension;

    R := ZeroVector(dim);
    C := ComplexZeroVector(dim);

    for i := 1 to len do
    begin
      e := Elements[i];
      if not (e is TAlgosimVector) then
        raise EAlgosimObjectException.Create(SCannotComputeSumOfContainer);
      if e.IsComplex then
        C := C + e.AsComplexVector
      else
        R := R + e.AsRealVector
    end;

    if C = ComplexZeroVector(dim) then
      Exit(ASO(R))
    else
      Exit(ASO(R + C));

  end

  else
    raise EAlgosimObjectException.Create(SCannotComputeSumOfObject);

end;

function TAlgosimObject.Part(const AIndices: array of Integer): TAlgosimObject;
var
  i: Integer;
  HighIdx: Integer;
begin
  Result := TAlgosimArray.Create;
  try
    HighIdx := ValueCount;
    Result.Capacity := Length(AIndices);
    for i := Low(AIndices) to High(AIndices) do
      if InRange(AIndices[i], 1, HighIdx) then
        Result.AddElement(Values[AIndices[i]])
      else
        raise EAlgosimObjectException.CreateFmt(SIndexOutOfBounds, [AIndices[i]]);
    Result.TrimExcess;
  except
    Result.Free;
    raise;
  end;
end;

function TAlgosimObject.Part2d(const AIndicesX,
  AIndicesY: array of Integer): TAlgosimObject;
begin
  raise EAlgosimObjectException.CreateFmt(SCannotExtractPart2d, [ClassTypeName]);
end;

function TAlgosimObject.Pick(APredicate: TASOPredicate; ALevel: Integer): TAlgosimArray;
begin
  Result := TAlgosimArray.Create;
  try
    AddMembersToArray(Result, APredicate, ALevel);
  except
    Result.Free;
    raise;
  end;
end;

function TAlgosimObject.PickRecursive(APredicate: TASOPredicate): TAlgosimArray;
begin
  Result := TAlgosimArray.Create;
  try
    AddMembersToArray(Result, APredicate);
  except
    Result.Free;
    raise;
  end;
end;

function TAlgosimObject.Part2d(const ARangesX,
  ARangesY: array of TRange): TAlgosimObject;
begin
  Result := Part2d(
    ParseRangeSeq(ARangesX, PlanarExtent.cx),
    ParseRangeSeq(ARangesY, PlanarExtent.cy));
end;

function TAlgosimObject.Part(const ARanges: array of TRange): TAlgosimObject;
begin
  Result := Part(ParseRangeSeq(ARanges, Self.ValueCount));
end;

function TAlgosimObject.N_product: TAlgosimNumericEntity;
var
  e: TAlgosimObject;
begin

  if IsObjectContainer then
  begin

    if ElementCount = 0 then
      Exit(ASOInt(1));

    e := Elements[1];

    if e is TAlgosimNumber then
      Result := N_NumberProduct

    else if (e is TAlgosimMatrix) and IsOrderedContainer then
      Result := N_MatrixProduct

    else
      raise EAlgosimObjectException.Create(SCannotComputeProductOfContainer);

  end

  else if IsValueContainer then
    Result := N_NumberProduct

  else
    raise EAlgosimObjectException.Create(SCannotComputeProductOfObject);

end;

procedure TAlgosimObject.Replace(APredicate: TASOPredicate;
  ANewValue: TAlgosimObject; ALevel: Integer);
var
  i: Integer;
begin
  if IsObjectContainer then
  begin
    for i := 1 to ElementCount do
      if ALevel > 1 then
        Elements[i].Replace(APredicate, ANewValue, ALevel - 1)
      else
        if not Assigned(APredicate) or APredicate(Elements[i]) then
          Elements[i] := ANewValue.Clone;
  end
  else
    raise EAlgosimObjectException.CreateFmt(SCannotReplace, [ClassTypeName]);
end;

procedure TAlgosimObject.Replace(AOldValue, ANewValue: TAlgosimObject;
  ALevel: Integer);
begin
  Replace(
    function(AObject: TAlgosimObject): Boolean
    begin
      Result := AObject.Equals(AOldValue)
    end,
    ANewValue,
    ALevel);
end;

procedure TAlgosimObject.Replace(ANewValue: TAlgosimObject; ALevel: Integer);
begin
  Replace(TASOPredicate(nil), ANewValue, ALevel);
end;

function TAlgosimObject.Part(A, B: Integer): TAlgosimObject;
var
  i: Integer;
begin

  Result := TAlgosimArray.Create;
  try
    if (A > B) or (A > ValueCount) or (B < 1) then Exit;
    A := Max(A, 1);
    B := Min(B, ValueCount);
    Result.Capacity := B - A + 1;
    for i := A to B do
      Result.AddElement(Values[i]);
  except
    Result.Free;
    raise;
  end;

end;

function TAlgosimObject.Part(A: Integer): TAlgosimObject;
begin
  Result := Part(A, ValueCount);
end;

procedure TAlgosimObject.Reverse;
begin
  raise EAlgosimObjectException.CreateFmt(SCannotReverse, [ClassTypeName]);
end;

function TAlgosimObject.RotLeft(N: Integer): TAlgosimObject;
begin
  raise EAlgosimObjectException.CreateFmt(SCannotRotate, [ClassTypeName]);
end;

function TAlgosimObject.RotRight(N: Integer): TAlgosimObject;
begin
  raise EAlgosimObjectException.CreateFmt(SCannotRotate, [ClassTypeName]);
end;

function TAlgosimObject.Row(const AIndex: Integer): TAlgosimObject;
begin
  Result := GetSubscriptedValue(TSubscript.Create(skRowIndex, AIndex));
end;

function TAlgosimObject.Rows: TAlgosimArray;
var
  i: Integer;
begin
  Result := TAlgosimArray.Create;
  try
    for i := 1 to PlanarExtent.cy do
      Result.Add(Row(i));
  except
    Result.Free;
    raise;
  end;
end;

function TAlgosimObject.ToASI: TASI;
begin
  if not TryToASI(Result) then
    raise EAlgosimObjectException.CreateFmt(SObjNoDBitInt, [8*Result.Size]);
end;

function TAlgosimObject.ToRat: TRationalNumber;
begin
  if not TryToRat(Result) then
    raise EAlgosimObjectException.Create(SObjNoRat);
end;

function TAlgosimObject.ToRationalNumber: TRationalNumber;
begin
  Result := ToRat;
end;

function TAlgosimObject.ToASR: TASR;
begin
  if not TryToASR(Result) then
    raise EAlgosimObjectException.Create(SObjNoFloat);
end;

function TAlgosimObject.ToASC: TASC;
begin
  if not TryToASC(Result) then
    raise EAlgosimObjectException.Create(SObjNoComplex);
end;

function TAlgosimObject.ToBoolean: Boolean;
begin
  if IsContainer then
    Result := ValueCount > 0
  else
    Result := True;
end;

function TAlgosimObject.ToCharacter: Char;
begin
  raise EAlgosimObjectException.CreateFmt(SConvChar, [ClassTypeName]);
end;

function TAlgosimObject.ToColor: TRGB;
begin
  raise EAlgosimObjectException.CreateFmt(SConvColor, [ClassTypeName]);
end;

function TAlgosimObject.ToComplexMatrix: TComplexMatrix;
begin
  raise EAlgosimObjectException.CreateFmt(SConvComplexMat, [ClassTypeName]);
end;

function TAlgosimObject.ToComplexNumber: TASC;
var
  obj: TAlgosimObject;
begin

  if IsObjectContainer then
  begin
    case ElementCount of
      0:
        raise EAlgosimObjectException.Create(SConvEmptyContNumber);
      1:
        Exit(Elements[1].ToComplexNumber)
    else
      raise EAlgosimObjectException.Create(SConvContNumber);
    end;
  end

  else if IsValueContainer then
  begin

    case ValueCount of
      0:
        raise EAlgosimObjectException.Create(SConvEmptyContNumber);
      1:
        begin
          obj := Values[1];
          try
            Exit(obj.ToComplexNumber);
          finally
            obj.Free;
          end;
        end
    else
      raise EAlgosimObjectException.Create(SConvContNumber);
    end;

  end

  else
    raise EAlgosimObjectException.CreateFmt(SConvObjComplexNum, [ClassTypeName]);

end;

function TAlgosimObject.ToComplexVector: TComplexVector;
var
  i, j, count: Integer;
begin

  if IsObjectContainer then
  begin
    count := 0;
    for i := 1 to ElementCount do
      if Elements[i] is TAlgosimNumber then
        Inc(count);
    Result.Dimension := count;
    j := 0;
    for i := 1 to ElementCount do
      if Elements[i] is TAlgosimNumber then
      begin
        Result[j] := Elements[i].ToComplexNumber;
        Inc(j);
      end;
  end

  else
    raise EAlgosimObjectException.CreateFmt(SConvComplexVect, [ClassTypeName]);

end;

function TAlgosimObject.ToInputString: string;
begin
  Result := GetAsSingleLineText(InputFormOptions);
end;

function TAlgosimObject.ToInt32: Int32;
begin
  if not TryToInt32(Result) then
    raise EAlgosimObjectException.CreateFmt(SObjNoDBitInt, [8*Result.Size]);
end;

function TAlgosimObject.ToInt64: Int64;
begin
  if not TryToInt64(Result) then
    raise EAlgosimObjectException.CreateFmt(SObjNoDBitInt, [8*Result.Size]);
end;

function TAlgosimObject.ToInteger: TASI;
begin
  Result := ToASI;
end;

function TAlgosimObject.ToList: TAlgosimArray;
var
  i: Integer;
begin
  Result := TAlgosimArray.Create;
  try
    if IsContainer then
      for i := 1 to ValueCount do
        Result.Add(Values[i])
    else
      Result.Add(Clone);
  except
    Result.Free;
    raise;
  end;
end;

function TAlgosimObject.ToTable: TASTable;
var
  y, x: Integer;
  obj: TAlgosimObject;
begin

  Result := TASTable.Create;
  try

    if IsPlanarValueContainer then
    begin
      with PlanarExtent do
        Result.SetSize(cx, cy);
      for y := 1 to Result.Height do
        for x := 1 to Result.Width do
        begin
          obj := ValueFromPoint[Point(x, y)];
          try
            Result.Strings[Point(x - 1, y - 1)] := obj.ToString;
          finally
            obj.Free;
          end;
        end;
    end
    else if IsObjectContainer then
    begin
      Result.SetSize(1, ElementCount);
      for y := 1 to ElementCount do
        Result.Strings[Point(0, y - 1)] := Elements[y].ToString;
    end
    else if IsValueContainer then
    begin
      Result.SetSize(1, ValueCount);
      for y := 1 to ValueCount do
      begin
        obj := Values[y];
        try
          Result.Strings[Point(0, y - 1)] := obj.ToString;
        finally
          obj.Free;
        end;
      end;
    end
    else
    begin
      Result.SetSize(1, 1);
      Result.Strings[Point(0, 0)] := ToString;
    end;

  except
    Result.Free;
    raise;
  end;

end;

function TAlgosimObject.ToMatrix: TAlgosimMatrix;
begin
  if IsComplex then
    Result := ASO(ToComplexMatrix)
  else
    Result := ASO(ToRealMatrix);
end;

function TAlgosimObject.ToNumber: TAlgosimObject;
begin
  if Self is TAlgosimNumber then
    Result := Clone
  else if IsObjectContainer and (ElementCount = 1) then
    Result := Elements[1].ToNumber
  else if IsComplex then
    Result := ASO(ToComplexNumber)
  else
    Result := ASO(ToRealNumber);
end;

function TAlgosimObject.ToPixel: TASPixel;
begin
  Result := ToColor;
end;

function TAlgosimObject.ToPreviewString: string;
begin
  Result := GetAsSingleLineText(DefaultFormatOptions);
  if Result.Length > 1024 then
    SetLength(Result, 1024);
end;

function TAlgosimObject.ToRealMatrix: TRealMatrix;
begin
  raise EAlgosimObjectException.CreateFmt(SConvRealMat, [ClassTypeName]);
end;

function TAlgosimObject.ToRealNumber: TASR;
var
  obj: TAlgosimObject;
begin

  if IsObjectContainer then
  begin

    case ElementCount of
      0:
        raise EAlgosimObjectException.Create(SConvEmptyContNumber);
      1:
        Exit(Elements[1].ToRealNumber)
    else
      raise EAlgosimObjectException.Create(SConvContNumber);
    end;

  end

  else if IsValueContainer then
  begin

    case ValueCount of
      0:
        raise EAlgosimObjectException.Create(SConvEmptyContNumber);
      1:
        begin
          obj := Values[1];
          try
            Exit(obj.ToRealNumber);
          finally
            obj.Free;
          end;
        end
    else
      raise EAlgosimObjectException.Create(SConvContNumber);
    end;

  end

  else
    raise EAlgosimObjectException.CreateFmt(SConvObjRealNum, [ClassTypeName]);

end;

function TAlgosimObject.ToRealVector: TRealVector;
var
  i, j, count: Integer;
begin

  if IsObjectContainer then
  begin
    count := 0;
    for i := 1 to ElementCount do
      if Elements[i] is TAlgosimNumber then
        Inc(count);
    Result.Dimension := count;
    j := 0;
    for i := 1 to ElementCount do
      if Elements[i] is TAlgosimNumber then
      begin
        Result[j] := Elements[i].ToRealNumber;
        Inc(j);
      end;
  end

  else
    raise EAlgosimObjectException.CreateFmt(SConvRealVect, [ClassTypeName]);

end;

function TAlgosimObject.ToSet: TAlgosimSet;
var
  i: Integer;
begin
  Result := TAlgosimSet.Create;
  try
    if IsObjectContainer then
      for i := 1 to ElementCount do
        Result.AddElement(Elements[i].Clone)
    else if IsValueContainer then
      for i := 1 to ValueCount do
        Result.AddElement(Values[i])
    else
      Result.AddElement(Clone);
  except
    Result.Free;
    raise;
  end;
end;

function TAlgosimObject.ToSpeech: string;
begin
  Result := ToString;
end;

function TAlgosimObject.ToBinaryObject: TAlgosimBinaryData;
var
  buf: PByte;
  len: UInt64;
begin

  if not GetBinaryData(buf, len) then
    raise EAlgosimObjectException.CreateFmt(SConvBlob, [ClassTypeName]);

  Result := TAlgosimBinaryData.Create;
  try
    Result.DataLength := len;
    if len > 0 then
      CopyMemory(Result.Data, buf, len);
  except
    Result.Free;
    raise;
  end;

end;

function TAlgosimObject.ToString: string;
begin
  Result := GetTypeName;
end;

function TAlgosimObject.ToVector: TAlgosimVector;
begin
  if IsComplex then
    Result := ASO(ToComplexVector)
  else
    Result := ASO(ToRealVector);
end;

procedure TAlgosimObject.TrimExcess;
begin

end;

procedure TAlgosimObject.Truncate(ANewLength: Integer);
begin
  raise EAlgosimObjectException.CreateFmt(SObjectNoContainer, [ClassTypeName]);
end;

function TAlgosimObject.UnaryMinus: TAlgosimObject;
begin
  raise EAlgosimObjectException.CreateFmt(SCannotComputeUnaryMinus,
    [ClassTypeName]);
end;

function TAlgosimObject.WithSpecificValues(
  AValues: TAlgosimArray): TAlgosimObject;
var
  i: Integer;
begin
  if IsContainer then
  begin
    Result := ASOClassType.Create;
    try
      for i := 1 to AValues.ElementCount do
        Result.Append(AValues.Elements[i].Clone);
    except
      Result.Free;
      raise;
    end;
  end
  else
    raise EAlgosimObjectException.CreateFmt(SObjectNoContainer, [ClassTypeName]);
end;

function TAlgosimObject.Random: TAlgosimObject;
begin
  Result := Values[1 + System.Random(ValueCount)];
end;

class function TAlgosimObject.RefEqualityComparer: IEqualityComparer<TAlgosimObject>;
begin
  Result := TEqualityComparer<TAlgosimObject>.Construct(
    function(const Left, Right: TAlgosimObject): Boolean
    begin
      Result := Left = Right;
    end,
    function(const Value: TAlgosimObject): Integer
    begin
    {$IFDEF CPUX64}
      Result := Integer(IntPtr(Value)) xor Integer(IntPtr(Value) shr 32);
    {$ELSE !CPUX64}
      Result := Integer(IntPtr(Value));
    {$ENDIF !CPUX64}
    end
  );
end;

procedure TAlgosimObject.Remove(const ARanges: array of TRange);
begin
  Remove(ParseRangeSeq(ARanges, ValueCount));
end;

procedure TAlgosimObject.Remove(const AIndices: array of Integer);
begin
  raise EAlgosimObjectException.CreateFmt(SObjectNoContainer, [ClassTypeName]);
end;

function TAlgosimObject.RemoveAdjacentDuplicates: TAlgosimObject;
begin
  raise EAlgosimObjectException.CreateFmt(SCannotConstructUniqueFromSorted, [ClassTypeName]);
end;

function TAlgosimObject.RemoveAdjacentDuplicatesEps(
  const Epsilon: TASR): TAlgosimObject;
begin
  raise EAlgosimObjectException.CreateFmt(SCannotConstructUniqueFromSorted, [ClassTypeName]);
end;

procedure TAlgosimObject.RemoveAll(AOldValue: TAlgosimObject; ALevel: Integer);
begin
  RemoveIf(
    function(AObject: TAlgosimObject): Boolean
    begin
      Result := AObject.Equals(AOldValue)
    end,
    ALevel);
end;

function TAlgosimObject.RemoveDuplicates: TAlgosimObject;
begin
  raise EAlgosimObjectException.CreateFmt(SCannotConstructUnique, [ClassTypeName]);
end;

function TAlgosimObject.RemoveDuplicatesEps(
  const Epsilon: TASR): TAlgosimObject;
begin
  raise EAlgosimObjectException.CreateFmt(SCannotConstructUnique, [ClassTypeName]);
end;

procedure TAlgosimObject.RemoveFirst(N: Integer);
begin
  raise EAlgosimObjectException.CreateFmt(SObjectNoContainer, [ClassTypeName]);
end;

procedure TAlgosimObject.RemoveIf(APredicate: TASOPredicate; ALevel: Integer);
var
  i: Integer;
  IndicesToRemove: TArray<Integer>;
  c: Integer;
  Val: TAlgosimObject;
begin
  if IsObjectContainer then
  begin
    if ALevel > 1 then
      for i := 1 to ElementCount do
        Elements[i].RemoveIf(APredicate, ALevel - 1)
    else
    begin
      SetLength(IndicesToRemove, ElementCount);
      c := 0;
      for i := 1 to ElementCount do
        if not Assigned(APredicate) or APredicate(Elements[i]) then
        begin
          IndicesToRemove[c] := i;
          Inc(c);
        end;
      SetLength(IndicesToRemove, c);
      Remove(IndicesToRemove);
    end;
  end
  else if IsValueContainer and (ALevel = 1) then
  begin
    SetLength(IndicesToRemove, ValueCount);
    c := 0;
    for i := 1 to ValueCount do
    begin
      Val := Values[i];
      try
        if not Assigned(APredicate) or APredicate(Val) then
        begin
          IndicesToRemove[c] := i;
          Inc(c);
        end;
      finally
        Val.Free;
      end;
    end;
    SetLength(IndicesToRemove, c);
    Remove(IndicesToRemove);
  end
  else
    raise EAlgosimObjectException.CreateFmt(SCannotRemove, [ClassTypeName]);
end;

{ TAlgosimNullObject }

constructor TAlgosimNullObject.Create(AObject: TAlgosimObject);
begin
  Create;
end;

function TAlgosimNullObject.Equals(Obj: TObject): Boolean;
begin
  Result := Obj is TAlgosimNullObject;
end;

function TAlgosimNullObject.GetAsSingleLineText(const AOptions: TFormatOptions): string;
begin
  Result := '';
end;

class function TAlgosimNullObject.SortClassSameObject(const Left,
  Right: TAlgosimObject; const AEpsilon: TASR): Boolean;
begin
  Result := True;
end;

function TAlgosimNullObject.ToBoolean: Boolean;
begin
  Result := False;
end;

function TAlgosimNullObject.ToInputString: string;
begin
  Result := '0;';
end;

function TAlgosimNullObject.ToString: string;
begin
  Result := '';
end;

{ TAlgosimNumericEntity }

function TAlgosimNumericEntity.Abs: TAlgosimNumericEntity;
begin
  raise EAlgosimObjectException.CreateFmt(SCannotComputeAbs, [ClassTypeName]);
end;

function TAlgosimNumericEntity.ApplyOptions(
  const AOptions: TFormatOptions): TFormatOptions;
begin
  Result := AOptions;
  if NumDigitsOverride then
    Result.Numbers.NumDigits := NumDigits;
  if NumberBase > 0 then
    Result.Numbers.Base := NumberBase;
  if DigitGroupingOverride then
  begin
    Result.Numbers.IntGrouping := DigitGrouping;
    Result.Numbers.FracGrouping := DigitGrouping;
  end;
  if NumberFormatOverride then
    Result.Numbers.NumberFormat := NumberFormat;
  if PrettyExpOverride then
    Result.Numbers.PrettyExp := PrettyExp;
  Result.Numbers.MinLength := Max(Result.Numbers.MinLength, MinLength);
end;

function TAlgosimNumericEntity.ConjugateTranspose: TAlgosimMatrix;
begin
  raise EAlgosimObjectException.CreateFmt(SCannotConjTranspose, [ClassTypeName]);
end;

procedure TAlgosimNumericEntity.Defuzz(const Eps: Double);
begin

end;

function TAlgosimNumericEntity.GetDigitGrouping: Integer;
begin
  Result := Integer((FFormatCode and FCM_GROUP) shr 32);
end;

function TAlgosimNumericEntity.GetDigitGroupingOverride: Boolean;
begin
  Result := FFormatCode and FCM_GROUPO <> 0;
end;

function TAlgosimNumericEntity.GetMinLength: Integer;
begin
  Result := Byte((FFormatCode and FCM_MINLEN) shr 48);
end;

function TAlgosimNumericEntity.GetNumberBase: Integer;
begin
  Result := Integer((FFormatCode and FCM_BASE) shr 16);
end;

function TAlgosimNumericEntity.GetNumberFormat: TNumberFormat;
begin
  Result := TNumberFormat(
    EnsureRange(
      (FFormatCode and FCM_NUMFMT) shr 40,
      Ord(Low(TNumberFormat)),
      Ord(High(TNumberFormat))
    )
  );
end;

function TAlgosimNumericEntity.GetNumberFormatOverride: Boolean;
begin
  Result := FFormatCode and FCM_NUMFMTO <> 0;
end;

function TAlgosimNumericEntity.GetNumDigits: Integer;
begin
  Result := Integer(FFormatCode and FCM_DIGITS);
end;

function TAlgosimNumericEntity.GetNumDigitsOverride: Boolean;
begin
  Result := FFormatCode and FCM_DIGITSO <> 0;
end;

function TAlgosimNumericEntity.GetPrettyExp: Boolean;
begin
  Result := FFormatCode and FCM_EXPFMT <> 0;
end;

function TAlgosimNumericEntity.GetPrettyExpOverride: Boolean;
begin
  Result := FFormatCode and FCM_EXPFMTO <> 0;
end;

function TAlgosimNumericEntity.GetStyle: TFormatStyle;
begin
  Result := TFormatStyle(Byte((FFormatCode and FCM_STYLE) shr 24));
end;

function TAlgosimNumericEntity.ImaginaryPart: TAlgosimNumericEntity;
begin
  raise EAlgosimObjectException.CreateFmt(SCannotComputeImgPart, [ClassTypeName]);
end;

function TAlgosimNumericEntity.Inverse: TAlgosimNumericEntity;
begin
  raise EAlgosimObjectException.CreateFmt(SCannotInvert, [ClassTypeName]);
end;

function TAlgosimNumericEntity.IsNegative(const Eps: Double): Boolean;
begin
  raise EAlgosimObjectException.CreateFmt(SCannotDetermineSign, [ClassTypeName]);
end;

function TAlgosimNumericEntity.IsNonNegative(const Eps: Double): Boolean;
begin
  raise EAlgosimObjectException.CreateFmt(SCannotDetermineSign, [ClassTypeName]);
end;

function TAlgosimNumericEntity.IsNonPositive(const Eps: Double): Boolean;
begin
  raise EAlgosimObjectException.CreateFmt(SCannotDetermineSign, [ClassTypeName]);
end;

function TAlgosimNumericEntity.IsNonZero(const Eps: Double): Boolean;
begin
  raise EAlgosimObjectException.CreateFmt(SCannotDetermineSign, [ClassTypeName]);
end;

function TAlgosimNumericEntity.IsPositive(const Eps: Double): Boolean;
begin
  raise EAlgosimObjectException.CreateFmt(SCannotDetermineSign, [ClassTypeName]);
end;

function TAlgosimNumericEntity.IsZero(const Eps: Double): Boolean;
begin
  raise EAlgosimObjectException.CreateFmt(SCannotDetermineSign, [ClassTypeName]);
end;

function TAlgosimNumericEntity.Norm(AType: TNormType; AParam: Integer;
  AYieldProc: TObjProc): TASR;
begin
  raise EAlgosimObjectException.CreateFmt(SCannotComputeNorm, [NormTypes[AType], ClassTypeName]);
end;

function TAlgosimNumericEntity.NormSquared: TAlgosimRealNumber;
begin
  raise EAlgosimObjectException.CreateFmt(SCannotNormSquare, [ClassTypeName]);
end;

function TAlgosimNumericEntity.RealPart: TAlgosimNumericEntity;
begin
  raise EAlgosimObjectException.CreateFmt(SCannotComputeRePart, [ClassTypeName]);
end;

function TAlgosimNumericEntity.ScaledBy(const AFactor: TASR): TAlgosimNumericEntity;
begin
  raise EAlgosimObjectException.CreateFmt(SCannotScale, [ClassTypeName]);
end;

procedure TAlgosimNumericEntity.SetDigitGrouping(Value: Integer);
begin
  if not InRange(Value, 0, 127) then
    raise EAlgosimObjectException.CreateFmt(SInvDigGrSize, [Value]);
  FFormatCode := (FFormatCode and not FCM_GROUP) or ((UInt64(Value) shl 32) and FCM_GROUP);
end;

procedure TAlgosimNumericEntity.SetDigitGroupingOverride(Value: Boolean);
begin
  if Value then
    FFormatCode := FFormatCode or FCM_GROUPO
  else
    FFormatCode := FFormatCode and not FCM_GROUPO;
end;

procedure TAlgosimNumericEntity.SetMinLength(Value: Integer);
begin
  if not InRange(Value, 0, 255) then
    raise EAlgosimObjectException.CreateFmt(SInvMinLen, [Value]);
  FFormatCode := (FFormatCode and not FCM_MINLEN) or ((UInt64(Value) shl 48) and FCM_MINLEN);
end;

procedure TAlgosimNumericEntity.SetNumberBase(Value: Integer);
begin
  if (Value <> 0) and not InRange(Value, 2, 36) then
    raise EAlgosimObjectException.CreateFmt(SInvNumberBase, [Value]);
  FFormatCode := (FFormatCode and not FCM_BASE) or ((Byte(Value) shl 16) and FCM_BASE);
end;

procedure TAlgosimNumericEntity.SetNumberFormat(Value: TNumberFormat);
begin
  if not InRange(Ord(Value), 0, 3) then
    Value := nfDefault;
  FFormatCode := (FFormatCode and not FCM_NUMFMT) or ((UInt64(Ord(Value)) shl 40) and FCM_NUMFMT);
end;

procedure TAlgosimNumericEntity.SetNumberFormatOverride(Value: Boolean);
begin
  if Value then
    FFormatCode := FFormatCode or FCM_NUMFMTO
  else
    FFormatCode := FFormatCode and not FCM_NUMFMTO;
end;

procedure TAlgosimNumericEntity.SetNumDigits(const Value: Integer);
begin
  if not InRange(Value, 0, 32767) then
    raise EAlgosimObjectException.CreateFmt(SInvNumDigits, [Value]);
  FFormatCode := (FFormatCode and not FCM_DIGITS) or (Word(Value) and FCM_DIGITS);
end;

procedure TAlgosimNumericEntity.SetNumDigitsOverride(const Value: Boolean);
begin
  if Value then
    FFormatCode := FFormatCode or FCM_DIGITSO
  else
    FFormatCode := FFormatCode and not FCM_DIGITSO;
end;

procedure TAlgosimNumericEntity.SetPrettyExp(Value: Boolean);
begin
  if Value then
    FFormatCode := FFormatCode or FCM_EXPFMT
  else
    FFormatCode := FFormatCode and not FCM_EXPFMT;
end;

procedure TAlgosimNumericEntity.SetPrettyExpOverride(Value: Boolean);
begin
  if Value then
    FFormatCode := FFormatCode or FCM_EXPFMTO
  else
    FFormatCode := FFormatCode and not FCM_EXPFMTO;
end;

procedure TAlgosimNumericEntity.SetStyle(const Value: TFormatStyle);
begin
  FFormatCode := (FFormatCode and not FCM_STYLE) or (Byte(Ord(Value)) shl 24);
end;

function TAlgosimNumericEntity.Square: TAlgosimNumericEntity;
begin
  raise EAlgosimObjectException.CreateFmt(SCannotSquare, [ClassTypeName]);
end;

function TAlgosimNumericEntity.Transpose: TAlgosimMatrix;
begin
  raise EAlgosimObjectException.CreateFmt(SCannotTranspose, [ClassTypeName]);
end;

{ TAlgosimNumber }

procedure TAlgosimNumber.AddNumbersToArray(AArray: TAlgosimArray; ALevel: Integer);
begin
  AArray.Add(Self.Clone);
end;

class function TAlgosimNumber.Divide(Left,
  Right: TAlgosimNumber): TAlgosimNumber;
begin
  Result := Right.DivideBy(Left);
end;

function TAlgosimNumber.Equals(Obj: TObject): Boolean;
var
  Num: TAlgosimNumber absolute Obj;
begin
  Result := Obj is TAlgosimNumber;
  if Result then
    if Self.IsComplex or Num.IsComplex then
      Result := Self.ToASC = Num.ToASC
    else
      Result := Self.ToASR = Num.ToASR;
end;

class function TAlgosimNumber.Multiply(Left,
  Right: TAlgosimNumber): TAlgosimNumber;
begin
  Result := Right.MultiplyTo(Left);
end;

class function TAlgosimNumber.Power(Left,
  Right: TAlgosimNumber): TAlgosimNumber;
begin
  Result := Right.RaiseTo(Left);
end;

procedure TAlgosimNumber.SetMaxLen(AValue: Integer);
begin
  NumDigitsOverride := True;
  NumDigits := AValue;
end;

class function TAlgosimNumber.SortClass: TSortClass;
begin
  Result := SORTCLASS_NUMBER;
end;

class function TAlgosimNumber.SortClassCompare(const Left,
  Right: TAlgosimObject): Integer;
begin
  if Left.IsComplex or Right.IsComplex then
    Result := CompareValue(Left.ToASC, Right.ToASC)
  else
    Result := CompareValue(Left.ToASR, Right.ToASR);
end;

class function TAlgosimNumber.SortClassSameObject(const Left,
  Right: TAlgosimObject; const AEpsilon: TASR): Boolean;
begin
  if Left.IsComplex or Right.IsComplex then
    Result := CSameValue(Left.ToASC, Right.ToASC, AEpsilon)
  else
    Result := SameValue(Left.ToASR, Right.ToASR, AEpsilon);
end;

class function TAlgosimNumber.Subtract(Left,
  Right: TAlgosimNumber): TAlgosimNumber;
begin
  Result := Right.SubtractFrom(Left);
end;

function TAlgosimNumber.ToColor: TRGB;
var
  intval: Integer;
begin
  if TryToInt32(intval) then
    if intval > 0 then
      Result := TRGB(TColor(RBSwap(intval)))
    else
      Result := TRGB(ColorToRGB(TColor(intval)))
  else
    raise EAlgosimObjectException.Create(SConvNonIntColor);
end;

class function TAlgosimNumber.Add(Left,
  Right: TAlgosimNumber): TAlgosimNumber;
begin
  Result := Right.AddTo(Left);
end;

class function TAlgosimNumber.LessThan(Left: TAlgosimNumber;
  Right: TAlgosimNumber): Boolean;
begin

  if Left.IsComplex or Right.IsComplex then
    raise EAlgosimObjectException.Create(SCmplxNoOrder);

  if (Left is TAlgosimInteger) and (Right is TAlgosimInteger) then
    Result := TAlgosimInteger(Left).Value < TAlgosimInteger(Right).Value
  else
    Result := Left.ToASR < Right.ToASR;

end;

class function TAlgosimNumber.LessThanOrEqualTo(Left: TAlgosimNumber;
  Right: TAlgosimNumber): Boolean;
begin

  if Left.IsComplex or Right.IsComplex then
    raise EAlgosimObjectException.Create(SCmplxNoOrder);

  if (Left is TAlgosimInteger) and (Right is TAlgosimInteger) then
    Result := TAlgosimInteger(Left).Value <= TAlgosimInteger(Right).Value
  else
    Result := Left.ToASR <= Right.ToASR;

end;

function TAlgosimNumber.SortClassGetHashCode: Integer;
var
  X: TASR;
begin
  // (TAlgosimComplexNumber overrides SortClassGetHashCode)
  X := Self.ToASR;
  Result := THashBobJenkins.GetHashValue(X, sizeof(X));
end;

function TAlgosimNumber.GetMaxLen: Integer;
begin
  Result := NumDigits;
end;

class function TAlgosimNumber.GreaterThan(Left: TAlgosimNumber;
  Right: TAlgosimNumber): Boolean;
begin

  if Left.IsComplex or Right.IsComplex then
    raise EAlgosimObjectException.Create(SCmplxNoOrder);

  if (Left is TAlgosimInteger) and (Right is TAlgosimInteger) then
    Result := TAlgosimInteger(Left).Value > TAlgosimInteger(Right).Value
  else
    Result := Left.ToASR > Right.ToASR;

end;

class function TAlgosimNumber.GreaterThanOrEqualTo(Left: TAlgosimNumber;
  Right: TAlgosimNumber): Boolean;
begin

  if Left.IsComplex or Right.IsComplex then
    raise EAlgosimObjectException.Create(SCmplxNoOrder);

  if (Left is TAlgosimInteger) and (Right is TAlgosimInteger) then
    Result := TAlgosimInteger(Left).Value >= TAlgosimInteger(Right).Value
  else
    Result := Left.ToASR >= Right.ToASR;

end;

{ TAlgosimRealNumber }

function TAlgosimRealNumber.Abs: TAlgosimNumericEntity;
begin
  Result := ASO(System.Abs(FValue));
end;

function TAlgosimRealNumber.AddASC(ASC: TAlgosimComplexNumber): TAlgosimNumber;
begin
  Result := ASO(TASC(FValue) + ASC.Value);
end;

function TAlgosimRealNumber.AddASI(ASI: TAlgosimInteger): TAlgosimNumber;
begin
  Result := ASO(FValue + TASR(ASI.Value));
end;

function TAlgosimRealNumber.AddRat(R: TAlgosimRationalNumber): TAlgosimNumber;
begin
  Result := ASO(FValue + TASR(R.Value));
end;

function TAlgosimRealNumber.AddASR(ASR: TAlgosimRealNumber): TAlgosimNumber;
begin
  Result := ASO(FValue + ASR.Value);
end;

function TAlgosimRealNumber.AddTo(ANum: TAlgosimNumber): TAlgosimNumber;
begin
  Result := ANum.AddASR(Self);
end;

function TAlgosimRealNumber.Argument: TAlgosimRealNumber;
begin
  if FValue >= 0 then
    Result := ASO(0)
  else
    Result := ASO(Pi);
end;

function TAlgosimRealNumber.AsMemberOfSimplestField: TAlgosimNumber;
begin
  Result := TAlgosimRealNumber(Clone);
end;

function TAlgosimRealNumber.ComputeFunction(const ARealDomain: TSDD;
  ARealFcn: TRealFunction; AComplexFcn: TComplexFunction): TAlgosimNumber;
begin
  if ARealDomain.Contains(Value) then
    Result := ASO(ARealFcn(Value))
  else
    Result := ASO(AComplexFcn(Value));
end;

function TAlgosimRealNumber.ComputeFunction(ARealFcn: TRealFunction;
  AComplexFcn: TComplexFunction): TAlgosimNumber;
begin
  Result := ASO(ARealFcn(Value));
end;

function TAlgosimRealNumber.Conjugate: TAlgosimNumber;
begin
  Result := TAlgosimNumber(Clone);
end;

constructor TAlgosimRealNumber.Create(AObject: TAlgosimObject);
begin
  CreateWithValue(AObject.ToRealNumber);
  if AObject is TAlgosimNumericEntity then
    FormatCode := TAlgosimNumericEntity(AObject).FormatCode;
end;

constructor TAlgosimRealNumber.CreateWithValue(const AValue: TASR);
begin
  Create;
  FValue := AValue;
end;

procedure TAlgosimRealNumber.Defuzz(const Eps: Double);
begin
  if IsInteger(FValue, Eps) then
    FValue := Round(FValue);
end;

function TAlgosimRealNumber.DivideASC(
  ASC: TAlgosimComplexNumber): TAlgosimNumber;
begin
  Result := ASO(TASC(FValue) / ASC.Value);
end;

function TAlgosimRealNumber.DivideASI(ASI: TAlgosimInteger): TAlgosimNumber;
begin
  Result := ASO(FValue / TASR(ASI.Value));
end;

function TAlgosimRealNumber.DivideRat(R: TAlgosimRationalNumber): TAlgosimNumber;
begin
  Result := ASO(FValue / TASR(R.Value));
end;

function TAlgosimRealNumber.ExplainedOutput(
  const AOptions: TFormatOptions): string;
var
  SSF: TSimpleSymbolicForm;
begin
  Result := GetAsSingleLineText(AOptions);
  if (Style = fsDefault) and not IsInteger(FValue) then
  begin
    SSF := ToSymbolicForm(FValue);
    if SSF.valid and SameValue2(TASR(SSF), FValue) then
      Result := Result + #9 + '(=' + SSF.ToString(ApplyOptions(AOptions)) + ')'
  end;
end;

function TAlgosimRealNumber.DivideASR(ASR: TAlgosimRealNumber): TAlgosimNumber;
begin
  Result := ASO(FValue / ASR.Value);
end;

function TAlgosimRealNumber.DivideBy(ANum: TAlgosimNumber): TAlgosimNumber;
begin
  Result := ANum.DivideASR(Self);
end;

function TAlgosimRealNumber.GetAsSingleLineText(const AOptions: TFormatOptions): string;
var
  Formatter: TASRFormatter;
begin

  if (Style <> fsDefault) and TryGetFormatter(Style, Formatter) then
    Result := Formatter(ApplyOptions(AOptions), FValue)
  else
    Result := ASNum.RealToStr(FValue, ApplyOptions(AOptions));

end;

function TAlgosimRealNumber.GetBinaryData(var Buf: PByte;
  var Len: UInt64): Boolean;
begin
  Buf := @FValue;
  Len := sizeof(FValue);
  Result := True;
end;

function TAlgosimRealNumber.GetMemorySize: UInt64;
begin
  Result := sizeof(FValue);
end;

function TAlgosimRealNumber.ImaginaryPart: TAlgosimNumericEntity;
begin
  Result := ASO(0);
end;

procedure TAlgosimRealNumber.Increase(AAmount: TASI);
begin
  FValue := FValue + AAmount;
end;

function TAlgosimRealNumber.Inverse: TAlgosimNumericEntity;
begin
  Result := ASO(1.0 / Value);
end;

function TAlgosimRealNumber.IsNegative(const Eps: Double): Boolean;
begin
  Result := (FValue < 0) and not Math.IsZero(FValue, Eps);
end;

function TAlgosimRealNumber.IsNonNegative(const Eps: Double): Boolean;
begin
  Result := (FValue > 0) or Math.IsZero(FValue, Eps);
end;

function TAlgosimRealNumber.IsNonPositive(const Eps: Double): Boolean;
begin
  Result := (FValue < 0) or Math.IsZero(FValue, Eps);
end;

function TAlgosimRealNumber.IsNonZero(const Eps: Double): Boolean;
begin
  Result := not Math.IsZero(FValue, Eps);
end;

function TAlgosimRealNumber.IsPositive(const Eps: Double): Boolean;
begin
  Result := (FValue > 0) and not Math.IsZero(FValue, Eps);
end;

function TAlgosimRealNumber.IsZero(const Eps: Double): Boolean;
begin
  Result := Math.IsZero(FValue, Eps);
end;

function TAlgosimRealNumber.MultiplyASC(
  ASC: TAlgosimComplexNumber): TAlgosimNumber;
begin
  Result := ASO(TASC(FValue) * ASC.Value);
end;

function TAlgosimRealNumber.MultiplyASI(ASI: TAlgosimInteger): TAlgosimNumber;
begin
  Result := ASO(FValue * TASR(ASI.Value));
end;

function TAlgosimRealNumber.MultiplyRat(R: TAlgosimRationalNumber): TAlgosimNumber;
begin
  Result := ASO(FValue * TASR(R.Value));
end;

function TAlgosimRealNumber.MultiplyASR(
  ASR: TAlgosimRealNumber): TAlgosimNumber;
begin
  Result := ASO(FValue * ASR.Value);
end;

function TAlgosimRealNumber.MultiplyTo(ANum: TAlgosimNumber): TAlgosimNumber;
begin
  Result := ANum.MultiplyASR(Self);
end;

function TAlgosimRealNumber.Norm(AType: TNormType; AParam: Integer;
  AYieldProc: TObjProc): TASR;
begin
  Result := System.Abs(FValue);
end;

function TAlgosimRealNumber.NormSquared: TAlgosimRealNumber;
begin
  Result := ASO(System.Sqr(FValue));
end;

function TAlgosimRealNumber.RaiseTo(ANum: TAlgosimNumber): TAlgosimNumber;
begin
  Result := ANum.RaiseToASR(Self);
end;

function TAlgosimRealNumber.RaiseToASC(
  ASC: TAlgosimComplexNumber): TAlgosimNumber;
begin
  Result := ASO(cpow(TASC(FValue), ASC.Value));
end;

function TAlgosimRealNumber.RaiseToASI(ASI: TAlgosimInteger): TAlgosimNumber;
begin
  Result := ASO(pow(FValue, TASR(ASI.Value)));
end;

function TAlgosimRealNumber.RaiseToRat(R: TAlgosimRationalNumber): TAlgosimNumber;
begin
  if (FValue < 0) and (R.Value.Denominator <> 1) then
    Result := ASO(cpow(TASC(FValue), TASC(TASR(R.FValue))))
  else
    Result := ASO(pow(FValue, TASR(R.Value)));
end;

function TAlgosimRealNumber.RaiseToASR(ASR: TAlgosimRealNumber): TAlgosimNumber;
begin
  if (FValue < 0) and not ASNum.IsInteger(ASR.Value) then
    Result := ASO(cpow(FValue, ASR.Value))
  else
    Result := ASO(pow(FValue, ASR.Value));
end;

function TAlgosimRealNumber.RealPart: TAlgosimNumericEntity;
begin
  result := ASO(FValue);
end;

function TAlgosimRealNumber.ScaledBy(
  const AFactor: TASR): TAlgosimNumericEntity;
begin
  Result := ASO(FValue * AFactor);
end;

procedure TAlgosimRealNumber.SetBinaryData(const Buf: PByte; const Len: UInt64);
begin
  if Len = sizeof(TASR) then
    FValue := PASR(Buf)^
  else
    raise EAlgosimObjectException.CreateFmt(SInvalidBlob, [ClassTypeName]);
end;

function TAlgosimRealNumber.Square: TAlgosimNumericEntity;
begin
  Result := ASO(Value * Value);
end;

function TAlgosimRealNumber.SubtractASC(
  ASC: TAlgosimComplexNumber): TAlgosimNumber;
begin
  Result := ASO(TASC(FValue) - ASC.Value);
end;

function TAlgosimRealNumber.SubtractASI(ASI: TAlgosimInteger): TAlgosimNumber;
begin
  Result := ASO(FValue - TASR(ASI.Value));
end;

function TAlgosimRealNumber.SubtractRat(R: TAlgosimRationalNumber): TAlgosimNumber;
begin
  Result := ASO(FValue - TASR(R.Value));
end;

function TAlgosimRealNumber.SubtractASR(
  ASR: TAlgosimRealNumber): TAlgosimNumber;
begin
  Result := ASO(FValue - ASR.Value);
end;

function TAlgosimRealNumber.SubtractFrom(ANum: TAlgosimNumber): TAlgosimNumber;
begin
  Result := ANum.SubtractASR(Self);
end;

function TAlgosimRealNumber.ToBoolean: Boolean;
begin
  Result := Value <> 0;
end;

function TAlgosimRealNumber.ToComplexMatrix: TComplexMatrix;
begin
  Result := TComplexMatrix.Create([Value]);
end;

function TAlgosimRealNumber.ToComplexNumber: TASC;
begin
  Result := FValue;
end;

function TAlgosimRealNumber.ToComplexVector: TComplexVector;
begin
  Result := TComplexVector.Create([Value]);
end;

function TAlgosimRealNumber.ToRealMatrix: TRealMatrix;
begin
  Result := TRealMatrix.Create([Value]);
end;

function TAlgosimRealNumber.ToRealNumber: TASR;
begin
  Result := Value;
end;

function TAlgosimRealNumber.ToRealVector: TRealVector;
begin
  Result := TRealVector.Create([Value]);
end;

function TAlgosimRealNumber.ToSpeech: string;
begin
  Result := RealToStr(FValue, DefaultFormatOptions)
    .Replace('E', ' times ', [rfIgnoreCase, rfReplaceAll])
    .Replace(MINUS_SIGN, ' negative ', [rfReplaceAll])
    .Replace(HYPHEN_MINUS, ' negative ', [rfReplaceAll])
    .Replace(DOT_OPERATOR, ' times ', [rfReplaceAll])
    .Replace('^', ' raised to the power of ', [rfReplaceAll])
end;

function TAlgosimRealNumber.ToString: string;
begin
  Result := RealToStr(FValue, ExchangeFormOptions);
end;

function TAlgosimRealNumber.TryToASI(out ASI: Int64): Boolean;
begin
  Result := ASNum.IsInteger(FValue);
  if Result then
    ASI := Round(FValue);
end;

function TAlgosimRealNumber.TryToRat(out R: TRationalNumber): Boolean;
begin
  try
    R := ASNum.ToFraction(FValue);
    Result := True;
  except
    Result := False;
  end;
end;

function TAlgosimRealNumber.TryToASR(out Val: TASR): Boolean;
begin
  Result := True;
  Val := FValue;
end;

function TAlgosimRealNumber.TryToASC(out Val: TASC): Boolean;
begin
  Result := True;
  Val := FValue;
end;

function TAlgosimRealNumber.TryToInt32(out Int: Integer): Boolean;
begin
  Result := ASNum.IsInteger32(FValue);
  if Result then
    Int := Round(FValue);
end;

function TAlgosimRealNumber.TryToInt64(out Int: Int64): Boolean;
begin
  Result := ASNum.IsInteger64(FValue);
  if Result then
    Int := Round(FValue);
end;

function TAlgosimRealNumber.UnaryMinus: TAlgosimObject;
begin
  Result := ASO(-FValue);
end;

{ TAlgosimComplexNumber }

function TAlgosimComplexNumber.Abs: TAlgosimNumericEntity;
begin
  Result := ASO(FValue.Modulus);
end;

function TAlgosimComplexNumber.AddASC(
  ASC: TAlgosimComplexNumber): TAlgosimNumber;
begin
  Result := ASO(FValue + ASC.Value);
end;

function TAlgosimComplexNumber.AddASI(ASI: TAlgosimInteger): TAlgosimNumber;
begin
  Result := ASO(FValue + TASC(TASR(ASI.Value)));
end;

function TAlgosimComplexNumber.AddRat(R: TAlgosimRationalNumber): TAlgosimNumber;
begin
  Result := ASO(FValue + TASC(TASR(R.Value)));
end;

function TAlgosimComplexNumber.AddASR(ASR: TAlgosimRealNumber): TAlgosimNumber;
begin
  Result := ASO(FValue + TASC(ASR.Value));
end;

function TAlgosimComplexNumber.AddTo(ANum: TAlgosimNumber): TAlgosimNumber;
begin
  Result := ANum.AddASC(Self);
end;

function TAlgosimComplexNumber.Argument: TAlgosimRealNumber;
begin
  Result := ASO(FValue.Argument);
end;

function TAlgosimComplexNumber.AsMemberOfSimplestField: TAlgosimNumber;
begin
  if FValue.IsReal then
    Result := TAlgosimRealNumber.CreateWithValue(FValue.Re)
  else
    Result := TAlgosimComplexNumber(Clone);
end;

function TAlgosimComplexNumber.ComputeFunction(const ARealDomain: TSDD;
  ARealFcn: TRealFunction; AComplexFcn: TComplexFunction): TAlgosimNumber;
begin
  Result := ASO(AComplexFcn(Value));
end;

function TAlgosimComplexNumber.ComputeFunction(ARealFcn: TRealFunction;
  AComplexFcn: TComplexFunction): TAlgosimNumber;
begin
  Result := ASO(AComplexFcn(Value));
end;

function TAlgosimComplexNumber.Conjugate: TAlgosimNumber;
begin
  Result := ASO(FValue.Conjugate);
end;

constructor TAlgosimComplexNumber.Create(AObject: TAlgosimObject);
begin
  CreateWithValue(AObject.ToComplexNumber);
  if AObject is TAlgosimNumericEntity then
    Self.FormatCode := TAlgosimNumericEntity(AObject).FormatCode;
end;

constructor TAlgosimComplexNumber.CreateWithValue(const AValue: TASC);
begin
  Create;
  FValue := AValue;
end;

procedure TAlgosimComplexNumber.Defuzz(const Eps: Double);
begin
  Value := Value.Defuzz(Eps);
end;

function TAlgosimComplexNumber.DivideASC(
  ASC: TAlgosimComplexNumber): TAlgosimNumber;
begin
  Result := ASO(FValue / ASC.Value);
end;

function TAlgosimComplexNumber.DivideASI(ASI: TAlgosimInteger): TAlgosimNumber;
begin
  Result := ASO(FValue / TASC(TASR(ASI.Value)));
end;

function TAlgosimComplexNumber.DivideRat(R: TAlgosimRationalNumber): TAlgosimNumber;
begin
  Result := ASO(FValue / TASC(TASR(R.Value)));
end;

function TAlgosimComplexNumber.DivideASR(
  ASR: TAlgosimRealNumber): TAlgosimNumber;
begin
  Result := ASO(FValue / TASC(ASR.Value));
end;

function TAlgosimComplexNumber.DivideBy(ANum: TAlgosimNumber): TAlgosimNumber;
begin
  Result := ANum.DivideASC(Self);
end;

function TAlgosimComplexNumber.GetAsSingleLineText(const AOptions: TFormatOptions): string;
begin
  Result := ComplexToStr(FValue, False, ApplyOptions(AOptions));
end;

function TAlgosimComplexNumber.GetBinaryData(var Buf: PByte;
  var Len: UInt64): Boolean;
begin
  Buf := @FValue;
  Len := sizeof(FValue);
  Result := True;
end;

function TAlgosimComplexNumber.GetMemorySize: UInt64;
begin
  Result := sizeof(FValue);
end;

function TAlgosimComplexNumber.SortClassGetHashCode: Integer;
begin
  if FValue.Im = 0 then
    Result := THashBobJenkins.GetHashValue(FValue.Re, sizeof(FValue.Re))
  else
    Result := THashBobJenkins.GetHashValue(FValue, sizeof(FValue))
end;

function TAlgosimComplexNumber.ImaginaryPart: TAlgosimNumericEntity;
begin
  Result := ASO(FValue.Im);
end;

procedure TAlgosimComplexNumber.Increase(AAmount: TASI);
begin
  FValue := FValue + AAmount;
end;

function TAlgosimComplexNumber.Inverse: TAlgosimNumericEntity;
begin
  Result := ASO(Value.Inverse);
end;

function TAlgosimComplexNumber.IsNonZero(const Eps: Double): Boolean;
begin
  Result := not CIsZero(FValue, Eps);
end;

function TAlgosimComplexNumber.IsZero(const Eps: Double): Boolean;
begin
  Result := CIsZero(FValue, Eps);
end;

function TAlgosimComplexNumber.MultiplyASC(
  ASC: TAlgosimComplexNumber): TAlgosimNumber;
begin
  Result := ASO(FValue * ASC.Value);
end;

function TAlgosimComplexNumber.MultiplyASI(
  ASI: TAlgosimInteger): TAlgosimNumber;
begin
  Result := ASO(FValue * TASC(TASR(ASI.Value)));
end;

function TAlgosimComplexNumber.MultiplyRat(
  R: TAlgosimRationalNumber): TAlgosimNumber;
begin
  Result := ASO(FValue * TASC(TASR(R.Value)));
end;

function TAlgosimComplexNumber.MultiplyASR(
  ASR: TAlgosimRealNumber): TAlgosimNumber;
begin
  Result := ASO(FValue * TASC(ASR.Value));
end;

function TAlgosimComplexNumber.MultiplyTo(ANum: TAlgosimNumber): TAlgosimNumber;
begin
  Result := ANum.MultiplyASC(Self);
end;

function TAlgosimComplexNumber.Norm(AType: TNormType; AParam: Integer;
  AYieldProc: TObjProc): TASR;
begin
  Result := FValue.Modulus;
end;

function TAlgosimComplexNumber.NormSquared: TAlgosimRealNumber;
begin
  Result := ASO(FValue.ModSqr);
end;

function TAlgosimComplexNumber.RaiseTo(ANum: TAlgosimNumber): TAlgosimNumber;
begin
  Result := ANum.RaiseToASC(Self);
end;

function TAlgosimComplexNumber.RaiseToASC(
  ASC: TAlgosimComplexNumber): TAlgosimNumber;
begin
  Result := ASO(cpow(FValue, ASC.Value));
end;

function TAlgosimComplexNumber.RaiseToASI(ASI: TAlgosimInteger): TAlgosimNumber;
begin
  Result := ASO(cpow(FValue, TASC(TASR(ASI.Value))));
end;

function TAlgosimComplexNumber.RaiseToRat(R: TAlgosimRationalNumber): TAlgosimNumber;
begin
  Result := ASO(cpow(FValue, TASC(TASR(R.Value))));
end;

function TAlgosimComplexNumber.RaiseToASR(
  ASR: TAlgosimRealNumber): TAlgosimNumber;
begin
  Result := ASO(cpow(FValue, TASC(ASR.Value)));
end;

function TAlgosimComplexNumber.RealPart: TAlgosimNumericEntity;
begin
  Result := ASO(FValue.Re);
end;

function TAlgosimComplexNumber.ScaledBy(
  const AFactor: TASR): TAlgosimNumericEntity;
begin
  Result := ASO(FValue * AFactor);
end;

procedure TAlgosimComplexNumber.SetBinaryData(const Buf: PByte;
  const Len: UInt64);
begin
  if Len = sizeof(TASC) then
    FValue := PASC(Buf)^
  else
    raise EAlgosimObjectException.CreateFmt(SInvalidBlob, [ClassTypeName]);
end;

function TAlgosimComplexNumber.Square: TAlgosimNumericEntity;
begin
  Result := ASO(Value.Sqr);
end;

function TAlgosimComplexNumber.SubtractASC(
  ASC: TAlgosimComplexNumber): TAlgosimNumber;
begin
  Result := ASO(FValue - ASC.Value);
end;

function TAlgosimComplexNumber.SubtractASI(
  ASI: TAlgosimInteger): TAlgosimNumber;
begin
  Result := ASO(FValue - TASC(TASR(ASI.Value)));
end;

function TAlgosimComplexNumber.SubtractRat(
  R: TAlgosimRationalNumber): TAlgosimNumber;
begin
  Result := ASO(FValue - TASC(TASR(R.Value)));
end;

function TAlgosimComplexNumber.SubtractASR(
  ASR: TAlgosimRealNumber): TAlgosimNumber;
begin
  Result := ASO(FValue - TASC(ASR.Value));
end;

function TAlgosimComplexNumber.SubtractFrom(
  ANum: TAlgosimNumber): TAlgosimNumber;
begin
  Result := ANum.SubtractASC(Self);
end;

function TAlgosimComplexNumber.ToBoolean: Boolean;
begin
  Result := Value <> ComplexZero;
end;

function TAlgosimComplexNumber.ToComplexMatrix: TComplexMatrix;
begin
  Result := TComplexMatrix.Create([Value]);
end;

function TAlgosimComplexNumber.ToComplexNumber: TASC;
begin
  Result := Value;
end;

function TAlgosimComplexNumber.ToComplexVector: TComplexVector;
begin
  Result := TComplexVector.Create([Value]);
end;

function TAlgosimComplexNumber.ToRealMatrix: TRealMatrix;
begin
  if Value.IsReal then
    Result := TRealMatrix.Create([Value.Re])
  else
    raise EAlgosimObjectException.CreateFmt(SConvComplexReal, [Value.pstr]);
end;

function TAlgosimComplexNumber.ToRealNumber: TASR;
begin
  if Value.IsReal then
    result := Value.Re
  else
    raise EAlgosimObjectException.CreateFmt(SConvComplexReal, [Value.pstr]);
end;

function TAlgosimComplexNumber.ToRealVector: TRealVector;
begin
  Result := TRealVector.Create([Value.Re, Value.Im]);
end;

function TAlgosimComplexNumber.ToString: string;
begin
  Result := ComplexToStr(FValue, False, ExchangeFormOptions);
end;

function TAlgosimComplexNumber.TryToASI(out ASI: Int64): Boolean;
begin
  Result := FValue.IsReal and ASNum.IsInteger(FValue.Re);
  if Result then
    ASI := Round(FValue.Re);
end;

function TAlgosimComplexNumber.TryToRat(out R: TRationalNumber): Boolean;
begin
  Result := FValue.IsReal and ASNum.IsInteger(FValue.Re);
  if Result then
    R := Round(FValue.Re);
end;

function TAlgosimComplexNumber.TryToASR(out Val: TASR): Boolean;
begin
  Result := FValue.IsReal;
  if Result then
    Val := FValue.Re;
end;

function TAlgosimComplexNumber.TryToASC(out Val: TASC): Boolean;
begin
  Result := True;
  Val := FValue;
end;

function TAlgosimComplexNumber.TryToInt32(out Int: Integer): Boolean;
begin
  Result := FValue.IsReal and ASNum.IsInteger32(FValue.Re);
  if Result then
    Int := Round(FValue.Re);
end;

function TAlgosimComplexNumber.TryToInt64(out Int: Int64): Boolean;
begin
  Result := FValue.IsReal and ASNum.IsInteger64(FValue.Re);
  if Result then
    Int := Round(FValue.Re);
end;

function TAlgosimComplexNumber.UnaryMinus: TAlgosimObject;
begin
  Result := ASO(-FValue);
end;

{ TAlgosimString }

function TAlgosimString.Accumulate(AInitialValue: TAlgosimObject;
  AFunction: TASOAccumulator): TAlgosimObject;
var
  i: Integer;
  char: TAlgosimString;
begin
  char := TAlgosimString.CreateWithValue('a');
  try
    Result := AInitialValue.Clone;
    try
      for i := 1 to FValue.Length do
      begin
        char.FValue[1] := FValue[i];
        TObjReplacer<TAlgosimObject>.Replace(Result, AFunction(Result, char));
      end;
    except
      Result.Free;
      raise;
    end;
  finally
    char.Free;
  end;
end;

function TAlgosimString.AccumulateStepsList(AInitialValue: TAlgosimObject;
  AFunction: TASOAccumulator): TAlgosimArray;
var
  i: Integer;
  char: TAlgosimString;
begin
  char := TAlgosimString.CreateWithValue('a');
  try
    Result := TAlgosimArray.Create;
    try
      Result.Capacity := FValue.Length;
      for i := 1 to FValue.Length do
      begin
        char.FValue[1] := FValue[i];
        AInitialValue := AFunction(AInitialValue, char);
        Result.Add(AInitialValue);
      end;
    except
      Result.Free;
      raise;
    end;
  finally
    char.Free;
  end;
end;

procedure TAlgosimString.AddNumbersToArray(AArray: TAlgosimArray; ALevel: Integer);
begin

end;

procedure TAlgosimString.Append(AElement: TAlgosimObject);
begin
  try
    FValue := FValue + AElement.ToCharacter;
  finally
    AElement.Free;
  end;
end;

procedure TAlgosimString.Apply(AFunction: TASOFunction;
  ACondition: TASOPredicate; ALevel: Integer);
var
  i: Integer;
  char: TAlgosimString;
  res: TAlgosimObject;
begin

  if ALevel > 1 then
    Exit;

  char := TAlgosimString.CreateWithValue('a');
  try
    for i := 1 to FValue.Length do
    begin
      char.FValue[1] := FValue[i];
      if not Assigned(ACondition) or ACondition(char) then
      begin
        res := AFunction(char);
        try
          if IsCharacter(res) then
            FValue[i] := TAlgosimString(res).Value[1]
          else
            raise EAlgosimObjectException.Create(SCharApply);
        finally
          res.Free;
        end;
      end;
    end;
  finally
    char.Free;
  end;

end;

procedure TAlgosimString.ExtendWith(AElement: TAlgosimObject);
begin
  try
    FValue := FValue + AElement.ToString;
  finally
    AElement.Free;
  end;
end;

class function TAlgosimString.Concat(
  const Args: array of TAlgosimString): TAlgosimString;
var
  len: Integer;
  i, j: Integer;
  S: string;
begin
  len := 0;
  for i := 0 to High(Args) do
    Inc(len, Args[i].FValue.Length);
  SetLength(S, len);
  j := 1;
  for i := 0 to High(Args) do
    if not Args[i].FValue.IsEmpty then
    begin
      Move(Args[i].FValue[1], S[j], Args[i].FValue.Length * sizeof(Char));
      Inc(j, Args[i].FValue.Length);
    end;
  Result := ASO(S);
end;

function TAlgosimString.Count(APredicate: TASOPredicate): Integer;
var
  i: Integer;
  char: TAlgosimString;
begin
  char := TAlgosimString.CreateWithValue('a');
  try
    Result := 0;
    for i := 1 to FValue.Length do
    begin
      char.FValue[1] := FValue[i];
      if APredicate(char) then
        Inc(Result);
    end;
  finally
    char.Free;
  end;
end;

constructor TAlgosimString.Create(AObject: TAlgosimObject);
begin
  CreateWithValue(AObject.ToString);
end;

constructor TAlgosimString.CreateWithValue(const AValue: string);
begin
  Create;
  FValue := AValue;
end;

function TAlgosimString.Equals(Obj: TObject): Boolean;
begin
  Result := (Obj is TAlgosimString) and (Value = TAlgosimString(Obj).Value);
end;

function TAlgosimString.Exists(APredicate: TASOPredicate): Boolean;
var
  i: Integer;
  char: TAlgosimString;
begin
  char := TAlgosimString.CreateWithValue('a');
  try
    for i := 1 to FValue.Length do
    begin
      char.FValue[1] := FValue[i];
      if APredicate(char) then
        Exit(True);
    end;
    Result := False;
  finally
    char.Free;
  end;
end;

function TAlgosimString.Filter(APredicate: TASOPredicate): TAlgosimObject;
var
  S: string;
  i, j: Integer;
  char: TAlgosimString;
begin
  SetLength(S, FValue.Length);
  j := 1;
  char := TAlgosimString.CreateWithValue('a');
  try
    for i := 1 to FValue.Length do
    begin
      char.FValue[1] := FValue[i];
      if APredicate(char) then
      begin
        S[j] := char.FValue[1];
        Inc(j);
      end;
    end;
  finally
    char.Free;
  end;
  SetLength(S, j - 1);
  Exit(ASO(S));
end;

function TAlgosimString.First(N: Integer): TAlgosimObject;
begin
  Result := ASO(Copy(Value, 1, N));
end;

function TAlgosimString.ForAll(APredicate: TASOPredicate): Boolean;
var
  i: Integer;
  char: TAlgosimString;
begin
  char := TAlgosimString.CreateWithValue('a');
  try
    for i := 1 to FValue.Length do
    begin
      char.FValue[1] := FValue[i];
      if not APredicate(char) then
        Exit(False);
    end;
    Result := True;
  finally
    char.Free;
  end;
end;

function TAlgosimString.GetAsMultilineText(const AOptions: TFormatOptions): string;
var
  EffectiveMaxLen: Integer;
begin
  if FMaxLen > 0 then
    EffectiveMaxLen := FMaxLen
  else
    EffectiveMaxLen := AOptions.Strings.MaxLen;
  if Length(FValue) > EffectiveMaxLen then
    Result := Copy(FValue, 1, EffectiveMaxLen) + EllipsisSymbol_HORIZONTAL_ELLIPSIS
  else
    Result := FValue;
  if AOptions.Strings.Quoted then
    Result := '"' + StringReplace(Result, '"', '""', [rfReplaceAll]) + '"';
end;

function TAlgosimString.GetAsSingleLineText(const AOptions: TFormatOptions): string;
begin
  Result := StringReplace(GetAsMultilineText(AOptions),
    sLineBreak, AOptions.Strings.NewLineStr, [rfReplaceAll]);
end;

function TAlgosimString.GetBinaryData(var Buf: PByte; var Len: UInt64): Boolean;
begin
  Buf := PByte(PChar(FValue));
  Len := FValue.Length * sizeof(Char);
  Result := True;
end;

function TAlgosimString.GetMaxLen: Integer;
begin
  Result := FMaxLen;
end;

function TAlgosimString.GetMemorySize: UInt64;
begin
  Result := FValue.Length * sizeof(Char);
end;

function TAlgosimString.SortClassGetHashCode: Integer;
begin
  Result := THashBobJenkins.GetHashValue(FValue);
end;

function TAlgosimString.GetValue(Index: Integer): TAlgosimObject;
var
  PhysIndex: Integer;
begin
  PhysIndex := GetPhysIndex1(Index, FValue.Length);
  Result := ASO(FValue[PhysIndex]);
end;

procedure TAlgosimString.SetBinaryData(const Buf: PByte; const Len: UInt64);
var
  bbuf: TBytes;
begin
  SetLength(bbuf, Len);
  if Len > 0 then
    Move(Buf^, bbuf[0], Len);
  FValue := TEncoding.Unicode.GetString(bbuf);
end;

procedure TAlgosimString.SetMaxLen(AValue: Integer);
begin
  FMaxLen := AValue;
end;

procedure TAlgosimString.SetValue(Index: Integer; AValue: TAlgosimObject);
var
  PhysIndex: Integer;
begin

  try

    PhysIndex := GetPhysIndex1(Index, FValue.Length);

    if IsCharacter(AValue) then
      FValue[PhysIndex] := TAlgosimString(AValue).Value[1]
    else
      raise EAlgosimObjectException.CreateFmt(SSetStringSubscriptNoChar, [AValue.TypeName])

  finally
    AValue.Free;
  end;

end;

procedure TAlgosimString.Shuffle;
var
  Arr: TArray<Char>;
begin
  Arr := FValue.ToCharArray;
  TShuffler<Char>.Shuffle(Arr);
  SetString(FValue, PChar(Arr), Length(Arr));
end;

class function TAlgosimString.SortClass: TSortClass;
begin
  Result := SORTCLASS_TEXT;
end;

class function TAlgosimString.SortClassCompare(const Left,
  Right: TAlgosimObject): Integer;
var
  L, R: string;
begin
  L := Left.ToString;
  R := Right.ToString;
  Result := Sign(CompareText(L, R));
  if Result = 0 then
    Result := Sign(CompareStr(L, R));
end;

class function TAlgosimString.SortClassSameObject(const Left,
  Right: TAlgosimObject; const AEpsilon: TASR): Boolean;
begin
  Result := Left.ToString = Right.ToString;
end;

procedure TAlgosimString.Swap(Index1, Index2: Integer);
var
  PhysIdx1, PhysIdx2: Integer;
  c: Char;
begin

  PhysIdx1 := GetPhysIndex1(Index1, FValue.Length);
  PhysIdx2 := GetPhysIndex1(Index2, FValue.Length);

  if PhysIdx1 = PhysIdx2 then
    Exit;

  c := FValue[PhysIdx1];
  FValue[PhysIdx1] := FValue[PhysIdx2];
  FValue[PhysIdx2] := c;

end;

function TAlgosimString.GetValueCount: Integer;
begin
  Result := FValue.Length;
end;

function TAlgosimString.IndexOfValue(AObj: TAlgosimObject): TAlgosimObject;
var
  s: string;
  i: Integer;
begin

  if not (AObj is TAlgosimString) then
    Exit(ASO(null));

  s := AObj.ToString;

  i := Pos(s, FValue);

  if i > 0 then
    Result := ASOInt(i)
  else
    Result := ASO(null);

end;

procedure TAlgosimString.Insert(AIndex: Integer; AElement: TAlgosimObject);
begin
  try
    System.Insert(AElement.ToString, FValue, GetPhysIndex1(AIndex, FValue.Length + 1));
  finally
    AElement.Free;
  end;
end;

function TAlgosimString.Last(N: Integer): TAlgosimObject;
begin
  Result := ASO(Copy(Value, Length(Value) - (N - 1)));
end;

class function TAlgosimString.LoadFromFile(const AFileName: string;
  AEncoding: TEncoding; const AParams: string): TAlgosimObject;
var
  PositiveEncoding: TTextEncoding;
  PossibleEncodings, MagicWordsFound: TTextEncodings;
begin
  if AEncoding = nil then
    if GuessEncodingOfFile(AFileName, PositiveEncoding, PossibleEncodings,
      MagicWordsFound) then
      AEncoding := GetVCLEncoding(PositiveEncoding)
    else
      AEncoding := TEncoding.UTF8;
  Result := ASO(TFile.ReadAllText(AFileName, AEncoding));
end;

function TAlgosimString.NumFix(const AStr: string): string;
var
  ActualLength: Integer;
  i: Integer;
begin
  SetLength(Result, AStr.Length);
  ActualLength := 0;
  for i := 1 to AStr.Length do
  begin
    if AStr[i].IsWhiteSpace then
      Continue
    else if AStr[i] = MINUS_SIGN then
    begin
      Inc(ActualLength);
      Result[ActualLength] := HYPHEN_MINUS;
    end
    else
    begin
      Inc(ActualLength);
      Result[ActualLength] := AStr[i];
    end
  end;
  SetLength(Result, ActualLength);
end;

function TAlgosimString.Part(A, B: Integer): TAlgosimObject;
begin
  Result := ASO(Copy(Value, A, B - A + 1));
end;

function TAlgosimString.Part(A: Integer): TAlgosimObject;
begin
  Result := ASO(Copy(Value, A));
end;

procedure TAlgosimString.Remove(const AIndices: array of Integer);
var
  Arr: TArray<Char>;
begin
  Arr := FValue.ToCharArray;
  TRemover<Char>.Remove(Arr, TranslatedIntSequence(AIndices));
  FValue := string.Create(Arr);
end;

function TAlgosimString.RemoveAdjacentDuplicates: TAlgosimObject;
begin
  Result := ASO(ASStrFcns.RemoveAdjacentDuplicates(FValue));
end;

function TAlgosimString.RemoveDuplicates: TAlgosimObject;
begin
  Result := ASO(ASStrFcns.RemoveDuplicates(FValue));
end;

procedure TAlgosimString.RemoveFirst(N: Integer);
begin
  System.Delete(FValue, 1, N);
end;

procedure TAlgosimString.Replace(APredicate: TASOPredicate;
  ANewValue: TAlgosimObject; ALevel: Integer);
var
  i: Integer;
  Obj: TAlgosimString;
  C: Char;
begin

  if ALevel > 1 then
    Exit;

  C := ANewValue.ToCharacter;

  Obj := TAlgosimString.CreateWithValue('x');
  try
    for i := 1 to FValue.Length do
    begin
      Obj.FValue[1] := FValue[i];
      if not Assigned(APredicate) or APredicate(Obj) then
        FValue[i] := C;
    end;
  finally
    Obj.Free;
  end;

end;

procedure TAlgosimString.Reverse;
begin
  FValue := StrUtils.ReverseString(FValue);
end;

function TAlgosimString.RotLeft(N: Integer): TAlgosimObject;
var
  S: string;
  i: Integer;
begin
  if N < 0 then
    Exit(RotRight(-N));
  SetLength(S, Value.Length);
  for i := 1 to S.Length do
    S[i] := Value[((i - 1 + N) mod S.Length) + 1];
  Result := ASO(S);
end;

function TAlgosimString.RotRight(N: Integer): TAlgosimObject;
var
  S: string;
  i: Integer;
begin
  if N < 0 then
    Exit(RotLeft(-N));
  SetLength(S, Value.Length);
  for i := 1 to S.Length do
    S[((i - 1 + N) mod S.Length) + 1] := Value[i];
  Result := ASO(S);
end;

function TAlgosimString.ToBoolean: Boolean;
begin
  if FValue.Trim.ToLower = 'true' then
    Result := True
  else if FValue.Trim.ToLower = 'false' then
    Result := False
  else
    raise EAlgosimObjectException.CreateFmt(SConvStrBool, [FValue]);
end;

function TAlgosimString.ToCharacter: char;
begin
  if FValue.Length = 1 then
    Result := FValue[1]
  else
    raise EAlgosimObjectException.CreateFmt(SConvStrCharLen, [FValue.Length]);
end;

function TAlgosimString.ToColor: TRGB;
begin
  Result := TRGB(ASColors.StrToColor(Value));
end;

function TAlgosimString.ToComplexMatrix: TComplexMatrix;
var
  rows: TArray<string>;
  sparts: TArray<TArray<string>>;
  cparts: TArray<TComplexVector>;
  i: Integer;
  j: Integer;
begin
  rows := FValue.Trim.Split([#13#10]);
  SetLength(sparts, Length(rows));
  SetLength(cparts, Length(rows));
  for i := Low(rows) to High(rows) do
    sparts[i] := rows[i].Split([',', ';', #9]);
  for i := Low(sparts) to High(sparts) do
  begin
    cparts[i].Dimension := Length(sparts[i]);
    for j := Low(sparts[i]) to High(sparts[i]) do
      if not TryStringToComplex(sparts[i][j].Trim, cparts[i].Data[j]) then
        raise EAlgosimObjectException.CreateFmt(SConvStrComplexNum, [sparts[i][j].Trim]);
  end;
  Result := TComplexMatrix.CreateFromRows(cparts);
end;

function TAlgosimString.ToComplexNumber: TASC;
var
  val: TASC;
begin
  if TryStringToComplex(NumFix(FValue), val) then
    Result := val
  else
    raise EAlgosimObjectException.CreateFmt(SConvStrComplexNum, [FValue]);
end;

function TAlgosimString.ToComplexVector: TComplexVector;
var
  vals: TList<TASC>;
  s, s2: string;
  z: TASC;
  i, p, q: Integer;
begin
  // Notice: FValue.Trim.Split([#13#10, ',', ';', #9]) is increadibly slow
  vals := TList<TASC>.Create;
  try
    p := 0;
    q := FValue.Length;
    while (q > 0) and FValue[q].IsWhitespace do
      Dec(q);
    for i := 1 to q + 1 do
      if (i > q) or (FValue[i] = #9) or (FValue[i] = ',') or (FValue[i] = ';') or (FValue[i] = #10) then
      begin
        s := Copy(FValue, p + 1, i - p - 1);
        s2 := NumFix(s);
        if (i > q) and s2.IsEmpty then
          Break;
        if TryStringToComplex(s2, z) then
          vals.Add(z)
        else
          raise EAlgosimObjectException.CreateFmt(SConvStrComplexNum, [s.Trim]);
        p := i;
      end;
    Result := TComplexVector.Create(vals.ToArray);
  finally
    vals.Free;
  end;
end;

function TAlgosimString.ToNumber: TAlgosimObject;
var
  IntVal: TASI;
  RatVal: TRationalNumber;
  RealVal: TASR;
begin
  if TryStrToInt64(NumFix(FValue), IntVal) then // TryToASI(IntVal) will accept "5.0" as well, a behaviour that is not desired here
    Result := ASOInt(IntVal)
  else if TryToASR(RealVal) then
    Result := ASO(RealVal)
  else if TryToRat(RatVal) then
    Result := ASORat(RatVal)
  else
    Result := ASO(ToComplexNumber);
end;

function TAlgosimString.ToRealMatrix: TRealMatrix;
var
  rows: TArray<string>;
  sparts: TArray<TArray<string>>;
  rparts: TArray<TRealVector>;
  i: Integer;
  j: Integer;
begin
  rows := FValue.Trim.Split([#13#10]);
  SetLength(sparts, Length(rows));
  SetLength(rparts, Length(rows));
  for i := Low(rows) to High(rows) do
    sparts[i] := rows[i].Split([',', ';', #9]);
  for i := Low(sparts) to High(sparts) do
  begin
    rparts[i].Dimension := Length(sparts[i]);
    for j := Low(sparts[i]) to High(sparts[i]) do
      if not TryStrToFloat(NumFix(sparts[i][j]), rparts[i].Data[j], DefaultFormatSettings) then
        raise EAlgosimObjectException.CreateFmt(SConvStrRealNum, [sparts[i][j].Trim]);
  end;
  Result := TRealMatrix.CreateFromRows(rparts);
end;

function TAlgosimString.ToRealNumber: TASR;
var
  val: TASR;
begin
  if TryStrToFloat(NumFix(FValue), val, DefaultFormatSettings) then
    Result := val
  else
    raise EAlgosimObjectException.CreateFmt(SConvStrRealNum, [FValue]);
end;

function TAlgosimString.ToRealVector: TRealVector;
var
  vals: TList<TASR>;
  s, s2: string;
  x: TASR;
  i, p, q: Integer;
begin
  // Notice: FValue.Trim.Split([#13#10, ',', ';', #9]) is increadibly slow
  vals := TList<TASR>.Create;
  try
    p := 0;
    q := FValue.Length;
    while (q > 0) and FValue[q].IsWhitespace do
      Dec(q);
    for i := 1 to q + 1 do
      if (i > q) or (FValue[i] = #9) or (FValue[i] = ',') or (FValue[i] = ';') or (FValue[i] = #10) then
      begin
        s := Copy(FValue, p + 1, i - p - 1);
        s2 := NumFix(s);
        if (i > q) and s2.IsEmpty then
          Break;
        if TryStrToFloat(s2, x, DefaultFormatSettings) then
          vals.Add(x)
        else
          raise EAlgosimObjectException.CreateFmt(SConvStrRealNum, [s.Trim]);
        p := i;
      end;
    Result := TRealVector.Create(vals.ToArray);
  finally
    vals.Free;
  end;
end;

function TAlgosimString.ToString: string;
begin
  Result := FValue;
end;

function TAlgosimString.ToTable: TASTable;
var
  rows: TArray<string>;
  cells: TArray<TArray<string>>;
  i, height, width: Integer;
  j: Integer;
begin
  rows := FValue.Trim.Split([#13#10]);
  SetLength(cells, Length(rows));
  for i := Low(rows) to High(rows) do
    cells[i] := rows[i].Split([#9]);
  height := Length(rows);
  width := 0; // silly
  if height > 0 then
  begin
    width := Length(cells[0]);
    for i := 1 to height - 1 do
      if Length(cells[i]) > width then
        width := Length(cells[i]);
  end;
  for i := Low(cells) to High(cells) do
    if Length(cells[i]) < width then
      SetLength(cells[i], width);
  Result := TASTable.Create;
  try
    Result.SetSize(width, height);
    for i := 0 to height - 1 do
      for j := 0 to width - 1 do
        Result.Strings[Point(j, i)] := cells[i, j];
  except
    Result.Free;
    raise;
  end;
end;

function TAlgosimString.ToVector: TAlgosimVector;
begin
  try
    Result := ASO(ToRealVector);
  except
    Result := ASO(ToComplexVector);
  end;
end;

procedure TAlgosimString.Truncate(ANewLength: Integer);
begin

  if ANewLength < 0 then
    raise EArrayException.Create(SNewLengthMustBeNonNegative);

  if ANewLength >= FValue.Length then
    Exit;

  SetLength(FValue, ANewLength);

end;

function TAlgosimString.TryToASC(out Val: TASC): Boolean;
begin
  Result := TryStringToComplex(NumFix(FValue), Val);
end;

function TAlgosimString.TryToASI(out ASI: TASI): Boolean;
var
  fltval: TASR;
begin
  Result := TryStrToInt64(NumFix(FValue), ASI);
  if not Result then
  begin
    Result := TryStrToFloat(NumFix(FValue), fltval, DefaultFormatSettings)
      and IsInteger(fltval);
    if Result then
      ASI := Round(fltval);
  end;
end;

function TAlgosimString.TryToASR(out Val: Extended): Boolean;
begin
  Result := TryStrToFloat(NumFix(FValue), val, DefaultFormatSettings);
end;

function TAlgosimString.TryToInt32(out Int: Integer): Boolean;
begin
  Result := TryStrToInt(NumFix(FValue), Int);
end;

function TAlgosimString.TryToInt64(out Int: Int64): Boolean;
begin
  Result := TryStrToInt64(NumFix(FValue), Int);
end;

function TAlgosimString.TryToRat(out R: TRationalNumber): Boolean;
var
  sep: Integer;
  ps, qs: string;
  p, q: TASI;
  RealVal: TASR;
begin

  sep := Pos('/', FValue);
  if sep = 0 then
  begin
    ps := FValue;
    qs := '1';
  end
  else
  begin
    ps := Copy(FValue, 1, sep - 1);
    qs := Copy(FValue, sep + 1);
  end;
  Result := TryStrToInt64(NumFix(ps), p) and TryStrToInt64(NumFix(qs), q);
  if Result then
  begin
    R := TRationalNumber.Create(p, q);
    Result := R.valid;
    if Result then
      Exit;
  end;

  Result := TryStrToFloat(NumFix(FValue), RealVal, DefaultFormatSettings);
  if Result then
  begin
    R := ToFraction(RealVal);
    Result := R.valid;
  end;

end;

function TAlgosimString.Part(const ARanges: array of TRange): TAlgosimObject;
begin
  Result := Part(ParseRangeSeq(ARanges, Value.Length));
end;

function TAlgosimString.Part(const AIndices: array of Integer): TAlgosimObject;
var
  S: string;
  i: Integer;
begin
  SetLength(S, Length(AIndices));
  for i := 0 to High(AIndices) do
    if InRange(AIndices[i], 1, Value.Length) then
      S[i + 1] := Value[AIndices[i]]
    else
      raise EAlgosimObjectException.CreateFmt(SIndexOutOfBounds, [AIndices[i]]);
  Result := ASO(S);
end;

{ TAlgosimBoolean }

constructor TAlgosimBoolean.Create;
begin
  inherited;
  FTrueSymbol := BoolStrs[True];
  FFalseSymbol := BoolStrs[False];
end;

procedure TAlgosimBoolean.AddNumbersToArray(AArray: TAlgosimArray; ALevel: Integer);
begin

end;

constructor TAlgosimBoolean.Create(AObject: TAlgosimObject);
begin
  CreateWithValue(AObject.ToBoolean);
end;

constructor TAlgosimBoolean.CreateWithValue(const AValue: Boolean);
begin
  Create;
  FValue := AValue;
end;

function TAlgosimBoolean.Equals(Obj: TObject): Boolean;
begin
  Result := (Obj is TAlgosimBoolean) and (Value = TAlgosimBoolean(Obj).Value);
end;

function TAlgosimBoolean.GetAsSingleLineText(const AOptions: TFormatOptions): string;
begin
  Result := IfThen(FValue, FTrueSymbol, FFalseSymbol);
end;

function TAlgosimBoolean.GetBinaryData(var Buf: PByte;
  var Len: UInt64): Boolean;
begin
  FValue := FValue <> False; // normalize, so that we can guarantee that the value is either $00 or $01
  Buf := @FValue;
  Len := sizeof(FValue);
  Result := True;
end;

function TAlgosimBoolean.GetMemorySize: UInt64;
begin
  Result := sizeof(FValue);
end;

function TAlgosimBoolean.SortClassGetHashCode: Integer;
begin
  Result := Ord(FValue);
end;

procedure TAlgosimBoolean.SetBinaryData(const Buf: PByte; const Len: UInt64);
var
  i: Integer;
begin

  for i := 0 to Len - 1 do
    if Buf[i] <> 0 then
    begin
      FValue := True;
      Exit;
    end;

  FValue := False;

end;

class function TAlgosimBoolean.SortClass: TSortClass;
begin
  Result := SORTCLASS_BOOLEAN;
end;

class function TAlgosimBoolean.SortClassCompare(const Left,
  Right: TAlgosimObject): Integer;
begin
  Result := CompareValue(Ord(Left.ToBoolean), Ord(Right.ToBoolean));
end;

class function TAlgosimBoolean.SortClassSameObject(const Left,
  Right: TAlgosimObject; const AEpsilon: TASR): Boolean;
begin
  Result := Left.ToBoolean = Right.ToBoolean;
end;

function TAlgosimBoolean.ToBoolean: Boolean;
begin
  Result := Value;
end;

function TAlgosimBoolean.ToComplexNumber: TASC;
begin
  if Value then
    Result := 1.0
  else
    Result := 0.0;
end;

function TAlgosimBoolean.ToNumber: TAlgosimObject;
begin
  if FValue then
    Result := ASOInt(1)
  else
    Result := ASOInt(0);
end;

function TAlgosimBoolean.ToRealNumber: TASR;
begin
  if Value then
    Result := 1.0
  else
    Result := 0.0;
end;

function TAlgosimBoolean.ToString: string;
begin
  Result := BoolStrs[FValue];
end;

function TAlgosimBoolean.TryToASI(out ASI: Int64): Boolean;
begin
  Result := True;
  if FValue then
    ASI := 1
  else
    ASI := 0;
end;

{ TAlgosimSuccessIndication }

constructor TAlgosimSuccessIndication.Create(AObject: TAlgosimObject);
begin
  Create;
end;

class function TAlgosimSuccessIndication.CreateWithValue(
  ASuccess: Boolean): TAlgosimObject;
begin
  if ASuccess then
    Result := TAlgosimSuccessIndication.Create
  else
    Result := TAlgosimFailure.Create;
end;

function TAlgosimSuccessIndication.Equals(Obj: TObject): Boolean;
begin
  Result := Obj is TAlgosimSuccessIndication;
end;

function TAlgosimSuccessIndication.GetAsSingleLineText(
  const AOptions: TFormatOptions): string;
begin
  Result := SuccessStr;
end;

class function TAlgosimSuccessIndication.SortClass: TSortClass;
begin
  Result := SORTCLASS_SUCCESS;
end;

class function TAlgosimSuccessIndication.SortClassSameObject(const Left,
  Right: TAlgosimObject; const AEpsilon: Extended): Boolean;
begin
  Result := True;
end;

function TAlgosimSuccessIndication.ToBoolean: Boolean;
begin
  Result := True;
end;

function TAlgosimSuccessIndication.ToInputString: string;
begin
  Result := 'DebugObject("success indication")';
end;

function TAlgosimSuccessIndication.ToString: string;
begin
  Result := SuccessStr;
end;

{ TAlgosimVector }

class function TAlgosimVector.Add(Left, Right: TAlgosimVector): TAlgosimVector;
begin
  if Left.IsComplex or Right.IsComplex then
    Result := ASO(Left.AsComplexVector + Right.AsComplexVector)
  else
    Result := ASO(Left.AsRealVector + Right.AsRealVector)
end;

class function TAlgosimVector.Add(Left: TAlgosimVector;
  Right: TAlgosimNumber): TAlgosimVector;
begin
  if Left.IsComplex or Right.IsComplex then
    Result := ASO(Left.AsComplexVector + Right.ToASC)
  else
    Result := ASO(Left.AsRealVector + Right.ToASR)
end;

class function TAlgosimVector.Angle(Left,
  Right: TAlgosimVector): TAlgosimNumber;
begin
  if Left.IsComplex or Right.IsComplex then
    Result := ASO(ASNum.angle(Left.AsComplexVector, Right.AsComplexVector))
  else
    Result := ASO(ASNum.angle(Left.AsRealVector, Right.AsRealVector))
end;

class function TAlgosimVector.Concat(const Args: array of TAlgosimVector): TAlgosimVector;
var
  Cplx: Boolean;
  i, j, len, c: Integer;
begin

  Cplx := False;
  for i := 0 to High(Args) do
    if Args[i].IsComplex then
    begin
      Cplx := True;
      Break;
    end;

  len := 0;
  for i := 0 to High(Args) do
    Inc(len, Args[i].Dimension);

  Result := NumArrayClass(False, Cplx).Create as TAlgosimVector;
  try
    Result.Dimension := len;
    c := 1;
    for i := 0 to High(Args) do
      for j := 1 to Args[i].ValueCount do
      begin
        Result.Values[c] := Args[i].Values[j];
        Inc(c);
      end;
  except
    Result.Free;
    raise;
  end;

end;

constructor TAlgosimVector.Create;
begin
  inherited;

end;

class function TAlgosimVector.CrossProduct(Left,
  Right: TAlgosimVector): TAlgosimVector;
begin
  if Left.IsComplex or Right.IsComplex then
    Result := ASO(ASNum.CrossProduct(Left.AsComplexVector, Right.AsComplexVector))
  else
    Result := ASO(ASNum.CrossProduct(Left.AsRealVector, Right.AsRealVector))
end;

class function TAlgosimVector.Divide(Left: TAlgosimVector;
  Right: TAlgosimNumber): TAlgosimVector;
begin
  if Left.IsComplex or Right.IsComplex then
    Result := ASO(Left.AsComplexVector / Right.ToASC)
  else
    Result := ASO(Left.AsRealVector / Right.ToASR)
end;

function TAlgosimVector.Equals(Obj: TObject): Boolean;
var
  Vect: TAlgosimVector absolute Obj;
begin
  Result := Obj is TAlgosimVector;
  if Result then
  begin
    if Self.IsComplex or Vect.IsComplex then
      Result := Self.AsComplexVector = Vect.AsComplexVector
    else
      Result := Self.AsRealVector = Vect.AsRealVector
  end;
end;

function TAlgosimVector.GetAsMultilineText(const AOptions: TFormatOptions): string;
var
  EffectiveMaxLen: Integer;
  strs: array of string;
  i: Integer;
  MaxWidth: Integer;
  BasisPos: Integer;
  PaddingStr: string;
begin

  if FMaxLen > 0 then
    EffectiveMaxLen := FMaxLen
  else
    EffectiveMaxLen := AOptions.Vectors.MaxLen;

  if (GetDimension > AOptions.Vectors.VerticalUntil) or (GetDimension <= 1) then
    Exit(GetAsSingleLineText(AOptions));

  if GetDimension <= EffectiveMaxLen then
    SetLength(strs, GetDimension)
  else
    SetLength(strs, EffectiveMaxLen + 1 {ellipsis});

  MaxWidth := 0;
  for i := 0 to High(strs) do
  begin
    strs[i] := GetElementAsStringFmt(i, AOptions);
    if strs[i].Length > MaxWidth then
      MaxWidth := strs[i].Length;
  end;

  if GetDimension > EffectiveMaxLen then
    strs[High(strs)] := EllipsisSymbol_VERTICAL_ELLIPSIS;

  BasisPos := High(strs) div 2 + High(strs) mod 2;

  PaddingStr := #32;

  Result := IfThen(BasisPos = 0, AOptions.Vectors.BasisChar, PaddingStr) +
    ParenLeftUpper +
    PadStr(strs[0], MaxWidth) +
    ParenRightUpper;

  for i := 1 to High(strs) - 1 do
    Result := Result + sLineBreak +
      IfThen(BasisPos = i, AOptions.Vectors.BasisChar, PaddingStr) +
      ParenLeftExtension +
      PadStr(strs[i], MaxWidth) +
      ParenRightExtension;

  Result := Result + sLineBreak +
    IfThen(BasisPos = High(strs), AOptions.Vectors.BasisChar, PaddingStr) +
    ParenLeftLower +
    PadStr(strs[High(strs)], MaxWidth) +
    ParenRightLower;

end;

function TAlgosimVector.GetAsSingleLineText(const AOptions: TFormatOptions): string;
var
  EffectiveMaxLen: Integer;
  i: Integer;
begin

  if FMaxLen > 0 then
    EffectiveMaxLen := FMaxLen
  else
    EffectiveMaxLen := AOptions.Vectors.MaxLen;

  Result := AOptions.Vectors.LeftDelim;
  if GetDimension > 0 then
    Result := Result + GetElementAsStringFmt(0, AOptions);
  for i := 1 to Math.Min(EffectiveMaxLen, GetDimension) - 1 do
    Result := Result + AOptions.Vectors.ComponentSep + GetElementAsStringFmt(i, AOptions);
  if GetDimension > EffectiveMaxLen then
    Result := Result + AOptions.Vectors.ComponentSep + EllipsisSymbol_HORIZONTAL_ELLIPSIS;
  Result := Result + AOptions.Vectors.RightDelim;

end;

function TAlgosimVector.GetMaxLen: Integer;
begin
  Result := FMaxLen;
end;

function TAlgosimVector.GetPlanarExtent: TSize;
begin
  Result.Width := 1;
  Result.Height := Dimension;
end;

function TAlgosimVector.GetValueCount: Integer;
begin
  Result := Dimension;
end;

class function TAlgosimVector.InnerProduct(Left,
  Right: TAlgosimVector): TAlgosimNumber;
begin
  if Left.IsComplex or Right.IsComplex then
    Result := ASO(Left.AsComplexVector * Right.AsComplexVector)
  else
    Result := ASO(Left.AsRealVector * Right.AsRealVector)
end;

class function TAlgosimVector.Multiply(Left: TAlgosimVector;
  Right: TAlgosimNumber): TAlgosimVector;
begin
  if Left.IsComplex or Right.IsComplex then
    Result := ASO(Left.AsComplexVector * Right.ToASC)
  else
    Result := ASO(Left.AsRealVector * Right.ToASR)
end;

procedure TAlgosimVector.SetMaxLen(AValue: Integer);
begin
  FMaxLen := AValue;
end;

procedure TAlgosimVector.SetPlanarExtent(const Value: TSize);
begin
  if Value.Width <> 1 then
    raise EAlgosimObjectException.Create(SVectPlanarExtentWidth);
  Dimension := Value.Height;
end;

class function TAlgosimVector.SortClass: TSortClass;
begin
  Result := SORTCLASS_VECTOR;
end;

class function TAlgosimVector.SortClassCompare(const Left,
  Right: TAlgosimObject): Integer;
var
  L, R: TAlgosimVector;
  RL, RR: TRealVector;
  CL, CR: TComplexVector;
  i: Integer;
begin
  L := Left as TAlgosimVector;
  R := Right as TAlgosimVector;
  Result := CompareValue(L.Dimension, R.Dimension);
  if Result = 0 then
  begin
    if L.IsComplex or R.IsComplex then
    begin
      CL := L.AsComplexVector;
      CR := R.AsComplexVector;
      for i := 0 to CL.Dimension - 1 do
      begin
        Result := CompareValue(CL[i], CR[i]);
        if Result <> EqualsValue then
          Exit;
      end;
    end
    else
    begin
      RL := L.AsRealVector;
      RR := R.AsRealVector;
      for i := 0 to RL.Dimension - 1 do
      begin
        Result := CompareValue(RL[i], RR[i]);
        if Result <> EqualsValue then
          Exit;
      end;
    end;
  end;
end;

class function TAlgosimVector.SortClassSameObject(const Left,
  Right: TAlgosimObject; const AEpsilon: TASR): Boolean;
var
  L, R: TAlgosimVector;
  RL, RR: TRealVector;
  CL, CR: TComplexVector;
  i: Integer;
begin
  L := Left as TAlgosimVector;
  R := Right as TAlgosimVector;
  Result := L.Dimension = R.Dimension;
  if Result then
  begin
    if L.IsComplex or R.IsComplex then
    begin
      CL := L.AsComplexVector;
      CR := R.AsComplexVector;
      for i := 0 to CL.Dimension - 1 do
        if not CSameValue(CL[i], CR[i], AEpsilon) then
          Exit(False);
    end
    else
    begin
      RL := L.AsRealVector;
      RR := R.AsRealVector;
      for i := 0 to RL.Dimension - 1 do
        if not SameValue(RL[i], RR[i], AEpsilon) then
          Exit(False);
    end;
  end;
end;

class function TAlgosimVector.Subtract(Left: TAlgosimVector;
  Right: TAlgosimNumber): TAlgosimVector;
begin
  if Left.IsComplex or Right.IsComplex then
    Result := ASO(Left.AsComplexVector - Right.ToASC)
  else
    Result := ASO(Left.AsRealVector - Right.ToASR)
end;

class function TAlgosimVector.Subtract(Left,
  Right: TAlgosimVector): TAlgosimVector;
begin
  if Left.IsComplex or Right.IsComplex then
    Result := ASO(Left.AsComplexVector - Right.AsComplexVector)
  else
    Result := ASO(Left.AsRealVector - Right.AsRealVector)
end;

function TAlgosimVector.ToSpeech: string;
var
  i: Integer;
begin
  Result := Format('Begin vector of dimension %d. ', [Dimension]);
  if GetDimension > 0 then
    Result := Result + GetElementAsStringFmt(0, DefaultFormatOptions);
  for i := 1 to GetDimension - 1 do
    Result := Result + '. Next: ' + GetElementAsStringFmt(i, DefaultFormatOptions) + '.';
  Result := Result + ' End vector.';
end;

function TAlgosimVector.ToString: string;
var
  i: Integer;
begin
  Result := '';
  if GetDimension > 0 then
    Result := GetElementAsStringFmt(0, ExchangeFormOptions);
  for i := 1 to GetDimension - 1 do
    Result := Result + sLineBreak + GetElementAsStringFmt(i, ExchangeFormOptions);
end;

function TAlgosimVector.WithSpecificValues(
  AValues: TAlgosimArray): TAlgosimObject;
begin
  Result := NumArrayClass(False, IsComplex or AValues.IsComplex).Create(AValues);
end;

{ TAlgosimRealVector }

function TAlgosimRealVector.Abs: TAlgosimNumericEntity;
begin
  Result := ASO(FValue.Abs);
end;

function TAlgosimRealVector.Accumulate(AInitialValue: TAlgosimObject;
  AFunction: TASOAccumulator): TAlgosimObject;
var
  i: Integer;
  num: TAlgosimRealNumber;
begin
  num := TAlgosimRealNumber.Create;
  try
    Result := AInitialValue.Clone;
    try
      for i := 0 to FValue.Dimension - 1 do
      begin
        num.FValue := FValue[i];
        TObjReplacer<TAlgosimObject>.Replace(Result, AFunction(Result, num));
      end;
    except
      Result.Free;
      raise;
    end;
  finally
    num.Free;
  end;
end;

function TAlgosimRealVector.AccumulateStepsList(AInitialValue: TAlgosimObject;
  AFunction: TASOAccumulator): TAlgosimArray;
var
  i: Integer;
  num: TAlgosimRealNumber;
begin
  num := TAlgosimRealNumber.Create;
  try
    Result := TAlgosimArray.Create;
    try
      Result.Capacity := FValue.Dimension;
      for i := 0 to FValue.Dimension - 1 do
      begin
        num.FValue := FValue[i];
        AInitialValue := AFunction(AInitialValue, num);
        Result.Add(AInitialValue);
      end;
    except
      Result.Free;
      raise;
    end;
  finally
    num.Free;
  end;
end;

procedure TAlgosimRealVector.AddNumbersToArray(AArray: TAlgosimArray; ALevel: Integer);
var
  i: Integer;
begin
  for i := 0 to FValue.Dimension - 1 do
    AArray.Add(ASO(FValue[i]));
end;

procedure TAlgosimRealVector.Append(AElement: TAlgosimObject);
begin
  try
    FValue.Append(AElement.ToASR);
  finally
    AElement.Free;
  end;
end;

procedure TAlgosimRealVector.Apply(AFunction: TASOFunction;
  ACondition: TASOPredicate; ALevel: Integer);
var
  i: Integer;
  num: TAlgosimRealNumber;
  res: TAlgosimObject;
  x: TASR;
begin

  if ALevel > 1 then
    Exit;

  num := TAlgosimRealNumber.Create;
  try
    for i := 0 to FValue.Dimension - 1 do
    begin
      num.Value := FValue[i];
      if not Assigned(ACondition) or ACondition(num) then
      begin
        res := AFunction(num);
        try
          if res.TryToASR(x) then
            FValue[i] := x
          else
            raise EAlgosimObjectException.Create(SRealNumberApply);
        finally
          res.Free;
        end;
      end;
    end;
  finally
    num.Free;
  end;

end;

function TAlgosimRealVector.CollapseSequences: TAlgosimArray;
var
  i: Integer;
  LastVal: TASR;
  c: TAlgosimInteger;
begin
  c := nil; // To make compiler happy
  LastVal := 0; //
  Result := TAlgosimArray.Create;
  try
    Result.Capacity := FValue.Dimension;
    for i := 0 to FValue.Dimension - 1 do
      if (i = 0) or (FValue[i] <> LastVal) then
      begin
        LastVal := FValue[i];
        c := ASOInt(1);
        Result.AddElement(
          ASO(
            [
              ASO(LastVal),
              c
            ]
          )
        );
      end
      else
        Inc(C.FValue);
    Result.TrimExcess;
  except
    Result.Free;
    raise;
  end;
end;

function TAlgosimRealVector.CollapseSequencesEps(
  const Epsilon: TASR): TAlgosimArray;
var
  i: Integer;
  LastValue: TASR;
  c: TAlgosimInteger;
begin
  c := nil; // To make compiler happy
  LastValue := 0; //
  Result := TAlgosimArray.Create;
  try
    Result.Capacity := FValue.Dimension;
    for i := 0 to FValue.Dimension - 1 do
      if (i = 0) or not SameValue(FValue[i], LastValue, Epsilon) then
      begin
        LastValue := FValue[i];
        c := ASOInt(1);
        Result.AddElement(
          ASO(
            [
              ASO(FValue[i]),
              c
            ]
          )
        );
      end
      else
        Inc(C.FValue);
    Result.TrimExcess;
  except
    Result.Free;
    raise;
  end;
end;

function TAlgosimRealVector.ConjugateTranspose: TAlgosimMatrix;
begin
  Result := ASO(TRealMatrix.CreateFromRows([FValue]));
end;

function TAlgosimRealVector.Count(APredicate: TASOPredicate): Integer;
var
  i: Integer;
  num: TAlgosimRealNumber;
begin
  num := TAlgosimRealNumber.Create;
  try
    Result := 0;
    for i := 0 to FValue.Dimension - 1 do
    begin
      num.Value := FValue[i];
      if APredicate(num) then
        Inc(Result);
    end;
  finally
    num.Free;
  end;
end;

constructor TAlgosimRealVector.Create(AObject: TAlgosimObject);
begin
  CreateWithValue(AObject.ToRealVector);
end;

constructor TAlgosimRealVector.CreateWithValue(const AValue: TRealVector);
begin
  Create;
  FValue := AValue;
end;

procedure TAlgosimRealVector.Defuzz(const Eps: Double);
begin
  Value.Defuzz(Eps);
end;

function TAlgosimRealVector.Exists(APredicate: TASOPredicate): Boolean;
var
  i: Integer;
  num: TAlgosimRealNumber;
begin
  num := TAlgosimRealNumber.Create;
  try
    for i := 0 to FValue.Dimension - 1 do
    begin
      num.Value := FValue[i];
      if APredicate(num) then
        Exit(True);
    end;
    Result := False;
  finally
    num.Free;
  end;
end;

function TAlgosimRealVector.Filter(APredicate: TASOPredicate): TAlgosimObject;
var
  vec: TRealVector;
  i, j: Integer;
  num: TAlgosimRealNumber;
begin
  vec.Dimension := FValue.Dimension;
  j := 0;
  num := TAlgosimRealNumber.Create;
  try
    for i := 0 to FValue.Dimension - 1 do
    begin
      num.FValue := FValue[i];
      if APredicate(num) then
      begin
        vec[j] := num.FValue;
        Inc(j);
      end;
    end;
  finally
    num.Free;
  end;
  vec.Dimension := j;
  Exit(ASO(vec));
end;

function TAlgosimRealVector.First(N: Integer): TAlgosimObject;
begin
  if N > 0 then
    Result := ASO(Value.Subvector(0, N - 1))
  else
    Result := TAlgosimRealVector.Create;
end;

function TAlgosimRealVector.ForAll(APredicate: TASOPredicate): Boolean;
var
  i: Integer;
  num: TAlgosimRealNumber;
begin
  num := TAlgosimRealNumber.Create;
  try
    for i := 0 to FValue.Dimension - 1 do
    begin
      num.Value := FValue[i];
      if not APredicate(num) then
        Exit(False);
    end;
    Result := True;
  finally
    num.Free;
  end;
end;

function TAlgosimRealVector.Frequencies: TAlgosimArray;
var
  Dict: TDictionary<TASR, TAlgosimInteger>;
  C: TAlgosimInteger;
  i: Integer;
begin
  Result := TAlgosimArray.Create;
  try
    Result.Capacity := FValue.Dimension;
    Dict := TDictionary<TASR, TAlgosimInteger>.Create;
    try
      for i := 0 to FValue.Dimension - 1 do
      begin
        if Dict.TryGetValue(FValue[i], C) then
          Inc(C.FValue)
        else
        begin
          C := ASOInt(1);
          Result.AddElement(
            ASO(
              [
                ASO(FValue[i]),
                C
              ]
            )
          );
          Dict.Add(FValue[i], C);
        end;
      end;
    finally
      Dict.Free;
    end;
    Result.TrimExcess;
  except
    Result.Free;
    raise;
  end;
end;

function TAlgosimRealVector.FrequenciesEps(const Epsilon: TASR): TAlgosimArray;
var
  i: Integer;
  j: Integer;
label
  NextValue;
begin
  Result := TAlgosimArray.Create;
  try
    Result.Capacity := FValue.Dimension;
    for i := 0 to FValue.Dimension - 1 do
    begin
      for j := 1 to Result.ElementCount do
        if SameValue(Result.Elements[j].Elements[1].ToASR, FValue[i], Epsilon) then
        begin
          Inc((Result.Elements[j].Elements[2] as TAlgosimInteger).FValue);
          goto NextValue;
        end;
      Result.AddElement(
        ASO(
          [
            ASO(FValue[i]),
            ASOInt(1)
          ]
        )
      );
      NextValue:
    end;
    Result.TrimExcess;
  except
    Result.Free;
    raise;
  end;
end;

function TAlgosimRealVector.GetDimension: Integer;
begin
  Result := FValue.Dimension;
end;

function TAlgosimRealVector.GetElementAsString(Index: Integer): string;
begin
  Result := RealToStr(FValue[Index], DefaultFormatOptions);
end;

function TAlgosimRealVector.GetElementAsStringFmt(Index: Integer;
  const AOptions: TFormatOptions): string;
begin
  Result := RealToStr(FValue[Index], ApplyOptions(AOptions));
end;

function TAlgosimRealVector.GetMemorySize: UInt64;
begin
  Result := FValue.Dimension * sizeof(TASR);
end;

function TAlgosimRealVector.SortClassGetHashCode: Integer;
begin
  if Length(FValue.Data) > 0 then
    Result := THashBobJenkins.GetHashValue(FValue.Data[0], Length(FValue.Data) * sizeof(FValue.Data[0]))
  else
    Result := 0;
end;

function TAlgosimRealVector.GetBinaryData(var Buf: PByte;
  var Len: UInt64): Boolean;
begin
  Buf := Pointer(FValue);
  Len := Dimension * sizeof(TASR);
  Result := True;
end;

function TAlgosimRealVector.GetValue(Index: Integer): TAlgosimObject;
var
  PhysIndex: Integer;
begin
  PhysIndex := GetPhysIndex0(Index, Dimension);
  Result := ASO(FValue[PhysIndex]);
end;

function TAlgosimRealVector.GetValueFromPoint(
  const APoint: TPoint): TAlgosimObject;
begin
  if (APoint.X = 1) and InRange(APoint.Y, 1, FValue.Dimension) then
    Result := ASO(FValue[APoint.Y - 1])
  else
    raise EAlgosimObjectException.CreateFmt(SIndexOutOfBounds2D, [APoint.Y, APoint.X]);
end;

function TAlgosimRealVector.ImaginaryPart: TAlgosimNumericEntity;
begin
  Result := ASO(ZeroVector(FValue.Dimension));
end;

function TAlgosimRealVector.IndexOfValue(AObj: TAlgosimObject): TAlgosimObject;
var
  x: TASR;
  i: Integer;
begin

  if not (AObj is TAlgosimNumber) then
    Exit(ASO(null));

  if not AObj.TryToASR(x) then
    Exit(ASO(null));

  for i := 0 to FValue.Dimension - 1 do
    if FValue[i] = x then
      Exit(ASOInt(i + 1));

  Result := ASO(null);

end;

function TAlgosimRealVector.IndexOfValueEps(AObj: TAlgosimObject;
  const AEpsilon: TASR): TAlgosimObject;
var
  x: TASR;
  i: Integer;
begin

  if not (AObj is TAlgosimNumber) then
    Exit(ASO(null));

  if not AObj.TryToASR(x) then
    Exit(ASO(null));

  for i := 0 to FValue.Dimension - 1 do
    if SameValue(FValue[i], x, AEpsilon) then
      Exit(ASOInt(i + 1));

  Result := ASO(null);

end;

function TAlgosimRealVector.IndicesOf(APredicate: TASOPredicate): TAlgosimArray;
var
  r: TAlgosimRealNumber;
  i: Integer;
begin

  Result := TAlgosimArray.Create;
  try

    Result.Capacity := CapCap(FValue.Dimension);

    r := TAlgosimRealNumber.Create;
    try
      for i := 0 to FValue.Dimension - 1 do
      begin
        r.Value := FValue.Data[i];
        if APredicate(r) then
          Result.Add(ASOInt(i + 1));
      end;
    finally
      r.Free;
    end;

    Result.TrimExcess;

  except
    Result.Free;
    raise;
  end;

end;

function TAlgosimRealVector.IndicesOfValue(AObj: TAlgosimObject): TAlgosimArray;
var
  x: TASR;
  i: Integer;
begin

  Result := TAlgosimArray.Create;
  try

    if not (AObj is TAlgosimNumber) then
      Exit;

    if not AObj.TryToASR(x) then
      Exit;

    Result.Capacity := CapCap(FValue.Dimension);

    for i := 0 to FValue.Dimension - 1 do
      if FValue[i] = x then
        Result.Add(ASOInt(i + 1));

    Result.TrimExcess;

  except
    Result.Free;
    raise;
  end;

end;

function TAlgosimRealVector.IndicesOfValueEps(AObj: TAlgosimObject;
  const AEpsilon: TASR): TAlgosimArray;
var
  x: TASR;
  i: Integer;
begin

  Result := TAlgosimArray.Create;
  try

    if not (AObj is TAlgosimNumber) then
      Exit;

    if not AObj.TryToASR(x) then
      Exit;

    Result.Capacity := CapCap(FValue.Dimension);

    for i := 0 to FValue.Dimension - 1 do
      if SameValue(FValue[i], x, AEpsilon) then
        Result.Add(ASOInt(i + 1));

    Result.TrimExcess;

  except
    Result.Free;
    raise;
  end;

end;

procedure TAlgosimRealVector.Insert(AIndex: Integer; AElement: TAlgosimObject);
begin
  try
    FValue.Insert(GetPhysIndex0(AIndex, FValue.Dimension + 1), AElement.ToASR);
  finally
    AElement.Free;
  end;
end;

function TAlgosimRealVector.IsNegative(const Eps: Double): Boolean;
begin
  Result := FValue.IsNegative(Eps);
end;

function TAlgosimRealVector.IsNonNegative(const Eps: Double): Boolean;
begin
  Result := FValue.IsNonNegative(Eps);
end;

function TAlgosimRealVector.IsNonPositive(const Eps: Double): Boolean;
begin
  Result := FValue.IsNonPositive(Eps);
end;

function TAlgosimRealVector.IsNonZero(const Eps: Double): Boolean;
begin
  Result := not FValue.IsZeroVector(Eps);
end;

function TAlgosimRealVector.IsPositive(const Eps: Double): Boolean;
begin
  Result := FValue.IsPositive(Eps);
end;

function TAlgosimRealVector.IsZero(const Eps: Double): Boolean;
begin
  Result := FValue.IsZeroVector(Eps);
end;

function TAlgosimRealVector.Last(N: Integer): TAlgosimObject;
begin
  if N > 0 then
    Result := ASO(Value.Subvector(Value.Dimension - N, Value.Dimension - 1))
  else
    Result := TAlgosimRealVector.Create;
end;

function TAlgosimRealVector.Norm(AType: TNormType; AParam: Integer;
  AYieldProc: TObjProc): TASR;
begin
  case AType of
    ntEuclidean, ntFrobenius:
      Result := FValue.Norm;
    ntPNorm:
      Result := FValue.pNorm(AParam);
    ntMaxNorm, ntMaxRowSum:
      Result := FValue.MaxNorm;
    ntSumNorm, ntMaxColSum:
      Result := FValue.SumNorm;
    ntKNorm:
      Result := FValue.kNorm(AParam);
  else
    Result := inherited;
  end;
end;

function TAlgosimRealVector.Normalized: TAlgosimVector;
begin
  Result := ASO(FValue.Normalized);
end;

function TAlgosimRealVector.NormalizedIfNonzero: TAlgosimVector;
begin
  Result := ASO(FValue.NormalizedIfNonzero);
end;

function TAlgosimRealVector.NormSquared: TAlgosimRealNumber;
begin
  Result := ASO(FValue.NormSqr);
end;

function TAlgosimRealVector.N_GeometricMean: TAlgosimNumber;
begin
  Result := ASO(ASNum.GeometricMean(FValue));
end;

function TAlgosimRealVector.N_HarmonicMean: TAlgosimNumber;
begin
  Result := ASO(ASNum.HarmonicMean(FValue));
end;

function TAlgosimRealVector.N_max: TAlgosimNumber;
begin
  Result := ASO(ASNum.max(FValue));
end;

function TAlgosimRealVector.N_min: TAlgosimNumber;
begin
  Result := ASO(ASNum.min(FValue));
end;

function TAlgosimRealVector.N_product: TAlgosimNumericEntity;
begin
  Result := ASO(ASNum.product(FValue));
end;

function TAlgosimRealVector.N_sum: TAlgosimNumericEntity;
begin
  Result := ASO(ASNum.sum(FValue));
end;

function TAlgosimRealVector.Part(A, B: Integer): TAlgosimObject;
begin
  Result := ASO(Value.Subvector(A - 1, B - 1));
end;

function TAlgosimRealVector.Part(
  const AIndices: array of Integer): TAlgosimObject;
begin
  Result := ASO(Value.Subvector(TranslatedIntSequence(AIndices)));
end;

function TAlgosimRealVector.Part(
  const ARanges: array of TRange): TAlgosimObject;
begin
  Result := Part(ParseRangeSeq(ARanges, Value.Dimension));
end;

function TAlgosimRealVector.Part(A: Integer): TAlgosimObject;
begin
  Result := ASO(Value.Subvector(A - 1, Value.Dimension - 1));
end;

function TAlgosimRealVector.RealPart: TAlgosimNumericEntity;
begin
  Result := TAlgosimNumericEntity(Self.Clone);
end;

procedure TAlgosimRealVector.Reverse;
begin
  FValue.Reverse;
end;

function TAlgosimRealVector.RotLeft(N: Integer): TAlgosimObject;
begin
  Result := ASO(Value shl N);
end;

function TAlgosimRealVector.RotRight(N: Integer): TAlgosimObject;
begin
  Result := ASO(Value shr N);
end;

function TAlgosimRealVector.ScaledBy(
  const AFactor: TASR): TAlgosimNumericEntity;
begin
  Result := ASO(FValue * AFactor);
end;

procedure TAlgosimRealVector.SetDimension(const Value: Integer);
begin
  FValue.Dimension := Value;
end;

procedure TAlgosimRealVector.SetValue(Index: Integer; AValue: TAlgosimObject);
var
  x: TASR;
  PhysIndex: Integer;
begin

  try

    PhysIndex := GetPhysIndex0(Index, Dimension);

    if (AValue is TAlgosimNumber) and AValue.TryToASR(x) then
      FValue[PhysIndex] := x
    else
      raise EAlgosimObjectException.CreateFmt(SSetRVectSubscriptNoRNum, [AValue.TypeName])

  finally
    AValue.Free;
  end;

end;

procedure TAlgosimRealVector.SetValueFromPoint(const APoint: TPoint;
  AValue: TAlgosimObject);
var
  x: TASR;
begin
  try
    if (APoint.X = 1) and InRange(APoint.Y, 1, FValue.Dimension) then
      if AValue.TryToASR(x) then
        FValue[APoint.Y - 1] := x
      else
        raise EAlgosimObjectException.CreateFmt(SSetRMatSubscriptNoRNum, [AValue.TypeName])
    else
      raise EAlgosimObjectException.CreateFmt(SIndexOutOfBounds2D, [APoint.Y, APoint.X]);
  finally
    AValue.Free;
  end;
end;

procedure TAlgosimRealVector.Shuffle;
begin
  FValue.Shuffle;
end;

procedure TAlgosimRealVector.Sort;
begin
  FValue.Sort;
end;

procedure TAlgosimRealVector.Sort(AComparer: IComparer<TAlgosimObject>);
var
  LeftObj, RightObj: TAlgosimRealNumber;
begin
  LeftObj := TAlgosimRealNumber.Create;
  try
    RightObj := TAlgosimRealNumber.Create;
    try
      FValue.Sort(TComparer<TASR>.Construct(
        function(const Left, Right: TASR): Integer
        begin
          LeftObj.Value := Left;
          RightObj.Value := Right;
          Result := AComparer.Compare(LeftObj, RightObj);
        end
      ));
    finally
      RightObj.Free;
    end;
  finally
    LeftObj.Free;
  end;
end;

procedure TAlgosimRealVector.Sort(AComparer: IComparer<TASR>);
begin
  FValue.Sort(AComparer);
end;

procedure TAlgosimRealVector.Sort(AComparer: IComparer<TASC>);
begin
  FValue.Sort(TComparer<TASR>.Construct(
    function(const Left, Right: TASR): Integer
    begin
      Result := AComparer.Compare(Left, Right)
    end
  ));
end;

function TAlgosimRealVector.Square: TAlgosimNumericEntity;
begin
  Result := ASO(Value * Value);
end;

procedure TAlgosimRealVector.Swap(Index1, Index2: Integer);
var
  PhysIdx1, PhysIdx2: Integer;
begin

  PhysIdx1 := GetPhysIndex0(Index1, FValue.Dimension);
  PhysIdx2 := GetPhysIndex0(Index2, FValue.Dimension);

  if PhysIdx1 = PhysIdx2 then
    Exit;

  FValue.Swap(PhysIdx1, PhysIdx2);

end;

function TAlgosimRealVector.ToComplexMatrix: TComplexMatrix;
begin
  Result := TComplexMatrix.Create(TComplexVector(Value));
end;

function TAlgosimRealVector.ToComplexNumber: TASC;
begin
  // Special case: a two-dimensional real vector can be converted to a
  // complex number in the natural way.
  if Dimension = 2 then
    Result := ASC(Value)
  else
    Result := inherited; // handles the (non-special) case of a one-dimensional vector
end;

function TAlgosimRealVector.ToComplexVector: TComplexVector;
begin
  Result := Value;
end;

function TAlgosimRealVector.ToRealMatrix: TRealMatrix;
begin
  Result := TRealMatrix.Create(Value);
end;

function TAlgosimRealVector.ToRealVector: TRealVector;
begin
  Result := FValue.Clone;
end;

function TAlgosimRealVector.Transpose: TAlgosimMatrix;
begin
  Result := ASO(TRealMatrix.CreateFromRows([FValue]));
end;

procedure TAlgosimRealVector.Truncate(ANewLength: Integer);
begin

  if ANewLength < 0 then
    raise EArrayException.Create(SNewLengthMustBeNonNegative);

  if ANewLength >= FValue.Dimension then
    Exit;

  FValue.Dimension := ANewLength;

end;

function TAlgosimRealVector.AsRealVector: TRealVector;
begin
  Result := FValue;
end;

procedure TAlgosimRealVector.ExtendWith(AElement: TAlgosimObject);
begin
  try
    if AElement is TAlgosimComplexNumber then
      raise Exception.Create(SObjNoFloat); // otherwise it will be implicitly treated as a vector in R^2, which in this case is highly unexpected
    FValue.ExtendWith(AElement.AsRealVector);
  finally
    AElement.Free;
  end;
end;

function TAlgosimRealVector.UnaryMinus: TAlgosimObject;
begin
  Result := ASO(-FValue);
end;

procedure TAlgosimRealVector.Remove(const AIndices: array of Integer);
begin
  FValue.Remove(TranslatedIntSequence(AIndices));
end;

function TAlgosimRealVector.RemoveAdjacentDuplicates: TAlgosimObject;
begin
  Result := ASO(FValue.UniqueAdj);
end;

function TAlgosimRealVector.RemoveAdjacentDuplicatesEps(
  const Epsilon: TASR): TAlgosimObject;
begin
  Result := ASO(FValue.UniqueAdjEps(Epsilon));
end;

function TAlgosimRealVector.RemoveDuplicates: TAlgosimObject;
begin
  Result := ASO(FValue.Unique);
end;

function TAlgosimRealVector.RemoveDuplicatesEps(
  const Epsilon: TASR): TAlgosimObject;
begin
  Result := ASO(FValue.UniqueEps(Epsilon));
end;

procedure TAlgosimRealVector.RemoveFirst(N: Integer);
begin
  FValue.RemoveFirst(N);
end;

procedure TAlgosimRealVector.Replace(APredicate: TASOPredicate;
  ANewValue: TAlgosimObject; ALevel: Integer);
var
  i: Integer;
  Obj: TAlgosimRealNumber;
  x: TASR;
begin

  if ALevel > 1 then
    Exit;

  x := ANewValue.ToASR;

  Obj := TAlgosimRealNumber.Create;
  try
    for i := 0 to FValue.Dimension - 1 do
    begin
      Obj.FValue := FValue[i];
      if not Assigned(APredicate) or APredicate(Obj) then
        FValue[i] := x;
    end;
  finally
    Obj.Free;
  end;

end;

{ TAlgosimComplexVector }

procedure TAlgosimComplexVector.Defuzz(const Eps: Double);
begin
  Value.Defuzz(Eps);
end;

function TAlgosimComplexVector.Abs: TAlgosimNumericEntity;
begin
  Result := ASO(FValue.Abs);
end;

function TAlgosimComplexVector.Accumulate(AInitialValue: TAlgosimObject;
  AFunction: TASOAccumulator): TAlgosimObject;
var
  i: Integer;
  num: TAlgosimComplexNumber;
begin
  num := TAlgosimComplexNumber.Create;
  try
    Result := AInitialValue.Clone;
    try
      for i := 0 to FValue.Dimension - 1 do
      begin
        num.FValue := FValue[i];
        TObjReplacer<TAlgosimObject>.Replace(Result, AFunction(Result, num));
      end;
    except
      Result.Free;
      raise;
    end;
  finally
    num.Free;
  end;
end;

function TAlgosimComplexVector.AccumulateStepsList(AInitialValue: TAlgosimObject;
  AFunction: TASOAccumulator): TAlgosimArray;
var
  i: Integer;
  num: TAlgosimComplexNumber;
begin
  num := TAlgosimComplexNumber.Create;
  try
    Result := TAlgosimArray.Create;
    try
      Result.Capacity := FValue.Dimension;
      for i := 0 to FValue.Dimension - 1 do
      begin
        num.FValue := FValue[i];
        AInitialValue := AFunction(AInitialValue, num);
        Result.Add(AInitialValue);
      end;
    except
      Result.Free;
      raise;
    end;
  finally
    num.Free;
  end;
end;

procedure TAlgosimComplexVector.AddNumbersToArray(AArray: TAlgosimArray; ALevel: Integer);
var
  i: Integer;
begin
  for i := 0 to FValue.Dimension - 1 do
    AArray.Add(ASO(FValue[i]));
end;

procedure TAlgosimComplexVector.Append(AElement: TAlgosimObject);
begin
  try
    FValue.Append(AElement.ToASC);
  finally
    AElement.Free;
  end;
end;

procedure TAlgosimComplexVector.Apply(AFunction: TASOFunction;
  ACondition: TASOPredicate; ALevel: Integer);
var
  i: Integer;
  num: TAlgosimComplexNumber;
  res: TAlgosimObject;
  z: TASC;
begin

  if ALevel > 1 then
    Exit;

  num := TAlgosimComplexNumber.Create;
  try
    for i := 0 to FValue.Dimension - 1 do
    begin
      num.Value := FValue[i];
      if not Assigned(ACondition) or ACondition(num) then
      begin
        res := AFunction(num);
        try
          if res.TryToASC(z) then
            FValue[i] := z
          else
            raise EAlgosimObjectException.Create(SComplexNumberApply);
        finally
          res.Free;
        end;
      end;
    end;
  finally
    num.Free;
  end;

end;

function TAlgosimComplexVector.CollapseSequences: TAlgosimArray;
var
  i: Integer;
  LastVal: TASC;
  c: TAlgosimInteger;
begin
  c := nil; // To make compiler happy
  LastVal := 0; //
  Result := TAlgosimArray.Create;
  try
    Result.Capacity := FValue.Dimension;
    for i := 0 to FValue.Dimension - 1 do
      if (i = 0) or (FValue[i] <> LastVal) then
      begin
        LastVal := FValue[i];
        c := ASOInt(1);
        Result.AddElement(
          ASO(
            [
              ASO(LastVal),
              c
            ]
          )
        );
      end
      else
        Inc(C.FValue);
    Result.TrimExcess;
  except
    Result.Free;
    raise;
  end;
end;

function TAlgosimComplexVector.CollapseSequencesEps(
  const Epsilon: TASR): TAlgosimArray;
var
  i: Integer;
  LastValue: TASC;
  c: TAlgosimInteger;
begin
  c := nil; // To make compiler happy
  LastValue := 0; //
  Result := TAlgosimArray.Create;
  try
    Result.Capacity := FValue.Dimension;
    for i := 0 to FValue.Dimension - 1 do
      if (i = 0) or not CSameValue(FValue[i], LastValue, Epsilon) then
      begin
        LastValue := FValue[i];
        c := ASOInt(1);
        Result.AddElement(
          ASO(
            [
              ASO(LastValue),
              c
            ]
          )
        );
      end
      else
        Inc(C.FValue);
    Result.TrimExcess;
  except
    Result.Free;
    raise;
  end;
end;

function TAlgosimComplexVector.ConjugateTranspose: TAlgosimMatrix;
begin
  Result := ASO(TComplexMatrix(FValue).Adjoint);
end;

function TAlgosimComplexVector.Count(APredicate: TASOPredicate): Integer;
var
  i: Integer;
  num: TAlgosimComplexNumber;
begin
  num := TAlgosimComplexNumber.Create;
  try
    Result := 0;
    for i := 0 to FValue.Dimension - 1 do
    begin
      num.Value := FValue[i];
      if APredicate(num) then
        Inc(Result);
    end;
  finally
    num.Free;
  end;
end;

constructor TAlgosimComplexVector.Create(AObject: TAlgosimObject);
begin
  CreateWithValue(AObject.ToComplexVector);
end;

constructor TAlgosimComplexVector.CreateWithValue(const AValue: TComplexVector);
begin
  Create;
  FValue := AValue;
end;

function TAlgosimComplexVector.Exists(APredicate: TASOPredicate): Boolean;
var
  i: Integer;
  num: TAlgosimComplexNumber;
begin
  num := TAlgosimComplexNumber.Create;
  try
    for i := 0 to FValue.Dimension - 1 do
    begin
      num.Value := FValue[i];
      if APredicate(num) then
        Exit(True);
    end;
    Result := False;
  finally
    num.Free;
  end;
end;

function TAlgosimComplexVector.Filter(APredicate: TASOPredicate): TAlgosimObject;
var
  vec: TComplexVector;
  i, j: Integer;
  num: TAlgosimComplexNumber;
begin
  vec.Dimension := FValue.Dimension;
  j := 0;
  num := TAlgosimComplexNumber.Create;
  try
    for i := 0 to FValue.Dimension - 1 do
    begin
      num.FValue := FValue[i];
      if APredicate(num) then
      begin
        vec[j] := num.FValue;
        Inc(j);
      end;
    end;
  finally
    num.Free;
  end;
  vec.Dimension := j;
  Exit(ASO(vec));
end;

function TAlgosimComplexVector.First(N: Integer): TAlgosimObject;
begin
  if N > 0 then
    Result := ASO(Value.Subvector(0, N - 1))
  else
    Result := TAlgosimComplexVector.Create;
end;

function TAlgosimComplexVector.ForAll(APredicate: TASOPredicate): Boolean;
var
  i: Integer;
  num: TAlgosimComplexNumber;
begin
  num := TAlgosimComplexNumber.Create;
  try
    for i := 0 to FValue.Dimension - 1 do
    begin
      num.Value := FValue[i];
      if not APredicate(num) then
        Exit(False);
    end;
    Result := True;
  finally
    num.Free;
  end;
end;

function TAlgosimComplexVector.Frequencies: TAlgosimArray;
var
  Dict: TDictionary<TASC, TAlgosimInteger>;
  C: TAlgosimInteger;
  i: Integer;
begin
  Result := TAlgosimArray.Create;
  try
    Result.Capacity := FValue.Dimension;
    Dict := TDictionary<TASC, TAlgosimInteger>.Create;
    try
      for i := 0 to FValue.Dimension - 1 do
      begin
        if Dict.TryGetValue(FValue[i], C) then
          Inc(C.FValue)
        else
        begin
          C := ASOInt(1);
          Result.AddElement(
            ASO(
              [
                ASO(FValue[i]),
                C
              ]
            )
          );
          Dict.Add(FValue[i], C);
        end;
      end;
    finally
      Dict.Free;
    end;
    Result.TrimExcess;
  except
    Result.Free;
    raise;
  end;
end;

function TAlgosimComplexVector.FrequenciesEps(
  const Epsilon: TASR): TAlgosimArray;
var
  i: Integer;
  j: Integer;
label
  NextValue;
begin
  Result := TAlgosimArray.Create;
  try
    Result.Capacity := FValue.Dimension;
    for i := 0 to FValue.Dimension - 1 do
    begin
      for j := 1 to Result.ElementCount do
        if CSameValue(Result.Elements[j].Elements[1].ToASC, FValue[i], Epsilon) then
        begin
          Inc((Result.Elements[j].Elements[2] as TAlgosimInteger).FValue);
          goto NextValue;
        end;
      Result.AddElement(
        ASO(
          [
            ASO(FValue[i]),
            ASOInt(1)
          ]
        )
      );
      NextValue:
    end;
    Result.TrimExcess;
  except
    Result.Free;
    raise;
  end;
end;

function TAlgosimComplexVector.GetDimension: Integer;
begin
  Result := FValue.Dimension;
end;

function TAlgosimComplexVector.GetElementAsString(Index: Integer): string;
begin
  Result := ComplexToStr(FValue[Index], False, DefaultFormatOptions)
end;

function TAlgosimComplexVector.GetElementAsStringFmt(Index: Integer;
  const AOptions: TFormatOptions): string;
begin
  Result := ComplexToStr(FValue[Index], False, ApplyOptions(AOptions))
end;

function TAlgosimComplexVector.GetMemorySize: UInt64;
begin
  Result := FValue.Dimension * sizeof(TASC);
end;

function TAlgosimComplexVector.SortClassGetHashCode: Integer;
var
  HasComplexComponent: Boolean;
  i: Integer;
  RV: TRealVector;
begin

  if FValue.Dimension = 0 then
    Exit(0);

  HasComplexComponent := False;
  for i := 0 to FValue.Dimension - 1 do
    if FValue[i].Im <> 0 then
    begin
      HasComplexComponent := True;
      Break;
    end;

  if HasComplexComponent then
    Result := THashBobJenkins.GetHashValue(FValue.Data[0], Length(FValue.Data) * sizeof(FValue.Data[0]))
  else
  begin
    RV := Self.AsRealVector;
    Result := THashBobJenkins.GetHashValue(RV.Data[0], Length(RV.Data) * sizeof(RV.Data[0]));
  end;

end;

function TAlgosimComplexVector.GetBinaryData(var Buf: PByte;
  var Len: UInt64): Boolean;
begin
  Buf := Pointer(FValue);
  Len := Dimension * sizeof(TASC);
  Result := True;
end;

function TAlgosimComplexVector.GetValue(Index: Integer): TAlgosimObject;
var
  PhysIndex: Integer;
begin
  PhysIndex := GetPhysIndex0(Index, Dimension);
  Result := ASO(FValue[PhysIndex]);
end;

function TAlgosimComplexVector.GetValueFromPoint(
  const APoint: TPoint): TAlgosimObject;
begin
  if (APoint.X = 1) and InRange(APoint.Y, 1, FValue.Dimension) then
    Result := ASO(FValue[APoint.Y - 1])
  else
    raise EAlgosimObjectException.CreateFmt(SIndexOutOfBounds2D, [APoint.Y, APoint.X]);
end;

function TAlgosimComplexVector.ImaginaryPart: TAlgosimNumericEntity;
begin
  Result := ASO(FValue.ImaginaryPart);
end;

function TAlgosimComplexVector.IndexOfValue(
  AObj: TAlgosimObject): TAlgosimObject;
var
  z: TASC;
  i: Integer;
begin

  if not (AObj is TAlgosimNumber) then
    Exit(ASO(null));

  if not AObj.TryToASC(z) then
    Exit(ASO(null));

  for i := 0 to FValue.Dimension - 1 do
    if FValue[i] = z then
      Exit(ASOInt(i + 1));

  Result := ASO(null);

end;

function TAlgosimComplexVector.IndexOfValueEps(AObj: TAlgosimObject;
  const AEpsilon: TASR): TAlgosimObject;
var
  z: TASC;
  i: Integer;
begin

  if not (AObj is TAlgosimNumber) then
    Exit(ASO(null));

  if not AObj.TryToASC(z) then
    Exit(ASO(null));

  for i := 0 to FValue.Dimension - 1 do
    if CSameValue(FValue[i], z, AEpsilon) then
      Exit(ASOInt(i + 1));

  Result := ASO(null);

end;

function TAlgosimComplexVector.IndicesOf(
  APredicate: TASOPredicate): TAlgosimArray;
var
  c: TAlgosimComplexNumber;
  i: Integer;
begin

  Result := TAlgosimArray.Create;
  try

    Result.Capacity := CapCap(FValue.Dimension);

    c := TAlgosimComplexNumber.Create;
    try
      for i := 0 to FValue.Dimension - 1 do
      begin
        c.Value := FValue.Data[i];
        if APredicate(c) then
          Result.Add(ASOInt(i + 1));
      end;
    finally
      c.Free;
    end;

    Result.TrimExcess;

  except
    Result.Free;
    raise;
  end;

end;

function TAlgosimComplexVector.IndicesOfValue(
  AObj: TAlgosimObject): TAlgosimArray;
var
  z: TASC;
  i: Integer;
begin

  Result := TAlgosimArray.Create;
  try

    if not (AObj is TAlgosimNumber) then
      Exit;

    if not AObj.TryToASC(z) then
      Exit;

    Result.Capacity := CapCap(FValue.Dimension);

    for i := 0 to FValue.Dimension - 1 do
      if FValue[i] = z then
        Result.Add(ASOInt(i + 1));

    Result.TrimExcess;

  except
    Result.Free;
    raise;
  end;

end;

function TAlgosimComplexVector.IndicesOfValueEps(AObj: TAlgosimObject;
  const AEpsilon: TASR): TAlgosimArray;
var
  z: TASC;
  i: Integer;
begin

  Result := TAlgosimArray.Create;
  try

    if not (AObj is TAlgosimNumber) then
      Exit;

    if not AObj.TryToASC(z) then
      Exit;

    Result.Capacity := CapCap(FValue.Dimension);

    for i := 0 to FValue.Dimension - 1 do
      if CSameValue(FValue[i], z, AEpsilon) then
        Result.Add(ASOInt(i + 1));

    Result.TrimExcess;

  except
    Result.Free;
    raise;
  end;

end;

procedure TAlgosimComplexVector.Insert(AIndex: Integer;
  AElement: TAlgosimObject);
begin
  try
    FValue.Insert(GetPhysIndex0(AIndex, FValue.Dimension + 1), AElement.ToASC);
  finally
    AElement.Free;
  end;
end;

function TAlgosimComplexVector.IsNonZero(const Eps: Double): Boolean;
begin
  Result := not FValue.IsZeroVector(Eps);
end;

function TAlgosimComplexVector.IsZero(const Eps: Double): Boolean;
begin
  Result := FValue.IsZeroVector(Eps);
end;

function TAlgosimComplexVector.Last(N: Integer): TAlgosimObject;
begin
  if N > 0 then
    Result := ASO(Value.Subvector(Value.Dimension - N, Value.Dimension - 1))
  else
    Result := TAlgosimComplexVector.Create;
end;

function TAlgosimComplexVector.Norm(AType: TNormType; AParam: Integer;
  AYieldProc: TObjProc): TASR;
begin
  case AType of
    ntEuclidean, ntFrobenius:
      Result := FValue.Norm;
    ntPNorm:
      Result := FValue.pNorm(AParam);
    ntMaxNorm, ntMaxRowSum:
      Result := FValue.MaxNorm;
    ntSumNorm, ntMaxColSum:
      Result := FValue.SumNorm;
    ntKNorm:
      Result := FValue.kNorm(AParam);
  else
    Result := inherited;
  end;
end;

function TAlgosimComplexVector.Normalized: TAlgosimVector;
begin
  Result := ASO(FValue.Normalized);
end;

function TAlgosimComplexVector.NormalizedIfNonzero: TAlgosimVector;
begin
  Result := ASO(FValue.NormalizedIfNonzero);
end;

function TAlgosimComplexVector.NormSquared: TAlgosimRealNumber;
begin
  Result := ASO(FValue.NormSqr);
end;

function TAlgosimComplexVector.N_GeometricMean: TAlgosimNumber;
begin
  Result := ASO(ASNum.GeometricMean(FValue));
end;

function TAlgosimComplexVector.N_HarmonicMean: TAlgosimNumber;
begin
  Result := ASO(ASNum.HarmonicMean(FValue));
end;

function TAlgosimComplexVector.N_product: TAlgosimNumericEntity;
begin
  Result := ASO(ASNum.product(FValue));
end;

function TAlgosimComplexVector.N_sum: TAlgosimNumericEntity;
begin
  Result := ASO(ASNum.sum(FValue));
end;

function TAlgosimComplexVector.Part(A, B: Integer): TAlgosimObject;
begin
  Result := ASO(Value.Subvector(A - 1, B - 1));
end;

function TAlgosimComplexVector.Part(
  const AIndices: array of Integer): TAlgosimObject;
begin
  Result := ASO(Value.Subvector(TranslatedIntSequence(AIndices)));
end;

function TAlgosimComplexVector.Part(
  const ARanges: array of TRange): TAlgosimObject;
begin
  Result := Part(ParseRangeSeq(ARanges, Value.Dimension));
end;

function TAlgosimComplexVector.Part(A: Integer): TAlgosimObject;
begin
  Result := ASO(Value.Subvector(A - 1, Value.Dimension - 1));
end;

function TAlgosimComplexVector.RealPart: TAlgosimNumericEntity;
begin
  Result := ASO(FValue.RealPart);
end;

procedure TAlgosimComplexVector.Reverse;
begin
  FValue.Reverse;
end;

function TAlgosimComplexVector.RotLeft(N: Integer): TAlgosimObject;
begin
  Result := ASO(Value shl N);
end;

function TAlgosimComplexVector.RotRight(N: Integer): TAlgosimObject;
begin
  Result := ASO(Value shr N);
end;

function TAlgosimComplexVector.ScaledBy(
  const AFactor: TASR): TAlgosimNumericEntity;
begin
  Result := ASO(FValue * AFactor);
end;

procedure TAlgosimComplexVector.SetDimension(const Value: Integer);
begin
  FValue.Dimension := Value;
end;

procedure TAlgosimComplexVector.SetValue(Index: Integer;
  AValue: TAlgosimObject);
var
  z: TASC;
  PhysIndex: Integer;
begin

  try

    PhysIndex := GetPhysIndex0(Index, Dimension);

    if (AValue is TAlgosimNumber) and AValue.TryToASC(z) then
      FValue[PhysIndex] := z
    else
      raise EAlgosimObjectException.CreateFmt(SSetCVectSubscriptNoNum, [AValue.TypeName])

  finally
    AValue.Free;
  end;

end;

procedure TAlgosimComplexVector.SetValueFromPoint(const APoint: TPoint;
  AValue: TAlgosimObject);
var
  z: TASC;
begin
  try
    if (APoint.X = 1) and InRange(APoint.Y, 1, FValue.Dimension) then
      if AValue.TryToASC(z) then
        FValue[APoint.Y - 1] := z
      else
        raise EAlgosimObjectException.CreateFmt(SSetCMatSubscriptNoNum, [AValue.TypeName])
    else
      raise EAlgosimObjectException.CreateFmt(SIndexOutOfBounds2D, [APoint.Y, APoint.X]);
  finally
    AValue.Free;
  end;
end;

procedure TAlgosimComplexVector.Shuffle;
begin
  FValue.Shuffle;
end;

procedure TAlgosimComplexVector.Sort(AComparer: IComparer<TAlgosimObject>);
var
  LeftObj, RightObj: TAlgosimComplexNumber;
begin
  LeftObj := TAlgosimComplexNumber.Create;
  try
    RightObj := TAlgosimComplexNumber.Create;
    try
      FValue.Sort(TComparer<TASC>.Construct(
        function(const Left, Right: TASC): Integer
        begin
          LeftObj.Value := Left;
          RightObj.Value := Right;
          Result := AComparer.Compare(LeftObj, RightObj);
        end
      ));
    finally
      RightObj.Free;
    end;
  finally
    LeftObj.Free;
  end;
end;

procedure TAlgosimComplexVector.Sort(AComparer: IComparer<TASR>);
begin
  FValue := TComplexVector(ToRealVector.Sort(AComparer));
end;

procedure TAlgosimComplexVector.Sort(AComparer: IComparer<TASC>);
begin
  FValue.Sort(AComparer);
end;

function TAlgosimComplexVector.Square: TAlgosimNumericEntity;
begin
  Result := ASO(Value * Value);
end;

procedure TAlgosimComplexVector.Swap(Index1, Index2: Integer);
var
  PhysIdx1, PhysIdx2: Integer;
begin

  PhysIdx1 := GetPhysIndex0(Index1, FValue.Dimension);
  PhysIdx2 := GetPhysIndex0(Index2, FValue.Dimension);

  if PhysIdx1 = PhysIdx2 then
    Exit;

  FValue.Swap(PhysIdx1, PhysIdx2);

end;

function TAlgosimComplexVector.ToComplexMatrix: TComplexMatrix;
begin
  Result := TComplexMatrix.Create(Value);
end;

function TAlgosimComplexVector.ToComplexVector: TComplexVector;
begin
  Result := Value.Clone;
end;

function TAlgosimComplexVector.AsComplexVector: TComplexVector;
begin
  Result := Value;
end;

procedure TAlgosimComplexVector.ExtendWith(AElement: TAlgosimObject);
begin
  try
    FValue.ExtendWith(AElement.AsComplexVector);
  finally
    AElement.Free;
  end;
end;

function TAlgosimComplexVector.ToRealMatrix: TRealMatrix;
var
  i: Integer;
begin
  Result := TRealMatrix.CreateUninitialized(TMatrixSize.Create(Self.Dimension, 1));
  for i := 0 to Dimension - 1 do
    if Value[i].IsReal then
      Result.Data[i] := Value[i].Re
    else
      raise EAlgosimObjectException.CreateFmt(SComplexVectToRealMatrix, [Value[i].pstr]);
end;

function TAlgosimComplexVector.ToRealVector: TRealVector;
var
  i: Integer;
begin
  Result.Dimension := Self.Dimension;
  for i := 0 to Dimension - 1 do
    if Value[i].IsReal then
      Result[i] := Value[i].Re
    else
      raise EAlgosimObjectException.CreateFmt(SComplexVectToReal, [Value[i].pstr]);
end;

function TAlgosimComplexVector.Transpose: TAlgosimMatrix;
begin
  Result := ASO(TComplexMatrix.CreateFromRows([FValue]));
end;

procedure TAlgosimComplexVector.Truncate(ANewLength: Integer);
begin

  if ANewLength < 0 then
    raise EArrayException.Create(SNewLengthMustBeNonNegative);

  if ANewLength >= FValue.Dimension then
    Exit;

  FValue.Dimension := ANewLength;

end;

function TAlgosimComplexVector.UnaryMinus: TAlgosimObject;
begin
  Result := ASO(-FValue);
end;

procedure TAlgosimComplexVector.Remove(const AIndices: array of integer);
begin
  FValue.Remove(TranslatedIntSequence(AIndices));
end;

function TAlgosimComplexVector.RemoveAdjacentDuplicates: TAlgosimObject;
begin
  Result := ASO(FValue.UniqueAdj);
end;

function TAlgosimComplexVector.RemoveAdjacentDuplicatesEps(
  const Epsilon: TASR): TAlgosimObject;
begin
  Result := ASO(FValue.UniqueAdjEps(Epsilon));
end;

function TAlgosimComplexVector.RemoveDuplicates: TAlgosimObject;
begin
  Result := ASO(FValue.Unique);
end;

function TAlgosimComplexVector.RemoveDuplicatesEps(
  const Epsilon: TASR): TAlgosimObject;
begin
  Result := ASO(FValue.UniqueEps(Epsilon));
end;

procedure TAlgosimComplexVector.RemoveFirst(N: Integer);
begin
  FValue.RemoveFirst(N);
end;

procedure TAlgosimComplexVector.Replace(APredicate: TASOPredicate;
  ANewValue: TAlgosimObject; ALevel: Integer);
var
  i: Integer;
  Obj: TAlgosimComplexNumber;
  z: TASC;
begin

  if ALevel > 1 then
    Exit;

  z := ANewValue.ToASC;

  Obj := TAlgosimComplexNumber.Create;
  try
    for i := 0 to FValue.Dimension - 1 do
    begin
      Obj.FValue := FValue[i];
      if not Assigned(APredicate) or APredicate(Obj) then
        FValue[i] := z;
    end;
  finally
    Obj.Free;
  end;

end;

{ TAlgosimMatrix }

class function TAlgosimMatrix.Add(Left, Right: TAlgosimMatrix): TAlgosimMatrix;
begin
  if Left.IsComplex or Right.IsComplex then
    Result := ASO(Left.AsComplexMatrix + Right.AsComplexMatrix)
  else
    Result := ASO(Left.AsRealMatrix + Right.AsRealMatrix);
end;

class function TAlgosimMatrix.Add(Left: TAlgosimMatrix;
  Right: TAlgosimNumber): TAlgosimMatrix;
begin
  if Left.IsComplex or Right.IsComplex then
    Result := ASO(Left.AsComplexMatrix + Right.ToASC)
  else
    Result := ASO(Left.AsRealMatrix + Right.ToASR);
end;

class function TAlgosimMatrix.Divide(Left: TAlgosimMatrix;
  Right: TAlgosimNumber): TAlgosimMatrix;
begin
  if Left.IsComplex or Right.IsComplex then
    Result := ASO(Left.AsComplexMatrix / Right.ToASC)
  else
    Result := ASO(Left.AsRealMatrix / Right.ToASR);
end;

function TAlgosimMatrix.Equals(Obj: TObject): Boolean;
var
  Mat: TAlgosimMatrix absolute Obj;
begin
  Result := Obj is TAlgosimMatrix;
  if Result then
  begin
    if Self.IsComplex or Mat.IsComplex then
      Result := Self.AsComplexMatrix = Mat.AsComplexMatrix
    else
      Result := Self.AsRealMatrix = Mat.AsRealMatrix
  end;
end;

procedure TAlgosimMatrix.SetMaxLen(AValue: Integer);
begin
  FMaxLen := AValue;
end;

procedure TAlgosimMatrix.SetPlanarExtent(const Value: TSize);
begin
  Dimension := Value;
end;

class function TAlgosimMatrix.SortClass: TSortClass;
begin
  Result := SORTCLASS_MATRIX;
end;

class function TAlgosimMatrix.SortClassCompare(const Left,
  Right: TAlgosimObject): Integer;
var
  L, R: TAlgosimMatrix;
  RL, RR: TRealMatrix;
  CL, CR: TComplexMatrix;
  i: Integer;
begin
  L := Left as TAlgosimMatrix;
  R := Right as TAlgosimMatrix;
  Result := CompareValue(L.Dimension.Rows, R.Dimension.Rows);
  if Result = 0 then
    Result := CompareValue(L.Dimension.Cols, R.Dimension.Cols);
  if Result = 0 then
  begin
    if L.IsComplex or R.IsComplex then
    begin
      CL := L.AsComplexMatrix;
      CR := R.AsComplexMatrix;
      for i := 0 to CL.Size.ElementCount - 1 do
      begin
        Result := CompareValue(CL.Data[i], CR.Data[i]);
        if Result <> EqualsValue then
          Exit;
      end;
    end
    else
    begin
      RL := L.AsRealMatrix;
      RR := R.AsRealMatrix;
      for i := 0 to RL.Size.ElementCount - 1 do
      begin
        Result := CompareValue(RL.Data[i], RR.Data[i]);
        if Result <> EqualsValue then
          Exit;
      end;
    end;
  end;
end;

class function TAlgosimMatrix.SortClassSameObject(const Left,
  Right: TAlgosimObject; const AEpsilon: TASR): Boolean;
var
  L, R: TAlgosimMatrix;
  RL, RR: TRealMatrix;
  CL, CR: TComplexMatrix;
  i: Integer;
begin
  L := Left as TAlgosimMatrix;
  R := Right as TAlgosimMatrix;
  Result := L.Dimension = R.Dimension;
  if Result then
  begin
    if L.IsComplex or R.IsComplex then
    begin
      CL := L.AsComplexMatrix;
      CR := R.AsComplexMatrix;
      for i := 0 to CL.Size.ElementCount - 1 do
        if not CSameValue(CL.Data[i], CR.Data[i], AEpsilon) then
          Exit(False);
    end
    else
    begin
      RL := L.AsRealMatrix;
      RR := R.AsRealMatrix;
      for i := 0 to RL.Size.ElementCount - 1 do
        if not SameValue(RL.Data[i], RR.Data[i], AEpsilon) then
          Exit(False);
    end;
  end;
end;

class function TAlgosimMatrix.Subtract(Left: TAlgosimMatrix;
  Right: TAlgosimNumber): TAlgosimMatrix;
begin
  if Left.IsComplex or Right.IsComplex then
    Result := ASO(Left.AsComplexMatrix - Right.ToASC)
  else
    Result := ASO(Left.AsRealMatrix - Right.ToASR);
end;

function TAlgosimMatrix.GetAsMultilineText(const AOptions: TFormatOptions): string;
var
  EffectiveMaxLen: Integer;
  s: TMatrixSize;
  strs: array of array of string;
  i, j: Integer;
  MaxWidth: Integer;

  function GetRow(Index: Integer): string;
  var
    k: Integer;
  begin

    if s.Cols = 0 then
      Exit('');

    if Index = -1 then
    begin
      Result := PadStr(EllipsisSymbol_VERTICAL_ELLIPSIS, MaxWidth);
      for k := 1 to High(strs[0]) do
        Result := Result + #32#32 + PadStr(EllipsisSymbol_VERTICAL_ELLIPSIS, MaxWidth)
    end
    else
    begin
      Result := PadStr(strs[Index, 0], MaxWidth, taRightJustify);
      for k := 1 to High(strs[Index]) do
        Result := Result + #32#32 + PadStr(strs[Index, k], MaxWidth, taRightJustify);
    end;

    if s.Cols > EffectiveMaxLen then
      if Index = -1 then
        Result := Result + #32#32 + PadStr(EllipsisSymbol_DOWN_RIGHT_DIAGONAL_ELLIPSIS, MaxWidth)
      else
        Result := Result + #32#32 + PadStr(EllipsisSymbol_MIDLINE_HORIZONTAL_ELLIPSIS, MaxWidth);

  end;

begin

  if FMaxLen > 0 then
    EffectiveMaxLen := FMaxLen
  else
    EffectiveMaxLen := AOptions.Vectors.MaxLen;

  s := GetDimension;

  if (s.Rows < 1) or (EffectiveMaxLen < 1) then
    Exit(GetAsSingleLineText(AOptions));

  SetLength(strs, Math.Min(EffectiveMaxLen, s.Rows), Math.Min(EffectiveMaxLen, s.Cols));

  MaxWidth := 0;
  for i := 0 to Math.Min(EffectiveMaxLen, s.Rows) - 1 do
    for j := 0 to Math.Min(EffectiveMaxLen, s.Cols) - 1 do
    begin
      strs[i, j] := GetElementAsStringFmt(i, j, AOptions);
      if strs[i, j].Length > MaxWidth then
        MaxWidth := strs[i, j].Length;
    end;

  if s.Rows = 1 then
    Result := '(' + GetRow(0) + ')'
  else
    Result := ParenLeftUpper + GetRow(0) + ParenRightUpper;

  for i := 1 to High(strs) - IfThen(s.Rows <= EffectiveMaxLen, 1) do
    Result := Result + sLineBreak + ParenLeftExtension + GetRow(i) + ParenRightExtension;

  if s.Rows > 1 then
    if s.Rows > EffectiveMaxLen then
      Result := Result + sLineBreak + ParenLeftLower + GetRow(-1) + ParenRightLower
    else
      Result := Result + sLineBreak + ParenLeftLower + GetRow(High(strs)) + ParenRightLower;

end;

function TAlgosimMatrix.GetAsSingleLineText(const AOptions: TFormatOptions): string;
var
  EffectiveMaxLen: Integer;
  s: TMatrixSize;
  i: Integer;
  j: Integer;
begin

  if FMaxLen > 0 then
    EffectiveMaxLen := FMaxLen
  else
    EffectiveMaxLen := AOptions.Vectors.MaxLen;

  s := GetDimension;

  Result := AOptions.Vectors.LeftDelim;

  for i := 0 to Math.Min(EffectiveMaxLen, s.Rows) - 1 do
  begin
    if i > 0 then
      Result := Result + AOptions.Vectors.ComponentSep;

    Result := Result + AOptions.Vectors.LeftDelim;

    for j := 0 to Math.Min(EffectiveMaxLen, s.Cols) - 1 do
    begin
      if j > 0 then
        Result := Result + AOptions.Vectors.ComponentSep;

      Result := Result + GetElementAsStringFmt(i, j, AOptions);
    end;

    if s.Cols > EffectiveMaxLen then
      Result := Result + AOptions.Vectors.ComponentSep + EllipsisSymbol_HORIZONTAL_ELLIPSIS;

    Result := Result + AOptions.Vectors.RightDelim;
  end;

  if s.Rows > EffectiveMaxLen then
    Result := Result + AOptions.Vectors.ComponentSep + EllipsisSymbol_HORIZONTAL_ELLIPSIS;

  Result := Result + AOptions.Vectors.RightDelim;

end;

function TAlgosimMatrix.GetMaxLen: Integer;
begin
  Result := FMaxLen;
end;

function TAlgosimMatrix.GetValueCount: Integer;
begin
  Result := Dimension.ElementCount;
end;

class function TAlgosimMatrix.Multiply(Left: TAlgosimMatrix;
  Right: TAlgosimVector): TAlgosimVector;
begin
  if Left.IsComplex or Right.IsComplex then
    Result := ASO((Left.AsComplexMatrix * Right.AsComplexVector).AsVector)
  else
    Result := ASO((Left.AsRealMatrix * Right.AsRealVector).AsVector);
end;

class function TAlgosimMatrix.Multiply(Left: TAlgosimMatrix;
  Right: TAlgosimNumber): TAlgosimMatrix;
begin
  if Left.IsComplex or Right.IsComplex then
    Result := ASO(Left.AsComplexMatrix * Right.ToASC)
  else
    Result := ASO(Left.AsRealMatrix * Right.ToASR);
end;

class function TAlgosimMatrix.Multiply(Left,
  Right: TAlgosimMatrix): TAlgosimMatrix;
begin
  if Left.IsComplex or Right.IsComplex then
    Result := ASO(Left.AsComplexMatrix * Right.AsComplexMatrix)
  else
    Result := ASO(Left.AsRealMatrix * Right.AsRealMatrix);
end;

function TAlgosimMatrix.GetPlanarExtent: TSize;
begin
  Result := Dimension;
end;

class function TAlgosimMatrix.Subtract(Left,
  Right: TAlgosimMatrix): TAlgosimMatrix;
begin
  if Left.IsComplex or Right.IsComplex then
    Result := ASO(Left.AsComplexMatrix - Right.AsComplexMatrix)
  else
    Result := ASO(Left.AsRealMatrix - Right.AsRealMatrix);
end;

function TAlgosimMatrix.ToSpeech: string;
var
  s: TMatrixSize;
  i: Integer;
  j: Integer;
begin
  s := GetDimension;
  Result := Format('Begin %d by %d matrix.', [s.Rows, s.Cols]) + sLineBreak;
  for i := 0 to s.Rows - 1 do
  begin
    Result := Result + 'Row ' + (i + 1).ToString + ': ';
    if s.Cols >= 1 then
      Result := Result + GetElementAsStringFmt(i, 0, DefaultFormatOptions);
    for j := 1 to s.Cols - 1 do
      Result := Result + '. Next: ' + GetElementAsStringFmt(i, j, DefaultFormatOptions);
    Result := Result + '.' + sLineBreak;
  end;
  Result := Result + 'End matrix.';
end;

function TAlgosimMatrix.ToString: string;
var
  s: TMatrixSize;
  i: Integer;
  j: Integer;
begin
  Result := '';
  s := GetDimension;
  for i := 0 to s.Rows - 1 do
  begin
    if s.Cols >= 1 then
      Result := Result + GetElementAsStringFmt(i, 0, ExchangeFormOptions);
    for j := 1 to s.Cols - 1 do
      Result := Result + Tabulator + GetElementAsStringFmt(i, j, ExchangeFormOptions);
    if i < s.Rows - 1 then
      Result := Result + sLineBreak;
  end;
end;

function TAlgosimMatrix.WithSpecificValues(
  AValues: TAlgosimArray): TAlgosimObject;
begin

  if Dimension.ElementCount <> AValues.ElementCount then
    raise EAlgosimObjectException.CreateFmt(SSpecValWrongLength2D,
      [Dimension.Rows, Dimension.Cols, AValues.ElementCount]);

  if IsComplex or AValues.IsComplex then
    Result := TAlgosimComplexMatrix.CreateWithValue(
      TComplexMatrix.Create(AValues.ToComplexVector.Data, Dimension.Cols)
    )
  else
    Result := TAlgosimRealMatrix.CreateWithValue(
      TRealMatrix.Create(AValues.ToRealVector.Data, Dimension.Cols)
    )

end;

{ TAlgosimRealMatrix }

procedure TAlgosimRealMatrix.Defuzz(const Eps: Double);
begin
  Value.Defuzz(Eps);
end;

function TAlgosimRealMatrix.DeletedAbsoluteRowSum(Index: Integer): TASR;
begin
  if not InRange(Index, 1, Value.Size.Rows) then
    raise EAlgosimObjectException.CreateFmt(SIndexOutOfBounds, [Index]);
  Result := Value.DeletedAbsoluteRowSum(Index - 1);
end;

function TAlgosimRealMatrix.Determinant: TAlgosimNumber;
begin
  Result := ASO(Value.Determinant);
end;

function TAlgosimRealMatrix.Abs: TAlgosimNumericEntity;
begin
  Result := ASO(FValue.Abs);
end;

function TAlgosimRealMatrix.Accumulate(AInitialValue: TAlgosimObject;
  AFunction: TASOAccumulator): TAlgosimObject;
var
  i: Integer;
  num: TAlgosimRealNumber;
begin
  num := TAlgosimRealNumber.Create;
  try
    Result := AInitialValue.Clone;
    try
      for i := 0 to FValue.Size.ElementCount - 1 do
      begin
        num.FValue := FValue.Data[i];
        TObjReplacer<TAlgosimObject>.Replace(Result, AFunction(Result, num));
      end;
    except
      Result.Free;
      raise;
    end;
  finally
    num.Free;
  end;
end;

function TAlgosimRealMatrix.AccumulateStepsList(AInitialValue: TAlgosimObject;
  AFunction: TASOAccumulator): TAlgosimArray;
var
  i: Integer;
  num: TAlgosimRealNumber;
begin
  num := TAlgosimRealNumber.Create;
  try
    Result := TAlgosimArray.Create;
    try
      Result.Capacity := FValue.Size.ElementCount;
      for i := 0 to FValue.Size.ElementCount - 1 do
      begin
        num.FValue := FValue.Data[i];
        AInitialValue := AFunction(AInitialValue, num);
        Result.Add(AInitialValue);
      end;
    except
      Result.Free;
      raise;
    end;
  finally
    num.Free;
  end;
end;

procedure TAlgosimRealMatrix.AddNumbersToArray(AArray: TAlgosimArray; ALevel: Integer);
var
  i: Integer;
begin
  for i := 0 to High(FValue.Data) do
    AArray.Add(ASO(FValue.Data[i]));
end;

function TAlgosimRealMatrix.AdjugateMatrix: TAlgosimMatrix;
begin
  result := ASO(Value.AdjugateMatrix);
end;

function TAlgosimRealMatrix.Antidiagonal: TAlgosimVector;
begin
  result := ASO(Value.AntiDiagonal);
end;

procedure TAlgosimRealMatrix.Apply(AFunction: TASOFunction;
  ACondition: TASOPredicate; ALevel: Integer);
var
  i: Integer;
  num: TAlgosimRealNumber;
  res: TAlgosimObject;
  x: TASR;
begin

  if ALevel > 1 then
    Exit;

  num := TAlgosimRealNumber.Create;
  try
    for i := 0 to FValue.Size.ElementCount - 1 do
    begin
      num.Value := FValue.Data[i];
      if not Assigned(ACondition) or ACondition(num) then
      begin
        res := AFunction(num);
        try
          if res.TryToASR(x) then
            FValue.Data[i] := x
          else
            raise EAlgosimObjectException.Create(SRealNumberApply);
        finally
          res.Free;
        end;
      end;
    end;
    FValue := FValue;
  finally
    num.Free;
  end;

end;

function TAlgosimRealMatrix.ColumnSpaceBasis: TAlgosimMatrix;
begin
  Result := ASO(Value.ColumnSpaceBasis);
end;

function TAlgosimRealMatrix.ConditionNumber(p: Integer): TASR;
begin
  Result := Value.ConditionNumber(p);
end;

function TAlgosimRealMatrix.ConjugateTranspose: TAlgosimMatrix;
begin
  Result := ASO(FValue.Transpose);
end;

function TAlgosimRealMatrix.Count(APredicate: TASOPredicate): Integer;
var
  i: Integer;
  num: TAlgosimRealNumber;
begin
  num := TAlgosimRealNumber.Create;
  try
    Result := 0;
    for i := 0 to FValue.Size.ElementCount - 1 do
    begin
      num.Value := FValue.Data[i];
      if APredicate(num) then
        Inc(Result);
    end;
  finally
    num.Free;
  end;
end;

constructor TAlgosimRealMatrix.Create(AObject: TAlgosimObject);
begin
  CreateWithValue(AObject.ToRealMatrix);
end;

constructor TAlgosimRealMatrix.CreateWithValue(const AValue: TRealMatrix);
begin
  Create;
  FValue := AValue;
end;

function TAlgosimRealMatrix.Eigenvalues: TComplexVector;
begin
  Result := Value.Eigenvalues;
end;

function TAlgosimRealMatrix.Eigenvectors: TAlgosimArray;
var
  vals: TRealVector;
  vects: TRealMatrix;
  cvals: TComplexVector;
  cvects: TComplexMatrix;
begin
  if Value.eigenvectors(vals, vects, False) then
    Result := TAlgosimAssignmentList.CreateWithValue([ASO(vals), ASO(vects)])
  else if TComplexMatrix(Value).eigenvectors(cvals, cvects, False) then
    Result := TAlgosimAssignmentList.CreateWithValue([ASO(cvals), ASO(cvects)])
  else
    raise EMathException.Create('Couldn''t compute eigenvectors.');
end;

function TAlgosimRealMatrix.Exists(APredicate: TASOPredicate): Boolean;
var
  i: Integer;
  num: TAlgosimRealNumber;
begin
  num := TAlgosimRealNumber.Create;
  try
    for i := 0 to FValue.Size.ElementCount - 1 do
    begin
      num.Value := FValue.Data[i];
      if APredicate(num) then
        Exit(True);
    end;
    Result := False;
  finally
    num.Free;
  end;
end;

function TAlgosimRealMatrix.ForAll(APredicate: TASOPredicate): Boolean;
var
  i: Integer;
  num: TAlgosimRealNumber;
begin
  num := TAlgosimRealNumber.Create;
  try
    for i := 0 to FValue.Size.ElementCount - 1 do
    begin
      num.Value := FValue.Data[i];
      if not APredicate(num) then
        Exit(False);
    end;
    Result := True;
  finally
    num.Free;
  end;
end;

function TAlgosimRealMatrix.Frequencies: TAlgosimArray;
var
  Dict: TDictionary<TASR, TAlgosimInteger>;
  C: TAlgosimInteger;
  i: Integer;
begin
  Result := TAlgosimArray.Create;
  try
    Result.Capacity := FValue.Size.ElementCount;
    Dict := TDictionary<TASR, TAlgosimInteger>.Create;
    try
      for i := 0 to FValue.Size.ElementCount - 1 do
      begin
        if Dict.TryGetValue(FValue.Data[i], C) then
          Inc(C.FValue)
        else
        begin
          C := ASOInt(1);
          Result.AddElement(
            ASO(
              [
                ASO(FValue.Data[i]),
                C
              ]
            )
          );
          Dict.Add(FValue.Data[i], C);
        end;
      end;
    finally
      Dict.Free;
    end;
    Result.TrimExcess;
  except
    Result.Free;
    raise;
  end;
end;

function TAlgosimRealMatrix.FrequenciesEps(const Epsilon: TASR): TAlgosimArray;
var
  i: Integer;
  j: Integer;
label
  NextValue;
begin
  Result := TAlgosimArray.Create;
  try
    Result.Capacity := FValue.Size.ElementCount;
    for i := 0 to FValue.Size.ElementCount - 1 do
    begin
      for j := 1 to Result.ElementCount do
        if SameValue(Result.Elements[j].Elements[1].ToASR, FValue.Data[i], Epsilon) then
        begin
          Inc((Result.Elements[j].Elements[2] as TAlgosimInteger).FValue);
          goto NextValue;
        end;
      Result.AddElement(
        ASO(
          [
            ASO(FValue.Data[i]),
            ASOInt(1)
          ]
        )
      );
      NextValue:
    end;
    Result.TrimExcess;
  except
    Result.Free;
    raise;
  end;
end;

function TAlgosimRealMatrix.GetDimension: TMatrixSize;
begin
  Result := FValue.Size;
end;

function TAlgosimRealMatrix.GetElementAsString(Y, X: Integer): string;
begin
  Result := RealToStr(FValue[Y, X], DefaultFormatOptions);
end;

function TAlgosimRealMatrix.GetElementAsStringFmt(Y, X: Integer;
  const AOptions: TFormatOptions): string;
begin
  Result := RealToStr(FValue[Y, X], ApplyOptions(AOptions));
end;

function TAlgosimRealMatrix.GetMemorySize: UInt64;
begin
  Result := FValue.Size.ElementCount * sizeof(TASR);
end;

procedure TAlgosimRealMatrix.Sort;
begin
  FValue.Sort(TASRComparer.StandardOrder);
end;

procedure TAlgosimRealMatrix.Sort(AComparer: IComparer<TAlgosimObject>);
var
  LeftObj, RightObj: TAlgosimRealNumber;
begin
  LeftObj := TAlgosimRealNumber.Create;
  try
    RightObj := TAlgosimRealNumber.Create;
    try
      FValue.Sort(TComparer<TASR>.Construct(
        function(const Left, Right: TASR): Integer
        begin
          LeftObj.Value := Left;
          RightObj.Value := Right;
          Result := AComparer.Compare(LeftObj, RightObj);
        end
      ));
    finally
      RightObj.Free;
    end;
  finally
    LeftObj.Free;
  end;
end;

procedure TAlgosimRealMatrix.Sort(AComparer: IComparer<TASR>);
begin
  FValue.Sort(AComparer);
end;

procedure TAlgosimRealMatrix.Sort(AComparer: IComparer<TASC>);
begin
  FValue.Sort(TComparer<TASR>.Construct(
    function(const Left, Right: TASR): Integer
    begin
      Result := AComparer.Compare(Left, Right)
    end
  ));
end;

function TAlgosimRealMatrix.SortClassGetHashCode: Integer;
begin
  if Length(FValue.Data) > 0 then
    Result := THashBobJenkins.GetHashValue(FValue.Data[0], Length(FValue.Data) * sizeof(FValue.Data[0]))
  else
    Result := 0;
end;

function TAlgosimRealMatrix.GetBinaryData(var Buf: PByte;
  var Len: UInt64): Boolean;
begin
  Buf := Pointer(FValue.Data);
  Len := Dimension.ElementCount * sizeof(TASR);
  Result := True;
end;

function TAlgosimRealMatrix.GetValue(Index: Integer): TAlgosimObject;
var
  PhysIndex: Integer;
begin
  PhysIndex := GetPhysIndex0(Index, Dimension.ElementCount);
  Result := ASO(FValue.Data[PhysIndex]);
end;

function TAlgosimRealMatrix.GetValueFromPoint(const APoint: TPoint): TAlgosimObject;
var
  PhysIndex: TPoint;
begin
  PhysIndex := GetPhysIndex2D0(APoint, Dimension);
  Result := ASO(FValue[PhysIndex.Y, PhysIndex.X]);
end;

function TAlgosimRealMatrix.GramSchmidt: TAlgosimMatrix;
begin
  Result := ASO(Value.GramSchmidt);
end;

function TAlgosimRealMatrix.HermitianSquare: TAlgosimMatrix;
begin
  Result := ASO(Value.HermitianSquare);
end;

function TAlgosimRealMatrix.ImaginaryPart: TAlgosimNumericEntity;
begin
  Result := ASO(ZeroMatrix(FValue.Size))
end;

function TAlgosimRealMatrix.IndexOfValue(AObj: TAlgosimObject): TAlgosimObject;
var
  x: TASR;
  i: Integer;
begin

  if not (AObj is TAlgosimNumber) then
    Exit(ASO(null));

  if not AObj.TryToASR(x) then
    Exit(ASO(null));

  for i := 0 to FValue.Size.ElementCount - 1 do
    if FValue.Data[i] = x then
      Exit(TAlgosimArray.CreateWithValue([i mod FValue.Size.Cols + 1, i div FValue.Size.Cols + 1]));

  Result := ASO(null);

end;

function TAlgosimRealMatrix.IndexOfValueEps(AObj: TAlgosimObject;
  const AEpsilon: TASR): TAlgosimObject;
var
  x: TASR;
  i: Integer;
begin

  if not (AObj is TAlgosimNumber) then
    Exit(ASO(null));

  if not AObj.TryToASR(x) then
    Exit(ASO(null));

  for i := 0 to FValue.Size.ElementCount - 1 do
    if SameValue(FValue.Data[i], x, AEpsilon) then
      Exit(TAlgosimArray.CreateWithValue([i mod FValue.Size.Cols + 1, i div FValue.Size.Cols + 1]));

  Result := ASO(null);

end;

function TAlgosimRealMatrix.IndicesOf(APredicate: TASOPredicate): TAlgosimArray;
var
  r: TAlgosimRealNumber;
  i: Integer;
begin

  Result := TAlgosimArray.Create;
  try

    Result.Capacity := CapCap(FValue.Size.ElementCount);

    r := TAlgosimRealNumber.Create;
    try
      for i := 0 to FValue.Size.ElementCount - 1 do
      begin
        r.Value := FValue.Data[i];
        if APredicate(r) then
          Result.Add(TAlgosimArray.CreateWithValue([i div FValue.Size.Cols + 1, i mod FValue.Size.Cols + 1]));
      end;
    finally
      r.Free;
    end;

    Result.TrimExcess;

  except
    Result.Free;
    raise;
  end;

end;

function TAlgosimRealMatrix.IndicesOfValue(AObj: TAlgosimObject): TAlgosimArray;
var
  x: TASR;
  i: Integer;
begin

  Result := TAlgosimArray.Create;
  try

    if not (AObj is TAlgosimNumber) then
      Exit;

    if not AObj.TryToASR(x) then
      Exit;

    Result.Capacity := CapCap(FValue.Size.ElementCount);

    for i := 0 to FValue.Size.ElementCount - 1 do
      if FValue.Data[i] = x then
        Result.Add(TAlgosimArray.CreateWithValue([i div FValue.Size.Cols + 1, i mod FValue.Size.Cols + 1]));

    Result.TrimExcess;

  except
    Result.Free;
    raise;
  end;

end;

function TAlgosimRealMatrix.IndicesOfValueEps(AObj: TAlgosimObject;
  const AEpsilon: TASR): TAlgosimArray;
var
  x: TASR;
  i: Integer;
begin

  Result := TAlgosimArray.Create;
  try

    if not (AObj is TAlgosimNumber) then
      Exit;

    if not AObj.TryToASR(x) then
      Exit;

    Result.Capacity := CapCap(FValue.Size.ElementCount);

    for i := 0 to FValue.Size.ElementCount - 1 do
      if SameValue(FValue.Data[i], x, AEpsilon) then
        Result.Add(TAlgosimArray.CreateWithValue([i div FValue.Size.Cols + 1, i mod FValue.Size.Cols + 1]));

    Result.TrimExcess;

  except
    Result.Free;
    raise;
  end;

end;

function TAlgosimRealMatrix.Inverse: TAlgosimNumericEntity;
begin
  Result := ASO(Value.Inverse);
end;

function TAlgosimRealMatrix.IsNegative(const Eps: Double): Boolean;
begin
  Result := FValue.IsNegative(Eps);
end;

function TAlgosimRealMatrix.IsNonNegative(const Eps: Double): Boolean;
begin
  Result := FValue.IsNonNegative(Eps);
end;

function TAlgosimRealMatrix.IsNonPositive(const Eps: Double): Boolean;
begin
  Result := FValue.IsNonPositive(Eps);
end;

function TAlgosimRealMatrix.IsNonZero(const Eps: Double): Boolean;
begin
  Result := not FValue.IsZeroMatrix(Eps);
end;

function TAlgosimRealMatrix.IsPositive(const Eps: Double): Boolean;
begin
  Result := FValue.IsPositive(Eps);
end;

function TAlgosimRealMatrix.IsSingular: Boolean;
begin
  Result := Value.IsSingular;
end;

function TAlgosimRealMatrix.IsZero(const Eps: Double): Boolean;
begin
  Result := FValue.IsZeroMatrix(Eps);
end;

function TAlgosimRealMatrix.MainDiagonal: TAlgosimVector;
begin
  Result := ASO(Value.MainDiagonal);
end;

function TAlgosimRealMatrix.Minor(Row, Col: Integer): TAlgosimNumber;
begin
  Result := ASO(Value.Minor(Row - 1, Col - 1));
end;

function TAlgosimRealMatrix.Cofactor(Row, Col: Integer): TAlgosimNumber;
begin
  Result := ASO(Value.Cofactor(Row - 1, Col - 1));
end;

function TAlgosimRealMatrix.CofactorMatrix: TAlgosimMatrix;
begin
  Result := ASO(Value.CofactorMatrix);
end;

function TAlgosimRealMatrix.Modulus: TAlgosimMatrix;
begin
  Result := ASO(Value.Modulus);
end;

function TAlgosimRealMatrix.Norm(AType: TNormType; AParam: Integer;
  AYieldProc: TObjProc): TASR;
begin
  case AType of
    ntEuclidean, ntFrobenius:
      Result := FValue.Norm;
    ntPNorm:
      Result := FValue.pNorm(AParam);
    ntMaxNorm:
      Result := FValue.MaxNorm;
    ntSumNorm:
      Result := FValue.SumNorm;
    ntKNorm:
      Result := FValue.kNorm(AParam);
    ntMaxColSum:
      Result := FValue.MaxColSumNorm;
    ntMaxRowSum:
      Result := FValue.MaxRowSumNorm;
    ntSpectral:
      Result := FValue.SpectralNorm;
  else
    Result := inherited;
  end;
end;

function TAlgosimRealMatrix.NormSquared: TAlgosimRealNumber;
begin
  Result := ASO(FValue.NormSqr);
end;

function TAlgosimRealMatrix.Nullity: Integer;
begin
  Result := Value.Nullity;
end;

function TAlgosimRealMatrix.NumZeroRows(const AEpsilon: TASR): Integer;
begin
  Result := Value.NumZeroRows(AEpsilon);
end;

function TAlgosimRealMatrix.N_GeometricMean: TAlgosimNumber;
begin
  Result := ASO(ASNum.GeometricMean(FValue));
end;

function TAlgosimRealMatrix.N_HarmonicMean: TAlgosimNumber;
begin
  Result := ASO(ASNum.HarmonicMean(FValue));
end;

function TAlgosimRealMatrix.N_max: TAlgosimNumber;
begin
  Result := ASO(ASNum.max(FValue));
end;

function TAlgosimRealMatrix.N_min: TAlgosimNumber;
begin
  Result := ASO(ASNum.min(FValue));
end;

function TAlgosimRealMatrix.N_product: TAlgosimNumericEntity;
begin
  Result := ASO(ASNum.product(FValue));
end;

function TAlgosimRealMatrix.N_sum: TAlgosimNumericEntity;
begin
  Result := ASO(ASNum.sum(FValue));
end;

function TAlgosimRealMatrix.NumTrailingZeroRows(const AEpsilon: TASR): Integer;
begin
  Result := Value.NumTrailingZeroRows(AEpsilon);
end;

function TAlgosimRealMatrix.Part2d(const AIndicesX,
  AIndicesY: array of Integer): TAlgosimObject;
begin
  Result := ASO(Value.Submatrix(
    TranslatedIntSequence(AIndicesY),
    TranslatedIntSequence(AIndicesX)));
end;

function TAlgosimRealMatrix.Power(AExp: Integer): TAlgosimMatrix;
begin
  Result := ASO(mpow(FValue, AExp));
end;

function TAlgosimRealMatrix.Rank: Integer;
begin
  Result := Value.Rank;
end;

function TAlgosimRealMatrix.RealPart: TAlgosimNumericEntity;
begin
  Result := TAlgosimNumericEntity(Self.Clone);
end;

function TAlgosimRealMatrix.RowEchelonForm: TAlgosimMatrix;
begin
  Result := ASO(Value.RowEchelonForm);
end;

function TAlgosimRealMatrix.ReducedRowEchelonForm: TAlgosimMatrix;
begin
  Result := ASO(Value.ReducedRowEchelonForm);
end;

procedure TAlgosimRealMatrix.Replace(APredicate: TASOPredicate;
  ANewValue: TAlgosimObject; ALevel: Integer);
var
  i: Integer;
  Obj: TAlgosimRealNumber;
  x: TASR;
begin

  if ALevel > 1 then
    Exit;

  x := ANewValue.ToASR;

  Obj := TAlgosimRealNumber.Create;
  try
    for i := 0 to FValue.Size.ElementCount - 1 do
    begin
      Obj.FValue := FValue.Data[i];
      if not Assigned(APredicate) or APredicate(Obj) then
        FValue.Data[i] := x;
    end;
  finally
    Obj.Free;
  end;

end;

procedure TAlgosimRealMatrix.Reverse;
begin
  FValue.Reverse;
end;

function TAlgosimRealMatrix.ScaledBy(
  const AFactor: TASR): TAlgosimNumericEntity;
begin
  Result := ASO(FValue * AFactor);
end;

procedure TAlgosimRealMatrix.SetDimension(const Value: TMatrixSize);
begin
  FValue.Size := Value;
end;

procedure TAlgosimRealMatrix.SetSubscript(ASubscript: TSubscript;
  AValue: TAlgosimObject);
begin
  case ASubscript.Kind of
    skRowIndex:
      try
        if InRange(ASubscript.Ordinal, 1, Value.Size.Rows) then
          Value.Rows[ASubscript.Ordinal - 1] := AValue.AsRealVector
        else
          raise EAlgosimObjectException.CreateFmt(SIndexOutOfBounds, [ASubscript.Ordinal]);
      finally
        AValue.Free;
      end;
    skColIndex:
      try
        if InRange(ASubscript.Ordinal, 1, Value.Size.Cols) then
          Value.Cols[ASubscript.Ordinal - 1] := AValue.AsRealVector
        else
          raise EAlgosimObjectException.CreateFmt(SIndexOutOfBounds, [ASubscript.Ordinal]);
      finally
        AValue.Free;
      end;
    skMainDiagonal:
      try
        Value.MainDiagonal := AValue.AsRealVector;
      finally
        AValue.Free;
      end;
    skSuperdiagonal:
      try
        Value.SuperDiagonal := AValue.AsRealVector;
      finally
        AValue.Free;
      end;
    skSubdiagonal:
      try
        Value.SubDiagonal := AValue.AsRealVector;
      finally
        AValue.Free;
      end;
    skAntidiagonal:
      try
        Value.AntiDiagonal := AValue.AsRealVector;
      finally
        AValue.Free;
      end;
  else
    inherited;
  end;
end;

procedure TAlgosimRealMatrix.SetValue(Index: Integer; AValue: TAlgosimObject);
var
  x: TASR;
  PhysIndex: Integer;
begin

  try

    PhysIndex := GetPhysIndex0(Index, Dimension.ElementCount);

    if (AValue is TAlgosimNumber) and AValue.TryToASR(x) then
      FValue.Data[PhysIndex] := x
    else
      raise EAlgosimObjectException.CreateFmt(SSetRMatSubscriptNoRNum, [AValue.TypeName])

  finally
    AValue.Free;
  end;

end;

procedure TAlgosimRealMatrix.SetValueFromPoint(const APoint: TPoint;
  AValue: TAlgosimObject);
var
  x: TASR;
  PhysIndex: TPoint;
begin

  try

    PhysIndex := GetPhysIndex2D0(APoint, Dimension);

    if AValue.TryToASR(x) then
      FValue[PhysIndex.Y, PhysIndex.X] := x
    else
      raise EAlgosimObjectException.CreateFmt(SSetRMatSubscriptNoRNum, [AValue.TypeName])

  finally
    AValue.Free;
  end;

end;

procedure TAlgosimRealMatrix.Shuffle;
begin
  FValue.Shuffle;
end;

function TAlgosimRealMatrix.SimilarHessenberg: TAlgosimMatrix;
begin
  Result := ASO(Value.SimilarHessenberg);
end;

function TAlgosimRealMatrix.SingularValues: TRealVector;
begin
  Result := Value.SingularValues;
end;

function TAlgosimRealMatrix.SpectralRadius: TASR;
begin
  Result := Value.SpectralRadius;
end;

function TAlgosimRealMatrix.Square: TAlgosimNumericEntity;
begin
  Result := ASO(Value.Sqr);
end;

function TAlgosimRealMatrix.Subdiagonal: TAlgosimVector;
begin
  Result := ASO(Value.SubDiagonal);
end;

function TAlgosimRealMatrix.Superdiagonal: TAlgosimVector;
begin
  Result := ASO(Value.SuperDiagonal);
end;

function TAlgosimRealMatrix.ToComplexMatrix: TComplexMatrix;
begin
  Result := Value;
end;

function TAlgosimRealMatrix.ToComplexVector: TComplexVector;
begin
  Result := TRealVector(Value.Data) {move semantics, will be copied during conversion};
end;

function TAlgosimRealMatrix.ToRealMatrix: TRealMatrix;
begin
  Result := Value.Clone;
end;

function TAlgosimRealMatrix.AsRealMatrix: TRealMatrix;
begin
  Result := Value;
end;

function TAlgosimRealMatrix.AsRealVector: TRealVector;
begin
  Result := TRealVector(Value.Data);
end;

function TAlgosimRealMatrix.ToRealVector: TRealVector;
begin
  Result := Value.AsVector;
end;

function TAlgosimRealMatrix.Trace: TAlgosimNumber;
begin
  Result := ASO(Value.Trace);
end;

function TAlgosimRealMatrix.Transpose: TAlgosimMatrix;
begin
  Result := ASO(FValue.Transpose);
end;

function TAlgosimRealMatrix.TryGetSubscriptedValue(ASubscript: TSubscript;
  out AValue: TAlgosimObject): Boolean;
begin
  case ASubscript.Kind of
    skRowIndex:
      begin
        if InRange(ASubscript.Ordinal, 1, Value.Size.Rows) then
          AValue := ASO(Value.Rows[ASubscript.Ordinal - 1])
        else
          raise EAlgosimObjectException.CreateFmt(SIndexOutOfBounds, [ASubscript.Ordinal]);
        Result := True;
      end;
    skColIndex:
      begin
        if InRange(ASubscript.Ordinal, 1, Value.Size.Cols) then
          AValue := ASO(Value.Cols[ASubscript.Ordinal - 1])
        else
          raise EAlgosimObjectException.CreateFmt(SIndexOutOfBounds, [ASubscript.Ordinal]);
        Result := True;
      end;
    skMainDiagonal:
      begin
        AValue := ASO(Value.MainDiagonal);
        Result := True;
      end;
    skSuperdiagonal:
      begin
        AValue := ASO(Value.SuperDiagonal);
        Result := True;
      end;
    skSubdiagonal:
      begin
        AValue := ASO(Value.SubDiagonal);
        Result := True;
      end;
    skAntidiagonal:
      begin
        AValue := ASO(Value.AntiDiagonal);
        Result := True;
      end;
  else
    Result := inherited;
  end;
end;

function TAlgosimRealMatrix.UnaryMinus: TAlgosimObject;
begin
  Result := ASO(-FValue);
end;

function TAlgosimRealMatrix.Vectorization: TAlgosimVector;
begin
  Result := ASO(Value.Vectorization);
end;

{ TAlgosimComplexMatrix }

procedure TAlgosimComplexMatrix.Defuzz(const Eps: Double);
begin
  Value.Defuzz(Eps);
end;

function TAlgosimComplexMatrix.DeletedAbsoluteRowSum(Index: Integer): TASR;
begin
  if not InRange(Index, 1, Value.Size.Rows) then
    raise EAlgosimObjectException.CreateFmt(SIndexOutOfBounds, [Index]);
  Result := Value.DeletedAbsoluteRowSum(Index - 1);
end;

function TAlgosimComplexMatrix.Determinant: TAlgosimNumber;
begin
  Result := ASO(Value.Determinant);
end;

function TAlgosimComplexMatrix.Abs: TAlgosimNumericEntity;
begin
  Result := ASO(FValue.Abs);
end;

function TAlgosimComplexMatrix.Accumulate(AInitialValue: TAlgosimObject;
  AFunction: TASOAccumulator): TAlgosimObject;
var
  i: Integer;
  num: TAlgosimComplexNumber;
begin
  num := TAlgosimComplexNumber.Create;
  try
    Result := AInitialValue.Clone;
    try
      for i := 0 to FValue.Size.ElementCount - 1 do
      begin
        num.FValue := FValue.Data[i];
        TObjReplacer<TAlgosimObject>.Replace(Result, AFunction(Result, num));
      end;
    except
      Result.Free;
      raise;
    end;
  finally
    num.Free;
  end;
end;

function TAlgosimComplexMatrix.AccumulateStepsList(AInitialValue: TAlgosimObject;
  AFunction: TASOAccumulator): TAlgosimArray;
var
  i: Integer;
  num: TAlgosimComplexNumber;
begin
  num := TAlgosimComplexNumber.Create;
  try
    Result := TAlgosimArray.Create;
    try
      Result.Capacity := FValue.Size.ElementCount;
      for i := 0 to FValue.Size.ElementCount - 1 do
      begin
        num.FValue := FValue.Data[i];
        AInitialValue := AFunction(AInitialValue, num);
        Result.Add(AInitialValue);
      end;
    except
      Result.Free;
      raise;
    end;
  finally
    num.Free;
  end;
end;

procedure TAlgosimComplexMatrix.AddNumbersToArray(AArray: TAlgosimArray; ALevel: Integer);
var
  i: Integer;
begin
  for i := 0 to High(FValue.Data) do
    AArray.Add(ASO(FValue.Data[i]));
end;

function TAlgosimComplexMatrix.AdjugateMatrix: TAlgosimMatrix;
begin
  result := ASO(Value.AdjugateMatrix);
end;

function TAlgosimComplexMatrix.Antidiagonal: TAlgosimVector;
begin
  result := ASO(Value.AntiDiagonal);
end;

procedure TAlgosimComplexMatrix.Apply(AFunction: TASOFunction;
  ACondition: TASOPredicate; ALevel: Integer);
var
  i: Integer;
  num: TAlgosimComplexNumber;
  res: TAlgosimObject;
  z: TASC;
begin

  if ALevel > 1 then
    Exit;

  num := TAlgosimComplexNumber.Create;
  try
    for i := 0 to FValue.Size.ElementCount - 1 do
    begin
      num.Value := FValue.Data[i];
      if not Assigned(ACondition) or ACondition(num) then
      begin
        res := AFunction(num);
        try
          if res.TryToASC(z) then
            FValue.Data[i] := z
          else
            raise EAlgosimObjectException.Create(SComplexNumberApply);
        finally
          res.Free;
        end;
      end;
    end;
  finally
    num.Free;
  end;

end;

function TAlgosimComplexMatrix.ColumnSpaceBasis: TAlgosimMatrix;
begin
  Result := ASO(Value.ColumnSpaceBasis);
end;

function TAlgosimComplexMatrix.ConditionNumber(p: Integer): TASR;
begin
  Result := Value.ConditionNumber(p);
end;

function TAlgosimComplexMatrix.ConjugateTranspose: TAlgosimMatrix;
begin
  Result := ASO(FValue.Adjoint);
end;

function TAlgosimComplexMatrix.Count(APredicate: TASOPredicate): Integer;
var
  i: Integer;
  num: TAlgosimComplexNumber;
begin
  num := TAlgosimComplexNumber.Create;
  try
    Result := 0;
    for i := 0 to FValue.Size.ElementCount - 1 do
    begin
      num.Value := FValue.Data[i];
      if APredicate(num) then
        Inc(Result);
    end;
  finally
    num.Free;
  end;
end;

constructor TAlgosimComplexMatrix.Create(AObject: TAlgosimObject);
begin
  CreateWithValue(AObject.ToComplexMatrix);
end;

constructor TAlgosimComplexMatrix.CreateWithValue(const AValue: TComplexMatrix);
begin
  Create;
  FValue := AValue;
end;

function TAlgosimComplexMatrix.Eigenvalues: TComplexVector;
begin
  Result := Value.Eigenvalues;
end;

function TAlgosimComplexMatrix.Eigenvectors: TAlgosimArray;
var
  vals: TComplexVector;
  vects: TComplexMatrix;
begin
  if Value.eigenvectors(vals, vects, False) then
    Result := TAlgosimAssignmentList.CreateWithValue([ASO(vals), ASO(vects)])
  else
    raise EMathException.Create('Couldn''t compute eigenvectors.');
end;

function TAlgosimComplexMatrix.Exists(APredicate: TASOPredicate): Boolean;
var
  i: Integer;
  num: TAlgosimComplexNumber;
begin
  num := TAlgosimComplexNumber.Create;
  try
    for i := 0 to FValue.Size.ElementCount - 1 do
    begin
      num.Value := FValue.Data[i];
      if APredicate(num) then
        Exit(True);
    end;
    Result := False;
  finally
    num.Free;
  end;
end;

function TAlgosimComplexMatrix.ForAll(APredicate: TASOPredicate): Boolean;
var
  i: Integer;
  num: TAlgosimComplexNumber;
begin
  num := TAlgosimComplexNumber.Create;
  try
    for i := 0 to FValue.Size.ElementCount - 1 do
    begin
      num.Value := FValue.Data[i];
      if not APredicate(num) then
        Exit(False);
    end;
    Result := True;
  finally
    num.Free;
  end;
end;

function TAlgosimComplexMatrix.Frequencies: TAlgosimArray;
var
  Dict: TDictionary<TASC, TAlgosimInteger>;
  C: TAlgosimInteger;
  i: Integer;
begin
  Result := TAlgosimArray.Create;
  try
    Result.Capacity := FValue.Size.ElementCount;
    Dict := TDictionary<TASC, TAlgosimInteger>.Create;
    try
      for i := 0 to FValue.Size.ElementCount - 1 do
      begin
        if Dict.TryGetValue(FValue.Data[i], C) then
          Inc(C.FValue)
        else
        begin
          C := ASOInt(1);
          Result.AddElement(
            ASO(
              [
                ASO(FValue.Data[i]),
                C
              ]
            )
          );
          Dict.Add(FValue.Data[i], C);
        end;
      end;
    finally
      Dict.Free;
    end;
    Result.TrimExcess;
  except
    Result.Free;
    raise;
  end;
end;

function TAlgosimComplexMatrix.FrequenciesEps(
  const Epsilon: TASR): TAlgosimArray;
var
  i: Integer;
  j: Integer;
label
  NextValue;
begin
  Result := TAlgosimArray.Create;
  try
    Result.Capacity := FValue.Size.ElementCount;
    for i := 0 to FValue.Size.ElementCount - 1 do
    begin
      for j := 1 to Result.ElementCount do
        if CSameValue(Result.Elements[j].Elements[1].ToASC, FValue.Data[i], Epsilon) then
        begin
          Inc((Result.Elements[j].Elements[2] as TAlgosimInteger).FValue);
          goto NextValue;
        end;
      Result.AddElement(
        ASO(
          [
            ASO(FValue.Data[i]),
            ASOInt(1)
          ]
        )
      );
      NextValue:
    end;
    Result.TrimExcess;
  except
    Result.Free;
    raise;
  end;
end;

function TAlgosimComplexMatrix.GetDimension: TMatrixSize;
begin
  Result := FValue.Size;
end;

function TAlgosimComplexMatrix.GetElementAsString(Y, X: Integer): string;
begin
  Result := ComplexToStr(FValue[Y, X], False, DefaultFormatOptions);
end;

function TAlgosimComplexMatrix.GetElementAsStringFmt(Y, X: Integer;
  const AOptions: TFormatOptions): string;
begin
  Result := ComplexToStr(FValue[Y, X], False, ApplyOptions(AOptions));
end;

function TAlgosimComplexMatrix.GetMemorySize: UInt64;
begin
  Result := FValue.Size.ElementCount * sizeof(TASC);
end;

procedure TAlgosimComplexMatrix.Sort(AComparer: IComparer<TAlgosimObject>);
var
  LeftObj, RightObj: TAlgosimComplexNumber;
begin
  LeftObj := TAlgosimComplexNumber.Create;
  try
    RightObj := TAlgosimComplexNumber.Create;
    try
      FValue.Sort(TComparer<TASC>.Construct(
        function(const Left, Right: TASC): Integer
        begin
          LeftObj.Value := Left;
          RightObj.Value := Right;
          Result := AComparer.Compare(LeftObj, RightObj);
        end
      ));
    finally
      RightObj.Free;
    end;
  finally
    LeftObj.Free;
  end;
end;

procedure TAlgosimComplexMatrix.Sort(AComparer: IComparer<TASR>);
begin
  FValue := TComplexMatrix(ToRealMatrix.Sort(AComparer));
end;

procedure TAlgosimComplexMatrix.Sort(AComparer: IComparer<TASC>);
begin
  FValue.Sort(AComparer);
end;

function TAlgosimComplexMatrix.SortClassGetHashCode: Integer;
var
  HasComplexComponent: Boolean;
  i: Integer;
  RM: TRealMatrix;
begin

  if FValue.Size.ElementCount = 0 then
    Exit(0);

  HasComplexComponent := False;
  for i := 0 to FValue.Size.ElementCount - 1 do
    if FValue.Data[i].Im <> 0 then
    begin
      HasComplexComponent := True;
      Break;
    end;

  if HasComplexComponent then
    Result := THashBobJenkins.GetHashValue(FValue.Data[0], Length(FValue.Data) * sizeof(FValue.Data[0]))
  else
  begin
    RM := AsRealMatrix;
    Result := THashBobJenkins.GetHashValue(RM.Data[0], Length(RM.Data) * sizeof(RM.Data[0]));
  end;

end;

function TAlgosimComplexMatrix.GetBinaryData(var Buf: PByte;
  var Len: UInt64): Boolean;
begin
  Buf := Pointer(FValue.Data);
  Len := Dimension.ElementCount * sizeof(TASC);
  Result := True;
end;

function TAlgosimComplexMatrix.GetValue(Index: Integer): TAlgosimObject;
var
  PhysIndex: Integer;
begin
  PhysIndex := GetPhysIndex0(Index, Dimension.ElementCount);
  Result := ASO(FValue.Data[PhysIndex]);
end;

function TAlgosimComplexMatrix.GetValueFromPoint(const APoint: TPoint): TAlgosimObject;
var
  PhysIndex: TPoint;
begin
  PhysIndex := GetPhysIndex2D0(APoint, Dimension);
  Result := ASO(FValue[PhysIndex.Y, PhysIndex.X]);
end;

function TAlgosimComplexMatrix.GramSchmidt: TAlgosimMatrix;
begin
  Result := ASO(Value.GramSchmidt);
end;

function TAlgosimComplexMatrix.HermitianSquare: TAlgosimMatrix;
begin
  Result := ASO(Value.HermitianSquare);
end;

function TAlgosimComplexMatrix.ImaginaryPart: TAlgosimNumericEntity;
begin
  Result := ASO(FValue.ImaginaryPart);
end;

function TAlgosimComplexMatrix.IndexOfValue(
  AObj: TAlgosimObject): TAlgosimObject;
var
  z: TASC;
  i: Integer;
begin

  if not (AObj is TAlgosimNumber) then
    Exit(ASO(null));

  if not AObj.TryToASC(z) then
    Exit(ASO(null));

  for i := 0 to FValue.Size.ElementCount - 1 do
    if FValue.Data[i] = z then
      Exit(TAlgosimArray.CreateWithValue([i mod FValue.Size.Cols + 1, i div FValue.Size.Cols + 1]));

  Result := ASO(null);

end;

function TAlgosimComplexMatrix.IndexOfValueEps(AObj: TAlgosimObject;
  const AEpsilon: TASR): TAlgosimObject;
var
  z: TASC;
  i: Integer;
begin

  if not (AObj is TAlgosimNumber) then
    Exit(ASO(null));

  if not AObj.TryToASC(z) then
    Exit(ASO(null));

  for i := 0 to FValue.Size.ElementCount - 1 do
    if CSameValue(FValue.Data[i], z, AEpsilon) then
      Exit(TAlgosimArray.CreateWithValue([i mod FValue.Size.Cols + 1, i div FValue.Size.Cols + 1]));

  Result := ASO(null);

end;

function TAlgosimComplexMatrix.IndicesOf(
  APredicate: TASOPredicate): TAlgosimArray;
var
  c: TAlgosimComplexNumber;
  i: Integer;
begin

  Result := TAlgosimArray.Create;
  try

    Result.Capacity := CapCap(FValue.Size.ElementCount);

    c := TAlgosimComplexNumber.Create;
    try
      for i := 0 to FValue.Size.ElementCount - 1 do
      begin
        c.Value := FValue.Data[i];
        if APredicate(c) then
          Result.Add(TAlgosimArray.CreateWithValue([i div FValue.Size.Cols + 1, i mod FValue.Size.Cols + 1]));
      end;
    finally
      c.Free;
    end;

    Result.TrimExcess;

  except
    Result.Free;
    raise;
  end;

end;

function TAlgosimComplexMatrix.IndicesOfValue(
  AObj: TAlgosimObject): TAlgosimArray;
var
  z: TASC;
  i: Integer;
begin

  Result := TAlgosimArray.Create;
  try

    if not (AObj is TAlgosimNumber) then
      Exit;

    if not AObj.TryToASC(z) then
      Exit;

    Result.Capacity := CapCap(FValue.Size.ElementCount);

    for i := 0 to FValue.Size.ElementCount - 1 do
      if FValue.Data[i] = z then
        Result.Add(TAlgosimArray.CreateWithValue([i div FValue.Size.Cols + 1, i mod FValue.Size.Cols + 1]));

    Result.TrimExcess;

  except
    Result.Free;
    raise;
  end;

end;

function TAlgosimComplexMatrix.IndicesOfValueEps(AObj: TAlgosimObject;
  const AEpsilon: TASR): TAlgosimArray;
var
  z: TASC;
  i: Integer;
begin

  Result := TAlgosimArray.Create;
  try

    if not (AObj is TAlgosimNumber) then
      Exit;

    if not AObj.TryToASC(z) then
      Exit;

    Result.Capacity := CapCap(FValue.Size.ElementCount);

    for i := 0 to FValue.Size.ElementCount - 1 do
      if CSameValue(FValue.Data[i], z, AEpsilon) then
        Result.Add(TAlgosimArray.CreateWithValue([i div FValue.Size.Cols + 1, i mod FValue.Size.Cols + 1]));

    Result.TrimExcess;

  except
    Result.Free;
    raise;
  end;

end;

function TAlgosimComplexMatrix.Inverse: TAlgosimNumericEntity;
begin
  Result := ASO(Value.Inverse);
end;

function TAlgosimComplexMatrix.IsNonZero(const Eps: Double): Boolean;
begin
  Result := not FValue.IsZeroMatrix(Eps);
end;

function TAlgosimComplexMatrix.IsSingular: Boolean;
begin
  Result := Value.IsSingular;
end;

function TAlgosimComplexMatrix.IsZero(const Eps: Double): Boolean;
begin
  Result := FValue.IsZeroMatrix(Eps);
end;

function TAlgosimComplexMatrix.MainDiagonal: TAlgosimVector;
begin
  Result := ASO(Value.MainDiagonal);
end;

function TAlgosimComplexMatrix.Minor(Row, Col: Integer): TAlgosimNumber;
begin
  Result := ASO(Value.Minor(Row - 1, Col - 1));
end;

function TAlgosimComplexMatrix.Cofactor(Row, Col: Integer): TAlgosimNumber;
begin
  Result := ASO(Value.Cofactor(Row - 1, Col - 1));
end;

function TAlgosimComplexMatrix.CofactorMatrix: TAlgosimMatrix;
begin
  Result := ASO(Value.CofactorMatrix);
end;

function TAlgosimComplexMatrix.Modulus: TAlgosimMatrix;
begin
  Result := ASO(Value.Modulus);
end;

function TAlgosimComplexMatrix.Norm(AType: TNormType; AParam: Integer;
  AYieldProc: TObjProc): TASR;
begin
  case AType of
    ntEuclidean, ntFrobenius:
      Result := FValue.Norm;
    ntPNorm:
      Result := FValue.pNorm(AParam);
    ntMaxNorm:
      Result := FValue.MaxNorm;
    ntSumNorm:
      Result := FValue.SumNorm;
    ntKNorm:
      Result := FValue.kNorm(AParam);
    ntMaxColSum:
      Result := FValue.MaxColSumNorm;
    ntMaxRowSum:
      Result := FValue.MaxRowSumNorm;
    ntSpectral:
      Result := FValue.SpectralNorm;
  else
    Result := inherited;
  end;
end;

function TAlgosimComplexMatrix.NormSquared: TAlgosimRealNumber;
begin
  Result := ASO(FValue.NormSqr);
end;

function TAlgosimComplexMatrix.Nullity: Integer;
begin
  Result := Value.Nullity;
end;

function TAlgosimComplexMatrix.NumZeroRows(const AEpsilon: TASR): Integer;
begin
  Result := Value.NumZeroRows(AEpsilon);
end;

function TAlgosimComplexMatrix.N_GeometricMean: TAlgosimNumber;
begin
  Result := ASO(ASNum.GeometricMean(FValue));
end;

function TAlgosimComplexMatrix.N_HarmonicMean: TAlgosimNumber;
begin
  Result := ASO(ASNum.HarmonicMean(FValue));
end;

function TAlgosimComplexMatrix.N_product: TAlgosimNumericEntity;
begin
  Result := ASO(ASNum.product(FValue));
end;

function TAlgosimComplexMatrix.N_sum: TAlgosimNumericEntity;
begin
  Result := ASO(ASNum.sum(FValue));
end;

function TAlgosimComplexMatrix.NumTrailingZeroRows(const AEpsilon: TASR): Integer;
begin
  Result := Value.NumTrailingZeroRows(AEpsilon);
end;

function TAlgosimComplexMatrix.Part2d(const AIndicesX,
  AIndicesY: array of Integer): TAlgosimObject;
begin
  Result := ASO(Value.Submatrix(
    TranslatedIntSequence(AIndicesY),
    TranslatedIntSequence(AIndicesX)));
end;

function TAlgosimComplexMatrix.Power(AExp: Integer): TAlgosimMatrix;
begin
  Result := ASO(mpow(FValue, AExp));
end;

function TAlgosimComplexMatrix.Rank: Integer;
begin
  Result := Value.Rank;
end;

function TAlgosimComplexMatrix.RealPart: TAlgosimNumericEntity;
begin
  Result := ASO(FValue.RealPart);
end;

function TAlgosimComplexMatrix.RowEchelonForm: TAlgosimMatrix;
begin
  Result := ASO(Value.RowEchelonForm);
end;

function TAlgosimComplexMatrix.ReducedRowEchelonForm: TAlgosimMatrix;
begin
  Result := ASO(Value.ReducedRowEchelonForm);
end;

procedure TAlgosimComplexMatrix.Replace(APredicate: TASOPredicate;
  ANewValue: TAlgosimObject; ALevel: Integer);
var
  i: Integer;
  Obj: TAlgosimComplexNumber;
  z: TASC;
begin

  if ALevel > 1 then
    Exit;

  z := ANewValue.ToASC;

  Obj := TAlgosimComplexNumber.Create;
  try
    for i := 0 to FValue.Size.ElementCount - 1 do
    begin
      Obj.FValue := FValue.Data[i];
      if not Assigned(APredicate) or APredicate(Obj) then
        FValue.Data[i] := z;
    end;
  finally
    Obj.Free;
  end;

end;

procedure TAlgosimComplexMatrix.Reverse;
begin
  FValue.Reverse;
end;

function TAlgosimComplexMatrix.ScaledBy(
  const AFactor: TASR): TAlgosimNumericEntity;
begin
  Result := ASO(FValue * AFactor);
end;

procedure TAlgosimComplexMatrix.SetDimension(const Value: TMatrixSize);
begin
  FValue.Size := Value;
end;

procedure TAlgosimComplexMatrix.SetSubscript(ASubscript: TSubscript;
  AValue: TAlgosimObject);
begin
  case ASubscript.Kind of
    skRowIndex:
      try
        if InRange(ASubscript.Ordinal, 1, Value.Size.Rows) then
          Value.Rows[ASubscript.Ordinal - 1] := AValue.AsComplexVector
        else
          raise EAlgosimObjectException.CreateFmt(SIndexOutOfBounds, [ASubscript.Ordinal]);
      finally
        AValue.Free;
      end;
    skColIndex:
      try
        if InRange(ASubscript.Ordinal, 1, Value.Size.Cols) then
          Value.Cols[ASubscript.Ordinal - 1] := AValue.AsComplexVector
        else
          raise EAlgosimObjectException.CreateFmt(SIndexOutOfBounds, [ASubscript.Ordinal]);
      finally
        AValue.Free;
      end;
    skMainDiagonal:
      try
        Value.MainDiagonal := AValue.AsComplexVector;
      finally
        AValue.Free;
      end;
    skSuperdiagonal:
      try
        Value.SuperDiagonal := AValue.AsComplexVector;
      finally
        AValue.Free;
      end;
    skSubdiagonal:
      try
        Value.SubDiagonal := AValue.AsComplexVector;
      finally
        AValue.Free;
      end;
    skAntidiagonal:
      try
        Value.AntiDiagonal := AValue.AsComplexVector;
      finally
        AValue.Free;
      end;
  else
    inherited;
  end;
end;

procedure TAlgosimComplexMatrix.SetValue(Index: Integer;
  AValue: TAlgosimObject);
var
  z: TASC;
  PhysIndex: Integer;
begin

  try

    PhysIndex := GetPhysIndex0(Index, Dimension.ElementCount);

    if (AValue is TAlgosimNumber) and AValue.TryToASC(z) then
      FValue.Data[PhysIndex] := z
    else
      raise EAlgosimObjectException.CreateFmt(SSetCMatSubscriptNoNum, [AValue.TypeName])

  finally
    AValue.Free;
  end;

end;

procedure TAlgosimComplexMatrix.SetValueFromPoint(const APoint: TPoint;
  AValue: TAlgosimObject);
var
  z: TASC;
  PhysIndex: TPoint;
begin

  try

    PhysIndex := GetPhysIndex2D0(APoint, Dimension);

    if AValue.TryToASC(z) then
      FValue[PhysIndex.Y, PhysIndex.X] := z
    else
      raise EAlgosimObjectException.CreateFmt(SSetCMatSubscriptNoNum, [AValue.TypeName])

  finally
    AValue.Free;
  end;
end;

procedure TAlgosimComplexMatrix.Shuffle;
begin
  FValue.Shuffle;
end;

function TAlgosimComplexMatrix.SimilarHessenberg: TAlgosimMatrix;
begin
  Result := ASO(Value.SimilarHessenberg);
end;

function TAlgosimComplexMatrix.SingularValues: TRealVector;
begin
  Result := Value.SingularValues;
end;

function TAlgosimComplexMatrix.SpectralRadius: TASR;
begin
  Result := Value.SpectralRadius;
end;

function TAlgosimComplexMatrix.Square: TAlgosimNumericEntity;
begin
  Result := ASO(Value.Sqr);
end;

function TAlgosimComplexMatrix.Subdiagonal: TAlgosimVector;
begin
  Result := ASO(Value.SubDiagonal);
end;

function TAlgosimComplexMatrix.Superdiagonal: TAlgosimVector;
begin
  Result := ASO(Value.SuperDiagonal);
end;

function TAlgosimComplexMatrix.ToComplexMatrix: TComplexMatrix;
begin
  Result := Value.Clone;
end;

function TAlgosimComplexMatrix.AsComplexMatrix: TComplexMatrix;
begin
  Result := Value
end;

function TAlgosimComplexMatrix.AsComplexVector: TComplexVector;
begin
  Result := TComplexVector(VAlue.Data);
end;

function TAlgosimComplexMatrix.ToComplexVector: TComplexVector;
begin
  Result := Value.AsVector;
end;

function TAlgosimComplexMatrix.ToRealMatrix: TRealMatrix;
var
  i: Integer;
begin

  Result := TRealMatrix.CreateUninitialized(Value.Size);
  for i := 0 to Value.Size.ElementCount - 1 do
    if Value.Data[i].IsReal then
      Result.Data[i] := Value.Data[i].Re
    else
      raise EAlgosimObjectException.CreateFmt(SComplexMatToReal, [Value.Data[i].pstr]);

end;

function TAlgosimComplexMatrix.ToRealVector: TRealVector;
var
  i: Integer;
begin

  for i := 0 to Value.Size.ElementCount - 1 do
    if not Value.Data[i].IsReal then
      raise EAlgosimObjectException.CreateFmt(SComplexMatToRealVect, [Value.Data[i].pstr]);

  Result.Dimension := Value.Size.ElementCount;
  for i := 0 to Result.Dimension - 1 do
    Result[i] := Value.Data[i].Re;

end;

function TAlgosimComplexMatrix.Trace: TAlgosimNumber;
begin
  Result := ASO(Value.Trace);
end;

function TAlgosimComplexMatrix.Transpose: TAlgosimMatrix;
begin
  Result := ASO(FValue.Transpose);
end;

function TAlgosimComplexMatrix.TryGetSubscriptedValue(ASubscript: TSubscript;
  out AValue: TAlgosimObject): Boolean;
begin
  case ASubscript.Kind of
    skRowIndex:
      begin
        if InRange(ASubscript.Ordinal, 1, Value.Size.Rows) then
          AValue := ASO(Value.Rows[ASubscript.Ordinal - 1])
        else
          raise EAlgosimObjectException.CreateFmt(SIndexOutOfBounds, [ASubscript.Ordinal]);
        Result := True;
      end;
    skColIndex:
      begin
        if InRange(ASubscript.Ordinal, 1, Value.Size.Cols) then
          AValue := ASO(Value.Cols[ASubscript.Ordinal - 1])
        else
          raise EAlgosimObjectException.CreateFmt(SIndexOutOfBounds, [ASubscript.Ordinal]);
        Result := True;
      end;
    skMainDiagonal:
      begin
        AValue := ASO(Value.MainDiagonal);
        Result := True;
      end;
    skSuperdiagonal:
      begin
        AValue := ASO(Value.SuperDiagonal);
        Result := True;
      end;
    skSubdiagonal:
      begin
        AValue := ASO(Value.SubDiagonal);
        Result := True;
      end;
    skAntidiagonal:
      begin
        AValue := ASO(Value.AntiDiagonal);
        Result := True;
      end;
  else
    Result := inherited;
  end;
end;

function TAlgosimComplexMatrix.UnaryMinus: TAlgosimObject;
begin
  Result := ASO(-FValue);
end;

function TAlgosimComplexMatrix.Vectorization: TAlgosimVector;
begin
  Result := ASO(Value.Vectorization);
end;

{ TAlgosimPixmap }

function TAlgosimPixmap.Accumulate(AInitialValue: TAlgosimObject;
  AFunction: TASOAccumulator): TAlgosimObject;
var
  i: Integer;
  color: TAlgosimColor;
begin
  color := TAlgosimColor.Create;
  try
    Result := AInitialValue.Clone;
    try
      for i := 0 to FValue.PixelCount - 1 do
      begin
        color.AsPixel := FValue.Data[i];
        TObjReplacer<TAlgosimObject>.Replace(Result, AFunction(Result, color));
      end;
    except
      Result.Free;
      raise;
    end;
  finally
    color.Free;
  end;
end;

function TAlgosimPixmap.AccumulateStepsList(AInitialValue: TAlgosimObject;
  AFunction: TASOAccumulator): TAlgosimArray;
var
  i: Integer;
  color: TAlgosimColor;
begin
  color := TAlgosimColor.Create;
  try
    Result := TAlgosimArray.Create;
    try
      Result.Capacity := FValue.PixelCount;
      for i := 0 to FValue.PixelCount - 1 do
      begin
        color.AsPixel := FValue.Data[i];
        AInitialValue := AFunction(AInitialValue, color);
        Result.Add(AInitialValue);
      end;
    except
      Result.Free;
      raise;
    end;
  finally
    color.Free;
  end;
end;

procedure TAlgosimPixmap.AddNumbersToArray(AArray: TAlgosimArray; ALevel: Integer);
begin

end;

procedure TAlgosimPixmap.Apply(AFunction: TASOFunction;
  ACondition: TASOPredicate; ALevel: Integer);
var
  i: Integer;
  color: TAlgosimColor;
  res: TAlgosimObject;
begin

  if ALevel > 1 then
    Exit;

  color := TAlgosimColor.Create;
  try
    for i := 0 to FValue.PixelCount - 1 do
    begin
      color.AsPixel := FValue.Data[i];
      if not Assigned(ACondition) or ACondition(color) then
      begin
        res := AFunction(color);
        try
          if res is TAlgosimColor then
            FValue.Data[i] := TAlgosimColor(res).AsPixel
          else
            raise EAlgosimObjectException.Create(SColorApply);
        finally
          res.Free;
        end;
      end;
    end;
  finally
    color.Free;
  end;

end;

procedure TAlgosimPixmap.CopyToClipboard;
begin
  FValue.CopyToClipboard;
end;

function TAlgosimPixmap.Count(APredicate: TASOPredicate): Integer;
var
  i: Integer;
  color: TAlgosimColor;
begin
  color := TAlgosimColor.Create;
  try
    Result := 0;
    for i := 0 to FValue.PixelCount - 1 do
    begin
      color.AsPixel := FValue.Data[i];
      if APredicate(color) then
        Inc(Result);
    end;
  finally
    color.Free;
  end;
end;

constructor TAlgosimPixmap.Create(AObject: TAlgosimObject);
begin
  if AObject is TAlgosimPixmap then
    CreateWithValue(TAlgosimPixmap(AObject).Value.Clone)
  else
    NoCopyConstr(AObject);
end;

constructor TAlgosimPixmap.CreateWithValue(const AValue: TASPixmap);
begin
  Create;
  FValue := AValue;
end;

function TAlgosimPixmap.Equals(Obj: TObject): Boolean;
begin
  Result := (Obj is TAlgosimPixmap) and (Value = TAlgosimPixmap(Obj).Value);
end;

function TAlgosimPixmap.Exists(APredicate: TASOPredicate): Boolean;
var
  i: Integer;
  color: TAlgosimColor;
begin
  color := TAlgosimColor.Create;
  try
    for i := 0 to FValue.PixelCount - 1 do
    begin
      color.AsPixel := FValue.Data[i];
      if APredicate(color) then
        Exit(True);
    end;
    Result := False;
  finally
    color.Free;
  end;
end;

function TAlgosimPixmap.ForAll(APredicate: TASOPredicate): Boolean;
var
  i: Integer;
  color: TAlgosimColor;
begin
  color := TAlgosimColor.Create;
  try
    for i := 0 to FValue.PixelCount - 1 do
    begin
      color.AsPixel := FValue.Data[i];
      if not APredicate(color) then
        Exit(False);
    end;
    Result := True;
  finally
    color.Free;
  end;
end;

function TAlgosimPixmap.Frequencies: TAlgosimArray;
var
  Dict: TDictionary<TASPixel, TAlgosimInteger>;
  C: TAlgosimInteger;
  i: Integer;
begin
  Result := TAlgosimArray.Create;
  try
    Result.Capacity := FValue.PixelCount;
    Dict := TDictionary<TASPixel, TAlgosimInteger>.Create;
    try
      for i := 0 to FValue.PixelCount - 1 do
      begin
        if Dict.TryGetValue(FValue.Data[i], C) then
          Inc(C.FValue)
        else
        begin
          C := ASOInt(1);
          Result.AddElement(
            ASO(
              [
                ASO(FValue.Data[i]),
                C
              ]
            )
          );
          Dict.Add(FValue.Data[i], C);
        end;
      end;
    finally
      Dict.Free;
    end;
    Result.TrimExcess;
  except
    Result.Free;
    raise;
  end;
end;

function TAlgosimPixmap.GetAsSingleLineText(const AOptions: TFormatOptions): string;
begin
  Result := ToString;
end;

function TAlgosimPixmap.GetBinaryData(var Buf: PByte; var Len: UInt64): Boolean;
begin
  Buf := PByte(FValue.Data);
  Len := Value.PixelCount * sizeof(TASPixel);
  Result := True;
end;

function TAlgosimPixmap.GetMemorySize: UInt64;
begin
  Result := FValue.MemoryUsage;
end;

function TAlgosimPixmap.SortClassGetHashCode: Integer;
begin
  if FValue.PixelCount = 0 then
    Exit(0);
  Result := THashBobJenkins.GetHashValue(FValue.Data^, FValue.PixelCount * sizeof(TASPixel));
end;

function TAlgosimPixmap.GetPlanarExtent: TSize;
begin
  Result.cx := FValue.Width;
  Result.cy := FValue.Height;
end;

function TAlgosimPixmap.GetValue(Index: Integer): TAlgosimObject;
begin
  if InRange(Index, 1, FValue.PixelCount) then
    Result := ASO(FValue.Data[Index - 1])
  else if InRange(-Index, 1, FValue.PixelCount) then
    Result := ASO(FValue.Data[FValue.PixelCount + Index])
  else
    raise EAlgosimObjectException.CreateFmt(SIndexOutOfBounds, [Index]);
end;

function TAlgosimPixmap.GetValueCount: Integer;
begin
  Result := FValue.PixelCount;
end;

function TAlgosimPixmap.GetValueFromPoint(const APoint: TPoint): TAlgosimObject;
begin
  if InRange(APoint.X, 0, FValue.Width - 1) and InRange(APoint.Y, 0, FValue.Height - 1) then
    Result := ASO(FValue[APoint.X, APoint.Y])
  else
    raise EAlgosimObjectException.CreateFmt(SIndexOutOfBounds2D, [APoint.Y, APoint.X]);
end;

class function TAlgosimPixmap.LoadFromFile(const AFileName: string;
  AEncoding: TEncoding; const AParams: string): TAlgosimObject;
var
  bm: TBitmap;
begin
  bm := LoadGraphicsFromFile(AFileName);
  try
    bm.PixelFormat := pf32bit;
    Result := ASO(TASPixmap(bm));
  finally
    bm.Free;
  end;
end;

procedure TAlgosimPixmap.Replace(APredicate: TASOPredicate;
  ANewValue: TAlgosimObject; ALevel: Integer);
var
  i: Integer;
  Obj: TAlgosimColor;
  c: TASPixel;
begin

  if ALevel > 1 then
    Exit;

  c := ANewValue.ToPixel;

  Obj := TAlgosimColor.Create;
  try
    for i := 0 to FValue.PixelCount - 1 do
    begin
      Obj.AsPixel := FValue.Data[i];
      if not Assigned(APredicate) or APredicate(Obj) then
        FValue.Data[i] := c;
    end;
  finally
    Obj.Free;
  end;

end;

procedure TAlgosimPixmap.Reverse;
begin
  FValue.ReversePixels;
end;

procedure TAlgosimPixmap.SaveToFile(const AFileName: string);
begin
  FValue.SaveToFile(AFileName);
end;

procedure TAlgosimPixmap.SaveToFile(ADlgOwner: TComponent;
  const ADefFileName: string = '');
begin
  FValue.SaveToFile(ADlgOwner, ADefFileName);
end;

procedure TAlgosimPixmap.SetPlanarExtent(const Value: TSize);
begin
  FValue.SetSize(Value.Width, Value.Height);
end;

procedure TAlgosimPixmap.SetValue(Index: Integer; AValue: TAlgosimObject);
var
  PhysIndex: Integer;
begin

  try

    PhysIndex := GetPhysIndex0(Index, FValue.PixelCount);

    if AValue is TAlgosimColor then
      FValue.Data[PhysIndex] := TAlgosimColor(AValue).AsPixel
    else
      raise EAlgosimObjectException.CreateFmt(SSetPixmapSubscriptNoColor, [AValue.TypeName])

  finally
    AValue.Free;
  end;

end;

procedure TAlgosimPixmap.SetValueFromPoint(const APoint: TPoint;
  AValue: TAlgosimObject);
begin
  try
    if InRange(APoint.X, 0, FValue.Width - 1) and InRange(APoint.Y, 0, FValue.Height - 1) then
      if AValue is TAlgosimColor then
        FValue[APoint.X, APoint.Y] := TAlgosimColor(AValue).AsPixel
      else
        raise EAlgosimObjectException.CreateFmt(SSetPixmapSubscriptNoColor, [AValue.TypeName])
    else
      raise EAlgosimObjectException.CreateFmt(SIndexOutOfBounds2D, [APoint.Y, APoint.X]);
  finally
    AValue.Free;
  end;
end;

procedure TAlgosimPixmap.Shuffle;
begin
  FValue.ShufflePixels;
end;

class function TAlgosimPixmap.SortClass: TSortClass;
begin
  Result := SORTCLASS_PIXMAP;
end;

class function TAlgosimPixmap.SortClassCompare(const Left,
  Right: TAlgosimObject): Integer;
var
  L, R: TAlgosimPixmap;
  i: Integer;
begin
  L := Left as TAlgosimPixmap;
  R := Right as TAlgosimPixmap;
  Result := CompareValue(L.Value.Height, R.Value.Height);
  if Result = 0 then
    Result := CompareValue(L.Value.Width, R.Value.Width);
  if Result = 0 then
    for i := 0 to L.Value.PixelCount - 1 do
    begin
      Result := ComparePixel(L.Value.Data[i], R.Value.Data[i]);
      if Result <> EqualsValue then
        Exit;
    end;
end;

class function TAlgosimPixmap.SortClassSameObject(const Left,
  Right: TAlgosimObject; const AEpsilon: TASR): Boolean;
var
  L, R: TAlgosimPixmap;
  i: Integer;
begin
  L := Left as TAlgosimPixmap;
  R := Right as TAlgosimPixmap;
  Result := L.PlanarExtent = R.PlanarExtent;
  if Result then
    for i := 0 to L.Value.PixelCount - 1 do
      if not SamePixel(L.Value.Data[i], R.Value.Data[i]) then
        Exit(False);
end;

function TAlgosimPixmap.ToRealMatrix: TRealMatrix;
var
  i: Integer;
begin
  Result := TRealMatrix.CreateUninitialized(TMatrixSize.Create(Value.Height, Value.Width));
  for i := 0 to Value.PixelCount - 1 do
    Result.Data[i] := TColor(Value.Data[i]);
end;

function TAlgosimPixmap.ToString: string;
resourcestring
  SPixmapSummary = 'A pixmap of size %d×%d.';
begin
  Result := Format(SPixmapSummary, [FValue.Width, FValue.Height]);
end;

function TAlgosimPixmap.WithSpecificValues(
  AValues: TAlgosimArray): TAlgosimObject;
var
  pixel: PASPixel;
  i: Integer;
begin

  if PlanarExtent.Width * PlanarExtent.Height <> AValues.ElementCount then
    raise EAlgosimObjectException.CreateFmt(SSpecValWrongLength2D,
      [PlanarExtent.Height, PlanarExtent.Width, AValues.ElementCount]);

  Result := TAlgosimPixmap.Create;
  try
    Result.PlanarExtent := PlanarExtent;
    pixel := TAlgosimPixmap(Result).FValue.Data;
    for i := 1 to AValues.ElementCount do
    begin
      pixel^ := AValues.Elements[i].ToPixel;
      Inc(pixel);
    end;
  except
    Result.Free;
    raise;
  end;

end;

{ TAlgosimSound }

procedure TAlgosimSound.CopyToClipboard;
begin
  FValue.CopyToClipboard;
end;

constructor TAlgosimSound.Create(AObject: TAlgosimObject);
begin
  if AObject is TAlgosimSound then
    CreateWithValue(TAlgosimSound(AObject).Value.Clone)
  else
    NoCopyConstr(AObject);
end;

constructor TAlgosimSound.CreateWithValue(const AValue: TASSound);
begin
  Create;
  FValue := AValue;
end;

function TAlgosimSound.Equals(Obj: TObject): Boolean;
begin
  Result := (Obj is TAlgosimSound) and (Value = TAlgosimSound(Obj).Value);
end;

function TAlgosimSound.GetAsSingleLineText(const AOptions: TFormatOptions): string;
begin
  Result := ToString;
end;

function TAlgosimSound.GetBinaryData(var Buf: PByte; var Len: UInt64): Boolean;
begin
  Buf := FValue.Data;
  Len := FValue.DataLength;
  Result := True;
end;

function TAlgosimSound.GetMemorySize: UInt64;
begin
  Result := FValue.DataLength;
end;

function TAlgosimSound.SortClassGetHashCode: Integer;
begin
  Result := CombineHashes(
    [
      FValue.SampleFrequency,
      FValue.BitsPerSample,
      FValue.ChannelCount,
      FValue.ChannelMask,
      FValue.DataLength,
      THashBobJenkins.GetHashValue(FValue.Data^, FValue.DataLength)
    ]
  )
end;

procedure TAlgosimSound.Invoke;
begin
  inherited;
  FValue.Play;
end;

class function TAlgosimSound.LoadFromFile(const AFileName: string;
  AEncoding: TEncoding; const AParams: string): TAlgosimObject;
begin
  Result := ASO(ASSounds.LoadSoundFromFile(AFileName));
end;

procedure TAlgosimSound.Reverse;
begin
  FValue := FValue.Reverse;
end;

procedure TAlgosimSound.SaveToFile(const AFileName: string);
begin
  FValue.SaveToFile(AFileName);
end;

class function TAlgosimSound.SortClass: TSortClass;
begin
  Result := SORTCLASS_SOUND;
end;

class function TAlgosimSound.SortClassCompare(const Left,
  Right: TAlgosimObject): Integer;
var
  L, R: TAlgosimSound;
  i: Integer;
begin
  L := Left as TAlgosimSound;
  R := Right as TAlgosimSound;
  Result := CompareValue(L.Value.Duration, R.Value.Duration);
  if Result = 0 then
    Result := CompareValue(L.Value.DataLength, R.Value.DataLength);
  if Result = 0 then
    for i := 0 to L.Value.DataLength do
    begin
      Result := CompareValue(L.Value.Data[i], R.Value.Data[i]);
      if Result <> EqualsValue then
        Exit;
    end;
end;

class function TAlgosimSound.SortClassSameObject(const Left,
  Right: TAlgosimObject; const AEpsilon: TASR): Boolean;
var
  L, R: TAlgosimSound;
begin
  L := Left as TAlgosimSound;
  R := Right as TAlgosimSound;
  Result := L.Value = R.Value;
end;

function TAlgosimSound.ToRealVector: TRealVector;
var
  i: Integer;
begin
  Result.Dimension := Value.DataLength;
  for i := 0 to Value.DataLength - 1 do
    Result[i] := Value.Data[i];
end;

function TAlgosimSound.ToString: string;
resourcestring
  SSoundSummary = 'A %f-second %d-bit %d Hz %d-channel sound.';
begin
  Result := Format(SSoundSummary, [FValue.Duration, FValue.BitsPerSample,
    FValue.SampleFrequency, FValue.ChannelCount], DefaultFormatSettings);
end;

{ TAlgosimArray }

function TAlgosimArray.AccumulateSteps(AInitialValue: TAlgosimObject;
  AFunction: TASOAccumulator): TAlgosimObject;
begin
  Result := AccumulateStepsList(AInitialValue, AFunction);
end;

function TAlgosimArray.Add(const AElement: TAlgosimObject): Integer;
begin

  if not Assigned(AElement) then
    raise EArrayException.Create(SArrayCannotInsertNil);

  if FActualLength = Length(FElements) then
    try
      Grow;
    except
      AElement.Free;
      raise;
    end;

  FElements[FActualLength] := AElement;
  Result := FActualLength;
  Inc(FActualLength);

end;

function TAlgosimArray.AddElement(const AElement: TAlgosimObject): Boolean;
begin
  Add(AElement);
  Result := True;
end;

procedure TAlgosimArray.Append(AElement: TAlgosimObject);
begin
  Add(AElement);
end;

procedure TAlgosimArray.Clear;
var
  i: Integer;
begin
  if FOwnsObjects then
    for i := FActualLength - 1 downto 0 do
      FreeAndNil(FElements[i]);
  SetLength(FElements, 0);
  FActualLength := 0;
end;

constructor TAlgosimArray.Create;
begin
  inherited;
  FActualLength := 0;
  FOwnsObjects := True;
end;

constructor TAlgosimArray.CreateUninitialized(Size: Integer);
begin
  Create;
  SetLength(FElements, Size);
  TZeroer<TAlgosimObject>.Zero(FElements);
  FActualLength := Size;
end;


constructor TAlgosimArray.Create(AObject: TAlgosimObject);
var
  i: Integer;
begin
  if AObject.IsObjectContainer then
  begin
    Create;
    for i := 1 to AObject.ElementCount do
      Add(AObject.Elements[i].Clone);
  end
  else
    CreateWithValue([AObject.Clone]);
  if AObject is TAlgosimArray then
    MaxLen := TAlgosimArray(AObject).MaxLen;
end;

constructor TAlgosimArray.CreateWithValue(const AElements: array of TASC);
var
  i: Integer;
begin
  CreateUninitialized(Length(AElements));
  for i := 0 to High(FElements) do
    FElements[i] := ASO(AElements[i]);
end;

constructor TAlgosimArray.CreateWithValue(const AElements: array of TASR);
var
  i: Integer;
begin
  CreateUninitialized(Length(AElements));
  for i := 0 to High(FElements) do
    FElements[i] := ASO(AElements[i]);
end;

constructor TAlgosimArray.CreateWithValue(const AElements: array of string);
var
  i: Integer;
begin
  CreateUninitialized(Length(AElements));
  for i := 0 to High(FElements) do
    FElements[i] := ASO(AElements[i]);
end;

constructor TAlgosimArray.CreateWithValue(const AElements: array of Integer);
var
  i: Integer;
begin
  CreateUninitialized(Length(AElements));
  for i := 0 to High(FElements) do
    FElements[i] := ASOInt(AElements[i]);
end;

constructor TAlgosimArray.CreateWithValue(const AElements: array of TASI);
var
  i: Integer;
begin
  CreateUninitialized(Length(AElements));
  for i := 0 to High(FElements) do
    FElements[i] := ASOInt(AElements[i]);
end;

constructor TAlgosimArray.CreateWithValue(const AElements: array of Boolean);
var
  i: Integer;
begin
  CreateUninitialized(Length(AElements));
  for i := 0 to High(FElements) do
    FElements[i] := ASO(AElements[i]);
end;

constructor TAlgosimArray.CreateWithValue(
  const AElements: array of TAlgosimObject);
var
  i: Integer;
begin

  try
    for i := 0 to High(AElements) do
      if not Assigned(AElements[i]) then
        raise EArrayException.CreateFmt(SArrayElementIsNil, [i]);

    Create;
    SetLength(FElements, Length(AElements));
  except
    for i := High(AElements) downto 0 do
      AElements[i].Free;
    raise;
  end;

  if Length(AElements) > 0 then
    Move(AElements[0], FElements[0], Length(AElements) * sizeof(TAlgosimObject));

  FActualLength := Length(FElements);

end;

destructor TAlgosimArray.Destroy;
begin
  Clear;
  inherited;
end;

function TAlgosimArray.Equals(Obj: TObject): Boolean;
var
  i: Integer;
begin
  if not (Obj is TAlgosimArray) then
    Exit(False);
  if TAlgosimArray(Obj).ElementCount <> ElementCount then
    Exit(False);
  for i := 1 to ElementCount do
    if not Elements[i].Equals(TAlgosimArray(Obj).Elements[i]) then
      Exit(False);
  Result := True;
end;

function TAlgosimArray.GetAsMultilineText(const AOptions: TFormatOptions): string;
var
  EffectiveMaxLen: Integer;
  i: Integer;
begin
  if FMaxLen > 0 then
    EffectiveMaxLen := FMaxLen
  else
    EffectiveMaxLen := AOptions.Lists.MaxLen;
  for i := 0 to Math.min(EffectiveMaxLen, FActualLength) - 1 do
    Result := Result + IfThen(i > 0, sLineBreak) + FElements[i].GetAsSingleLineText(AOptions);
  if FActualLength > EffectiveMaxLen then
    Result := Result + IfThen(not Result.IsEmpty, sLineBreak) + EllipsisSymbol_VERTICAL_ELLIPSIS;
end;

function TAlgosimArray.GetAsSingleLineText(const AOptions: TFormatOptions): string;
var
  EffectiveMaxLen: Integer;
  i: Integer;
begin
  if FMaxLen > 0 then
    EffectiveMaxLen := FMaxLen
  else
    EffectiveMaxLen := AOptions.Lists.MaxLen;
  Result := AOptions.Lists.LeftDelim;
  for i := 0 to Math.min(EffectiveMaxLen, FActualLength) - 1 do
    Result := Result + IfThen(i > 0, AOptions.Lists.ElementSep) + FElements[i].GetAsSingleLineText(AOptions);
  if FActualLength > EffectiveMaxLen then
    Result := Result + IfThen(EffectiveMaxLen > 0, AOptions.Lists.ElementSep) + EllipsisSymbol_HORIZONTAL_ELLIPSIS;
  Result := Result + AOptions.Lists.RightDelim;
end;

function TAlgosimArray.GetElement(Index: Integer): TAlgosimObject;
begin
  if InRange(Index, 1, FActualLength) then
    Result := FElements[Index - 1]
  else if InRange(-Index, 1, FActualLength) then
    Result := FElements[FActualLength + Index]
  else
    raise EArrayException.CreateFmt(SIndexOutOfBounds, [Index]);
end;

function TAlgosimArray.GetElementCount: Integer;
begin
  Result := FActualLength;
end;

function TAlgosimArray.GetMaxLen: Integer;
begin
  Result := FMaxLen;
end;

function TAlgosimArray.GetMemorySize: UInt64;
var
  i: Integer;
begin
  Result := Length(FElements) * sizeof(TAlgosimObject);
  for i := 0 to FActualLength - 1 do
    Inc(Result, FElements[i].MemorySize);
end;

function TAlgosimArray.SortClassGetHashCode: Integer;
var
  Hashes: TArray<Integer>;
  i: Integer;
begin
  SetLength(Hashes, ElementCount + 1);
  Hashes[0] := ElementCount;
  for i := 1 to ElementCount do
    Hashes[i] := Elements[i].GetHashCode;
  Result := CombineHashes(Hashes);
end;

function TAlgosimArray.GetCapacity: Integer;
begin
  Result := Length(FElements)
end;

procedure TAlgosimArray.Group(AGroupSize: Integer);
var
  q, r, GroupCount: Integer;
  GroupIndex: Integer;
  grp: TAlgosimArray;
  i: Integer;
begin

  if AGroupSize < 1 then
    raise EArrayException.CreateFmt(SInvalidGroupSize, [AGroupSize]);

  q := ElementCount div AGroupSize;
  r := ElementCount mod AGroupSize;

  GroupCount := q;
  if r <> 0 then
    Inc(GroupCount);

  for GroupIndex := 0 to GroupCount - 1 do
  begin
    grp := TAlgosimArray.Create;
    grp.ElementCount := AGroupSize;
    if GroupIndex < q then
      Move(FElements[GroupIndex * AGroupSize], grp.FElements[0], AGroupSize * sizeof(TAlgosimObject))
    else
    begin
      Move(FElements[GroupIndex * AGroupSize], grp.FElements[0], r * sizeof(TAlgosimObject));
      for i := r to AGroupSize - 1 do
        grp.FElements[i] := ASO(null);
    end;
    FElements[GroupIndex] := grp;
  end;

  ElementCount := GroupCount;

end;

procedure TAlgosimArray.Grow;
begin
  if Length(FElements) < 10 then
    SetLength(FElements, 20)
  else
    SetLength(FElements, Ceil(1.5 * Length(FElements)));
end;

procedure TAlgosimArray.Insert(AIndex: Integer; AElement: TAlgosimObject);
var
  i: Integer;
  PhysIndex: Integer;
begin

  if not Assigned(AElement) then
    raise EArrayException.Create(SArrayCannotInsertNil);

  try

    PhysIndex := GetPhysIndex0(AIndex, FActualLength + 1);

    if FActualLength = Length(FElements) then
      Grow;

    for i := FActualLength downto PhysIndex + 1 do
      FElements[i] := FElements[i - 1];

  except
    AElement.Free;
    raise;
  end;

  FElements[PhysIndex] := AElement;
  Inc(FActualLength);

end;

function TAlgosimArray.Is2D(out Size: TSize): Boolean;
var
  i: Integer;
begin
  Size := TSize.Create(0, 0);
  if ElementCount = 0 then
    Exit(True);
  Size.cy := ElementCount;
  if Elements[1] is TAlgosimArray then
    Size.cx := Elements[1].ElementCount
  else
    Exit(False);
  for i := 2 to ElementCount do
    if not (Elements[i] is TAlgosimArray) or (Elements[i].ElementCount <> Size.cx) then
      Exit(False);
  Result := True;
end;

procedure TAlgosimArray.Reverse;
begin
  TrimExcess;
  TReverser<TAlgosimObject>.Reverse(FElements);
end;

function TAlgosimArray.RotLeft(N: Integer): TAlgosimObject;
var
  arr: TAlgosimArray;
  i: Integer;
begin

  if N < 0 then
    Exit(RotRight(-N));

  arr := TAlgosimArray.Create;
  try
    arr.ElementCount := ElementCount;
    for i := 0 to ElementCount - 1 do
      arr.FElements[i] := FElements[(i + N) mod ElementCount].Clone;
  except
    arr.Free;
    raise;
  end;

  Result := arr;

end;

function TAlgosimArray.RotRight(N: Integer): TAlgosimObject;
var
  arr: TAlgosimArray;
  i: Integer;
begin

  if N < 0 then
    Exit(RotLeft(-N));

  arr := TAlgosimArray.Create;
  try
    arr.ElementCount := ElementCount;
    for i := 0 to ElementCount - 1 do
      arr.FElements[(i + N) mod ElementCount] := FElements[i].Clone;
  except
    arr.Free;
    raise;
  end;

  Result := arr;

end;

function TAlgosimArray.CollapseSequences: TAlgosimArray;
var
  i: Integer;
  c: TAlgosimInteger;
  CurObj, PrevObj: TAlgosimObject;
begin
  c := nil; // To make compiler happy
  Result := TAlgosimArray.Create;
  try
    Result.Capacity := ElementCount;
    PrevObj := nil;
    for i := 1 to ElementCount do
    begin
      CurObj := Elements[i];
      if CurObj.Equals(PrevObj) then
        Inc(c.FValue)
      else
      begin
        c := ASOInt(1);
        try
          Result.Add(
            ASO(
              [
                CurObj.Clone,
                c
              ]
            )
          )
        except
          c.Free;
          raise;
        end;
      end;
      PrevObj := CurObj;
    end;
    Result.TrimExcess;
  except
    Result.Free;
    raise;
  end;
end;

function TAlgosimArray.CollapseSequencesEps(const Epsilon: TASR): TAlgosimArray;
var
  i: Integer;
  c: TAlgosimInteger;
  CurObj, PrevObj: TAlgosimObject;
begin
  c := nil; // To make compiler happy
  Result := TAlgosimArray.Create;
  try
    Result.Capacity := ElementCount;
    PrevObj := nil;
    for i := 1 to ElementCount do
    begin
      CurObj := Elements[i];
      if (i = 1) or not SameASO(CurObj, PrevObj, Epsilon) then
      begin
        PrevObj := CurObj;
        c := ASOInt(1);
        try
          Result.Add(
            ASO(
              [
                CurObj.Clone,
                c
              ]
            )
          )
        except
          c.Free;
          raise;
        end;
      end
      else
        Inc(c.FValue);
    end;
    Result.TrimExcess;
  except
    Result.Free;
    raise;
  end;
end;

class function TAlgosimArray.Concat(const Args: array of TAlgosimArray): TAlgosimArray;
var
  len: Integer;
  i: Integer;
  j: Integer;
begin

  len := 0;
  for i := 0 to High(Args) do
    Inc(len, Args[i].ElementCount);

  Result := TAlgosimArray.Create;
  try
    Result.Capacity := len;
    for i := 0 to High(Args) do
      for j := 1 to Args[i].ElementCount do
        Result.Add(Args[i].Release(j));
  except
    Result.Free;
    raise;
  end;

end;

function TAlgosimArray.AsArray: TArray<TAlgosimObject>;
begin
  SetLength(Result, FActualLength);
  if FActualLength > 0 then
    Move(FElements[0], Result[0], FActualLength * sizeof(TAlgosimObject));
end;

function TAlgosimArray.ExplainedOutput(const AOptions: TFormatOptions): string;
var
  OuterRes: string absolute Result;
  AllInts: Boolean;

  function IsEligibleForPrettyPrint: Boolean;
  var
    i: Integer;
  const
    N_LIM = 50;
    X_LIM = 18;
    Y_LIM = 18;
  begin
    if ElementCount > 10000 then
      Exit(False);
    if ElementsAre(TAlgosimInteger) then
    begin
      AllInts := True;
      Exit(True);
    end;
    if ElementCount > N_LIM then
      Exit(False);
    if not ElementsAre(TAlgosimNumericArray) then
      Exit(False);
    for i := 1 to ElementCount do
    begin
      if (Elements[i] is TAlgosimVector) and (Elements[i].ValueCount > AOptions.Vectors.VerticalUntil) then
        Exit(False);
      with Elements[i].PlanarExtent do
        if (cx > X_LIM) or (cy > Y_LIM) then
          Exit(False);
    end;
    Result := True;
  end;

  function DoPrettyPrint: Boolean;
  var
    Parts: TArray<string>;
    i: Integer;
    EffectiveMaxLen: Integer;
  begin
    if FMaxLen > 0 then
      EffectiveMaxLen := FMaxLen
    else
      EffectiveMaxLen := AOptions.Lists.MaxLen;
    SetLength(Parts, Min(ElementCount, EffectiveMaxLen));
    for i := 1 to Length(Parts) do
      Parts[i - 1] := Elements[i].GetAsMultilineText(AOptions);
    OuterRes := BlockCombineStrings(Parts, IfThen(AllInts, 80, 100), 2);
    if ElementCount > Length(Parts) then
      OuterRes := OuterRes + EllipsisSymbol_HORIZONTAL_ELLIPSIS;
    Result := True;
  end;

begin

  AllInts := False;
  if not IsEligibleForPrettyPrint or not DoPrettyPrint then
    Result := GetAsMultilineText(AOptions);

end;

procedure TAlgosimArray.ExtendWith(AElement: TAlgosimObject);
var
  NewArr: TAlgosimArray;
  i: Integer;
begin
  try
    if not (AElement is TAlgosimArray) then
      raise EAlgosimObjectException.CreateFmt(SObjectNoContainer, [AElement.ClassTypeName]);
    NewArr := TAlgosimArray(AElement);
    if FActualLength + NewArr.ElementCount > Capacity then
      Capacity := FActualLength + NewArr.ElementCount;
    NewArr.OwnsObjects := False;
    for i := FActualLength to FActualLength + NewArr.ElementCount - 1 do {noexcept from here}
      FElements[i] := NewArr.FElements[i - FActualLength];
    Inc(FActualLength, NewArr.ElementCount);
  finally
    AElement.Free;
  end;
end;

function TAlgosimArray.Frequencies: TAlgosimArray;
var
  Dict: TDictionary<TAlgosimObject, TAlgosimInteger>;
  C: TAlgosimInteger;
  i: Integer;
  j: Integer;
label
  NextItem;
begin

  if not Debug_DisableASOHashing then
  begin

    Result := TAlgosimArray.Create;
    try
      Result.Capacity := ElementCount;
      Dict := TDictionary<TAlgosimObject, TAlgosimInteger>.Create(ValEqualityComparer);
      try
        for i := 1 to ElementCount do
        begin
          if Dict.TryGetValue(Elements[i], C) then
            Inc(C.FValue)
          else
          begin
            C := ASOInt(1);
            Result.AddElement(
              ASO(
                [
                  Elements[i].Clone,
                  C
                ]
              )
            );
            Dict.Add(Elements[i], C);
          end;
        end;
      finally
        Dict.Free;
      end;
      Result.TrimExcess;
    except
      Result.Free;
      raise;
    end;

  end
  else
  begin

    Result := TAlgosimArray.Create;
    try
      Result.Capacity := ElementCount;
      for i := 1 to ElementCount do
      begin
        for j := 1 to Result.ElementCount do
          if Result.Elements[j].Elements[1].Equals(Elements[i]) then
          begin
            Inc((Result.Elements[j].Elements[2] as TAlgosimInteger).FValue);
            goto NextItem;
          end;
          Result.AddElement(
            ASO(
              [
                Elements[i].Clone,
                ASOInt(1)
              ]
            )
          );
        NextItem:
      end;
      Result.TrimExcess;
    except
      Result.Free;
      raise;
    end;

  end;

end;

function TAlgosimArray.FrequenciesEps(const Epsilon: TASR): TAlgosimArray;
var
  i: Integer;
  j: Integer;
label
  NextItem;
begin

  Result := TAlgosimArray.Create;
  try
    Result.Capacity := ElementCount;
    for i := 1 to ElementCount do
    begin
      for j := 1 to Result.ElementCount do
        if SameASO(Result.Elements[j].Elements[1], Elements[i], Epsilon) then
        begin
          Inc((Result.Elements[j].Elements[2] as TAlgosimInteger).FValue);
          goto NextItem;
        end;
        Result.AddElement(
          ASO(
            [
              Elements[i].Clone,
              ASOInt(1)
            ]
          )
        );
      NextItem:
    end;
    Result.TrimExcess;
  except
    Result.Free;
    raise;
  end;

end;

procedure TAlgosimArray.SetCapacity(const Value: Integer);
begin
  SetLength(FElements, Value);
  FActualLength := min(FActualLength, Value);
end;

procedure TAlgosimArray.SetElement(Index: Integer; Value: TAlgosimObject);
var
  PhysIndex: Integer;
begin

  if not Assigned(Value) then
    Exit;

  try
    PhysIndex := GetPhysIndex0(Index, FActualLength);
  except
    Value.Free;
    raise;
  end;

  if FElements[PhysIndex] = Value then
    Exit;

  FreeAndNil(FElements[PhysIndex]);
  FElements[PhysIndex] := Value;

end;

procedure TAlgosimArray.SetElementCount(const AValue: Integer);
begin
  SetLength(FElements, AValue);
  FActualLength := AValue;
end;

procedure TAlgosimArray.SetMaxLen(AValue: Integer);
begin
  FMaxLen := AValue;
end;

procedure TAlgosimArray.Shuffle;
begin
  TrimExcess;
  TShuffler<TAlgosimObject>.Shuffle(FElements);
end;

procedure TAlgosimArray.Sort;
begin
  TrimExcess;
  TArray.Sort<TAlgosimObject>(FElements,
    TComparer<TAlgosimObject>.Construct(
      function(const Left, Right: TAlgosimObject): Integer
      begin
        Result := CompareASO(Left, Right)
      end
    )
  );
end;

procedure TAlgosimArray.Sort(AComparer: IComparer<TASR>);
begin
  TrimExcess;
  TArray.Sort<TAlgosimObject>(FElements,
    TComparer<TAlgosimObject>.Construct(
      function(const Left, Right: TAlgosimObject): Integer
      begin
        Result := AComparer.Compare(Left.ToASR, Right.ToASR)
      end
    )
  );
end;

procedure TAlgosimArray.Sort(AComparer: IComparer<TASC>);
begin
  TrimExcess;
  TArray.Sort<TAlgosimObject>(FElements,
    TComparer<TAlgosimObject>.Construct(
      function(const Left, Right: TAlgosimObject): Integer
      begin
        Result := AComparer.Compare(Left.ToASC, Right.ToASC)
      end
    )
  );
end;

class function TAlgosimArray.SortClass: TSortClass;
begin
  Result := SORTCLASS_ARRAY;
end;

class function TAlgosimArray.SortClassCompare(const Left,
  Right: TAlgosimObject): Integer;
var
  i: Integer;
begin
  Result := CompareValue(Left.ElementCount, Right.ElementCount);
  if Result = 0 then
    for i := 1 to Left.ElementCount do
    begin
      Result := CompareASO(Left.Elements[i], Right.Elements[i]);
      if Result <> EqualsValue then
        Exit;
    end;
end;

class function TAlgosimArray.SortClassSameObject(const Left,
  Right: TAlgosimObject; const AEpsilon: TASR): Boolean;
var
  i: Integer;
begin
  Result := Left.ElementCount = Right.ElementCount;
  if Result then
    for i := 1 to Left.ElementCount do
      if not SameASO(Left.Elements[i], Right.Elements[i], AEpsilon) then
        Exit(False);
end;

procedure TAlgosimArray.Sort(AComparer: IComparer<TAlgosimObject>);
begin
  TrimExcess;
  TArray.Sort<TAlgosimObject>(FElements, AComparer);
end;

procedure TAlgosimArray.Swap(Index1, Index2: Integer);
var
  PhysIdx1, PhysIdx2: Integer;
begin

  PhysIdx1 := GetPhysIndex0(Index1, FActualLength);
  PhysIdx2 := GetPhysIndex0(Index2, FActualLength);

  if PhysIdx1 = PhysIdx2 then
    Exit;

  TSwapper<TAlgosimObject>.Swap(FElements[PhysIdx1], FElements[PhysIdx2])

end;

procedure TAlgosimArray.Truncate(ANewLength: Integer);
var
  i: Integer;
begin

  if ANewLength < 0 then
    raise EArrayException.Create(SNewLengthMustBeNonNegative);

  if ANewLength >= FActualLength then
    Exit;

  for i := FActualLength - 1 downto ANewLength do
    FreeAndNil(FElements[i]);

  SetLength(FElements, ANewLength);
  FActualLength := ANewLength;

end;

function TAlgosimArray.Release(Index: Integer): TAlgosimObject;
begin
  if InRange(Index, 1, FActualLength) then
  begin
    Result := FElements[Index - 1];
    FElements[Index - 1] := nil;
  end
  else if InRange(-Index, 1, FActualLength) then
  begin
    Result := FElements[FActualLength + Index];
    FElements[FActualLength + Index] := nil;
  end
  else
    raise EArrayException.CreateFmt(SIndexOutOfBounds, [Index]);
end;

procedure TAlgosimArray.Remove(const AIndices: array of Integer);
begin
  TrimExcess;
  TObjRemover<TAlgosimObject>.Remove(FElements, TranslatedIntSequence(AIndices));
  FActualLength := Length(FElements);
end;

function TAlgosimArray.RemoveAdjacentDuplicates: TAlgosimObject;
var
  i: Integer;
begin

  Result := TAlgosimArray.Create;
  try
    Result.Capacity := ElementCount;
    for i := 1 to ElementCount do
      if (i = 1) or not Elements[i].Equals(Elements[Pred(i)]) then
        Result.AddElement(Elements[i].Clone);
  except
    Result.Free;
    raise;
  end;

end;

function TAlgosimArray.RemoveAdjacentDuplicatesEps(
  const Epsilon: TASR): TAlgosimObject;
var
  i: Integer;
begin

  Result := TAlgosimArray.Create;
  try
    Result.Capacity := ElementCount;
    for i := 1 to ElementCount do
      if (i = 1) or not SameASO(Elements[i], Result.Elements[Result.ElementCount], Epsilon) then
        Result.AddElement(Elements[i].Clone);
  except
    Result.Free;
    raise;
  end;

end;

function TAlgosimArray.RemoveDuplicates: TAlgosimObject;
var
  i: Integer;
  Dict: TDictionary<TAlgosimObject, Pointer>;
begin

  if not Debug_DisableASOHashing then
  begin

    Result := TAlgosimArray.Create;
    try
      Result.Capacity := ElementCount;
      Dict := TDictionary<TAlgosimObject, Pointer>.Create(ValEqualityComparer);
      try
        for i := 1 to ElementCount do
          if not Dict.ContainsKey(Elements[i]) then
          begin
            Result.AddElement(Elements[i].Clone);
            Dict.Add(Elements[i], nil);
          end;
      finally
        Dict.Free;
      end;
    except
      Result.Free;
      raise;
    end;

  end
  else
  begin

    // O(n^2)
    Result := TAlgosimArray.Create;
    try
      Result.Capacity := ElementCount;
      for i := 1 to ElementCount do
        if not Result.Contains(Elements[i]) then
          Result.AddElement(Elements[i].Clone);
    except
      Result.Free;
      raise;
    end;

  end;

end;

function TAlgosimArray.RemoveDuplicatesEps(const Epsilon: TASR): TAlgosimObject;
var
  i: Integer;
begin

  // Always applicable O(n^2) algorithm
  Result := TAlgosimArray.Create;
  try
    Result.Capacity := ElementCount;
    for i := 1 to ElementCount do
      if not Result.ContainsEps(Elements[i], Epsilon) then
        Result.AddElement(Elements[i].Clone);
  except
    Result.Free;
    raise;
  end;

end;

procedure TAlgosimArray.RemoveFirst(N: Integer);
begin
  TrimExcess;
  TObjRemover<TAlgosimObject>.RemoveFirst(FElements, N);
  FActualLength := Length(FElements);
end;

function TAlgosimArray.Zip(const AArrays: array of TAlgosimArray): TAlgosimArray;
var
  ObjectArray: array of TAlgosimObject;
begin
  SetLength(ObjectArray, Length(AArrays));
  if Length(AArrays) > 0 then
    Move(AArrays[0], ObjectArray[0], Length(AArrays) * sizeof(Pointer));
  with TAlgosimArray.CreateWithValue(ObjectArray) do
    try
      Result := Transpose;
    finally
      Free;
    end;
end;

function TAlgosimArray.ToComplexMatrix: TComplexMatrix;
var
  S: TSize;
  i: Integer;
  j: Integer;
begin
  if not Is2D(S) then
    raise Exception.Create(SCannotConvertNon2DListToMatrix);
  Result := TRealMatrix.CreateUninitialized(S);
  for i := 1 to ElementCount do
    for j := 1 to Elements[i].ElementCount do
      Result[i - 1, j - 1] := Elements[i].Elements[j].ToASC;
end;

function TAlgosimArray.ToRealMatrix: TRealMatrix;
var
  S: TSize;
  i: Integer;
  j: Integer;
begin
  if not Is2D(S) then
    raise Exception.Create(SCannotConvertNon2DListToMatrix);
  Result := TRealMatrix.CreateUninitialized(S);
  for i := 1 to ElementCount do
    for j := 1 to Elements[i].ElementCount do
      Result[i - 1, j - 1] := Elements[i].Elements[j].ToASR;
end;

function TAlgosimArray.ToSpeech: string;
var
  i: Integer;
begin
  if ElementCount = 0 then
    Exit('Empty list.');
  Result := 'Begin list of length ' + ElementCount.ToString + '.'#13#10;
  for i := 1 to ElementCount do
    Result := Result + 'Element ' + i.ToString + ': ' + Elements[i].ToSpeech + '.'#13#10;
  Result := Result + 'End list.';
end;

function TAlgosimArray.ToString: string;
begin
  Result := GetAsMultilineText(ExchangeFormOptions);
end;

function TAlgosimArray.ToStringArray: TArray<string>;
var
  i: Integer;
begin
  SetLength(Result, ElementCount);
  for i := 1 to ElementCount do
    Result[i - 1] := Elements[i].ToString;
end;

function TAlgosimArray.ToTable: TASTable;
var
  S: TSize;
  i: Integer;
  j: Integer;
begin
  if not Is2D(S) then
    Exit(inherited);
  Result := TASTable.Create;
  try
    Result.SetSize(S.Width, S.Height);
    for i := 1 to ElementCount do
      for j := 1 to Elements[i].ElementCount do
        Result.Strings[Point(j - 1, i - 1)] := Elements[i].Elements[j].GetAsSingleLineText(DefaultFormatOptions);
  except
    Result.Free;
    raise;
  end;
end;

function TAlgosimArray.Transpose: TAlgosimArray;

  procedure Fail;
  begin
    raise EArrayException.Create(SCannotTransposeNonMatrix);
  end;

var
  i, j: Integer;
begin

  if ElementCount = 0 then
    Exit(TAlgosimArray.Create(Self));

  if not ElementsAre(TAlgosimArray) then
    Fail;

  for i := 1 to ElementCount - 1 do
    if TAlgosimArray(FElements[i]).ElementCount <> TAlgosimArray(FElements[0]).ElementCount then
      Fail;

  Result := TAlgosimArray.Create;
  try
    Result.ElementCount := TAlgosimArray(FElements[0]).ElementCount;
    for i := 0 to TAlgosimArray(FElements[0]).ElementCount - 1 do
    begin
      Result.FElements[i] := TAlgosimArray.Create;
      TAlgosimArray(Result.FElements[i]).ElementCount := ElementCount;
      for j := 0 to ElementCount - 1 do
        TAlgosimArray(Result.FElements[i]).FElements[j] := TAlgosimArray(FElements[j]).FElements[i].Clone;
    end;
  except
    Result.Free;
    raise;
  end;

end;

procedure TAlgosimArray.TrimExcess;
begin
  SetLength(FElements, FActualLength);
end;

constructor TAlgosimArray.CreateWithValue(const AElements: array of Char);
var
  i: Integer;
begin
  CreateUninitialized(Length(AElements));
  for i := 0 to High(FElements) do
    FElements[i] := ASO(AElements[i]);
end;

{ TAlgosimStructure }

function TAlgosimStructure.Add(const AName: string; const AValue: TAlgosimObject;
  ASkipUniquenessCheck: Boolean): Integer;
begin

  try

    RequestTypeChange;

    if not ASkipUniquenessCheck and HasMember(AName) then
      raise EStructureException.CreateFmt(SStructAlreadyHasMember, [AName]);

    if not IsValidMemberName(AName) then
      raise EStructureException.CreateFmt(SStructInvalidMemberName, [AName]);

    if not Assigned(AValue) then
      raise EStructureException.Create(SMemberValueIsNil);

  except
    AValue.Free;
    raise;
  end;

  Result := FMembers.Add(TMemberObj.Create(AName, AValue));

end;

procedure TAlgosimStructure.RequestTypeChange;
begin

end;

function TAlgosimStructure.AddElement(const AElement: TAlgosimObject): Boolean;
begin
  Add(Format('Member%d', [MemberCount + 1]), AElement);
  Result := True;
end;

procedure TAlgosimStructure.Clear;
begin
  RequestTypeChange;
  FMembers.Clear;
end;

constructor TAlgosimStructure.Create;
begin
  inherited;
  FMembers := TObjectList<TMemberObj>.Create;
end;

constructor TAlgosimStructure.Create(AObject: TAlgosimObject);
var
  OldStruct: TAlgosimStructure;
  i: Integer;
begin
  if AObject is TAlgosimStructure then
  begin
    Create;
    OldStruct := TAlgosimStructure(AObject);
    for i := 0 to OldStruct.MemberCount - 1 do
      Add(OldStruct.FMembers[i].Name, OldStruct.FMembers[i].Value.Clone);
  end
  else
    NoCopyConstr(AObject);
end;

constructor TAlgosimStructure.CreateWithValue(const AMembers: array of TMemberRef);
var
  i, FirstOwned: Integer;
begin

  FirstOwned := 0;

  try

    Create;

    for i := 0 to High(AMembers) do
    begin
      FirstOwned := i + 1;
      Add(AMembers[i].Name, AMembers[i].Value);
    end;

  except
    for i := FirstOwned to High(AMembers) do
      AMembers[i].Value.Free;
    raise;
  end;

end;

constructor TAlgosimStructure.CreateWithValue(const ANames: array of string;
  const AValues: array of TAlgosimObject);
var
  i, FirstOwned: Integer;
begin

  FirstOwned := 0;

  try

    if Length(ANames) <> Length(AValues) then
      raise EStructureException.Create(SStructNameValCountMismatch);

    Create;
    for i := 0 to High(ANames) do
    begin
      FirstOwned := i + 1;
      Add(ANames[i], AValues[i]);
    end;

  except
    for i := FirstOwned to High(AValues) do
      AValues[i].Free;
    raise;
  end;

end;

procedure TAlgosimStructure.Delete(AIndex: Integer);
var
  PhysIndex: Integer;
begin
  RequestTypeChange;
  PhysIndex := GetPhysIndex0(AIndex, FMembers.Count);
  FMembers.Delete(PhysIndex);
end;

procedure TAlgosimStructure.Delete(const AName: string);
var
  i: Integer;
begin
  RequestTypeChange;
  for i := 0 to FMembers.Count - 1 do
    if FMembers[i].Name = AName then
    begin
      FMembers.Delete(i);
      Exit;
    end;
  raise EStructureException.CreateFmt(SStructNoMemberFound, [AName]);
end;

destructor TAlgosimStructure.Destroy;
begin
  FMembers.Free;
  inherited;
end;

function TAlgosimStructure.Equals(Obj: TObject): Boolean;
var
  i: Integer;
begin
  if not (Obj is TAlgosimStructure) then
    Exit(False);
  if TAlgosimStructure(Obj).MemberCount <> MemberCount then
    Exit(False);
  for i := 1 to MemberCount do
    if not ((Members[i].Name = TAlgosimStructure(Obj).Members[i].Name) and
      Members[i].Value.Equals(TAlgosimStructure(Obj).Members[i].Value))
    then
      Exit(False);
  Result := True;
end;

function TAlgosimStructure.Filter(APredicate: TASOPredicate): TAlgosimObject;
var
  i: Integer;
begin
  Result := TAlgosimStructure.Create;
  try
    for i := 0 to FMembers.Count - 1 do
      if APredicate(FMembers[i].Value) then
        TAlgosimStructure(Result).Add(FMembers[i].Name, FMembers[i].Value.Clone, True);
  except
    Result.Free;
    raise;
  end;
end;

function TAlgosimStructure.GetAsMultilineText(const AOptions: TFormatOptions): string;
begin
  Result := GetPrefixedMultilineText('', AOptions);
end;

function TAlgosimStructure.GetAsSingleLineText(const AOptions: TFormatOptions): string;

  function WriteMember(Index: Integer): string;
  begin
    if AOptions.Strings.Quoted then
      Result := '"' + FMembers[Index].Name + '"' + AOptions.Structures.ValueSep +
        FMembers[Index].Value.GetAsSingleLineText(AOptions)
    else
      Result := FMembers[Index].Name + AOptions.Structures.ValueSep +
        FMembers[Index].Value.GetAsSingleLineText(AOptions);
  end;

var
  i: Integer;
begin
  Result := AOptions.Structures.LeftDelim;
  if FMembers.Count > 0 then
    Result := Result + WriteMember(0);
  for i := 1 to FMembers.Count - 1 do
    Result := Result + AOptions.Structures.MemberSep + WriteMember(i);
  Result := Result + AOptions.Structures.RightDelim;
end;

function TAlgosimStructure.GetElement(Index: Integer): TAlgosimObject;
begin
  Result := GetValueByIndex(Index);
end;

function TAlgosimStructure.GetElementCount: Integer;
begin
  Result := FMembers.Count;
end;

function TAlgosimStructure.SortClassGetHashCode: Integer;
var
  Hashes: TArray<Integer>;
  i: Integer;
begin
  SetLength(Hashes, MemberCount + 1);
  Hashes[0] := MemberCount;
  for i := 1 to MemberCount do
  begin
    Hashes[i] := CombineHashes(
      [
        THashBobJenkins.GetHashValue(Members[i].Name),
        Members[i].Value.GetHashCode
      ]
    );
  end;
  Result := CombineHashes(Hashes);
end;

function TAlgosimStructure.GetMember(Index: Integer): TMemberRef;
begin

  if InRange(Index, 1, FMembers.Count) then
    Result := FMembers[Index - 1].Ref
  else if InRange(-Index, 1, FMembers.Count) then
    Result := FMembers[FMembers.Count + Index].Ref
  else
    raise EStructureException.CreateFmt(SIndexOutOfBounds, [Index]);

end;

function TAlgosimStructure.GetMemberCount: Integer;
begin
  Result := FMembers.Count;
end;

function TAlgosimStructure.GetMemorySize: UInt64;
var
  i: Integer;
begin
  Result := 0;
  for i := 0 to FMembers.Count - 1 do
    Inc(Result, FMembers[i].Value.MemorySize);
end;

function TAlgosimStructure.GetPrefixedMultilineText(
  const APrefix: string; const AOptions: TFormatOptions): string;
var
  i: Integer;
  Prefix: string;
begin
  if APrefix = '' then
    Prefix := ''
  else
    Prefix := APrefix + AOptions.Structures.AccessSep;
  Result := '';
  for i := 0 to FMembers.Count - 1 do
    if FMembers[i].Value is TAlgosimStructure then
      Result := Result + IfThen(i > 0, sLineBreak) + TAlgosimStructure(FMembers[i].Value).GetPrefixedMultilineText(Prefix + FMembers[i].Name, AOptions)
    else
      Result := Result + IfThen(i > 0, sLineBreak) + Prefix + FMembers[i].Name + AOptions.Structures.ValueSep + FMembers[i].Value.GetAsSingleLineText(AOptions);
end;

function TAlgosimStructure.TryGetSubscriptedValue(
  ASubscript: TSubscript; out AValue: TAlgosimObject): Boolean;
begin
  if (ASubscript.Kind = skIndexObject) and (ASubscript.Obj is TAlgosimString) then
  begin
    AValue := Values[ASubscript.Obj.ToString].Clone;
    Result := True;
  end
  else if ASubscript.Kind = skIdentifier then
  begin
    AValue := Values[ASubscript.Ident].Clone;
    Result := True;
  end
  else
    Result := inherited TryGetSubscriptedValue(ASubscript, AValue){e.g. integer subscript};
end;

procedure TAlgosimStructure.Truncate(ANewLength: Integer);
begin

  if ANewLength < 0 then
    raise EArrayException.Create(SNewLengthMustBeNonNegative);

  if ANewLength >= FMembers.Count then
    Exit;

  FMembers.Count := ANewLength;

end;

function TAlgosimStructure.TryGetSubscriptedRef(
  ASubscript: TSubscript; out AValue: TAlgosimObject): Boolean;
begin
  if (ASubscript.Kind = skIndexObject) and (ASubscript.Obj is TAlgosimString) then
  begin
    AValue := Values[ASubscript.Obj.ToString];
    Result := True;
  end
  else if ASubscript.Kind = skIdentifier then
  begin
    AValue := Values[ASubscript.Ident];
    Result := True;
  end
  else
    Result := inherited TryGetSubscriptedRef(ASubscript, AValue){e.g. integer subscript};
end;

function TAlgosimStructure.GetValue(const AName: string): TAlgosimObject;
var
  i: Integer;
begin
  for i := 0 to FMembers.Count - 1 do
    if FMembers[i].Name = AName then
      Exit(FMembers[i].Value);
  raise EStructureException.CreateFmt(SStructNoMemberFound, [AName]);
end;

function TAlgosimStructure.GetValueByIndex(Index: Integer): TAlgosimObject;
begin
  Result := GetMember(Index).Value;
end;

function TAlgosimStructure.HasIntegerMember(const AName: string; out AValue: TASI): Boolean;
var
  index: Integer;
begin
  index := IndexOfName(AName);
  Result := (index <> 0) and FMembers[index].Value.TryToASI(AValue);
end;

function TAlgosimStructure.HasIntegerMember(const AName: string; out AValue: Integer): Boolean;
var
  index: Integer;
begin
  index := IndexOfName(AName);
  Result := (index <> 0) and FMembers[index].Value.TryToInt32(AValue);
end;

function TAlgosimStructure.HasMember(const AName: string): Boolean;
begin
  Result := IndexOfName(AName) <> 0;
end;

function TAlgosimStructure.IndexOfName(const AName: string): Integer;
var
  i: Integer;
begin
  for i := 0 to FMembers.Count - 1 do
    if FMembers[i].Name = AName then
      Exit(i + 1);
  Result := 0;
end;

procedure TAlgosimStructure.Insert(Index: Integer; const AName: string;
  const AValue: TAlgosimObject);
begin

  try

    RequestTypeChange;

    if not InRange(Index, 1, FMembers.Count + 1) then
      raise EStructureException.CreateFmt(SIndexOutOfBounds, [Index]);

    Dec(Index);

    if HasMember(AName) then
      raise EStructureException.CreateFmt(SStructAlreadyHasMember, [AName]);

  except
    AValue.Free;
    raise;
  end;

  FMembers.Insert(Index, TMemberObj.Create(AName, AValue));

end;

class function TAlgosimStructure.IsValidMemberName(const AName: string): Boolean;
begin
  Result := IsValidIdent(AName);
end;

procedure TAlgosimStructure.Release(Index: Integer);
var
  PhysIndex: Integer;
begin
  PhysIndex := GetPhysIndex0(Index, FMembers.Count);
  FMembers[PhysIndex].Value := nil;
end;

procedure TAlgosimStructure.Release(const AName: string);
var
  i: Integer;
begin
  for i := 0 to FMembers.Count - 1 do
    if FMembers[i].Name = AName then
    begin
      FMembers[i].Value := nil;
      Exit;
    end;
  raise EStructureException.CreateFmt(SStructNoMemberFound, [AName]);
end;

procedure TAlgosimStructure.RenameMember(const AOldName, ANewName: string);
var
  idx: Integer;
begin

  if AOldName = ANewName then
    Exit;

  RequestTypeChange;

  idx := IndexOfName(AOldName);

  if idx = 0 then
    raise EStructureException.CreateFmt(SStructNoMemberFound, [AOldName]);

  if HasMember(ANewName) then
    raise EStructureException.CreateFmt(SStructAlreadyHasMember, [ANewName]);

  if not IsValidMemberName(ANewName) then
    raise EStructureException.CreateFmt(SStructInvalidMemberName, [ANewName]);

  Dec(idx);

  FMembers[idx].Name := ANewName;

end;

procedure TAlgosimStructure.SetElement(Index: Integer; AValue: TAlgosimObject);
begin
  SetValueByIndex(Index, AValue);
end;

procedure TAlgosimStructure.SetMember(Index: Integer; const AMember: TMemberRef);
var
  PhysIndex: Integer;
begin

  if not Assigned(AMember.Value) then
    raise EStructureException.Create(SMemberValueIsNil);

  try

    PhysIndex := GetPhysIndex0(Index, FMembers.Count);

    if FMembers[PhysIndex].Name <> AMember.Name then
      RequestTypeChange;

    if not IsValidMemberName(AMember.Name) then
      raise EStructureException.CreateFmt(SStructInvalidMemberName, [AMember.Name]);

  except
    AMember.Value.Free;
    raise;
  end;

  FMembers[PhysIndex] := TMemberObj.Create(AMember);

end;

procedure TAlgosimStructure.SetSubscript(ASubscript: TSubscript;
  AValue: TAlgosimObject);
begin
  if ASubscript.Kind = skIdentifier then
    Values[ASubscript.Ident] := AValue
  else if (ASubscript.Kind = skIndexObject) and (ASubscript.Obj is TAlgosimString) then
    Values[ASubscript.Obj.ToString] := AValue
  else
    inherited;
end;

procedure TAlgosimStructure.SetValue(const AName: string;
  const AValue: TAlgosimObject);
var
  i: Integer;
begin
  for i := 0 to FMembers.Count - 1 do {noexcept}
    if FMembers[i].Name = AName then
    begin
      TObjReplacer<TAlgosimObject>.Replace(FMembers[i].Value, AValue);
      Exit;
    end;
  Add(AName, AValue); {frees on exception}
end;

procedure TAlgosimStructure.SetValueByIndex(AIndex: Integer;
  const AValue: TAlgosimObject);
var
  PhysIndex: Integer;
begin

  if not Assigned(AValue) then
    raise EStructureException.Create(SMemberValueIsNil);

  try
    PhysIndex := GetPhysIndex0(AIndex, FMembers.Count);
  except
    AValue.Free;
    raise;
  end;

  FMembers[PhysIndex].Value.Free;
  FMembers[PhysIndex].Value := AValue;

end;

class function TAlgosimStructure.SortClass: TSortClass;
begin
  Result := SORTCLASS_STRUCTURE;
end;

class function TAlgosimStructure.SortClassCompare(const Left,
  Right: TAlgosimObject): Integer;
var
  L, R: TAlgosimStructure;
  i: Integer;
begin
  L := Left as TAlgosimStructure;
  R := Right as TAlgosimStructure;
  Result := CompareValue(L.MemberCount, R.MemberCount);
  if Result <> 0 then
    Exit;
  for i := 1 to L.MemberCount do
  begin
    Result := CompareStr(L.Members[i].Name, R.Members[i].Name);
    if Result <> 0 then
      Exit;
  end;
  for i := 1 to L.MemberCount do
  begin
    Result := CompareASO(L.Members[i].Value, R.Members[i].Value);
    if Result <> 0 then
      Exit;
  end;
end;

class function TAlgosimStructure.SortClassSameObject(const Left,
  Right: TAlgosimObject; const AEpsilon: TASR): Boolean;
var
  L, R: TAlgosimStructure;
  i: Integer;
begin
  L := Left as TAlgosimStructure;
  R := Right as TAlgosimStructure;
  if L.MemberCount <> R.MemberCount then
    Exit(False);
  for i := 1 to L.MemberCount do
    if L.Members[i].Name <> R.Members[i].Name then
      Exit(False);
  for i := 1 to L.MemberCount do
    if not SameASO(L.Members[i].Value, R.Members[i].Value, AEpsilon) then
      Exit(False);
  Result := True;
end;

function TAlgosimStructure.ToColor: TRGB;
begin
  Result := ASOToColor(Self);
end;

function TAlgosimStructure.ToSpeech: string;
var
  i: Integer;
begin
  Result := 'Begin structure.'#13#10;
  for i := 0 to FMembers.Count - 1 do
    Result := Result + 'Member name: ' + FMembers[i].Name + '. Member value: ' + FMembers[i].Value.ToSpeech + '.'#13#10;
  Result := Result + 'End structure.';
end;

function TAlgosimStructure.ToString: string;
begin
  Result := GetAsMultilineText(ExchangeFormOptions);
end;

function TAlgosimStructure.ToStructType(const AName: string): TAlgosimStructureType;
begin
  Result := TAlgosimStructureType.Create(Self);
  Result.Name := AName;
end;

function TAlgosimStructure.ValidateAgainst(AStructureType: TAlgosimStructure): Boolean;
var
  i: Integer;
begin

  Result := False;

  if MemberCount <> AStructureType.MemberCount then
    Exit;

  for i := 1 to MemberCount do
    if (Members[i].Name <> AStructureType.Members[i].Name) then
      Exit
    else if AStructureType.Members[i].Value is TAlgosimStructure then
      if
        not (Members[i].Value is TAlgosimStructure)
          or
        not TAlgosimStructure(Members[i].Value).ValidateAgainst(TAlgosimStructure(AStructureType.Members[i].Value))
      then
        Exit;

  Result := True;

end;

{ TAlgosimStructureType }

function TAlgosimStructureType.New(
  const AMembers: array of TAlgosimStructure.TMemberRef): TAlgosimTypedStructure;
begin
  Result := TAlgosimTypedStructure.Create(Self, AMembers);
end;

function TAlgosimStructureType.New(
  const AValues: array of TAlgosimObject): TAlgosimTypedStructure;
begin
  Result := TAlgosimTypedStructure.Create(Self, AValues);
end;

constructor TAlgosimStructureType.Create(AObject: TAlgosimObject);
begin
  inherited;
  if AObject is TAlgosimStructureType then
    FName := TAlgosimStructureType(AObject).Name;
end;

function TAlgosimStructureType.New: TAlgosimTypedStructure;
begin
  Result := TAlgosimTypedStructure.Create(Self);
end;

{ TAlgosimTypedStructure }

constructor TAlgosimTypedStructure.Create(AObject: TAlgosimObject);
begin
  inherited;
  if AObject is TAlgosimTypedStructure then
    FStructureTypeName := TAlgosimTypedStructure(AObject).FStructureTypeName
  else if AObject is TAlgosimStructureType then
    FStructureTypeName := TAlgosimStructureType(AObject).Name;
end;

constructor TAlgosimTypedStructure.Create(AStructureType: TAlgosimStructureType;
  const AValues: array of TAlgosimObject);
var
  i: Integer;
begin
  try
    if Length(AValues) > AStructureType.MemberCount then
      raise EStructureException.CreateFmt(SStructTooManyMembers,
        [Length(AValues), AStructureType.Name, AStructureType.MemberCount]);
    Create(AStructureType);
    for i := 0 to High(AValues) do
    begin
      Self.FMembers[i].Value.Free;
      Self.FMembers[i].Value := AValues[i];
    end;
  except
    for i := 0 to High(AValues) do
      if (i >= FMembers.Count) or (FMembers[i].Value <> AValues[i]) then
        AValues[i].Free;
    raise;
  end;
end;

procedure TAlgosimTypedStructure.AfterConstruction;
begin
  inherited;
  FConstructed := True;
end;

constructor TAlgosimTypedStructure.Create(AStructureType: TAlgosimStructureType;
  const AMembers: array of TAlgosimStructure.TMemberRef);
var
  i: Integer;
  idx: Integer;
  j: Integer;
begin
  try
    Create(AStructureType);
    for i := 0 to High(AMembers) do
    begin
      idx := IndexOfName(AMembers[i].Name);
      if idx <> 0 then
      begin
        FMembers[idx-1].Value.Free;
        FMembers[idx-1].Value := AMembers[i].Value;
      end
      else
        raise EStructureException.CreateFmt(SStructMemberNotInType,
          [AStructureType.Name, AMembers[i].Name]);
    end;
  except
    for i := 0 to High(AMembers) do
    begin
      for j := 0 to FMembers.Count - 1 do
        if FMembers[j].Value = AMembers[i].Value then
          FMembers[j].Value := nil;
      AMembers[i].Value.Free;
    end;
    raise;
  end;
end;

function TAlgosimTypedStructure.GetTypeName: string;
begin
  Result := Format(SStructureOfType, [FStructureTypeName])
end;

procedure TAlgosimTypedStructure.RequestTypeChange;
begin
  inherited;
  if FConstructed then
    raise EStructureException.Create(SCannotAlterTypedStruct);
end;

{ TAlgosimSet }

constructor TAlgosimSet.Create;
begin
  inherited;
  FValue := TObjectDictionary<TAlgosimObject, pointer>.Create([doOwnsKeys]);
  FValue.OnKeyNotify := OnDictChanged;
end;

procedure TAlgosimSet.OnDictChanged(Sender: TObject; const Item: TAlgosimObject;
  Action: TCollectionNotification);
begin
  FValueArray := nil;
end;

class function TAlgosimSet.ProperSubset(U, V: TAlgosimSet): Boolean;
var
  Pair: TPair<TAlgosimObject, Pointer>;
begin
  if U.Cardinality >= V.Cardinality then
    Exit(False);
  for Pair in U.FValue do
    if not V.FValue.ContainsKey(Pair.Key) then
      Exit(False);
  Result := True;
end;

class function TAlgosimSet.SortClass: TSortClass;
begin
  Result := SORTCLASS_SET;
end;

class function TAlgosimSet.SortClassCompare(const Left,
  Right: TAlgosimObject): Integer;
begin
  raise EAlgosimObjectException.Create('Sets have no default order.');
end;

function TAlgosimSet.SortClassGetHashCode: Integer;
begin
  raise EAlgosimObjectException.Create('Sets have no default hasher.');
end;

// O(n log n). False neagtives possible.

//class function TAlgosimSet.SortClassSameObject(const Left,
//  Right: TAlgosimObject; const AEpsilon: Extended): Boolean;
//var
//  L, R: TAlgosimSet;
//  LArr, RArr: TArray<TAlgosimObject>;
//  i: Integer;
//begin
//  L := Left as TAlgosimSet;
//  R := Right as TAlgosimSet;
//  if L.Cardinality <> R.Cardinality then
//    Exit(False);
//  LArr := L.ToArray;
//  RArr := R.ToArray;
//  TArray.Sort<TAlgosimObject>(LArr, TAlgosimObject.Comparer);
//  TArray.Sort<TAlgosimObject>(RArr, TAlgosimObject.Comparer);
//  for i := 0 to High(LArr) do
//    if not SameASO(LArr[i], RArr[i], AEpsilon) then
//      Exit(False);
//  Result := True;
//end;

// O(n^2). False positives possible. (Might not even be subtle differences.)
// Would be improved if the reverse order test was added.

//class function TAlgosimSet.SortClassSameObject(const Left,
//  Right: TAlgosimObject; const AEpsilon: Extended): Boolean;
//var
//  L, R: TAlgosimSet;
//  LPair, RPair: TPair<TAlgosimObject, Pointer>;
//  Found: Boolean;
//begin
//  L := Left as TAlgosimSet;
//  R := Right as TAlgosimSet;
//  if L.Cardinality <> R.Cardinality then
//    Exit(False);
//  for LPair in L.FValue do
//  begin
//    Found := False;
//    for RPair in R.FValue do
//    begin
//      if SameASO(LPair.Key, RPair.Key, AEpsilon) then
//      begin
//        Found := True;
//        Break;
//      end;
//    end;
//    if not Found then
//      Exit(False);
//  end;
//  Result := True;
//end;

// Fast and always correct.

class function TAlgosimSet.SortClassSameObject(const Left,
  Right: TAlgosimObject; const AEpsilon: Extended): Boolean;
begin
  raise EAlgosimObjectException.Create('Sets cannot be compared with epsilons.');
end;

class function TAlgosimSet.Subset(U, V: TAlgosimSet): Boolean;
var
  Pair: TPair<TAlgosimObject, Pointer>;
begin
  if U.Cardinality > V.Cardinality then
    Exit(False);
  for Pair in U.FValue do
    if not V.FValue.ContainsKey(Pair.Key) then
      Exit(False);
  Result := True;
end;

class function TAlgosimSet.SymmetricDifference(U, V: TAlgosimSet): TAlgosimSet;
var
  Pair: TPair<TAlgosimObject, Pointer>;
begin
  Result := TAlgosimSet.Create;
  try
    for Pair in U.FValue do
      if not V.FValue.ContainsKey(Pair.Key) then
        Result.AddElement(Pair.Key.Clone);
    for Pair in V.FValue do
      if not U.FValue.ContainsKey(Pair.Key) then
        Result.AddElement(Pair.Key.Clone);
  except
    Result.Free;
    raise;
  end;
end;


class function TAlgosimSet.Intersection(U, V: TAlgosimSet): TAlgosimSet;
var
  Pair: TPair<TAlgosimObject, Pointer>;
begin
  Result := TAlgosimSet.Create;
  try
    for Pair in U.FValue do
      if V.FValue.ContainsKey(Pair.Key) then
        Result.AddElement(Pair.Key.Clone);
  except
    Result.Free;
    raise;
  end;
end;

class function TAlgosimSet.Union(U, V: TAlgosimSet): TAlgosimSet;
var
  Pair: TPair<TAlgosimObject, Pointer>;
begin
  Result := TAlgosimSet.Create(U);
  try
    for Pair in V.FValue do
      Result.AddElement(Pair.Key.Clone);
  except
    Result.Free;
    raise;
  end;
end;

function TAlgosimSet.AddElement(const AElement: TAlgosimObject): Boolean;
var
  elm: TAlgosimObject;
  int: TASI;
  p: TPair<TAlgosimObject, Pointer>;
begin

  elm := AElement;
  try

    if (elm is TAlgosimRealNumber) or (elm is TAlgosimComplexNumber) then
    begin
      if AElement.TryToASI(int) then // since sets by default use hashing and not epsilons, we stronly dislike fuzz and prefer ints
        TObjReplacer<TAlgosimObject>.Replace(elm, ASOInt(int))
      else if Cardinality <= MAX_EPS_CARD then // if we can afford it, use epsilons
      begin
        for p in FValue do
          if (p.Key is TAlgosimNumber) and SameASO(elm, p.Key) then
            Exit(False);
        FValue.Add(elm, nil);
        elm := nil;
        Exit(True);
      end;
    end;

    Result := not FValue.ContainsKey(elm);
    if Result then
    begin
      FValue.Add(elm, nil);
      elm := nil;
    end;

  finally
    elm.Free;
  end;

end;

function TAlgosimSet.ToArray: TArray<TAlgosimObject>;
begin
  if FValueArray = nil then
    FValueArray := FValue.Keys.ToArray;
  Result := FValueArray;
end;

function TAlgosimSet.ToSpeech: string;
var
  i: Integer;
begin
  if Cardinality = 0 then
    Exit('Empty set.');
  if Cardinality = 1 then
    Exit('Singleton set: ' + Elements[1].ToSpeech + '.');
  Result := 'Begin set of size ' + ElementCount.ToString + '.'#13#10;
  for i := 1 to ElementCount do
    Result := Result + 'Element: ' + Elements[i].ToSpeech + '.'#13#10;
  Result := Result + 'End set.';
end;

function TAlgosimSet.ToString: string;
begin
  Result := GetAsMultilineText(ExchangeFormOptions);
end;

class function TAlgosimSet.CartesianProduct(U, V: TAlgosimSet): TAlgosimSet;
var
  UPair, VPair: TPair<TAlgosimObject, Pointer>;
begin
  Result := TAlgosimSet.Create;
  try
    for UPair in U.FValue do
      for VPair in V.FValue do
        Result.AddElement(ASO([UPair.Key.Clone, VPair.Key.Clone]));
  except
    Result.Free;
    raise;
  end;
end;

class function TAlgosimSet.CartesianProduct(
  const ASets: array of TAlgosimSet): TAlgosimSet;
var
  i, lengths: array of Integer;

  function IndexExists: Boolean;
  begin
    Result := i[High(i)] <= lengths[High(i)] - 1;
  end;

  procedure NextIndex;
  var
    j, k: Integer;
  begin
    for j := 0 to High(i) do
      if (i[j] < lengths[j] - 1) or (j = High(i)) then
      begin
        Inc(i[j]);
        for k := Pred(j) downto 0 do
          i[k] := 0;
        Exit;
      end;
  end;

var
  clones: array of TAlgosimObject;
  l: Integer;

begin
  Result := TAlgosimSet.Create;
  try
    if Length(ASets) = 0 then
      Exit;
    SetLength(lengths, Length(ASets));
    for l := 0 to High(ASets) do
    begin
      lengths[l] := ASets[l].ElementCount;
      if lengths[l] = 0 then
        Exit;
    end;
    SetLength(i, Length(ASets));
    while IndexExists do
    begin
      SetLength(clones, Length(ASets));
      try
        for l := 0 to High(ASets) do
          clones[l] := ASets[l].Elements[i[l]+1].Clone;
      except
        for l := 0 to High(clones) do
          clones[l].Free;
        raise;
      end;
      Result.AddElement(ASO(clones));
      NextIndex;
    end;
  except
    Result.Free;
    raise;
  end;
end;

procedure TAlgosimSet.Clear;
begin
  FValue.Clear;
end;

constructor TAlgosimSet.Create(AObject: TAlgosimObject);
var
  i: Integer;
begin
  Create;
  if AObject.IsContainer then
  begin
    Capacity := AObject.ValueCount;
    for i := 1 to AObject.ValueCount do
      AddElement(AObject.Values[i]);
  end
  else
    AddElement(AObject);
end;

destructor TAlgosimSet.Destroy;
begin
  FValue.Free;
  inherited;
end;

class function TAlgosimSet.Difference(U, V: TAlgosimSet): TAlgosimSet;
var
  Pair: TPair<TAlgosimObject, Pointer>;
begin
  Result := TAlgosimSet.Create;
  try
    for Pair in U.FValue do
      if not V.FValue.ContainsKey(Pair.Key) then
        Result.AddElement(Pair.Key.Clone);
  except
    Result.Free;
    raise;
  end;
end;

class function TAlgosimSet.ElementOf(x: TAlgosimObject;
  U: TAlgosimSet): Boolean;
var
  p: TPair<TAlgosimObject, Pointer>;
begin
  if U.Cardinality <= MAX_EPS_CARD then
  begin
    for p in U.FValue do
      if SameASO(x, p.Key) then
        Exit(True);
    Result := False;
  end
  else
    Result := U.FValue.ContainsKey(x);
end;

function TAlgosimSet.Equals(Obj: TObject): Boolean;
var
  &Set: TAlgosimSet absolute Obj;
begin
  Result := (Obj is TAlgosimSet) and (&Set.Cardinality = Self.Cardinality) and
    TAlgosimSet.Subset(&Set, Self);
end;

function TAlgosimSet.GetAsSingleLineText(const AOptions: TFormatOptions): string;
var
  card: Integer;
  ElemList: TList<string>;
  i: Integer;
  totlen: Integer;
  s: string;
  p: PChar;
begin

  card := Cardinality;

  if (card = 0) and not AOptions.Sets.EmptySetSymbol.IsEmpty then
    Exit(AOptions.Sets.EmptySetSymbol);

  ElemList := TList<string>.Create;
  try

    if card > AOptions.Sets.MaxLen then
      ElemList.Capacity := AOptions.Sets.MaxLen + 1
    else
      ElemList.Capacity := card;

    for i := 1 to Math.Min(FValue.Count, AOptions.Sets.MaxLen)  do
      ElemList.Add(Elements[i].GetAsSingleLineText(AOptions));

    if card > AOptions.Sets.MaxLen then
      ElemList.Add(EllipsisSymbol_HORIZONTAL_ELLIPSIS);

    totlen := 0;
    for i := 0 to ElemList.Count - 1 do
      Inc(totlen, ElemList[i].Length);

    Inc(totlen, (ElemList.Count - 1) * AOptions.Sets.ElementSep.Length);
    Inc(totlen, AOptions.Sets.LeftDelim.Length);
    Inc(totlen, AOptions.Sets.RightDelim.Length);

    SetLength(Result, totlen);
    p := PChar(Result);

    if not AOptions.Sets.LeftDelim.IsEmpty then
    begin
      Move(AOptions.Sets.LeftDelim[1], p^, AOptions.Sets.LeftDelim.Length * sizeof(Char));
      Inc(p, AOptions.Sets.LeftDelim.Length);
    end;

    if ElemList.Count > 0 then
    begin
      s := ElemList.Items[0];
      Move(PChar(s)^, p^, s.Length * sizeof(Char));
      Inc(p, s.Length);
    end;

    for i := 1 to ElemList.Count - 1 do
    begin
      if not AOptions.Sets.ElementSep.IsEmpty then
      begin
        Move(AOptions.Sets.ElementSep[1], p^, AOptions.Sets.ElementSep.Length * sizeof(Char));
        Inc(p, AOptions.Sets.ElementSep.Length);
      end;
      s := ElemList.Items[i];
      Move(PChar(s)^, p^, s.Length * sizeof(Char));
      Inc(p, s.Length);
    end;

    if not AOptions.Sets.RightDelim.IsEmpty then
      Move(AOptions.Sets.RightDelim[1], p^, AOptions.Sets.RightDelim.Length * sizeof(Char));

  finally
    ElemList.Free;
  end;

end;

function TAlgosimSet.GetElement(Index: Integer): TAlgosimObject;
var
  PhysIndex: Integer;
begin

  if FValueArray = nil then
    FValueArray := FValue.Keys.ToArray;

  PhysIndex := GetPhysIndex0(Index, Length(FValueArray));
  Result := FValueArray[PhysIndex];

end;

function TAlgosimSet.GetElementCount: Integer;
begin
  Result := FValue.Count;
end;

function TAlgosimSet.GetMemorySize: UInt64;
var
  i: Integer;
begin
  Result := 0;
  for i := 0 to ElementCount - 1 do
    Inc(Result, Elements[i].MemorySize);
end;

{ TAlgosimTable }

function TAlgosimTable.Accumulate(AInitialValue: TAlgosimObject;
  AFunction: TASOAccumulator): TAlgosimObject;
var
  y, x: Integer;
  str: TAlgosimString;
begin
  str := TAlgosimString.Create;
  try
    Result := AInitialValue.Clone;
    try
      for y := 0 to FValue.Height - 1 do
        for x := 0 to FValue.Width - 1 do
        begin
          str.FValue := FValue.Strings[Point(x, y)];
          TObjReplacer<TAlgosimObject>.Replace(Result, AFunction(Result, str));
        end;
    except
      Result.Free;
      raise;
    end;
  finally
    str.Free;
  end;
end;

function TAlgosimTable.AccumulateStepsList(AInitialValue: TAlgosimObject;
  AFunction: TASOAccumulator): TAlgosimArray;
var
  y, x: Integer;
  str: TAlgosimString;
begin
  str := TAlgosimString.Create;
  try
    Result := TAlgosimArray.Create;
    try
      Result.Capacity := FValue.Height * FValue.Width;
      for y := 0 to FValue.Height - 1 do
        for x := 0 to FValue.Width - 1 do
        begin
          str.FValue := FValue.Strings[Point(x, y)];
          AInitialValue := AFunction(AInitialValue, str);
          Result.Add(AInitialValue);
        end;
    except
      Result.Free;
      raise;
    end;
  finally
    str.Free;
  end;
end;

procedure TAlgosimTable.Apply(AFunction: TASOFunction;
  ACondition: TASOPredicate; ALevel: Integer);
var
  y, x: Integer;
  str: TAlgosimString;
  res: TAlgosimObject;
begin

  if ALevel > 1 then
    Exit;

  str := TAlgosimString.Create;
  try
    for y := 0 to FValue.Height - 1 do
      for x := 0 to FValue.Width - 1 do
      begin
        str.Value := FValue.Strings[Point(x, y)];
        if not Assigned(ACondition) or ACondition(str) then
        begin
          res := AFunction(str);
          try
            if res is TAlgosimString then
              FValue.Strings[Point(x, y)] := TAlgosimString(res).Value
            else
              raise EAlgosimObjectException.Create(SStringApply);
          finally
            res.Free;
          end;
        end;
      end;
  finally
    str.Free;
  end;

end;

function TAlgosimTable.Count(APredicate: TASOPredicate): Integer;
var
  y, x: Integer;
  str: TAlgosimString;
begin
  str := TAlgosimString.Create;
  try
    Result := 0;
    for y := 0 to FValue.Height - 1 do
      for x := 0 to FValue.Width - 1 do
      begin
        str.Value := FValue.Strings[Point(x, y)];
        if APredicate(str) then
          Inc(Result);
      end;
  finally
    str.Free;
  end;
end;

constructor TAlgosimTable.Create(AObject: TAlgosimObject);
begin
  CreateWithValue(AObject.ToTable);
  if AObject is TAlgosimTable then
  begin
    FAlignment := TAlgosimTable(AObject).Alignment;
    FColSPacing := TAlgosimTable(AObject).ColSpacing;
  end;
end;

constructor TAlgosimTable.Create;
begin
  inherited;
  FColSpacing := 2;
end;

constructor TAlgosimTable.CreateWithValue(const AValue: TASTable);
begin
  Create;
  FValue := AValue;
end;

destructor TAlgosimTable.Destroy;
begin
  FValue.Free;
  inherited;
end;

function TAlgosimTable.Equals(Obj: TObject): Boolean;
begin
  Result := (Obj is TAlgosimTable) and Value.Equals(TAlgosimTable(Obj).Value);
end;

function TAlgosimTable.Exists(APredicate: TASOPredicate): Boolean;
var
  y, x: Integer;
  str: TAlgosimString;
begin
  str := TAlgosimString.Create;
  try
    for y := 0 to FValue.Height - 1 do
      for x := 0 to FValue.Width - 1 do
      begin
        str.Value := FValue.Strings[Point(x, y)];
        if APredicate(str) then
          Exit(True)
      end;
    Result := False;
  finally
    str.Free;
  end;
end;

function TAlgosimTable.ForAll(APredicate: TASOPredicate): Boolean;
var
  y, x: Integer;
  str: TAlgosimString;
begin
  str := TAlgosimString.Create;
  try
    for y := 0 to FValue.Height - 1 do
      for x := 0 to FValue.Width - 1 do
      begin
        str.Value := FValue.Strings[Point(x, y)];
        if not APredicate(str) then
          Exit(False)
      end;
    Result := True;
  finally
    str.Free;
  end;
end;

function TAlgosimTable.GetAsMultilineText(const AOptions: TFormatOptions): string;
var
  Widths: TArray<Integer>;
  TotalWidth: Integer;
  Lines: TArray<string>;
  x, y, i: Integer;
  S, sp: string;
begin
  SetLength(Widths, FValue.Width);
  for x := 0 to FValue.Width - 1 do
    for y := 0 to FValue.Height - 1 do
      if FValue[Point(x, y)].Length > Widths[x] then
        Widths[x] := FValue[Point(x, y)].Length;
  TotalWidth := SumInt(Widths);
  if FValue.Width >= 2 then
    Inc(TotalWidth, FColSpacing * (FValue.Width - 1));
  SetLength(Lines, FValue.Height);
  sp := StringOfChar(#32, FColSpacing);
  for y := 0 to FValue.Height - 1 do
  begin
    SetLength(Lines[y], TotalWidth);
    i := 1;
    for x := 0 to FValue.Width - 1 do
    begin
      S := Pad(FValue[Point(x, y)], Widths[x], FAlignment);
      if S.Length > 0 then
        MoveChars(S[1], Lines[y][i], S.Length);
      Inc(i, S.Length);
      if (x < FValue.Width - 1) and (FColSpacing > 0) then
      begin
        MoveChars(sp[1], Lines[y][i], FColSpacing);
        Inc(i, FColSpacing);
      end;
    end;
  end;
  Result := string.Join(sLineBreak, Lines);
end;

function TAlgosimTable.GetAsSingleLineText(const AOptions: TFormatOptions): string;
begin
  Result := StringReplace(FValue.ToString, sLineBreak, #$21B2, [rfReplaceAll]);
end;

function TAlgosimTable.SortClassGetHashCode: Integer;
var
  w, h: Integer;
  x, y: Integer;
  S: string;
begin
  w := FValue.Width;
  h := FValue.Height;
  Result := THashBobJenkins.GetHashValue(w, sizeof(w));
  Result := THashBobJenkins.GetHashValue(h, sizeof(h), Result);
  for y := 0 to h - 1 do
    for x := 0 to w - 1 do
    begin
      S := FValue[Point(x, y)];
      Result := THashBobJenkins.GetHashValue(PChar(S)^, S.Length * sizeof(Char), Result);
    end;
end;

function TAlgosimTable.GetPlanarExtent: TSize;
begin
  Result.cx := FValue.Width;
  Result.cy := FValue.Height;
end;

function TAlgosimTable.GetValue(Index: Integer): TAlgosimObject;
var
  PhysIndex: Integer;
begin
  PhysIndex := GetPhysIndex0(Index, ValueCount);
  Result := ASO(FValue.Strings[Point(PhysIndex mod FValue.Width, PhysIndex div FValue.Width)])
end;

function TAlgosimTable.GetValueCount: Integer;
begin
  Result := FValue.Width * FValue.Height;
end;

function TAlgosimTable.GetValueFromPoint(const APoint: TPoint): TAlgosimObject;
var
  TranslatedPoint: TPoint;
begin
  TranslatedPoint := ASNum.TranslatedPoint(APoint);
  if FValue.CellExists(TranslatedPoint) then
    Result := ASO(FValue[TranslatedPoint])
  else
    raise EAlgosimObjectException.CreateFmt(SIndexOutOfBounds2D, [APoint.Y, APoint.X]);
end;

procedure TAlgosimTable.Replace(APredicate: TASOPredicate;
  ANewValue: TAlgosimObject; ALevel: Integer);
var
  x, y: Integer;
  Obj: TAlgosimString;
  s: string;
begin

  if ALevel > 1 then
    Exit;

  s := ANewValue.ToString;

  Obj := TAlgosimString.Create;
  try
    for y := 0 to FValue.Height - 1 do
      for x := 0 to FValue.Width - 1 do
      begin
        Obj.FValue := FValue.Strings[Point(x, y)];
        if not Assigned(APredicate) or APredicate(Obj) then
          FValue.Strings[Point(x, y)] := s;
      end;
  finally
    Obj.Free;
  end;

end;

procedure TAlgosimTable.SaveToFile(const AFileName: string);
begin
  FValue.SaveTextToFile(AFileName);
end;

procedure TAlgosimTable.SetPlanarExtent(const Value: TSize);
begin
  FValue.SetSize(Value.Width, Value.Height);
end;

procedure TAlgosimTable.SetValue(Index: Integer; AValue: TAlgosimObject);
var
  PhysIndex: Integer;
begin

  try

    PhysIndex := GetPhysIndex0(Index, ValueCount);

    if AValue is TAlgosimString then
      FValue.Strings[Point(PhysIndex mod FValue.Width, PhysIndex div FValue.Width)] := TAlgosimString(AValue).Value
    else
      raise EAlgosimObjectException.CreateFmt(SSetTableSubscriptNoString, [AValue.TypeName])

  finally
    AValue.Free;
  end;

end;

procedure TAlgosimTable.SetValueFromPoint(const APoint: TPoint;
  AValue: TAlgosimObject);
var
  TranslatedPoint: TPoint;
begin
  TranslatedPoint := ASNum.TranslatedPoint(APoint);
  try
    if FValue.CellExists(TranslatedPoint) then
      if AValue is TAlgosimString then
        FValue[TranslatedPoint] := AValue.ToString
      else
        raise EAlgosimObjectException.CreateFmt(SSetTableSubscriptNoString, [AValue.TypeName])
    else
      raise EAlgosimObjectException.CreateFmt(SIndexOutOfBounds2D, [APoint.Y, APoint.X]);
  finally
    AValue.Free;
  end;
end;

class function TAlgosimTable.SortClass: TSortClass;
begin
  Result := SORTCLASS_TABLE;
end;

class function TAlgosimTable.SortClassCompare(const Left,
  Right: TAlgosimObject): Integer;
var
  L, R: TAlgosimTable;
  y: Integer;
  x: Integer;
begin
  L := Left as TAlgosimTable;
  R := Right as TAlgosimTable;
  Result := CompareValue(L.Value.Height, R.Value.Height);
  if Result = 0 then
    Result := CompareValue(L.Value.Width, R.Value.Width);
  if Result = 0 then
    for y := 0 to L.Value.Height - 1 do
      for x := 0 to L.Value.Width - 1 do
      begin
        Result := CompareStr(L.Value.Strings[Point(X, Y)],
          R.Value.Strings[Point(X, Y)]);
        if Result <> EqualsValue then
          Exit;
      end;
end;

class function TAlgosimTable.SortClassSameObject(const Left,
  Right: TAlgosimObject; const AEpsilon: TASR): Boolean;
var
  L, R: TAlgosimTable;
  y: Integer;
  x: Integer;
begin
  L := Left as TAlgosimTable;
  R := Right as TAlgosimTable;
  Result := L.PlanarExtent = R.PlanarExtent;
  if Result then
    for y := 0 to L.Value.Height - 1 do
      for x := 0 to L.Value.Width - 1 do
        if L.Value.Strings[Point(X, Y)] <> R.Value.Strings[Point(X, Y)] then
          Exit(False);
end;

function TAlgosimTable.ToRealMatrix: TRealMatrix;
var
  y, x: Integer;
  S: string;
  val: TASR;
begin
  Result := TRealMatrix.CreateUninitialized(TMatrixSize.Create(Value.Height, Value.Width));
  for y := 0 to Value.Height - 1 do
    for x := 0 to Value.Width - 1 do
    begin
      S := Value.Strings[Point(x, y)];
      if TryStrToFloat(S, val, DefaultFormatSettings) then
        Result[y, x] := val
      else
        raise EAlgosimObjectException.CreateFmt(SConvStrRealNum, [S]);
    end;
end;

function TAlgosimTable.ToString: string;
begin
  Result := FValue.ToString;
end;

function TAlgosimTable.ToTable: TASTable;
begin
  Result := FValue.Clone;
end;

function TAlgosimTable.WithSpecificValues(
  AValues: TAlgosimArray): TAlgosimObject;
var
  i: Integer;
  y: Integer;
  x: Integer;
begin

  if PlanarExtent.Width * PlanarExtent.Height <> AValues.ElementCount then
    raise EAlgosimObjectException.CreateFmt(SSpecValWrongLength2D,
      [PlanarExtent.Height, PlanarExtent.Width, AValues.ElementCount]);

  Result := TAlgosimTable.Create;
  try
    Result.PlanarExtent := PlanarExtent;
    i := 1;
    for y := 0 to Result.PlanarExtent.Height - 1 do
      for x := 0 to Result.PlanarExtent.Width - 1 do
      begin
        TAlgosimTable(Result).FValue.Strings[Point(x, y)] := AValues.Elements[i].ToString;
        Inc(i);
      end;
  except
    Result.Free;
    raise;
  end;

end;

function TAlgosimTable.ToComplexMatrix: TComplexMatrix;
var
  y, x: Integer;
  S: string;
  val: TASC;
begin
  Result := TComplexMatrix.CreateUninitialized(TMatrixSize.Create(Value.Height, Value.Width));
  for y := 0 to Value.Height - 1 do
    for x := 0 to Value.Width - 1 do
    begin
      S := Value.Strings[Point(x, y)];
      if TryStringToComplex(S, val) then
        Result[y, x] := val
      else
        raise EAlgosimObjectException.CreateFmt(SConvStrComplexNum, [S]);
    end;
end;

function TAlgosimTable.ToMatrix: TAlgosimMatrix;
begin
  try
    Result := ASO(ToRealMatrix)
  except
    Result := ASO(ToComplexMatrix);
  end;
end;

{ ASO functions }

function ASO(const Value: TASR): TAlgosimRealNumber;
begin
  Result := TAlgosimRealNumber.CreateWithValue(Value);
end;

function ASO(const Value: TASR; AFormatStyle: TFormatStyle): TAlgosimRealNumber;
begin
  Result := TAlgosimRealNumber.CreateWithValue(Value);
  Result.Style := AFormatStyle;
end;

function ASOInt(const Value: TASI): TAlgosimInteger;
begin
  Result := TAlgosimInteger.CreateWithValue(Value);
end;

function ASOInt(const Value: TASI; AFormatStyle: TFormatStyle): TAlgosimInteger;
begin
  Result := TAlgosimInteger.CreateWithValue(Value);
  Result.Style := AFormatStyle;
end;

function ASOInt(const Value: TASI; ABase: Integer): TAlgosimInteger;
begin
  Result := TAlgosimInteger.CreateWithValue(Value);
  Result.NumberBase := ABase;
end;

function ASORat(const Value: TRationalNumber): TAlgosimRationalNumber;
begin
  Result := TAlgosimRationalNumber.CreateWithValue(Value);
end;

function ASORat(const Value: TRationalNumber;
  AFormatStyle: TFormatStyle): TAlgosimRationalNumber;
begin
  Result := TAlgosimRationalNumber.CreateWithValue(Value);
  Result.Style := AFormatStyle;
end;

function ASO(const AGUID: TGUID): TAlgosimReference;
begin
  Result := TAlgosimReference.CreateWithValue(AGUID);
end;

function ASOBreak(ADepth: Integer = 1): TAlgosimBreak;
begin
  Result := TAlgosimBreak.CreateWithValue(ADepth);
end;

function ASOContinue: TAlgosimContinue;
begin
  Result := TAlgosimContinue.Create;
end;

function ASO(const Value: TASC): TAlgosimComplexNumber;
begin
  Result := TAlgosimComplexNumber.CreateWithValue(Value);
end;

function ASO(const Value: string): TAlgosimString;
begin
  Result := TAlgosimString.CreateWithValue(Value);
end;

function ASO(const Value: Boolean): TAlgosimBoolean;
begin
  Result := TAlgosimBoolean.CreateWithValue(Value);
end;

function ASO(const Value: TRealVector): TAlgosimRealVector;
begin
  Result := TAlgosimRealVector.CreateWithValue(Value);
end;

function ASO(const Value: TComplexVector): TAlgosimComplexVector;
begin
  Result := TAlgosimComplexVector.CreateWithValue(Value);
end;

function ASO(const Value: TRealMatrix): TAlgosimRealMatrix;
begin
  Result := TAlgosimRealMatrix.CreateWithValue(Value);
end;

function ASO(const Value: TComplexMatrix): TAlgosimComplexMatrix;
begin
  Result := TAlgosimComplexMatrix.CreateWithValue(Value);
end;

function ASO(const Value: TASPixmap): TAlgosimPixmap;
begin
  Result := TAlgosimPixmap.CreateWithValue(Value);
end;

function ASO(const Value: TASSound): TAlgosimSound;
begin
  Result := TAlgosimSound.CreateWithValue(Value);
end;

function ASO(const Value: TASTable): TAlgosimTable;
begin
  Result := TAlgosimTable.CreateWithValue(Value);
end;

function ASO(const Value: TRGB): TAlgosimColor;
begin
  Result := TAlgosimColor.CreateWithValue(Value);
end;

function ASO(const Value: THSV): TAlgosimColor;
begin
  Result := TAlgosimColor.CreateWithValue(Value);
end;

function ASO(const Value: THSL): TAlgosimColor;
begin
  Result := TAlgosimColor.CreateWithValue(Value);
end;

function ASO(const Value: TASPixel): TAlgosimColor;
begin
  Result := TAlgosimColor.CreateWithValue(TRGB(Value), Value.Alpha / 255);
end;

function ASOColor(const Value: TColor): TAlgosimColor;
begin
  Result := TAlgosimColor.CreateWithValue(Value, 1);
end;

function ASO(const Value: Exception): TAlgosimFailure; overload;
begin
  if Value is ESyntaxException then
    Result := TAlgosimSyntaxError.Create
  else if Value is EParseException then
    Result := TAlgosimParserError.Create
  else
    Result := TAlgosimFailure.Create;
  Result.FailureReason := Value.Message;
end;

function ASO(const Elements: array of TAlgosimObject): TAlgosimArray;
begin
  Result := TAlgosimArray.CreateWithValue(Elements);
end;

function ASO(const Names: array of string;
  const Values: array of TAlgosimObject): TAlgosimStructure;
begin
  Result := TAlgosimStructure.CreateWithValue(Names, Values);
end;

function ASO(const Members: array of TAlgosimStructure.TMemberRef): TAlgosimStructure;
begin
  Result := TAlgosimStructure.CreateWithValue(Members);
end;

function ASO(const Value: TASOSignal; const AReason: string): TAlgosimObject;
begin
  case Value of
    success:
      Result := TAlgosimSuccessIndication.Create;
    failure:
      begin
        Result := TAlgosimFailure.Create;
        TAlgosimFailure(Result).FailureReason := AReason;
      end;
    null:
      Result := TAlgosimNullObject.Create;
    _break:
      Result := TAlgosimBreak.Create;
    _continue:
      Result := TAlgosimContinue.Create;
  else
    raise EAlgosimObjectException.CreateFmt(SUnsupportedSignalType,
      [Ord(Value)]);
  end;
end;

function sm(const AName: string; AValue: TAlgosimObject): TAlgosimStructure.TMemberRef;
begin
  Result.Name := AName;
  Result.Value := AValue;
end;

function IsNonNull(AObject: TAlgosimObject): Boolean;
begin
  Result := Assigned(AObject) and not (AObject is TAlgosimNullObject);
end;

function IsControl(AObject: TAlgosimObject): Boolean;
begin
  Result := AObject is TAlgosimControlFlowObject;
end;

function IsFailure(AObject: TAlgosimObject): Boolean;
begin
  Result := AObject is TAlgosimFailure;
end;

function IsCharacter(AObject: TAlgosimObject): Boolean;
begin
  Result := (AObject is TAlgosimString) and (TAlgosimString(AObject).Value.Length = 1);
end;

function IsTypedStructure(AObject: TAlgosimObject;
  AType: TAlgosimStructureType): Boolean;
begin
  Result := (AObject is TAlgosimTypedStructure) and
    (TAlgosimTypedStructure(AObject).StructureTypeName = AType.Name);
end;

{ TAlgosimColor }

constructor TAlgosimColor.Create(AObject: TAlgosimObject);
begin
  if AObject is TAlgosimColor then
    CreateWithValue(TAlgosimColor(AObject).FData.FValue, TAlgosimColor(AObject).FData.FAlpha)
  else
    CreateWithValue(AObject.ToColor);
end;

constructor TAlgosimColor.Create;
begin
  inherited;
  FData.FAlpha := 1.0;
end;

constructor TAlgosimColor.CreateWithValue(const AValue: THSL;
  const AAlpha: Double);
begin
  inherited Create;
  FData.FValue := TRGB(AValue);
  FData.FAlpha := AAlpha;
end;

constructor TAlgosimColor.CreateWithValue(const AValue: THSV;
  const AAlpha: Double);
begin
  inherited Create;
  FData.FValue := TRGB(AValue);
  FData.FAlpha := AAlpha;
end;

constructor TAlgosimColor.CreateWithValue(const AValue: TRGB;
  const AAlpha: Double);
begin
  inherited Create;
  FData.FValue := AValue;
  FData.FAlpha := AAlpha;
end;

constructor TAlgosimColor.CreateWithValue(const AValue: TColor;
  const AAlpha: Double);
begin
  inherited Create;
  FData.FValue := AValue;
  FData.FAlpha := AAlpha;
end;

function TAlgosimColor.Equals(Obj: TObject): Boolean;
begin
  Result := (Obj is TAlgosimColor) and
    (TAlgosimColor(Obj).FData.FValue = Self.FData.FValue) and
    (TAlgosimColor(Obj).FData.FAlpha = Self.FData.FAlpha);
end;

function TAlgosimColor.ExplainedOutput(const AOptions: TFormatOptions): string;
var
  ColorName: string;
begin
  Result := GetAsSingleLineText(AOptions);
  if TryGetColorName(FData.FValue, ColorName) then
    Result := Result + ' (' + ColorToHex(FData.FValue) + ', ' + ColorName + ')'
  else
    Result := Result + ' (' + ColorToHex(FData.FValue) + ')'
end;

function TAlgosimColor.GetAsSingleLineText(const AOptions: TFormatOptions): string;
begin
  Result := Format('rgba(%.3f, %.3f, %.3f, %.3f)',
    [
      FData.FValue.Red,
      FData.FValue.Green,
      FData.FValue.Blue,
      FData.FAlpha
    ],
    DefaultFormatSettings);
end;

function TAlgosimColor.ToColor: TRGB;
begin
  Result := FData.FValue;
end;

function TAlgosimColor.ToInputString: string;
begin
  Result := Format('rgb(%g, %g, %g)',
    [
      FData.FValue.Red,
      FData.FValue.Green,
      FData.FValue.Blue
    ],
    DefaultFormatSettings);
end;

function TAlgosimColor.ToPixel: TASPixel;
begin
  Result := Value;
  Result.Alpha := Round(255 * Alpha);
end;

function TAlgosimColor.ToSpeech: string;
var
  ColorName: string;
begin
  if TryGetColorName(FData.FValue, ColorName) then
    Result := 'Colour ' + ColorName
  else
    Result := Format(
      'Colour. Red: %.1f%%. Green: %.1f%%. Blue: %.1f%%.',
      [
        100*FData.FValue.Red,
        100*FData.FValue.Green,
        100*FData.FValue.Blue
      ],
      TFormatSettings.Invariant
    );
end;

function TAlgosimColor.ToString: string;
begin
  Result := Format('rgba(%g, %g, %g, %g)',
    [
      FData.FValue.Red,
      FData.FValue.Green,
      FData.FValue.Blue,
      FData.FAlpha
    ],
    DefaultFormatSettings);
end;

function TAlgosimColor.TryToASI(out ASI: TASI): Boolean;
begin
  ASI := RBSwap(TColor(FData.FValue));
  Result := True;
end;

function TAlgosimColor.TryToInt32(out Int: Integer): Boolean;
begin
  Int := RBSwap(TColor(FData.FValue));
  Result := True;
end;

function TAlgosimColor.GetBinaryData(var Buf: PByte; var Len: UInt64): Boolean;
begin
  Buf := @FData;
  Len := sizeof(FData);
  Result := True;
end;

function TAlgosimColor.GetMemorySize: UInt64;
begin
  Result := sizeof(FData);
end;

function TAlgosimColor.SortClassGetHashCode: Integer;
begin
  Result := THashBobJenkins.GetHashValue(FData.FValue, sizeof(FData.FValue));
  Result := THashBobJenkins.GetHashValue(FData.FAlpha, sizeof(FData.FAlpha), Result);
end;

function TAlgosimColor.GetAsPixel: TASPixel;
begin
  Result := Value;
  Result.Alpha := Round(255 * Alpha);
end;

procedure TAlgosimColor.SetAsPixel(const APixel: TASPixel);
begin
  Value := APixel;
  Alpha := APixel.Alpha / 255;
end;

procedure TAlgosimColor.SetBinaryData(const Buf: PByte; const Len: UInt64);
begin
  if Len = sizeof(FData) then
    CopyMemory(@FData, Buf, Len)
  else
    raise EAlgosimObjectException.CreateFmt(SInvalidBlob, [ClassTypeName]);
end;

class function TAlgosimColor.SortClass: TSortClass;
begin
  Result := SORTCLASS_COLOR;
end;

class function TAlgosimColor.SortClassCompare(const Left,
  Right: TAlgosimObject): Integer;
var
  L, R: TAlgosimColor;
begin
  L := Left as TAlgosimColor;
  R := Right as TAlgosimColor;
  Result := CompareColor(L.FData.FValue, R.FData.FValue);
  if Result = 0 then
    Result := CompareValue(L.FData.FAlpha, R.FData.FAlpha);
end;

class function TAlgosimColor.SortClassSameObject(const Left,
  Right: TAlgosimObject; const AEpsilon: TASR): Boolean;
var
  L, R: TAlgosimColor;
begin
  L := Left as TAlgosimColor;
  R := Right as TAlgosimColor;
  Result := SameColor(L.FData.FValue, R.FData.FValue) and (Round(255*L.FData.FAlpha) = Round(255*R.FData.FAlpha));
end;

{ TSimpleDomainDescription }

function TSimpleDomainDescription.Contains(const x: TASR): Boolean;
begin
  case Kind of
    sddLt:
      Result := x < a;
    sddLeq:
      Result := x <= a;
    sddGt:
      Result := x > a;
    sddGeq:
      Result := x >= a;
    sddBii:
      Result := (a <= x) and (x <= b);
    sddBei:
      Result := (a < x) and (x <= b);
    sddBie:
      Result := (a <= x) and (x < b);
    sddBee:
      Result := (a < x) and (x < b);
    else
      raise EKernelException.Create(SInvalidSDDKind);
  end;
  if Complement then
    Result := not Result;
end;

{ TAlgosimBinaryData }

constructor TAlgosimBinaryData.Create(AObject: TAlgosimObject);
var
  buf: PByte;
  len: UInt64;
begin
  if AObject.GetBinaryData(buf, len) then
  begin
    Create;
    DataLength := len;
    if len > 0 then
      CopyMemory(Data, buf, len);
  end
  else
    raise EAlgosimObjectException.CreateFmt(SConvBlob, [ClassTypeName]);
end;

constructor TAlgosimBinaryData.CreateWithValue(const ABytes: array of Byte);
begin
  Create;
  SetLength(FData, Length(ABytes));
  if Length(ABytes) > 0 then
    Move(ABytes[0], FData[0], Length(ABytes));
end;

function TAlgosimBinaryData.Equals(Obj: TObject): Boolean;
begin
  Result := (Obj is TAlgosimBinaryData) and
    (TAlgosimBinaryData(Obj).DataLength = DataLength) and
    CompareMem(TAlgosimBinaryData(Obj).Data, Data, DataLength);
end;

function TAlgosimBinaryData.GetAsMultilineText(const AOptions: TFormatOptions): string;
begin
  Result := BytesToString(FData);
end;

function TAlgosimBinaryData.GetAsSingleLineText(const AOptions: TFormatOptions): string;
begin
  Result := BytesToString(FData);
end;

function TAlgosimBinaryData.GetBinaryData(var Buf: PByte;
  var Len: UInt64): Boolean;
begin
  Buf := Data;
  Len := DataLength;
  Result := True;
end;

function TAlgosimBinaryData.GetDataLength: NativeInt;
begin
  Result := Length(FData);
end;

function TAlgosimBinaryData.GetDataPtr: PByte;
begin
  Result := Pointer(FData);
end;

function TAlgosimBinaryData.GetMemorySize: UInt64;
begin
  Result := Length(FData);
end;

function TAlgosimBinaryData.SortClassGetHashCode: Integer;
begin
  if Length(FData) > 0 then
    Result := THashBobJenkins.GetHashValue(FData[0], Length(FData))
  else
    Result := 0;
end;

function TAlgosimBinaryData.GetValue(Index: Integer): TAlgosimObject;
begin
  if InRange(Index, 1, DataLength) then
    Result := ASO(FData[Index - 1])
  else if InRange(-Index, 1, DataLength) then
    Result := ASO(FData[DataLength + Index])
  else
    raise EAlgosimObjectException.CreateFmt(SIndexOutOfBounds, [Index]);
end;

function TAlgosimBinaryData.GetValueCount: Integer;
begin
  Result := Length(FData);
end;

class function TAlgosimBinaryData.LoadFromFile(const AFileName: string;
  AEncoding: TEncoding; const AParams: string): TAlgosimObject;
begin
  Result := CreateWithValue(TFile.ReadAllBytes(AFileName));
end;

procedure TAlgosimBinaryData.SaveToFile(const AFileName: string);
begin
  TFile.WriteAllBytes(AFileName, FData);
end;

procedure TAlgosimBinaryData.SetBinaryData(const Buf: PByte; const Len: UInt64);
begin
  SetLength(FData, Len);
  if Len > 0 then
    Move(Buf^, FData[0], Len);
end;

procedure TAlgosimBinaryData.SetDataLength(const Value: NativeInt);
begin
  SetLength(FData, Value);
end;

procedure TAlgosimBinaryData.SetValue(Index: Integer; AValue: TAlgosimObject);
var
  IntVal: Integer;
  PhysIndex: Integer;
begin

  try

    PhysIndex := GetPhysIndex0(Index, DataLength);

    if (AValue is TAlgosimNumber) and AValue.TryToInt32(IntVal) and InRange(IntVal, Byte.MinValue, Byte.MaxValue) then
      FData[PhysIndex] := IntVal
    else
      raise EAlgosimObjectException.CreateFmt(SSetBinarySubscriptNoByte, [AValue.TypeName])

  finally
    AValue.Free;
  end;

end;

class function TAlgosimBinaryData.SortClass: TSortClass;
begin
  Result := SORTCLASS_BINARYDATA;
end;

class function TAlgosimBinaryData.SortClassCompare(const Left,
  Right: TAlgosimObject): Integer;
var
  L, R: TAlgosimBinaryData;
  i: Integer;
begin
  L := Left as TAlgosimBinaryData;
  R := Right as TAlgosimBinaryData;
  Result := CompareValue(L.DataLength, R.DataLength);
  if Result = 0 then
    for i := 0 to L.DataLength - 1 do
    begin
      Result := CompareValue(L.Data[i], R.Data[i]);
      if Result <> EqualsValue then
        Exit;
    end;
end;

class function TAlgosimBinaryData.SortClassSameObject(const Left,
  Right: TAlgosimObject; const AEpsilon: TASR): Boolean;
var
  L, R: TAlgosimBinaryData;
  i: Integer;
begin
  L := Left as TAlgosimBinaryData;
  R := Right as TAlgosimBinaryData;
  Result := L.DataLength = R.DataLength;
  if Result then
    for i := 0 to L.DataLength - 1 do
      if L.Data[i] <> R.Data[i] then
        Exit(False);
end;

function TAlgosimBinaryData.ToASO<T>: T;
begin
  Result := T.Create;
  try
    Result.SetBinaryData(FData);
  except
    Result.Free;
    raise;
  end;
end;

function TAlgosimBinaryData.ToBoolean: Boolean;
begin
  with ToASO<TAlgosimBoolean> do
    try
      Result := Value;
    finally
      Free;
    end;
end;

function TAlgosimBinaryData.ToComplexNumber: TASC;
begin
  with ToASO<TAlgosimComplexNumber> do
    try
      Result := Value;
    finally
      Free;
    end;
end;

function TAlgosimBinaryData.ToInteger: TASI;
begin
  with ToASO<TAlgosimInteger> do
    try
      Result := Value;
    finally
      Free;
    end;
end;

function TAlgosimBinaryData.ToRationalNumber: TRationalNumber;
begin
  with ToASO<TAlgosimRationalNumber> do
    try
      Result := Value;
    finally
      Free;
    end;
end;

function TAlgosimBinaryData.ToRealNumber: TASR;
begin
  with ToASO<TAlgosimRealNumber> do
    try
      Result := Value;
    finally
      Free;
    end;
end;

function TAlgosimBinaryData.ToSpeech: string;
begin
  Result := 'Binary data: ' + BytesToString(FData).Replace(#32, ', ');
end;

function TAlgosimBinaryData.ToString: string;
begin
  with ToASO<TAlgosimString> do
    try
      Result := Value;
    finally
      Free;
    end;
end;

procedure TAlgosimBinaryData.Truncate(ANewLength: Integer);
begin

  if ANewLength < 0 then
    raise EArrayException.Create(SNewLengthMustBeNonNegative);

  if ANewLength >= Length(FData) then
    Exit;

  SetLength(FData, ANewLength);

end;

function TAlgosimBinaryData.TryToASI(out ASI: Int64): Boolean;
begin
  try
    with ToASO<TAlgosimInteger> do
      try
        ASI := Value;
        Result := True;
      finally
        Free;
      end;
  except
    Result := False;
  end;
end;

{ TAlgosimAssignmentList }

{ TAlgosimInteger }

function TAlgosimInteger.Abs: TAlgosimNumericEntity;
begin
  if TInt64Guard.CanAbs(FValue) then
    Result := ASOInt(System.Abs(FValue))
  else
    Result := ASO(System.Abs(TASR(FValue)));
end;

function TAlgosimInteger.AddASC(ASC: TAlgosimComplexNumber): TAlgosimNumber;
begin
  Result := ASO(TASC(TASR(FValue)) + ASC.Value);
end;

function TAlgosimInteger.AddASI(ASI: TAlgosimInteger): TAlgosimNumber;
begin
  if TInt64Guard.CanAdd(FValue, ASI.Value) then
    Result := ASOInt(FValue + ASI.Value)
  else
    Result := ASO(TASR(FValue) + TASR(ASI.Value));
end;

function TAlgosimInteger.AddRat(R: TAlgosimRationalNumber): TAlgosimNumber;
var
  res: TRationalNumber;
begin
  res := TRationalNumber(FValue) + R.Value;
  if res.valid then
    Result := ASORat(res)
  else
    Result := ASO(TASR(FValue) + TASR(R.Value));
end;

function TAlgosimInteger.AddASR(ASR: TAlgosimRealNumber): TAlgosimNumber;
begin
  Result := ASO(TASR(FValue) + ASR.Value);
end;

function TAlgosimInteger.AddTo(ANum: TAlgosimNumber): TAlgosimNumber;
begin
  Result := ANum.AddASI(Self);
end;

function TAlgosimInteger.Argument: TAlgosimRealNumber;
begin
  if FValue >= 0 then
    Result := ASO(0)
  else
    Result := ASO(Pi);
end;

function TAlgosimInteger.AsMemberOfSimplestField: TAlgosimNumber;
begin
  Result := TAlgosimRealNumber.CreateWithValue(FValue);
end;

function TAlgosimInteger.ComputeFunction(ARealFcn: TRealFunction;
  AComplexFcn: TComplexFunction): TAlgosimNumber;
begin
  Result := ASO(ARealFcn(Value));
end;

function TAlgosimInteger.ComputeFunction(const ARealDomain: TSDD;
  ARealFcn: TRealFunction; AComplexFcn: TComplexFunction): TAlgosimNumber;
begin
  if ARealDomain.Contains(Value) then
    Result := ASO(ARealFcn(Value))
  else
    Result := ASO(AComplexFcn(Value));
end;

function TAlgosimInteger.Conjugate: TAlgosimNumber;
begin
  Result := TAlgosimNumber(Self.Clone);
end;

constructor TAlgosimInteger.Create(AObject: TAlgosimObject);
begin
  CreateWithValue(AObject.ToASI);
  if AObject is TAlgosimNumericEntity then
    FormatCode := TAlgosimNumericEntity(AObject).FormatCode;
end;

constructor TAlgosimInteger.CreateWithValue(const AValue: TASI);
begin
  Create;
  FValue := AValue;
end;

function TAlgosimInteger.DivideASC(ASC: TAlgosimComplexNumber): TAlgosimNumber;
begin
  Result := ASO(TASC(TASR(FValue)) / ASC.Value);
end;

function TAlgosimInteger.DivideASI(ASI: TAlgosimInteger): TAlgosimNumber;
var
  res: TRationalNumber;
begin
  if TInt64Guard.CanDivEv(FValue, ASI.Value) then
    Result := ASOInt(FValue div ASI.Value)
  else
  begin
    res := RationalNumber(FValue, ASI.Value);
    if res.valid then
      Result := ASORat(res)
    else
      Result := ASO(TASR(FValue) / ASI.Value);
  end;
end;

function TAlgosimInteger.DivideRat(R: TAlgosimRationalNumber): TAlgosimNumber;
var
  res: TRationalNumber;
begin
  res := TRationalNumber(FValue) / R.Value;
  if res.valid then
    Result := ASORat(res)
  else
    Result := ASO(TASR(FValue) / TASR(R.Value));
end;

function TAlgosimInteger.Equals(Obj: TObject): Boolean;
begin
  if Obj is TAlgosimInteger then
    Result := Value = TAlgosimInteger(Obj).Value
  else
    Result := inherited;
end;

function TAlgosimInteger.ExplainedOutput(
  const AOptions: TFormatOptions): string;

  function BaseSuffix(ABase: Integer): string;
  var
    j: Integer;
  begin
    Result := ABase.ToString;
    for j := 1 to Result.Length do
      Result[j] := UnicodeSubscript(Result[j]);
  end;

var
  AppliedOptions, BaseSpecficOptions: TFormatOptions;
  b: Integer;
  AdditionalForms: TArray<string>;
begin
  Result := GetAsSingleLineText(AOptions);
  if (Style = fsDefault) and (FValue <> 0) then
  begin
    AppliedOptions := ApplyOptions(AOptions);
    for b in (AppliedOptions.Numbers.PreferredBases - [AppliedOptions.Numbers.Base]) do
    begin
      BaseSpecficOptions := AppliedOptions;
      BaseSpecficOptions.Numbers.Base := b;
      case b of
        2:
          BaseSpecficOptions.Numbers.IntGrouping := 8;
      end;
      TArrBuilder<string>.Add(AdditionalForms, IntegerToStr(FValue, BaseSpecficOptions) + BaseSuffix(b));
    end;
    if Length(AdditionalForms) > 0 then
      Result := Result + #9 + '(' + string.Join(', ', AdditionalForms) + ')';
  end;
end;

function TAlgosimInteger.DivideASR(ASR: TAlgosimRealNumber): TAlgosimNumber;
begin
  Result := ASO(TASR(FValue) / ASR.Value);
end;

function TAlgosimInteger.DivideBy(ANum: TAlgosimNumber): TAlgosimNumber;
begin
  Result := ANum.DivideASI(Self);
end;

function TAlgosimInteger.GetAsSingleLineText(
  const AOptions: TFormatOptions): string;
var
  Formatter: TASRFormatter;
begin

  if (Style <> fsDefault) and TryGetFormatter(Style, Formatter) then
    Result := Formatter(ApplyOptions(AOptions), FValue)
  else
    Result := IntegerToStr(FValue, ApplyOptions(AOptions));

end;

function TAlgosimInteger.GetBinaryData(var Buf: PByte;
  var Len: UInt64): Boolean;
begin
  Buf := @FValue;
  Len := sizeof(FValue);
  Result := True;
end;

function TAlgosimInteger.GetMemorySize: UInt64;
begin
  Result := sizeof(FValue);
end;

function TAlgosimInteger.ImaginaryPart: TAlgosimNumericEntity;
begin
  Result := ASO(0);
end;

procedure TAlgosimInteger.Increase(AAmount: TASI);
begin
  Inc(FValue, AAmount);
end;

function TAlgosimInteger.Inverse: TAlgosimNumericEntity;
var
  res: TRationalNumber;
begin
  res := TRationalNumber(Value).inv;
  if res.valid then
    Result := ASORat(res)
  else
    Result := ASO(1.0 / Value);
end;

function TAlgosimInteger.IsNegative(const Eps: Double): Boolean;
begin
  Result := FValue < 0;
end;

function TAlgosimInteger.IsNonNegative(const Eps: Double): Boolean;
begin
  Result := FValue >= 0;
end;

function TAlgosimInteger.IsNonPositive(const Eps: Double): Boolean;
begin
  Result := FValue <= 0;
end;

function TAlgosimInteger.IsNonZero(const Eps: Double): Boolean;
begin
  Result := FValue <> 0;
end;

function TAlgosimInteger.IsPositive(const Eps: Double): Boolean;
begin
  Result := FValue > 0;
end;

function TAlgosimInteger.IsZero(const Eps: Double): Boolean;
begin
  Result := FValue = 0;
end;

function TAlgosimInteger.MultiplyASC(
  ASC: TAlgosimComplexNumber): TAlgosimNumber;
begin
  Result := ASO(TASC(TASR(FValue)) * ASC.Value);
end;

function TAlgosimInteger.MultiplyASI(ASI: TAlgosimInteger): TAlgosimNumber;
begin
  if TInt64Guard.CanMul(FValue, ASI.Value) then
    Result := ASOInt(FValue * ASI.Value)
  else
    Result := ASO(TASR(FValue) * TASR(ASI.Value));
end;

function TAlgosimInteger.MultiplyRat(R: TAlgosimRationalNumber): TAlgosimNumber;
var
  res: TRationalNumber;
begin
  res := TRationalNumber(FValue) * R.Value;
  if res.valid then
    Result := ASORat(res)
  else
    Result := ASO(TASR(FValue) * TASR(R.Value));
end;

function TAlgosimInteger.MultiplyASR(ASR: TAlgosimRealNumber): TAlgosimNumber;
begin
  Result := ASO(TASR(FValue) * ASR.Value);
end;

function TAlgosimInteger.MultiplyTo(ANum: TAlgosimNumber): TAlgosimNumber;
begin
  Result := ANum.MultiplyASI(Self)
end;

function TAlgosimInteger.Norm(AType: TNormType; AParam: Integer;
  AYieldProc: TObjProc): TASR;
begin
  Result := System.Abs(FValue);
end;

function TAlgosimInteger.NormSquared: TAlgosimRealNumber;
begin
  Result := ASO(System.Sqr(FValue));
end;

function TAlgosimInteger.RaiseTo(ANum: TAlgosimNumber): TAlgosimNumber;
begin
  Result := ANum.RaiseToASI(Self);
end;

function TAlgosimInteger.RaiseToASC(ASC: TAlgosimComplexNumber): TAlgosimNumber;
begin
  Result := ASO(cpow(TASC(TASR(FValue)), ASC.Value));
end;

function TAlgosimInteger.RaiseToASI(ASI: TAlgosimInteger): TAlgosimNumber;
const
  SafeMin = TASI.MinValue div 2;
  SafeMax = TASI.MaxValue div 2;
var
  fltval, fltvalpb, fltvalpbpe: TASR;
  PosExp: TASI;
  rat: TRationalNumber;
begin

  if (FValue = 0) and (ASI.Value = 0) then
    raise EMathException.Create('Zero raised to the power of zero.');

  if (FValue = 0) and (ASI.Value < 0) then
    raise EMathException.Create('Division by zero.');

  fltval := pow(TASR(FValue), TASR(ASI.Value)); // fallback value
  fltvalpb := pow(System.Abs(TASR(FValue)), TASR(ASI.Value)); // order of magnitude estimate

  if ASI.Value >= 0 then
  begin
    if InRange(fltvalpb, SafeMin, SafeMax) then
      Exit(ASOInt(intpow(FValue, ASI.Value)));
  end

  else // negative exponent
  begin

    if ASI.Value <> ASI.Value.MinValue then // need to compute Abs of it
    begin

      PosExp := -ASI.Value; // safe since ASI.Value <> ASI.Value.MinValue

      fltvalpbpe := pow(System.Abs(TASR(FValue)), TASR(PosExp)); // order of magnitude estimate with positive exponent

      if InRange(fltvalpbpe, SafeMin, SafeMax) then
      begin
        rat := TRationalNumber(intpow(FValue, PosExp)).inv;
        if rat.valid then
          Exit(ASORat(rat))
      end

    end;

  end;

  Result := ASO(fltval); // failure, fallback to TASR

end;

function TAlgosimInteger.RaiseToRat(R: TAlgosimRationalNumber): TAlgosimNumber;
var
  tmp: TAlgosimInteger;
begin
  if R.Value.valid and (R.Value.Denominator = 1) then
  begin
    tmp := TAlgosimInteger.CreateWithValue(R.Value.Numerator);
    try
      Result := RaiseToASI(tmp);
    finally
      tmp.Free;
    end;
  end
  else
    if FValue < 0 then
      Result := ASO(cpow(TASC(FValue), TASC(TASR(R.Value))))
    else
      Result := ASO(pow(TASR(FValue), TASR(R.Value)));
end;

function TAlgosimInteger.RaiseToASR(ASR: TAlgosimRealNumber): TAlgosimNumber;
begin
  if (FValue < 0) and not ASNum.IsInteger32(ASR.Value) then
    Result := ASO(cpow(TASC(TASR(FValue)), ASR.Value))
  else
    Result := ASO(pow(TASR(FValue), ASR.Value))
end;

function TAlgosimInteger.RealPart: TAlgosimNumericEntity;
begin
  Result := ASO(FValue);
end;

function TAlgosimInteger.ScaledBy(const AFactor: TASR): TAlgosimNumericEntity;
begin
  Result := ASO(TASR(FValue) * AFactor);
end;

procedure TAlgosimInteger.SetBinaryData(const Buf: PByte; const Len: UInt64);
begin
  if Len = sizeof(TASI) then
    FValue := PASI(Buf)^
  else
    raise EAlgosimObjectException.CreateFmt(SInvalidBlob, [ClassTypeName]);
end;

function TAlgosimInteger.Square: TAlgosimNumericEntity;
begin
  if TInt64Guard.CanSqr(FValue) then
    Result := TAlgosimInteger.CreateWithValue(FValue * FValue)
  else
    Result := ASO(TASR(FValue) * TASR(FValue));
end;

function TAlgosimInteger.SubtractASC(
  ASC: TAlgosimComplexNumber): TAlgosimNumber;
begin
  Result := ASO(TASC(TASR(FValue)) - ASC.Value);
end;

function TAlgosimInteger.SubtractASI(ASI: TAlgosimInteger): TAlgosimNumber;
begin
  if TInt64Guard.CanSub(FValue, ASI.Value) then
    Result := ASOInt(FValue - ASI.Value)
  else
    Result := ASO(TASR(FValue) - TASR(ASI.Value));
end;

function TAlgosimInteger.SubtractRat(R: TAlgosimRationalNumber): TAlgosimNumber;
var
  res: TRationalNumber;
begin
  res := TRationalNumber(FValue) - R.Value;
  if res.valid then
    Result := ASORat(res)
  else
    Result := ASO(TASR(FValue) - TASR(R.Value));
end;

function TAlgosimInteger.SubtractASR(ASR: TAlgosimRealNumber): TAlgosimNumber;
begin
  Result := ASO(TASR(FValue) - ASR.Value);
end;

function TAlgosimInteger.SubtractFrom(ANum: TAlgosimNumber): TAlgosimNumber;
begin
  Result := ANum.SubtractASI(Self);
end;

function TAlgosimInteger.ToBoolean: Boolean;
begin
  Result := FValue <> 0;
end;

function TAlgosimInteger.ToComplexMatrix: TComplexMatrix;
begin
  Result := TComplexMatrix.Create([Value]);
end;

function TAlgosimInteger.ToComplexNumber: TASC;
begin
  Result := ASC(FValue);
end;

function TAlgosimInteger.ToComplexVector: TComplexVector;
begin
  Result := TComplexVector.Create([Value]);
end;

function TAlgosimInteger.ToRealMatrix: TRealMatrix;
begin
  Result := TRealMatrix.Create([Value]);
end;

function TAlgosimInteger.ToRealNumber: TASR;
begin
  Result := Value;
end;

function TAlgosimInteger.ToRealVector: TRealVector;
begin
  Result := TRealVector.Create([Value]);
end;

function TAlgosimInteger.ToString: string;
begin
  Result := IntegerToStr(FValue, ExchangeFormOptions);
end;

function TAlgosimInteger.TryToASI(out ASI: Int64): Boolean;
begin
  Result := True;
  ASI := FValue;
end;

function TAlgosimInteger.TryToRat(out R: TRationalNumber): Boolean;
begin
  Result := True;
  R := FValue;
end;

function TAlgosimInteger.TryToASR(out Val: TASR): Boolean;
begin
  Result := True;
  Val := FValue;
end;

function TAlgosimInteger.TryToASC(out Val: TASC): Boolean;
begin
  Result := True;
  Val := FValue;
end;

function TAlgosimInteger.TryToInt32(out Int: Integer): Boolean;
begin
  Result := InRange(FValue, Int.MinValue, Int.MaxValue);
  if Result then
    Int := FValue;
end;

function TAlgosimInteger.TryToInt64(out Int: Int64): Boolean;
begin
  Result := True;
  Int := FValue;
end;

function TAlgosimInteger.UnaryMinus: TAlgosimObject;
begin
  if FValue = TASI.MinValue then
    Result := TAlgosimRealNumber.CreateWithValue(-FValue)
  else
    Result := TAlgosimInteger.CreateWithValue(-FValue);
end;

{ TNormTypeHelper }

class function TNormTypeHelper.FromString(const AStr: string): TNormType;
var
  i: TNormType;
begin
  for i := Low(TNormType) to High(TNormType) do
    if SameText(AStr, i.ToShortString) or SameText(AStr, i.ToString) then
      Exit(i);
  raise EUnknownIdentifier.CreateFmt(SUnknownNormType, [AStr]);
end;

function TNormTypeHelper.ToString: string;
begin
  Result := NormTypes[Self];
end;

function TNormTypeHelper.ToShortString: string;
begin
  Result := ShortNormTypes[Self];
end;

{ TAlgosimRationalNumber }

function TAlgosimRationalNumber.Abs: TAlgosimNumericEntity;
var
  res: TRationalNumber;
begin
  res := FValue.Abs;
  if res.valid then
    Result := ASORat(res)
  else
    Result := ASO(System.Abs(TASR(FValue)));
end;

function TAlgosimRationalNumber.AddASC(
  ASC: TAlgosimComplexNumber): TAlgosimNumber;
begin
  Result := ASO(TASC(TASR(FValue)) + ASC.Value);
end;

function TAlgosimRationalNumber.AddASI(ASI: TAlgosimInteger): TAlgosimNumber;
var
  res: TRationalNumber;
begin
  res := FValue + TRationalNumber(ASI.Value);
  if res.valid then
    Result := ASORat(res)
  else
    Result := ASO(TASR(FValue) + TASR(ASI.Value));
end;

function TAlgosimRationalNumber.AddRat(R: TAlgosimRationalNumber): TAlgosimNumber;
var
  res: TRationalNumber;
begin
  res := FValue + R.Value;
  if res.valid then
    Result := ASORat(res)
  else
    Result := ASO(TASR(FValue) + TASR(R.Value));
end;

function TAlgosimRationalNumber.AddASR(ASR: TAlgosimRealNumber): TAlgosimNumber;
begin
  Result := ASO(TASR(FValue) + ASR.Value);
end;

function TAlgosimRationalNumber.AddTo(ANum: TAlgosimNumber): TAlgosimNumber;
begin
  Result := ANum.AddRat(Self);
end;

function TAlgosimRationalNumber.Argument: TAlgosimRealNumber;
begin
  if FValue.Sign >= 0 then
    Result := ASO(0)
  else
    Result := ASO(Pi);
end;

function TAlgosimRationalNumber.AsMemberOfSimplestField: TAlgosimNumber;
begin
  Result := TAlgosimRealNumber.CreateWithValue(TASR(FValue));
end;

function TAlgosimRationalNumber.ComputeFunction(ARealFcn: TRealFunction;
  AComplexFcn: TComplexFunction): TAlgosimNumber;
begin
  Result := ASO(ARealFcn(TASR(FValue)));
end;

function TAlgosimRationalNumber.ComputeFunction(const ARealDomain: TSDD;
  ARealFcn: TRealFunction; AComplexFcn: TComplexFunction): TAlgosimNumber;
begin
  if ARealDomain.Contains(TASR(FValue)) then
    Result := ASO(ARealFcn(TASR(FValue)))
  else
    Result := ASO(AComplexFcn(TASC(TASR(FValue))));
end;

function TAlgosimRationalNumber.Conjugate: TAlgosimNumber;
begin
  Result := TAlgosimNumber(Self.Clone);
end;

constructor TAlgosimRationalNumber.Create(AObject: TAlgosimObject);
begin
  CreateWithValue(AObject.ToRat);
  if AObject is TAlgosimNumericEntity then
    Self.FormatCode := TAlgosimNumericEntity(AObject).FormatCode;
end;

constructor TAlgosimRationalNumber.CreateWithValue(const AValue: TRationalNumber);
begin
  Create;
  FValue := AValue;
end;

function TAlgosimRationalNumber.DivideASC(
  ASC: TAlgosimComplexNumber): TAlgosimNumber;
begin
  Result := ASO(TASC(TASR(FValue)) / ASC.Value);
end;

function TAlgosimRationalNumber.DivideASI(ASI: TAlgosimInteger): TAlgosimNumber;
var
  res: TRationalNumber;
begin
  res := FValue / TRationalNumber(ASI.Value);
  if res.valid then
    Result := ASORat(res)
  else
    Result := ASO(TASR(FValue) / TASR(ASI.Value));
end;

function TAlgosimRationalNumber.DivideRat(R: TAlgosimRationalNumber): TAlgosimNumber;
var
  res: TRationalNumber;
begin
  res := FValue / R.Value;
  if res.valid then
    Result := ASORat(res)
  else
    Result := ASO(TASR(FValue) / TASR(R.Value));
end;

function TAlgosimRationalNumber.ExplainedOutput(
  const AOptions: TFormatOptions): string;
var
  LOptions: TFormatOptions;
begin
  if (Style = fsDefault) and (FValue.Denominator <> 1) and FValue.valid then
  begin
    LOptions := ApplyOptions(AOptions);
    Result := FValue.ToString(LOptions);
    if LOptions.Numbers.NumberFormat = nfFraction then
      Result := Result + #9 + '(=' + FValue.ToString(FixNumFmt(LOptions, nfDefault)) + ')'
    else
      Result := Result + #9 + '(=' + FValue.ToString(FixNumFmt(LOptions, nfFraction)) + ')'
  end
  else
    Result := GetAsSingleLineText(AOptions);
end;

function TAlgosimRationalNumber.DivideASR(
  ASR: TAlgosimRealNumber): TAlgosimNumber;
begin
  Result := ASO(TASR(FValue) / ASR.Value);
end;

function TAlgosimRationalNumber.DivideBy(ANum: TAlgosimNumber): TAlgosimNumber;
begin
  Result := ANum.DivideRat(Self);
end;

function TAlgosimRationalNumber.GetAsSingleLineText(
  const AOptions: TFormatOptions): string;
var
  Formatter: TASRFormatter;
begin

  if (Style <> fsDefault) and TryGetFormatter(Style, Formatter) then
    Result := Formatter(ApplyOptions(AOptions), FValue)
  else
    Result := FValue.ToString(ApplyOptions(AOptions));

end;

function TAlgosimRationalNumber.GetBinaryData(var Buf: PByte;
  var Len: UInt64): Boolean;
begin
  Buf := @FValue;
  Len := sizeof(FValue);
  Result := True;
end;

function TAlgosimRationalNumber.GetMemorySize: UInt64;
begin
  Result := sizeof(FValue);
end;

function TAlgosimRationalNumber.ImaginaryPart: TAlgosimNumericEntity;
begin
  Result := ASO(0);
end;

procedure TAlgosimRationalNumber.Increase(AAmount: TASI);
begin
  FValue := FValue + AAmount;
end;

function TAlgosimRationalNumber.Inverse: TAlgosimNumericEntity;
var
  res: TRationalNumber;
begin
  res := Value.inv;
  if res.valid then
    Result := ASORat(res)
  else
    Result := ASO(1.0 / TASR(Value));
end;

function TAlgosimRationalNumber.IsNegative(const Eps: Double): Boolean;
begin
  Result := FValue.Sign = -1;
end;

function TAlgosimRationalNumber.IsNonNegative(const Eps: Double): Boolean;
begin
  Result := FValue.Sign <> -1;
end;

function TAlgosimRationalNumber.IsNonPositive(const Eps: Double): Boolean;
begin
  Result := FValue.Sign <> 1;
end;

function TAlgosimRationalNumber.IsNonZero(const Eps: Double): Boolean;
begin
  Result := FValue.Sign <> 0;
end;

function TAlgosimRationalNumber.IsPositive(const Eps: Double): Boolean;
begin
  Result := FValue.Sign = 1;
end;

function TAlgosimRationalNumber.IsZero(const Eps: Double): Boolean;
begin
  Result := FValue.Sign = 0;
end;

function TAlgosimRationalNumber.MultiplyASC(
  ASC: TAlgosimComplexNumber): TAlgosimNumber;
begin
  Result := ASO(TASC(TASR(FValue)) * ASC.Value);
end;

function TAlgosimRationalNumber.MultiplyASI(
  ASI: TAlgosimInteger): TAlgosimNumber;
var
  res: TRationalNumber;
begin
  res := FValue * TRationalNumber(ASI.Value);
  if res.valid then
    Result := ASORat(res)
  else
    Result := ASO(TASR(FValue) * TASR(ASI.Value));
end;

function TAlgosimRationalNumber.MultiplyRat(
  R: TAlgosimRationalNumber): TAlgosimNumber;
var
  res: TRationalNumber;
begin
  res := FValue * R.Value;
  if res.valid then
    Result := ASORat(res)
  else
    Result := ASO(TASR(FValue) * TASR(R.Value));
end;

function TAlgosimRationalNumber.MultiplyASR(
  ASR: TAlgosimRealNumber): TAlgosimNumber;
begin
  Result := ASO(TASR(FValue) * ASR.Value);
end;

function TAlgosimRationalNumber.MultiplyTo(
  ANum: TAlgosimNumber): TAlgosimNumber;
begin
  Result := ANum.MultiplyRat(Self);
end;

function TAlgosimRationalNumber.Norm(AType: TNormType; AParam: Integer;
  AYieldProc: TObjProc): TASR;
begin
  Result := System.Abs(TASR(FValue));
end;

function TAlgosimRationalNumber.NormSquared: TAlgosimRealNumber;
begin
  Result := ASO(System.Sqr(TASR(FValue)));
end;

function TAlgosimRationalNumber.RaiseTo(ANum: TAlgosimNumber): TAlgosimNumber;
begin
  Result := ANum.RaiseToRat(Self);
end;

function TAlgosimRationalNumber.RaiseToASC(
  ASC: TAlgosimComplexNumber): TAlgosimNumber;
begin
  Result := ASO(cpow(TASC(TASR(FValue)), ASC.Value));
end;

function TAlgosimRationalNumber.RaiseToASI(
  ASI: TAlgosimInteger): TAlgosimNumber;
var
  res: TRationalNumber;
begin
  res := TRationalNumber.Power(FValue, ASI.Value);
  if res.valid then
    Result := ASORat(res)
  else
    Result := ASO(pow(TASR(FValue), ASI.Value));
end;

function TAlgosimRationalNumber.RaiseToRat(
  R: TAlgosimRationalNumber): TAlgosimNumber;
var
  res: TRationalNumber;
begin

  if R.Value.Denominator = 1 then
  begin
    res := TRationalNumber.Power(FValue, R.Value.Numerator);
    if res.valid then
      Exit(ASORat(res));
  end;

  if FValue.Numerator < 0 then
    Result := ASO(cpow(TASC(TASR(FValue)), TASC(TASR(R.Value))))
  else
    Result := ASO(pow(TASR(FValue), R.Value)); // fallback to TASR

end;

function TAlgosimRationalNumber.RaiseToASR(
  ASR: TAlgosimRealNumber): TAlgosimNumber;
begin
  if (FValue.Numerator < 0) and not ASNum.IsInteger32(ASR.Value) then
    Result := ASO(cpow(TASC(TASR(FValue)), ASR.Value))
  else
    Result := ASO(pow(TASR(FValue), ASR.Value))
end;

function TAlgosimRationalNumber.RealPart: TAlgosimNumericEntity;
begin
  Result := ASO(FValue);
end;

function TAlgosimRationalNumber.ScaledBy(
  const AFactor: TASR): TAlgosimNumericEntity;
begin
  Result := ASO(TASR(FValue) * AFactor);
end;

procedure TAlgosimRationalNumber.SetBinaryData(const Buf: PByte;
  const Len: UInt64);
begin
  if Len = sizeof(FValue) then
    with PRationalNumber(Buf)^ do
      FValue := TRationalNumber.Create(Numerator, Denominator)
  else
    raise EAlgosimObjectException.CreateFmt(SInvalidBlob, [ClassTypeName]);
end;

function TAlgosimRationalNumber.Square: TAlgosimNumericEntity;
var
  res: TRationalNumber;
begin
  res := FValue.sqr;
  if res.valid then
    Result := ASORat(res)
  else
    Result := ASO(TASR(FValue) * TASR(FValue));
end;

function TAlgosimRationalNumber.SubtractASC(
  ASC: TAlgosimComplexNumber): TAlgosimNumber;
begin
  Result := ASO(TASC(TASR(FValue)) - ASC.Value);
end;

function TAlgosimRationalNumber.SubtractASI(
  ASI: TAlgosimInteger): TAlgosimNumber;
var
  res: TRationalNumber;
begin
  res := FValue - TRationalNumber(ASI.Value);
  if res.valid then
    Result := ASORat(res)
  else
    Result := ASO(TASR(FValue) - TASR(ASI.Value));
end;

function TAlgosimRationalNumber.SubtractRat(
  R: TAlgosimRationalNumber): TAlgosimNumber;
var
  res: TRationalNumber;
begin
  res := FValue - R.Value;
  if res.valid then
    Result := ASORat(res)
  else
    Result := ASO(TASR(FValue) - TASR(R.Value));
end;

function TAlgosimRationalNumber.SubtractASR(
  ASR: TAlgosimRealNumber): TAlgosimNumber;
begin
  Result := ASO(TASR(FValue) - ASR.Value);
end;

function TAlgosimRationalNumber.SubtractFrom(
  ANum: TAlgosimNumber): TAlgosimNumber;
begin
  Result := ANum.SubtractRat(Self);
end;

function TAlgosimRationalNumber.ToBoolean: Boolean;
begin
  Result := FValue.valid and (FValue.Numerator <> 0);
end;

function TAlgosimRationalNumber.ToComplexMatrix: TComplexMatrix;
begin
  Result := TComplexMatrix.Create([TASR(FValue)]);
end;

function TAlgosimRationalNumber.ToComplexNumber: TASC;
begin
  Result := ASC(FValue);
end;

function TAlgosimRationalNumber.ToComplexVector: TComplexVector;
begin
  Result := TComplexVector.Create([TASR(FValue)]);
end;

function TAlgosimRationalNumber.ToRealMatrix: TRealMatrix;
begin
  Result := TRealMatrix.Create([FValue]);
end;

function TAlgosimRationalNumber.ToRealNumber: TASR;
begin
  Result := FValue;
end;

function TAlgosimRationalNumber.ToRealVector: TRealVector;
begin
  Result := TRealVector.Create([FValue]);
end;

function TAlgosimRationalNumber.ToString: string;
begin
  Result := FValue.ToString(ExchangeFormOptions);
end;

function TAlgosimRationalNumber.TryToASC(out Val: TASC): Boolean;
begin
  Result := True;
  Val := TASR(FValue);
end;

function TAlgosimRationalNumber.TryToASI(out ASI: Int64): Boolean;
var
  tmp: TRationalNumber;
begin
  tmp := FValue;
  tmp.ToSimplestForm;
  Result := tmp.valid and (tmp.Denominator = 1);
  if Result then
    ASI := tmp.Numerator;
end;

function TAlgosimRationalNumber.TryToRat(out R: TRationalNumber): Boolean;
begin
  Result := True;
  R := FValue;
end;

function TAlgosimRationalNumber.TryToASR(out Val: TASR): Boolean;
begin
  Result := True;
  Val := FValue;
end;

function TAlgosimRationalNumber.TryToInt32(out Int: Integer): Boolean;
var
  tmp: TASI;
begin
  Result := TryToASI(tmp) and InRange(tmp, Int.MinValue, Int.MaxValue);
  if Result then
    Int := Integer(tmp);
end;

function TAlgosimRationalNumber.TryToInt64(out Int: Int64): Boolean;
begin
  Result := TryToASI(Int);
end;

function TAlgosimRationalNumber.UnaryMinus: TAlgosimObject;
var
  res: TRationalNumber;
begin
  res := -FValue;
  if res.valid then
    Result := ASORat(res)
  else
    Result := ASO(-TASR(FValue));
end;

{ AlgosimObjectAttribute }

constructor AlgosimObjectAttribute.Create(const AClassTypeName: string;
  AClassFlags: TAlgosimObjectClassFlags; const AExportExts: string);
begin
  Data := TAlgosimObjectClassData.Create(AClassTypeName, AClassFlags,
    AExportExts);
end;

{ TAlgosimObjectClassData }

constructor TAlgosimObjectClassData.Create(const AClassTypeName: string;
  AClassFlags: TAlgosimObjectClassFlags; const AExportExts: string);
var
  i: Integer;
begin
  ClassTypeName := AClassTypeName;
  ClassFlags := AClassFlags;
  ExportExts := AExportExts.Split([',']);
  for i := Low(ExportExts) to High(ExportExts) do
    ExportExts[i] := ExportExts[i].Trim;
end;

{ TSubscript }

constructor TSubscript.Create(const AIdent: string);
begin
  Kind := skIdentifier;
  Ident := AIdent;
end;

constructor TSubscript.Create(const AIndex: TAlgosimObject);
begin
  Kind := skIndexObject;
  Obj := AIndex;
end;

constructor TSubscript.Create(const AKind: TSubscriptKind;
  const AOrd: Integer);
begin
  Kind := AKind;
  Ordinal := AOrd;
end;

function TSubscript.ToString: string;
begin
  case Kind of
    skIndexObject:
      Result := '[' + Obj.ToString + ']';
    skIdentifier:
      Result := '.' + Ident;
    skFirst:
      Result := '.first';
    skLast:
      Result := '.last';
    skRandom:
      Result := '.random';
    skRowIndex:
      Result := '.rows[' + Ordinal.ToString + ']';
    skColIndex:
      Result := '.cols[' + Ordinal.ToString + ']';
  else
    Result := '[?]';
  end;
end;

{ TAlgosimStructure.TMemberObj }

constructor TAlgosimStructure.TMemberObj.Create(const AName: string;
  AValue: TAlgosimObject);
begin
  Name := AName;
  Value := AValue;
end;

constructor TAlgosimStructure.TMemberObj.Create(const AMemberRef: TMemberRef);
begin
  Name := AMemberRef.Name;
  Value := AMemberRef.Value;
end;

destructor TAlgosimStructure.TMemberObj.Destroy;
begin
  Value.Free;
  inherited;
end;

function TAlgosimStructure.TMemberObj.Ref: TMemberRef;
begin
  Result := TMemberRef.Create(Name, Value);
end;

{ TAlgosimStructure.TMemberRef }

constructor TAlgosimStructure.TMemberRef.Create(const AName: string;
  AValue: TAlgosimObject);
begin
  Name := AName;
  Value := AValue;
end;

{ TASOArrSpec }

class function TASOArrSpec.TrySpec<T>(const AArgs: TArray<TAlgosimObject>;
  out ASpec: TArray<T>): Boolean;
var
  i: Integer;
begin
  ASpec := nil;
  for i := 0 to High(AArgs) do
    if not (AArgs[i] is T) then
      Exit(False);
  Result := True;
  SetLength(ASpec, Length(AArgs));
  CopyMemory(Pointer(ASpec), Pointer(AArgs), Length(AArgs) * sizeof(Pointer));
end;

{ TAlgosimControlFlowObject }

constructor TAlgosimControlFlowObject.Create(AObject: TAlgosimObject);
begin
  Create;
end;

function TAlgosimControlFlowObject.Equals(Obj: TObject): Boolean;
begin
  Result := Obj.ClassType = Self.ClassType;
end;

function TAlgosimControlFlowObject.GetAsSingleLineText(
  const AOptions: TFormatOptions): string;
begin
  Result := ToString;
end;

class function TAlgosimControlFlowObject.SortClass: TSortClass;
begin
  Result := SORTCLASS_CONTROL;
end;

class function TAlgosimControlFlowObject.SortClassSameObject(const Left,
  Right: TAlgosimObject; const AEpsilon: Extended): Boolean;
begin
  Result := Left.ClassType = Right.ClassType;
end;

function TAlgosimControlFlowObject.ToString: string;
begin
  Result := 'control';
end;

{ TAlgosimFailure }

constructor TAlgosimFailure.Create;
begin
  inherited;
  FFailureSource := TList<TClass>.Create;
end;

constructor TAlgosimFailure.Create(AObject: TAlgosimObject);
begin
  Create;
  if AObject is TAlgosimFailure then
  begin
    FailureReason := TAlgosimFailure(AObject).FailureReason;
    Source.AddRange(TAlgosimFailure(AObject).Source);
  end;
end;

destructor TAlgosimFailure.Destroy;
begin
  FFailureSource.Free;
  inherited;
end;

function TAlgosimFailure.ToInputString: string;
begin
  Result := Format('fail("%s")', [FFailureReason]);
end;

function TAlgosimFailure.ToString: string;
begin
  Result := 'failure';
end;

{ TAlgosimBreak }

function TAlgosimBreak.Consume: Boolean;
begin
  Dec(FDepth);
  Result := FDepth = 0;
end;

constructor TAlgosimBreak.Create;
begin
  inherited;
  FDepth := 1;
end;

constructor TAlgosimBreak.CreateWithValue(ADepth: Integer);
begin
  Create;
  FDepth := ADepth;
end;

function TAlgosimBreak.ToString: string;
begin
  if FDepth = FDepth.MaxValue then
    Result := 'exit()'
  else
    Result := Format('break(%d)', [FDepth]);
end;

{ TAlgosimContinue }

function TAlgosimContinue.ToString: string;
begin
  Result := 'continue()';
end;

{ TAlgosimSyntaxError }

function TAlgosimSyntaxError.ToString: string;
begin
  Result := 'syntax error';
end;

{ TAlgosimParserError }

function TAlgosimParserError.ToString: string;
begin
  Result := 'parser error';
end;

function LoadResObject(const AName: string): TAlgosimObject;
var
  r: TResInfo;
  bm: TBitmap;
begin
  r := ResLookup(AName);
  case r.ResKind of
    rkText:
      Result := ASO(LoadResString(r.ResName));
    rkBitmap:
      begin
        bm := LoadResBitmap(r.ResName);
        try
          bm.PixelFormat := pf32bit;
          Result := ASO(TASPixmap.Create(bm));
        finally
          bm.Free;
        end;
      end;
    rkSound:
      Result := ASO(LoadResSound(r.ResName));
  else
    raise Exception.Create('Unsupported resource type.');
  end;
end;

{ TAlgosimReference }

procedure TAlgosimReference.AddSubref(const AName: string; const ARef: TGUID);
begin
  if Assigned(FSubrefs) then
    FSubrefs.AddOrSetValue(AName, TAlgosimReference.CreateWithValue(ARef));
end;

procedure TAlgosimReference.AddSubref(const AName: string;
  ARef: TAlgosimReference);
begin
  if Assigned(FSubrefs) then
    FSubrefs.AddOrSetValue(AName, ARef)
  else
    ARef.Free;
end;

constructor TAlgosimReference.Create(AObject: TAlgosimObject);
var
  sr: TPair<string, TAlgosimReference>;
begin
  if AObject is TAlgosimReference then
  begin
    Create;
    FGUID := TAlgosimReference(AObject).FGUID;
    if Assigned(FSubrefs) then
      for sr in TAlgosimReference(AObject).FSubrefs do
        FSubrefs.Add(sr.Key, sr.Value.Clone as TAlgosimReference);
  end
  else
    NoCopyConstr(AObject);
end;

constructor TAlgosimReference.Create;
begin
  FSubrefs := TObjectDictionary<string, TAlgosimReference>.Create([doOwnsValues]);
end;

constructor TAlgosimReference.CreateWithValue(const AGUID: TGUID);
begin
  Create;
  FGUID := AGUID;
end;

destructor TAlgosimReference.Destroy;
begin
  FreeAndNil(FSubrefs);
  inherited;
end;

function TAlgosimReference.Equals(Obj: TObject): Boolean;
begin
  Result := (Obj is TAlgosimReference) and (TAlgosimReference(Obj).GUID = Self.GUID);
end;

procedure TAlgosimReference.SaveToFile(const AFileName: string;
  AOptions: TAlgosimStructure; AContext: TObject);
begin

  if AContext is TExecutionContextRefObject then
    if TExecutionContextRefObject(AContext).Context.Perform
      (
        CLIENT_COMMAND_EXPORTVISUAL,
        NativeInt(@FGUID),
        NativeInt(PChar(AFileName)),
        NativeInt(AOptions)
      )
    then
      Exit;

  raise Exception.Create('Couldn''t save.');

end;

function TAlgosimReference.ToString: string;
var
  sr: TPair<string, TAlgosimReference>;
begin
  Result := GUIDToString(FGUID);
  if Assigned(FSubrefs) then
    for sr in FSubrefs do
      Result := Result + sLineBreak + sr.Key + ': ' + sr.Value.ToString
end;

function TAlgosimReference.TryGetSubscriptedRef(ASubscript: TSubscript;
  out AValue: TAlgosimObject): Boolean;
begin

  AValue := nil;

  if
    Assigned(FSubrefs)
      and
    (
      (
        (ASubscript.Kind = skIdentifier)
          and
        FSubrefs.TryGetValue(ASubscript.Ident, TAlgosimReference(AValue))
      )
        or
      (
        (ASubscript.Kind = skIndexObject)
          and
        FSubrefs.TryGetValue(ASubscript.Obj.ToString, TAlgosimReference(AValue))
      )
    )
  then
    Result := True
  else
    raise EAlgosimObjectException.Create('Unknown reference.');

end;

function TAlgosimReference.TryGetSubscriptedValue(ASubscript: TSubscript;
  out AValue: TAlgosimObject): Boolean;
begin
  AValue := nil;
  Result := TryGetSubscriptedRef(ASubscript, AValue);
  if Result and Assigned(AValue) then
    AValue := AValue.Clone;
end;

end.
