{ *********************************************************************** }
{                                                                         }
{ Delphi Visual Component Library                                         }
{                                                                         }
{ Copyright (c) 1996-2004 Borland Software Corporation                    }
{                                                                         }
{ *********************************************************************** }

unit Borland.Vcl.Variants;

interface

                                        
                                  
                                 

uses
  System.Reflection, System.Collections, System.Runtime.InteropServices,
  SysUtils;

//==============================================================================
type
  TVarType = Integer;

const        //   Hex   | Decimal  | Other names for this type
  varEmpty     = $0000; // = 0     | Unassigned, Nil
  varNull      = $0001; // = 1     | Null, System.DBNull
  varSmallInt  = $0002; // = 2     | I2, System.Int16
  varInteger   = $0003; // = 3     | I4, System.Int32
  varSingle    = $0004; // = 4     | R4, System.Single
  varDouble    = $0005; // = 5     | R8, System.Double
  varCurrency  = $0006; // = 6     | Borland.Delphi.System.Currency
  varDate      = $0007; // = 7     | Borland.Delphi.System.TDateTime
  varString    = $0008; // = 8     | WideString, System.String
//varDispatch  * $0009; // = 9     | NOT IMPLIMENTED
  varError     = $000A; // = 10    | Exception, System.Exception
  varBoolean   = $000B; // = 11    | Bool, System.Boolean
  varObject    = $000C; // = 12    | TObject, System.Object
//varUnknown   * $000D; // = 13    | NOT IMPLIMENTED
  varDecimal   = $000E; // = 14    | System.Decimal
//UNUSED SLOT  * $000F; // = 15    | UNDEFINED
  varShortInt  = $0010; // = 16    | I1, System.SByte
  varByte      = $0011; // = 17    | U1, System.Byte
  varWord      = $0012; // = 18    | U2, System.UInt16
  varLongWord  = $0013; // = 19    | U4, System.UInt32
  varInt64     = $0014; // = 20    | I8, System.Int64
  varUInt64    = $0015; // = 21    | U8, System.UInt64
  varChar      = $0016; // = 22    | WideChar, System.Char
  varDateTime  = $0017; // = 23    | System.DateTime;

  varFirst     = varEmpty;
  varLast      = varDateTime;

  varArray     = $2000; // = 8192  | System.Array, Dynamic Arrays
//varByRef     = $4000; // = 16384 | NOT IMPLIMENTED
  varTypeMask  = $0FFF; // = 4095
  varUndefined = -1;

  varOleStr    = varString deprecated;
  varVariant   = varObject deprecated;

type
  Variant = type TObject;
  OleVariant = type TObject;

//==============================================================================
procedure VarClear(var AValue: Variant);
function VarIsClear(const AValue: Variant): Boolean;
function VarIsEmpty(const AValue: Variant): Boolean;
procedure VarCheckEmpty(const AValue: Variant);
function VarIsNull(const AValue: Variant): Boolean;

function VarType(const AValue: Variant): TVarType;
function VarTypeAsText(const AVarType: TVarType): string; overload;
function VarTypeAsText(const AValue: Variant): string; overload;

function VarIsType(const AValue: Variant; const AVarType: TVarType): Boolean; overload;
function VarIsType(const AValue: Variant; const AVarTypes: array of TVarType): Boolean; overload;
function VarAsType(const AValue: Variant; const AVarType: TVarType): Variant; overload;
function VarAsType(const AValue: Variant; const ASystemType: System.Type): Variant; overload;
procedure VarCast(var Destination: Variant; const Source: Variant; const AVarType: TVarType);

function VarIsOrdinal(const AValue: Variant): Boolean;
function VarIsFloat(const AValue: Variant): Boolean;
function VarIsNumeric(const AValue: Variant): Boolean;

function VarIsStr(const AValue: Variant): Boolean;
function VarToStr(const AValue: Variant): string;
function VarToStrDef(const AValue: Variant; const ANullString: string): string;
function VarToWideStr(const AValue: Variant): WideString;
function VarToWideStrDef(const AValue: Variant; const ANullString: WideString): WideString;

function VarCopy(const AValue: Variant): Variant;

function VarInRange(const AValue, AMin, AMax: Variant): Boolean;
function VarEnsureRange(const AValue, AMin, AMax: Variant): Variant;
function VarSameValue(const ALeft, ARight: Variant): Boolean;

type
  TVariantRelationship = (vrEqual, vrLessThan, vrGreaterThan, vrNotEqual);

function VarCompareValue(const ALeft, ARight: Variant): TVariantRelationship;

{ Variant array support }

function VarIsArray(const AValue: Variant): Boolean;
function VarArrayElementsType(const AValue: Variant): TVarType;
function VarArrayElementsIsType(const AValue: Variant; AVarType: TVarType): Boolean; overload;
function VarArrayElementsIsType(const AValue: Variant; const AVarTypes: array of TVarType): Boolean; overload;

function VarArrayCreate(const ABounds: array of Integer; AVarType: TVarType): Variant; overload;
function VarArrayCreate(const ABounds: array of Integer; ASystemType: System.Type): Variant; overload;
function VarArrayOf(const AValues: array of Variant): Variant;

function VarArrayDimCount(const AValue: Variant): Integer;
function VarArrayLowBound(const AValue: Variant; ADim: Integer): Integer;
function VarArrayHighBound(const AValue: Variant; ADim: Integer): Integer;

function VarArrayGet(const AValue: Variant; const AIndices: array of Integer): Variant;
procedure VarArrayPut(var AValue: Variant; const AData: Variant; const AIndices: array of Integer);

{ Variant error/exception support }

function VarIsError(const AValue: Variant): Boolean; overload;
function VarIsError(const AValue: Variant; out AResult: Integer): Boolean; overload;
function VarIsError(const AValue: Variant; out AException: Exception): Boolean; overload;

function VarFromErrorCode(const AResult: Integer): Variant;
function VarFromException(const AException: Exception): Variant;
function VarToException(const AValue: Variant): Exception;

function VarAsError(const AResult: Integer): Variant; deprecated;

{ Global constants }

function Unassigned: Variant; // Unassigned standard constant
function Null: Variant;       // DBNull standard constant

{ Custom variant registration }

function RegisterCustomVariantType(const AClass: TClass): TVarType; overload;
function RegisterCustomVariantType(const ASystemType: System.Type): TVarType; overload;

function FindCustomVariantType(const AClassName: string; out ACustomVariant: Variant): Boolean;

//==============================================================================
type
  EVariantError = class(Exception);
  EVariantInvalidOpError = class(EVariantError);
  EVariantInvalidNullOpError = class(EVariantInvalidOpError);
  EVariantInvalidInvokeError = class(EVariantInvalidOpError);
  EVariantInvalidArgError = class(EVariantError);
  EVariantInvalidVarTypeError = class(EVariantError);
  EVariantTypeCastError = class(EVariantError);
  EVariantTypeNullCastError = class(EVariantTypeCastError);
  EVariantTypeCastOverflowError = class(EVariantTypeCastError);
  EVariantIsEmptyError = class(EVariantError);
  EVariantArrayError = class(EVariantError);
  EVariantNotAnArrayError = class(EVariantArrayError);
  EVariantIsAnArrayError = class(EVariantArrayError);
  EVariantArrayCreateError = class(EVariantArrayError);
  EVariantArrayInvalidDim = class(EVariantArrayError);
  EVariantTooManyCustomTypes = class(EVariantError);

//==============================================================================
type
  TNullCompareRule = (ncrError, ncrStrict, ncrLoose);
  TBooleanToStringRule = (bsrAsIs, bsrLower, bsrUpper);

  TSimpleType = (stErr, stEmp, stNul, stBoo, stI32, stU32, stI64,
                 stU64, stDec, stDtm, stDbl, stStr, stObj);

  VariantHelper = class helper for Variant
  private
    class var
      LastCustomVarType: Integer;
      CustomVarTypes: array of System.Type;
      CustomVarTypeLocker: TMultiReadExclusiveWriteSynchronizer;

    //==============================================================================
    class function FindTypeCode(const AValue: Variant): TypeCode; static;
    class function FindSimpleType(const AValue: Variant;
      out AVarType: TVarType): TSimpleType; overload; static;
    class function FindSimpleType(const ALeft, ARight: Variant; out LeftVarType,
      RightVarType: TVarType): TSimpleType; overload; static;

    class function SafeGetType(const AInstance: TObject): System.Type; static;
    class function ConvertToType(const AValue: Variant;
      const ANewType: System.Type): Variant; static;

    class function HandleExceptionTranslation(E: Exception;
      const AType: TVarType): Boolean; overload; static;
    class function HandleExceptionTranslation(E: Exception;
      const AFirstType, ASecondType: TVarType): Boolean; overload; static;

    class function FindTypeInvoke(const AType: System.Type;
      const AValue: Variant; const ANames: array of string;
      const AParams: array of Variant; out AResult: Variant): Boolean; static;
    class function FindAndInvoke(const AReference, AValue: Variant;
      const ANames: array of string; const AParams: array of Variant;
      out AResult: Variant): Boolean; static;

    class function FindClassInvoke(const AReference: Variant;
      const ANames: array of string; const AParams: array of Variant;
      out AResult: Variant): Boolean; overload; static;
    class function FindClassInvoke(const AReference: Variant;
      const ANames: array of string; const AParams: array of Variant;
      out AResult: Boolean): Boolean; overload; static;

    class function FindObjectInvoke(const AValue: Variant;
      const ANames: array of string; const AParams: array of Variant;
      out AResult: Variant): Boolean; overload; static;
    class function FindObjectInvoke(const AValue: Variant;
      const ANames: array of string; const AParams: array of Variant;
      out AResult: Boolean): Boolean; overload; static;

    class function FindUnifiedType(const ALeft, ARight: Variant;
      out AUnifiedType: System.Type; out ANewLeft, ANewRight: Variant): Boolean; static;
    class function FindUnifiedCompare(const ALeft, ARight: Variant;
      out ARelationship: TVariantRelationship): Boolean; static;

    class function FindBinaryMethodCast(const ALeft, ARight: Variant;
      AUseLeft: Boolean; const ANames: array of string;
      out AResult: Variant): MethodInfo; static;
    class function FindBinaryMethodInvoke(const ALeft, ARight: Variant;
      const ANames: array of string; out AResult: Variant): Boolean; overload; static;
    class function FindBinaryMethodInvoke(const ALeft, ARight: Variant;
      const ANames: array of string; out AResult: Boolean): Boolean; overload; static;

    class function BooleanAsInteger(const Value: Boolean): Integer; static;


    //==============================================================================
    class function ToDBNull(const AObject: TObject): DBNull; static;
    class function ToInt16(const AObject: TObject): SmallInt; static;
    class function ToInt32(const AObject: TObject): Integer; static;
    class function ToSingle(const AObject: TObject): Single; static;
    class function ToDouble(const AObject: TObject): Double; static;
    class function ToDateTime(const AObject: TObject): DateTime; static;
    class function ToString(const AObject: TObject): String; overload; static;
    class function ToBoolean(const AObject: TObject): Boolean; static;
    class function ToDecimal(const AObject: TObject): Decimal; static;
    class function ToSByte(const AObject: TObject): ShortInt; static;
    class function ToByte(const AObject: TObject): Byte; static;
    class function ToUInt16(const AObject: TObject): Word; static;
    class function ToUInt32(const AObject: TObject): LongWord; static;
    class function ToInt64(const AObject: TObject): Int64; static;
    class function ToUInt64(const AObject: TObject): UInt64; static;
    class function ToChar(const AObject: TObject): Char; static;
    class function ToCurrency(const AObject: TObject): Currency; static;
    class function ToTDateTime(const AObject: TObject): TDateTime; static;
    class function ToBytes(const AObject: TObject): TBytes; static;

  //==============================================================================
  public
    class var
      // This controls how NULL variants act when doing operations.  TRUE: will
      //  cause exceptions in undefined cases.  FALSE: will simply cause the
      //  undefined cases to act like an Empty normally would.  To control
      //  how NULL behaves with comparisons use the following NullEqualityRule
      //  and NullMagnitudeRule.
      // Defaults to False;
      NullStrictOperations: Boolean;

      // This controls how NULL variants act when doing EQUALITY (= & <>) comparisons.
      //  ncrError:  comparison causes an exception
      //  ncrStrict: comparison always fails (returns FALSE)
      //  ncrLoose:  simply identity testing works (NULL = NULL and NULL <> OTHER)
      // Defaults to ncrLoose
      NullEqualityRule: TNullCompareRule;

      // This controls how NULL variants act when doing MAGNITUDE (> & <) comparisons.
      //  ncrError:  comparison causes an exception
      //  ncrStrict: comparison always fails (returns FALSE)
      //  ncrLoose:  NULLs are treated like empties (similar to how Delphi v5 worked)
      // Defaults to ncrLoose
      NullMagnitudeRule: TNullCompareRule;

      // This is the value returned when a NULL is converted into a string.  Other
      //  environments return 'NULL' instead of Delphi's default of an empty string.
      // Defaults to ''
      NullAsStringValue: string;

      // This controls how NULL variants act when doing conversions.  TRUE: will
      //  cause exceptions in undefined cases.  FALSE: will simply cause the
      //  undefined cases to return what, with one exception, Empty normally
      //  would.  The one exception is NULL to string conversion which then will
      //  return the contents of NullAsStringValue, see below.
      // Defaults to True
      NullStrictConvert: Boolean;

      // This controls how we fix up results after doing boolean to string conversions
      //  bsrAsIs:  Result is left alone
      //  bsrLower: Result will be all lowercase
      //  bsrUpper: Result will be all UPPERCASE
      // Defaults to bsrAsIs
      BooleanToStringRule: TBooleanToStringRule;

      // This is the value returned when a TRUE is converted into an ORDINAL.  For
      //  ordinals that don't support the negative values the ABS() of this value
      //  is used.
      // Defaults to -1
      BooleanTrueAsOrdinalValue: Integer;

  //==============================================================================
  public
    class constructor Create;

    //==============================================================================
    class procedure VarInvalidOpError; overload; static;
    class procedure VarInvalidOpError(const ASourceType: TVarType); overload; static;
    class procedure VarInvalidOpError(const ASourceType, ADestType: TVarType); overload; static;
    class procedure VarInvalidOpError(const AOperation: string;
      const ASourceType: TVarType); overload; static;
    class procedure VarInvalidOpError(const AOperation: string;
      const ASourceType, ADestType: TVarType); overload; static;
    class procedure VarInvalidNullOpError(const AOperation: string); static;
    class procedure VarInvalidInvokeError(const AMethod: string;
      const ASourceType: TVarType); overload; static;
    class procedure VarInvalidInvokeError(const AMethod: string;
      const ASourceType, ADestType: TVarType); overload; static;
    class procedure VarInvalidArgError; static;
    class procedure VarInvalidVarTypeError(const ASourceType: TVarType); static;
    class procedure VarTypeCastError; overload; static;
    class procedure VarTypeCastError(const ASourceType: TVarType); overload; static;
    class procedure VarTypeCastError(const ASourceType, ADestType: TVarType); overload; static;
    class procedure VarTypeNullCastError(const ASourceType: TVarType); static;
    class procedure VarTypeCastOverflowError(const ASourceType: TVarType); overload; static;
    class procedure VarTypeCastOverflowError(const ASourceType, ADestType: TVarType); overload; static;
    class procedure VarIsEmptyError; static;
    class procedure VarNotAnArrayError; static;
    class procedure VarIsAnArrayError; static;
    class procedure VarArrayCreateIllegalBoundsError; static;
    class procedure VarArrayInvalidDimError(Dim: Integer); static;
    class procedure VarTooManyCustomTypesError; static;
    class procedure VarDivideByZeroError; static;

    //==============================================================================
    class procedure UnhandledConversionException(E: Exception;
      const OldType, NewType: TVarType); static;
    class procedure UnhandledOperationException(E: Exception; const AOperation: string;
      const ALeft, ARight: Variant); overload; static;
    class procedure UnhandledOperationException(E: Exception; const AOperation: string;
      const AValue: Variant); overload; static;
    class procedure UnhandledInvokeException(E: Exception; const AMethod: string;
      const ALeft, ARight: Variant); overload; static;
    class procedure UnhandledInvokeException(E: Exception; const AMethod: string;
      const AValue: Variant); overload; static;

    //==============================================================================
    class function RegisterCustomVariantType(const ASystemType: System.Type): TVarType; static;
    class function FindCustomVariantType(const AClassName: string; out ACustomVariant: Variant): Boolean; static;

    //==============================================================================
    class function VarType(const AValue: Variant): TVarType; overload; static;
    class function VarType(const ASystemType: System.Type): TVarType; overload; static;
    class function VarType(const AObject: TObject): TVarType; overload; static;
    class function VarTypeAsText(const AValue: Variant): string; overload; static;
    class function VarTypeAsText(const AVarType: TVarType): string; overload; static;
    class function VarTypeToSystemType(const AVarType: TVarType): System.Type; static;

    //==============================================================================
    class function Unassigned: Variant; static;
    class function Null: Variant; static;

    class function IsEmpty(const AValue: Variant): Boolean; static;
    class function IsNull(const AValue: Variant): Boolean; static;
    class function IsClear(const AValue: Variant): Boolean; static;

    class procedure Clear(var AValue: Variant); static;
    class function Copy(const AValue: Variant): Variant; static;
    class function CopyAsVarType(const AValue: Variant; const AVarType: TVarType): Variant; static;
    class function CopyAsSystemType(const AValue: Variant;
      const ASystemType: System.Type; Reattempt: Boolean = True): Variant; static;

    class function ToStr(const AValue: Variant; const ANullString: string = ''): string; static;
    class function InRange(const AValue, AMin, AMax: Variant): Boolean; static;
    class function EnsureRange(const AValue, AMin, AMax: Variant): Variant; static;
    class function SameValue(const ALeft, ARight: Variant): Boolean; static;
    class function CompareValue(const ALeft, ARight: Variant): TVariantRelationship; static;

    //==============================================================================
    class function IsArray(const AValue: Variant): Boolean; static;
    class function ArrayElementsType(const AValue: Variant; out AVarType: TVarType): Boolean; overload; static;
    class function ArrayElementsType(const AValue: Variant): TVarType; overload; static;
    class function ArrayCreate(const ABounds: array of Integer;
      AVarType: TVarType): Variant; overload; static;
    class function ArrayCreate(const ABounds: array of Integer;
      ASystemType: System.Type): Variant; overload; static;
    class function ArrayOf(const AValues: array of Variant): Variant; static;
    class function ArrayDimCount(const AValue: Variant): Integer; static;
    class function ArrayLowBound(const AValue: Variant; ADim: Integer): Integer; static;
    class function ArrayHighBound(const AValue: Variant; ADim: Integer): Integer; static;
    class function ArrayGetElement(const AValue: Variant;
      const AIndices: array of Integer): Variant; static;
    class procedure ArrayPutElement(const AValue: Variant;
      const AData: Variant; const AIndices: array of Integer); static;

    function get_Items(AIndices: array of Integer): Variant;
    procedure set_Items(AIndices: array of Integer; const AValue: Variant);
    property Items[AIndices: array of Integer]: Variant read get_Items write set_Items; default;

    //==============================================================================
    class function IsError(const AValue: Variant): Boolean; overload; static;
    class function IsError(const AValue: Variant; out AResult: Integer): Boolean; overload; static;
    class function IsError(const AValue: Variant; out AException: Exception): Boolean; overload; static;
    class function FromErrorCode(const AResult: Integer): Variant; static;
    class function FromException(const AException: Exception): Variant; static;
    class function ToException(const AValue: Variant): Exception; static;

    //==============================================================================
    class operator Inc(const AValue: Variant): Variant;
    class operator Dec(const AValue: Variant): Variant;
    class operator Trunc(const AValue: Variant): Variant;
    class operator Round(const AValue: Variant): Variant;

    class operator Negative(const AValue: Variant): Variant;
    class operator Positive(const AValue: Variant): Variant;

    class operator Add(const ALeft, ARight: Variant): Variant;
    class operator Subtract(const ALeft, ARight: Variant): Variant;
    class operator Multiply(const ALeft, ARight: Variant): Variant;
    class operator Divide(const ALeft, ARight: Variant): Variant;
    class operator IntDivide(const ALeft, ARight: Variant): Variant;
    class operator Modulus(const ALeft, ARight: Variant): Variant;

    class operator LogicalNot(const AValue: Variant): Variant;
    class operator LogicalAnd(const ALeft, ARight: Variant): Variant;
    class operator LogicalOr(const ALeft, ARight: Variant): Variant;
    class operator LogicalXor(const ALeft, ARight: Variant): Variant;

    class operator LeftShift(const ALeft, ARight: Variant): Variant;
    class operator RightShift(const ALeft, ARight: Variant): Variant;

    class operator Equal(const ALeft, ARight: Variant): Boolean;
    class operator NotEqual(const ALeft, ARight: Variant): Boolean;
    class operator LessThan(const ALeft, ARight: Variant): Boolean;
    class operator LessThanOrEqual(const ALeft, ARight: Variant): Boolean;
    class operator GreaterThan(const ALeft, ARight: Variant): Boolean;
    class operator GreaterThanOrEqual(const ALeft, ARight: Variant): Boolean;

    //==============================================================================
    class operator Implicit(const AValue: DBNull): Variant;
    class operator Implicit(const AValue: SmallInt): Variant;
    class operator Implicit(const AValue: Integer): Variant;
    class operator Implicit(const AValue: Single): Variant;
    class operator Implicit(const AValue: Double): Variant;
    class operator Implicit(const AValue: DateTime): Variant;
    class operator Implicit(const AValue: String): Variant;
    class operator Implicit(const AValue: Boolean): Variant;
    class operator Implicit(const AValue: Decimal): Variant;
    class operator Implicit(const AValue: ShortInt): Variant;
    class operator Implicit(const AValue: Byte): Variant;
    class operator Implicit(const AValue: Word): Variant;
    class operator Implicit(const AValue: LongWord): Variant;
    class operator Implicit(const AValue: Int64): Variant;
    class operator Implicit(const AValue: UInt64): Variant;
    class operator Implicit(const AValue: Char): Variant;
    class operator Implicit(const AValue: Currency): Variant;
    class operator Implicit(const AValue: TDateTime): Variant;
    class operator Implicit(const AValue: Extended): Variant;
    class operator Implicit(const AValue: TBytes): Variant;
    class operator Implicit(const AValue: AnsiString): Variant;

    class operator Implicit(const AValue: Variant): DBNull;
    class operator Implicit(const AValue: Variant): SmallInt;
    class operator Implicit(const AValue: Variant): Integer;
    class operator Implicit(const AValue: Variant): Single;
    class operator Implicit(const AValue: Variant): Double;
    class operator Implicit(const AValue: Variant): DateTime;
    class operator Implicit(const AValue: Variant): String;
    class operator Implicit(const AValue: Variant): Boolean;
    class operator Implicit(const AValue: Variant): Decimal;
    class operator Implicit(const AValue: Variant): ShortInt;
    class operator Implicit(const AValue: Variant): Byte;
    class operator Implicit(const AValue: Variant): Word;
    class operator Implicit(const AValue: Variant): LongWord;
    class operator Implicit(const AValue: Variant): Int64;
    class operator Implicit(const AValue: Variant): UInt64;
    class operator Implicit(const AValue: Variant): Char;
    class operator Implicit(const AValue: Variant): Currency;
    class operator Implicit(const AValue: Variant): TDateTime;
    class operator Implicit(const AValue: Variant): Extended;
    class operator Implicit(const AValue: Variant): TBytes;
    class operator Implicit(const AValue: Variant): AnsiString;
    class operator Implicit(const AValue: Variant): WordBool;
    class operator Implicit(const AValue: Variant): LongBool;

    /// Explicit Variant<->TObject is handled by the compiler
    /// LVariant := Variant(Objreference)
    /// LObject := TObject(Varreference)
  end;

  //==============================================================================
  OleVariantHelper = class helper for OleVariant
  public
    function get_Items(AIndices: array of Integer): OleVariant;
    procedure set_Items(AIndices: array of Integer; const AValue: OleVariant);
    property Items[AIndices: array of Integer]: OleVariant read get_Items write set_Items; default;

    //==============================================================================
    class operator Inc(const AValue: OleVariant): OleVariant;
    class operator Dec(const AValue: OleVariant): OleVariant;
    class operator Trunc(const AValue: OleVariant): OleVariant;
    class operator Round(const AValue: OleVariant): OleVariant;

    class operator Negative(const AValue: OleVariant): OleVariant;
    class operator Positive(const AValue: OleVariant): OleVariant;

    class operator Add(const ALeft, ARight: OleVariant): OleVariant;
    class operator Subtract(const ALeft, ARight: OleVariant): OleVariant;
    class operator Multiply(const ALeft, ARight: OleVariant): OleVariant;
    class operator Divide(const ALeft, ARight: OleVariant): OleVariant;
    class operator IntDivide(const ALeft, ARight: OleVariant): OleVariant;
    class operator Modulus(const ALeft, ARight: OleVariant): OleVariant;

    class operator LogicalNot(const AValue: OleVariant): OleVariant;
    class operator LogicalAnd(const ALeft, ARight: OleVariant): OleVariant;
    class operator LogicalOr(const ALeft, ARight: OleVariant): OleVariant;
    class operator LogicalXor(const ALeft, ARight: OleVariant): OleVariant;

    class operator LeftShift(const ALeft, ARight: OleVariant): OleVariant;
    class operator RightShift(const ALeft, ARight: OleVariant): OleVariant;

    class operator Equal(const ALeft, ARight: OleVariant): Boolean;
    class operator NotEqual(const ALeft, ARight: OleVariant): Boolean;
    class operator LessThan(const ALeft, ARight: OleVariant): Boolean;
    class operator LessThanOrEqual(const ALeft, ARight: OleVariant): Boolean;
    class operator GreaterThan(const ALeft, ARight: OleVariant): Boolean;
    class operator GreaterThanOrEqual(const ALeft, ARight: OleVariant): Boolean;

    //==============================================================================
    class operator Implicit(const AValue: DBNull): OleVariant;
    class operator Implicit(const AValue: SmallInt): OleVariant;
    class operator Implicit(const AValue: Integer): OleVariant;
    class operator Implicit(const AValue: Single): OleVariant;
    class operator Implicit(const AValue: Double): OleVariant;
    class operator Implicit(const AValue: DateTime): OleVariant;
    class operator Implicit(const AValue: String): OleVariant;
    class operator Implicit(const AValue: Boolean): OleVariant;
    class operator Implicit(const AValue: Decimal): OleVariant;
    class operator Implicit(const AValue: ShortInt): OleVariant;
    class operator Implicit(const AValue: Byte): OleVariant;
    class operator Implicit(const AValue: Word): OleVariant;
    class operator Implicit(const AValue: LongWord): OleVariant;
    class operator Implicit(const AValue: Int64): OleVariant;
    class operator Implicit(const AValue: UInt64): OleVariant;
    class operator Implicit(const AValue: Char): OleVariant;
    class operator Implicit(const AValue: TDateTime): OleVariant;
    class operator Implicit(const AValue: Currency): OleVariant;
    class operator Implicit(const AValue: Extended): OleVariant;
    class operator Implicit(const AValue: TBytes): OleVariant;

    class operator Implicit(const AValue: OleVariant): DBNull;
    class operator Implicit(const AValue: OleVariant): SmallInt;
    class operator Implicit(const AValue: OleVariant): Integer;
    class operator Implicit(const AValue: OleVariant): Single;
    class operator Implicit(const AValue: OleVariant): Double;
    class operator Implicit(const AValue: OleVariant): DateTime;
    class operator Implicit(const AValue: OleVariant): String;
    class operator Implicit(const AValue: OleVariant): Boolean;
    class operator Implicit(const AValue: OleVariant): Decimal;
    class operator Implicit(const AValue: OleVariant): ShortInt;
    class operator Implicit(const AValue: OleVariant): Byte;
    class operator Implicit(const AValue: OleVariant): Word;
    class operator Implicit(const AValue: OleVariant): LongWord;
    class operator Implicit(const AValue: OleVariant): Int64;
    class operator Implicit(const AValue: OleVariant): UInt64;
    class operator Implicit(const AValue: OleVariant): Char;
    class operator Implicit(const AValue: OleVariant): TDateTime;
    class operator Implicit(const AValue: OleVariant): Currency;
    class operator Implicit(const AValue: OleVariant): Extended;
    class operator Implicit(const AValue: OleVariant): TBytes;

    class operator Implicit(const AValue: OleVariant): Variant;
    class operator Implicit(const AValue: Variant): OleVariant;
  end;

implementation

{$RANGECHECKS ON}
{$OVERFLOWCHECKS ON}
{$FINITEFLOAT ON}

uses
  SysConst;

                                                                       
const
  // goes from TypeCode to our vartype
  CTypeCodeToVarTypeArray: array [0..20] of TVarType =
    { 0: Empty}   (varEmpty,
    { 1: Object}   varObject,
    { 2: DBNull}   varNull,
    { 3: Boolean}  varBoolean,
    { 4: Char}     varChar,
    { 5: Sbyte}    varShortInt,
    { 6: Byte}     varByte,
    { 7: Int16}    varSmallInt,
    { 8: UInt16}   varWord,
    { 9: Int32}    varInteger,
    {10: UInt32}   varLongWord,
    {11: Int64}    varInt64,
    {12: UInt64}   varUInt64,
    {13: Single}   varSingle,
    {14: Double}   varDouble,
    {15: Decimal}  varDecimal,
    {16: DateTime} varDateTime,
    {17: n/a}      varUndefined,
    {18: String}   varString,
    {19: n/a}      varUndefined,
    {20: n/a}      varUndefined);

  // goes from TVarType to TSimpleType
  CVarTypeToSimpleTypeArray: array [varFirst..varLast] of TSimpleType =
    {varEmpty    = $0000}(stEmp,
    {varNull     = $0001} stNul,
    {varSmallInt = $0002} stI32,
    {varInteger  = $0003} stI32,
    {varSingle   = $0004} stDbl,
    {varDouble   = $0005} stDbl,
    {varCurrency = $0006} stDec,
    {varDate     = $0007} stDtm,
    {varString   = $0008} stStr,
    {varDispatch * $0009} stErr,
    {varError    = $000A} stErr,
    {varBoolean  = $000B} stBoo,
    {varObject   = $000C} stObj,
    {varUnknown  * $000D} stErr,
    {varDecimal  = $000E} stDec,
    {UNUSED SLOT * $000F} stErr,
    {varShortInt = $0010} stI32,
    {varByte     = $0011} stU32,
    {varWord     = $0012} stU32,
    {varLongWord = $0013} stU32,
    {varInt64    = $0014} stI64,
    {varUInt64   = $0015} stU64,
    {varChar     = $0016} stStr,
    {varDateTime = $0017} stDtm);

  // goes from two simple types to a unified simple type
  CCoercionMatrix: array [TSimpleType, TSimpleType] of TSimpleType = (
           {stErr, stEmp, stNul, stBoo, stI32, stU32, stI64, stU64, stDec, stDtm, stDbl, stStr, stObj}
    {stErr}(stErr, stErr, stErr, stErr, stErr, stErr, stErr, stErr, stErr, stErr, stErr, stErr, stErr),
    {stEmp}(stErr, stEmp, stNul, stBoo, stI32, stU32, stI64, stU64, stDec, stDtm, stDbl, stStr, stObj),
    {stNul}(stErr, stNul, stNul, stNul, stNul, stNul, stNul, stNul, stNul, stNul, stNul, stNul, stNul),
    {stBoo}(stErr, stBoo, stNul, stBoo, stI32, stU32, stI64, stU64, stDec, stDtm, stDbl, stBoo, stObj),
    {stI32}(stErr, stI32, stNul, stI32, stI32, stU32, stI64, stU64, stDec, stDtm, stDbl, stI32, stObj),
    {stU32}(stErr, stU32, stNul, stU32, stU32, stU32, stU64, stU64, stDec, stDtm, stDbl, stU32, stObj),
    {stI64}(stErr, stI64, stNul, stI64, stI64, stU64, stI64, stU64, stDec, stDtm, stDbl, stI64, stObj),
    {stU64}(stErr, stU64, stNul, stU64, stU64, stU64, stU64, stU64, stDec, stDtm, stDbl, stU64, stObj),
    {stDec}(stErr, stDec, stNul, stDec, stDec, stDec, stDec, stDec, stDec, stDtm, stDbl, stDec, stObj),
    {stDtm}(stErr, stDtm, stNul, stDtm, stDtm, stDtm, stDtm, stDtm, stDtm, stDtm, stDtm, stDtm, stObj),
    {stDbl}(stErr, stDbl, stNul, stDbl, stDbl, stDbl, stDbl, stDbl, stDbl, stDtm, stDbl, stDbl, stObj),
    {stStr}(stErr, stStr, stNul, stBoo, stI32, stU32, stI64, stU64, stDec, stDtm, stDbl, stStr, stObj),
    {stObj}(stErr, stObj, stNul, stObj, stObj, stObj, stObj, stObj, stObj, stObj, stObj, stObj, stObj));

  // goes from TVarType to actual System.Type
  CVarTypeToClass: array [varFirst..varLast] of System.Type =
    {varEmpty    = $0000}(nil,
    {varNull     = $0001} TypeOf(DBNull),
    {varSmallInt = $0002} TypeOf(SmallInt),
    {varInteger  = $0003} TypeOf(Integer),
    {varSingle   = $0004} TypeOf(Single),
    {varDouble   = $0005} TypeOf(Double),
    {varCurrency = $0006} TypeOf(Currency),
    {varDate     = $0007} TypeOf(TDateTime),
    {varString   = $0008} TypeOf(String),
    {varDispatch * $0009} nil,
    {varError    = $000A} TypeOf(Exception),
    {varBoolean  = $000B} TypeOf(Boolean),
    {varObject   = $000C} TypeOf(TObject),
    {varUnknown  * $000D} nil,
    {varDecimal  = $000E} TypeOf(Decimal),
    {UNUSED SLOT * $000F} nil,
    {varShortInt = $0010} TypeOf(ShortInt),
    {varByte     = $0011} TypeOf(Byte),
    {varWord     = $0012} TypeOf(Word),
    {varLongWord = $0013} TypeOf(LongWord),
    {varInt64    = $0014} TypeOf(Int64),
    {varUInt64   = $0015} TypeOf(UInt64),
    {varChar     = $0016} TypeOf(Char),
    {varDateTime = $0017} TypeOf(DateTime));

  // goes from TVarType to 'readable' english text
  CVarTypeAsText: array [varFirst..varLast] of string =
    {varEmpty    = $0000}('Empty',     // do not localize
    {varNull     = $0001} 'Null',      // do not localize
    {varSmallInt = $0002} 'SmallInt',  // do not localize
    {varInteger  = $0003} 'Integer',   // do not localize
    {varSingle   = $0004} 'Single',    // do not localize
    {varDouble   = $0005} 'Double',    // do not localize
    {varCurrency = $0006} 'Currency',  // do not localize
    {varDate     = $0007} 'TDateTime', // do not localize
    {varString   = $0008} 'String',    // do not localize
    {varDispatch * $0009} '',          // do not localize
    {varError    = $000A} 'Error',     // do not localize
    {varBoolean  = $000B} 'Boolean',   // do not localize
    {varObject   = $000C} 'Object',    // do not localize
    {varUnknown  * $000D} '',          // do not localize
    {varDecimal  = $000E} 'Decimal',   // do not localize
    {UNUSED SLOT * $000F} '',          // do not localize
    {varShortInt = $0010} 'ShortInt',  // do not localize
    {varByte     = $0011} 'Byte',      // do not localize
    {varWord     = $0012} 'Word',      // do not localize
    {varLongWord = $0013} 'LongWord',  // do not localize
    {varInt64    = $0014} 'Int64',     // do not localize
    {varUInt64   = $0015} 'UInt64',    // do not localize
    {varChar     = $0016} 'Char',      // do not localize
    {varDateTime = $0017} 'DateTime'); // do not localize

  // misc consts used by VariantHelper
  CArrayVarTypeAsText = 'Array of %s';          // do not localize
  CCopyOp = 'Copy';                             // do not localize
  CTruncOp = 'Trunc';                           // do not localize
  CRoundOp = 'Round';                           // do not localize
  CNegativeOp = 'Negative';                     // do not localize
  CPositiveOp = 'Positive';                     // do not localize
  CAddOp = 'Add';                               // do not localize
  CSubtractOp = 'Subtract';                     // do not localize
  CMultiplyOp = 'Multiply';                     // do not localize
  CDivideOp = 'Divide';                         // do not localize
  CIntDivideOp = 'IntDivide';                   // do not localize
  CModulusOp = 'Modulus';                       // do not localize
  CLogicalNotOp = 'LogicalNot';                 // do not localize
  CLogicalAndOp = 'LogicalAnd';                 // do not localize
  CLogicalOrOp = 'LogicalOr';                   // do not localize
  CLogicalXorOp = 'LogicalXor';                 // do not localize
  CLeftShiftOp = 'LeftShift';                   // do not localize
  CRightShiftOp = 'RightShift';                 // do not localize
  CEqualOp = 'Equal';                           // do not localize
  CNotEqualOp = 'NotEqual';                     // do not localize
  CLessThanOp = 'LessThan';                     // do not localize
  CLessThanOrEqualOp = 'LessThanOrEqual';       // do not localize
  CGreaterThanOp = 'GreaterThan';               // do not localize
  CGreaterThanOrEqualOp = 'GreaterThanOrEqual'; // do not localize

{ CustomVariantType support }
{ There is room for 3839 ($0EFF) custom types, more than twice as much as
  Delphi 7.  Not that anyone whould ever used that many. }
const
  FirstCustomVarType = 0;
  FirstCustomVarTypeOffset = $0100;
  MaximumNumberOfCustomVarTypes = $0EFF;
  CustomVarTypeIncrement = $000F;

const
  COrdinalTypes = [varBoolean, varShortInt, varByte, varSmallInt, varWord,
                   varInteger, varLongWord, varInt64, varUInt64];
  CFloatTypes =   [varDecimal, varCurrency, varSingle, varDouble];
  CStringTypes =  [varChar, varString];

//==============================================================================
class constructor VariantHelper.Create;
begin
  NullStrictOperations := False;
  NullEqualityRule := ncrLoose;
  NullMagnitudeRule := ncrLoose;
  NullAsStringValue := '';
  NullStrictConvert := True;
  BooleanToStringRule := bsrAsIs;
  BooleanTrueAsOrdinalValue := -1;

  LastCustomVarType := FirstCustomVarType - 1;
  CustomVarTypeLocker := TMultiReadExclusiveWriteSynchronizer.Create;
end;


//==============================================================================
class function VariantHelper.FindTypeCode(const AValue: Variant): TypeCode;
begin
  if AValue = nil then
    Result := TypeCode.Empty
  else
    Result := Convert.GetTypeCode(AValue);
end;

class function VariantHelper.FindSimpleType(const AValue: Variant;
  out AVarType: TVarType): TSimpleType;
begin
  AVarType := CTypeCodeToVarTypeArray[Integer(FindTypeCode(AValue))];
  if AVarType = varObject then
    if AValue is Currency then
      AVarType := varCurrency
    else if AValue is TDateTime then
      AVarType := varDate;
  Result := CVarTypeToSimpleTypeArray[AVarType];
end;

class function VariantHelper.FindSimpleType(const ALeft, ARight: Variant; out LeftVarType,
  RightVarType: TVarType): TSimpleType;
begin
  Result := CCoercionMatrix[FindSimpleType(ALeft, LeftVarType),
                            FindSimpleType(ARight, RightVarType)];
end;

//==============================================================================
class function VariantHelper.SafeGetType(const AInstance: TObject): System.Type;
begin
  if AInstance = nil then
    Result := TypeOf(TObject)
  else
    Result := AInstance.GetType;
end;

class function VariantHelper.FindTypeInvoke(const AType: System.Type;
  const AValue: Variant; const ANames: array of string;
  const AParams: array of Variant; out AResult: Variant): Boolean;
var
  LMethod: MethodInfo;
  I, LParamCount: Integer;
  LObjArray: array of TObject;
  LTypeArray: array of System.Type;
begin
  // assume the worst
  Result := False;

  // create a param type array
  LParamCount := Length(AParams);
  SetLength(LObjArray, LParamCount);
  SetLength(LTypeArray, LParamCount);
  for I := 0 to LParamCount - 1 do
  begin
    LObjArray[I] := AParams[I];
    LTypeArray[I] := SafeGetType(LObjArray[I]);
  end;

  // try to find the method
  LMethod := nil;
  for I := Low(ANames) to High(ANames) do
  begin

    // find the ARight method for the job at hand
    LMethod := AType.GetMethod(ANames[I], LTypeArray);
    if LMethod <> nil then
      Break;
  end;

  // then try to invoke it
  if LMethod <> nil then
  try
    AResult := Variant(LMethod.Invoke(AValue, LObjArray));
    Result := True;
  except
    on E: Exception do
      if LParamCount > 0 then
        if AValue <> nil then
          UnhandledInvokeException(E, LMethod.Name, AValue, AParams[0])
        else
          UnhandledInvokeException(E, LMethod.Name, AParams[0])
      else
        raise;
    else
      Result := False;
  end;
end;

class function VariantHelper.FindAndInvoke(const AReference, AValue: Variant;
  const ANames: array of string; const AParams: array of Variant;
  out AResult: Variant): Boolean;
begin
  Result := FindTypeInvoke(SafeGetType(AReference), AValue, ANames, AParams, AResult);
end;

class function VariantHelper.FindClassInvoke(const AReference: Variant;
  const ANames: array of string; const AParams: array of Variant;
  out AResult: Variant): Boolean;
begin
  Result := FindAndInvoke(AReference, Unassigned, ANames, AParams, AResult);
end;

class function VariantHelper.FindClassInvoke(const AReference: Variant;
  const ANames: array of string; const AParams: array of Variant;
  out AResult: Boolean): Boolean;
var
  LResult: Variant;
begin
  Result := FindAndInvoke(AReference, Unassigned, ANames, AParams, LResult);
  if Result then
    AResult := Boolean(LResult);
end;

class function VariantHelper.FindObjectInvoke(const AValue: Variant;
  const ANames: array of string; const AParams: array of Variant;
  out AResult: Variant): Boolean;
begin
  Result := FindAndInvoke(AValue, AValue, ANames, AParams, AResult);
end;

class function VariantHelper.FindObjectInvoke(const AValue: Variant;
  const ANames: array of string; const AParams: array of Variant;
  out AResult: Boolean): Boolean;
var
  LResult: Variant;
begin
  Result := FindAndInvoke(AValue, AValue, ANames, AParams, LResult);
  if Result then
    AResult := Boolean(LResult);
end;

class function VariantHelper.ConvertToType(const AValue: Variant;
  const ANewType: System.Type): Variant;
begin
  Result := nil;
  if not FindTypeInvoke(ANewType, nil, ['FromObject', 'op_Implicit', 'op_Explicit'], [AValue], Result) then
    Result := Variant(Convert.ChangeType(AValue, ANewType));
end;

class function VariantHelper.FindUnifiedType(const ALeft, ARight: Variant;
  out AUnifiedType: System.Type; out ANewLeft, ANewRight: Variant): Boolean;
var
  LNewLeft, LNewRight: Variant;
  LLeftType, LRightType: System.Type;
begin
  LNewLeft := ALeft;
  LNewRight := ARight;
  LLeftType := SafeGetType(LNewLeft);
  LRightType := SafeGetType(LNewRight);

  if LLeftType.Equals(LRightType) then
    AUnifiedType := LLeftType
  else
    try
      LNewRight := ConvertToType(LNewRight, LLeftType);
      AUnifiedType := LLeftType;
    except
      try
        LNewLeft := ConvertToType(LNewLeft, LRightType);
        AUnifiedType := LRightType;
      except
        LNewLeft := nil;
        LNewRight := nil;
        AUnifiedType := nil;
      end;
    end;

  Result := AUnifiedType <> nil;
end;

class function VariantHelper.FindUnifiedCompare(const ALeft, ARight: Variant;
  out ARelationship: TVariantRelationship): Boolean;
var
  LUnifiedType: System.Type;
  LNewLeft, LNewRight: Variant;
  LCompareResult: Integer;
begin
  ARelationship := vrNotEqual;
  if FindUnifiedType(ALeft, ARight, LUnifiedType, LNewLeft, LNewRight) and
     (LNewLeft is IComparable) then
    try
      LCompareResult := (LNewLeft as IComparable).CompareTo(LNewRight);
      if LCompareResult < 0 then
        ARelationship := vrLessThan
      else if LCompareResult > 0 then
        ARelationship := vrGreaterThan
      else
        ARelationship := vrEqual;
      Result := True;
    except
      Result := False;
    end
  else
    Result := False;
end;

class function VariantHelper.FindBinaryMethodCast(const ALeft, ARight: Variant;
  AUseLeft: Boolean; const ANames: array of string;
  out AResult: Variant): MethodInfo;
var
  I, J: Integer;
  LMethods: array of MethodInfo;
  LMethod: MethodInfo;
  LParams: array of ParameterInfo;
begin
  Result := nil;

  if AUseLeft then
    LMethods := SafeGetType(ALeft).GetMethods
  else
    LMethods := SafeGetType(ARight).GetMethods;

  for I := 0 to Length(LMethods) - 1 do
  begin
    LMethod := LMethods[I];
    for J := Low(ANames) to High(ANames) do
    begin
      if SameText(LMethod.Name, ANames[J]) then
      begin
        LParams := LMethod.GetParameters;
        if Length(LParams) = 2 then
        begin
          if AUseLeft then
          begin
            if LParams[0].ParameterType.IsInstanceOfType(ALeft) then
            begin
              try
                AResult := CopyAsSystemType(ARight, LParams[1].ParameterType);
                Result := LMethod;
                Exit;
              except
                // ignore the exception and look for another overload or method
              end;
            end;
          end
          else // use ARight
          begin
            if LParams[1].ParameterType.IsInstanceOfType(ARight) then
            begin
              try
                AResult := CopyAsSystemType(ALeft, LParams[0].ParameterType);
                Result := LMethod;
                Exit;
              except
                // ignore the exception and look for another overload or method
              end;
            end;
          end;
        end;
      end;
    end;
  end;
end;

class function VariantHelper.FindBinaryMethodInvoke(const ALeft, ARight: Variant;
  const ANames: array of string; out AResult: Variant): Boolean;
var
  LMethod: MethodInfo;
  LTypeArray: array of System.Type;
  LObjects: array of TObject;
  I: Integer;
  LRefObj, LCastVal: Variant;
begin
  // assume the worst
  Result := False;
  LMethod := nil;

  // initialize to the most likely case
  SetLength(LObjects, 2);
  LObjects[0] := ALeft;
  LObjects[1] := ARight;
  LRefObj := ALeft;

  // first check if there is an exact match on types
  SetLength(LTypeArray, 2);
  LTypeArray[0] := SafeGetType(LObjects[0]);
  LTypeArray[1] := SafeGetType(LObjects[1]);
  for I := Low(ANames) to High(ANames) do
  begin
    LMethod := SafeGetType(ALeft).GetMethod(ANames[I], LTypeArray);
    if LMethod <> nil then
      Break;
    LMethod := SafeGetType(ARight).GetMethod(ANames[I], LTypeArray);
    if LMethod <> nil then
    begin
      LRefObj := ARight;
      Break;
    end;
  end;

  // if that didn't work, we need to look for a match we can use with a type
  // cast. Try casting the ARight hand side first.
  if LMethod = nil then
  begin
    LMethod := FindBinaryMethodCast(ALeft, ARight, True, ANames, LCastVal);
    if LMethod <> nil then
      LObjects[1] := LCastVal
    else // ok, try casting the ALeft hand side
    begin
      LMethod := FindBinaryMethodCast(ALeft, ARight, False, ANames, LCastVal);
      if LMethod <> nil then
      begin
        LRefObj := ARight;
        LObjects[0] := LCastVal;
      end;
    end;
  end;

  // then try to invoke it
  if LMethod <> nil then
    try
      AResult := Variant(LMethod.Invoke(LRefObj, LObjects));
      Result := True;
    except
      on E: Exception do
        UnhandledInvokeException(E, LMethod.Name, ALeft, ARight);
      else
        Result := False;
    end;
end;

class function VariantHelper.FindBinaryMethodInvoke(const ALeft, ARight: Variant;
  const ANames: array of string; out AResult: Boolean): Boolean;
var
  LResult: Variant;
begin
  Result := FindBinaryMethodInvoke(ALeft, ARight, ANames, LResult);
  if Result then
    AResult := Boolean(LResult);
end;

//==============================================================================
class procedure VariantHelper.VarInvalidOpError;
begin
  raise EVariantInvalidOpError.Create(SInvalidVarOp);
end;

class procedure VariantHelper.VarInvalidOpError(const ASourceType: TVarType);
begin
  raise EVariantInvalidOpError.CreateFmt(SVariantInvalidOpOn1,
    [VarTypeAsText(ASourceType)]);
end;

class procedure VariantHelper.VarInvalidOpError(const ASourceType, ADestType: TVarType);
begin
  raise EVariantInvalidOpError.CreateFmt(SVariantInvalidOpOn2,
    [VarTypeAsText(ASourceType), VarTypeAsText(ADestType)]);
end;

class procedure VariantHelper.VarInvalidOpError(const AOperation: string; const ASourceType: TVarType);
begin
  raise EVariantInvalidOpError.CreateFmt(SVariantInvalidOpXOn1,
    [AOperation, VarTypeAsText(ASourceType)]);
end;

class procedure VariantHelper.VarInvalidOpError(const AOperation: string; const ASourceType, ADestType: TVarType);
begin
  raise EVariantInvalidOpError.CreateFmt(SVariantInvalidOpXOn2,
    [AOperation, VarTypeAsText(ASourceType), VarTypeAsText(ADestType)]);
end;

class procedure VariantHelper.VarInvalidNullOpError(const AOperation: string);
begin
  raise EVariantInvalidNullOpError.CreateFmt(SInvalidVarNullOp, [AOperation]);
end;

class procedure VariantHelper.VarInvalidInvokeError(const AMethod: string; const ASourceType: TVarType);
begin
  raise EVariantInvalidInvokeError.CreateFmt(SVariantInvalidInvoke,
    [AMethod, VarTypeAsText(ASourceType)]);
end;

class procedure VariantHelper.VarInvalidInvokeError(const AMethod: string; const ASourceType, ADestType: TVarType);
begin
  raise EVariantInvalidInvokeError.CreateFmt(SVariantInvalidInvoke,
    [AMethod, VarTypeAsText(ASourceType), VarTypeAsText(ADestType)]);
end;

class procedure VariantHelper.VarInvalidVarTypeError(const ASourceType: TVarType);
begin
  raise EVariantInvalidVarTypeError.CreateFmt(SVariantInvalidVarType,
    [ASourceType]);
end;

class procedure VariantHelper.VarInvalidArgError;
begin
  raise EVariantInvalidArgError.Create(SVarInvalid);
end;

class procedure VariantHelper.VarTypeCastError;
begin
  raise EVariantTypeCastError.Create(SInvalidVarCast);
end;

class procedure VariantHelper.VarTypeCastError(const ASourceType: TVarType);
begin
  raise EVariantTypeCastError.CreateFmt(SVariantTypeCastOn,
    [VarTypeAsText(ASourceType)]);
end;

class procedure VariantHelper.VarTypeCastError(const ASourceType, ADestType: TVarType);
begin
  raise EVariantTypeCastError.CreateFmt(SVariantTypeCastTo,
    [VarTypeAsText(ASourceType), VarTypeAsText(ADestType)]);
end;

class procedure VariantHelper.VarTypeNullCastError(const ASourceType: TVarType);
begin
  raise EVariantTypeNullCastError.CreateFmt(SVariantTypeCastTo,
    [VarTypeAsText(ASourceType), VarTypeAsText(varNull)]);
end;

class procedure VariantHelper.VarTypeCastOverflowError(const ASourceType: TVarType);
begin
  raise EVariantTypeCastOverflowError.CreateFmt(SVarTypeConvertOverflow1,
    [VarTypeAsText(ASourceType)]);
end;

class procedure VariantHelper.VarTypeCastOverflowError(const ASourceType, ADestType: TVarType);
begin
  raise EVariantTypeCastOverflowError.CreateFmt(SVarTypeConvertOverflow2,
    [VarTypeAsText(ASourceType), VarTypeAsText(ADestType)]);
end;

class procedure VariantHelper.VarIsEmptyError;
begin
  raise EVariantIsEmptyError.Create(SVarIsEmpty);
end;

class procedure VariantHelper.VarNotAnArrayError;
begin
  raise EVariantNotAnArrayError.Create(SVarInvalid);
end;

class procedure VariantHelper.VarIsAnArrayError;
begin
  raise EVariantIsAnArrayError.Create(SVarInvalid);
end;

class procedure VariantHelper.VarArrayCreateIllegalBoundsError;
begin
  raise EVariantArrayCreateError.Create(SVariantArrayCreateErrorIllegalBounds);
end;

class procedure VariantHelper.VarArrayInvalidDimError(Dim: Integer);
begin
  raise EVariantArrayInvalidDim.CreateFmt(SVariantArrayInvalidDim, [Dim]);
end;

class procedure VariantHelper.VarTooManyCustomTypesError;
begin
  raise EVariantTooManyCustomTypes.Create(SVarTypeTooManyCustomTypes);
end;

class procedure VariantHelper.VarDivideByZeroError;
begin
  raise EDivByZero.Create(SDivByZero);
end;


//==============================================================================
class function VariantHelper.HandleExceptionTranslation(E: Exception;
  const AFirstType, ASecondType: TVarType): Boolean;
begin
  Result := True;

  if (E is EDivByZero) or (E is EZeroDivide) then
    raise E

  else if (E is OverflowException) or
          (E is ERangeError) or
          (E is EOverflow) or
          (E is ArgumentOutOfRangeException) or
          (E is EVariantTypeCastOverflowError) then
    VarTypeCastOverflowError(AFirstType, ASecondType)

  else if (E is FormatException) or
          (E is ArithmeticException) or
          (E is InvalidCastException) or
          (E is EConvertError) or
          (E is ArgumentException) or
          (E is EVariantTypeCastError) then
    VarTypeCastError(AFirstType, ASecondType)

  else if E is TargetInvocationException then
    Result := HandleExceptionTranslation(TargetInvocationException(E).InnerException, AFirstType, ASecondType);
end;

class function VariantHelper.HandleExceptionTranslation(E: Exception;
  const AType: TVarType): Boolean;
begin
  Result := True;

  if (E is EDivByZero) or (E is EZeroDivide) then
    raise E

  else if (E is OverflowException) or
          (E is ERangeError) or
          (E is EOverflow) or
          (E is ArgumentOutOfRangeException) or
          (E is EVariantTypeCastOverflowError) then
    VarTypeCastOverflowError(AType)

  else if (E is FormatException) or
          (E is ArithmeticException) or
          (E is InvalidCastException) or
          (E is EConvertError) or
          (E is ArgumentException) or
          (E is EVariantTypeCastError) then
    VarTypeCastError(AType)

  else if (E is TargetInvocationException) then
    Result := HandleExceptionTranslation(TargetInvocationException(E).InnerException, AType);
end;

class procedure VariantHelper.UnhandledConversionException(E: Exception;
  const OldType, NewType: TVarType);
begin
  if HandleExceptionTranslation(E, OldType, NewType) then
    VarTypeCastError(OldType, NewType);
end;

class procedure VariantHelper.UnhandledOperationException(E: Exception;
  const AOperation: string; const ALeft, ARight: Variant);
begin
  if HandleExceptionTranslation(E, VarType(ALeft), VarType(ARight)) then
    VarInvalidOpError(AOperation, VarType(ALeft), VarType(ARight));
end;

class procedure VariantHelper.UnhandledOperationException(E: Exception;
  const AOperation: string; const AValue: Variant);
begin
  if HandleExceptionTranslation(E, VarType(AValue)) then
    VarInvalidOpError(AOperation, VarType(AValue));
end;

class procedure VariantHelper.UnhandledInvokeException(E: Exception;
  const AMethod: string; const ALeft, ARight: Variant);
begin
  if HandleExceptionTranslation(E, VarType(ALeft), VarType(ARight)) then
    VarInvalidInvokeError(AMethod, VarType(ALeft), VarType(ARight));
end;

class procedure VariantHelper.UnhandledInvokeException(E: Exception;
  const AMethod: string; const AValue: Variant);
begin
  if HandleExceptionTranslation(E, VarType(AValue)) then
    VarInvalidInvokeError(AMethod, VarType(AValue));
end;

//==============================================================================
class function VariantHelper.RegisterCustomVariantType(const ASystemType: System.Type): TVarType;
var
  I: Integer;
begin
  if CustomVarTypeLocker.BeginWrite then
    try
      // try to find it first
      for I := FirstCustomVarType to LastCustomVarType do
        if CustomVarTypes[I].Equals(ASystemType) then
        begin
          Result := I + FirstCustomVarTypeOffset;
          Exit;
        end;

      // ok then try to add it to the list
      Result := LastCustomVarType + 1;
      if Result > MaximumNumberOfCustomVarTypes then
        VarTooManyCustomTypesError;
      if Result >= Length(CustomVarTypes) then
        SetLength(CustomVarTypes, ((Result div CustomVarTypeIncrement) + 1) * CustomVarTypeIncrement);
      CustomVarTypes[Result] := ASystemType;
      LastCustomVarType := Result;
      Inc(Result, FirstCustomVarTypeOffset);
    finally
      CustomVarTypeLocker.EndWrite;
    end
  else
    Result := varUndefined;
end;

class function VariantHelper.FindCustomVariantType(const AClassName: string;
  out ACustomVariant: Variant): Boolean;
var
  I: Integer;
  LConstructor: ConstructorInfo;
begin
  ACustomVariant := Unassigned;
  CustomVarTypeLocker.BeginRead;
  try
    for I := FirstCustomVarType to LastCustomVarType do
      if CustomVarTypes[I].ClassName = AClassName then
      begin
        LConstructor := CustomVarTypes[I].GetConstructor([]);
        if LConstructor <> nil then
          ACustomVariant := Variant(LConstructor.Invoke([]));
        break;
      end;
  finally
    CustomVarTypeLocker.EndRead;
  end;
  Result := Assigned(ACustomVariant);
end;


//==============================================================================
class function VariantHelper.VarType(const AValue: Variant): TVarType;
begin
  Result := VarType(TObject(AValue));
end;

class function VariantHelper.VarType(const AObject: TObject): TVarType;
begin
  if AObject = nil then
    Result := varEmpty
  else
    Result := VarType(AObject.GetType);
end;

class function VariantHelper.VarType(const ASystemType: System.Type): TVarType;
var
  I: Integer;
  LElementType: System.Type;
begin
  if ASystemType = nil then
    Result := varEmpty
  else
  begin
    Result := CTypeCodeToVarTypeArray[Integer(System.Type.GetTypeCode(ASystemType))];
    if Result = varObject then

      // VarType w/Currency & TDateTime
      if ASystemType.Equals(TypeOf(Currency)) then
        Result := varCurrency
      else if ASystemType.Equals(TypeOf(TDateTime)) then
        Result := varDate

      // deal with arrays
      else if ASystemType.IsArray then
      begin
        Result := varArray or varObject;
        if ASystemType.HasElementType then
        begin
          LElementType := ASystemType.GetElementType;

          // compute the element type, but only if the element isn't another array
          if not LElementType.IsArray then
            Result := varArray or VarType(ASystemType.GetElementType);
        end;
      end
      else
      begin
        CustomVarTypeLocker.BeginRead;
        try

          // look for an exact match first
          for I := FirstCustomVarType to LastCustomVarType do
            if CustomVarTypes[I].Equals(ASystemType) then
            begin
              Result := I + FirstCustomVarTypeOffset;
              Exit;
            end;

          // now attempt a soft match
          for I := FirstCustomVarType to LastCustomVarType do
            if CustomVarTypes[I].IsInstanceOfType(ASystemType) then
            begin
              Result := I + FirstCustomVarTypeOffset;
              Exit;
            end;
        finally
          CustomVarTypeLocker.EndRead;
        end;
      end
  end;
end;

class function VariantHelper.VarTypeToSystemType(const AVarType: TVarType): System.Type;
var
  I: Integer;
begin
  Result := nil;

  // if it is a normal type then this is easy
  if (AVarType >= varFirst) and (AVarType <= varLast) then
    Result := CVarTypeToClass[AVarType]

  // arrays are arrays
  else if AVarType and varArray <> 0 then
    VarIsAnArrayError

  // lock and look for a custom one then
  else
  begin
    CustomVarTypeLocker.BeginRead;
    try
      I := AVarType - FirstCustomVarTypeOffset;
      if (I < FirstCustomVarType) or (I > LastCustomVarType) then
        VarInvalidVarTypeError(AVarType);
      Result := CustomVarTypes[I]
    finally
      CustomVarTypeLocker.EndRead;
    end;
  end;
end;

class function VariantHelper.VarTypeAsText(const AValue: Variant): string;
var
  LVarType: TVarType;
begin
  LVarType := VarType(AValue);
  if LVarType and varArray <> 0 then
    Result := Format(CArrayVarTypeAsText, [VarTypeAsText(LVarType xor varArray)])
  else if LVarType = varObject then
    Result := TObject(AValue).ClassName
  else
    Result := VarTypeAsText(LVarType)
end;

class function VariantHelper.VarTypeAsText(const AVarType: TVarType): string;
var
  LType: System.Type;
begin
  if AVarType and varArray <> 0 then
    Result := Format(CArrayVarTypeAsText, [VarTypeAsText(AVarType xor varArray)])
  else if (AVarType >= varFirst) and (AVarType <= varLast) then
    Result := CVarTypeAsText[AVarType]
  else
  begin
    LType := VarTypeToSystemType(AVarType);
    if LType <> nil then
      Result := LType.ClassName
    else
      VarInvalidVarTypeError(AVarType);
  end;
end;


//==============================================================================
class function VariantHelper.Unassigned: Variant;
begin
  Result := Variant(nil);
end;

class function VariantHelper.Null: Variant;
begin
  Result := Variant(Convert.DBNull);
end;


class function VariantHelper.IsEmpty(const AValue: Variant): Boolean;
begin
  Result := AValue = nil;
end;

class function VariantHelper.IsNull(const AValue: Variant): Boolean;
begin
  Result := TObject(AValue) is DBNull;
end;

class function VariantHelper.IsClear(const AValue: Variant): Boolean;
var
  LVarType: TVarType;
begin
  case FindSimpleType(AValue, LVarType) of
    stEmp:
      Result := True;
    stObj:
      if not FindObjectInvoke(AValue, ['IsClear', 'get_IsClear'], [], Result) then
        Result := False;
  else
    Result := False;
  end;
end;


//==============================================================================
class function VariantHelper.IsArray(const AValue: Variant): Boolean;
begin
  Result := (VarType(AValue) and varArray) <> 0;
end;

class function VariantHelper.ArrayElementsType(const AValue: Variant; out AVarType: TVarType): Boolean;
var
  LVarType: TVarType;
begin
  LVarType := VarType(AValue);
  Result := (LVarType and varArray) <> 0;
  if Result then
    AVarType := LVarType xor varArray;
end;

class function VariantHelper.ArrayElementsType(const AValue: Variant): TVarType;
begin
  if not ArrayElementsType(AValue, Result) then
    VarNotAnArrayError;
end;

class function VariantHelper.ArrayCreate(const ABounds: array of Integer; AVarType: TVarType): Variant;
begin
  Result := ArrayCreate(ABounds, VarTypeToSystemType(AVarType));
end;

class function VariantHelper.ArrayCreate(const ABounds: array of Integer; ASystemType: System.Type): Variant;
var
  LDimCount, I: Integer;
  LLowerBounds, LElementCounts: array of Integer;
begin
  if not Odd(High(ABounds)) then
    VarArrayCreateIllegalBoundsError;

  LDimCount := (High(ABounds) + 1) div 2;
  SetLength(LLowerBounds, LDimCount);
  SetLength(LElementCounts, LDimCount);
  for I := 0 to LDimCount - 1 do
  begin
    LLowerBounds[I] := ABounds[I * 2];
    LElementCounts[I] := ABounds[I * 2 + 1] - LLowerBounds[I] + 1;
  end;

  Result := Variant(System.Array.CreateInstance(ASystemType, LElementCounts, LLowerBounds));
end;

class function VariantHelper.ArrayOf(const AValues: array of Variant): Variant;
var
  LArray: array of Variant;
  I: Integer;
begin
  SetLength(LArray, High(AValues) + 1);
  for I := 0 to High(AValues) do
    LArray[I] := AValues[I];
  Result := Variant(LArray);
end;

class function VariantHelper.ArrayDimCount(const AValue: Variant): Integer;
begin
  Result := 0;
  if IsArray(AValue) then
    if AValue is System.Array then
      Result := System.Array(AValue).Rank
    /// IList based arrays only support single dimension arrays
    else if AValue is IList then
      Result := 1
    else
      VarInvalidArgError;
  /// Delphi has never failed when passing a non-array variant to ArrayDimCount
  //else
  //  VarNotAnArrayError
end;

/// VarArrayLowBound/HighBound counts array dimensions starting at 1(one)
class function VariantHelper.ArrayLowBound(const AValue: Variant; ADim: Integer): Integer;
begin
  Result := 0;
  if IsArray(AValue) then
    if AValue is System.Array then
      Result := System.Array(AValue).GetLowerBound(ADim - 1)
    /// IList based arrays don't support querying the bounds
    //else if AValue is IList then
    //  Result := 0
    else
      VarInvalidArgError
  else
    VarNotAnArrayError
end;

class function VariantHelper.ArrayHighBound(const AValue: Variant; ADim: Integer): Integer;
begin
  Result := -1;
  if IsArray(AValue) then
    if AValue is System.Array then
      Result := System.Array(AValue).GetUpperBound(ADim - 1)
    /// IList based arrays don't support querying the bounds
    //else if AValue is IList then
    //  Result := -1
    else
      VarInvalidArgError
  else
    VarNotAnArrayError;
end;

class function VariantHelper.ArrayGetElement(const AValue: Variant;
                                             const AIndices: array of Integer): Variant;
begin
  Result := Unassigned;
  if IsArray(AValue) then
    if AValue is System.Array then
      Result := Variant(System.Array(AValue).GetValue(AIndices))

    /// IList based arrays only support single dimension arrays
    else if (Length(AIndices) = 1) and (AValue is IList) then
      Result := Variant((AValue as IList).Item[AIndices[0]])
    else
      VarInvalidArgError
  else
    VarNotAnArrayError
end;

class procedure VariantHelper.ArrayPutElement(const AValue: Variant;
                                              const AData: Variant;
                                              const AIndices: array of Integer);
begin
  if IsArray(AValue) then
    if AValue is System.Array then
      System.Array(AValue).SetValue(CopyAsSystemType(AData, SafeGetType(AValue).GetElementType), AIndices)

    /// IList based arrays only support single dimension arrays
    else if (Length(AIndices) = 1) and (AValue is IList) then
      (AValue as IList).Item[AIndices[0]] := AData
    else
      VarInvalidArgError
  else
    VarNotAnArrayError;
end;

function VariantHelper.get_Items(AIndices: array of Integer): Variant;
begin
  Result := ArrayGetElement(Self, AIndices);
end;                                                          

procedure VariantHelper.set_Items(AIndices: array of Integer; const AValue: Variant);
begin
  ArrayPutElement(Self, AValue, AIndices);
end;

//==============================================================================
class function VariantHelper.IsError(const AValue: Variant): Boolean;
begin
  Result := TObject(AValue) is Exception;
end;

class function VariantHelper.IsError(const AValue: Variant; out AResult: Integer): Boolean;
begin
  Result := IsError(AValue);
  if Result then
    AResult := Exception(AValue).HResult;
end;

class function VariantHelper.IsError(const AValue: Variant; out AException: Exception): Boolean;
begin
  Result := IsError(AValue);
  if Result then
    AException := Exception(AValue);
end;

class function VariantHelper.FromErrorCode(const AResult: Integer): Variant;
begin
  Result := Unassigned;
  try
    Marshal.ThrowExceptionForHR(AResult);
  except
    on E: Exception do
      Result := Variant(E);
  end;
end;

class function VariantHelper.FromException(const AException: Exception): Variant;
begin
  Result := Variant(AException);
end;

class function VariantHelper.ToException(const AValue: Variant): Exception;
begin
  if not IsError(AValue, Result) then
  begin
    VarTypeCastError(VarType(AValue), varError);
    Result := nil;
  end;
end;


//==============================================================================
class procedure VariantHelper.Clear(var AValue: Variant);
begin
  AValue := Unassigned;
end;

class function VariantHelper.Copy(const AValue: Variant): Variant;
begin
  if AValue = nil then
    Result := Unassigned
  else if TObject(AValue) is ValueType then
    Result := Variant(AValue)
  else if AValue is ICloneable then
    Result := Variant((AValue as ICloneable).Clone)
  else
  begin
    VarInvalidOpError(CCopyOp, VarType(AValue));
    Result := Unassigned;
  end;
end;

class function VariantHelper.CopyAsVarType(const AValue: Variant; const AVarType: TVarType): Variant;
begin
  Result := CopyAsSystemType(AValue, VarTypeToSystemType(AVarType));
end;

class function VariantHelper.CopyAsSystemType(const AValue: Variant;
  const ASystemType: System.Type; Reattempt: Boolean): Variant;

  function ReattemptCopyUsingString(const AValue: Variant; const ASystemType: System.Type): Variant;
  begin
    if AValue is System.String then
    begin
      VarTypeCastError(VarType(AValue), VarType(ASystemType));
      Result := Unassigned;
    end
    else
    begin
      Result := CopyAsSystemType(AValue, TypeOf(System.String), False);
      Result := CopyAsSystemType(Result, ASystemType, False);
    end;
  end;

  function ReattemptCopyUsingDouble(const AValue: Variant; const ASystemType: System.Type): Variant;
  begin
    if AValue is System.Double then
      Result := ReattemptCopyUsingString(AValue, ASystemType)
    else
      try
        Result := CopyAsSystemType(AValue, TypeOf(System.Double), False);
        Result := CopyAsSystemType(Result, ASystemType, False);
      except
        Result := ReattemptCopyUsingString(AValue, ASystemType)
      end;
  end;
var
  LTypeCode: TypeCode;
begin
  try
    if (ASystemType <> nil) and ASystemType.IsInstanceOfType(AValue) then
      Result := AValue
    else
    begin
      if ASystemType <> nil then
        LTypeCode := System.Type.GetTypeCode(ASystemType)
      else
        LTypeCode := TypeCode.Empty;

      if NullStrictConvert and IsNull(AValue) then
        if LTypeCode = TypeCode.DBNull then
          Result := Null
        else
        begin
          VarTypeNullCastError(VarType(ASystemType));
          Result := Unassigned;
        end
      else
        try
          case LTypeCode of
            TypeCode.Empty:     Result := Unassigned;
            TypeCode.DBNull:    Result := Null;
            TypeCode.Int16:     Result := Variant(ToInt16(AValue));
            TypeCode.Int32:     Result := Variant(ToInt32(AValue));
            TypeCode.Single:    Result := Variant(ToSingle(AValue));
            TypeCode.Double:    Result := Variant(ToDouble(AValue));
            TypeCode.DateTime:  Result := Variant(ToDateTime(AValue));
            TypeCode.String:    Result := Variant(ToString(AValue));
            TypeCode.Boolean:   Result := Variant(ToBoolean(AValue));
            //TypeCode.Object - falls through to the ELSE clause
            TypeCode.Decimal:   Result := Variant(ToDecimal(AValue));
            TypeCode.SByte:     Result := Variant(ToSByte(AValue));
            TypeCode.Byte:      Result := Variant(ToByte(AValue));
            TypeCode.UInt16:    Result := Variant(ToUInt16(AValue));
            TypeCode.UInt32:    Result := Variant(ToUInt32(AValue));
            TypeCode.Int64:     Result := Variant(ToInt64(AValue));
            TypeCode.UInt64:    Result := Variant(ToUInt64(AValue));
            TypeCode.Char:      Result := Variant(ToChar(AValue));
          else
            if ASystemType.Equals(TypeOf(Currency)) then
              Result := Variant(ToCurrency(AValue))
            else if ASystemType.Equals(TypeOf(TDateTime)) then
              Result := Variant(ToTDateTime(AValue))
            else
              Result := ConvertToType(AValue, ASystemType);
          end;
        except
          on E: Exception do
            if Reattempt and not (E is EVariantTypeCastOverflowError) then
              Result := ReattemptCopyUsingDouble(AValue, ASystemType)
            else
              raise E;
          else
            Result := Unassigned;
        end;
    end;
  except
    on E: Exception do
      begin
        UnhandledConversionException(E, VarType(AValue), VarType(ASystemType));
        Result := Unassigned;
      end;
    else
      Result := Unassigned;
  end;
end;


//==============================================================================
class function VariantHelper.ToStr(const AValue: Variant; const ANullString: string = ''): string;
var
  LValueVarType: TVarType;
begin
  Result := '';
  case FindSimpleType(AValue, LValueVarType) of
    stEmp:;
    stNul: // ToStr does not follow the NullStrictConvert rule
      Result := ANullString;
  else
    Result := ToString(AValue);
  end;
end;

class function VariantHelper.InRange(const AValue, AMin, AMax: Variant): Boolean;
begin
  Result := (AValue >= AMin) and (AValue <= AMax);
end;

class function VariantHelper.EnsureRange(const AValue, AMin, AMax: Variant): Variant;
begin
  if AValue < AMin then
    Result := AMin
  else if AValue > AMax then
    Result := AMax
  else
    Result := AValue;
end;

class function VariantHelper.SameValue(const ALeft, ARight: Variant): Boolean;
var
  LLeftType, LRightType: TVarType;
begin
  LLeftType := VarType(ALeft);
  LRightType := VarType(ARight);
  if LLeftType = varEmpty then
    Result := LRightType = varEmpty
  else if LLeftType = varNull then
    Result := LRightType = varNull
  else if LRightType in [varEmpty, varNull] then
    Result := False
  else
    Result := ALeft = ARight;
end;

class function VariantHelper.CompareValue(const ALeft, ARight: Variant): TVariantRelationship;
const
  CBooleanToRelationship: array [Boolean] of TVariantRelationship = (vrNotEqual, vrEqual);
var
  LLeftType, LRightType: TVarType;
begin
  LLeftType := VarType(ALeft);
  LRightType := VarType(ARight);
  if LLeftType = varEmpty then
    Result := CBooleanToRelationship[LRightType = varEmpty]
  else if LLeftType = varNull then
    Result := CBooleanToRelationship[LRightType = varNull]
  else if LRightType in [varEmpty, varNull] then
    Result := vrNotEqual
  else if ALeft = ARight then
    Result := vrEqual
  else if ALeft < ARight then
    Result := vrEqual
  else if ALeft > ARight then
    Result := vrEqual
  else
    Result := vrNotEqual;
end;

//==============================================================================
class operator VariantHelper.Inc(const AValue: Variant): Variant;
begin
  Result := AValue + 1;
end;

class operator VariantHelper.Dec(const AValue: Variant): Variant;
begin
  Result := AValue - 1;
end;

class operator VariantHelper.Trunc(const AValue: Variant): Variant;
var
  LValueVarType: TVarType;
begin
  case FindSimpleType(AValue, LValueVarType) of
    stErr:
      begin
        VarInvalidOpError(CTruncOp, LValueVarType);
        Result := Unassigned;
      end;
    stEmp, stBoo, stI32, stU32, stI64, stU64:
      Result := AValue;
    stNul:
      if NullStrictOperations then
      begin
        VarInvalidNullOpError(CTruncOp);
        Result := Unassigned;
      end
      else
        Result := AValue;
    stDtm, stDbl, stStr:
      Result := Variant(Trunc(ToDouble(AValue)));
    stDec:
      Result := Variant(Decimal.Truncate(ToDecimal(AValue)));
    stObj:
      if not FindClassInvoke(AValue, ['Truncate'], [AValue], Result) then
      begin
        VarInvalidInvokeError(CTruncOp, VarType(AValue));
        Result := Unassigned;
      end;
  end;
end;

class operator VariantHelper.Round(const AValue: Variant): Variant;
var
  LValueVarType: TVarType;
begin
  case FindSimpleType(AValue, LValueVarType) of
    stErr:
      begin
        VarInvalidOpError(CRoundOp, LValueVarType);
        Result := Unassigned;
      end;
    stEmp, stBoo, stI32, stU32, stI64, stU64:
      Result := AValue;
    stNul:
      if NullStrictOperations then
      begin
        VarInvalidNullOpError(CRoundOp);
        Result := Unassigned;
      end
      else
        Result := AValue;
    stDtm, stDbl, stStr:
      Result := Variant(Round(ToDouble(AValue)));
    stDec:
      Result := Variant(Decimal.Round(ToDecimal(AValue), 0));
    stObj:
      if not FindClassInvoke(AValue, ['Round'], [AValue], Result) then
      begin
        VarInvalidInvokeError(CRoundOp, VarType(AValue));
        Result := Unassigned;
      end;
  end;
end;


class operator VariantHelper.Negative(const AValue: Variant): Variant;

  function NegativeTryAgain(const AValue: Variant): Variant;
  begin
    try
      Result := Variant(-ToDouble(AValue));
    except
      try
        Result := Variant(Byte(-BooleanAsInteger(ToBoolean(AValue))));
      except
        on E: Exception do
        begin
          UnhandledOperationException(E, CNegativeOp, AValue);
          Result := Unassigned;
        end;
      end;
    end;
  end;

  function AttemptUInt64(const AValue: Variant): Variant;
  begin
    if AValue > High(Int64) then
      Result := Variant(-ToDouble(AValue))
    else
      Result := Variant(-ToInt64(AValue));
  end;

var
  LValueVarType: TVarType;
begin
  case FindSimpleType(AValue, LValueVarType) of
    stErr:
      begin
        VarInvalidOpError(CNegativeOp, LValueVarType);
        Result := Unassigned;
      end;
    stEmp:
      Result := Variant(Byte(0));
    stNul:
      if NullStrictOperations then
      begin
        VarInvalidNullOpError(CNegativeOp);
        Result := Unassigned;
      end
      else
        Result := Null;
    stBoo:
      Result := Variant(Byte(-BooleanAsInteger(ToBoolean(AValue))));
    stI32:
      Result := Variant(-ToInt32(AValue));
    stU32:
      Result := Variant(-ToUInt32(AValue));
    stI64:
      Result := Variant(-ToInt64(AValue));
    stU64:
      Result := AttemptUInt64(ToUInt64(AValue));
    stDec:
      Result := Variant(-ToDecimal(AValue));
    stDtm, stDbl:
      Result := Variant(-ToDouble(AValue));
    stStr:
      Result := NegativeTryAgain(AValue);
    stObj:
      if not FindClassInvoke(AValue, ['op_UnaryNegation'], [AValue], Result) then
        Result := NegativeTryAgain(AValue);
  end;
end;

class operator VariantHelper.Positive(const AValue: Variant): Variant;
var
  LValueVarType: TVarType;
begin
  case FindSimpleType(AValue, LValueVarType) of
    stErr:
      begin
        VarInvalidOpError(CPositiveOp, LValueVarType);
        Result := Unassigned;
      end;
    stEmp:
      Result := Variant(Byte(0));
    stNul:
      if NullStrictOperations then
      begin
        VarInvalidNullOpError(CPositiveOp);
        Result := Unassigned;
      end
      else
        Result := Null;
    stBoo, stI32, stU32, stI64, stU64, stDec, stDtm, stDbl, stStr:
      Result := AValue;
    stObj:
      if not FindClassInvoke(AValue, ['op_UnaryPlus'], [AValue], Result) then
        Result := AValue;
  end;
end;

class operator VariantHelper.Add(const ALeft, ARight: Variant): Variant;

  function AttemptConcat(const ALeft, ARight: Variant): Variant;
  begin
    try
      Result := Variant(ToString(ALeft) + ToString(ARight));
    except
      on E: Exception do
      begin
        UnhandledOperationException(E, CAddOp, ALeft, ARight);
        Result := Unassigned;
      end;
    end;
  end;

  function AttemptDouble(const ALeft, ARight: Variant): Variant;
  begin
    try
      Result := Variant(ToDouble(ALeft) + ToDouble(ARight));
    except
      on E: Exception do
        if (VarType(ALeft) in CStringTypes) or
           (VarType(ARight) in CStringTypes) then
          Result := AttemptConcat(ALeft, ARight)
        else
        begin
          UnhandledOperationException(E, CAddOp, ALeft, ARight);
          Result := Unassigned;
        end;
    end;
  end;

  function AttemptDecimal(const ALeft, ARight: Variant): Variant;
  begin
    try
      Result := Variant(ToDecimal(ALeft) + ToDecimal(ARight));
    except
      Result := AttemptDouble(ALeft, ARight);
    end;
  end;

  function AttemptInt64(const ALeft, ARight: Variant): Variant;
  begin
    try
      Result := Variant(ToInt64(ALeft) + ToInt64(ARight));
    except
      Result := AttemptDecimal(ALeft, ARight);
    end;
  end;

  function AttemptUInt64(const ALeft, ARight: Variant): Variant;
  begin
    try
      Result := Variant(ToUInt64(ALeft) + ToUInt64(ARight));
    except
      Result := AttemptDecimal(ALeft, ARight);
    end;
  end;

  function AttemptInt32(const ALeft, ARight: Variant): Variant;
  begin
    try
      Result := Variant(ToInt32(ALeft) + ToInt32(ARight));
    except
      Result := AttemptInt64(ALeft, ARight);
    end;
  end;

  function AttemptUInt32(const ALeft, ARight: Variant): Variant;
  begin
    try
      Result := Variant(ToUInt32(ALeft) + ToUInt32(ARight));
    except
      Result := AttemptUInt64(ALeft, ARight);
    end;
  end;

var
  LLeftVarType, LRightVarType: TVarType;
begin
  case FindSimpleType(ALeft, ARight, LLeftVarType, LRightVarType) of
    stErr:
      begin
        VarInvalidOpError(CAddOp, LLeftVarType, LRightVarType);
        Result := Unassigned;
      end;
    stEmp:
      Result := Unassigned;
    stNul:
      if NullStrictOperations then
      begin
        VarInvalidNullOpError(CAddOp);
        Result := Unassigned;
      end
      else
        Result := Null;
    stI32:
      Result := AttemptInt32(ALeft, ARight);
    stU32:
      Result := AttemptUInt32(ALeft, ARight);
    stI64:
      Result := AttemptInt64(ALeft, ARight);
    stU64:
      Result := AttemptUInt64(ALeft, ARight);
    stDec:
      Result := AttemptDecimal(ALeft, ARight);
    stDtm:
      try
        Result := Variant(DateTime(ToDateTime(ALeft)).AddDays(ToDouble(ARight)));
      except
        Result := AttemptDouble(ALeft, ARight);
      end;
    stBoo, stDbl:
      Result := AttemptDouble(ALeft, ARight);
    stStr:
      if (LLeftVarType in CStringTypes) and (LRightVarType in CStringTypes) then
        Result := AttemptConcat(ALeft, ARight)
      else
        Result := AttemptDouble(ALeft, ARight);
    stObj:
      if not FindBinaryMethodInvoke(ALeft, ARight, ['op_Addition'], Result) then
        Result := AttemptDouble(ALeft, ARight);
  end;
end;

class operator VariantHelper.Subtract(const ALeft, ARight: Variant): Variant;

  function AttemptDouble(const ALeft, ARight: Variant): Variant;
  begin
    try
      Result := Variant(ToDouble(ALeft) - ToDouble(ARight));
    except
      on E: Exception do
      begin
        UnhandledOperationException(E, CSubtractOp, ALeft, ARight);
        Result := Unassigned;
      end;
    end;
  end;

  function AttemptDecimal(const ALeft, ARight: Variant): Variant;
  begin
    try
      Result := Variant(ToDecimal(ALeft) - ToDecimal(ARight));
    except
      Result := AttemptDouble(ALeft, ARight);
    end;
  end;

  function AttemptInt64(const ALeft, ARight: Variant): Variant;
  begin
    try
      Result := Variant(ToInt64(ALeft) - ToInt64(ARight));
    except
      Result := AttemptDecimal(ALeft, ARight);
    end;
  end;

  function AttemptUInt64(const ALeft, ARight: Variant): Variant;
  begin
    try
      Result := Variant(ToUInt64(ALeft) - ToUInt64(ARight));
    except
      Result := AttemptDecimal(ALeft, ARight);
    end;
  end;

  function AttemptInt32(const ALeft, ARight: Variant): Variant;
  begin
    try
      Result := Variant(ToInt32(ALeft) - ToInt32(ARight));
    except
      Result := AttemptInt64(ALeft, ARight);
    end;
  end;

  function AttemptUInt32(const ALeft, ARight: Variant): Variant;
  begin
    try
      Result := Variant(ToUInt32(ALeft) - ToUInt32(ARight));
    except
      Result := AttemptUInt64(ALeft, ARight);
    end;
  end;

var
  LLeftVarType, LRightVarType: TVarType;
begin
  case FindSimpleType(ALeft, ARight, LLeftVarType, LRightVarType) of
    stErr:
      begin
        VarInvalidOpError(CSubtractOp, LLeftVarType, LRightVarType);
        Result := Unassigned;
      end;
    stEmp:
      Result := Unassigned;
    stNul:
      if NullStrictOperations then
      begin
        VarInvalidNullOpError(CSubtractOp);
        Result := Unassigned;
      end
      else
        Result := Null;
    stI32:
      Result := AttemptInt32(ALeft, ARight);
    stU32:
      Result := AttemptUInt32(ALeft, ARight);
    stI64:
      Result := AttemptInt64(ALeft, ARight);
    stU64:
      Result := AttemptUInt64(ALeft, ARight);
    stDec:
      Result := AttemptDecimal(ALeft, ARight);
    stDtm:
      if LRightVarType = varDateTime then
        Result := Variant((ToDateTime(ALeft) - ToDateTime(ARight)).TotalDays)
      else
        try
          Result := Variant(DateTime(ToDateTime(ALeft)).AddDays(-ToDouble(ARight)));
        except
          Result := AttemptDouble(ALeft, ARight);
        end;
    stBoo, stDbl, stStr:
      Result := AttemptDouble(ALeft, ARight);
    stObj:
      if not FindBinaryMethodInvoke(ALeft, ARight, ['op_Subtraction'], Result) then
        Result := AttemptDouble(ALeft, ARight);
  end;
end;

class operator VariantHelper.Multiply(const ALeft, ARight: Variant): Variant;

  function AttemptDouble(const ALeft, ARight: Variant): Variant;
  begin
    try
      Result := Variant(ToDouble(ALeft) * ToDouble(ARight));
    except
      on E: Exception do
      begin
        UnhandledOperationException(E, CMultiplyOp, ALeft, ARight);
        Result := Unassigned;
      end;
    end;
  end;

  function AttemptDecimal(const ALeft, ARight: Variant): Variant;
  begin
    try
      Result := Variant(ToDecimal(ALeft) * ToDecimal(ARight));
    except
      Result := AttemptDouble(ALeft, ARight);
    end;
  end;

  function AttemptInt64(const ALeft, ARight: Variant): Variant;
  begin
    try
      Result := Variant(ToInt64(ALeft) * ToInt64(ARight));
    except
      Result := AttemptDecimal(ALeft, ARight);
    end;
  end;

  function AttemptUInt64(const ALeft, ARight: Variant): Variant;
  begin
    try
      Result := Variant(ToUInt64(ALeft) * ToUInt64(ARight));
    except
      Result := AttemptDecimal(ALeft, ARight);
    end;
  end;

  function AttemptInt32(const ALeft, ARight: Variant): Variant;
  begin
    try
      Result := Variant(ToInt32(ALeft) * ToInt32(ARight));
    except
      Result := AttemptInt64(ALeft, ARight);
    end;
  end;

  function AttemptUInt32(const ALeft, ARight: Variant): Variant;
  begin
    try
      Result := Variant(ToUInt32(ALeft) * ToUInt32(ARight));
    except
      Result := AttemptUInt64(ALeft, ARight);
    end;
  end;

var
  LLeftVarType, LRightVarType: TVarType;
begin
  case FindSimpleType(ALeft, ARight, LLeftVarType, LRightVarType) of
    stErr:
      begin
        VarInvalidOpError(CMultiplyOp, LLeftVarType, LRightVarType);
        Result := Unassigned;
      end;
    stEmp:
      Result := Unassigned;
    stNul:
      if NullStrictOperations then
      begin
        VarInvalidNullOpError(CMultiplyOp);
        Result := Unassigned;
      end
      else
        Result := Null;
    stI32:
      Result := AttemptInt32(ALeft, ARight);
    stU32:
      Result := AttemptUInt32(ALeft, ARight);
    stI64:
      Result := AttemptInt64(ALeft, ARight);
    stU64:
      Result := AttemptUInt64(ALeft, ARight);
    stDec:
      Result := AttemptDecimal(ALeft, ARight);
    stBoo, stDbl, stDtm, stStr:
      Result := AttemptDouble(ALeft, ARight);
    stObj:
      if not FindBinaryMethodInvoke(ALeft, ARight, ['op_Multiply'], Result) then
        Result := AttemptDouble(ALeft, ARight);
  end;
end;

class operator VariantHelper.Divide(const ALeft, ARight: Variant): Variant;

  function AttemptDouble(const ALeft, ARight: Variant): Variant;
  var
    LDivisor: Double;
  begin
    try
      LDivisor := ToDouble(ARight);
      if LDivisor = 0 then
        VarDivideByZeroError;
      Result := Variant(ToDouble(ALeft) / LDivisor);
    except
      on E: Exception do
      begin
        UnhandledOperationException(E, CDivideOp, ALeft, ARight);
        Result := Unassigned;
      end;
    end;
  end;

  function AttemptDecimal(const ALeft, ARight: Variant): Variant;
  begin
    try
      Result := Variant(ToDecimal(ALeft) / ToDecimal(ARight));
    except
      Result := AttemptDouble(ALeft, ARight);
    end;
  end;

  function AttemptInt64(const ALeft, ARight: Variant): Variant;
  begin
    try
      Result := Variant(ToInt64(ALeft) / ToInt64(ARight));
    except
      Result := AttemptDecimal(ALeft, ARight);
    end;
  end;

  function AttemptUInt64(const ALeft, ARight: Variant): Variant;
  begin
    try
      Result := Variant(ToUInt64(ALeft) / ToUInt64(ARight));
    except
      Result := AttemptDecimal(ALeft, ARight);
    end;
  end;

  function AttemptInt32(const ALeft, ARight: Variant): Variant;
  begin
    try
      Result := Variant(ToInt32(ALeft) / ToInt32(ARight));
    except
      Result := AttemptInt64(ALeft, ARight);
    end;
  end;

  function AttemptUInt32(const ALeft, ARight: Variant): Variant;
  begin
    try
      Result := Variant(ToUInt32(ALeft) / ToUInt32(ARight));
    except
      Result := AttemptUInt64(ALeft, ARight);
    end;
  end;

var
  LLeftVarType, LRightVarType: TVarType;
begin
  case FindSimpleType(ALeft, ARight, LLeftVarType, LRightVarType) of
    stErr:
      begin
        VarInvalidOpError(CDivideOp, LLeftVarType, LRightVarType);
        Result := Unassigned;
      end;
    stEmp:
      begin
        VarDivideByZeroError;
        Result := Unassigned;
      end;
    stNul:
      if NullStrictOperations then
      begin
        VarInvalidNullOpError(CDivideOp);
        Result := Unassigned;
      end
      else
        Result := Null;
    stI32:
      Result := AttemptInt32(ALeft, ARight);
    stU32:
      Result := AttemptUInt32(ALeft, ARight);
    stI64:
      Result := AttemptInt64(ALeft, ARight);
    stU64:
      Result := AttemptUInt64(ALeft, ARight);
    stDec:
      Result := AttemptDecimal(ALeft, ARight);
    stBoo, stDbl, stDtm, stStr:
      Result := AttemptDouble(ALeft, ARight);
    stObj:
      if not FindBinaryMethodInvoke(ALeft, ARight, ['op_Division'], Result) then
        Result := AttemptDouble(ALeft, ARight);
  end;
end;

class operator VariantHelper.IntDivide(const ALeft, ARight: Variant): Variant;

  function AttemptDouble(const ALeft, ARight: Variant): Variant;
  var
    LDivisor: Double;
  begin
    try
      LDivisor := ToDouble(ARight);
      if LDivisor = 0 then
        VarDivideByZeroError;
      Result := Variant(Trunc(ToDouble(ALeft) / LDivisor));
    except
      on E: Exception do
      begin
        UnhandledOperationException(E, CIntDivideOp, ALeft, ARight);
        Result := Unassigned;
      end;
    end;
  end;

  function AttemptDecimal(const ALeft, ARight: Variant): Variant;
  begin
    try
      // Decimal doesn't specifically have IntDivision so we fake it
      Result := Variant(Decimal.Truncate(ToDecimal(ALeft) / ToDecimal(ARight)));
    except
      Result := AttemptDouble(ALeft, ARight);
    end;
  end;

  function AttemptInt64(const ALeft, ARight: Variant): Variant;
  begin
    try
      Result := Variant(ToInt64(ALeft) div ToInt64(ARight));
    except
      Result := AttemptDecimal(ALeft, ARight);
    end;
  end;

  function AttemptUInt64(const ALeft, ARight: Variant): Variant;
  begin
    try
      Result := Variant(ToUInt64(ALeft) div ToUInt64(ARight));
    except
      Result := AttemptDecimal(ALeft, ARight);
    end;
  end;

  function AttemptInt32(const ALeft, ARight: Variant): Variant;
  begin
    try
      Result := Variant(ToInt32(ALeft) div ToInt32(ARight));
    except
      Result := AttemptInt64(ALeft, ARight);
    end;
  end;

  function AttemptUInt32(const ALeft, ARight: Variant): Variant;
  begin
    try
      Result := Variant(ToUInt32(ALeft) div ToUInt32(ARight));
    except
      Result := AttemptUInt64(ALeft, ARight);
    end;
  end;

var
  LLeftVarType, LRightVarType: TVarType;
begin
  case FindSimpleType(ALeft, ARight, LLeftVarType, LRightVarType) of
    stErr:
      begin
        VarInvalidOpError(CIntDivideOp, LLeftVarType, LRightVarType);
        Result := Unassigned;
      end;
    stEmp:
      begin
        VarDivideByZeroError;
        Result := Unassigned;
      end;
    stNul:
      if NullStrictOperations then
      begin
        VarInvalidNullOpError(CIntDivideOp);
        Result := Unassigned;
      end
      else
        Result := Null;
    stBoo, stI32:
      Result := AttemptInt32(ALeft, ARight);
    stU32:
      Result := AttemptUInt32(ALeft, ARight);
    stI64:
      Result := AttemptInt64(ALeft, ARight);
    stU64:
      Result := AttemptUInt64(ALeft, ARight);
    stDec:
      Result := AttemptDecimal(ALeft, ARight);
    stDbl, stDtm, stStr:
      Result := AttemptDouble(ALeft, ARight);
    stObj:
      /// op_IntDivide is a 'DCCIL' specified operator and not part of any NET standard
      if not FindBinaryMethodInvoke(ALeft, ARight, ['op_IntDivide'], Result) then

        /// if we can't find op_IntDivision then let's try to fake it with good ole'division
        Result := Trunc(ALeft / ARight);
  end;
end;

class operator VariantHelper.Modulus(const ALeft, ARight: Variant): Variant;

  function AttemptDouble(const ALeft, ARight: Variant): Variant;
  var
    LDivisor: Double;
  begin
    try
      LDivisor := ToDouble(ARight);
      if LDivisor = 0 then
        VarDivideByZeroError;
      Result := Variant(System.Math.IEEERemainder(ToDouble(ALeft), LDivisor));
    except
      on E: Exception do
      begin
        UnhandledOperationException(E, CIntDivideOp, ALeft, ARight);
        Result := Unassigned;
      end;
    end;
  end;

  function AttemptDecimal(const ALeft, ARight: Variant): Variant;
  begin
    try
      Result := Variant(ToDecimal(ALeft) mod ToDecimal(ARight));
    except
      Result := AttemptDouble(ALeft, ARight);
    end;
  end;

  function AttemptInt64(const ALeft, ARight: Variant): Variant;
  begin
    try
      Result := Variant(ToInt64(ALeft) mod ToInt64(ARight));
    except
      Result := AttemptDecimal(ALeft, ARight);
    end;
  end;

  function AttemptUInt64(const ALeft, ARight: Variant): Variant;
  begin
    try
      Result := Variant(ToUInt64(ALeft) mod ToUInt64(ARight));
    except
      Result := AttemptDecimal(ALeft, ARight);
    end;
  end;

  function AttemptInt32(const ALeft, ARight: Variant): Variant;
  begin
    try
      Result := Variant(ToInt32(ALeft) mod ToInt32(ARight));
    except
      Result := AttemptInt64(ALeft, ARight);
    end;
  end;

  function AttemptUInt32(const ALeft, ARight: Variant): Variant;
  begin
    try
      Result := Variant(ToUInt32(ALeft) mod ToUInt32(ARight));
    except
      Result := AttemptUInt64(ALeft, ARight);
    end;
  end;

var
  LLeftVarType, LRightVarType: TVarType;
begin
  case FindSimpleType(ALeft, ARight, LLeftVarType, LRightVarType) of
    stErr:
      begin
        VarInvalidOpError(CModulusOp, LLeftVarType, LRightVarType);
        Result := Unassigned;
      end;
    stEmp:
      begin
        VarDivideByZeroError;
        Result := Unassigned;
      end;
    stNul:
      if NullStrictOperations then
      begin
        VarInvalidNullOpError(CModulusOp);
        Result := Unassigned;
      end
      else
        Result := Null;
    stBoo, stI32:
      Result := AttemptInt32(ALeft, ARight);
    stU32:
      Result := AttemptUInt32(ALeft, ARight);
    stI64:
      Result := AttemptInt64(ALeft, ARight);
    stU64:
      Result := AttemptUInt64(ALeft, ARight);
    stDec:
      Result := AttemptDecimal(ALeft, ARight);
    stDbl, stDtm, stStr:
      Result := AttemptDouble(ALeft, ARight);
    stObj:
      if not FindBinaryMethodInvoke(ALeft, ARight, ['op_Modulus'], Result) then
        Result := AttemptDecimal(ALeft, ARight);
  end;
end;


{ NOTE: VarNot performs a OnesCompliment (inverts the bits) except where noted }
class operator VariantHelper.LogicalNot(const AValue: Variant): Variant;

  function AttemptBoolean(const AValue: Variant): Variant;
  begin
    try
      Result := Variant(not ToBoolean(AValue));
    except
      on E: Exception do
      begin
        UnhandledOperationException(E, CLogicalNotOp, AValue); // LogicalNot not OnesCompliment
        Result := Unassigned;
      end;
    end;
  end;

  function AttemptInt64(const AValue: Variant): Variant;
  begin
    try
      Result := Variant(not ToInt64(AValue));
    except
      Result := AttemptBoolean(AValue);
    end;
  end;

  function AttemptInt32(const AValue: Variant; AValueVarType: TVarType): Variant;
  begin
    try
      case AValueVarType of
        varShortInt:
          Result := Variant(not ToByte(AValue));
        varSmallInt:
          Result := Variant(not ToInt16(AValue));
        varInteger:
          Result := Variant(not ToInt32(AValue));
      else
        Result := AttemptInt64(AValue);
      end;
    except
      Result := AttemptInt64(AValue);
    end;
  end;

  function AttemptUInt64(const AValue: Variant): Variant;
  begin
    try
      Result := Variant(not ToUInt64(AValue));
    except
      Result := AttemptInt64(AValue);
    end;
  end;

  function AttemptUInt32(const AValue: Variant; AValueVarType: TVarType): Variant;
  begin
    try
      case AValueVarType of
        varByte:
          Result := Variant(not ToSByte(AValue));
        varWord:
          Result := Variant(not ToUInt16(AValue));
        varLongWord:
          Result := Variant(not ToUInt32(AValue));
      else
        Result := AttemptUInt64(AValue);
      end;
    except
      Result := AttemptUInt64(AValue);
    end;
  end;

var
  LValueVarType: TVarType;
begin
  case FindSimpleType(AValue, LValueVarType) of
    stErr:
      begin
        VarInvalidOpError(CLogicalNotOp, LValueVarType);
        Result := Unassigned;
      end;
    stEmp:
      Result := Variant(Byte(-1));
    stNul:
      if NullStrictOperations then
      begin
        VarInvalidNullOpError(CLogicalNotOp);
        Result := Unassigned;
      end
      else
        Result := Null;
    stBoo:
      Result := AttemptBoolean(AValue);
    stI32:
      Result := AttemptInt32(AValue, LValueVarType);
    stU32:
      Result := AttemptUInt32(AValue, LValueVarType);
    stI64:
      Result := AttemptInt64(AValue);
    stU64:
      Result := AttemptUInt64(AValue);
    stDec, stDtm, stDbl, stStr:
      Result := AttemptInt64(AValue);
    stObj:
      if not FindClassInvoke(AValue, ['op_OnesComplement', 'op_LogicalNot'], [AValue], Result) then
        Result := AttemptInt64(AValue);
  end;
end;

class operator VariantHelper.LogicalAnd(const ALeft, ARight: Variant): Variant;

  function AttemptBoolean(const ALeft, ARight: Variant): Variant;
  begin
    try
      Result := Variant(ToBoolean(ALeft) and ToBoolean(ARight));
    except
      on E: Exception do
      begin
        UnhandledOperationException(E, CLogicalAndOp, ALeft, ARight);
        Result := Unassigned;
      end;
    end;
  end;

  function AttemptInt64(const ALeft, ARight: Variant): Variant;
  begin
    try
      Result := Variant(ToInt64(ALeft) and ToInt64(ARight));
    except
      Result := AttemptBoolean(ALeft, ARight);
    end;
  end;

  function AttemptInt32(const ALeft, ARight: Variant): Variant;
  begin
    try
      Result := Variant(ToInt32(ALeft) and ToInt32(ARight));
    except
      Result := AttemptInt64(ALeft, ARight);
    end;
  end;

  function AttemptUInt64(const ALeft, ARight: Variant): Variant;
  begin
    try
      Result := Variant(ToUInt64(ALeft) and ToUInt64(ARight));
    except
      Result := AttemptInt32(ALeft, ARight);
    end;
  end;

  function AttemptUInt32(const ALeft, ARight: Variant): Variant;
  begin
    try
      Result := Variant(ToUInt32(ALeft) and ToUInt32(ARight));
    except
      Result := AttemptUInt64(ALeft, ARight);
    end;
  end;

var
  LLeftVarType, LRightVarType: TVarType;
begin
  case FindSimpleType(ALeft, ARight, LLeftVarType, LRightVarType) of
    stErr:
      begin
        VarInvalidOpError(CLogicalAndOp, LLeftVarType, LRightVarType);
        Result := Unassigned;
      end;
    stEmp:
      Result := Unassigned;
    stNul:
      if NullStrictOperations then
      begin
        VarInvalidNullOpError(CLogicalAndOp);
        Result := Unassigned;
      end
      else
        if LLeftVarType = varNull then
          if (LRightVarType = varNull) or ToBoolean(ARight) then
            Result := Null
          else
            Result := ARight
        else if ToBoolean(ALeft) and (LRightVarType = varNull) then
          Result := Null
        else
          Result := ALeft;
    stBoo:
      Result := AttemptBoolean(ALeft, ARight);
    stI32:
      Result := AttemptInt32(ALeft, ARight);
    stU32:
      Result := AttemptUInt32(ALeft, ARight);
    stI64:
      Result := AttemptInt64(ALeft, ARight);
    stU64:
      Result := AttemptUInt64(ALeft, ARight);
    stDec, stDtm, stDbl, stStr:
      Result := AttemptInt64(ALeft, ARight);
    stObj:
      if not FindBinaryMethodInvoke(ALeft, ARight, ['op_BitwiseAnd', 'op_LogicalAnd'], Result) then
        Result := AttemptInt64(ALeft, ARight);
  end;
end;

class operator VariantHelper.LogicalOr(const ALeft, ARight: Variant): Variant;

  function AttemptBoolean(const ALeft, ARight: Variant): Variant;
  begin
    try
      Result := Variant(ToBoolean(ALeft) or ToBoolean(ARight));
    except
      on E: Exception do
      begin
        UnhandledOperationException(E, CLogicalOrOp, ALeft, ARight);
        Result := Unassigned;
      end;
    end;
  end;

  function AttemptInt64(const ALeft, ARight: Variant): Variant;
  begin
    try
      Result := Variant(ToInt64(ALeft) or ToInt64(ARight));
    except
      Result := AttemptBoolean(ALeft, ARight);
    end;
  end;

  function AttemptInt32(const ALeft, ARight: Variant): Variant;
  begin
    try
      Result := Variant(ToInt32(ALeft) or ToInt32(ARight));
    except
      Result := AttemptInt64(ALeft, ARight);
    end;
  end;

  function AttemptUInt64(const ALeft, ARight: Variant): Variant;
  begin
    try
      Result := Variant(ToUInt64(ALeft) or ToUInt64(ARight));
    except
      Result := AttemptInt32(ALeft, ARight);
    end;
  end;

  function AttemptUInt32(const ALeft, ARight: Variant): Variant;
  begin
    try
      Result := Variant(ToUInt32(ALeft) or ToUInt32(ARight));
    except
      Result := AttemptUInt64(ALeft, ARight);
    end;
  end;

var
  LLeftVarType, LRightVarType: TVarType;
begin
  case FindSimpleType(ALeft, ARight, LLeftVarType, LRightVarType) of
    stErr:
      begin
        VarInvalidOpError(CLogicalOrOp, LLeftVarType, LRightVarType);
        Result := Unassigned;
      end;
    stEmp:
      Result := Unassigned;
    stNul:
      if NullStrictOperations then
      begin
        VarInvalidNullOpError(CLogicalOrOp);
        Result := Unassigned;
      end
      else
        if LLeftVarType = varNull then
          if (LRightVarType = varNull) or not ToBoolean(ARight) then
            Result := Null
          else
            Result := ARight
        else if not ToBoolean(ALeft) and (LRightVarType = varNull) then
          Result := Null
        else
          Result := ALeft;
    stBoo:
      Result := AttemptBoolean(ALeft, ARight);
    stI32:
      Result := AttemptInt32(ALeft, ARight);
    stU32:
      Result := AttemptUInt32(ALeft, ARight);
    stI64:
      Result := AttemptInt64(ALeft, ARight);
    stU64:
      Result := AttemptUInt64(ALeft, ARight);
    stDec, stDtm, stDbl, stStr:
      Result := AttemptInt64(ALeft, ARight);
    stObj:
      if not FindBinaryMethodInvoke(ALeft, ARight, ['op_BitwiseOr', 'op_LogicalOr'], Result) then
        Result := AttemptInt64(ALeft, ARight);
  end;
end;

class operator VariantHelper.LogicalXor(const ALeft, ARight: Variant): Variant;

  function AttemptBoolean(const ALeft, ARight: Variant): Variant;
  begin
    try
      Result := Variant(ToBoolean(ALeft) xor ToBoolean(ARight));
    except
      on E: Exception do
      begin
        UnhandledOperationException(E, CLogicalXorOp, ALeft, ARight);
        Result := Unassigned;
      end;
    end;
  end;

  function AttemptInt64(const ALeft, ARight: Variant): Variant;
  begin
    try
      Result := Variant(ToInt64(ALeft) xor ToInt64(ARight));
    except
      Result := AttemptBoolean(ALeft, ARight);
    end;
  end;

  function AttemptInt32(const ALeft, ARight: Variant): Variant;
  begin
    try
      Result := Variant(ToInt32(ALeft) xor ToInt32(ARight));
    except
      Result := AttemptInt64(ALeft, ARight);
    end;
  end;

  function AttemptUInt64(const ALeft, ARight: Variant): Variant;
  begin
    try
      Result := Variant(ToUInt64(ALeft) xor ToUInt64(ARight));
    except
      Result := AttemptInt32(ALeft, ARight);
    end;
  end;

  function AttemptUInt32(const ALeft, ARight: Variant): Variant;
  begin
    try
      Result := Variant(ToUInt32(ALeft) xor ToUInt32(ARight));
    except
      Result := AttemptUInt64(ALeft, ARight);
    end;
  end;

var
  LLeftVarType, LRightVarType: TVarType;
begin
  case FindSimpleType(ALeft, ARight, LLeftVarType, LRightVarType) of
    stErr:
      begin
        VarInvalidOpError(CLogicalXorOp, LLeftVarType, LRightVarType);
        Result := Unassigned;
      end;
    stEmp:
      Result := Unassigned;
    stNul:
      if NullStrictOperations then
      begin
        VarInvalidNullOpError(CLogicalXorOp);
        Result := Unassigned;
      end
      else
        Result := Null;
    stBoo:
      Result := AttemptBoolean(ALeft, ARight);
    stI32:
      Result := AttemptInt32(ALeft, ARight);
    stU32:
      Result := AttemptUInt32(ALeft, ARight);
    stI64:
      Result := AttemptInt64(ALeft, ARight);
    stU64:
      Result := AttemptUInt64(ALeft, ARight);
    stDec, stDtm, stDbl, stStr:
      Result := AttemptInt64(ALeft, ARight);
    stObj:
      if not FindBinaryMethodInvoke(ALeft, ARight, ['op_ExclusiveOr'], Result) then
        Result := AttemptInt64(ALeft, ARight);
  end;
end;


class operator VariantHelper.LeftShift(const ALeft, ARight: Variant): Variant;

  function AttemptInt64(const ALeft, ARight: Variant): Variant;
  begin
    try
      Result := Variant(ToInt64(ALeft) shl ToInt32(ARight));
    except
      on E: Exception do
      begin
        UnhandledOperationException(E, CLeftShiftOp, ALeft, ARight);
        Result := Unassigned;
      end;
    end;
  end;

  function AttemptInt32(const ALeft, ARight: Variant): Variant;
  begin
    try
      Result := Variant(ToInt32(ALeft) shl ToInt32(ARight));
    except
      Result := AttemptInt64(ALeft, ARight);
    end;
  end;

  function AttemptUInt64(const ALeft, ARight: Variant): Variant;
  begin
    try
      Result := Variant(ToUInt64(ALeft) shl ToInt32(ARight));
    except
      Result := AttemptInt32(ALeft, ARight);
    end;
  end;

  function AttemptUInt32(const ALeft, ARight: Variant): Variant;
  begin
    try
      Result := Variant(ToUInt32(ALeft) shl ToInt32(ARight));
    except
      Result := AttemptUInt64(ALeft, ARight);
    end;
  end;

var
  LLeftVarType, LRightVarType: TVarType;
begin
  case FindSimpleType(ALeft, ARight, LLeftVarType, LRightVarType) of
    stErr:
      begin
        VarInvalidOpError(CLeftShiftOp, LLeftVarType, LRightVarType);
        Result := Unassigned;
      end;
    stEmp:
      Result := Unassigned;
    stNul:
      if NullStrictOperations then
      begin
        VarInvalidNullOpError(CLeftShiftOp);
        Result := Unassigned;
      end
      else
        Result := Null;
    stBoo, stI32:
      Result := AttemptInt32(ALeft, ARight);
    stU32:
      Result := AttemptUInt32(ALeft, ARight);
    stI64:
      Result := AttemptInt64(ALeft, ARight);
    stU64:
      Result := AttemptUInt64(ALeft, ARight);
    stDec, stDtm, stDbl, stStr:
      Result := AttemptInt64(ALeft, ARight);
    stObj:
      if not FindBinaryMethodInvoke(ALeft, ARight, ['op_LeftShift', 'op_SignedLeftShift'], Result) then
        Result := AttemptInt64(ALeft, ARight);
  end;
end;

class operator VariantHelper.RightShift(const ALeft, ARight: Variant): Variant;

  function AttemptInt64(const ALeft, ARight: Variant): Variant;
  begin
    try
      Result := Variant(ToInt64(ALeft) shr ToInt32(ARight));
    except
      on E: Exception do
      begin
        UnhandledOperationException(E, CRightShiftOp, ALeft, ARight);
        Result := Unassigned;
      end;
    end;
  end;

  function AttemptInt32(const ALeft, ARight: Variant): Variant;
  begin
    try
      Result := Variant(ToInt32(ALeft) shr ToInt32(ARight));
    except
      Result := AttemptInt64(ALeft, ARight);
    end;
  end;

  function AttemptUInt64(const ALeft, ARight: Variant): Variant;
  begin
    try
      Result := Variant(ToUInt64(ALeft) shr ToInt32(ARight));
    except
      Result := AttemptInt32(ALeft, ARight);
    end;
  end;

  function AttemptUInt32(const ALeft, ARight: Variant): Variant;
  begin
    try
      Result := Variant(ToUInt32(ALeft) shr ToInt32(ARight));
    except
      Result := AttemptUInt64(ALeft, ARight);
    end;
  end;

var
  LLeftVarType, LRightVarType: TVarType;
begin
  case FindSimpleType(ALeft, ARight, LLeftVarType, LRightVarType) of
    stErr:
      begin
        VarInvalidOpError(CRightShiftOp, LLeftVarType, LRightVarType);
        Result := Unassigned;
      end;
    stEmp:
      Result := Unassigned;
    stNul:
      if NullStrictOperations then
      begin
        VarInvalidNullOpError(CRightShiftOp);
        Result := Unassigned;
      end
      else
        Result := Null;
    stBoo, stI32:
      Result := AttemptInt32(ALeft, ARight);
    stU32:
      Result := AttemptUInt32(ALeft, ARight);
    stI64:
      Result := AttemptInt64(ALeft, ARight);
    stU64:
      Result := AttemptUInt64(ALeft, ARight);
    stDec, stDtm, stDbl, stStr:
      Result := AttemptInt64(ALeft, ARight);
    stObj:
      if not FindBinaryMethodInvoke(ALeft, ARight, ['op_RightShift', 'op_SignedRightShift'], Result) then
        Result := AttemptInt64(ALeft, ARight);
  end;
end;


class operator VariantHelper.Equal(const ALeft, ARight: Variant): Boolean;

  function AttemptString(const ALeft, ARight: Variant): Boolean;
  begin
    try
      Result := ToString(ALeft) = ToString(ARight);
    except
      on E: Exception do
      begin
        UnhandledOperationException(E, CEqualOp, ALeft, ARight);
        Result := False;
      end;
    end;
  end;

  function AttemptDouble(const ALeft, ARight: Variant): Boolean;
  begin
    try
      Result := ToDouble(ALeft) = ToDouble(ARight);
    except
      Result := AttemptString(ALeft, ARight);
    end;
  end;

  function AttemptDecimal(const ALeft, ARight: Variant): Boolean;
  begin
    try
      Result := ToDecimal(ALeft) = ToDecimal(ARight);
    except
      Result := AttemptDouble(ALeft, ARight);
    end;
  end;

  function AttemptInt64(const ALeft, ARight: Variant): Boolean;
  begin
    try
      Result := ToInt64(ALeft) = ToInt64(ARight);
    except
      Result := AttemptDecimal(ALeft, ARight);
    end;
  end;

  function AttemptUInt64(const ALeft, ARight: Variant): Boolean;
  begin
    try
      Result := ToUInt64(ALeft) = ToUInt64(ARight);
    except
      Result := AttemptDecimal(ALeft, ARight);
    end;
  end;

  function AttemptInt32(const ALeft, ARight: Variant): Boolean;
  begin
    try
      Result := ToInt32(ALeft) = ToInt32(ARight);
    except
      Result := AttemptInt64(ALeft, ARight);
    end;
  end;

  function AttemptUInt32(const ALeft, ARight: Variant): Boolean;
  begin
    try
      Result := ToUInt32(ALeft) = ToUInt32(ARight);
    except
      Result := AttemptUInt64(ALeft, ARight);
    end;
  end;

var
  LLeftVarType, LRightVarType: TVarType;
  LRelationship: TVariantRelationship;
begin
  case FindSimpleType(ALeft, ARight, LLeftVarType, LRightVarType) of
    stErr:
      begin
        VarInvalidOpError(CEqualOp, LLeftVarType, LRightVarType);
        Result := False;
      end;
    stEmp:
      Result := (LLeftVarType = varEmpty) and (LRightVarType = varEmpty);
    stNul:
      begin
        Result := False;
        case NullEqualityRule of
          ncrError:
            VarInvalidNullOpError(CEqualOp);
          ncrLoose:
            Result := (LLeftVarType = varNull) and (LRightVarType = varNull);
        end;
      end;
    stBoo:
      try
        Result := ToBoolean(ALeft) = ToBoolean(ARight);
      except
        Result := AttemptInt32(ALeft, ARight);
      end;
    stI32:
      Result := AttemptInt32(ALeft, ARight);
    stU32:
      Result := AttemptUInt32(ALeft, ARight);
    stI64:
      Result := AttemptInt64(ALeft, ARight);
    stU64:
      Result := AttemptUInt64(ALeft, ARight);
    stDec:
      Result := AttemptDecimal(ALeft, ARight);
    stDtm:
      try
        Result := ToDateTime(ALeft) = ToDateTime(ARight);
      except
        Result := AttemptDouble(ALeft, ARight);
      end;
    stDbl, stStr:
      Result := AttemptDouble(ALeft, ARight);
    stObj:
      if FindUnifiedCompare(ALeft, ARight, LRelationship) then
        Result := LRelationship = vrEqual
      else if not FindBinaryMethodInvoke(ALeft, ARight, ['op_Equality'], Result) then
        try
          Result := AttemptDouble(ALeft, ARight);
        except
          try
            Result := TObject.Equals(ALeft, ARight);
          except
            on E: Exception do
            begin
              UnhandledOperationException(E, CEqualOp, ALeft, ARight);
              Result := False;
            end;
          end;
        end;
  end;
end;

class operator VariantHelper.NotEqual(const ALeft, ARight: Variant): Boolean;

  function AttemptString(const ALeft, ARight: Variant): Boolean;
  begin
    try
      Result := ToString(ALeft) <> ToString(ARight);
    except
      on E: Exception do
      begin
        UnhandledOperationException(E, CNotEqualOp, ALeft, ARight);
        Result := False;
      end;
    end;
  end;

  function AttemptDouble(const ALeft, ARight: Variant): Boolean;
  begin
    try
      Result := ToDouble(ALeft) <> ToDouble(ARight);
    except
      Result := AttemptString(ALeft, ARight);
    end;
  end;

  function AttemptDecimal(const ALeft, ARight: Variant): Boolean;
  begin
    try
      Result := ToDecimal(ALeft) <> ToDecimal(ARight);
    except
      Result := AttemptDouble(ALeft, ARight);
    end;
  end;

  function AttemptInt64(const ALeft, ARight: Variant): Boolean;
  begin
    try
      Result := ToInt64(ALeft) <> ToInt64(ARight);
    except
      Result := AttemptDecimal(ALeft, ARight);
    end;
  end;

  function AttemptUInt64(const ALeft, ARight: Variant): Boolean;
  begin
    try
      Result := ToUInt64(ALeft) <> ToUInt64(ARight);
    except
      Result := AttemptDecimal(ALeft, ARight);
    end;
  end;

  function AttemptInt32(const ALeft, ARight: Variant): Boolean;
  begin
    try
      Result := ToInt32(ALeft) <> ToInt32(ARight);
    except
      Result := AttemptInt64(ALeft, ARight);
    end;
  end;

  function AttemptUInt32(const ALeft, ARight: Variant): Boolean;
  begin
    try
      Result := ToUInt32(ALeft) <> ToUInt32(ARight);
    except
      Result := AttemptUInt64(ALeft, ARight);
    end;
  end;

var
  LLeftVarType, LRightVarType: TVarType;
  LRelationship: TVariantRelationship;
begin
  case FindSimpleType(ALeft, ARight, LLeftVarType, LRightVarType) of
    stErr:
      begin
        VarInvalidOpError(CNotEqualOp, LLeftVarType, LRightVarType);
        Result := False;
      end;
    stEmp:
      Result := not ((LLeftVarType = varEmpty) and (LRightVarType = varEmpty));
    stNul:
      begin
        Result := False;
        case NullEqualityRule of
          ncrError:
            VarInvalidNullOpError(CNotEqualOp);
          ncrLoose:
            Result := not ((LLeftVarType = varNull) and (LRightVarType = varNull));
        end;
      end;
    stBoo:
      try
        Result := ToBoolean(ALeft) <> ToBoolean(ARight);
      except
        Result := AttemptInt32(ALeft, ARight);
      end;
    stI32:
      Result := AttemptInt32(ALeft, ARight);
    stU32:
      Result := AttemptUInt32(ALeft, ARight);
    stI64:
      Result := AttemptInt64(ALeft, ARight);
    stU64:
      Result := AttemptUInt64(ALeft, ARight);
    stDec:
      Result := AttemptDecimal(ALeft, ARight);
    stDtm:
      try
        Result := ToDateTime(ALeft) <> ToDateTime(ARight);
      except
        Result := AttemptDouble(ALeft, ARight);
      end;
    stDbl, stStr:
      Result := AttemptDouble(ALeft, ARight);
    stObj:
      if FindUnifiedCompare(ALeft, ARight, LRelationship) then
        Result := LRelationship <> vrEqual
      else if not FindBinaryMethodInvoke(ALeft, ARight, ['op_Inequality'], Result) then
        try
          Result := AttemptDouble(ALeft, ARight);
        except
          try
            Result := not (ALeft = ARight);
          except
            on E: Exception do
            begin
              UnhandledOperationException(E, CNotEqualOp, ALeft, ARight);
              Result := False;
            end;
          end;
        end;
  end;
end;

class operator VariantHelper.LessThan(const ALeft, ARight: Variant): Boolean;

  function AttemptString(const ALeft, ARight: Variant): Boolean;
  begin
    try
      Result := ToString(ALeft) < ToString(ARight);
    except
      on E: Exception do
      begin
        UnhandledOperationException(E, CLessThanOp, ALeft, ARight);
        Result := False;
      end;
    end;
  end;

  function AttemptDouble(const ALeft, ARight: Variant): Boolean;
  begin
    try
      Result := ToDouble(ALeft) < ToDouble(ARight);
    except
      Result := AttemptString(ALeft, ARight);
    end;
  end;

  function AttemptDecimal(const ALeft, ARight: Variant): Boolean;
  begin
    try
      Result := ToDecimal(ALeft) < ToDecimal(ARight);
    except
      Result := AttemptDouble(ALeft, ARight);
    end;
  end;

  function AttemptInt64(const ALeft, ARight: Variant): Boolean;
  begin
    try
      Result := ToInt64(ALeft) < ToInt64(ARight);
    except
      Result := AttemptDecimal(ALeft, ARight);
    end;
  end;

  function AttemptUInt64(const ALeft, ARight: Variant): Boolean;
  begin
    try
      Result := ToUInt64(ALeft) < ToUInt64(ARight);
    except
      Result := AttemptDecimal(ALeft, ARight);
    end;
  end;

  function AttemptInt32(const ALeft, ARight: Variant): Boolean;
  begin
    try
      Result := ToInt32(ALeft) < ToInt32(ARight);
    except
      Result := AttemptInt64(ALeft, ARight);
    end;
  end;

  function AttemptUInt32(const ALeft, ARight: Variant): Boolean;
  begin
    try
      Result := ToUInt32(ALeft) < ToUInt32(ARight);
    except
      Result := AttemptUInt64(ALeft, ARight);
    end;
  end;

var
  LLeftVarType, LRightVarType: TVarType;
  LRelationship: TVariantRelationship;
begin
  case FindSimpleType(ALeft, ARight, LLeftVarType, LRightVarType) of
    stErr:
      begin
        VarInvalidOpError(CLessThanOp, LLeftVarType, LRightVarType);
        Result := False;
      end;
    stEmp:
      Result := (LLeftVarType = varEmpty) and (LRightVarType <> varEmpty);
    stNul:
      begin
        Result := False;
        case NullEqualityRule of
          ncrError:
            VarInvalidNullOpError(CLessThanOp);
          ncrLoose:
            Result := (LLeftVarType = varNull) and (LRightVarType <> varNull);
        end;
      end;
    stBoo:
      try
        Result := ToBoolean(ALeft) < ToBoolean(ARight);
      except
        Result := AttemptInt32(ALeft, ARight);
      end;
    stI32:
      Result := AttemptInt32(ALeft, ARight);
    stU32:
      Result := AttemptUInt32(ALeft, ARight);
    stI64:
      Result := AttemptInt64(ALeft, ARight);
    stU64:
      Result := AttemptUInt64(ALeft, ARight);
    stDec:
      Result := AttemptDecimal(ALeft, ARight);
    stDtm:
      try
        Result := ToDateTime(ALeft) < ToDateTime(ARight);
      except
        Result := AttemptDouble(ALeft, ARight);
      end;
    stDbl, stStr:
      Result := AttemptDouble(ALeft, ARight);
    stObj:
      if FindUnifiedCompare(ALeft, ARight, LRelationship) then
        Result := LRelationship = vrLessThan
      else if not FindBinaryMethodInvoke(ALeft, ARight, ['op_LessThan'], Result) then
        Result := AttemptDouble(ALeft, ARight);
  end;
end;

class operator VariantHelper.LessThanOrEqual(const ALeft, ARight: Variant): Boolean;

  function AttemptString(const ALeft, ARight: Variant): Boolean;
  begin
    try
      Result := ToString(ALeft) <= ToString(ARight);
    except
      on E: Exception do
      begin
        UnhandledOperationException(E, CLessThanOrEqualOp, ALeft, ARight);
        Result := False;
      end;
    end;
  end;

  function AttemptDouble(const ALeft, ARight: Variant): Boolean;
  begin
    try
      Result := ToDouble(ALeft) <= ToDouble(ARight);
    except
      Result := AttemptString(ALeft, ARight);
    end;
  end;

  function AttemptDecimal(const ALeft, ARight: Variant): Boolean;
  begin
    try
      Result := ToDecimal(ALeft) <= ToDecimal(ARight);
    except
      Result := AttemptDouble(ALeft, ARight);
    end;
  end;

  function AttemptInt64(const ALeft, ARight: Variant): Boolean;
  begin
    try
      Result := ToInt64(ALeft) <= ToInt64(ARight);
    except
      Result := AttemptDecimal(ALeft, ARight);
    end;
  end;

  function AttemptUInt64(const ALeft, ARight: Variant): Boolean;
  begin
    try
      Result := ToUInt64(ALeft) <= ToUInt64(ARight);
    except
      Result := AttemptDecimal(ALeft, ARight);
    end;
  end;

  function AttemptInt32(const ALeft, ARight: Variant): Boolean;
  begin
    try
      Result := ToInt32(ALeft) <= ToInt32(ARight);
    except
      Result := AttemptInt64(ALeft, ARight);
    end;
  end;

  function AttemptUInt32(const ALeft, ARight: Variant): Boolean;
  begin
    try
      Result := ToUInt32(ALeft) <= ToUInt32(ARight);
    except
      Result := AttemptUInt64(ALeft, ARight);
    end;
  end;

var
  LLeftVarType, LRightVarType: TVarType;
  LRelationship: TVariantRelationship;
begin
  case FindSimpleType(ALeft, ARight, LLeftVarType, LRightVarType) of
    stErr:
      begin
        VarInvalidOpError(CLessThanOrEqualOp, LLeftVarType, LRightVarType);
        Result := False;
      end;
    stEmp:
      Result := LLeftVarType = varEmpty;
    stNul:
      begin
        Result := False;
        case NullEqualityRule of
          ncrError:
            VarInvalidNullOpError(CLessThanOrEqualOp);
          ncrLoose:
            Result := LLeftVarType = varNull;
        end;
      end;
    stBoo:
      try
        Result := ToBoolean(ALeft) <= ToBoolean(ARight);
      except
        Result := AttemptInt32(ALeft, ARight);
      end;
    stI32:
      Result := AttemptInt32(ALeft, ARight);
    stU32:
      Result := AttemptUInt32(ALeft, ARight);
    stI64:
      Result := AttemptInt64(ALeft, ARight);
    stU64:
      Result := AttemptUInt64(ALeft, ARight);
    stDec:
      Result := AttemptDecimal(ALeft, ARight);
    stDtm:
      try
        Result := ToDateTime(ALeft) <= ToDateTime(ARight);
      except
        Result := AttemptDouble(ALeft, ARight);
      end;
    stDbl, stStr:
      Result := AttemptDouble(ALeft, ARight);
    stObj:
      if FindUnifiedCompare(ALeft, ARight, LRelationship) then
        Result := LRelationship in [vrLessThan, vrEqual]
      else if not FindBinaryMethodInvoke(ALeft, ARight, ['op_LessThanOrEqual'], Result) then
        try
          Result := AttemptDouble(ALeft, ARight);
        except
          try
            Result := (ALeft < ARight) or (ALeft = ARight);
          except
            on E: Exception do
            begin
              UnhandledOperationException(E, CLessThanOrEqualOp, ALeft, ARight);
              Result := False;
            end;
          end;
        end;
  end;
end;

class operator VariantHelper.GreaterThan(const ALeft, ARight: Variant): Boolean;

  function AttemptString(const ALeft, ARight: Variant): Boolean;
  begin
    try
      Result := ToString(ALeft) > ToString(ARight);
    except
      on E: Exception do
      begin
        UnhandledOperationException(E, CGreaterThanOp, ALeft, ARight);
        Result := False;
      end;
    end;
  end;

  function AttemptDouble(const ALeft, ARight: Variant): Boolean;
  begin
    try
      Result := ToDouble(ALeft) > ToDouble(ARight);
    except
      Result := AttemptString(ALeft, ARight);
    end;
  end;

  function AttemptDecimal(const ALeft, ARight: Variant): Boolean;
  begin
    try
      Result := ToDecimal(ALeft) > ToDecimal(ARight);
    except
      Result := AttemptDouble(ALeft, ARight);
    end;
  end;

  function AttemptInt64(const ALeft, ARight: Variant): Boolean;
  begin
    try
      Result := ToInt64(ALeft) > ToInt64(ARight);
    except
      Result := AttemptDecimal(ALeft, ARight);
    end;
  end;

  function AttemptUInt64(const ALeft, ARight: Variant): Boolean;
  begin
    try
      Result := ToUInt64(ALeft) > ToUInt64(ARight);
    except
      Result := AttemptDecimal(ALeft, ARight);
    end;
  end;

  function AttemptInt32(const ALeft, ARight: Variant): Boolean;
  begin
    try
      Result := ToInt32(ALeft) > ToInt32(ARight);
    except
      Result := AttemptInt64(ALeft, ARight);
    end;
  end;

  function AttemptUInt32(const ALeft, ARight: Variant): Boolean;
  begin
    try
      Result := ToUInt32(ALeft) > ToUInt32(ARight);
    except
      Result := AttemptUInt64(ALeft, ARight);
    end;
  end;

var
  LLeftVarType, LRightVarType: TVarType;
  LRelationship: TVariantRelationship;
begin
  case FindSimpleType(ALeft, ARight, LLeftVarType, LRightVarType) of
    stErr:
      begin
        VarInvalidOpError(CGreaterThanOp, LLeftVarType, LRightVarType);
        Result := False;
      end;
    stEmp:
      Result := (LLeftVarType <> varEmpty) and (LRightVarType = varEmpty);
    stNul:
      begin
        Result := False;
        case NullEqualityRule of
          ncrError:
            VarInvalidNullOpError(CGreaterThanOp);
          ncrLoose:
            Result := (LLeftVarType <> varNull) and (LRightVarType = varNull);
        end;
      end;
    stBoo:
      try
        Result := ToBoolean(ALeft) > ToBoolean(ARight);
      except
        Result := AttemptInt32(ALeft, ARight);
      end;
    stI32:
      Result := AttemptInt32(ALeft, ARight);
    stU32:
      Result := AttemptUInt32(ALeft, ARight);
    stI64:
      Result := AttemptInt64(ALeft, ARight);
    stU64:
      Result := AttemptUInt64(ALeft, ARight);
    stDec:
      Result := AttemptDecimal(ALeft, ARight);
    stDtm:
      try
        Result := ToDateTime(ALeft) > ToDateTime(ARight);
      except
        Result := AttemptDouble(ALeft, ARight);
      end;
    stDbl, stStr:
      Result := AttemptDouble(ALeft, ARight);
    stObj:
      if FindUnifiedCompare(ALeft, ARight, LRelationship) then
        Result := LRelationship = vrGreaterThan
      else if not FindBinaryMethodInvoke(ALeft, ARight, ['op_GreaterThan'], Result) then
        Result := AttemptDouble(ALeft, ARight);
  end;
end;

class operator VariantHelper.GreaterThanOrEqual(const ALeft, ARight: Variant): Boolean;

  function AttemptString(const ALeft, ARight: Variant): Boolean;
  begin
    try
      Result := ToString(ALeft) >= ToString(ARight);
    except
      on E: Exception do
      begin
        UnhandledOperationException(E, CGreaterThanOrEqualOp, ALeft, ARight);
        Result := False;
      end;
    end;
  end;

  function AttemptDouble(const ALeft, ARight: Variant): Boolean;
  begin
    try
      Result := ToDouble(ALeft) >= ToDouble(ARight);
    except
      Result := AttemptString(ALeft, ARight);
    end;
  end;

  function AttemptDecimal(const ALeft, ARight: Variant): Boolean;
  begin
    try
      Result := ToDecimal(ALeft) >= ToDecimal(ARight);
    except
      Result := AttemptDouble(ALeft, ARight);
    end;
  end;

  function AttemptInt64(const ALeft, ARight: Variant): Boolean;
  begin
    try
      Result := ToInt64(ALeft) >= ToInt64(ARight);
    except
      Result := AttemptDecimal(ALeft, ARight);
    end;
  end;

  function AttemptUInt64(const ALeft, ARight: Variant): Boolean;
  begin
    try
      Result := ToUInt64(ALeft) >= ToUInt64(ARight);
    except
      Result := AttemptDecimal(ALeft, ARight);
    end;
  end;

  function AttemptInt32(const ALeft, ARight: Variant): Boolean;
  begin
    try
      Result := ToInt32(ALeft) >= ToInt32(ARight);
    except
      Result := AttemptInt64(ALeft, ARight);
    end;
  end;

  function AttemptUInt32(const ALeft, ARight: Variant): Boolean;
  begin
    try
      Result := ToUInt32(ALeft) >= ToUInt32(ARight);
    except
      Result := AttemptUInt64(ALeft, ARight);
    end;
  end;

var
  LLeftVarType, LRightVarType: TVarType;
  LRelationship: TVariantRelationship;
begin
  case FindSimpleType(ALeft, ARight, LLeftVarType, LRightVarType) of
    stErr:
      begin
        VarInvalidOpError(CGreaterThanOrEqualOp, LLeftVarType, LRightVarType);
        Result := False;
      end;
    stEmp:
      Result := LRightVarType = varEmpty;
    stNul:
      begin
        Result := False;
        case NullEqualityRule of
          ncrError:
            VarInvalidNullOpError(CGreaterThanOrEqualOp);
          ncrLoose:
            Result := LRightVarType = varNull;
        end;
      end;
    stBoo:
      try
        Result := ToBoolean(ALeft) >= ToBoolean(ARight);
      except
        Result := AttemptInt32(ALeft, ARight);
      end;
    stI32:
      Result := AttemptInt32(ALeft, ARight);
    stU32:
      Result := AttemptUInt32(ALeft, ARight);
    stI64:
      Result := AttemptInt64(ALeft, ARight);
    stU64:
      Result := AttemptUInt64(ALeft, ARight);
    stDec:
      Result := AttemptDecimal(ALeft, ARight);
    stDtm:
      try
        Result := ToDateTime(ALeft) >= ToDateTime(ARight);
      except
        Result := AttemptDouble(ALeft, ARight);
      end;
    stDbl, stStr:
      Result := AttemptDouble(ALeft, ARight);
    stObj:
      if FindUnifiedCompare(ALeft, ARight, LRelationship) then
        Result := LRelationship in [vrGreaterThan, vrEqual]
      else if not FindBinaryMethodInvoke(ALeft, ARight, ['op_GreaterThanOrEqual'], Result) then
        try
          Result := AttemptDouble(ALeft, ARight);
        except
          try
            Result := (ALeft > ARight) or (ALeft = ARight);
          except
            on E: Exception do
            begin
              UnhandledOperationException(E, CGreaterThanOrEqualOp, ALeft, ARight);
              Result := False;
            end;
          end;
        end;
  end;
end;

//==============================================================================
class operator VariantHelper.Implicit(const AValue: DBNull): Variant;
begin
  Result := Variant(TObject(AValue));
end;

class operator VariantHelper.Implicit(const AValue: SmallInt): Variant;
begin
  Result := Variant(TObject(AValue));
end;

class operator VariantHelper.Implicit(const AValue: Integer): Variant;
begin
  Result := Variant(TObject(AValue));
end;

class operator VariantHelper.Implicit(const AValue: Single): Variant;
begin
  Result := Variant(TObject(AValue));
end;

class operator VariantHelper.Implicit(const AValue: Double): Variant;
begin
  Result := Variant(TObject(AValue));
end;

class operator VariantHelper.Implicit(const AValue: DateTime): Variant;
begin
  Result := Variant(TObject(AValue));
end;

class operator VariantHelper.Implicit(const AValue: String): Variant;
begin
  Result := Variant(TObject(AValue));
end;

class operator VariantHelper.Implicit(const AValue: Boolean): Variant;
begin
  Result := Variant(TObject(AValue));
end;

class operator VariantHelper.Implicit(const AValue: Decimal): Variant;
begin
  Result := Variant(TObject(AValue));
end;

class operator VariantHelper.Implicit(const AValue: ShortInt): Variant;
begin
  Result := Variant(TObject(AValue));
end;

class operator VariantHelper.Implicit(const AValue: Byte): Variant;
begin
  Result := Variant(TObject(AValue));
end;

class operator VariantHelper.Implicit(const AValue: Word): Variant;
begin
  Result := Variant(TObject(AValue));
end;

class operator VariantHelper.Implicit(const AValue: LongWord): Variant;
begin
  Result := Variant(TObject(AValue));
end;

class operator VariantHelper.Implicit(const AValue: Int64): Variant;
begin
  Result := Variant(TObject(AValue));
end;

class operator VariantHelper.Implicit(const AValue: UInt64): Variant;
begin
  Result := Variant(TObject(AValue));
end;

class operator VariantHelper.Implicit(const AValue: Char): Variant;
begin
  Result := Variant(TObject(AValue));
end;

class operator VariantHelper.Implicit(const AValue: Currency): Variant;
begin
  Result := Variant(TObject(AValue));
end;

class operator VariantHelper.Implicit(const AValue: TDateTime): Variant;
begin
  Result := Variant(TObject(AValue));
end;

class operator VariantHelper.Implicit(const AValue: Extended): Variant;
begin
  Result := Variant(TObject(Double(AValue)));
end;

class operator VariantHelper.Implicit(const AValue: TBytes): Variant;
begin
  Result := Variant(TObject(AValue));
end;

class operator VariantHelper.Implicit(const AValue: AnsiString): Variant;
begin
  Result := String(AValue);
end;


class operator VariantHelper.Implicit(const AValue: Variant): DBNull;
begin
  Result := ToDBNull(AValue);
end;

class operator VariantHelper.Implicit(const AValue: Variant): SmallInt;
begin
  Result := ToInt16(AValue);
end;

class operator VariantHelper.Implicit(const AValue: Variant): Integer;
begin
  Result := ToInt32(AValue);
end;

class operator VariantHelper.Implicit(const AValue: Variant): Single;
begin
  Result := ToSingle(AValue);
end;

class operator VariantHelper.Implicit(const AValue: Variant): Double;
begin
  Result := ToDouble(AValue);
end;

class operator VariantHelper.Implicit(const AValue: Variant): DateTime;
begin
  Result := ToDateTime(AValue);
end;

class operator VariantHelper.Implicit(const AValue: Variant): String;
begin
  Result := ToString(AValue);
end;

class operator VariantHelper.Implicit(const AValue: Variant): Boolean;
begin
  Result := ToBoolean(AValue);
end;

class operator VariantHelper.Implicit(const AValue: Variant): Decimal;
begin
  Result := ToDecimal(AValue);
end;

class operator VariantHelper.Implicit(const AValue: Variant): ShortInt;
begin
  Result := ToSByte(AValue);
end;

class operator VariantHelper.Implicit(const AValue: Variant): Byte;
begin
  Result := ToByte(AValue);
end;

class operator VariantHelper.Implicit(const AValue: Variant): Word;
begin
  Result := ToUInt16(AValue);
end;

class operator VariantHelper.Implicit(const AValue: Variant): LongWord;
begin
  Result := ToUInt32(AValue);
end;

class operator VariantHelper.Implicit(const AValue: Variant): Int64;
begin
  Result := ToInt64(AValue);
end;

class operator VariantHelper.Implicit(const AValue: Variant): UInt64;
begin
  Result := ToUInt64(AValue);
end;

class operator VariantHelper.Implicit(const AValue: Variant): Char;
begin
  Result := ToChar(AValue);
end;

class operator VariantHelper.Implicit(const AValue: Variant): Currency;
begin
  Result := ToCurrency(AValue);
end;

class operator VariantHelper.Implicit(const AValue: Variant): TDateTime;
begin
  Result := ToTDateTime(AValue);
end;

class operator VariantHelper.Implicit(const AValue: Variant): Extended;
begin
  Result := ToDouble(AValue);
end;

class operator VariantHelper.Implicit(const AValue: Variant): TBytes;
begin
  Result := ToBytes(AValue);
end;

class operator VariantHelper.Implicit(const AValue: Variant): AnsiString;
begin
  Result := ToString(AValue);
end;

class operator VariantHelper.Implicit(const AValue: Variant): WordBool;
begin
  Result := ToBoolean(AValue);
end;

class operator VariantHelper.Implicit(const AValue: Variant): LongBool;
begin
  Result := ToBoolean(AValue);
end;


//==============================================================================
class function VariantHelper.BooleanAsInteger(const Value: Boolean): Integer;
begin
  if Value then
    Result := BooleanTrueAsOrdinalValue
  else
    Result := 0;
end;

class function VariantHelper.ToDBNull(const AObject: TObject): DBNull;
begin
  if AObject is System.DBNull then
    Result := Null
  else
  begin
    Result := Null;
    if NullStrictConvert then
      VarTypeNullCastError(varDouble);
  end;
end;

class function VariantHelper.ToSingle(const AObject: TObject): Single;
begin
  try
    Result := ToDouble(AObject);
  except
    on E: Exception do
    begin
      UnhandledConversionException(E, VarType(AObject), varSingle);
      Result := 0;
    end;
  end;
end;

class function VariantHelper.ToDouble(const AObject: TObject): Double;

  function ViaString(const AValue: string): Double;
  var
    LInt64: Int64;
  begin
    if not TryStrToFloat(AValue, Result) then
      if TryStrToInt64(AValue, LInt64) then
        Result := LInt64
      else
        try
          Result := TDateTime.Parse(AValue);
        except
          Result := BooleanAsInteger(StrToBool(AValue, True));
        end;
  end;

begin
  Result := 0;
  try
    case VarType(AObject) of
      varEmpty:;
      varNull:
        if NullStrictConvert then
          VarTypeNullCastError(varDouble);
      varSmallInt:
        Result := SmallInt(AObject);
      varInteger:
        Result := Integer(AObject);
      varSingle:
        Result := Single(AObject);
      varDouble:
        Result := Double(AObject);
      varCurrency:
        Result := Currency(AObject);
      varDate:
        Result := TDateTime(AObject);
      varString:
        Result := ViaString(String(AObject));
      varBoolean:
        Result := BooleanAsInteger(Boolean(AObject));
      varObject:
        Result := Convert.ToDouble(AObject);
      varDecimal:
        Result := Decimal.ToDouble(Decimal(AObject));
      varShortInt:
        Result := ShortInt(AObject);
      varByte:
        Result := Byte(AObject);
      varWord:
        Result := Word(AObject);
      varLongWord:
        Result := LongWord(AObject);
      varInt64:
        Result := Int64(AObject);
      varUInt64:
        Result := UInt64(AObject);
      varChar:
        Result := ViaString(Char(AObject));
      varDateTime:
        Result := DateTime(AObject).ToOADate;
    else
      Result := Convert.ToDouble(AObject)
    end;
  except
    on E: Exception do
      UnhandledConversionException(E, VarType(AObject), varDouble);
  end;
end;

class function VariantHelper.ToDateTime(const AObject: TObject): DateTime;
begin
  if AObject is DateTime then
    Result := DateTime(AObject)
  else
    Result := ToTDateTime(AObject);
end;

class function VariantHelper.ToString(const AObject: TObject): String;
begin
  Result := '';
  try
    case VarType(AObject) of
      varEmpty:;
      varNull:
        if NullStrictConvert then
          VarTypeNullCastError(varString);
      varSmallInt:
        Result := IntToStr(SmallInt(AObject));
      varInteger:
        Result := IntToStr(Integer(AObject));
      varSingle:
        Result := FloatToStr(Single(AObject));
      varDouble:
        Result := FloatToStr(Double(AObject));
      varCurrency:
        Result := CurrToStr(Currency(AObject));
      varDate:
        Result := DateTimeToStr(TDateTime(AObject));
      varString:
        Result := String(AObject);
      varBoolean:
        begin
          Result := BoolToStr(Boolean(AObject), True);
          case BooleanToStringRule of
            bsrAsIs:;
            bsrLower: Result := Lowercase(Result);
            bsrUpper: Result := Uppercase(Result);
          end;
        end;
      varObject:
        Result := Convert.ToString(AObject);
      varDecimal:
        Result := Decimal(AObject).ToString;
      varShortInt:
        Result := IntToStr(ShortInt(AObject));
      varByte:
        Result := UIntToStr(Byte(AObject));
      varWord:
        Result := UIntToStr(Word(AObject));
      varLongWord:
        Result := UIntToStr(LongWord(AObject));
      varInt64:
        Result := IntToStr(Int64(AObject));
      varUInt64:
        Result := UIntToStr(UInt64(AObject));
      varChar:
        Result := Char(AObject);
      varDateTime:
        Result := DateTimeToStr(DateTime(AObject));
    else
      Result := Convert.ToString(AObject)
    end;
  except
    on E: Exception do
      UnhandledConversionException(E, VarType(AObject), varString);
  end;
end;

class function VariantHelper.ToBoolean(const AObject: TObject): Boolean;

  function ViaString(const AValue: string): Boolean;
  var
    LInt64: Int64;
  begin
    if not TryStrToBool(AValue, Result) then
      if TryStrToInt64(AValue, LInt64) then
        Result := LInt64 <> 0
      else
        Result := TDateTime.Parse(AValue) <> 0;
  end;

begin
  Result := False;
  try
    case VarType(AObject) of
      varEmpty:;
      varNull:
        if NullStrictConvert then
          VarTypeNullCastError(varBoolean);
      varSmallInt:
        Result := SmallInt(AObject) <> 0;
      varInteger:
        Result := Integer(AObject) <> 0;
      varSingle:
        Result := Single(AObject) <> 0;
      varDouble:
        Result := Double(AObject) <> 0;
      varCurrency:
        Result := Currency(AObject) <> 0;
      varDate:
        Result := TDateTime(AObject) <> 0;
      varString:
        Result := ViaString(String(AObject));
      varBoolean:
        Result := Boolean(AObject);
      varObject:
        Result := Convert.ToBoolean(AObject);
      varDecimal:
        Result := Decimal(AObject) <> 0;
      varShortInt:
        Result := ShortInt(AObject) <> 0;
      varByte:
        Result := Byte(AObject) <> 0;
      varWord:
        Result := Word(AObject) <> 0;
      varLongWord:
        Result := LongWord(AObject) <> 0;
      varInt64:
        Result := Int64(AObject) <> 0;
      varUInt64:
        Result := UInt64(AObject) <> 0;
      varChar:
        Result := ViaString(Char(AObject));
      varDateTime:
        Result := DateTime(AObject).ToOADate <> 0;
    else
      Result := Convert.ToBoolean(AObject)
    end;
  except
    on E: Exception do
      UnhandledConversionException(E, VarType(AObject), varBoolean);
  end;
end;

class function VariantHelper.ToDecimal(const AObject: TObject): Decimal;

  function ViaString(const AValue: string): Decimal;
  var
    LDouble: Double;
    LInt64: Int64;
  begin
    try
      Result := Decimal.Parse(AValue);
    except
      if TryStrToFloat(AValue, LDouble) then
        Result := LDouble
      else if TryStrToInt64(AValue, LInt64) then
        Result := LInt64
      else
        try
          Result := Double(TDateTime.Parse(AValue));
        except
          Result := BooleanAsInteger(StrToBool(AValue, True));
        end;
    end;
  end;

begin
  Result := 0;
  try
    case VarType(AObject) of
      varEmpty:;
      varNull:
        if NullStrictConvert then
          VarTypeNullCastError(varDecimal);
      varSmallInt:
        Result := SmallInt(AObject);
      varInteger:
        Result := Integer(AObject);
      varSingle:
        Result := Single(AObject);
      varDouble:
        Result := Double(AObject);
      varCurrency:
        Result := Currency(AObject);
      varDate:
        Result := Double(TDateTime(AObject));
      varString:
        Result := ViaString(String(AObject));
      varBoolean:
        Result := BooleanAsInteger(Boolean(AObject));
      varObject:
        Result := Convert.ToDecimal(AObject);
      varDecimal:
        Result := Decimal(AObject);
      varShortInt:
        Result := ShortInt(AObject);
      varByte:
        Result := Byte(AObject);
      varWord:
        Result := Word(AObject);
      varLongWord:
        Result := LongWord(AObject);
      varInt64:
        Result := Int64(AObject);
      varUInt64:
        Result := UInt64(AObject);
      varChar:
        Result := ViaString(Char(AObject));
      varDateTime:
        Result := DateTime(AObject).ToOADate;
    else
      Result := Convert.ToDecimal(AObject)
    end;
  except
    on E: Exception do
      UnhandledConversionException(E, VarType(AObject), varDecimal);
  end;
end;

class function VariantHelper.ToSByte(const AObject: TObject): ShortInt;
begin
  try
    Result := ToInt32(AObject);
  except
    on E: Exception do
    begin
      UnhandledConversionException(E, VarType(AObject), varShortInt);
      Result := 0;
    end;
  end;
end;

class function VariantHelper.ToInt16(const AObject: TObject): SmallInt;
begin
  try
    Result := ToInt32(AObject);
  except
    on E: Exception do
    begin
      UnhandledConversionException(E, VarType(AObject), varSmallInt);
      Result := 0;
    end;
  end;
end;

class function VariantHelper.ToInt32(const AObject: TObject): Integer;

  function ViaString(const AValue: string): Integer;
  var
    LDouble: Double;
  begin
    if not TryStrToInt(AValue, Result) then
      if TryStrToFloat(AValue, LDouble) then
        Result := Round(LDouble)
      else
        try
          Result := Round(TDateTime.Parse(AValue));
        except
          Result := BooleanAsInteger(StrToBool(AValue, True));
        end;
  end;

begin
  Result := 0;
  try
    case VarType(AObject) of
      varEmpty:;
      varNull:
        if NullStrictConvert then
          VarTypeNullCastError(varInteger);
      varSmallInt:
        Result := SmallInt(AObject);
      varInteger:
        Result := Integer(AObject);
      varSingle:
        Result := Round(Single(AObject));
      varDouble:
        Result := Round(Double(AObject));
      varCurrency:
        Result := Round(Currency(AObject));
      varDate:
        Result := Round(TDateTime(AObject));
      varString:
        Result := ViaString(String(AObject));
      varBoolean:
        Result := BooleanAsInteger(Boolean(AObject));
      varObject:
        Result := Convert.ToInt32(AObject);
      varDecimal:
        Result := Decimal.ToInt32(Decimal(AObject));
      varShortInt:
        Result := ShortInt(AObject);
      varByte:
        Result := Byte(AObject);
      varWord:
        Result := Word(AObject);
      varLongWord:
        Result := LongWord(AObject);
      varInt64:
        Result := Int64(AObject);
      varUInt64:
        Result := Convert.ToInt32(Convert.ToUInt64(AObject));
      varChar:
        Result := ViaString(Char(AObject));
      varDateTime:
        Result := Round(DateTime(AObject).ToOADate);
    else
      Result := Convert.ToInt32(AObject)
    end;
  except
    on E: Exception do
      UnhandledConversionException(E, VarType(AObject), varInteger);
  end;
end;

class function VariantHelper.ToByte(const AObject: TObject): Byte;
begin
  try
    Result := ToUInt32(AObject);
  except
    on E: Exception do
    begin
      UnhandledConversionException(E, VarType(AObject), varByte);
      Result := 0;
    end;
  end;
end;

class function VariantHelper.ToUInt16(const AObject: TObject): Word;
begin
  try
    Result := ToUInt32(AObject);
  except
    on E: Exception do
    begin
      UnhandledConversionException(E, VarType(AObject), varWord);
      Result := 0;
    end;
  end;
end;

class function VariantHelper.ToUInt32(const AObject: TObject): LongWord;

  function ViaString(const AValue: string): LongWord;
  var
    LDouble: Double;
  begin
    if not TryStrToLongWord(AValue, Result) then
      if TryStrToFloat(AValue, LDouble) then
        Result := Round(LDouble)
      else
        try
          Result := Round(TDateTime.Parse(AValue));
        except
          Result := Abs(BooleanAsInteger(StrToBool(AValue, True)));
        end;
  end;

begin
  Result := 0;
  try
    case VarType(AObject) of
      varEmpty:;
      varNull:
        if NullStrictConvert then
          VarTypeNullCastError(varLongWord);
      varSmallInt:
        Result := SmallInt(AObject);
      varInteger:
        Result := Integer(AObject);
      varSingle:
        Result := Round(Single(AObject));
      varDouble:
        Result := Round(Double(AObject));
      varCurrency:
        Result := Round(Currency(AObject));
      varDate:
        Result := Round(TDateTime(AObject));
      varString:
        Result := ViaString(String(AObject));
      varBoolean:
        Result := Abs(BooleanAsInteger(Boolean(AObject)));
      varObject:
        Result := Convert.ToUInt32(AObject);
      varDecimal:
        Result := Decimal.ToUInt32(Decimal(AObject));
      varShortInt:
        Result := ShortInt(AObject);
      varByte:
        Result := Byte(AObject);
      varWord:
        Result := Word(AObject);
      varLongWord:
        Result := LongWord(AObject);
      varInt64:
        Result := Int64(AObject);
      varUInt64:
        Result := Convert.ToUInt32(UInt64(AObject));
      varChar:
        Result := ViaString(Char(AObject));
      varDateTime:
        Result := Round(DateTime(AObject).ToOADate);
    else
      Result := Convert.ToUInt32(AObject)
    end;
  except
    on E: Exception do
      UnhandledConversionException(E, VarType(AObject), varLongWord);
  end;
end;

class function VariantHelper.ToInt64(const AObject: TObject): Int64;

  function ViaString(const AValue: string): Int64;
  var
    LDouble: Double;
  begin
    if not TryStrToInt64(AValue, Result) then
      if TryStrToFloat(AValue, LDouble) then
        Result := Round(LDouble)
      else
        try
          Result := Round(TDateTime.Parse(AValue));
        except
          Result := BooleanAsInteger(StrToBool(AValue, True));
        end;
  end;

begin
  Result := 0;
  try
    case VarType(AObject) of
      varEmpty:;
      varNull:
        if NullStrictConvert then
          VarTypeNullCastError(varInt64);
      varSmallInt:
        Result := SmallInt(AObject);
      varInteger:
        Result := Integer(AObject);
      varSingle:
        Result := Round(Single(AObject));
      varDouble:
        Result := Round(Double(AObject));
      varCurrency:
        Result := Round(Currency(AObject));
      varDate:
        Result := Round(TDateTime(AObject));
      varString:
        Result := ViaString(String(AObject));
      varBoolean:
        Result := BooleanAsInteger(Boolean(AObject));
      varObject:
        Result := Convert.ToInt64(AObject);
      varDecimal:
        Result := Decimal.ToInt64(Decimal(AObject));
      varShortInt:
        Result := ShortInt(AObject);
      varByte:
        Result := Byte(AObject);
      varWord:
        Result := Word(AObject);
      varLongWord:
        Result := LongWord(AObject);
      varInt64:
        Result := Int64(AObject);
      varUInt64:
        Result := Convert.ToInt64(Convert.ToUInt64(AObject));
      varChar:
        Result := ViaString(Char(AObject));
      varDateTime:
        Result := Round(DateTime(AObject).ToOADate);
    else
      Result := Convert.ToInt64(AObject)
    end;
  except
    on E: Exception do
      UnhandledConversionException(E, VarType(AObject), varInt64);
  end;
end;

// ToUInt64 uses System.Convert.ToUInt64 as DCCIL currently doesn't range
//   check assignments to UInt64 correctly.
class function VariantHelper.ToUInt64(const AObject: TObject): UInt64;

  function ViaDouble(const AValue: Double): UInt64;
  begin
    // Round only supports Int64's range, doing it this way allows
    //  us to support the full range of UInt64.
    Result := Convert.ToUInt64(Math.Round(AValue));
  end;

  function ViaString(const AValue: string): UInt64;
  var
    LDouble: Double;
  begin
    if not TryStrToUInt64(AValue, Result) then
      if TryStrToFloat(AValue, LDouble) then
        Result := ViaDouble(LDouble)
      else
        try
          Result := ViaDouble(TDateTime.Parse(AValue));
        except
          Result := Abs(BooleanAsInteger(StrToBool(AValue, True)));
        end;
  end;

begin
  Result := 0;
  try
    case VarType(AObject) of
      varEmpty:;
      varNull:
        if NullStrictConvert then
          VarTypeNullCastError(varUInt64);
      varSmallInt:
        Result := Convert.ToUInt64(SmallInt(AObject));
      varInteger:
        Result := Convert.ToUInt64(Integer(AObject));
      varSingle:
        Result := ViaDouble(Single(AObject));
      varDouble:
        Result := ViaDouble(Double(AObject));
      varCurrency:
        Result := ViaDouble(Currency(AObject));
      varDate:
        Result := ViaDouble(TDateTime(AObject));
      varString:
        Result := ViaString(String(AObject));
      varBoolean:
        Result := Abs(BooleanAsInteger(Boolean(AObject)));
      varObject:
        Result := Convert.ToUInt64(AObject);
      varDecimal:
        Result := Decimal.ToUInt64(Decimal(AObject));
      varShortInt:
        Result := Convert.ToUInt64(ShortInt(AObject));
      varByte:
        Result := Byte(AObject);
      varWord:
        Result := Word(AObject);
      varLongWord:
        Result := LongWord(AObject);
      varInt64:
        Result := Convert.ToUInt64(Int64(AObject));
      varUInt64:
        Result := UInt64(AObject);
      varChar:
        Result := ViaString(Char(AObject));
      varDateTime:
        Result := ViaDouble(DateTime(AObject).ToOADate);
    else
      Result := Convert.ToUInt64(AObject)
    end;
  except
    on E: Exception do
      UnhandledConversionException(E, VarType(AObject), varUInt64);
  end;
end;

class function VariantHelper.ToChar(const AObject: TObject): Char;
begin
  if AObject is System.Char then
    Result := Char(AObject)
  else
    Result := ToString(AObject)[1];
end;

class function VariantHelper.ToCurrency(const AObject: TObject): Currency;

  function ViaString(const AValue: string): Currency;
  var
    LInt64: Int64;
  begin
    if not TryStrToCurr(AValue, Result) then
      if TryStrToInt64(AValue, LInt64) then
        Result := LInt64
      else
        try
          Result := TDateTime.Parse(AValue).ToOADate;
        except
          Result := BooleanAsInteger(StrToBool(AValue, True));
        end;
  end;

begin
  Result := 0;
  try
    case VarType(AObject) of
      varEmpty:;
      varNull:
        if NullStrictConvert then
          VarTypeNullCastError(varCurrency);
      varSmallInt:
        Result := SmallInt(AObject);
      varInteger:
        Result := Integer(AObject);
      varSingle:
        Result := Single(AObject);
      varDouble:
        Result := Double(AObject);
      varCurrency:
        Result := Currency(AObject);
      varDate:
        Result := Double(TDateTime(AObject));
      varString:
        Result := ViaString(String(AObject));
      varBoolean:
        Result := BooleanAsInteger(Boolean(AObject));
      varObject:
        Result := Convert.ToDecimal(AObject);
      varDecimal:
        Result := Decimal(AObject);
      varShortInt:
        Result := ShortInt(AObject);
      varByte:
        Result := Byte(AObject);
      varWord:
        Result := Word(AObject);
      varLongWord:
        Result := LongWord(AObject);
      varInt64:
        Result := Int64(AObject);
      varUInt64:
        Result := UInt64(AObject);
      varChar:
        Result := ViaString(Char(AObject));
      varDateTime:
        Result := DateTime(AObject).ToOADate;
    else
      Result := Convert.ToDecimal(AObject)
    end;
  except
    on E: Exception do
      UnhandledConversionException(E, VarType(AObject), varCurrency);
  end;
end;

class function VariantHelper.ToTDateTime(const AObject: TObject): TDateTime;

  function ViaString(const AValue: string): Double;
  var
    LInt64: Int64;
  begin
    if not TryStrToFloat(AValue, Result) then
      if TryStrToInt64(AValue, LInt64) then
        Result := LInt64
      else
        try
          Result := TDateTime.Parse(AValue);
        except
          Result := BooleanAsInteger(StrToBool(AValue, True));
        end;
  end;

begin
  Result := 0;
  try
    case VarType(AObject) of
      varEmpty:;
      varNull:
        if NullStrictConvert then
          VarTypeNullCastError(varDouble);
      varSmallInt:
        Result := SmallInt(AObject);
      varInteger:
        Result := Integer(AObject);
      varSingle:
        Result := Single(AObject);
      varDouble:
        Result := Double(AObject);
      varCurrency:
        Result := Double(Currency(AObject));
      varDate:
        Result := TDateTime(AObject);
      varString:
        Result := ViaString(String(AObject));
      varBoolean:
        Result := BooleanAsInteger(Boolean(AObject));
      varObject:
        Result := Convert.ToDouble(AObject);
      varDecimal:
        Result := Decimal.ToDouble(Decimal(AObject));
      varShortInt:
        Result := ShortInt(AObject);
      varByte:
        Result := Byte(AObject);
      varWord:
        Result := Word(AObject);
      varLongWord:
        Result := LongWord(AObject);
      varInt64:
        Result := Int64(AObject);
      varUInt64:
        Result := UInt64(AObject);
      varChar:
        Result := ViaString(Char(AObject));
      varDateTime:
        Result := DateTime(AObject);
    else
      Result := Convert.ToDateTime(AObject)
    end;
  except
    on E: Exception do
      UnhandledConversionException(E, VarType(AObject), varDate);
  end;
end;

class function VariantHelper.ToBytes(const AObject: TObject): TBytes;
begin
  if VarType(AObject) = varArray or varByte then
    Result := TBytes(AObject)
  else
                                                                 
    VarTypeCastError(VarType(AObject), varArray or varByte);
end;


//==============================================================================
function VarType(const AValue: Variant): TVarType;
begin
  Result := Variant.VarType(AValue);
end;

function VarIsType(const AValue: Variant; const AVarType: TVarType): Boolean; overload;
begin
  Result := VarType(AValue) = AVarType;
end;

function VarIsType(const AValue: Variant; const AVarTypes: array of TVarType): Boolean; overload;
var
  LValueType: TVarType;
  I: Integer;
begin
  Result := False;
  LValueType := VarType(AValue);
  for I := Low(AVarTypes) to High(AVarTypes) do
    if LValueType = AVarTypes[I] then
    begin
      Result := True;
      Break;
    end;
end;

function VarTypeAsText(const AValue: Variant): string;
begin
  Result := Variant.VarTypeAsText(AValue);
end;

function VarTypeAsText(const AVarType: TVarType): string;
begin
  Result := Variant.VarTypeAsText(AVarType);
end;

function VarIsEmpty(const AValue: Variant): Boolean;
begin
  Result := Variant.IsEmpty(AValue);
end;

procedure VarCheckEmpty(const AValue: Variant);
begin
  if Variant.IsEmpty(AValue) then
    Variant.VarIsEmptyError;
end;

function VarIsNull(const AValue: Variant): Boolean;
begin
  Result := Variant.IsNull(AValue);
end;

function VarIsClear(const AValue: Variant): Boolean;
begin
  Result := Variant.IsClear(AValue);
end;

function VarIsOrdinal(const AValue: Variant): Boolean;
begin
  Result := VarType(AValue) in COrdinalTypes;
end;

function VarIsFloat(const AValue: Variant): Boolean;
begin
  Result := VarType(AValue) in CFloatTypes;
end;

function VarIsNumeric(const AValue: Variant): Boolean;
begin
  Result := VarType(AValue) in (COrdinalTypes + CFloatTypes);
end;

function VarIsStr(const AValue: Variant): Boolean;
begin
  Result := VarType(AValue) in CStringTypes;
end;

function VarAsType(const AValue: Variant; const AVarType: TVarType): Variant;
begin
  Result := Variant.CopyAsVarType(AValue, AVarType);
end;

function VarAsType(const AValue: Variant; const ASystemType: System.Type): Variant;
begin
  Result := Variant.CopyAsSystemType(AValue, ASystemType);
end;

function VarToStr(const AValue: Variant): string;
begin
  Result := Variant.ToStr(AValue, Variant.NullAsStringValue);
end;

function VarToStrDef(const AValue: Variant; const ANullString: string): string;
begin
  Result := Variant.ToStr(AValue, ANullString);
end;

function VarToWideStr(const AValue: Variant): WideString;
begin
  Result := Variant.ToStr(AValue, Variant.NullAsStringValue);
end;

function VarToWideStrDef(const AValue: Variant; const ANullString: WideString): WideString;
begin
  Result := Variant.ToStr(AValue, ANullString);
end;

function VarInRange(const AValue, AMin, AMax: Variant): Boolean;
begin
  Result := Variant.InRange(AValue, AMin, AMax);
end;

function VarEnsureRange(const AValue, AMin, AMax: Variant): Variant;
begin
  Result := Variant.EnsureRange(AValue, AMin, AMax);
end;

function VarSameValue(const ALeft, ARight: Variant): Boolean;
begin
  Result := Variant.SameValue(ALeft, ARight);
end;

function VarCompareValue(const ALeft, ARight: Variant): TVariantRelationship;
begin
  Result := Variant.CompareValue(ALeft, ARight);
end;


//==============================================================================
function VarIsArray(const AValue: Variant): Boolean;
begin
  Result := Variant.IsArray(AValue);
end;

function VarArrayElementsType(const AValue: Variant): TVarType;
begin
  Result := Variant.ArrayElementsType(AValue);
end;

function VarArrayElementsIsType(const AValue: Variant; AVarType: TVarType): Boolean;
var
  LVarType: TVarType;
begin
  Result := Variant.ArrayElementsType(AValue, LVarType) and
            (LVarType = AVarType);
end;

function VarArrayElementsIsType(const AValue: Variant; const AVarTypes: array of TVarType): Boolean;
var
  LValueType: TVarType;
  I: Integer;
begin
  Result := False;
  LValueType := VarArrayElementsType(AValue);
  for I := Low(AVarTypes) to High(AVarTypes) do
    if LValueType = AVarTypes[I] then
    begin
      Result := True;
      Break;
    end;
end;

function VarArrayCreate(const ABounds: array of Integer; AVarType: TVarType): Variant;
begin
  Result := Variant.ArrayCreate(ABounds, AVarType);
end;

function VarArrayCreate(const ABounds: array of Integer; ASystemType: System.Type): Variant;
begin
  Result := Variant.ArrayCreate(ABounds, ASystemType);
end;

function VarArrayOf(const AValues: array of Variant): Variant;
begin
  Result := Variant.ArrayOf(AValues);
end;

function VarArrayDimCount(const AValue: Variant): Integer;
begin
  Result := Variant.ArrayDimCount(AValue);
end;

function VarArrayLowBound(const AValue: Variant; ADim: Integer): Integer;
begin
  Result := Variant.ArrayLowBound(AValue, ADim);
end;

function VarArrayHighBound(const AValue: Variant; ADim: Integer): Integer;
begin
  Result := Variant.ArrayHighBound(AValue, ADim);
end;

function VarArrayGet(const AValue: Variant; const AIndices: array of Integer): Variant;
begin
  Result := Variant.ArrayGetElement(AValue, AIndices);
end;

procedure VarArrayPut(var AValue: Variant; const AData: Variant; const AIndices: array of Integer);
begin
  Variant.ArrayPutElement(AValue, AData, AIndices);
end;

//==============================================================================
function VarIsError(const AValue: Variant): Boolean;
begin
  Result := Variant.IsError(AValue);
end;

function VarIsError(const AValue: Variant; out AResult: Integer): Boolean;
begin
  Result := Variant.IsError(AValue, AResult);
end;

function VarIsError(const AValue: Variant; out AException: Exception): Boolean;
begin
  Result := Variant.IsError(AValue, AException);
end;

function VarFromErrorCode(const AResult: Integer): Variant;
begin
  Result := Variant.FromErrorCode(AResult);
end;

function VarFromException(const AException: Exception): Variant;
begin
  Result := Variant.FromException(AException);
end;

function VarToException(const AValue: Variant): Exception;
begin
  Result := Variant.ToException(AValue);
end;

function VarAsError(const AResult: Integer): Variant;
begin
  Result := VarFromErrorCode(AResult);
end;

//==============================================================================
function Unassigned: Variant;
begin
  Result := Variant.Unassigned;
end;

function Null: Variant;
begin
  Result := Variant.Null;
end;

//==============================================================================
procedure VarClear(var AValue: Variant);
begin
  Variant.Clear(AValue);
end;

function VarCopy(const AValue: Variant): Variant;
begin
  Result := Variant.Copy(AValue);
end;

procedure VarCast(var Destination: Variant; const Source: Variant; const AVarType: TVarType);
begin
  Destination := Variant.CopyAsVarType(Source, AVarType);
end;

//==============================================================================
function RegisterCustomVariantType(const AClass: TClass): TVarType;
begin
  Result := Variant.RegisterCustomVariantType(AClass.ClassInfo);
end;

function RegisterCustomVariantType(const ASystemType: System.Type): TVarType;
begin
  Result := Variant.RegisterCustomVariantType(ASystemType);
end;

function FindCustomVariantType(const AClassName: string; out ACustomVariant: Variant): Boolean;
begin
  Result := Variant.FindCustomVariantType(AClassName, ACustomVariant);
end;


//==============================================================================
function OleVariantHelper.get_Items(AIndices: array of Integer): OleVariant;
begin
  Result := Variant(Self).get_Items(AIndices);
end;

procedure OleVariantHelper.set_Items(AIndices: array of Integer; const AValue: OleVariant);
begin
  Variant(Self).set_Items(AIndices, AValue);
end;

class operator OleVariantHelper.Inc(const AValue: OleVariant): OleVariant;
var
  LValue: Variant;
begin
  LValue := AValue;
  Inc(LValue);
  Result := LValue;
end;

class operator OleVariantHelper.Dec(const AValue: OleVariant): OleVariant;
var
  LValue: Variant;
begin
  LValue := AValue;
  Dec(LValue);
  Result := LValue;
end;

class operator OleVariantHelper.Trunc(const AValue: OleVariant): OleVariant;
begin
  Result := Trunc(Variant(AValue));
end;

class operator OleVariantHelper.Round(const AValue: OleVariant): OleVariant;
begin
  Result := Round(Variant(AValue));
end;

class operator OleVariantHelper.Negative(const AValue: OleVariant): OleVariant;
begin
  Result := -Variant(AValue);
end;

class operator OleVariantHelper.Positive(const AValue: OleVariant): OleVariant;
begin
  Result := +Variant(AValue);
end;

class operator OleVariantHelper.Add(const ALeft, ARight: OleVariant): OleVariant;
begin
  Result := Variant(ALeft) + Variant(ARight);
end;

class operator OleVariantHelper.Subtract(const ALeft, ARight: OleVariant): OleVariant;
begin
  Result := Variant(ALeft) - Variant(ARight);
end;

class operator OleVariantHelper.Multiply(const ALeft, ARight: OleVariant): OleVariant;
begin
  Result := Variant(ALeft) * Variant(ARight);
end;

class operator OleVariantHelper.Divide(const ALeft, ARight: OleVariant): OleVariant;
begin
  Result := Variant(ALeft) / Variant(ARight);
end;

class operator OleVariantHelper.IntDivide(const ALeft, ARight: OleVariant): OleVariant;
begin
  Result := Variant(ALeft) div Variant(ARight);
end;

class operator OleVariantHelper.Modulus(const ALeft, ARight: OleVariant): OleVariant;
begin
  Result := Variant(ALeft) mod Variant(ARight);
end;

class operator OleVariantHelper.LogicalNot(const AValue: OleVariant): OleVariant;
begin
  Result := not Variant(AValue);
end;

class operator OleVariantHelper.LogicalAnd(const ALeft, ARight: OleVariant): OleVariant;
begin
  Result := Variant(ALeft) and Variant(ARight);
end;

class operator OleVariantHelper.LogicalOr(const ALeft, ARight: OleVariant): OleVariant;
begin
  Result := Variant(ALeft) or Variant(ARight);
end;

class operator OleVariantHelper.LogicalXor(const ALeft, ARight: OleVariant): OleVariant;
begin
  Result := Variant(ALeft) xor Variant(ARight);
end;

class operator OleVariantHelper.LeftShift(const ALeft, ARight: OleVariant): OleVariant;
begin
  Result := Variant(ALeft) shl Variant(ARight);
end;

class operator OleVariantHelper.RightShift(const ALeft, ARight: OleVariant): OleVariant;
begin
  Result := Variant(ALeft) shr Variant(ARight);
end;

class operator OleVariantHelper.Equal(const ALeft, ARight: OleVariant): Boolean;
begin
  Result := Variant(ALeft) = Variant(ARight);
end;

class operator OleVariantHelper.NotEqual(const ALeft, ARight: OleVariant): Boolean;
begin
  Result := Variant(ALeft) <> Variant(ARight);
end;

class operator OleVariantHelper.LessThan(const ALeft, ARight: OleVariant): Boolean;
begin
  Result := Variant(ALeft) < Variant(ARight);
end;

class operator OleVariantHelper.LessThanOrEqual(const ALeft, ARight: OleVariant): Boolean;
begin
  Result := Variant(ALeft) <= Variant(ARight);
end;

class operator OleVariantHelper.GreaterThan(const ALeft, ARight: OleVariant): Boolean;
begin
  Result := Variant(ALeft) > Variant(ARight);
end;

class operator OleVariantHelper.GreaterThanOrEqual(const ALeft, ARight: OleVariant): Boolean;
begin
  Result := Variant(ALeft) >= Variant(ARight);
end;

class operator OleVariantHelper.Implicit(const AValue: DBNull): OleVariant;
begin
  Result := Variant(AValue);
end;

class operator OleVariantHelper.Implicit(const AValue: SmallInt): OleVariant;
begin
  Result := Variant(AValue);
end;

class operator OleVariantHelper.Implicit(const AValue: Integer): OleVariant;
begin
  Result := Variant(AValue);
end;

class operator OleVariantHelper.Implicit(const AValue: Single): OleVariant;
begin
  Result := Variant(AValue);
end;

class operator OleVariantHelper.Implicit(const AValue: Double): OleVariant;
begin
  Result := Variant(AValue);
end;

class operator OleVariantHelper.Implicit(const AValue: DateTime): OleVariant;
begin
  Result := Variant(AValue);
end;

class operator OleVariantHelper.Implicit(const AValue: String): OleVariant;
begin
  Result := Variant(AValue);
end;

class operator OleVariantHelper.Implicit(const AValue: Boolean): OleVariant;
begin
  Result := Variant(AValue);
end;

class operator OleVariantHelper.Implicit(const AValue: Decimal): OleVariant;
begin
  Result := Variant(AValue);
end;

class operator OleVariantHelper.Implicit(const AValue: ShortInt): OleVariant;
begin
  Result := Variant(AValue);
end;

class operator OleVariantHelper.Implicit(const AValue: Byte): OleVariant;
begin
  Result := Variant(AValue);
end;

class operator OleVariantHelper.Implicit(const AValue: Word): OleVariant;
begin
  Result := Variant(AValue);
end;

class operator OleVariantHelper.Implicit(const AValue: LongWord): OleVariant;
begin
  Result := Variant(AValue);
end;

class operator OleVariantHelper.Implicit(const AValue: Int64): OleVariant;
begin
  Result := Variant(AValue);
end;

class operator OleVariantHelper.Implicit(const AValue: UInt64): OleVariant;
begin
  Result := Variant(AValue);
end;

class operator OleVariantHelper.Implicit(const AValue: Char): OleVariant;
begin
  Result := Variant(AValue);
end;

class operator OleVariantHelper.Implicit(const AValue: TDateTime): OleVariant;
begin
  Result := Variant(AValue);
end;

class operator OleVariantHelper.Implicit(const AValue: Currency): OleVariant;
begin
  Result := Variant(AValue);
end;

class operator OleVariantHelper.Implicit(const AValue: Extended): OleVariant;
begin
  Result := Variant(AValue);
end;

class operator OleVariantHelper.Implicit(const AValue: TBytes): OleVariant;
begin
  Result := Variant(AValue);
end;

class operator OleVariantHelper.Implicit(const AValue: OleVariant): DBNull;
begin
  Result := Variant(AValue);
end;

class operator OleVariantHelper.Implicit(const AValue: OleVariant): SmallInt;
begin
  Result := Variant(AValue);
end;

class operator OleVariantHelper.Implicit(const AValue: OleVariant): Integer;
begin
  Result := Variant(AValue);
end;

class operator OleVariantHelper.Implicit(const AValue: OleVariant): Single;
begin
  Result := Variant(AValue);
end;

class operator OleVariantHelper.Implicit(const AValue: OleVariant): Double;
begin
  Result := Variant(AValue);
end;

class operator OleVariantHelper.Implicit(const AValue: OleVariant): DateTime;
begin
  Result := Variant(AValue);
end;

class operator OleVariantHelper.Implicit(const AValue: OleVariant): String;
begin
  Result := Variant(AValue);
end;

class operator OleVariantHelper.Implicit(const AValue: OleVariant): Boolean;
begin
  Result := Variant(AValue);
end;

class operator OleVariantHelper.Implicit(const AValue: OleVariant): Decimal;
begin
  Result := Variant(AValue);
end;

class operator OleVariantHelper.Implicit(const AValue: OleVariant): ShortInt;
begin
  Result := Variant(AValue);
end;

class operator OleVariantHelper.Implicit(const AValue: OleVariant): Byte;
begin
  Result := Variant(AValue);
end;

class operator OleVariantHelper.Implicit(const AValue: OleVariant): Word;
begin
  Result := Variant(AValue);
end;

class operator OleVariantHelper.Implicit(const AValue: OleVariant): LongWord;
begin
  Result := Variant(AValue);
end;

class operator OleVariantHelper.Implicit(const AValue: OleVariant): Int64;
begin
  Result := Variant(AValue);
end;

class operator OleVariantHelper.Implicit(const AValue: OleVariant): UInt64;
begin
  Result := Variant(AValue);
end;

class operator OleVariantHelper.Implicit(const AValue: OleVariant): Char;
begin
  Result := Variant(AValue);
end;

class operator OleVariantHelper.Implicit(const AValue: OleVariant): TDateTime;
begin
  Result := Variant(AValue);
end;

class operator OleVariantHelper.Implicit(const AValue: OleVariant): Currency;
begin
  Result := Variant(AValue);
end;

class operator OleVariantHelper.Implicit(const AValue: OleVariant): Extended;
begin
  Result := Variant(AValue);
end;

class operator OleVariantHelper.Implicit(const AValue: OleVariant): TBytes;
begin
  Result := Variant(AValue);
end;

class operator OleVariantHelper.Implicit(const AValue: OleVariant): Variant;
begin
  Result := Variant(TObject(AValue));
end;

class operator OleVariantHelper.Implicit(const AValue: Variant): OleVariant;
begin
  Result := OleVariant(TObject(AValue));
end;

end.
