Home About Units Download Documents Links Contact SourceForge
Units: Utils: Source

{$INCLUDE ..\cDefines.inc}
unit cUtils;

{                                                                              }
{                    Miscellaneous utility functions v3.36                     }
{                                                                              }
{             This unit is copyright ?2000-2004 by David J Butler             }
{                                                                              }
{                  This unit is part of Delphi Fundamentals.                   }
{                     Its original file name is cUtils.pas                     }
{                      It was generated 1 Aug 2004 23:29.                      }
{       The latest version is available from the Fundamentals home page        }
{                     http://fundementals.sourceforge.net/                     }
{                                                                              }
{                I invite you to use this unit, free of charge.                }
{        I invite you to distibute this unit, but it must be for free.         }
{             I also invite you to contribute to its development,              }
{             but do not distribute a modified copy of this file.              }
{                                                                              }
{          A forum is available on SourceForge for general discussion          }
{             http://sourceforge.net/forum/forum.php?forum_id=2117             }
{                                                                              }
{                                                                              }
{ Revision history:                                                            }
{   2000/02/02  0.01  Initial version                                          }
{   2000/03/08  1.02  Added RealArray / IntegerArray functions.                }
{   2000/04/10  1.03  Added Append, Renamed Delete to Remove and added         }
{                     StringArrays.                                            }
{   2000/05/03  1.04  Added Path functions.                                    }
{   2000/05/08  1.05  Revision.                                                }
{                     188 lines interface. 1171 lines implementation.          }
{   2000/06/01  1.06  Added Range and Dup constructors for dynamic arrays.     }
{   2000/06/03  1.07  Added ArrayInsert functions.                             }
{   2000/06/06  1.08  Moved bit functions from cMaths.                         }
{   2000/06/08  1.09  Removed TInteger, TReal, TRealArray, TIntegerArray.      }
{                     299 lines interface. 2019 lines implementations.         }
{   2000/06/10  1.10  Added linked lists for Integer, Int64, Extended and      }
{                     String.                                                  }
{                     518 lines interface. 3396 lines implementation.          }
{   2000/06/14  1.11  cUtils now generated from a template using a source      }
{                     pre-processor that uses cUtils.                          }
{                     560 lines interface. 1328 lines implementation.          }
{                     Produced source: 644 lines interface, 4716 lines         }
{                     implementation.                                          }
{   2000/07/04  1.12  Revision for Fundamentals release.                       }
{   2000/07/24  1.13  Added TrimArray functions.                               }
{   2000/07/26  1.14  Added Difference functions.                              }
{   2000/09/02  1.15  Added RemoveDuplicates functions.                        }
{                     Added Count functions.                                   }
{                     Fixed bug in Sort.                                       }
{   2000/09/27  1.16  Fixed bug in ArrayInsert.                                }
{   2000/11/29  1.17  Moved SetFPUPrecision to cSysUtils.                      }
{   2001/05/03  1.18  Improved bit functions. Added Pascal versions of         }
{                     assembly routines.                                       }
{                     Templ: 867 lines interface, 2886 lines implementation.   }
{                     Source: 939 lines interface, 9796 lines implementation.  }
{   2001/05/13  1.19  Added CharCount.                                         }
{   2001/05/15  1.20  Added PosNext (ClassType, ObjectArray).                  }
{   2001/05/18  1.21  Added hashing functions from cMaths.                     }
{   2001/07/07  1.22  Added TBinaryTreeNode.                                   }
{   2001/11/11  2.23  Revision.                                                }
{   2002/01/03  2.24  Moved EncodeBase64, DecodeBase64 from cMaths and         }
{                     optimized. Added LongWordToHex, HexToLongWord.           }
{   2002/03/30  2.25  Fixed bug in DecodeBase64.                               }
{   2002/04/02  2.26  Removed dependencies on all other units (incl. Delphi    )
{                     units) to remove initialization code associated with     }
{                     SysUtils. This allows usage of cUtils in projects        }
{                     and still have very small binaries.                      }
{                     Fixed bug in LongWordToHex.                              }
{   2002/05/31  3.27  Refactored for Fundamentals 3.                           }
{                     Moved linked lists to cLinkedLists.                      }
{   2002/08/09  3.28  Added HashInteger.                                       }
{   2002/10/06  3.29  Renamed Cond to iif.                                     }
{   2002/12/12  3.30  Small revisions.                                         }
{   2003/03/14  3.31  Removed ApproxZero. Added FloatZero, FloatsEqual and     }
{                     FloatsCompare. Added documentation and test cases for    }
{                     comparison functions.                                    }
{                     Added support for Currency type.                         }
{   2003/07/27  3.32  Added fast ZeroMem and FillMem routines.                 }
{   2003/09/11  3.33  Added InterfaceArray functions.                          }
{   2004/01/18  3.34  Added WideStringArray functions.                         }
{   2004/07/24  3.35  Optimizations of Sort functions.                         }
{   2005/08/01  3.36  Improved validation in base conversion routines.         }
{                                                                              }
interface

const
  UnitName      = 'cUtils';
  UnitVersion   = '3.36';
  UnitDesc      = 'Miscelleanous utility functions';
  UnitCopyright = 'Copyright (c) 2000-2004 David J Butler';

  FundamentalsMajorVersion = 3;
  FundamentalsMinorVersion = 28;



{$WRITEABLECONST OFF}

{                                                                              }
{ Integer types                                                                }
{   Byte      unsigned 8 bits                                                  }
{   Word      unsigned 16 bits                                                 }
{   LongWord  unsigned 32 bits                                                 }
{   ShortInt  signed 8 bits                                                    }
{   SmallInt  signed 16 bits                                                   }
{   LongInt   signed 32 bits                                                   }
{   Int64     signed 64 bits                                                   }
{   Integer   signed system word                                               }
{   Cardinal  unsigned system word                                             }
{                                                                              }
type
  Int8      = ShortInt;
  Int16     = SmallInt;
  Int32     = LongInt;
  LargeInt  = Int64;
  PLargeInt = ^LargeInt;
  Word8     = Byte;
  Word16    = Word;
  Word32    = LongWord;

  {$IFDEF DELPHI5_DOWN}
  PBoolean  = ^Boolean;
  PByte     = ^Byte;
  PWord     = ^Word;
  PLongWord = ^LongWord;
  PShortInt = ^ShortInt;
  PSmallInt = ^SmallInt;
  PLongInt  = ^LongInt;
  PInteger  = ^Integer;
  PInt64    = ^Int64;
  {$ENDIF}

  LongIntRec = packed record
    case Integer of
      0 : (Lo, Hi : Word);
      1 : (Words  : Array[0..1] of Word);
      2 : (Bytes  : Array[0..3] of Byte);
  end;

const
  MinByte     = Low(Byte);
  MaxByte     = High(Byte);
  MinWord     = Low(Word);
  MaxWord     = High(Word);
  MinShortInt = Low(ShortInt);
  MaxShortInt = High(ShortInt);
  MinSmallInt = Low(SmallInt);
  MaxSmallInt = High(SmallInt);
  MinLongWord = LongWord(Low(LongWord));
  MaxLongWord = LongWord(High(LongWord));
  MinLongInt  = LongInt(Low(LongInt));
  MaxLongInt  = LongInt(High(LongInt));
  MaxInt64    = Int64(High(Int64));
  MinInt64    = Int64(Low(Int64));
  MinInteger  = Integer(Low(Integer));
  MaxInteger  = Integer(High(Integer));
  MinCardinal = Cardinal(Low(Cardinal));
  MaxCardinal = Cardinal(High(Cardinal));

const
  BitsPerByte      = 8;
  BitsPerWord      = 16;
  BitsPerLongWord  = 32;
  BytesPerCardinal = Sizeof(Cardinal);
  BitsPerCardinal  = BytesPerCardinal * 8;

{ Min returns smallest of A and B                                              }
{ Max returns greatest of A and B                                              }
function  MinI(const A, B: Integer): Integer;
function  MaxI(const A, B: Integer): Integer;
function  MinC(const A, B: Cardinal): Cardinal;
function  MaxC(const A, B: Cardinal): Cardinal;

{ Clip returns Value if in Low..High range, otherwise Low or High              }
function  Clip(const Value: Integer; const Low, High: Integer): Integer;
function  ClipByte(const Value: Integer): Integer;
function  ClipWord(const Value: Integer): Integer;
function  ClipLongWord(const Value: Int64): LongWord;
function  SumClipI(const A, I: Integer): Integer;
function  SumClipC(const A: Cardinal; const I: Integer): Cardinal;

{ InXXXRange returns True if A in range of type XXX                            }
function  InByteRange(const A: Int64): Boolean;
function  InWordRange(const A: Int64): Boolean;
function  InLongWordRange(const A: Int64): Boolean;
function  InShortIntRange(const A: Int64): Boolean;
function  InSmallIntRange(const A: Int64): Boolean;
function  InLongIntRange(const A: Int64): Boolean;



{                                                                              }
{ Real types                                                                   }
{                                                                              }
{   Floating point:                                                            }
{     Single    32 bits  7-8 significant digits                                }
{     Double    64 bits  15-16 significant digits                              }
{     Extended  80 bits  19-20 significant digits                              }
{                                                                              }
{   Fixed point:                                                               }
{     Currency  64 bits  19-20 significant digits, 4 after the decimal point.  }
{                                                                              }
type
  Float  = Extended;
  PFloat = ^Float;

const
  MinSingle   : Single   = 1.5E-45;
  MaxSingle   : Single   = 3.4E+38;
  MinDouble   : Double   = 5.0E-324;
  MaxDouble   : Double   = 1.7E+308;
  MinExtended : Extended = 3.4E-4932;
  MaxExtended : Extended = 1.1E+4932;
  {$IFDEF FREEPASCAL}
  MinCurrency = -922337203685477.5807;
  MaxCurrency = 922337203685477.5807;
  {$ELSE}
  MinCurrency : Currency = -922337203685477.5807;
  MaxCurrency : Currency = 922337203685477.5807;
  {$ENDIF}

{$IFDEF DELPHI5_DOWN}
type
  PSingle   = ^Single;
  PDouble   = ^Double;
  PExtended = ^Extended;
  PCurrency = ^Currency;
{$ENDIF}

type
  TExtended = packed record
    Case Boolean of
      True: (
        Mantissa : packed Array[0..1] of LongWord; { MSB of [1] is the normalized 1 bit }
        Exponent : Word; { MSB is the sign bit }
      );
      False: (Value: Extended);
  end;

const
  ExtendedNan      : TExtended = (Mantissa:($FFFFFFFF, $FFFFFFFF); Exponent:$7FFF);
  ExtendedInfinity : TExtended = (Mantissa:($00000000, $80000000); Exponent:$7FFF);

{ Min returns smallest of A and B                                              }
{ Max returns greatest of A and B                                              }
{ Clip returns Value if in Low..High range, otherwise Low or High              }
function  MinF(const A, B: Extended): Extended;
function  MaxF(const A, B: Extended): Extended;
function  ClipF(const Value: Extended; const Low, High: Extended): Extended;

{ InXXXRange returns True if A in range of type XXX                            }
function  InSingleRange(const A: Extended): Boolean;
function  InDoubleRange(const A: Extended): Boolean;
function  InCurrencyRange(const A: Extended): Boolean; overload;
function  InCurrencyRange(const A: Int64): Boolean; overload;



{                                                                              }
{ Bit functions                                                                }
{   All bit functions operate on 32-bit values (LongWord).                     }
{                                                                              }
function  ClearBit(const Value, BitIndex: LongWord): LongWord;
function  SetBit(const Value, BitIndex: LongWord): LongWord;
function  IsBitSet(const Value, BitIndex: LongWord): Boolean;
function  ToggleBit(const Value, BitIndex: LongWord): LongWord;
function  IsHighBitSet(const Value: LongWord): Boolean;

function  SetBitScanForward(const Value: LongWord): Integer; overload;
function  SetBitScanForward(const Value, BitIndex: LongWord): Integer; overload;
function  SetBitScanReverse(const Value: LongWord): Integer; overload;
function  SetBitScanReverse(const Value, BitIndex: LongWord): Integer; overload;
function  ClearBitScanForward(const Value: LongWord): Integer; overload;
function  ClearBitScanForward(const Value, BitIndex: LongWord): Integer; overload;
function  ClearBitScanReverse(const Value: LongWord): Integer; overload;
function  ClearBitScanReverse(const Value, BitIndex: LongWord): Integer; overload;

function  ReverseBits(const Value: LongWord): LongWord; overload;
function  ReverseBits(const Value: LongWord; const BitCount: Integer): LongWord; overload;
function  SwapEndian(const Value: LongWord): LongWord;
Procedure SwapEndianBuf(var Buf; const Count: Integer);
function  TwosComplement(const Value: LongWord): LongWord;

function  RotateLeftBits(const Value: LongWord; const Bits: Byte): LongWord;
function  RotateRightBits(const Value: LongWord; const Bits: Byte): LongWord;

function  BitCount(const Value: LongWord): LongWord;
function  IsPowerOfTwo(const Value: LongWord): Boolean;

function  LowBitMask(const HighBitIndex: LongWord): LongWord;
function  HighBitMask(const LowBitIndex: LongWord): LongWord;
function  RangeBitMask(const LowBitIndex, HighBitIndex: LongWord): LongWord;

function  SetBitRange(const Value: LongWord;
          const LowBitIndex, HighBitIndex: LongWord): LongWord;
function  ClearBitRange(const Value: LongWord;
          const LowBitIndex, HighBitIndex: LongWord): LongWord;
function  ToggleBitRange(const Value: LongWord;
          const LowBitIndex, HighBitIndex: LongWord): LongWord;
function  IsBitRangeSet(const Value: LongWord;
          const LowBitIndex, HighBitIndex: LongWord): Boolean;
function  IsBitRangeClear(const Value: LongWord;
          const LowBitIndex, HighBitIndex: LongWord): Boolean;

const
  BitMaskTable: Array[0..31] of LongWord =
    ($00000001, $00000002, $00000004, $00000008,
     $00000010, $00000020, $00000040, $00000080,
     $00000100, $00000200, $00000400, $00000800,
     $00001000, $00002000, $00004000, $00008000,
     $00010000, $00020000, $00040000, $00080000,
     $00100000, $00200000, $00400000, $00800000,
     $01000000, $02000000, $04000000, $08000000,
     $10000000, $20000000, $40000000, $80000000);



{                                                                              }
{ Sets                                                                         }
{                                                                              }
type
  CharSet = Set of Char;
  ByteSet = Set of Byte;
  PCharSet = ^CharSet;
  PByteSet = ^ByteSet;

const
  CompleteCharSet = [#0..#255];
  CompleteByteSet = [0..255];

function  AsCharSet(const C: Array of Char): CharSet;
function  AsByteSet(const C: Array of Byte): ByteSet;
procedure ComplementChar(var C: CharSet; const Ch: Char);
procedure ClearCharSet(var C: CharSet);
procedure FillCharSet(var C: CharSet);
procedure ComplementCharSet(var C: CharSet);
procedure AssignCharSet(var DestSet: CharSet; const SourceSet: CharSet); overload;
procedure Union(var DestSet: CharSet; const SourceSet: CharSet); overload;
procedure Difference(var DestSet: CharSet; const SourceSet: CharSet); overload;
procedure Intersection(var DestSet: CharSet; const SourceSet: CharSet); overload;
procedure XORCharSet(var DestSet: CharSet; const SourceSet: CharSet);
function  IsSubSet(const A, B: CharSet): Boolean;
function  IsEqual(const A, B: CharSet): Boolean; overload;
function  IsEmpty(const C: CharSet): Boolean;
function  IsComplete(const C: CharSet): Boolean;
function  CharCount(const C: CharSet): Integer; overload;
procedure ConvertCaseInsensitive(var C: CharSet);
function  CaseInsensitiveCharSet(const C: CharSet): CharSet;



{                                                                              }
{ Range functions                                                              }
{                                                                              }
function  IntRangeLength(const Low, High: Integer): Int64;
function  IntRangeAdjacent(const Low1, High1, Low2, High2: Integer): Boolean;
function  IntRangeOverlap(const Low1, High1, Low2, High2: Integer): Boolean;
function  IntRangeHasElement(const Low, High, Element: Integer): Boolean;

function  IntRangeIncludeElement(var Low, High: Integer;
          const Element: Integer): Boolean;
function  IntRangeIncludeElementRange(var Low, High: Integer;
          const LowElement, HighElement: Integer): Boolean;

function  CardinalRangeLength(const Low, High: Cardinal): Int64;
function  CardinalRangeAdjacent(const Low1, High1, Low2, High2: Cardinal): Boolean;
function  CardinalRangeOverlap(const Low1, High1, Low2, High2: Cardinal): Boolean;
function  CardinalRangeHasElement(const Low, High, Element: Cardinal): Boolean;

function  CardinalRangeIncludeElement(var Low, High: Cardinal;
          const Element: Cardinal): Boolean;
function  CardinalRangeIncludeElementRange(var Low, High: Cardinal;
          const LowElement, HighElement: Cardinal): Boolean;



{                                                                              }
{ Swap                                                                         }
{                                                                              }
procedure Swap(var X, Y: Boolean); overload;
procedure Swap(var X, Y: Byte); overload;
procedure Swap(var X, Y: Word); overload;
procedure Swap(var X, Y: LongWord); overload;
procedure Swap(var X, Y: ShortInt); overload;
procedure Swap(var X, Y: SmallInt); overload;
procedure Swap(var X, Y: LongInt); overload;
procedure Swap(var X, Y: Int64); overload;
procedure Swap(var X, Y: Single); overload;
procedure Swap(var X, Y: Double); overload;
procedure Swap(var X, Y: Extended); overload;
procedure Swap(var X, Y: Currency); overload;
procedure Swap(var X, Y: String); overload;
procedure Swap(var X, Y: WideString); overload;
procedure Swap(var X, Y: Pointer); overload;
procedure Swap(var X, Y: TObject); overload;
procedure SwapObjects(var X, Y);



{                                                                              }
{ Inline if                                                                    }
{                                                                              }
{   iif returns TrueValue if Expr is True, otherwise it returns FalseValue.    }
{                                                                              }
function  iif(const Expr: Boolean; const TrueValue: Integer;
          const FalseValue: Integer = 0): Integer; overload;
function  iif(const Expr: Boolean; const TrueValue: Int64;
          const FalseValue: Int64 = 0): Int64; overload;
function  iif(const Expr: Boolean; const TrueValue: Extended;
          const FalseValue: Extended = 0.0): Extended; overload;
function  iif(const Expr: Boolean; const TrueValue: String;
          const FalseValue: String = ''): String; overload;
function  iif(const Expr: Boolean; const TrueValue: TObject;
          const FalseValue: TObject = nil): TObject; overload;



{                                                                              }
{ Comparison                                                                   }
{                                                                              }
type
  TCompareResult = (
      crLess,
      crEqual,
      crGreater,
      crUndefined);
  TCompareResultSet = Set of TCompareResult;

function  ReverseCompareResult(const C: TCompareResult): TCompareResult;



{                                                                              }
{ Direct comparison                                                            }
{                                                                              }
{   Compare(I1, I2) returns crLess if I1 < I2, crEqual if I1 = I2 or           }
{   crGreater if I1 > I2.                                                      }
{                                                                              }
function  Compare(const I1, I2: Boolean): TCompareResult; overload;
function  Compare(const I1, I2: Integer): TCompareResult; overload;
function  Compare(const I1, I2: Int64): TCompareResult; overload;
function  Compare(const I1, I2: Extended): TCompareResult; overload;
function  Compare(const I1, I2: String): TCompareResult; overload;
function  WideCompare(const I1, I2: WideString): TCompareResult;



{                                                                              }
{ Approximate comparison of floating point values                              }
{                                                                              }
{   FloatZero, FloatOne, FloatsEqual and FloatsCompare are functions for       }
{   comparing floating point numbers based on a fixed CompareDelta difference  }
{   between the values. This means that values are considered equal if the     }
{   unsigned difference between the values are less than CompareDelta.         }
{                                                                              }
const
  // Minimum CompareDelta values for the different floating point types:
  // The values were chosen to be slightly higher than the minimum value that
  // the floating-point type can store.
  SingleCompareDelta   = 1.0E-34;
  DoubleCompareDelta   = 1.0E-280;
  ExtendedCompareDelta = 1.0E-4400;

  // Default CompareDelta is set to SingleCompareDelta. This allows any type
  // of floating-point value to be compared with any other.
  DefaultCompareDelta = SingleCompareDelta;

function  FloatZero(const A: Extended;
          const CompareDelta: Extended = DefaultCompareDelta): Boolean;
function  FloatOne(const A: Extended;
          const CompareDelta: Extended = DefaultCompareDelta): Boolean;

function  FloatsEqual(const A, B: Extended;
          const CompareDelta: Extended = DefaultCompareDelta): Boolean;
function  FloatsCompare(const A, B: Extended;
          const CompareDelta: Extended = DefaultCompareDelta): TCompareResult;



{                                                                              }
{ Scaled approximate comparison of floating point values                       }
{                                                                              }
{   ApproxEqual and ApproxCompare are functions for comparing floating point   }
{   numbers based on a scaled order of magnitude difference between the        }
{   values. CompareEpsilon is the ratio applied to the largest of the two      }
{   exponents to give the maximum difference (CompareDelta) for comparison.    }
{                                                                              }
{   For example:                                                               }
{                                                                              }
{   When the CompareEpsilon is 1.0E-9, the result of                           }
{                                                                              }
{   ApproxEqual(1.0E+20, 1.000000001E+20) = False, but the result of           }
{   ApproxEqual(1.0E+20, 1.0000000001E+20) = True, ie the first 9 digits of    }
{   the mantissas of the values must be the same.                              }
{                                                                              }
{   Note that for A <> 0.0, the value of ApproxEqual(A, 0.0) will always be    }
{   False. Rather use the unscaled FloatZero, FloatsEqual and FloatsCompare    }
{   functions when specifically testing for zero.                              }
{                                                                              }
const
  // Smallest (most sensitive) CompareEpsilon values allowed for the different
  // floating point types:
  SingleCompareEpsilon   = 1.0E-5;
  DoubleCompareEpsilon   = 1.0E-13;
  ExtendedCompareEpsilon = 1.0E-17;

  // Default CompareEpsilon is set for half the significant digits of the
  // Extended type.
  DefaultCompareEpsilon  = 1.0E-10;

function  ApproxEqual(const A, B: Extended;
          const CompareEpsilon: Double = DefaultCompareEpsilon): Boolean;
function  ApproxCompare(const A, B: Extended;
          const CompareEpsilon: Double = DefaultCompareEpsilon): TCompareResult;



{                                                                              }
{ Special floating-point values                                                }
{                                                                              }
{   FloatIsInfinity is True if A is a positive or negative infinity.           }
{   FloatIsNaN is True if A is Not-a-Number.                                   }
{                                                                              }
function  FloatIsInfinity(const A: Extended): Boolean;
function  FloatIsNaN(const A: Extended): Boolean;



{                                                                              }
{ Base Conversion                                                              }
{                                                                              }
{   EncodeBase64 converts a binary string (S) to a base 64 string using        }
{   Alphabet. if Pad is True, the result will be padded with PadChar to be a   }
{   multiple of PadMultiple.                                                   }
{                                                                              }
{   DecodeBase64 converts a base 64 string using Alphabet (64 characters for   }
{   values 0-63) to a binary string.                                           }
{                                                                              }
const
  s_HexDigitsUpper: String[16] = '0123456789ABCDEF';
  s_HexDigitsLower: String[16] = '0123456789abcdef';

function  IsHexChar(const Ch: Char): Boolean;
function  IsHexWideChar(const Ch: WideChar): Boolean;
function  HexCharValue(const Ch: Char): Byte;
function  HexWideCharValue(const Ch: WideChar): Byte;

function  LongWordToBin(const I: LongWord; const Digits: Byte = 0): String;
function  LongWordToOct(const I: LongWord; const Digits: Byte = 0): String;
function  LongWordToHex(const I: LongWord; const Digits: Byte = 0;
          const UpperCase: Boolean = True): String;
function  LongWordToStr(const I: LongWord; const Digits: Byte = 0): String;

function  IsValidBinStr(const S: String): Boolean;
function  IsValidOctStr(const S: String): Boolean;
function  IsValidDecStr(const S: String): Boolean;
function  IsValidHexStr(const S: String): Boolean;

{ xxxStrToLongWord converts a number in a specific base to a LongWord value.   }
{ Valid is False on return if the string could not be converted.               }
function  BinStrToLongWord(const S: String; var Valid: Boolean): LongWord;
function  OctStrToLongWord(const S: String; var Valid: Boolean): LongWord;
function  DecStrToLongWord(const S: String; var Valid: Boolean): LongWord;
function  HexStrToLongWord(const S: String; var Valid: Boolean): LongWord;

function  EncodeBase64(const S, Alphabet: String; const Pad: Boolean = False;
          const PadMultiple: Integer = 4; const PadChar: Char = '='): String;
function  DecodeBase64(const S, Alphabet: String; const PadSet: CharSet = []): String;

const
  b64_MIMEBase64 = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';
  b64_UUEncode   = ' !"#$%&''()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_';
  b64_XXEncode   = '+-0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz';

function  MIMEBase64Decode(const S: String): String;
function  MIMEBase64Encode(const S: String): String;
function  UUDecode(const S: String): String;
function  XXDecode(const S: String): String;

function  BytesToHex(const P: Pointer; const Count: Integer;
          const UpperCase: Boolean = True): String;



{                                                                              }
{ Type conversion                                                              }
{                                                                              }
function  PointerToStr(const P: Pointer): String;
function  StrToPointer(const S: String): Pointer;
function  ObjectClassName(const O: TObject): String;
function  ClassClassName(const C: TClass): String;
function  ObjectToStr(const O: TObject): String;
function  ClassToStr(const C: TClass): String;
function  CharSetToStr(const C: CharSet): String;
function  StrToCharSet(const S: String): CharSet;



{                                                                              }
{ Hashing functions                                                            }
{                                                                              }
{   HashBuf uses a every byte in the buffer to calculate a hash.               }
{                                                                              }
{   HashStrBuf/HashStr is a general purpose string hashing function.           }
{   For large strings, HashStr will sample up to 48 bytes from the string.     }
{                                                                              }
{   If Slots = 0 the hash value is in the LongWord range (0-$FFFFFFFF),        }
{   otherwise the value is in the range from 0 to Slots-1. Note that the       }
{   'mod' operation, which is used when Slots <> 0, is comparitively slow.     }
{                                                                              }
function  HashBuf(const Buf; const BufSize: Integer;
          const Slots: LongWord = 0): LongWord;
function  HashStrBuf(const StrBuf: Pointer; const StrLength: Integer;
          const Slots: LongWord = 0): LongWord;
function  HashStrBufNoCase(const StrBuf: Pointer; const StrLength: Integer;
          const Slots: LongWord = 0): LongWord;
function  HashStr(const S: String; const Slots: LongWord = 0;
          const CaseSensitive: Boolean = True): LongWord;
function  HashInteger(const I: Integer; const Slots: LongWord = 0): LongWord;
function  HashLongWord(const I: LongWord; const Slots: LongWord = 0): LongWord;



{                                                                              }
{ Memory operations                                                            }
{                                                                              }
{$IFDEF DELPHI5_DOWN}
type
  PPointer = ^Pointer;
{$ENDIF}

const
  Bytes1KB  = 1024;
  Bytes1MB  = 1024 * Bytes1KB;
  Bytes1GB  = 1024 * Bytes1MB;
  Bytes64KB = 64 * Bytes1KB;
  Bytes64MB = 64 * Bytes1MB;
  Bytes2GB  = 2 * LongWord(Bytes1GB);

procedure FillMem(var Buf; const Count: Integer; const Value: Byte);
procedure ZeroMem(var Buf; const Count: Integer);
procedure MoveMem(const Source; var Dest; const Count: Integer);
function  CompareMem(const Buf1; const Buf2; const Count: Integer): Boolean;
function  CompareMemNoCase(const Buf1; const Buf2; const Count: Integer): TCompareResult;
procedure ReverseMem(var Buf; const Size: Integer);



{                                                                              }
{ IInterface                                                                   }
{                                                                              }
{$IFDEF DELPHI5_DOWN}
type
  IInterface = interface
    ['{00000000-0000-0000-C000-000000000046}']
    function QueryInterface(const IID: TGUID; out Obj): HResult; stdcall;
    function _AddRef: Integer; stdcall;
    function _Release: Integer; stdcall;
  end;
{$ENDIF}



{                                                                              }
{ Array pointers                                                               }
{                                                                              }

{ Maximum array elements                                                       }
const
  MaxArraySize = $7FFFFFFF; // 2 Gigabytes
  MaxByteArrayElements = MaxArraySize div Sizeof(Byte);
  MaxWordArrayElements = MaxArraySize div Sizeof(Word);
  MaxLongWordArrayElements = MaxArraySize div Sizeof(LongWord);
  MaxCardinalArrayElements = MaxArraySize div Sizeof(Cardinal);
  MaxShortIntArrayElements = MaxArraySize div Sizeof(ShortInt);
  MaxSmallIntArrayElements = MaxArraySize div Sizeof(SmallInt);
  MaxLongIntArrayElements = MaxArraySize div Sizeof(LongInt);
  MaxIntegerArrayElements = MaxArraySize div Sizeof(Integer);
  MaxInt64ArrayElements = MaxArraySize div Sizeof(Int64);
  MaxSingleArrayElements = MaxArraySize div Sizeof(Single);
  MaxDoubleArrayElements = MaxArraySize div Sizeof(Double);
  MaxExtendedArrayElements = MaxArraySize div Sizeof(Extended);
  MaxCurrencyArrayElements = MaxArraySize div Sizeof(Currency);
  MaxStringArrayElements = MaxArraySize div Sizeof(String);
  MaxWideStringArrayElements = MaxArraySize div Sizeof(WideString);
  MaxPointerArrayElements = MaxArraySize div Sizeof(Pointer);
  MaxObjectArrayElements = MaxArraySize div Sizeof(TObject);
  MaxInterfaceArrayElements = MaxArraySize div Sizeof(IInterface);
  MaxBooleanArrayElements = MaxArraySize div Sizeof(Boolean);
  MaxCharSetArrayElements = MaxArraySize div Sizeof(CharSet);
  MaxByteSetArrayElements = MaxArraySize div Sizeof(ByteSet);

{ Static array types                                                           }
type
  TStaticByteArray = Array[0..MaxByteArrayElements - 1] of Byte;
  TStaticWordArray = Array[0..MaxWordArrayElements - 1] of Word;
  TStaticLongWordArray = Array[0..MaxLongWordArrayElements - 1] of LongWord;
  TStaticShortIntArray = Array[0..MaxShortIntArrayElements - 1] of ShortInt;
  TStaticSmallIntArray = Array[0..MaxSmallIntArrayElements - 1] of SmallInt;
  TStaticLongIntArray = Array[0..MaxLongIntArrayElements - 1] of LongInt;
  TStaticInt64Array = Array[0..MaxInt64ArrayElements - 1] of Int64;
  TStaticSingleArray = Array[0..MaxSingleArrayElements - 1] of Single;
  TStaticDoubleArray = Array[0..MaxDoubleArrayElements - 1] of Double;
  TStaticExtendedArray = Array[0..MaxExtendedArrayElements - 1] of Extended;
  TStaticCurrencyArray = Array[0..MaxCurrencyArrayElements - 1] of Currency;
  TStaticStringArray = Array[0..MaxStringArrayElements - 1] of String;
  TStaticWideStringArray = Array[0..MaxWideStringArrayElements - 1] of WideString;
  TStaticPointerArray = Array[0..MaxPointerArrayElements - 1] of Pointer;
  TStaticObjectArray = Array[0..MaxObjectArrayElements - 1] of TObject;
  TStaticInterfaceArray = Array[0..MaxInterfaceArrayElements - 1] of IInterface;
  TStaticBooleanArray = Array[0..MaxBooleanArrayElements - 1] of Boolean;
  TStaticCharSetArray = Array[0..MaxCharSetArrayElements - 1] of CharSet;
  TStaticByteSetArray = Array[0..MaxByteSetArrayElements - 1] of ByteSet;
  TStaticCardinalArray = TStaticLongWordArray;
  TStaticIntegerArray = TStaticLongIntArray;

{ Array pointers                                                               }
type
  PByteArray = ^TStaticByteArray;
  PWordArray = ^TStaticWordArray;
  PLongWordArray = ^TStaticLongWordArray;
  PCardinalArray = ^TStaticCardinalArray;
  PShortIntArray = ^TStaticShortIntArray;
  PSmallIntArray = ^TStaticSmallIntArray;
  PLongIntArray = ^TStaticLongIntArray;
  PIntegerArray = ^TStaticIntegerArray;
  PInt64Array = ^TStaticInt64Array;
  PSingleArray = ^TStaticSingleArray;
  PDoubleArray = ^TStaticDoubleArray;
  PExtendedArray = ^TStaticExtendedArray;
  PCurrencyArray = ^TStaticCurrencyArray;
  PStringArray = ^TStaticStringArray;
  PWideStringArray = ^TStaticWideStringArray;
  PPointerArray = ^TStaticPointerArray;
  PObjectArray = ^TStaticObjectArray;
  PInterfaceArray = ^TStaticInterfaceArray;
  PBooleanArray = ^TStaticBooleanArray;
  PCharSetArray = ^TStaticCharSetArray;
  PByteSetArray = ^TStaticByteSetArray;



{                                                                              }
{ Dynamic arrays                                                               }
{                                                                              }
type
  ByteArray = Array of Byte;
  WordArray = Array of Word;
  LongWordArray = Array of LongWord;
  ShortIntArray = Array of ShortInt;
  SmallIntArray = Array of SmallInt;
  LongIntArray = Array of LongInt;
  Int64Array = Array of Int64;
  SingleArray = Array of Single;
  DoubleArray = Array of Double;
  ExtendedArray = Array of Extended;
  CurrencyArray = Array of Currency;
  StringArray = Array of String;
  WideStringArray = Array of WideString;
  PointerArray = Array of Pointer;
  ObjectArray = Array of TObject;
  InterfaceArray = Array of IInterface;
  BooleanArray = Array of Boolean;
  CharSetArray = Array of CharSet;
  ByteSetArray = Array of ByteSet;
  IntegerArray = LongIntArray;
  CardinalArray = LongWordArray;


function  Append(var V: ByteArray; const R: Byte): Integer; overload;
function  Append(var V: WordArray; const R: Word): Integer; overload;
function  Append(var V: LongWordArray; const R: LongWord): Integer; overload;
function  Append(var V: ShortIntArray; const R: ShortInt): Integer; overload;
function  Append(var V: SmallIntArray; const R: SmallInt): Integer; overload;
function  Append(var V: LongIntArray; const R: LongInt): Integer; overload;
function  Append(var V: Int64Array; const R: Int64): Integer; overload;
function  Append(var V: SingleArray; const R: Single): Integer; overload;
function  Append(var V: DoubleArray; const R: Double): Integer; overload;
function  Append(var V: ExtendedArray; const R: Extended): Integer; overload;
function  Append(var V: CurrencyArray; const R: Currency): Integer; overload;
function  Append(var V: StringArray; const R: String): Integer; overload;
function  Append(var V: WideStringArray; const R: WideString): Integer; overload;
function  Append(var V: BooleanArray; const R: Boolean): Integer; overload;
function  Append(var V: PointerArray; const R: Pointer): Integer; overload;
function  Append(var V: ObjectArray; const R: TObject): Integer; overload;
function  Append(var V: InterfaceArray; const R: IInterface): Integer; overload;
function  Append(var V: ByteSetArray; const R: ByteSet): Integer; overload;
function  Append(var V: CharSetArray; const R: CharSet): Integer; overload;
function  AppendByteArray(var V: ByteArray; const R: Array of Byte): Integer; overload;
function  AppendWordArray(var V: WordArray; const R: Array of Word): Integer; overload;
function  AppendCardinalArray(var V: CardinalArray; const R: Array of LongWord): Integer; overload;
function  AppendShortIntArray(var V: ShortIntArray; const R: Array of ShortInt): Integer; overload;
function  AppendSmallIntArray(var V: SmallIntArray; const R: Array of SmallInt): Integer; overload;
function  AppendIntegerArray(var V: IntegerArray; const R: Array of LongInt): Integer; overload;
function  AppendInt64Array(var V: Int64Array; const R: Array of Int64): Integer; overload;
function  AppendSingleArray(var V: SingleArray; const R: Array of Single): Integer; overload;
function  AppendDoubleArray(var V: DoubleArray; const R: Array of Double): Integer; overload;
function  AppendExtendedArray(var V: ExtendedArray; const R: Array of Extended): Integer; overload;
function  AppendCurrencyArray(var V: CurrencyArray; const R: Array of Currency): Integer; overload;
function  AppendStringArray(var V: StringArray; const R: Array of String): Integer; overload;
function  AppendPointerArray(var V: PointerArray; const R: Array of Pointer): Integer; overload;
function  AppendObjectArray(var V: ObjectArray; const R: ObjectArray): Integer; overload;
function  AppendCharSetArray(var V: CharSetArray; const R: Array of CharSet): Integer; overload;
function  AppendByteSetArray(var V: ByteSetArray; const R: Array of ByteSet): Integer; overload;


function  Remove(var V: ByteArray; const Idx: Integer; const Count: Integer = 1): Integer; overload;
function  Remove(var V: WordArray; const Idx: Integer; const Count: Integer = 1): Integer; overload;
function  Remove(var V: LongWordArray; const Idx: Integer; const Count: Integer = 1): Integer; overload;
function  Remove(var V: ShortIntArray; const Idx: Integer; const Count: Integer = 1): Integer; overload;
function  Remove(var V: SmallIntArray; const Idx: Integer; const Count: Integer = 1): Integer; overload;
function  Remove(var V: LongIntArray; const Idx: Integer; const Count: Integer = 1): Integer; overload;
function  Remove(var V: Int64Array; const Idx: Integer; const Count: Integer = 1): Integer; overload;
function  Remove(var V: SingleArray; const Idx: Integer; const Count: Integer = 1): Integer; overload;
function  Remove(var V: DoubleArray; const Idx: Integer; const Count: Integer = 1): Integer; overload;
function  Remove(var V: ExtendedArray; const Idx: Integer; const Count: Integer = 1): Integer; overload;
function  Remove(var V: CurrencyArray; const Idx: Integer; const Count: Integer = 1): Integer; overload;
function  Remove(var V: StringArray; const Idx: Integer; const Count: Integer = 1): Integer; overload;
function  Remove(var V: WideStringArray; const Idx: Integer; const Count: Integer = 1): Integer; overload;
function  Remove(var V: PointerArray; const Idx: Integer; const Count: Integer = 1): Integer; overload;
function  Remove(var V: ObjectArray; const Idx: Integer; const Count: Integer = 1;
          const FreeObjects: Boolean = False): Integer; overload;
function  Remove(var V: InterfaceArray; const Idx: Integer; const Count: Integer = 1): Integer; overload;

procedure RemoveDuplicates(var V: ByteArray; const IsSorted: Boolean); overload;
procedure RemoveDuplicates(var V: WordArray; const IsSorted: Boolean); overload;
procedure RemoveDuplicates(var V: LongWordArray; const IsSorted: Boolean); overload;
procedure RemoveDuplicates(var V: ShortIntArray; const IsSorted: Boolean); overload;
procedure RemoveDuplicates(var V: SmallIntArray; const IsSorted: Boolean); overload;
procedure RemoveDuplicates(var V: LongIntArray; const IsSorted: Boolean); overload;
procedure RemoveDuplicates(var V: Int64Array; const IsSorted: Boolean); overload;
procedure RemoveDuplicates(var V: SingleArray; const IsSorted: Boolean); overload;
procedure RemoveDuplicates(var V: DoubleArray; const IsSorted: Boolean); overload;
procedure RemoveDuplicates(var V: ExtendedArray; const IsSorted: Boolean); overload;
procedure RemoveDuplicates(var V: StringArray; const IsSorted: Boolean); overload;
procedure RemoveDuplicates(var V: PointerArray; const IsSorted: Boolean); overload;

procedure TrimArrayLeft(var S: ByteArray; const TrimList: Array of Byte); overload;
procedure TrimArrayLeft(var S: WordArray; const TrimList: Array of Word); overload;
procedure TrimArrayLeft(var S: LongWordArray; const TrimList: Array of LongWord); overload;
procedure TrimArrayLeft(var S: ShortIntArray; const TrimList: Array of ShortInt); overload;
procedure TrimArrayLeft(var S: SmallIntArray; const TrimList: Array of SmallInt); overload;
procedure TrimArrayLeft(var S: LongIntArray; const TrimList: Array of LongInt); overload;
procedure TrimArrayLeft(var S: Int64Array; const TrimList: Array of Int64); overload;
procedure TrimArrayLeft(var S: SingleArray; const TrimList: Array of Single); overload;
procedure TrimArrayLeft(var S: DoubleArray; const TrimList: Array of Double); overload;
procedure TrimArrayLeft(var S: ExtendedArray; const TrimList: Array of Extended); overload;
procedure TrimArrayLeft(var S: StringArray; const TrimList: Array of String); overload;
procedure TrimArrayLeft(var S: PointerArray; const TrimList: Array of Pointer); overload;

procedure TrimArrayRight(var S: ByteArray; const TrimList: Array of Byte); overload;
procedure TrimArrayRight(var S: WordArray; const TrimList: Array of Word); overload;
procedure TrimArrayRight(var S: LongWordArray; const TrimList: Array of LongWord); overload;
procedure TrimArrayRight(var S: ShortIntArray; const TrimList: Array of ShortInt); overload;
procedure TrimArrayRight(var S: SmallIntArray; const TrimList: Array of SmallInt); overload;
procedure TrimArrayRight(var S: LongIntArray; const TrimList: Array of LongInt); overload;
procedure TrimArrayRight(var S: Int64Array; const TrimList: Array of Int64); overload;
procedure TrimArrayRight(var S: SingleArray; const TrimList: Array of Single); overload;
procedure TrimArrayRight(var S: DoubleArray; const TrimList: Array of Double); overload;
procedure TrimArrayRight(var S: ExtendedArray; const TrimList: Array of Extended); overload;
procedure TrimArrayRight(var S: StringArray; const TrimList: Array of String); overload;
procedure TrimArrayRight(var S: PointerArray; const TrimList: Array of Pointer); overload;

function  ArrayInsert(var V: ByteArray; const Idx: Integer; const Count: Integer): Integer; overload;
function  ArrayInsert(var V: WordArray; const Idx: Integer; const Count: Integer): Integer; overload;
function  ArrayInsert(var V: LongWordArray; const Idx: Integer; const Count: Integer): Integer; overload;
function  ArrayInsert(var V: ShortIntArray; const Idx: Integer; const Count: Integer): Integer; overload;
function  ArrayInsert(var V: SmallIntArray; const Idx: Integer; const Count: Integer): Integer; overload;
function  ArrayInsert(var V: LongIntArray; const Idx: Integer; const Count: Integer): Integer; overload;
function  ArrayInsert(var V: Int64Array; const Idx: Integer; const Count: Integer): Integer; overload;
function  ArrayInsert(var V: SingleArray; const Idx: Integer; const Count: Integer): Integer; overload;
function  ArrayInsert(var V: DoubleArray; const Idx: Integer; const Count: Integer): Integer; overload;
function  ArrayInsert(var V: ExtendedArray; const Idx: Integer; const Count: Integer): Integer; overload;
function  ArrayInsert(var V: CurrencyArray; const Idx: Integer; const Count: Integer): Integer; overload;
function  ArrayInsert(var V: StringArray; const Idx: Integer; const Count: Integer): Integer; overload;
function  ArrayInsert(var V: WideStringArray; const Idx: Integer; const Count: Integer): Integer; overload;
function  ArrayInsert(var V: PointerArray; const Idx: Integer; const Count: Integer): Integer; overload;
function  ArrayInsert(var V: ObjectArray; const Idx: Integer; const Count: Integer): Integer; overload;
function  ArrayInsert(var V: InterfaceArray; const Idx: Integer; const Count: Integer): Integer; overload;

procedure FreeObjectArray(var V); overload;
procedure FreeObjectArray(var V; const LoIdx, HiIdx: Integer); overload;
procedure FreeAndNilObjectArray(var V: ObjectArray);

function  PosNext(const Find: Byte; const V: ByteArray; const PrevPos: Integer = -1;
          const IsSortedAscending: Boolean = False): Integer; overload;
function  PosNext(const Find: Word; const V: WordArray; const PrevPos: Integer = -1;
          const IsSortedAscending: Boolean = False): Integer; overload;
function  PosNext(const Find: LongWord; const V: LongWordArray; const PrevPos: Integer = -1;
          const IsSortedAscending: Boolean = False): Integer; overload;
function  PosNext(const Find: ShortInt; const V: ShortIntArray; const PrevPos: Integer = -1;
          const IsSortedAscending: Boolean = False): Integer; overload;
function  PosNext(const Find: SmallInt; const V: SmallIntArray; const PrevPos: Integer = -1;
          const IsSortedAscending: Boolean = False): Integer; overload;
function  PosNext(const Find: LongInt; const V: LongIntArray; const PrevPos: Integer = -1;
          const IsSortedAscending: Boolean = False): Integer; overload;
function  PosNext(const Find: Int64; const V: Int64Array; const PrevPos: Integer = -1;
          const IsSortedAscending: Boolean = False): Integer; overload;
function  PosNext(const Find: Single; const V: SingleArray; const PrevPos: Integer = -1;
          const IsSortedAscending: Boolean = False): Integer; overload;
function  PosNext(const Find: Double; const V: DoubleArray; const PrevPos: Integer = -1;
          const IsSortedAscending: Boolean = False): Integer; overload;
function  PosNext(const Find: Extended; const V: ExtendedArray; const PrevPos: Integer = -1;
          const IsSortedAscending: Boolean = False): Integer; overload;
function  PosNext(const Find: Boolean; const V: BooleanArray; const PrevPos: Integer = -1;
          const IsSortedAscending: Boolean = False): Integer; overload;
function  PosNext(const Find: String; const V: StringArray; const PrevPos: Integer = -1;
          const IsSortedAscending: Boolean = False): Integer; overload;
function  PosNext(const Find: Pointer; const V: PointerArray;
          const PrevPos: Integer = -1): Integer; overload;
function  PosNext(const Find: TObject; const V: ObjectArray;
          const PrevPos: Integer = -1): Integer; overload;
function  PosNext(const ClassType: TClass; const V: ObjectArray;
          const PrevPos: Integer = -1): Integer; overload;
function  PosNext(const ClassName: String; const V: ObjectArray;
          const PrevPos: Integer = -1): Integer; overload;

function  Count(const Find: Byte; const V: ByteArray;
          const IsSortedAscending: Boolean = False): Integer; overload;
function  Count(const Find: Word; const V: WordArray;
          const IsSortedAscending: Boolean = False): Integer; overload;
function  Count(const Find: LongWord; const V: LongWordArray;
          const IsSortedAscending: Boolean = False): Integer; overload;
function  Count(const Find: ShortInt; const V: ShortIntArray;
          const IsSortedAscending: Boolean = False): Integer; overload;
function  Count(const Find: SmallInt; const V: SmallIntArray;
          const IsSortedAscending: Boolean = False): Integer; overload;
function  Count(const Find: LongInt; const V: LongIntArray;
          const IsSortedAscending: Boolean = False): Integer; overload;
function  Count(const Find: Int64; const V: Int64Array;
          const IsSortedAscending: Boolean = False): Integer; overload;
function  Count(const Find: Single; const V: SingleArray;
          const IsSortedAscending: Boolean = False): Integer; overload;
function  Count(const Find: Double; const V: DoubleArray;
          const IsSortedAscending: Boolean = False): Integer; overload;
function  Count(const Find: Extended; const V: ExtendedArray;
          const IsSortedAscending: Boolean = False): Integer; overload;
function  Count(const Find: String; const V: StringArray;
          const IsSortedAscending: Boolean = False): Integer; overload;
function  Count(const Find: Boolean; const V: BooleanArray;
          const IsSortedAscending: Boolean = False): Integer; overload;

procedure RemoveAll(const Find: Byte; var V: ByteArray;
          const IsSortedAscending: Boolean = False); overload; 
procedure RemoveAll(const Find: Word; var V: WordArray;
          const IsSortedAscending: Boolean = False); overload; 
procedure RemoveAll(const Find: LongWord; var V: LongWordArray;
          const IsSortedAscending: Boolean = False); overload; 
procedure RemoveAll(const Find: ShortInt; var V: ShortIntArray;
          const IsSortedAscending: Boolean = False); overload; 
procedure RemoveAll(const Find: SmallInt; var V: SmallIntArray;
          const IsSortedAscending: Boolean = False); overload; 
procedure RemoveAll(const Find: LongInt; var V: LongIntArray;
          const IsSortedAscending: Boolean = False); overload; 
procedure RemoveAll(const Find: Int64; var V: Int64Array;
          const IsSortedAscending: Boolean = False); overload; 
procedure RemoveAll(const Find: Single; var V: SingleArray;
          const IsSortedAscending: Boolean = False); overload; 
procedure RemoveAll(const Find: Double; var V: DoubleArray;
          const IsSortedAscending: Boolean = False); overload; 
procedure RemoveAll(const Find: Extended; var V: ExtendedArray;
          const IsSortedAscending: Boolean = False); overload; 
procedure RemoveAll(const Find: String; var V: StringArray;
          const IsSortedAscending: Boolean = False); overload; 

function  Intersection(const V1, V2: ByteArray;
          const IsSortedAscending: Boolean = False): ByteArray; overload;
function  Intersection(const V1, V2: WordArray;
          const IsSortedAscending: Boolean = False): WordArray; overload;
function  Intersection(const V1, V2: LongWordArray;
          const IsSortedAscending: Boolean = False): LongWordArray; overload;
function  Intersection(const V1, V2: ShortIntArray;
          const IsSortedAscending: Boolean = False): ShortIntArray; overload;
function  Intersection(const V1, V2: SmallIntArray;
          const IsSortedAscending: Boolean = False): SmallIntArray; overload;
function  Intersection(const V1, V2: LongIntArray;
          const IsSortedAscending: Boolean = False): LongIntArray; overload;
function  Intersection(const V1, V2: Int64Array;
          const IsSortedAscending: Boolean = False): Int64Array; overload;
function  Intersection(const V1, V2: SingleArray;
          const IsSortedAscending: Boolean = False): SingleArray; overload;
function  Intersection(const V1, V2: DoubleArray;
          const IsSortedAscending: Boolean = False): DoubleArray; overload;
function  Intersection(const V1, V2: ExtendedArray;
          const IsSortedAscending: Boolean = False): ExtendedArray; overload;
function  Intersection(const V1, V2: StringArray;
          const IsSortedAscending: Boolean = False): StringArray; overload;

function  Difference(const V1, V2: ByteArray;
          const IsSortedAscending: Boolean = False): ByteArray; overload;
function  Difference(const V1, V2: WordArray;
          const IsSortedAscending: Boolean = False): WordArray; overload;
function  Difference(const V1, V2: LongWordArray;
          const IsSortedAscending: Boolean = False): LongWordArray; overload;
function  Difference(const V1, V2: ShortIntArray;
          const IsSortedAscending: Boolean = False): ShortIntArray; overload;
function  Difference(const V1, V2: SmallIntArray;
          const IsSortedAscending: Boolean = False): SmallIntArray; overload;
function  Difference(const V1, V2: LongIntArray;
          const IsSortedAscending: Boolean = False): LongIntArray; overload;
function  Difference(const V1, V2: Int64Array;
          const IsSortedAscending: Boolean = False): Int64Array; overload;
function  Difference(const V1, V2: SingleArray;
          const IsSortedAscending: Boolean = False): SingleArray; overload;
function  Difference(const V1, V2: DoubleArray;
          const IsSortedAscending: Boolean = False): DoubleArray; overload;
function  Difference(const V1, V2: ExtendedArray;
          const IsSortedAscending: Boolean = False): ExtendedArray; overload;
function  Difference(const V1, V2: StringArray;
          const IsSortedAscending: Boolean = False): StringArray; overload;

procedure Reverse(var V: ByteArray); overload;
procedure Reverse(var V: WordArray); overload;
procedure Reverse(var V: LongWordArray); overload;
procedure Reverse(var V: ShortIntArray); overload;
procedure Reverse(var V: SmallIntArray); overload;
procedure Reverse(var V: LongIntArray); overload;
procedure Reverse(var V: Int64Array); overload;
procedure Reverse(var V: SingleArray); overload;
procedure Reverse(var V: DoubleArray); overload;
procedure Reverse(var V: ExtendedArray); overload;
procedure Reverse(var V: StringArray); overload;
procedure Reverse(var V: PointerArray); overload;
procedure Reverse(var V: ObjectArray); overload;

function  AsBooleanArray(const V: Array of Boolean): BooleanArray; overload;
function  AsByteArray(const V: Array of Byte): ByteArray; overload;
function  AsWordArray(const V: Array of Word): WordArray; overload;
function  AsLongWordArray(const V: Array of LongWord): LongWordArray; overload;
function  AsCardinalArray(const V: Array of Cardinal): CardinalArray; overload;
function  AsShortIntArray(const V: Array of ShortInt): ShortIntArray; overload;
function  AsSmallIntArray(const V: Array of SmallInt): SmallIntArray; overload;
function  AsLongIntArray(const V: Array of LongInt): LongIntArray; overload;
function  AsIntegerArray(const V: Array of Integer): IntegerArray; overload;
function  AsInt64Array(const V: Array of Int64): Int64Array; overload;
function  AsSingleArray(const V: Array of Single): SingleArray; overload;
function  AsDoubleArray(const V: Array of Double): DoubleArray; overload;
function  AsExtendedArray(const V: Array of Extended): ExtendedArray; overload;
function  AsCurrencyArray(const V: Array of Currency): CurrencyArray; overload;
function  AsStringArray(const V: Array of String): StringArray; overload;
function  AsWideStringArray(const V: Array of WideString): WideStringArray; overload;
function  AsPointerArray(const V: Array of Pointer): PointerArray; overload;
function  AsCharSetArray(const V: Array of CharSet): CharSetArray; overload;
function  AsObjectArray(const V: Array of TObject): ObjectArray; overload;
function  AsInterfaceArray(const V: Array of IInterface): InterfaceArray; overload;

function  RangeByte(const First: Byte; const Count: Integer;
          const Increment: Byte = 1): ByteArray;
function  RangeWord(const First: Word; const Count: Integer;
          const Increment: Word = 1): WordArray;
function  RangeLongWord(const First: LongWord; const Count: Integer;
          const Increment: LongWord = 1): LongWordArray;
function  RangeCardinal(const First: Cardinal; const Count: Integer;
          const Increment: Cardinal = 1): CardinalArray;
function  RangeShortInt(const First: ShortInt; const Count: Integer;
          const Increment: ShortInt = 1): ShortIntArray;
function  RangeSmallInt(const First: SmallInt; const Count: Integer;
          const Increment: SmallInt = 1): SmallIntArray;
function  RangeLongInt(const First: LongInt; const Count: Integer;
          const Increment: LongInt = 1): LongIntArray;
function  RangeInteger(const First: Integer; const Count: Integer;
          const Increment: Integer = 1): IntegerArray;
function  RangeInt64(const First: Int64; const Count: Integer;
          const Increment: Int64 = 1): Int64Array;
function  RangeSingle(const First: Single; const Count: Integer;
          const Increment: Single = 1): SingleArray;
function  RangeDouble(const First: Double; const Count: Integer;
          const Increment: Double = 1): DoubleArray;
function  RangeExtended(const First: Extended; const Count: Integer;
          const Increment: Extended = 1): ExtendedArray;

function  DupByte(const V: Byte; const Count: Integer): ByteArray;
function  DupWord(const V: Word; const Count: Integer): WordArray;
function  DupLongWord(const V: LongWord; const Count: Integer): LongWordArray;
function  DupCardinal(const V: Cardinal; const Count: Integer): CardinalArray;
function  DupShortInt(const V: ShortInt; const Count: Integer): ShortIntArray;
function  DupSmallInt(const V: SmallInt; const Count: Integer): SmallIntArray;
function  DupLongInt(const V: LongInt; const Count: Integer): LongIntArray;
function  DupInteger(const V: Integer; const Count: Integer): IntegerArray;
function  DupInt64(const V: Int64; const Count: Integer): Int64Array;
function  DupSingle(const V: Single; const Count: Integer): SingleArray;
function  DupDouble(const V: Double; const Count: Integer): DoubleArray;
function  DupExtended(const V: Extended; const Count: Integer): ExtendedArray;
function  DupCurrency(const V: Currency; const Count: Integer): CurrencyArray;
function  DupString(const V: String; const Count: Integer): StringArray;
function  DupCharSet(const V: CharSet; const Count: Integer): CharSetArray;
function  DupObject(const V: TObject; const Count: Integer): ObjectArray;

procedure SetLengthAndZero(var V: ByteArray; const NewLength: Integer); overload;
procedure SetLengthAndZero(var V: WordArray; const NewLength: Integer); overload;
procedure SetLengthAndZero(var V: LongWordArray; const NewLength: Integer); overload;
procedure SetLengthAndZero(var V: ShortIntArray; const NewLength: Integer); overload;
procedure SetLengthAndZero(var V: SmallIntArray; const NewLength: Integer); overload;
procedure SetLengthAndZero(var V: LongIntArray; const NewLength: Integer); overload;
procedure SetLengthAndZero(var V: Int64Array; const NewLength: Integer); overload;
procedure SetLengthAndZero(var V: SingleArray; const NewLength: Integer); overload;
procedure SetLengthAndZero(var V: DoubleArray; const NewLength: Integer); overload;
procedure SetLengthAndZero(var V: ExtendedArray; const NewLength: Integer); overload;
procedure SetLengthAndZero(var V: CurrencyArray; const NewLength: Integer); overload;
procedure SetLengthAndZero(var V: CharSetArray; const NewLength: Integer); overload;
procedure SetLengthAndZero(var V: BooleanArray; const NewLength: Integer); overload;
procedure SetLengthAndZero(var V: PointerArray; const NewLength: Integer); overload;
procedure SetLengthAndZero(var V: ObjectArray; const NewLength: Integer;
          const FreeObjects: Boolean = False); overload;

function  IsEqual(const V1, V2: ByteArray): Boolean; overload;
function  IsEqual(const V1, V2: WordArray): Boolean; overload;
function  IsEqual(const V1, V2: LongWordArray): Boolean; overload;
function  IsEqual(const V1, V2: ShortIntArray): Boolean; overload;
function  IsEqual(const V1, V2: SmallIntArray): Boolean; overload;
function  IsEqual(const V1, V2: LongIntArray): Boolean; overload;
function  IsEqual(const V1, V2: Int64Array): Boolean; overload;
function  IsEqual(const V1, V2: SingleArray): Boolean; overload;
function  IsEqual(const V1, V2: DoubleArray): Boolean; overload;
function  IsEqual(const V1, V2: ExtendedArray): Boolean; overload;
function  IsEqual(const V1, V2: CurrencyArray): Boolean; overload;
function  IsEqual(const V1, V2: StringArray): Boolean; overload;
function  IsEqual(const V1, V2: CharSetArray): Boolean; overload;

function  ByteArrayToLongIntArray(const V: ByteArray): LongIntArray;
function  WordArrayToLongIntArray(const V: WordArray): LongIntArray;
function  ShortIntArrayToLongIntArray(const V: ShortIntArray): LongIntArray;
function  SmallIntArrayToLongIntArray(const V: SmallIntArray): LongIntArray;
function  LongIntArrayToInt64Array(const V: LongIntArray): Int64Array;
function  LongIntArrayToSingleArray(const V: LongIntArray): SingleArray;
function  LongIntArrayToDoubleArray(const V: LongIntArray): DoubleArray;
function  LongIntArrayToExtendedArray(const V: LongIntArray): ExtendedArray;
function  SingleArrayToDoubleArray(const V: SingleArray): DoubleArray;
function  SingleArrayToExtendedArray(const V: SingleArray): ExtendedArray;
function  SingleArrayToCurrencyArray(const V: SingleArray): CurrencyArray;
function  SingleArrayToLongIntArray(const V: SingleArray): LongIntArray;
function  SingleArrayToInt64Array(const V: SingleArray): Int64Array;
function  DoubleArrayToExtendedArray(const V: DoubleArray): ExtendedArray;
function  DoubleArrayToCurrencyArray(const V: DoubleArray): CurrencyArray;
function  DoubleArrayToLongIntArray(const V: DoubleArray): LongIntArray;
function  DoubleArrayToInt64Array(const V: DoubleArray): Int64Array;
function  ExtendedArrayToCurrencyArray(const V: ExtendedArray): CurrencyArray;
function  ExtendedArrayToLongIntArray(const V: ExtendedArray): LongIntArray;
function  ExtendedArrayToInt64Array(const V: ExtendedArray): Int64Array;

function  ByteArrayFromIndexes(const V: ByteArray;
          const Indexes: IntegerArray): ByteArray;
function  WordArrayFromIndexes(const V: WordArray;
          const Indexes: IntegerArray): WordArray;
function  LongWordArrayFromIndexes(const V: LongWordArray;
          const Indexes: IntegerArray): LongWordArray;
function  CardinalArrayFromIndexes(const V: CardinalArray;
          const Indexes: IntegerArray): CardinalArray;
function  ShortIntArrayFromIndexes(const V: ShortIntArray;
          const Indexes: IntegerArray): ShortIntArray;
function  SmallIntArrayFromIndexes(const V: SmallIntArray;
          const Indexes: IntegerArray): SmallIntArray;
function  LongIntArrayFromIndexes(const V: LongIntArray;
          const Indexes: IntegerArray): LongIntArray;
function  IntegerArrayFromIndexes(const V: IntegerArray;
          const Indexes: IntegerArray): IntegerArray;
function  Int64ArrayFromIndexes(const V: Int64Array;
          const Indexes: IntegerArray): Int64Array;
function  SingleArrayFromIndexes(const V: SingleArray;
          const Indexes: IntegerArray): SingleArray;
function  DoubleArrayFromIndexes(const V: DoubleArray;
          const Indexes: IntegerArray): DoubleArray;
function  ExtendedArrayFromIndexes(const V: ExtendedArray;
          const Indexes: IntegerArray): ExtendedArray;
function  StringArrayFromIndexes(const V: StringArray;
          const Indexes: IntegerArray): StringArray;

procedure Sort(const V: ByteArray); overload;
procedure Sort(const V: WordArray); overload;
procedure Sort(const V: LongWordArray); overload;
procedure Sort(const V: ShortIntArray); overload;
procedure Sort(const V: SmallIntArray); overload;
procedure Sort(const V: LongIntArray); overload;
procedure Sort(const V: Int64Array); overload;
procedure Sort(const V: SingleArray); overload;
procedure Sort(const V: DoubleArray); overload;
procedure Sort(const V: ExtendedArray); overload;
procedure Sort(const V: StringArray); overload;

procedure Sort(const Key: IntegerArray; const Data: IntegerArray); overload;
procedure Sort(const Key: IntegerArray; const Data: Int64Array); overload;
procedure Sort(const Key: IntegerArray; const Data: StringArray); overload;
procedure Sort(const Key: IntegerArray; const Data: ExtendedArray); overload;
procedure Sort(const Key: IntegerArray; const Data: PointerArray); overload;
procedure Sort(const Key: StringArray; const Data: IntegerArray); overload;
procedure Sort(const Key: StringArray; const Data: Int64Array); overload;
procedure Sort(const Key: StringArray; const Data: StringArray); overload;
procedure Sort(const Key: StringArray; const Data: ExtendedArray); overload;
procedure Sort(const Key: StringArray; const Data: PointerArray); overload;
procedure Sort(const Key: ExtendedArray; const Data: IntegerArray); overload;
procedure Sort(const Key: ExtendedArray; const Data: Int64Array); overload;
procedure Sort(const Key: ExtendedArray; const Data: StringArray); overload;
procedure Sort(const Key: ExtendedArray; const Data: ExtendedArray); overload;
procedure Sort(const Key: ExtendedArray; const Data: PointerArray); overload;



{                                                                              }
{ Self testing code                                                            }
{                                                                              }
procedure SelfTest;



implementation



{$IFDEF DELPHI}{$IFDEF OS_WIN32}{$IFDEF CPU_INTEL386}
  {$DEFINE USE_ASM386}
{$ENDIF}{$ENDIF}{$ENDIF}

{                                                                              }
{ Integer                                                                      }
{                                                                              }
function MinI(const A, B: Integer): Integer;
begin
  if A < B then
    Result := A else
    Result := B;
end;

function MaxI(const A, B: Integer): Integer;
begin
  if A > B then
    Result := A else
    Result := B;
end;

function MinC(const A, B: Cardinal): Cardinal;
begin
  if A < B then
    Result := A else
    Result := B;
end;

function MaxC(const A, B: Cardinal): Cardinal;
begin
  if A > B then
    Result := A else
    Result := B;
end;

function Clip(const Value: Integer; const Low, High: Integer): Integer;
begin
  if Value < Low then
    Result := Low else
  if Value > High then
    Result := High else
    Result := Value;
end;

function ClipByte(const Value: Integer): Integer;
begin
  if Value < MinByte then
    Result := MinByte else
  if Value > MaxByte then
    Result := MaxByte else
    Result := Value;
end;

function ClipWord(const Value: Integer): Integer;
begin
  if Value < MinWord then
    Result := MinWord else
  if Value > MaxWord then
    Result := MaxWord else
    Result := Value;
end;

function ClipLongWord(const Value: Int64): LongWord;
begin
  if Value < MinLongWord then
    Result := MinLongWord else
  if Value > MaxLongWord then
    Result := MaxLongWord else
    Result := LongWord(Value);
end;

function SumClipI(const A, I: Integer): Integer;
begin
  if I >= 0 then
    if A >= MaxInteger - I then
      Result := MaxInteger else
      Result := A + I
  else
    if A <= MinInteger - I then
      Result := MinInteger else
      Result := A + I;
end;

function SumClipC(const A: Cardinal; const I: Integer): Cardinal;
var B : Cardinal;
begin
  if I >= 0 then
    if A >= MaxCardinal - Cardinal(I) then
      Result := MaxCardinal else
      Result := A + Cardinal(I)
  else
    begin
      B := Cardinal(-I);
      if A <= B then
        Result := 0 else
        Result := A - B;
    end;
end;

function InByteRange(const A: Int64): Boolean;
begin
  Result := (A >= MinByte) and (A <= MaxByte);
end;

function InWordRange(const A: Int64): Boolean;
begin
  Result := (A >= MinWord) and (A <= MaxWord);
end;

function InLongWordRange(const A: Int64): Boolean;
begin
  Result := (A >= MinLongWord) and (A <= MaxLongWord);
end;

function InShortIntRange(const A: Int64): Boolean;
begin
  Result := (A >= MinShortInt) and (A <= MaxShortInt);
end;

function InSmallIntRange(const A: Int64): Boolean;
begin
  Result := (A >= MinSmallInt) and (A <= MaxSmallInt);
end;

function InLongIntRange(const A: Int64): Boolean;
begin
  Result := (A >= MinLongInt) and (A <= MaxLongInt);
end;



{                                                                              }
{ Real                                                                         }
{                                                                              }
function MinF(const A, B: Extended): Extended;
begin
  if A < B then
    Result := A else
    Result := B;
end;

function MaxF(const A, B: Extended): Extended;
begin
  if A > B then
    Result := A else
    Result := B;
end;

function ClipF(const Value: Extended; const Low, High: Extended): Extended;
begin
  if Value < Low then
    Result := Low else
  if Value > High then
    Result := High else
    Result := Value;
end;

function InSingleRange(const A: Extended): Boolean;
var B : Extended;
begin
  B := Abs(A);
  Result := (B >= MinSingle) and (B <= MaxSingle);
end;

function InDoubleRange(const A: Extended): Boolean;
var B : Extended;
begin
  B := Abs(A);
  Result := (B >= MinDouble) and (B <= MaxDouble);
end;

function InCurrencyRange(const A: Extended): Boolean;
begin
  Result := (A >= MinCurrency) and (A <= MaxCurrency);
end;

function InCurrencyRange(const A: Int64): Boolean;
begin
  Result := (A >= MinCurrency) and (A <= MaxCurrency);
end;



{                                                                              }
{ Bit functions                                                                }
{                                                                              }

{ Assembly versions of ReverseBits and SwapEndian taken from the               }
{ Delphi Encryption Compendium by Hagen Reddmann (HaReddmann@aol.com)          }
{$IFDEF USE_ASM386}
function ReverseBits(const Value: LongWord): LongWord;
asm
      BSWAP   EAX
      MOV     EDX, EAX
      AND     EAX, 0AAAAAAAAh
      SHR     EAX, 1
      AND     EDX, 055555555h
      SHL     EDX, 1
      OR      EAX, EDX
      MOV     EDX, EAX
      AND     EAX, 0CCCCCCCCh
      SHR     EAX, 2
      AND     EDX, 033333333h
      SHL     EDX, 2
      OR      EAX, EDX
      MOV     EDX, EAX
      AND     EAX, 0F0F0F0F0h
      SHR     EAX, 4
      AND     EDX, 00F0F0F0Fh
      SHL     EDX, 4
      OR      EAX, EDX
end;
{$ELSE}
function ReverseBits(const Value: LongWord): LongWord;
var I : Byte;
begin
  Result := 0;
  For I := 0 to 31 do
    if Value and BitMaskTable[I] <> 0 then
      Result := Result or BitMaskTable[31 - I];
end;
{$ENDIF}

function ReverseBits(const Value: LongWord; const BitCount: Integer): LongWord;
var I : Integer;
  V : LongWord;
begin
  V := Value;
  Result := 0;
  For I := 0 to MinI(BitCount, BitsPerLongWord) - 1 do
    begin
      Result := (Result shl 1) or (V and 1);
      V := V shr 1;
    end;
end;

{$IFDEF USE_ASM386}
function SwapEndian(const Value: LongWord): LongWord;
asm
      XCHG    AH, AL
      ROL     EAX, 16
      XCHG    AH, AL
end;
{$ELSE}
function SwapEndian(const Value: LongWord): LongWord;
type Bytes4 = packed record
       B1, B2, B3, B4 : Byte;
     end;
var Val : Bytes4 absolute Value;
    Res : Bytes4 absolute Result;
begin
  Res.B4 := Val.B1;
  Res.B3 := Val.B2;
  Res.B2 := Val.B3;
  Res.B1 := Val.B4;
end;
{$ENDIF}

procedure SwapEndianBuf(var Buf; const Count: Integer);
var P : PLongWord;
    I : Integer;
begin
  P := @Buf;
  For I := 1 to Count do
    begin
      P^ := SwapEndian(P^);
      Inc(P);
    end;
end;

{$IFDEF USE_ASM386}
function TwosComplement(const Value: LongWord): LongWord;
asm
      NEG EAX
end;
{$ELSE}
function TwosComplement(const Value: LongWord): LongWord;
begin
  Result := not Value + 1;
end;
{$ENDIF}

{$IFDEF USE_ASM386}
function RotateLeftBits(const Value: LongWord; const Bits: Byte): LongWord;
asm
      MOV   CL, DL
      ROL   EAX, CL
end;
{$ELSE}
function RotateLeftBits(const Value: LongWord; const Bits: Byte): LongWord;
var I : Integer;
begin
  Result := Value;
  For I := 1 to Bits do
    if Value and $80000000 = 0 then
      Result := Value shl 1 else
      Result := (Value shl 1) or 1;
end;
{$ENDIF}

{$IFDEF USE_ASM386}
function RotateRightBits(const Value: LongWord; const Bits: Byte): LongWord;
asm
      MOV   CL, DL
      ROL   EAX, CL
end;
{$ELSE}
function RotateRightBits(const Value: LongWord; const Bits: Byte): LongWord;
var I : Integer;
begin
  Result := Value;
  For I := 1 to Bits do
    if Value and 1 = 0 then
      Result := Value shr 1 else
      Result := (Value shr 1) or $80000000;
end;
{$ENDIF}

{$IFDEF USE_ASM386}
function SetBit(const Value, BitIndex: LongWord): LongWord;
asm
      {$IFOPT R+}
      CMP     BitIndex, BitsPerLongWord
      JAE     @Fin
      {$ENDIF}
      OR      EAX, DWORD PTR [BitIndex * 4 + BitMaskTable]
    @Fin:
end;
{$ELSE}
function SetBit(const Value, BitIndex: LongWord): LongWord;
begin
  Result := Value or BitMaskTable[BitIndex];
end;
{$ENDIF}

{$IFDEF USE_ASM386}
function ClearBit(const Value, BitIndex: LongWord): LongWord;
asm
      {$IFOPT R+}
      CMP     BitIndex, BitsPerLongWord
      JAE     @Fin
      {$ENDIF}
      MOV     ECX, DWORD PTR [BitIndex * 4 + BitMaskTable]
      NOT     ECX
      AND     EAX, ECX
    @Fin:
end;
{$ELSE}
function ClearBit(const Value, BitIndex: LongWord): LongWord;
begin
  Result := Value and not BitMaskTable[BitIndex];
end;
{$ENDIF}

{$IFDEF USE_ASM386}
function ToggleBit(const Value, BitIndex: LongWord): LongWord;
asm
      {$IFOPT R+}
      CMP     BitIndex, BitsPerLongWord
      JAE     @Fin
      {$ENDIF}
      XOR     EAX, DWORD PTR [BitIndex * 4 + BitMaskTable]
    @Fin:
end;
{$ELSE}
function ToggleBit(const Value, BitIndex: LongWord): LongWord;
begin
  Result := Value xor BitMaskTable[BitIndex];
end;
{$ENDIF}

{$IFDEF USE_ASM386}
function IsHighBitSet(const Value: LongWord): Boolean;
asm
      TEST    Value, $80000000
      SETNZ   AL
end;
{$ELSE}
function IsHighBitSet(const Value: LongWord): Boolean;
begin
  Result := Value and $80000000 <> 0;
end;
{$ENDIF}

{$IFDEF USE_ASM386}
function IsBitSet(const Value, BitIndex: LongWord): Boolean;
asm
      {$IFOPT R+}
      CMP     BitIndex, BitsPerLongWord
      JAE     @Fin
      {$ENDIF}
      MOV     ECX, DWORD PTR BitMaskTable [BitIndex * 4]
      TEST    Value, ECX
      SETNZ   AL
    @Fin:
end;
{$ELSE}
function IsBitSet(const Value, BitIndex: LongWord): Boolean;
begin
  Result := Value and BitMaskTable[BitIndex] <> 0;
end;
{$ENDIF}

{$IFDEF USE_ASM386}
function SetBitScanForward(const Value: LongWord): Integer;
asm
      OR      EAX, EAX
      JZ      @NoBits
      BSF     EAX, EAX
      RET
  @NoBits:
      MOV     EAX, -1
end;

function SetBitScanForward(const Value, BitIndex: LongWord): Integer;
asm
      {$IFOPT R+}
      CMP     BitIndex, BitsPerLongWord
      JAE     @@zq
      {$ENDIF}
      MOV     ECX, BitIndex
      MOV     EDX, $FFFFFFFF
      SHL     EDX, CL
      AND     EDX, EAX
      JE      @@zq
      BSF     EAX, EDX
      RET
@@zq: MOV     EAX, -1
end;
{$ELSE}
function SetBitScanForward(const Value, BitIndex: LongWord): Integer;
var I : Byte;
begin
  {$IFOPT R+}
  if BitIndex < BitsPerLongWord then
  {$ENDIF}
  For I := Byte(BitIndex) to 31 do
    if Value and BitMaskTable[I] <> 0 then
      begin
        Result := I;
        exit;
      end;
  Result := -1;
end;

function SetBitScanForward(const Value: LongWord): Integer;
begin
  Result := SetBitScanForward(Value, 0);
end;
{$ENDIF}

{$IFDEF USE_ASM386}
function SetBitScanReverse(const Value: LongWord): Integer;
asm
      OR      EAX, EAX
      JZ      @NoBits
      BSR     EAX, EAX
      RET
  @NoBits:
      MOV     EAX, -1
end;

function SetBitScanReverse(const Value, BitIndex: LongWord): Integer;
asm
      {$IFOPT R+}
      CMP     EDX, BitsPerLongWord
      JAE     @@zq
      {$ENDIF}
      LEA     ECX, [EDX-31]
      MOV     EDX, $FFFFFFFF
      NEG     ECX
      SHR     EDX, CL
      AND     EDX, EAX
      JE      @@zq
      BSR     EAX, EDX
      RET
@@zq: MOV     EAX, -1
end;
{$ELSE}
function SetBitScanReverse(const Value, BitIndex: LongWord): Integer;
var I : Byte;
begin
  {$IFOPT R+}
  if BitIndex < BitsPerLongWord then
  {$ENDIF}
  For I := Byte(BitIndex) downto 0 do
    if Value and BitMaskTable[I] <> 0 then
      begin
        Result := I;
        exit;
      end;
  Result := -1;
end;

function SetBitScanReverse(const Value: LongWord): Integer;
begin
  Result := SetBitScanReverse(Value, 31);
end;
{$ENDIF}

{$IFDEF USE_ASM386}
function ClearBitScanForward(const Value: LongWord): Integer;
asm
      NOT     EAX
      OR      EAX, EAX
      JZ      @NoBits
      BSF     EAX, EAX
      RET
  @NoBits:
      MOV     EAX, -1
end;

function ClearBitScanForward(const Value, BitIndex: LongWord): Integer;
asm
      {$IFOPT R+}
      CMP     EDX, BitsPerLongWord
      JAE     @@zq
      {$ENDIF}
      MOV     ECX, EDX
      MOV     EDX, $FFFFFFFF
      NOT     EAX
      SHL     EDX, CL
      AND     EDX, EAX
      JE      @@zq
      BSF     EAX, EDX
      RET
@@zq: MOV     EAX, -1
end;
{$ELSE}
function ClearBitScanForward(const Value, BitIndex: LongWord): Integer;
var I : Byte;
begin
  {$IFOPT R+}
  if BitIndex < BitsPerLongWord then
  {$ENDIF}
  For I := Byte(BitIndex) to 31 do
    if Value and BitMaskTable[I] = 0 then
      begin
        Result := I;
        exit;
      end;
  Result := -1;
end;

function ClearBitScanForward(const Value: LongWord): Integer;
begin
  Result := ClearBitScanForward(Value, 0);
end;
{$ENDIF}

{$IFDEF USE_ASM386}
function ClearBitScanReverse(const Value: LongWord): Integer;
asm
      NOT     EAX
      OR      EAX, EAX
      JZ      @NoBits
      BSR     EAX, EAX
      RET
  @NoBits:
      MOV     EAX, -1
end;

function ClearBitScanReverse(const Value, BitIndex: LongWord): Integer;
asm
      {$IFOPT R+}
      CMP     EDX, BitsPerLongWord
      JAE     @@zq
      {$ENDIF}
      LEA     ECX, [EDX-31]
      MOV     EDX, $FFFFFFFF
      NEG     ECX
      NOT     EAX
      SHR     EDX, CL
      AND     EDX, EAX
      JE      @@zq
      BSR     EAX, EDX
      RET
@@zq: MOV     EAX, -1
end;
{$ELSE}
function ClearBitScanReverse(const Value, BitIndex: LongWord): Integer;
var I : Byte;
begin
  {$IFOPT R+}
  if BitIndex < BitsPerLongWord then
  {$ENDIF}
  For I := Byte(BitIndex) downto 0 do
    if Value and BitMaskTable[I] = 0 then
      begin
        Result := I;
        exit;
      end;
  Result := -1;
end;

function ClearBitScanReverse(const Value: LongWord): Integer;
begin
  Result := ClearBitScanReverse(Value, 31);
end;
{$ENDIF}

const
  BitCountTable : Array[0..255] of Byte =
    (0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4,
     1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5,
     1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5,
     2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
     1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5,
     2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
     2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
     3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,
     1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5,
     2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
     2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
     3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,
     2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
     3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,
     3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,
     4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8);

{$IFDEF USE_ASM386}
function BitCount(const Value: LongWord): LongWord;
asm
      MOVZX   EDX, AL
      MOVZX   EDX, BYTE PTR [EDX + BitCountTable]
      MOVZX   ECX, AH
      ADD     DL, BYTE PTR [ECX + BitCountTable]
      SHR     EAX, 16
      MOVZX   ECX, AH
      ADD     DL, BYTE PTR [ECX + BitCountTable]
      AND     EAX, $FF
      ADD     DL, BYTE PTR [EAX + BitCountTable]
      MOV     AL, DL
end;
{$ELSE}
function BitCount(const Value: LongWord): LongWord;
var V : Array[0..3] of Byte absolute Value;
begin
  Result := BitCountTable[V[0]] + BitCountTable[V[1]] +
            BitCountTable[V[2]] + BitCountTable[V[3]];
end;
{$ENDIF}

function IsPowerOfTwo(const Value: LongWord): Boolean;
begin
  Result := BitCount(Value) = 1;
end;

function LowBitMask(const HighBitIndex: LongWord): LongWord;
begin
  {$IFOPT R+}
  if HighBitIndex >= BitsPerLongWord then
    Result := 0 else
  {$ENDIF}
  Result := BitMaskTable[HighBitIndex] - 1;
end;

function HighBitMask(const LowBitIndex: LongWord): LongWord;
begin
  {$IFOPT R+}
  if LowBitIndex >= BitsPerLongWord then
    Result := 0 else
  {$ENDIF}
  Result := not BitMaskTable[LowBitIndex] + 1;
end;

function RangeBitMask(const LowBitIndex, HighBitIndex: LongWord): LongWord;
begin
  {$IFOPT R+}
  if (LowBitIndex >= BitsPerLongWord) and (HighBitIndex >= BitsPerLongWord) then
    begin
      Result := 0;
      exit;
    end;
  {$ENDIF}
  Result := $FFFFFFFF;
  if LowBitIndex > 0 then
    Result := Result xor (BitMaskTable[LowBitIndex] - 1);
  if HighBitIndex < 31 then
    Result := Result xor (not BitMaskTable[HighBitIndex + 1] + 1);
end;

function SetBitRange(const Value: LongWord; const LowBitIndex, HighBitIndex: LongWord): LongWord;
begin
  Result := Value or RangeBitMask(LowBitIndex, HighBitIndex);
end;

function ClearBitRange(const Value: LongWord; const LowBitIndex, HighBitIndex: LongWord): LongWord;
begin
  Result := Value and not RangeBitMask(LowBitIndex, HighBitIndex);
end;

function ToggleBitRange(const Value: LongWord; const LowBitIndex, HighBitIndex: LongWord): LongWord;
begin
  Result := Value xor RangeBitMask(LowBitIndex, HighBitIndex);
end;

function IsBitRangeSet(const Value: LongWord; const LowBitIndex, HighBitIndex: LongWord): Boolean;
var M: LongWord;
begin
  M := RangeBitMask(LowBitIndex, HighBitIndex);
  Result := Value and M = M;
end;

function IsBitRangeClear(const Value: LongWord; const LowBitIndex, HighBitIndex: LongWord): Boolean;
begin
  Result := Value and RangeBitMask(LowBitIndex, HighBitIndex) = 0;
end;



{                                                                              }
{ Sets                                                                         }
{                                                                              }
function AsCharSet(const C: Array of Char): CharSet;
var I: Integer;
begin
  Result := [];
  For I := 0 to High(C) do
    Include(Result, C[I]);
end;

function AsByteSet(const C: Array of Byte): ByteSet;
var I: Integer;
begin
  Result := [];
  For I := 0 to High(C) do
    Include(Result, C[I]);
end;

{$IFDEF USE_ASM386}
procedure ComplementChar(var C: CharSet; const Ch: Char);
asm
      MOVZX   ECX, DL
      BTC     [EAX], ECX
end;
{$ELSE}
procedure ComplementChar(var C: CharSet; const Ch: Char);
begin
  if Ch in C then
    Exclude(C, Ch) else
    Include(C, Ch);
end;
{$ENDIF}

{$IFDEF USE_ASM386}
procedure ClearCharSet(var C: CharSet);
asm
      XOR     EDX, EDX
      MOV     [EAX], EDX
      MOV     [EAX + 4], EDX
      MOV     [EAX + 8], EDX
      MOV     [EAX + 12], EDX
      MOV     [EAX + 16], EDX
      MOV     [EAX + 20], EDX
      MOV     [EAX + 24], EDX
      MOV     [EAX + 28], EDX
end;
{$ELSE}
procedure ClearCharSet(var C: CharSet);
begin
  C := [];
end;
{$ENDIF}

{$IFDEF USE_ASM386}
procedure FillCharSet(var C: CharSet);
asm
      MOV     EDX, $FFFFFFFF
      MOV     [EAX], EDX
      MOV     [EAX + 4], EDX
      MOV     [EAX + 8], EDX
      MOV     [EAX + 12], EDX
      MOV     [EAX + 16], EDX
      MOV     [EAX + 20], EDX
      MOV     [EAX + 24], EDX
      MOV     [EAX + 28], EDX
end;
{$ELSE}
procedure FillCharSet(var C: CharSet);
begin
  C := [#0..#255];
end;
{$ENDIF}

{$IFDEF USE_ASM386}
procedure ComplementCharSet(var C: CharSet);
asm
      NOT     DWORD PTR [EAX]
      NOT     DWORD PTR [EAX + 4]
      NOT     DWORD PTR [EAX + 8]
      NOT     DWORD PTR [EAX + 12]
      NOT     DWORD PTR [EAX + 16]
      NOT     DWORD PTR [EAX + 20]
      NOT     DWORD PTR [EAX + 24]
      NOT     DWORD PTR [EAX + 28]
end;
{$ELSE}
procedure ComplementCharSet(var C: CharSet);
begin
  C := [#0..#255] - C;
end;
{$ENDIF}

{$IFDEF USE_ASM386}
procedure AssignCharSet(var DestSet: CharSet; const SourceSet: CharSet);
asm
      MOV     ECX, [EDX]
      MOV     [EAX], ECX
      MOV     ECX, [EDX + 4]
      MOV     [EAX + 4], ECX
      MOV     ECX, [EDX + 8]
      MOV     [EAX + 8], ECX
      MOV     ECX, [EDX + 12]
      MOV     [EAX + 12], ECX
      MOV     ECX, [EDX + 16]
      MOV     [EAX + 16], ECX
      MOV     ECX, [EDX + 20]
      MOV     [EAX + 20], ECX
      MOV     ECX, [EDX + 24]
      MOV     [EAX + 24], ECX
      MOV     ECX, [EDX + 28]
      MOV     [EAX + 28], ECX
end;
{$ELSE}
procedure AssignCharSet(var DestSet: CharSet; const SourceSet: CharSet);
begin
  DestSet := SourceSet;
end;
{$ENDIF}

{$IFDEF USE_ASM386}
procedure Union(var DestSet: CharSet; const SourceSet: CharSet);
asm
      MOV     ECX, [EDX]
      OR      [EAX], ECX
      MOV     ECX, [EDX + 4]
      OR      [EAX + 4], ECX
      MOV     ECX, [EDX + 8]
      OR      [EAX + 8], ECX
      MOV     ECX, [EDX + 12]
      OR      [EAX + 12], ECX
      MOV     ECX, [EDX + 16]
      OR      [EAX + 16], ECX
      MOV     ECX, [EDX + 20]
      OR      [EAX + 20], ECX
      MOV     ECX, [EDX + 24]
      OR      [EAX + 24], ECX
      MOV     ECX, [EDX + 28]
      OR      [EAX + 28], ECX
end;
{$ELSE}
procedure Union(var DestSet: CharSet; const SourceSet: CharSet);
begin
  DestSet := DestSet + SourceSet;
end;
{$ENDIF}

{$IFDEF USE_ASM386}
procedure Difference(var DestSet: CharSet; const SourceSet: CharSet);
asm
      MOV     ECX, [EDX]
      NOT     ECX
      AND     [EAX], ECX
      MOV     ECX, [EDX + 4]
      NOT     ECX
      AND     [EAX + 4], ECX
      MOV     ECX, [EDX + 8]
      NOT     ECX
      AND     [EAX + 8],ECX
      MOV     ECX, [EDX + 12]
      NOT     ECX
      AND     [EAX + 12], ECX
      MOV     ECX, [EDX + 16]
      NOT     ECX
      AND     [EAX + 16], ECX
      MOV     ECX, [EDX + 20]
      NOT     ECX
      AND     [EAX + 20], ECX
      MOV     ECX, [EDX + 24]
      NOT     ECX
      AND     [EAX + 24], ECX
      MOV     ECX, [EDX + 28]
      NOT     ECX
      AND     [EAX + 28], ECX
end;
{$ELSE}
procedure Difference(var DestSet: CharSet; const SourceSet: CharSet);
begin
  DestSet := DestSet - SourceSet;
end;
{$ENDIF}

{$IFDEF USE_ASM386}
procedure Intersection(var DestSet: CharSet; const SourceSet: CharSet);
asm
      MOV     ECX, [EDX]
      AND     [EAX], ECX
      MOV     ECX, [EDX + 4]
      AND     [EAX + 4], ECX
      MOV     ECX, [EDX + 8]
      AND     [EAX + 8], ECX
      MOV     ECX, [EDX + 12]
      AND     [EAX + 12], ECX
      MOV     ECX, [EDX + 16]
      AND     [EAX + 16], ECX
      MOV     ECX, [EDX + 20]
      AND     [EAX + 20], ECX
      MOV     ECX, [EDX + 24]
      AND     [EAX + 24], ECX
      MOV     ECX, [EDX + 28]
      AND     [EAX + 28], ECX
end;
{$ELSE}
procedure Intersection(var DestSet: CharSet; const SourceSet: CharSet);
begin
  DestSet := DestSet * SourceSet;
end;
{$ENDIF}

{$IFDEF USE_ASM386}
procedure XORCharSet(var DestSet: CharSet; const SourceSet: CharSet);
asm
      MOV     ECX, [EDX]
      XOR     [EAX], ECX
      MOV     ECX, [EDX + 4]
      XOR     [EAX + 4], ECX
      MOV     ECX, [EDX + 8]
      XOR     [EAX + 8], ECX
      MOV     ECX, [EDX + 12]
      XOR     [EAX + 12], ECX
      MOV     ECX, [EDX + 16]
      XOR     [EAX + 16], ECX
      MOV     ECX, [EDX + 20]
      XOR     [EAX + 20], ECX
      MOV     ECX, [EDX + 24]
      XOR     [EAX + 24], ECX
      MOV     ECX, [EDX + 28]
      XOR     [EAX + 28], ECX
end;
{$ELSE}
procedure XORCharSet(var DestSet: CharSet; const SourceSet: CharSet);
var Ch: Char;
begin
  For Ch := #0 to #255 do
    if Ch in DestSet then
      begin
        if Ch in SourceSet then
          Exclude(DestSet, Ch);
      end else
      if Ch in SourceSet then
        Include(DestSet, Ch);
end;
{$ENDIF}

{$IFDEF USE_ASM386}
function IsSubSet(const A, B: CharSet): Boolean;
asm
      MOV     ECX, [EDX]
      NOT     ECX
      AND     ECX, [EAX]
      JNE     @Fin0
      MOV     ECX, [EDX + 4]
      NOT     ECX
      AND     ECX, [EAX + 4]
      JNE     @Fin0
      MOV     ECX, [EDX + 8]
      NOT     ECX
      AND     ECX, [EAX + 8]
      JNE     @Fin0
      MOV     ECX, [EDX + 12]
      NOT     ECX
      AND     ECX, [EAX + 12]
      JNE     @Fin0
      MOV     ECX, [EDX + 16]
      NOT     ECX
      AND     ECX, [EAX + 16]
      JNE     @Fin0
      MOV     ECX, [EDX + 20]
      NOT     ECX
      AND     ECX, [EAX + 20]
      JNE     @Fin0
      MOV     ECX, [EDX + 24]
      NOT     ECX
      AND     ECX, [EAX + 24]
      JNE     @Fin0
      MOV     ECX, [EDX + 28]
      NOT     ECX
      AND     ECX, [EAX + 28]
      JNE     @Fin0
      MOV     EAX, 1
      RET
@Fin0:
      XOR     EAX, EAX
end;
{$ELSE}
function IsSubSet(const A, B: CharSet): Boolean;
begin
  Result := A <= B;
end;
{$ENDIF}

{$IFDEF USE_ASM386}
function IsEqual(const A, B: CharSet): Boolean;
asm
      MOV     ECX, [EDX]
      XOR     ECX, [EAX]
      JNE     @Fin0
      MOV     ECX, [EDX + 4]
      XOR     ECX, [EAX + 4]
      JNE     @Fin0
      MOV     ECX, [EDX + 8]
      XOR     ECX, [EAX + 8]
      JNE     @Fin0
      MOV     ECX, [EDX + 12]
      XOR     ECX, [EAX + 12]
      JNE     @Fin0
      MOV     ECX, [EDX + 16]
      XOR     ECX, [EAX + 16]
      JNE     @Fin0
      MOV     ECX, [EDX + 20]
      XOR     ECX, [EAX + 20]
      JNE     @Fin0
      MOV     ECX, [EDX + 24]
      XOR     ECX, [EAX + 24]
      JNE     @Fin0
      MOV     ECX, [EDX + 28]
      XOR     ECX, [EAX + 28]
      JNE     @Fin0
      MOV     EAX, 1
      RET
@Fin0:
      XOR     EAX, EAX
end;
{$ELSE}
function IsEqual(const A, B: CharSet): Boolean;
begin
  Result := A = B;
end;
{$ENDIF}

{$IFDEF USE_ASM386}
function IsEmpty(const C: CharSet): Boolean;
asm
      MOV     EDX, [EAX]
      OR      EDX, [EAX + 4]
      OR      EDX, [EAX + 8]
      OR      EDX, [EAX + 12]
      OR      EDX, [EAX + 16]
      OR      EDX, [EAX + 20]
      OR      EDX, [EAX + 24]
      OR      EDX, [EAX + 28]
      JNE     @Fin0
      MOV     EAX, 1
      RET
@Fin0:
      XOR     EAX,EAX
end;
{$ELSE}
function IsEmpty(const C: CharSet): Boolean;
begin
  Result := C = [];
end;
{$ENDIF}

{$IFDEF USE_ASM386}
function IsComplete(const C: CharSet): Boolean;
asm
      MOV     EDX, [EAX]
      AND     EDX, [EAX + 4]
      AND     EDX, [EAX + 8]
      AND     EDX, [EAX + 12]
      AND     EDX, [EAX + 16]
      AND     EDX, [EAX + 20]
      AND     EDX, [EAX + 24]
      AND     EDX, [EAX + 28]
      CMP     EDX, $FFFFFFFF
      JNE     @Fin0
      MOV     EAX, 1
      RET
@Fin0:
      XOR     EAX, EAX
end;
{$ELSE}
function IsComplete(const C: CharSet): Boolean;
begin
  Result := C = CompleteCharSet;
end;
{$ENDIF}

{$IFDEF USE_ASM386}
function CharCount(const C: CharSet): Integer;
asm
      PUSH    EBX
      PUSH    ESI
      MOV     EBX, EAX
      XOR     ESI, ESI
      MOV     EAX, [EBX]
      CALL    BitCount
      ADD     ESI, EAX
      MOV     EAX, [EBX + 4]
      CALL    BitCount
      ADD     ESI, EAX
      MOV     EAX, [EBX + 8]
      CALL    BitCount
      ADD     ESI, EAX
      MOV     EAX, [EBX + 12]
      CALL    BitCount
      ADD     ESI, EAX
      MOV     EAX, [EBX + 16]
      CALL    BitCount
      ADD     ESI, EAX
      MOV     EAX, [EBX + 20]
      CALL    BitCount
      ADD     ESI, EAX
      MOV     EAX, [EBX + 24]
      CALL    BitCount
      ADD     ESI, EAX
      MOV     EAX, [EBX + 28]
      CALL    BitCount
      ADD     EAX, ESI
      POP     ESI
      POP     EBX
end;
{$ELSE}
function CharCount(const C: CharSet): Integer;
var I: Char;
begin
  Result := 0;
  For I := #0 to #255 do
    if I in C then
      Inc(Result);
end;
{$ENDIF}

{$IFDEF USE_ASM386}
procedure ConvertCaseInsensitive(var C: CharSet);
asm
      MOV     ECX, [EAX + 12]
      AND     ECX, $3FFFFFF
      OR      [EAX + 8], ECX
      MOV     ECX, [EAX + 8]
      AND     ECX, $3FFFFFF
      OR      [EAX + 12], ECX
end;
{$ELSE}
procedure ConvertCaseInsensitive(var C: CharSet);
var Ch: Char;
begin
  For Ch := 'A' to 'Z' do
    if Ch in C then
      Include(C, Char(Ord(Ch) + 32));
  For Ch := 'a' to 'z' do
    if Ch in C then
      Include(C, Char(Ord(Ch) - 32));
end;
{$ENDIF}

function CaseInsensitiveCharSet(const C: CharSet): CharSet;
begin
  AssignCharSet(Result, C);
  ConvertCaseInsensitive(Result);
end;



{                                                                              }
{ Range functions                                                              }
{                                                                              }
function IntRangeLength(const Low, High: Integer): Int64;
begin
  if Low > High then
    Result := 0
  else
    Result := Int64(High - Low) + 1;
end;

function IntRangeAdjacent(const Low1, High1, Low2, High2: Integer): Boolean;
begin
  Result := ((Low2 > MinInteger) and (High1 = Low2 - 1)) or
            ((High2 < MaxInteger) and (Low1 = High2 + 1));
end;

function IntRangeOverlap(const Low1, High1, Low2, High2: Integer): Boolean;
begin
  Result := ((Low1 >= Low2) and (Low1 <= High2)) or
            ((Low2 >= Low1) and (Low2 <= High1));
end;

function IntRangeHasElement(const Low, High, Element: Integer): Boolean;
begin
  Result := (Element >= Low) and (Element <= High);
end;

function IntRangeIncludeElement(var Low, High: Integer;
    const Element: Integer): Boolean;
begin
  Result := (Element >= Low) and (Element <= High);
  if Result then
    exit;
  if (Element < Low) and (Element + 1 = Low) then
    begin
      Low := Element;
      Result := True;
    end else
  if (Element > High) and (Element - 1 = High) then
    begin
      High := Element;
      Result := True;
    end;
end;

function IntRangeIncludeElementRange(var Low, High: Integer;
    const LowElement, HighElement: Integer): Boolean;
begin
  Result := (LowElement >= Low) and (HighElement <= High);
  if Result then
    exit;
  if ((Low >= LowElement) and (Low <= HighElement)) or
     ((Low > MinInteger) and (Low - 1 = HighElement)) then
    begin
      Low := LowElement;
      Result := True;
    end;
  if ((High >= LowElement) and (High <= HighElement)) or
     ((High < MaxInteger) and (High + 1 = LowElement)) then
    begin
      High := HighElement;
      Result := True;
    end;
end;

function CardinalRangeLength(const Low, High: Cardinal): Int64;
begin
  if Low > High then
    Result := 0
  else
    Result := Int64(High - Low) + 1;
end;

function CardinalRangeAdjacent(const Low1, High1, Low2, High2: Cardinal): Boolean;
begin
  Result := ((Low2 > MinCardinal) and (High1 = Low2 - 1)) or
            ((High2 < MaxCardinal) and (Low1 = High2 + 1));
end;

function CardinalRangeOverlap(const Low1, High1, Low2, High2: Cardinal): Boolean;
begin
  Result := ((Low1 >= Low2) and (Low1 <= High2)) or
            ((Low2 >= Low1) and (Low2 <= High1));
end;

function CardinalRangeHasElement(const Low, High, Element: Cardinal): Boolean;
begin
  Result := (Element >= Low) and (Element <= High);
end;

function CardinalRangeIncludeElement(var Low, High: Cardinal;
    const Element: Cardinal): Boolean;
begin
  Result := (Element >= Low) and (Element <= High);
  if Result then
    exit;
  if (Element < Low) and (Element + 1 = Low) then
    begin
      Low := Element;
      Result := True;
    end else
  if (Element > High) and (Element - 1 = High) then
    begin
      High := Element;
      Result := True;
    end;
end;

function CardinalRangeIncludeElementRange(var Low, High: Cardinal;
    const LowElement, HighElement: Cardinal): Boolean;
begin
  Result := (LowElement >= Low) and (HighElement <= High);
  if Result then
    exit;
  if ((Low >= LowElement) and (Low <= HighElement)) or
     ((Low > MinCardinal) and (Low - 1 = HighElement)) then
    begin
      Low := LowElement;
      Result := True;
    end;
  if ((High >= LowElement) and (High <= HighElement)) or
     ((High < MaxCardinal) and (High + 1 = LowElement)) then
    begin
      High := HighElement;
      Result := True;
    end;
end;



{                                                                              }
{ Swap                                                                         }
{                                                                              }
{$IFDEF USE_ASM386}
procedure Swap(var X, Y: Boolean);
asm
    mov cl, [edx]
    xchg byte ptr [eax], cl
    mov [edx], cl
end;
{$ELSE}
procedure Swap(var X, Y: Boolean);
var F: Boolean;
begin
  F := X;
  X := Y;
  Y := F;
end;
{$ENDIF}

{$IFDEF USE_ASM386}
procedure Swap(var X, Y: Byte);
asm
    mov cl, [edx]
    xchg byte ptr [eax], cl
    mov [edx], cl
end;
{$ELSE}
procedure Swap(var X, Y: Byte);
var F: Byte;
begin
  F := X;
  X := Y;
  Y := F;
end;
{$ENDIF}

{$IFDEF USE_ASM386}
procedure Swap(var X, Y: ShortInt);
asm
    mov cl, [edx]
    xchg byte ptr [eax], cl
    mov [edx], cl
end;
{$ELSE}
procedure Swap(var X, Y: ShortInt);
var F: ShortInt;
begin
  F := X;
  X := Y;
  Y := F;
end;
{$ENDIF}

{$IFDEF USE_ASM386}
procedure Swap(var X, Y: Word);
asm
    mov cx, [edx]
    xchg word ptr [eax], cx
    mov [edx], cx
end;
{$ELSE}
procedure Swap(var X, Y: Word);
var F: Word;
begin
  F := X;
  X := Y;
  Y := F;
end;
{$ENDIF}

{$IFDEF USE_ASM386}
procedure Swap(var X, Y: SmallInt);
asm
    mov cx, [edx]
    xchg word ptr [eax], cx
    mov [edx], cx
end;
{$ELSE}
procedure Swap(var X, Y: SmallInt);
var F: SmallInt;
begin
  F := X;
  X := Y;
  Y := F;
end;
{$ENDIF}

{$IFDEF USE_ASM386}
procedure Swap(var X, Y: LongInt);
asm
    mov ecx, [edx]
    xchg [eax], ecx
    mov [edx], ecx
end;
{$ELSE}
procedure Swap(var X, Y: LongInt);
var F: LongInt;
begin
  F := X;
  X := Y;
  Y := F;
end;
{$ENDIF}

{$IFDEF USE_ASM386}
procedure Swap(var X, Y: LongWord);
asm
    mov ecx, [edx]
    xchg [eax], ecx
    mov [edx], ecx
end;
{$ELSE}
procedure Swap(var X, Y: LongWord);
var F: LongWord;
begin
  F := X;
  X := Y;
  Y := F;
end;
{$ENDIF}

{$IFDEF USE_ASM386}
procedure Swap(var X, Y: Pointer);
asm
    mov ecx, [edx]
    xchg [eax], ecx
    mov [edx], ecx
end;
{$ELSE}
procedure Swap(var X, Y: Pointer);
var F: Pointer;
begin
  F := X;
  X := Y;
  Y := F;
end;
{$ENDIF}

{$IFDEF USE_ASM386}
procedure Swap(var X, Y: TObject);
asm
    mov ecx, [edx]
    xchg [eax], ecx
    mov [edx], ecx
end;
{$ELSE}
procedure Swap(var X, Y: TObject);
var F: TObject;
begin
  F := X;
  X := Y;
  Y := F;
end;
{$ENDIF}

procedure Swap(var X, Y: Int64);
var F: Int64;
begin
  F := X;
  X := Y;
  Y := F;
end;

procedure Swap(var X, Y: Single);
var F: Single;
begin
  F := X;
  X := Y;
  Y := F;
end;

procedure Swap(var X, Y: Double);
var F: Double;
begin
  F := X;
  X := Y;
  Y := F;
end;

procedure Swap(var X, Y: Extended);
var F: Extended;
begin
  F := X;
  X := Y;
  Y := F;
end;

procedure Swap(var X, Y: Currency);
var F: Currency;
begin
  F := X;
  X := Y;
  Y := F;
end;

procedure Swap(var X, Y: String);
var F: String;
begin
  F := X;
  X := Y;
  Y := F;
end;

procedure Swap(var X, Y: WideString);
var F: WideString;
begin
  F := X;
  X := Y;
  Y := F;
end;

{$IFDEF USE_ASM386}
procedure SwapObjects(var X, Y);
asm
    mov ecx, [edx]
    xchg [eax], ecx
    mov [edx], ecx
end;
{$ELSE}
procedure SwapObjects(var X, Y);
var F: TObject;
begin
  F := TObject(X);
  TObject(X) := TObject(Y);
  TObject(Y) := F;
end;
{$ENDIF}



{                                                                              }
{ iif                                                                          }
{                                                                              }
function iif(const Expr: Boolean; const TrueValue, FalseValue: Integer): Integer;
begin
  if Expr then
    Result := TrueValue else
    Result := FalseValue;
end;

function iif(const Expr: Boolean; const TrueValue, FalseValue: Int64): Int64;
begin
  if Expr then
    Result := TrueValue else
    Result := FalseValue;
end;

function iif(const Expr: Boolean; const TrueValue, FalseValue: Extended): Extended;
begin
  if Expr then
    Result := TrueValue else
    Result := FalseValue;
end;

function iif(const Expr: Boolean; const TrueValue, FalseValue: String): String;
begin
  if Expr then
    Result := TrueValue else
    Result := FalseValue;
end;

function iif(const Expr: Boolean; const TrueValue, FalseValue: TObject): TObject;
begin
  if Expr then
    Result := TrueValue else
    Result := FalseValue;
end;




{                                                                              }
{ Compare                                                                      }
{                                                                              }
function ReverseCompareResult(const C: TCompareResult): TCompareResult;
begin
  if C = crLess then
    Result := crGreater else
  if C = crGreater then
    Result := crLess else
    Result := C;
end;

function Compare(const I1, I2: Integer): TCompareResult;
begin
  if I1 < I2 then
    Result := crLess else
  if I1 > I2 then
    Result := crGreater else
    Result := crEqual;
end;

function Compare(const I1, I2: Int64): TCompareResult;
begin
  if I1 < I2 then
    Result := crLess else
  if I1 > I2 then
    Result := crGreater else
    Result := crEqual;
end;

function Compare(const I1, I2: Extended): TCompareResult;
begin
  if I1 < I2 then
    Result := crLess else
  if I1 > I2 then
    Result := crGreater else
    Result := crEqual;
end;

function Compare(const I1, I2: Boolean): TCompareResult;
begin
  if I1 = I2 then
    Result := crEqual else
  if I1 then
    Result := crGreater else
    Result := crLess;
end;

function Compare(const I1, I2: String): TCompareResult;
begin
  if I1 = I2 then
    Result := crEqual else
  if I1 > I2 then
    Result := crGreater else
    Result := crLess;
end;

function WideCompare(const I1, I2: WideString): TCompareResult;
begin
  if I1 = I2 then
    Result := crEqual else
  if I1 > I2 then
    Result := crGreater else
    Result := crLess;
end;



{                                                                              }
{ Approximate comparison                                                       }
{                                                                              }
function FloatZero(const A: Extended; const CompareDelta: Extended): Boolean;
begin
  Assert(CompareDelta >= 0.0, 'CompareDelta >= 0.0');
  Result := Abs(A) <= CompareDelta;
end;

function FloatOne(const A: Extended; const CompareDelta: Extended): Boolean;
begin
  Assert(CompareDelta >= 0.0, 'CompareDelta >= 0.0');
  Result := Abs(A - 1.0) <= CompareDelta;
end;

function FloatsEqual(const A, B: Extended; const CompareDelta: Extended): Boolean;
begin
  Assert(CompareDelta >= 0.0, 'CompareDelta >= 0.0');
  Result := Abs(A - B) <= CompareDelta;
end;

function FloatsCompare(const A, B: Extended; const CompareDelta: Extended): TCompareResult;
var D: Extended;
begin
  Assert(CompareDelta >= 0.0, 'CompareDelta >= 0.0');
  D := A - B;
  if Abs(D) <= CompareDelta then
    Result := crEqual else
  if D >= CompareDelta then
    Result := crGreater else
    Result := crLess;
end;



{                                                                              }
{ Scaled approximate comparison                                                }
{                                                                              }
{   The ApproxEqual and ApproxCompare functions were taken from the freeware   }
{   FltMath unit by Tempest Software, as taken from Knuth, Seminumerical       }
{   Algorithms, 2nd ed., Addison-Wesley, 1981, pp. 217-220.                    }
{                                                                              }
function ApproxEqual(const A, B: Extended; const CompareEpsilon: Double): Boolean;
var ExtA : TExtended absolute A;
    ExtB : TExtended absolute B;
    ExpA : Word;
    ExpB : Word;
    Exp  : TExtended;
begin
  ExpA := ExtA.Exponent and $7FFF;
  ExpB := ExtB.Exponent and $7FFF;
  if (ExpA = $7FFF) and
     ((ExtA.Mantissa[1] <> $80000000) or (ExtA.Mantissa[0] <> 0)) then
    { A is NaN }
    Result := False else
  if (ExpB = $7FFF) and
     ((ExtB.Mantissa[1] <> $80000000) or (ExtB.Mantissa[0] <> 0)) then
    { B is NaN }
    Result := False else
  if (ExpA = $7FFF) or (ExpB = $7FFF) then
    { A or B is infinity. Use the builtin comparison, which will       }
    { properly account for signed infinities, comparing infinity with  }
    { infinity, or comparing infinity with a finite value.             }
    Result := A = B else
  begin
    { We are comparing two finite values, so take the difference and   }
    { compare that against the scaled Epsilon.                         }
    Exp.Value := 1.0;
    if ExpA < ExpB then
      Exp.Exponent := ExpB else
      Exp.Exponent := ExpA;
    Result := Abs(A - B) <= (CompareEpsilon * Exp.Value);
  end;
end;

function ApproxCompare(const A, B: Extended; const CompareEpsilon: Double): TCompareResult;
var ExtA : TExtended absolute A;
    ExtB : TExtended absolute B;
    ExpA : Word;
    ExpB : Word;
    Exp  : TExtended;
    D, E : Extended;
begin
  ExpA := ExtA.Exponent and $7FFF;
  ExpB := ExtB.Exponent and $7FFF;
  if (ExpA = $7FFF) and
     ((ExtA.Mantissa[1] <> $80000000) or (ExtA.Mantissa[0] <> 0)) then
    { A is NaN }
    Result := crUndefined else
  if (ExpB = $7FFF) and
     ((ExtB.Mantissa[1] <> $80000000) or (ExtB.Mantissa[0] <> 0)) then
    { B is NaN }
    Result := crUndefined else
  if (ExpA = $7FFF) or (ExpB = $7FFF) then
    { A or B is infinity. Use the builtin comparison, which will       }
    { properly account for signed infinities, comparing infinity with  }
    { infinity, or comparing infinity with a finite value.             }
    Result := Compare(A, B) else
  begin
    { We are comparing two finite values, so take the difference and   }
    { compare that against the scaled Epsilon.                         }
    Exp.Value := 1.0;
    if ExpA < ExpB then
      Exp.Exponent := ExpB else
      Exp.Exponent := ExpA;
    E := CompareEpsilon * Exp.Value;
    D := A - B;
    if Abs(D) <= E then
      Result := crEqual else
    if D >= E then
      Result := crGreater else
      Result := crLess;
  end;
end;



{                                                                              }
{ Special floating-point values                                                }
{                                                                              }
function FloatIsInfinity(const A: Extended): Boolean;
var Ext : TExtended absolute A;
begin
  if Ext.Exponent and $7FFF <> $7FFF then
    Result := False
  else
    Result := (Ext.Mantissa[1] = $80000000) and (Ext.Mantissa[0] = 0);
end;

function FloatIsNaN(const A: Extended): Boolean;
var Ext : TExtended absolute A;
begin
  if Ext.Exponent and $7FFF <> $7FFF then
    Result := False
  else
    Result := (Ext.Mantissa[1] <> $80000000) or (Ext.Mantissa[0] <> 0)
end;



{                                                                              }
{ Base Conversion                                                              }
{                                                                              }
function LongWordToBase(const I: LongWord; const Digits, Base: Byte;
         const UpperCase: Boolean = True): String;
var D : LongWord;
    L : Byte;
    P : PChar;
    V : Byte;
begin
  Assert(Base <= 16, 'Maximum base is 16');
  if I = 0 then
    begin
      if Digits = 0 then
        L := 1
      else
        L := Digits;
      SetLength(Result, L);
      FillChar(Pointer(Result)^, L, '0');
      exit;
    end;
  L := 0;
  D := I;
  While D > 0 do
    begin
      Inc(L);
      D := D div Base;
    end;
  if L < Digits then
    L := Digits;
  SetLength(Result, L);
  P := Pointer(Result);
  Inc(P, L - 1);
  D := I;
  While D > 0 do
    begin
      V := D mod Base + 1;
      if UpperCase then
        P^ := s_HexDigitsUpper[V] else
        P^ := s_HexDigitsLower[V];
      Dec(P);
      Dec(L);
      D := D div Base;
    end;
  While L > 0 do
    begin
      P^ := '0';
      Dec(P);
      Dec(L);
    end;
end;

function LongWordToBin(const I: LongWord; const Digits: Byte): String;
begin
  Result := LongWordToBase(I, Digits, 2);
end;

function LongWordToOct(const I: LongWord; const Digits: Byte): String;
begin
  Result := LongWordToBase(I, Digits, 8);
end;

function LongWordToHex(const I: LongWord; const Digits: Byte;
         const UpperCase: Boolean): String;
begin
  Result := LongWordToBase(I, Digits, 16, UpperCase);
end;

function LongWordToStr(const I: LongWord; const Digits: Byte): String;
begin
  Result := LongWordToBase(I, Digits, 10);
end;

const
  HexLookup: Array[Char] of Byte = (
      $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF,
      $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF,
      $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF,
      0,   1,   2,   3,   4,   5,   6,   7,   8,   9,   $FF, $FF, $FF, $FF, $FF, $FF,
      $FF, 10,  11,  12,  13,  14,  15,  $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF,
      $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF,
      $FF, 10,  11,  12,  13,  14,  15,  $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF,
      $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF,
      $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF,
      $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF,
      $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF,
      $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF,
      $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF,
      $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF,
      $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF,
      $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF);

function IsHexChar(const Ch: Char): Boolean;
begin
  Result := HexLookup[Ch] <= 15;
end;

function IsHexWideChar(const Ch: WideChar): Boolean;
begin
  if Ord(Ch) <= $FF then
    Result := HexLookup[Char(Ch)] <= 15
  else
    Result := False;
end;

function HexCharValue(const Ch: Char): Byte;
begin
  Result := HexLookup[Ch];
end;

function HexWideCharValue(const Ch: WideChar): Byte;
begin
  if Ord(Ch) <= $FF then
    Result := HexLookup[Char(Ch)]
  else
    Result := $FF;
end;

function IsValidBaseStr(const S: String; const V: CharSet): Boolean;
var I : Integer;
    P : PChar;
begin
  I := Length(S);
  if I = 0 then
    begin
      Result := False;
      exit;
    end;
  P := Pointer(S);
  While I > 0 do
    if not (P^ in V) then
      begin
        Result := False;
        exit;
      end else
      begin
        Dec(I);
        Inc(P);
      end;
  Result := True;
end;

function IsValidBinStr(const S: String): Boolean;
begin
  Result := IsValidBaseStr(S, ['0'..'1']);
end;

function IsValidOctStr(const S: String): Boolean;
begin
  Result := IsValidBaseStr(S, ['0'..'7']);
end;

function IsValidDecStr(const S: String): Boolean;
begin
  Result := IsValidBaseStr(S, ['0'..'9']);
end;

function IsValidHexStr(const S: String): Boolean;
begin
  Result := IsValidBaseStr(S, ['0'..'9', 'A'..'F', 'a'..'f']);
end;

function BaseStrToLongWord(const S: String; const BaseLog2: Byte;
    var Valid: Boolean): LongWord;
var M : Byte;
    L : LongWord;
    P : Byte;
    C : Byte;
    Q : PChar;
begin
  Assert(BaseLog2 <= 4, 'BaseLog2 <= 4');
  P := Length(S);
  if P = 0 then // empty string is invalid
    begin
      Valid := False;
      Result := 0;
      exit;
    end;
  M := (1 shl BaseLog2) - 1; // maximum digit value
  L := 0;
  Result := 0;
  Q := Pointer(S);
  Inc(Q, P - 1);
  Repeat
    C := HexLookup[Q^];
    if C > M then // invalid digit
      begin
        Valid := False;
        Result := 0;
        exit;
      end;
    Inc(Result, LongWord(C) shl L);
    Inc(L, BaseLog2);
    if L > 32 then // overflow
      begin
        Valid := False;
        Result := 0;
        exit;
      end;
    Dec(P);
    Dec(Q);
  Until P = 0;
  Valid := True;
end;

function BinStrToLongWord(const S: String; var Valid: Boolean): LongWord;
begin
  Result := BaseStrToLongWord(S, 1, Valid);
end;

function OctStrToLongWord(const S: String; var Valid: Boolean): LongWord;
begin
  Result := BaseStrToLongWord(S, 3, Valid);
end;

function HexStrToLongWord(const S: String; var Valid: Boolean): LongWord;
begin
  Result := BaseStrToLongWord(S, 4, Valid);
end;

function DecStrToLongWord(const S: String; var Valid: Boolean): LongWord;
var L : Integer;
    P : PChar;
    C : Char;
    F : LongWord;
    R : Int64;
begin
  L := Length(S);
  if L = 0 then // empty string
    begin
      Result := 0;
      Valid := False;
      exit;
    end;
  R := 0;
  F := 1;
  P := Pointer(S);
  Inc(P, L - 1);
  Repeat
    C := P^;
    if not (C in ['0'..'9']) then // invalid character
      begin
        Valid := False;
        Result := 0;
        exit;
      end;
    Inc(R, Int64(Ord(C) - Ord('0')) * F);
    if R > MaxLongWord then // overflow, value too large
      begin
        Valid := False;
        Result := 0;
        exit;
      end;
    Dec(P);
    Dec(L);
    if L > 0 then
      begin
        if F = 1000000000 then // overflow, too many digits
          begin
            Valid := False;
            Result := 0;
            exit;
          end;
        F := F * 10;
      end;
  Until L = 0;
  Valid := True;
  Result := LongWord(R);
end;

function EncodeBase64(const S, Alphabet: String; const Pad: Boolean; const PadMultiple: Integer; const PadChar: Char): String;
var R, C : Byte;
    F, L, M, N, U : Integer;
    P : PChar;
    T : Boolean;
begin
  Assert(Length(Alphabet) = 64, 'Alphabet must contain 64 characters');
  L := Length(S);
  if L = 0 then
    begin
      Result := '';
      exit;
    end;
  M := L mod 3;
  N := (L div 3) * 4 + M;
  if M > 0 then
    Inc(N);
  T := Pad and (PadMultiple > 1);
  if T then
    begin
      U := N mod PadMultiple;
      if U > 0 then
        begin
          U := PadMultiple - U;
          Inc(N, U);
        end;
    end else
    U := 0;
  SetLength(Result, N);
  P := Pointer(Result);
  R := 0;
  For F := 0 to L - 1 do
    begin
      C := Byte(S [F + 1]);
      Case F mod 3 of
        0 : begin
              P^ := Alphabet[C shr 2 + 1];
              Inc(P);
              R := (C and 3) shl 4;
            end;
        1 : begin
              P^ := Alphabet[C shr 4 + R + 1];
              Inc(P);
              R := (C and $0F) shl 2;
            end;
        2 : begin
              P^ := Alphabet[C shr 6 + R + 1];
              Inc(P);
              P^ := Alphabet[C and $3F + 1];
              Inc(P);
            end;
      end;
    end;
  if M > 0 then
    begin
      P^ := Alphabet[R + 1];
      Inc(P);
    end;
  For F := 1 to U do
    begin
      P^ := PadChar;
      Inc(P);
    end;
end;

function DecodeBase64(const S, Alphabet: String; const PadSet: CharSet): String;
var F, L, M, P : Integer;
    B, OutPos  : Byte;
    OutB       : Array[1..3] of Byte;
    Lookup     : Array[Char] of Byte;
    R          : PChar;
begin
  Assert(Length(Alphabet) = 64, 'Alphabet must contain 64 characters');
  L := Length(S);
  P := 0;
  if PadSet <> [] then
    While (L - P > 0) and (S[L - P] in PadSet) do
      Inc(P);
  M := L - P;
  if M = 0 then
    begin
      Result := '';
      exit;
    end;
  SetLength(Result, (M * 3) div 4);
  FillChar(Lookup, Sizeof(Lookup), #0);
  For F := 0 to 63 do
    Lookup[Alphabet[F + 1]] := Byte(F);
  R := Pointer(Result);
  OutPos := 0;
  For F := 1 to L - P do
    begin
      B := Lookup[S[F]];
      Case OutPos of
          0 : OutB[1] := B shl 2;
          1 : begin
                OutB[1] := OutB[1] or (B shr 4);
                R^ := Char(OutB[1]);
                Inc(R);
                OutB[2] := (B shl 4) and $FF;
              end;
          2 : begin
                OutB[2] := OutB[2] or (B shr 2);
                R^ := Char(OutB[2]);
                Inc(R);
                OutB[3] := (B shl 6) and $FF;
              end;
          3 : begin
                OutB[3] := OutB[3] or B;
                R^ := Char(OutB[3]);
                Inc(R);
              end;
        end;
      OutPos := (OutPos + 1) mod 4;
    end;
  if (OutPos > 0) and (P = 0) then // incomplete encoding, add the partial byte if not 0
    if OutB[OutPos] <> 0 then
      Result := Result + Char(OutB[OutPos]);
end;

function MIMEBase64Encode(const S: String): String;
begin
  Result := EncodeBase64(S, b64_MIMEBase64, True, 4, '=');
end;

function UUDecode(const S: String): String;
begin
  // Line without size indicator (first byte = length + 32)
  Result := DecodeBase64(S, b64_UUEncode, ['`']);
end;

function MIMEBase64Decode(const S: String): String;
begin
  Result := DecodeBase64(S, b64_MIMEBase64, ['=']);
end;

function XXDecode(const S: String): String;
begin
  Result := DecodeBase64(S, b64_XXEncode, []);
end;

function BytesToHex(const P: Pointer; const Count: Integer;
         const UpperCase: Boolean): String;
var Q : PByte;
    D : PChar;
    L : Integer;
    V : Byte;
begin
  Q := P;
  L := Count;
  if (L <= 0) or not Assigned(Q) then
    begin
      Result := '';
      exit;
    end;
  SetLength(Result, Count * 2);
  D := Pointer(Result);
  While L > 0 do
    begin
      V := Q^ shr 4 + 1;
      if UpperCase then
        D^ := s_HexDigitsUpper[V] else
        D^ := s_HexDigitsLower[V];
      Inc(D);
      V := Q^ and $F + 1;
      if UpperCase then
        D^ := s_HexDigitsUpper[V] else
        D^ := s_HexDigitsLower[V];
      Inc(D);
      Inc(Q);
      Dec(L);
    end;
end;



{                                                                              }
{ Type conversion                                                              }
{                                                                              }
function PointerToStr(const P: Pointer): String;
begin
  Result := '$' + LongWordToHex(LongWord(P), 8);
end;

function StrToPointer(const S: String): Pointer;
var V : Boolean;
begin
  Result := Pointer(HexStrToLongWord(S, V));
end;

function ObjectClassName(const O: TObject): String;
begin
  if not Assigned(O) then
    Result := 'nil' else
    Result := O.ClassName;
end;

function ClassClassName(const C: TClass): String;
begin
  if not Assigned(C) then
    Result := 'nil' else
    Result := C.ClassName;
end;

function ObjectToStr(const O: TObject): String;
begin
  if not Assigned(O) then
    Result := 'nil' else
    Result := O.ClassName + '@' + LongWordToHex(LongWord(O), 8);
end;

function ClassToStr(const C: TClass): String;
begin
  if not Assigned(C) then
    Result := 'nil' else
    Result := C.ClassName + '@' + LongWordToHex(LongWord(C), 8);
end;

{$IFDEF USE_ASM386}
function CharSetToStr(const C: CharSet): String; // Andrew N. Driazgov
asm
      PUSH    EBX
      MOV     ECX, $100
      MOV     EBX, EAX
      PUSH    ESI
      MOV     EAX, EDX
      SUB     ESP, ECX
      XOR     ESI, ESI
      XOR     EDX, EDX
@@lp: BT      [EBX], EDX
      JC      @@mm
@@nx: INC     EDX
      DEC     ECX
      JNE     @@lp
      MOV     ECX, ESI
      MOV     EDX, ESP
      CALL    System.@LStrFromPCharLen
      ADD     ESP, $100
      POP     ESI
      POP     EBX
      RET
@@mm: MOV     [ESP + ESI], DL
      INC     ESI
      JMP     @@nx
end;
{$ELSE}
function CharSetToStr(const C: CharSet): String;
// Implemented recursively to avoid multiple memory allocations
  procedure CharMatch(const Start: Char; const Count: Integer);
  var Ch : Char;
  begin
    For Ch := Start to #255 do
      if Ch in C then
        begin
          if Ch = #255 then
            SetLength(Result, Count + 1) else
            CharMatch(Char(Byte(Ch) + 1), Count + 1);
          Result[Count + 1] := Ch;
          exit;
        end;
    SetLength(Result, Count);
  end;
begin
  CharMatch(#0, 0);
end;
{$ENDIF}

{$IFDEF USE_ASM386}
function StrToCharSet(const S: String): CharSet; // Andrew N. Driazgov
asm
      XOR     ECX, ECX
      MOV     [EDX], ECX
      MOV     [EDX + 4], ECX
      MOV     [EDX + 8], ECX
      MOV     [EDX + 12], ECX
      MOV     [EDX + 16], ECX
      MOV     [EDX + 20], ECX
      MOV     [EDX + 24], ECX
      MOV     [EDX + 28], ECX
      TEST    EAX, EAX
      JE      @@qt
      MOV     ECX, [EAX - 4]
      PUSH    EBX
      SUB     ECX, 8
      JS      @@nx
@@lp: MOVZX   EBX, BYTE PTR [EAX]
      BTS     [EDX], EBX
      MOVZX   EBX, BYTE PTR [EAX + 1]
      BTS     [EDX], EBX
      MOVZX   EBX, BYTE PTR [EAX + 2]
      BTS     [EDX], EBX
      MOVZX   EBX, BYTE PTR [EAX + 3]
      BTS     [EDX], EBX
      MOVZX   EBX, BYTE PTR [EAX + 4]
      BTS     [EDX], EBX
      MOVZX   EBX, BYTE PTR [EAX + 5]
      BTS     [EDX], EBX
      MOVZX   EBX, BYTE PTR [EAX + 6]
      BTS     [EDX], EBX
      MOVZX   EBX, BYTE PTR [EAX + 7]
      BTS     [EDX], EBX
      ADD     EAX, 8
      SUB     ECX, 8
      JNS     @@lp
@@nx: JMP     DWORD PTR @@tV[ECX * 4 + 32]
@@tV: DD      @@ex, @@t1, @@t2, @@t3
      DD      @@t4, @@t5, @@t6, @@t7
@@t7: MOVZX   EBX, BYTE PTR [EAX + 6]
      BTS     [EDX], EBX
@@t6: MOVZX   EBX, BYTE PTR [EAX + 5]
      BTS     [EDX], EBX
@@t5: MOVZX   EBX, BYTE PTR [EAX + 4]
      BTS     [EDX], EBX
@@t4: MOVZX   EBX, BYTE PTR [EAX + 3]
      BTS     [EDX], EBX
@@t3: MOVZX   EBX, BYTE PTR [EAX + 2]
      BTS     [EDX], EBX
@@t2: MOVZX   EBX, BYTE PTR [EAX + 1]
      BTS     [EDX], EBX
@@t1: MOVZX   EBX, BYTE PTR [EAX]
      BTS     [EDX], EBX
@@ex: POP     EBX
@@qt:
end;
{$ELSE}
function StrToCharSet(const S: String): CharSet;
var I: Integer;
begin
  ClearCharSet(Result);
  For I := 1 to Length(S) do
    Include(Result, S [I]);
end;
{$ENDIF}



{                                                                              }
{ Hash functions                                                               }
{   Derived from a CRC32 algorithm.                                            }
{                                                                              }
var
  HashTableInit   : Boolean = False;
  HashTable       : Array[Byte] of LongWord;
  HashTableNoCase : Array[Byte] of LongWord;
  HashPoly        : LongWord = $EDB88320;

procedure InitHashTable;
var I, J : Byte;
    R    : LongWord;
begin
  For I := $00 to $FF do
    begin
      R := I;
      For J := 8 downto 1 do
        if R and 1 <> 0 then
          R := (R shr 1) xor HashPoly
        else
          R := R shr 1;
      HashTable[I] := R;
    end;
  Move(HashTable, HashTableNoCase, Sizeof(HashTable));
  For I := Ord('A') to Ord('Z') do
    HashTableNoCase[I] := HashTableNoCase[I or 32];
  HashTableInit := True;
end;

function Hash(const Hash: LongWord; const Buf; const BufSize: Integer): LongWord;
var P : PByte;
    I : Integer;
begin
  P := @Buf;
  Result := Hash;
  For I := 1 to BufSize do
    begin
      Result := HashTable[Byte(Result) xor P^] xor (Result shr 8);
      Inc(P);
    end;
end;

function HashNoCase(const Hash: LongWord; const Buf; const BufSize: Integer): LongWord;
var P : PByte;
    I : Integer;
begin
  P := @Buf;
  Result := Hash;
  For I := 1 to BufSize do
    begin
      Result := HashTableNoCase[P^] xor (Result shr 8);
      Inc(P);
    end;
end;

function HashBuf(const Buf; const BufSize: Integer; const Slots: LongWord): LongWord;
begin
  if not HashTableInit then
    InitHashTable;
  Result := not Hash($FFFFFFFF, Buf, BufSize);
  // Mod into slots
  if Slots <> 0 then
    Result := Result mod Slots;
end;

function HashStrBuf(const StrBuf: Pointer; const StrLength: Integer;
    const Slots: LongWord): LongWord;
var P    : PChar;
    I, J : Integer;
begin
  if not HashTableInit then
    InitHashTable;
  P := StrBuf;
  if StrLength <= 48 then // Hash all characters for short strings
    Result := Hash($FFFFFFFF, P^, StrLength)
  else
    begin
      // Hash first 16 bytes
      Result := Hash($FFFFFFFF, P^, 16);
      // Hash last 16 bytes
      Inc(P, StrLength - 16);
      Result := Hash(Result, P^, 16);
      // Hash 16 bytes sampled from rest of string
      I := (StrLength - 48) div 16;
      P := StrBuf;
      Inc(P, 16);
      For J := 1 to 16 do
        begin
          Result := HashTable[Byte(Result) xor Byte(P^)] xor (Result shr 8);
          Inc(P, I + 1);
        end;
    end;
  // Mod into slots
  if Slots <> 0 then
    Result := Result mod Slots;
end;

function HashStrBufNoCase(const StrBuf: Pointer; const StrLength: Integer;
    const Slots: LongWord): LongWord;
var P    : PChar;
    I, J : Integer;
begin
  if not HashTableInit then
    InitHashTable;
  P := StrBuf;
  if StrLength <= 48 then // Hash all characters for short strings
    Result := HashNoCase($FFFFFFFF, P^, StrLength)
  else
    begin
      // Hash first 16 bytes
      Result := HashNoCase($FFFFFFFF, P^, 16);
      // Hash last 16 bytes
      Inc(P, StrLength - 16);
      Result := HashNoCase(Result, P^, 16);
      // Hash 16 bytes sampled from rest of string
      I := (StrLength - 48) div 16;
      P := StrBuf;
      Inc(P, 16);
      For J := 1 to 16 do
        begin
          Result := HashTableNoCase[Byte(P^)] xor (Result shr 8);
          Inc(P, I + 1);
        end;
    end;
  // Mod into slots
  if Slots <> 0 then
    Result := Result mod Slots;
end;

function HashStr(const S: String; const Slots: LongWord; const CaseSensitive: Boolean): LongWord;
begin
  if CaseSensitive then
    Result := HashStrBuf(Pointer(S), Length(S), Slots)
  else
    Result := HashStrBufNoCase(Pointer(S), Length(S), Slots);
end;

{ HashInteger based on the CRC32 algorithm. It is a very good all purpose hash }
{ with a highly uniform distribution of results.                               }
function HashInteger(const I: Integer; const Slots: LongWord): LongWord;
var P : PByte;
begin
  if not HashTableInit then
    InitHashTable;
  Result := $FFFFFFFF;
  P := @I;
  Result := HashTable[Byte(Result) xor P^] xor (Result shr 8);
  Inc(P);
  Result := HashTable[Byte(Result) xor P^] xor (Result shr 8);
  Inc(P);
  Result := HashTable[Byte(Result) xor P^] xor (Result shr 8);
  Inc(P);
  Result := HashTable[Byte(Result) xor P^] xor (Result shr 8);
  if Slots <> 0 then
    Result := Result mod Slots;
end;

function HashLongWord(const I: LongWord; const Slots: LongWord): LongWord;
var P : PByte;
begin
  if not HashTableInit then
    InitHashTable;
  Result := $FFFFFFFF;
  P := @I;
  Result := HashTable[Byte(Result) xor P^] xor (Result shr 8);
  Inc(P);
  Result := HashTable[Byte(Result) xor P^] xor (Result shr 8);
  Inc(P);
  Result := HashTable[Byte(Result) xor P^] xor (Result shr 8);
  Inc(P);
  Result := HashTable[Byte(Result) xor P^] xor (Result shr 8);
  if Slots <> 0 then
    Result := Result mod Slots;
end;



{                                                                              }
{ Memory                                                                       }
{                                                                              }
{$IFDEF USE_ASM386}
procedure FillMem(var Buf; const Count: Integer; const Value: Byte);
asm
    OR     EDX, EDX
    JLE    @Fin
    PUSH   EDI
    MOV    EDI, EAX
    MOV    CH, CL
    MOV    EAX, ECX
    SHL    EAX, 16
    MOV    AX, CX
    CMP    EDX, 12
    JBE    @SmallFillMem
  @GeneralFillMem:
    MOV    ECX, EDX
    SHR    ECX, 2
    REP    STOSD
    AND    EDX, 3
  @SmallFillMem:
    JMP    DWORD PTR @JumpTable[EDX * 4]
  @JumpTable:
    DD     @Fill0,  @Fill1,  @Fill2,  @Fill3
    DD     @Fill4,  @Fill5,  @Fill6,  @Fill7
    DD     @Fill8,  @Fill9,  @Fill10, @Fill11
    DD     @Fill12
  @Fill12:
    MOV    DWORD PTR [EDI], EAX
    MOV    DWORD PTR [EDI + 4], EAX
    MOV    DWORD PTR [EDI + 8], EAX
    POP    EDI
    RET
  @Fill11:
    MOV    BYTE PTR [EDI + 10], AL
  @Fill10:
    MOV    DWORD PTR [EDI], EAX
    MOV    DWORD PTR [EDI + 4], EAX
    MOV    WORD PTR [EDI + 8], AX
    POP    EDI
    RET
  @Fill9:
    MOV    BYTE PTR [EDI + 8], AL
  @Fill8:
    MOV    DWORD PTR [EDI], EAX
    MOV    DWORD PTR [EDI + 4], EAX
    POP    EDI
    RET
  @Fill7:
    MOV    BYTE PTR [EDI + 6], AL
  @Fill6:
    MOV    DWORD PTR [EDI], EAX
    MOV    WORD PTR [EDI + 4], AX
    POP    EDI
    RET
  @Fill5:
    MOV    BYTE PTR [EDI + 4], AL
  @Fill4:
    MOV    DWORD PTR [EDI], EAX
    POP    EDI
    RET
  @Fill3:
    MOV    BYTE PTR [EDI + 2], AL
  @Fill2:
    MOV    WORD PTR [EDI], AX
    POP    EDI
    RET
  @Fill1:
    MOV    BYTE PTR [EDI], AL
  @Fill0:
    POP    EDI
  @Fin:
    RET
end;
{$ELSE}
procedure FillMem(var Buf; const Count: Integer; const Value: Byte);
begin
  FillChar(Buf, Count, Value);
end;
{$ENDIF}

{$IFDEF USE_ASM386}
procedure ZeroMem(var Buf; const Count: Integer);
asm
    OR     EDX, EDX
    JLE    @Zero0
    CMP    EDX, 12
    JA     @GeneralZeroMem
    XOR    ECX, ECX
    JMP    DWORD PTR @JumpTable[EDX * 4]
  @JumpTable:
    DD     @Zero0,  @Zero1,  @Zero2,  @Zero3
    DD     @Zero4,  @Zero5,  @Zero6,  @Zero7
    DD     @Zero8,  @Zero9,  @Zero10, @Zero11
    DD     @Zero12
  @Zero12:
    MOV    DWORD PTR [EAX], ECX
    MOV    DWORD PTR [EAX + 4], ECX
    MOV    DWORD PTR [EAX + 8], ECX
    RET
  @Zero11:
    MOV    BYTE PTR [EAX + 10], CL
  @Zero10:
    MOV    DWORD PTR [EAX], ECX
    MOV    DWORD PTR [EAX + 4], ECX
    MOV    WORD PTR [EAX + 8], CX
    RET
  @Zero9:
    MOV    BYTE PTR [EAX + 8], CL
  @Zero8:
    MOV    DWORD PTR [EAX], ECX
    MOV    DWORD PTR [EAX + 4], ECX
    RET
  @Zero7:
    MOV    BYTE PTR [EAX + 6], CL
  @Zero6:
    MOV    DWORD PTR [EAX], ECX
    MOV    WORD PTR [EAX + 4], CX
    RET
  @Zero5:
    MOV    BYTE PTR [EAX + 4], CL
  @Zero4:
    MOV    DWORD PTR [EAX], ECX
    RET
  @Zero3:
    MOV    BYTE PTR [EAX + 2], CL
  @Zero2:
    MOV    WORD PTR [EAX], CX
    RET
  @Zero1:
    MOV    BYTE PTR [EAX], CL
  @Zero0:
    RET
  @GeneralZeroMem:
    PUSH   EDI
    MOV    EDI, EAX
    XOR    EAX, EAX
    MOV    ECX, EDX
    SHR    ECX, 2
    REP    STOSD
    AND    EDX, 3
    XOR    ECX, ECX
    MOV    EAX, EDI
    POP    EDI
    JMP    DWORD PTR @JumpTable[EDX * 4]
end;
{$ELSE}
procedure ZeroMem(var Buf; const Count: Integer);
begin
  FillChar(Buf, Count, #0);
end;
{$ENDIF}

{$IFDEF USE_ASM386}
procedure MoveMem(const Source; var Dest; const Count: Integer);
asm
    OR     ECX, ECX
    JLE    @Move0
    CMP    ECX, 10
    JA     @GeneralMove
    JMP    DWORD PTR @JumpTable[ECX * 4]
  @JumpTable:
    DD     @Move0,  @Move1,        @Move2,   @Move3
    DD     @Move4,  @Move5,        @Move6,   @GeneralMove
    DD     @Move8,  @GeneralMove,  @Move10,  @GeneralMove
    DD     @Move12
  @Move12:
    MOV    ECX, [EAX]
    MOV    [EDX], ECX
    MOV    ECX, [EAX + 4]
    MOV    EAX, [EAX + 8]
    MOV    [EDX + 4], ECX
    MOV    [EDX + 8], EAX
    RET
  @Move10:
    MOV    ECX, [EAX]
    MOV    [EDX], ECX
    MOV    ECX, [EAX + 4]
    MOV    AX, [EAX + 8]
    MOV    [EDX + 4], ECX
    MOV    [EDX + 8], AX
    RET
  @Move8:
    MOV    ECX, [EAX]
    MOV    EAX, [EAX + 4]
    MOV    [EDX], ECX
    MOV    [EDX + 4], EAX
    RET
  @Move6:
    MOV    ECX, [EAX]
    MOV    AX, [EAX + 4]
    MOV    [EDX], ECX
    MOV    [EDX + 4], AX
    RET
  @Move5:
    MOV    ECX, [EAX]
    MOV    AL, [EAX + 4]
    MOV    [EDX], ECX
    MOV    [EDX + 4], AL
    RET
  @Move4:
    MOV    ECX, [EAX]
    MOV    [EDX], ECX
    RET
  @Move3:
    MOV    CX, [EAX]
    MOV    AL, [EAX + 2]
    MOV    [EDX], CX
    MOV    [EDX + 2], AL
    RET
  @Move2:
    MOV    CX, [EAX]
    MOV    [EDX], CX
    RET
  @Move1:
    MOV    CL, [EAX]
    MOV    [EDX], CL
    RET
  @GeneralMove:
    CALL   Move
  @Move0:
    RET
end;
{$ELSE}
procedure MoveMem(const Source; var Dest; const Count: Integer);
begin
  if Count <= 0 then
    exit;
  if Count > 4 then
    Move(Source, Dest, Count)
  else
    Case Count of // optimization for small moves
      1 : PByte(@Dest)^ := PByte(@Source)^;
      2 : PWord(@Dest)^ := PWord(@Source)^;
      4 : PLongWord(@Dest)^ := PLongWord(@Source)^;
    else
      Move(Source, Dest, Count);
    end;
end;
{$ENDIF}

{$IFDEF USE_ASM386}
function CompareMem(const Buf1; const Buf2; const Count: Integer): Boolean; assembler;
asm
    PUSH    ESI
    PUSH    EDI
    MOV     ESI, Buf1
    MOV     EDI, Buf2
    MOV     EDX, ECX
    XOR     EAX, EAX
    AND     EDX, 3
    SHR     ECX, 1
    SHR     ECX, 1
    REPE    CMPSD
    JNE     @Fin
    MOV     ECX, EDX
    REPE    CMPSB
    JNE     @Fin
    INC     EAX
  @Fin:
    POP     EDI
    POP     ESI
end;
{$ELSE}
function CompareMem(const Buf1; const Buf2; const Count: Integer): Boolean;
var P, Q : Pointer;
    D, I : Integer;
begin
  if Count <= 0 then
    begin
      Result := True;
      exit;
    end;
  P := @Buf1;
  Q := @Buf2;
  D := LongWord(Count) div 4;
  For I := 1 to D do
    if PLongWord(P)^ = PLongWord(Q)^ then
      begin
        Inc(PLongWord(P));
        Inc(PLongWord(Q));
      end else
      begin
        Result := False;
        exit;
      end;
  D := LongWord(Count) and 3;
  For I := 1 to D do
    if PByte(P)^ = PByte(Q)^ then
      begin
        Inc(PByte(P));
        Inc(PByte(Q));
      end else
      begin
        Result := False;
        exit;
      end;
  Result := True;
end;
{$ENDIF}

function CompareMemNoCase(const Buf1; const Buf2; const Count: Integer): TCompareResult;
var P, Q : Pointer;
    I    : Integer;
    C, D : Byte;
begin
  if Count <= 0 then
    begin
      Result := crEqual;
      exit;
    end;
  P := @Buf1;
  Q := @Buf2;
  For I := 1 to Count do
    begin
      C := PByte(P)^;
      D := PByte(Q)^;
      if C in [Ord('A')..Ord('Z')] then
        C := C or 32;
      if D in [Ord('A')..Ord('Z')] then
        D := D or 32;
      if C = D then
        begin
          Inc(PByte(P));
          Inc(PByte(Q));
        end
      else
        begin
          if C < D then
            Result := crLess else
            Result := crGreater;
          exit;
        end;
    end;
  Result := crEqual;
end;

procedure ReverseMem(var Buf; const Size: Integer);
var I : Integer;
    P : PByte;
    Q : PByte;
    T : Byte;
begin
  P := @Buf;
  Q := P;
  Inc(Q, Size - 1);
  For I := 1 to Size div 2 do
    begin
      T := P^;
      P^ := Q^;
      Q^ := T;
      Inc(P);
      Dec(Q);
    end;
end;



{                                                                              }
{ Append                                                                       }
{                                                                              }
function Append(var V: ByteArray; const R: Byte): Integer;
begin
  Result := Length(V);
  SetLength(V, Result + 1);
  V[Result] := R;
end;

function Append(var V: WordArray; const R: Word): Integer;
begin
  Result := Length(V);
  SetLength(V, Result + 1);
  V[Result] := R;
end;

function Append(var V: LongWordArray; const R: LongWord): Integer;
begin
  Result := Length(V);
  SetLength(V, Result + 1);
  V[Result] := R;
end;

function Append(var V: ShortIntArray; const R: ShortInt): Integer;
begin
  Result := Length(V);
  SetLength(V, Result + 1);
  V[Result] := R;
end;

function Append(var V: SmallIntArray; const R: SmallInt): Integer;
begin
  Result := Length(V);
  SetLength(V, Result + 1);
  V[Result] := R;
end;

function Append(var V: LongIntArray; const R: LongInt): Integer;
begin
  Result := Length(V);
  SetLength(V, Result + 1);
  V[Result] := R;
end;

function Append(var V: Int64Array; const R: Int64): Integer;
begin
  Result := Length(V);
  SetLength(V, Result + 1);
  V[Result] := R;
end;

function Append(var V: SingleArray; const R: Single): Integer;
begin
  Result := Length(V);
  SetLength(V, Result + 1);
  V[Result] := R;
end;

function Append(var V: DoubleArray; const R: Double): Integer;
begin
  Result := Length(V);
  SetLength(V, Result + 1);
  V[Result] := R;
end;

function Append(var V: ExtendedArray; const R: Extended): Integer;
begin
  Result := Length(V);
  SetLength(V, Result + 1);
  V[Result] := R;
end;

function Append(var V: CurrencyArray; const R: Currency): Integer;
begin
  Result := Length(V);
  SetLength(V, Result + 1);
  V[Result] := R;
end;

function Append(var V: StringArray; const R: String): Integer;
begin
  Result := Length(V);
  SetLength(V, Result + 1);
  V[Result] := R;
end;

function Append(var V: WideStringArray; const R: WideString): Integer;
begin
  Result := Length(V);
  SetLength(V, Result + 1);
  V[Result] := R;
end;

function Append(var V: BooleanArray; const R: Boolean): Integer;
begin
  Result := Length(V);
  SetLength(V, Result + 1);
  V[Result] := R;
end;

function Append(var V: PointerArray; const R: Pointer): Integer;
begin
  Result := Length(V);
  SetLength(V, Result + 1);
  V[Result] := R;
end;

function Append(var V: ObjectArray; const R: TObject): Integer;
begin
  Result := Length(V);
  SetLength(V, Result + 1);
  V[Result] := R;
end;

function Append(var V: InterfaceArray; const R: IInterface): Integer;
begin
  Result := Length(V);
  SetLength(V, Result + 1);
  V[Result] := R;
end;

function Append(var V: ByteSetArray; const R: ByteSet): Integer;
begin
  Result := Length(V);
  SetLength(V, Result + 1);
  V[Result] := R;
end;

function Append(var V: CharSetArray; const R: CharSet): Integer;
begin
  Result := Length(V);
  SetLength(V, Result + 1);
  V[Result] := R;
end;


function AppendByteArray(var V: ByteArray; const R: Array of Byte): Integer;
var L : Integer;
begin
  Result := Length(V);
  L := Length(R);
  if L > 0 then
    begin
      SetLength(V, Result + L);
      Move(R[0], V[Result], Sizeof(Byte) * L);
    end;
end;

function AppendWordArray(var V: WordArray; const R: Array of Word): Integer;
var L : Integer;
begin
  Result := Length(V);
  L := Length(R);
  if L > 0 then
    begin
      SetLength(V, Result + L);
      Move(R[0], V[Result], Sizeof(Word) * L);
    end;
end;

function AppendCardinalArray(var V: CardinalArray; const R: Array of LongWord): Integer;
var L : Integer;
begin
  Result := Length(V);
  L := Length(R);
  if L > 0 then
    begin
      SetLength(V, Result + L);
      Move(R[0], V[Result], Sizeof(LongWord) * L);
    end;
end;

function AppendShortIntArray(var V: ShortIntArray; const R: Array of ShortInt): Integer;
var L : Integer;
begin
  Result := Length(V);
  L := Length(R);
  if L > 0 then
    begin
      SetLength(V, Result + L);
      Move(R[0], V[Result], Sizeof(ShortInt) * L);
    end;
end;

function AppendSmallIntArray(var V: SmallIntArray; const R: Array of SmallInt): Integer;
var L : Integer;
begin
  Result := Length(V);
  L := Length(R);
  if L > 0 then
    begin
      SetLength(V, Result + L);
      Move(R[0], V[Result], Sizeof(SmallInt) * L);
    end;
end;

function AppendIntegerArray(var V: IntegerArray; const R: Array of LongInt): Integer;
var L : Integer;
begin
  Result := Length(V);
  L := Length(R);
  if L > 0 then
    begin
      SetLength(V, Result + L);
      Move(R[0], V[Result], Sizeof(LongInt) * L);
    end;
end;

function AppendInt64Array(var V: Int64Array; const R: Array of Int64): Integer;
var L : Integer;
begin
  Result := Length(V);
  L := Length(R);
  if L > 0 then
    begin
      SetLength(V, Result + L);
      Move(R[0], V[Result], Sizeof(Int64) * L);
    end;
end;

function AppendSingleArray(var V: SingleArray; const R: Array of Single): Integer;
var L : Integer;
begin
  Result := Length(V);
  L := Length(R);
  if L > 0 then
    begin
      SetLength(V, Result + L);
      Move(R[0], V[Result], Sizeof(Single) * L);
    end;
end;

function AppendDoubleArray(var V: DoubleArray; const R: Array of Double): Integer;
var L : Integer;
begin
  Result := Length(V);
  L := Length(R);
  if L > 0 then
    begin
      SetLength(V, Result + L);
      Move(R[0], V[Result], Sizeof(Double) * L);
    end;
end;

function AppendExtendedArray(var V: ExtendedArray; const R: Array of Extended): Integer;
var L : Integer;
begin
  Result := Length(V);
  L := Length(R);
  if L > 0 then
    begin
      SetLength(V, Result + L);
      Move(R[0], V[Result], Sizeof(Extended) * L);
    end;
end;

function AppendCurrencyArray(var V: CurrencyArray; const R: Array of Currency): Integer;
var L : Integer;
begin
  Result := Length(V);
  L := Length(R);
  if L > 0 then
    begin
      SetLength(V, Result + L);
      Move(R[0], V[Result], Sizeof(Currency) * L);
    end;
end;

function AppendPointerArray(var V: PointerArray; const R: Array of Pointer): Integer;
var L : Integer;
begin
  Result := Length(V);
  L := Length(R);
  if L > 0 then
    begin
      SetLength(V, Result + L);
      Move(R[0], V[Result], Sizeof(Pointer) * L);
    end;
end;

function AppendCharSetArray(var V: CharSetArray; const R: Array of CharSet): Integer;
var L : Integer;
begin
  Result := Length(V);
  L := Length(R);
  if L > 0 then
    begin
      SetLength(V, Result + L);
      Move(R[0], V[Result], Sizeof(CharSet) * L);
    end;
end;

function AppendByteSetArray(var V: ByteSetArray; const R: Array of ByteSet): Integer;
var L : Integer;
begin
  Result := Length(V);
  L := Length(R);
  if L > 0 then
    begin
      SetLength(V, Result + L);
      Move(R[0], V[Result], Sizeof(ByteSet) * L);
    end;
end;


function AppendObjectArray(var V: ObjectArray; const R: ObjectArray): Integer;
var I, LR : Integer;
begin
  Result := Length(V);
  LR := Length(R);
  if LR > 0 then
    begin
      SetLength(V, Result + LR);
      For I := 0 to LR - 1 do
        V[Result + I] := R[I];
    end;
end;

function AppendStringArray(var V: StringArray; const R: Array of String): Integer;
var I, LR : Integer;
begin
  Result := Length(V);
  LR := Length(R);
  if LR > 0 then
    begin
      SetLength(V, Result + LR);
      For I := 0 to LR - 1 do
        V[Result + I] := R[I];
    end;
end;


  
{                                                                              }
{ FreeAndNil                                                                   }
{                                                                              }
procedure FreeAndNil(var Obj);
var Temp : TObject;
begin
  Temp := TObject(Obj);
  Pointer(Obj) := nil;
  Temp.Free;
end;



{                                                                              }
{ Remove                                                                       }
{                                                                              }
function Remove(var V: ByteArray; const Idx: Integer; const Count: Integer): Integer;
var I, J, L, M : Integer;
begin
  L := Length(V);
  if (Idx >= L) or (Idx + Count <= 0) or (L = 0) or (Count = 0) then
    begin
      Result := 0;
      exit;
    end;
  I := MaxI(Idx, 0);
  J := MinI(Count, L - I);
  M := L - J - I;
  if M > 0 then
    Move(V[I + J], V[I], M * SizeOf(Byte));
  SetLength(V, L - J);
  Result := J;
end;

function Remove(var V: WordArray; const Idx: Integer; const Count: Integer): Integer;
var I, J, L, M : Integer;
begin
  L := Length(V);
  if (Idx >= L) or (Idx + Count <= 0) or (L = 0) or (Count = 0) then
    begin
      Result := 0;
      exit;
    end;
  I := MaxI(Idx, 0);
  J := MinI(Count, L - I);
  M := L - J - I;
  if M > 0 then
    Move(V[I + J], V[I], M * SizeOf(Word));
  SetLength(V, L - J);
  Result := J;
end;

function Remove(var V: LongWordArray; const Idx: Integer; const Count: Integer): Integer;
var I, J, L, M : Integer;
begin
  L := Length(V);
  if (Idx >= L) or (Idx + Count <= 0) or (L = 0) or (Count = 0) then
    begin
      Result := 0;
      exit;
    end;
  I := MaxI(Idx, 0);
  J := MinI(Count, L - I);
  M := L - J - I;
  if M > 0 then
    Move(V[I + J], V[I], M * SizeOf(LongWord));
  SetLength(V, L - J);
  Result := J;
end;

function Remove(var V: ShortIntArray; const Idx: Integer; const Count: Integer): Integer;
var I, J, L, M : Integer;
begin
  L := Length(V);
  if (Idx >= L) or (Idx + Count <= 0) or (L = 0) or (Count = 0) then
    begin
      Result := 0;
      exit;
    end;
  I := MaxI(Idx, 0);
  J := MinI(Count, L - I);
  M := L - J - I;
  if M > 0 then
    Move(V[I + J], V[I], M * SizeOf(ShortInt));
  SetLength(V, L - J);
  Result := J;
end;

function Remove(var V: SmallIntArray; const Idx: Integer; const Count: Integer): Integer;
var I, J, L, M : Integer;
begin
  L := Length(V);
  if (Idx >= L) or (Idx + Count <= 0) or (L = 0) or (Count = 0) then
    begin
      Result := 0;
      exit;
    end;
  I := MaxI(Idx, 0);
  J := MinI(Count, L - I);
  M := L - J - I;
  if M > 0 then
    Move(V[I + J], V[I], M * SizeOf(SmallInt));
  SetLength(V, L - J);
  Result := J;
end;

function Remove(var V: LongIntArray; const Idx: Integer; const Count: Integer): Integer;
var I, J, L, M : Integer;
begin
  L := Length(V);
  if (Idx >= L) or (Idx + Count <= 0) or (L = 0) or (Count = 0) then
    begin
      Result := 0;
      exit;
    end;
  I := MaxI(Idx, 0);
  J := MinI(Count, L - I);
  M := L - J - I;
  if M > 0 then
    Move(V[I + J], V[I], M * SizeOf(LongInt));
  SetLength(V, L - J);
  Result := J;
end;

function Remove(var V: Int64Array; const Idx: Integer; const Count: Integer): Integer;
var I, J, L, M : Integer;
begin
  L := Length(V);
  if (Idx >= L) or (Idx + Count <= 0) or (L = 0) or (Count = 0) then
    begin
      Result := 0;
      exit;
    end;
  I := MaxI(Idx, 0);
  J := MinI(Count, L - I);
  M := L - J - I;
  if M > 0 then
    Move(V[I + J], V[I], M * SizeOf(Int64));
  SetLength(V, L - J);
  Result := J;
end;

function Remove(var V: SingleArray; const Idx: Integer; const Count: Integer): Integer;
var I, J, L, M : Integer;
begin
  L := Length(V);
  if (Idx >= L) or (Idx + Count <= 0) or (L = 0) or (Count = 0) then
    begin
      Result := 0;
      exit;
    end;
  I := MaxI(Idx, 0);
  J := MinI(Count, L - I);
  M := L - J - I;
  if M > 0 then
    Move(V[I + J], V[I], M * SizeOf(Single));
  SetLength(V, L - J);
  Result := J;
end;

function Remove(var V: DoubleArray; const Idx: Integer; const Count: Integer): Integer;
var I, J, L, M : Integer;
begin
  L := Length(V);
  if (Idx >= L) or (Idx + Count <= 0) or (L = 0) or (Count = 0) then
    begin
      Result := 0;
      exit;
    end;
  I := MaxI(Idx, 0);
  J := MinI(Count, L - I);
  M := L - J - I;
  if M > 0 then
    Move(V[I + J], V[I], M * SizeOf(Double));
  SetLength(V, L - J);
  Result := J;
end;

function Remove(var V: ExtendedArray; const Idx: Integer; const Count: Integer): Integer;
var I, J, L, M : Integer;
begin
  L := Length(V);
  if (Idx >= L) or (Idx + Count <= 0) or (L = 0) or (Count = 0) then
    begin
      Result := 0;
      exit;
    end;
  I := MaxI(Idx, 0);
  J := MinI(Count, L - I);
  M := L - J - I;
  if M > 0 then
    Move(V[I + J], V[I], M * SizeOf(Extended));
  SetLength(V, L - J);
  Result := J;
end;

function Remove(var V: CurrencyArray; const Idx: Integer; const Count: Integer): Integer;
var I, J, L, M : Integer;
begin
  L := Length(V);
  if (Idx >= L) or (Idx + Count <= 0) or (L = 0) or (Count = 0) then
    begin
      Result := 0;
      exit;
    end;
  I := MaxI(Idx, 0);
  J := MinI(Count, L - I);
  M := L - J - I;
  if M > 0 then
    Move(V[I + J], V[I], M * SizeOf(Currency));
  SetLength(V, L - J);
  Result := J;
end;

function Remove(var V: PointerArray; const Idx: Integer; const Count: Integer): Integer;
var I, J, L, M : Integer;
begin
  L := Length(V);
  if (Idx >= L) or (Idx + Count <= 0) or (L = 0) or (Count = 0) then
    begin
      Result := 0;
      exit;
    end;
  I := MaxI(Idx, 0);
  J := MinI(Count, L - I);
  M := L - J - I;
  if M > 0 then
    Move(V[I + J], V[I], M * SizeOf(Pointer));
  SetLength(V, L - J);
  Result := J;
end;


function Remove(var V: ObjectArray; const Idx: Integer; const Count: Integer; const FreeObjects: Boolean): Integer;
var I, J, K, L, M : Integer;
begin
  L := Length(V);
  if (Idx >= L) or (Idx + Count <= 0) or (L = 0) or (Count = 0) then
    begin
      Result := 0;
      exit;
    end;
  I := MaxI(Idx, 0);
  J := MinI(Count, L - I);
  if FreeObjects then
    For K := I to I + J - 1 do
      FreeAndNil(V[K]);
  M := L - J - I;
  if M > 0 then
    Move(V[I + J], V[I], M * SizeOf(Pointer));
  SetLength(V, L - J);
  Result := J;
end;

function Remove(var V: StringArray; const Idx: Integer; const Count: Integer): Integer;
var I, J, K, L : Integer;
begin
  L := Length(V);
  if (Idx >= L) or (Idx + Count <= 0) or (L = 0) or (Count = 0) then
    begin
      Result := 0;
      exit;
    end;
  I := MaxI(Idx, 0);
  J := MinI(Count, L - I);
  For K := I to L - J - 1 do
    V[K] := V[K + J];
  SetLength(V, L - J);
  Result := J;
end;

function Remove(var V: WideStringArray; const Idx: Integer; const Count: Integer): Integer;
var I, J, K, L : Integer;
begin
  L := Length(V);
  if (Idx >= L) or (Idx + Count <= 0) or (L = 0) or (Count = 0) then
    begin
      Result := 0;
      exit;
    end;
  I := MaxI(Idx, 0);
  J := MinI(Count, L - I);
  For K := I to L - J - 1 do
    V[K] := V[K + J];
  SetLength(V, L - J);
  Result := J;
end;

function Remove(var V: InterfaceArray; const Idx: Integer; const Count: Integer): Integer;
var I, J, K, L, M : Integer;
begin
  L := Length(V);
  if (Idx >= L) or (Idx + Count <= 0) or (L = 0) or (Count = 0) then
    begin
      Result := 0;
      exit;
    end;
  I := MaxI(Idx, 0);
  J := MinI(Count, L - I);
  For K := I to I + J - 1 do
    V[K] := nil;
  M := L - J - I;
  if M > 0 then
    Move(V[I + J], V[I], M * SizeOf(IInterface));
  FillChar(V[L - J], J * SizeOf(IInterface), #0);
  SetLength(V, L - J);
  Result := J;
end;

procedure FreeObjectArray(var V);
var I : Integer;
    A : ObjectArray absolute V;
begin
  For I := Length(A) - 1 downto 0 do
    FreeAndNil(A[I]);
end;

procedure FreeObjectArray(var V; const LoIdx, HiIdx: Integer);
var I : Integer;
    A : ObjectArray absolute V;
begin
  For I := HiIdx downto LoIdx do
    FreeAndNil(A[I]);
end;

// Note: The parameter can not be changed to be untyped and then typecasted
// using an absolute variable, as in FreeObjectArray. The reference counting
// will be done incorrectly.
procedure FreeAndNilObjectArray(var V: ObjectArray);
var W : ObjectArray;
begin
  W := V;
  V := nil;
  FreeObjectArray(W);
end;


{                                                                              }
{ RemoveDuplicates                                                             }
{                                                                              }
procedure RemoveDuplicates(var V: ByteArray; const IsSorted: Boolean);
var I, C, J, L : Integer;
    F          : Byte;
begin
  L := Length(V);
  if L = 0 then
    exit;

  if IsSorted then
    begin
      J := 0;
      Repeat
        F := V[J];
        I := J + 1;
        While (I < L) and (V[I] = F) do
          Inc(I);
        C := I - J;
        if C > 1 then
          begin
            Remove(V, J + 1, C - 1);
            Dec(L, C - 1);
            Inc(J);
          end
        else
          J := I;
      Until J >= L;
    end else
    begin
      J := 0;
      Repeat
        Repeat
          I := PosNext(V[J], V, J);
          if I >= 0 then
            Remove(V, I, 1);
        Until I < 0;
        Inc(J);
      Until J >= Length(V);
    end;
end;

procedure RemoveDuplicates(var V: WordArray; const IsSorted: Boolean);
var I, C, J, L : Integer;
    F          : Word;
begin
  L := Length(V);
  if L = 0 then
    exit;

  if IsSorted then
    begin
      J := 0;
      Repeat
        F := V[J];
        I := J + 1;
        While (I < L) and (V[I] = F) do
          Inc(I);
        C := I - J;
        if C > 1 then
          begin
            Remove(V, J + 1, C - 1);
            Dec(L, C - 1);
            Inc(J);
          end
        else
          J := I;
      Until J >= L;
    end else
    begin
      J := 0;
      Repeat
        Repeat
          I := PosNext(V[J], V, J);
          if I >= 0 then
            Remove(V, I, 1);
        Until I < 0;
        Inc(J);
      Until J >= Length(V);
    end;
end;

procedure RemoveDuplicates(var V: LongWordArray; const IsSorted: Boolean);
var I, C, J, L : Integer;
    F          : LongWord;
begin
  L := Length(V);
  if L = 0 then
    exit;

  if IsSorted then
    begin
      J := 0;
      Repeat
        F := V[J];
        I := J + 1;
        While (I < L) and (V[I] = F) do
          Inc(I);
        C := I - J;
        if C > 1 then
          begin
            Remove(V, J + 1, C - 1);
            Dec(L, C - 1);
            Inc(J);
          end
        else
          J := I;
      Until J >= L;
    end else
    begin
      J := 0;
      Repeat
        Repeat
          I := PosNext(V[J], V, J);
          if I >= 0 then
            Remove(V, I, 1);
        Until I < 0;
        Inc(J);
      Until J >= Length(V);
    end;
end;

procedure RemoveDuplicates(var V: ShortIntArray; const IsSorted: Boolean);
var I, C, J, L : Integer;
    F          : ShortInt;
begin
  L := Length(V);
  if L = 0 then
    exit;

  if IsSorted then
    begin
      J := 0;
      Repeat
        F := V[J];
        I := J + 1;
        While (I < L) and (V[I] = F) do
          Inc(I);
        C := I - J;
        if C > 1 then
          begin
            Remove(V, J + 1, C - 1);
            Dec(L, C - 1);
            Inc(J);
          end
        else
          J := I;
      Until J >= L;
    end else
    begin
      J := 0;
      Repeat
        Repeat
          I := PosNext(V[J], V, J);
          if I >= 0 then
            Remove(V, I, 1);
        Until I < 0;
        Inc(J);
      Until J >= Length(V);
    end;
end;

procedure RemoveDuplicates(var V: SmallIntArray; const IsSorted: Boolean);
var I, C, J, L : Integer;
    F          : SmallInt;
begin
  L := Length(V);
  if L = 0 then
    exit;

  if IsSorted then
    begin
      J := 0;
      Repeat
        F := V[J];
        I := J + 1;
        While (I < L) and (V[I] = F) do
          Inc(I);
        C := I - J;
        if C > 1 then
          begin
            Remove(V, J + 1, C - 1);
            Dec(L, C - 1);
            Inc(J);
          end
        else
          J := I;
      Until J >= L;
    end else
    begin
      J := 0;
      Repeat
        Repeat
          I := PosNext(V[J], V, J);
          if I >= 0 then
            Remove(V, I, 1);
        Until I < 0;
        Inc(J);
      Until J >= Length(V);
    end;
end;

procedure RemoveDuplicates(var V: LongIntArray; const IsSorted: Boolean);
var I, C, J, L : Integer;
    F          : LongInt;
begin
  L := Length(V);
  if L = 0 then
    exit;

  if IsSorted then
    begin
      J := 0;
      Repeat
        F := V[J];
        I := J + 1;
        While (I < L) and (V[I] = F) do
          Inc(I);
        C := I - J;
        if C > 1 then
          begin
            Remove(V, J + 1, C - 1);
            Dec(L, C - 1);
            Inc(J);
          end
        else
          J := I;
      Until J >= L;
    end else
    begin
      J := 0;
      Repeat
        Repeat
          I := PosNext(V[J], V, J);
          if I >= 0 then
            Remove(V, I, 1);
        Until I < 0;
        Inc(J);
      Until J >= Length(V);
    end;
end;

procedure RemoveDuplicates(var V: Int64Array; const IsSorted: Boolean);
var I, C, J, L : Integer;
    F          : Int64;
begin
  L := Length(V);
  if L = 0 then
    exit;

  if IsSorted then
    begin
      J := 0;
      Repeat
        F := V[J];
        I := J + 1;
        While (I < L) and (V[I] = F) do
          Inc(I);
        C := I - J;
        if C > 1 then
          begin
            Remove(V, J + 1, C - 1);
            Dec(L, C - 1);
            Inc(J);
          end
        else
          J := I;
      Until J >= L;
    end else
    begin
      J := 0;
      Repeat
        Repeat
          I := PosNext(V[J], V, J);
          if I >= 0 then
            Remove(V, I, 1);
        Until I < 0;
        Inc(J);
      Until J >= Length(V);
    end;
end;

procedure RemoveDuplicates(var V: SingleArray; const IsSorted: Boolean);
var I, C, J, L : Integer;
    F          : Single;
begin
  L := Length(V);
  if L = 0 then
    exit;

  if IsSorted then
    begin
      J := 0;
      Repeat
        F := V[J];
        I := J + 1;
        While (I < L) and (V[I] = F) do
          Inc(I);
        C := I - J;
        if C > 1 then
          begin
            Remove(V, J + 1, C - 1);
            Dec(L, C - 1);
            Inc(J);
          end
        else
          J := I;
      Until J >= L;
    end else
    begin
      J := 0;
      Repeat
        Repeat
          I := PosNext(V[J], V, J);
          if I >= 0 then
            Remove(V, I, 1);
        Until I < 0;
        Inc(J);
      Until J >= Length(V);
    end;
end;

procedure RemoveDuplicates(var V: DoubleArray; const IsSorted: Boolean);
var I, C, J, L : Integer;
    F          : Double;
begin
  L := Length(V);
  if L = 0 then
    exit;

  if IsSorted then
    begin
      J := 0;
      Repeat
        F := V[J];
        I := J + 1;
        While (I < L) and (V[I] = F) do
          Inc(I);
        C := I - J;
        if C > 1 then
          begin
            Remove(V, J + 1, C - 1);
            Dec(L, C - 1);
            Inc(J);
          end
        else
          J := I;
      Until J >= L;
    end else
    begin
      J := 0;
      Repeat
        Repeat
          I := PosNext(V[J], V, J);
          if I >= 0 then
            Remove(V, I, 1);
        Until I < 0;
        Inc(J);
      Until J >= Length(V);
    end;
end;

procedure RemoveDuplicates(var V: ExtendedArray; const IsSorted: Boolean);
var I, C, J, L : Integer;
    F          : Extended;
begin
  L := Length(V);
  if L = 0 then
    exit;

  if IsSorted then
    begin
      J := 0;
      Repeat
        F := V[J];
        I := J + 1;
        While (I < L) and (V[I] = F) do
          Inc(I);
        C := I - J;
        if C > 1 then
          begin
            Remove(V, J + 1, C - 1);
            Dec(L, C - 1);
            Inc(J);
          end
        else
          J := I;
      Until J >= L;
    end else
    begin
      J := 0;
      Repeat
        Repeat
          I := PosNext(V[J], V, J);
          if I >= 0 then
            Remove(V, I, 1);
        Until I < 0;
        Inc(J);
      Until J >= Length(V);
    end;
end;

procedure RemoveDuplicates(var V: StringArray; const IsSorted: Boolean);
var I, C, J, L : Integer;
    F          : String;
begin
  L := Length(V);
  if L = 0 then
    exit;

  if IsSorted then
    begin
      J := 0;
      Repeat
        F := V[J];
        I := J + 1;
        While (I < L) and (V[I] = F) do
          Inc(I);
        C := I - J;
        if C > 1 then
          begin
            Remove(V, J + 1, C - 1);
            Dec(L, C - 1);
            Inc(J);
          end
        else
          J := I;
      Until J >= L;
    end else
    begin
      J := 0;
      Repeat
        Repeat
          I := PosNext(V[J], V, J);
          if I >= 0 then
            Remove(V, I, 1);
        Until I < 0;
        Inc(J);
      Until J >= Length(V);
    end;
end;

procedure RemoveDuplicates(var V: PointerArray; const IsSorted: Boolean);
var I, C, J, L : Integer;
    F          : Pointer;
begin
  L := Length(V);
  if L = 0 then
    exit;

  if IsSorted then
    begin
      J := 0;
      Repeat
        F := V[J];
        I := J + 1;
        While (I < L) and (V[I] = F) do
          Inc(I);
        C := I - J;
        if C > 1 then
          begin
            Remove(V, J + 1, C - 1);
            Dec(L, C - 1);
            Inc(J);
          end
        else
          J := I;
      Until J >= L;
    end else
    begin
      J := 0;
      Repeat
        Repeat
          I := PosNext(V[J], V, J);
          if I >= 0 then
            Remove(V, I, 1);
        Until I < 0;
        Inc(J);
      Until J >= Length(V);
    end;
end;



procedure TrimArrayLeft(var S: ByteArray; const TrimList: Array of Byte); overload;
var I, J : Integer;
    R    : Boolean;
begin
  I := 0;
  R := True;
  While R and (I < Length(S)) do
    begin
      R := False;
      For J := 0 to High(TrimList) do
        if S[I] = TrimList[J] then
          begin
            R := True;
            Inc(I);
            break;
          end;
    end;
  if I > 0 then
    Remove(S, 0, I - 1);
end;


procedure TrimArrayLeft(var S: WordArray; const TrimList: Array of Word); overload;
var I, J : Integer;
    R    : Boolean;
begin
  I := 0;
  R := True;
  While R and (I < Length(S)) do
    begin
      R := False;
      For J := 0 to High(TrimList) do
        if S[I] = TrimList[J] then
          begin
            R := True;
            Inc(I);
            break;
          end;
    end;
  if I > 0 then
    Remove(S, 0, I - 1);
end;


procedure TrimArrayLeft(var S: LongWordArray; const TrimList: Array of LongWord); overload;
var I, J : Integer;
    R    : Boolean;
begin
  I := 0;
  R := True;
  While R and (I < Length(S)) do
    begin
      R := False;
      For J := 0 to High(TrimList) do
        if S[I] = TrimList[J] then
          begin
            R := True;
            Inc(I);
            break;
          end;
    end;
  if I > 0 then
    Remove(S, 0, I - 1);
end;


procedure TrimArrayLeft(var S: ShortIntArray; const TrimList: Array of ShortInt); overload;
var I, J : Integer;
    R    : Boolean;
begin
  I := 0;
  R := True;
  While R and (I < Length(S)) do
    begin
      R := False;
      For J := 0 to High(TrimList) do
        if S[I] = TrimList[J] then
          begin
            R := True;
            Inc(I);
            break;
          end;
    end;
  if I > 0 then
    Remove(S, 0, I - 1);
end;


procedure TrimArrayLeft(var S: SmallIntArray; const TrimList: Array of SmallInt); overload;
var I, J : Integer;
    R    : Boolean;
begin
  I := 0;
  R := True;
  While R and (I < Length(S)) do
    begin
      R := False;
      For J := 0 to High(TrimList) do
        if S[I] = TrimList[J] then
          begin
            R := True;
            Inc(I);
            break;
          end;
    end;
  if I > 0 then
    Remove(S, 0, I - 1);
end;


procedure TrimArrayLeft(var S: LongIntArray; const TrimList: Array of LongInt); overload;
var I, J : Integer;
    R    : Boolean;
begin
  I := 0;
  R := True;
  While R and (I < Length(S)) do
    begin
      R := False;
      For J := 0 to High(TrimList) do
        if S[I] = TrimList[J] then
          begin
            R := True;
            Inc(I);
            break;
          end;
    end;
  if I > 0 then
    Remove(S, 0, I - 1);
end;


procedure TrimArrayLeft(var S: Int64Array; const TrimList: Array of Int64); overload;
var I, J : Integer;
    R    : Boolean;
begin
  I := 0;
  R := True;
  While R and (I < Length(S)) do
    begin
      R := False;
      For J := 0 to High(TrimList) do
        if S[I] = TrimList[J] then
          begin
            R := True;
            Inc(I);
            break;
          end;
    end;
  if I > 0 then
    Remove(S, 0, I - 1);
end;


procedure TrimArrayLeft(var S: SingleArray; const TrimList: Array of Single); overload;
var I, J : Integer;
    R    : Boolean;
begin
  I := 0;
  R := True;
  While R and (I < Length(S)) do
    begin
      R := False;
      For J := 0 to High(TrimList) do
        if S[I] = TrimList[J] then
          begin
            R := True;
            Inc(I);
            break;
          end;
    end;
  if I > 0 then
    Remove(S, 0, I - 1);
end;


procedure TrimArrayLeft(var S: DoubleArray; const TrimList: Array of Double); overload;
var I, J : Integer;
    R    : Boolean;
begin
  I := 0;
  R := True;
  While R and (I < Length(S)) do
    begin
      R := False;
      For J := 0 to High(TrimList) do
        if S[I] = TrimList[J] then
          begin
            R := True;
            Inc(I);
            break;
          end;
    end;
  if I > 0 then
    Remove(S, 0, I - 1);
end;


procedure TrimArrayLeft(var S: ExtendedArray; const TrimList: Array of Extended); overload;
var I, J : Integer;
    R    : Boolean;
begin
  I := 0;
  R := True;
  While R and (I < Length(S)) do
    begin
      R := False;
      For J := 0 to High(TrimList) do
        if S[I] = TrimList[J] then
          begin
            R := True;
            Inc(I);
            break;
          end;
    end;
  if I > 0 then
    Remove(S, 0, I - 1);
end;


procedure TrimArrayLeft(var S: StringArray; const TrimList: Array of String); overload;
var I, J : Integer;
    R    : Boolean;
begin
  I := 0;
  R := True;
  While R and (I < Length(S)) do
    begin
      R := False;
      For J := 0 to High(TrimList) do
        if S[I] = TrimList[J] then
          begin
            R := True;
            Inc(I);
            break;
          end;
    end;
  if I > 0 then
    Remove(S, 0, I - 1);
end;


procedure TrimArrayLeft(var S: PointerArray; const TrimList: Array of Pointer); overload;
var I, J : Integer;
    R    : Boolean;
begin
  I := 0;
  R := True;
  While R and (I < Length(S)) do
    begin
      R := False;
      For J := 0 to High(TrimList) do
        if S[I] = TrimList[J] then
          begin
            R := True;
            Inc(I);
            break;
          end;
    end;
  if I > 0 then
    Remove(S, 0, I - 1);
end;



procedure TrimArrayRight(var S: ByteArray; const TrimList: Array of Byte); overload;
var I, J : Integer;
    R    : Boolean;
begin
  I := Length(S) - 1;
  R := True;
  While R and (I >= 0) do
    begin
      R := False;
      For J := 0 to High(TrimList) do
        if S[I] = TrimList[J] then
          begin
            R := True;
            Dec(I);
            break;
          end;
    end;
  if I < Length(S) - 1 then
    SetLength(S, I + 1);
end;


procedure TrimArrayRight(var S: WordArray; const TrimList: Array of Word); overload;
var I, J : Integer;
    R    : Boolean;
begin
  I := Length(S) - 1;
  R := True;
  While R and (I >= 0) do
    begin
      R := False;
      For J := 0 to High(TrimList) do
        if S[I] = TrimList[J] then
          begin
            R := True;
            Dec(I);
            break;
          end;
    end;
  if I < Length(S) - 1 then
    SetLength(S, I + 1);
end;


procedure TrimArrayRight(var S: LongWordArray; const TrimList: Array of LongWord); overload;
var I, J : Integer;
    R    : Boolean;
begin
  I := Length(S) - 1;
  R := True;
  While R and (I >= 0) do
    begin
      R := False;
      For J := 0 to High(TrimList) do
        if S[I] = TrimList[J] then
          begin
            R := True;
            Dec(I);
            break;
          end;
    end;
  if I < Length(S) - 1 then
    SetLength(S, I + 1);
end;


procedure TrimArrayRight(var S: ShortIntArray; const TrimList: Array of ShortInt); overload;
var I, J : Integer;
    R    : Boolean;
begin
  I := Length(S) - 1;
  R := True;
  While R and (I >= 0) do
    begin
      R := False;
      For J := 0 to High(TrimList) do
        if S[I] = TrimList[J] then
          begin
            R := True;
            Dec(I);
            break;
          end;
    end;
  if I < Length(S) - 1 then
    SetLength(S, I + 1);
end;


procedure TrimArrayRight(var S: SmallIntArray; const TrimList: Array of SmallInt); overload;
var I, J : Integer;
    R    : Boolean;
begin
  I := Length(S) - 1;
  R := True;
  While R and (I >= 0) do
    begin
      R := False;
      For J := 0 to High(TrimList) do
        if S[I] = TrimList[J] then
          begin
            R := True;
            Dec(I);
            break;
          end;
    end;
  if I < Length(S) - 1 then
    SetLength(S, I + 1);
end;


procedure TrimArrayRight(var S: LongIntArray; const TrimList: Array of LongInt); overload;
var I, J : Integer;
    R    : Boolean;
begin
  I := Length(S) - 1;
  R := True;
  While R and (I >= 0) do
    begin
      R := False;
      For J := 0 to High(TrimList) do
        if S[I] = TrimList[J] then
          begin
            R := True;
            Dec(I);
            break;
          end;
    end;
  if I < Length(S) - 1 then
    SetLength(S, I + 1);
end;


procedure TrimArrayRight(var S: Int64Array; const TrimList: Array of Int64); overload;
var I, J : Integer;
    R    : Boolean;
begin
  I := Length(S) - 1;
  R := True;
  While R and (I >= 0) do
    begin
      R := False;
      For J := 0 to High(TrimList) do
        if S[I] = TrimList[J] then
          begin
            R := True;
            Dec(I);
            break;
          end;
    end;
  if I < Length(S) - 1 then
    SetLength(S, I + 1);
end;


procedure TrimArrayRight(var S: SingleArray; const TrimList: Array of Single); overload;
var I, J : Integer;
    R    : Boolean;
begin
  I := Length(S) - 1;
  R := True;
  While R and (I >= 0) do
    begin
      R := False;
      For J := 0 to High(TrimList) do
        if S[I] = TrimList[J] then
          begin
            R := True;
            Dec(I);
            break;
          end;
    end;
  if I < Length(S) - 1 then
    SetLength(S, I + 1);
end;


procedure TrimArrayRight(var S: DoubleArray; const TrimList: Array of Double); overload;
var I, J : Integer;
    R    : Boolean;
begin
  I := Length(S) - 1;
  R := True;
  While R and (I >= 0) do
    begin
      R := False;
      For J := 0 to High(TrimList) do
        if S[I] = TrimList[J] then
          begin
            R := True;
            Dec(I);
            break;
          end;
    end;
  if I < Length(S) - 1 then
    SetLength(S, I + 1);
end;


procedure TrimArrayRight(var S: ExtendedArray; const TrimList: Array of Extended); overload;
var I, J : Integer;
    R    : Boolean;
begin
  I := Length(S) - 1;
  R := True;
  While R and (I >= 0) do
    begin
      R := False;
      For J := 0 to High(TrimList) do
        if S[I] = TrimList[J] then
          begin
            R := True;
            Dec(I);
            break;
          end;
    end;
  if I < Length(S) - 1 then
    SetLength(S, I + 1);
end;


procedure TrimArrayRight(var S: StringArray; const TrimList: Array of String); overload;
var I, J : Integer;
    R    : Boolean;
begin
  I := Length(S) - 1;
  R := True;
  While R and (I >= 0) do
    begin
      R := False;
      For J := 0 to High(TrimList) do
        if S[I] = TrimList[J] then
          begin
            R := True;
            Dec(I);
            break;
          end;
    end;
  if I < Length(S) - 1 then
    SetLength(S, I + 1);
end;


procedure TrimArrayRight(var S: PointerArray; const TrimList: Array of Pointer); overload;
var I, J : Integer;
    R    : Boolean;
begin
  I := Length(S) - 1;
  R := True;
  While R and (I >= 0) do
    begin
      R := False;
      For J := 0 to High(TrimList) do
        if S[I] = TrimList[J] then
          begin
            R := True;
            Dec(I);
            break;
          end;
    end;
  if I < Length(S) - 1 then
    SetLength(S, I + 1);
end;



{                                                                              }
{ ArrayInsert                                                                  }
{                                                                              }
function ArrayInsert(var V: ByteArray; const Idx: Integer; const Count: Integer): Integer;
var I, L : Integer;
    P    : Pointer;
begin
  L := Length(V);
  if (Idx > L) or (Idx + Count <= 0) or (Count <= 0) then
    begin
      Result := -1;
      exit;
    end;
  SetLength(V, L + Count);
  I := Idx;
  if I < 0 then
    I := 0;
  P := @V[I];
  if I < L then
    Move(P^, V[I + Count], (L - I) * Sizeof(Byte));
  FillChar(P^, Count * Sizeof(Byte), #0);
  Result := I;
end;

function ArrayInsert(var V: WordArray; const Idx: Integer; const Count: Integer): Integer;
var I, L : Integer;
    P    : Pointer;
begin
  L := Length(V);
  if (Idx > L) or (Idx + Count <= 0) or (Count <= 0) then
    begin
      Result := -1;
      exit;
    end;
  SetLength(V, L + Count);
  I := Idx;
  if I < 0 then
    I := 0;
  P := @V[I];
  if I < L then
    Move(P^, V[I + Count], (L - I) * Sizeof(Word));
  FillChar(P^, Count * Sizeof(Word), #0);
  Result := I;
end;

function ArrayInsert(var V: LongWordArray; const Idx: Integer; const Count: Integer): Integer;
var I, L : Integer;
    P    : Pointer;
begin
  L := Length(V);
  if (Idx > L) or (Idx + Count <= 0) or (Count <= 0) then
    begin
      Result := -1;
      exit;
    end;
  SetLength(V, L + Count);
  I := Idx;
  if I < 0 then
    I := 0;
  P := @V[I];
  if I < L then
    Move(P^, V[I + Count], (L - I) * Sizeof(LongWord));
  FillChar(P^, Count * Sizeof(LongWord), #0);
  Result := I;
end;

function ArrayInsert(var V: ShortIntArray; const Idx: Integer; const Count: Integer): Integer;
var I, L : Integer;
    P    : Pointer;
begin
  L := Length(V);
  if (Idx > L) or (Idx + Count <= 0) or (Count <= 0) then
    begin
      Result := -1;
      exit;
    end;
  SetLength(V, L + Count);
  I := Idx;
  if I < 0 then
    I := 0;
  P := @V[I];
  if I < L then
    Move(P^, V[I + Count], (L - I) * Sizeof(ShortInt));
  FillChar(P^, Count * Sizeof(ShortInt), #0);
  Result := I;
end;

function ArrayInsert(var V: SmallIntArray; const Idx: Integer; const Count: Integer): Integer;
var I, L : Integer;
    P    : Pointer;
begin
  L := Length(V);
  if (Idx > L) or (Idx + Count <= 0) or (Count <= 0) then
    begin
      Result := -1;
      exit;
    end;
  SetLength(V, L + Count);
  I := Idx;
  if I < 0 then
    I := 0;
  P := @V[I];
  if I < L then
    Move(P^, V[I + Count], (L - I) * Sizeof(SmallInt));
  FillChar(P^, Count * Sizeof(SmallInt), #0);
  Result := I;
end;

function ArrayInsert(var V: LongIntArray; const Idx: Integer; const Count: Integer): Integer;
var I, L : Integer;
    P    : Pointer;
begin
  L := Length(V);
  if (Idx > L) or (Idx + Count <= 0) or (Count <= 0) then
    begin
      Result := -1;
      exit;
    end;
  SetLength(V, L + Count);
  I := Idx;
  if I < 0 then
    I := 0;
  P := @V[I];
  if I < L then
    Move(P^, V[I + Count], (L - I) * Sizeof(LongInt));
  FillChar(P^, Count * Sizeof(LongInt), #0);
  Result := I;
end;

function ArrayInsert(var V: Int64Array; const Idx: Integer; const Count: Integer): Integer;
var I, L : Integer;
    P    : Pointer;
begin
  L := Length(V);
  if (Idx > L) or (Idx + Count <= 0) or (Count <= 0) then
    begin
      Result := -1;
      exit;
    end;
  SetLength(V, L + Count);
  I := Idx;
  if I < 0 then
    I := 0;
  P := @V[I];
  if I < L then
    Move(P^, V[I + Count], (L - I) * Sizeof(Int64));
  FillChar(P^, Count * Sizeof(Int64), #0);
  Result := I;
end;

function ArrayInsert(var V: SingleArray; const Idx: Integer; const Count: Integer): Integer;
var I, L : Integer;
    P    : Pointer;
begin
  L := Length(V);
  if (Idx > L) or (Idx + Count <= 0) or (Count <= 0) then
    begin
      Result := -1;
      exit;
    end;
  SetLength(V, L + Count);
  I := Idx;
  if I < 0 then
    I := 0;
  P := @V[I];
  if I < L then
    Move(P^, V[I + Count], (L - I) * Sizeof(Single));
  FillChar(P^, Count * Sizeof(Single), #0);
  Result := I;
end;

function ArrayInsert(var V: DoubleArray; const Idx: Integer; const Count: Integer): Integer;
var I, L : Integer;
    P    : Pointer;
begin
  L := Length(V);
  if (Idx > L) or (Idx + Count <= 0) or (Count <= 0) then
    begin
      Result := -1;
      exit;
    end;
  SetLength(V, L + Count);
  I := Idx;
  if I < 0 then
    I := 0;
  P := @V[I];
  if I < L then
    Move(P^, V[I + Count], (L - I) * Sizeof(Double));
  FillChar(P^, Count * Sizeof(Double), #0);
  Result := I;
end;

function ArrayInsert(var V: ExtendedArray; const Idx: Integer; const Count: Integer): Integer;
var I, L : Integer;
    P    : Pointer;
begin
  L := Length(V);
  if (Idx > L) or (Idx + Count <= 0) or (Count <= 0) then
    begin
      Result := -1;
      exit;
    end;
  SetLength(V, L + Count);
  I := Idx;
  if I < 0 then
    I := 0;
  P := @V[I];
  if I < L then
    Move(P^, V[I + Count], (L - I) * Sizeof(Extended));
  FillChar(P^, Count * Sizeof(Extended), #0);
  Result := I;
end;

function ArrayInsert(var V: CurrencyArray; const Idx: Integer; const Count: Integer): Integer;
var I, L : Integer;
    P    : Pointer;
begin
  L := Length(V);
  if (Idx > L) or (Idx + Count <= 0) or (Count <= 0) then
    begin
      Result := -1;
      exit;
    end;
  SetLength(V, L + Count);
  I := Idx;
  if I < 0 then
    I := 0;
  P := @V[I];
  if I < L then
    Move(P^, V[I + Count], (L - I) * Sizeof(Currency));
  FillChar(P^, Count * Sizeof(Currency), #0);
  Result := I;
end;

function ArrayInsert(var V: StringArray; const Idx: Integer; const Count: Integer): Integer;
var I, L : Integer;
    P    : Pointer;
begin
  L := Length(V);
  if (Idx > L) or (Idx + Count <= 0) or (Count <= 0) then
    begin
      Result := -1;
      exit;
    end;
  SetLength(V, L + Count);
  I := Idx;
  if I < 0 then
    I := 0;
  P := @V[I];
  if I < L then
    Move(P^, V[I + Count], (L - I) * Sizeof(String));
  FillChar(P^, Count * Sizeof(String), #0);
  Result := I;
end;

function ArrayInsert(var V: WideStringArray; const Idx: Integer; const Count: Integer): Integer;
var I, L : Integer;
    P    : Pointer;
begin
  L := Length(V);
  if (Idx > L) or (Idx + Count <= 0) or (Count <= 0) then
    begin
      Result := -1;
      exit;
    end;
  SetLength(V, L + Count);
  I := Idx;
  if I < 0 then
    I := 0;
  P := @V[I];
  if I < L then
    Move(P^, V[I + Count], (L - I) * Sizeof(WideString));
  FillChar(P^, Count * Sizeof(WideString), #0);
  Result := I;
end;

function ArrayInsert(var V: PointerArray; const Idx: Integer; const Count: Integer): Integer;
var I, L : Integer;
    P    : Pointer;
begin
  L := Length(V);
  if (Idx > L) or (Idx + Count <= 0) or (Count <= 0) then
    begin
      Result := -1;
      exit;
    end;
  SetLength(V, L + Count);
  I := Idx;
  if I < 0 then
    I := 0;
  P := @V[I];
  if I < L then
    Move(P^, V[I + Count], (L - I) * Sizeof(Pointer));
  FillChar(P^, Count * Sizeof(Pointer), #0);
  Result := I;
end;

function ArrayInsert(var V: ObjectArray; const Idx: Integer; const Count: Integer): Integer;
var I, L : Integer;
    P    : Pointer;
begin
  L := Length(V);
  if (Idx > L) or (Idx + Count <= 0) or (Count <= 0) then
    begin
      Result := -1;
      exit;
    end;
  SetLength(V, L + Count);
  I := Idx;
  if I < 0 then
    I := 0;
  P := @V[I];
  if I < L then
    Move(P^, V[I + Count], (L - I) * Sizeof(Pointer));
  FillChar(P^, Count * Sizeof(Pointer), #0);
  Result := I;
end;

function ArrayInsert(var V: InterfaceArray; const Idx: Integer; const Count: Integer): Integer;
var I, L : Integer;
    P    : Pointer;
begin
  L := Length(V);
  if (Idx > L) or (Idx + Count <= 0) or (Count <= 0) then
    begin
      Result := -1;
      exit;
    end;
  SetLength(V, L + Count);
  I := Idx;
  if I < 0 then
    I := 0;
  P := @V[I];
  if I < L then
    Move(P^, V[I + Count], (L - I) * Sizeof(IInterface));
  FillChar(P^, Count * Sizeof(IInterface), #0);
  Result := I;
end;



{                                                                              }
{ PosNext                                                                      }
{   PosNext finds the next occurance of Find in V, -1 if it was not found.     }
{     Searches from Item[PrevPos + 1], ie PrevPos = -1 to find first           }
{     occurance.                                                               }
{                                                                              }
function PosNext(const Find: Byte; const V: ByteArray; const PrevPos: Integer;
    const IsSortedAscending: Boolean): Integer;
var I, L, H : Integer;
    D       : Byte;
begin
  if IsSortedAscending then // binary search
    begin
      if MaxI(PrevPos + 1, 0) = 0 then // find first
        begin
          L := 0;
          H := Length(V) - 1;
          While L <= H do
            begin
              I := (L + H) div 2;
              D := V[I];
              if Find = D then
                begin
                  While (I > 0) and (V[I - 1] = Find) do
                    Dec(I);
                  Result := I;
                  exit;
                end else
              if D > Find then
                H := I - 1
              else
                L := I + 1;
            end;
          Result := -1;
        end
      else // find next
        if PrevPos >= Length(V) - 1 then
          Result := -1
        else
          if V[PrevPos + 1] = Find then
            Result := PrevPos + 1
          else
            Result := -1;
    end
  else
    begin // linear search
      For I := MaxI(PrevPos + 1, 0) to Length(V) - 1 do
        if V[I] = Find then
          begin
            Result := I;
            exit;
          end;
      Result := -1;
    end;
end;

function PosNext(const Find: Word; const V: WordArray; const PrevPos: Integer;
    const IsSortedAscending: Boolean): Integer;
var I, L, H : Integer;
    D       : Word;
begin
  if IsSortedAscending then // binary search
    begin
      if MaxI(PrevPos + 1, 0) = 0 then // find first
        begin
          L := 0;
          H := Length(V) - 1;
          While L <= H do
            begin
              I := (L + H) div 2;
              D := V[I];
              if Find = D then
                begin
                  While (I > 0) and (V[I - 1] = Find) do
                    Dec(I);
                  Result := I;
                  exit;
                end else
              if D > Find then
                H := I - 1
              else
                L := I + 1;
            end;
          Result := -1;
        end
      else // find next
        if PrevPos >= Length(V) - 1 then
          Result := -1
        else
          if V[PrevPos + 1] = Find then
            Result := PrevPos + 1
          else
            Result := -1;
    end
  else
    begin // linear search
      For I := MaxI(PrevPos + 1, 0) to Length(V) - 1 do
        if V[I] = Find then
          begin
            Result := I;
            exit;
          end;
      Result := -1;
    end;
end;

function PosNext(const Find: LongWord; const V: LongWordArray; const PrevPos: Integer;
    const IsSortedAscending: Boolean): Integer;
var I, L, H : Integer;
    D       : LongWord;
begin
  if IsSortedAscending then // binary search
    begin
      if MaxI(PrevPos + 1, 0) = 0 then // find first
        begin
          L := 0;
          H := Length(V) - 1;
          While L <= H do
            begin
              I := (L + H) div 2;
              D := V[I];
              if Find = D then
                begin
                  While (I > 0) and (V[I - 1] = Find) do
                    Dec(I);
                  Result := I;
                  exit;
                end else
              if D > Find then
                H := I - 1
              else
                L := I + 1;
            end;
          Result := -1;
        end
      else // find next
        if PrevPos >= Length(V) - 1 then
          Result := -1
        else
          if V[PrevPos + 1] = Find then
            Result := PrevPos + 1
          else
            Result := -1;
    end
  else
    begin // linear search
      For I := MaxI(PrevPos + 1, 0) to Length(V) - 1 do
        if V[I] = Find then
          begin
            Result := I;
            exit;
          end;
      Result := -1;
    end;
end;

function PosNext(const Find: ShortInt; const V: ShortIntArray; const PrevPos: Integer;
    const IsSortedAscending: Boolean): Integer;
var I, L, H : Integer;
    D       : ShortInt;
begin
  if IsSortedAscending then // binary search
    begin
      if MaxI(PrevPos + 1, 0) = 0 then // find first
        begin
          L := 0;
          H := Length(V) - 1;
          While L <= H do
            begin
              I := (L + H) div 2;
              D := V[I];
              if Find = D then
                begin
                  While (I > 0) and (V[I - 1] = Find) do
                    Dec(I);
                  Result := I;
                  exit;
                end else
              if D > Find then
                H := I - 1
              else
                L := I + 1;
            end;
          Result := -1;
        end
      else // find next
        if PrevPos >= Length(V) - 1 then
          Result := -1
        else
          if V[PrevPos + 1] = Find then
            Result := PrevPos + 1
          else
            Result := -1;
    end
  else
    begin // linear search
      For I := MaxI(PrevPos + 1, 0) to Length(V) - 1 do
        if V[I] = Find then
          begin
            Result := I;
            exit;
          end;
      Result := -1;
    end;
end;

function PosNext(const Find: SmallInt; const V: SmallIntArray; const PrevPos: Integer;
    const IsSortedAscending: Boolean): Integer;
var I, L, H : Integer;
    D       : SmallInt;
begin
  if IsSortedAscending then // binary search
    begin
      if MaxI(PrevPos + 1, 0) = 0 then // find first
        begin
          L := 0;
          H := Length(V) - 1;
          While L <= H do
            begin
              I := (L + H) div 2;
              D := V[I];
              if Find = D then
                begin
                  While (I > 0) and (V[I - 1] = Find) do
                    Dec(I);
                  Result := I;
                  exit;
                end else
              if D > Find then
                H := I - 1
              else
                L := I + 1;
            end;
          Result := -1;
        end
      else // find next
        if PrevPos >= Length(V) - 1 then
          Result := -1
        else
          if V[PrevPos + 1] = Find then
            Result := PrevPos + 1
          else
            Result := -1;
    end
  else
    begin // linear search
      For I := MaxI(PrevPos + 1, 0) to Length(V) - 1 do
        if V[I] = Find then
          begin
            Result := I;
            exit;
          end;
      Result := -1;
    end;
end;

function PosNext(const Find: LongInt; const V: LongIntArray; const PrevPos: Integer;
    const IsSortedAscending: Boolean): Integer;
var I, L, H : Integer;
    D       : LongInt;
begin
  if IsSortedAscending then // binary search
    begin
      if MaxI(PrevPos + 1, 0) = 0 then // find first
        begin
          L := 0;
          H := Length(V) - 1;
          While L <= H do
            begin
              I := (L + H) div 2;
              D := V[I];
              if Find = D then
                begin
                  While (I > 0) and (V[I - 1] = Find) do
                    Dec(I);
                  Result := I;
                  exit;
                end else
              if D > Find then
                H := I - 1
              else
                L := I + 1;
            end;
          Result := -1;
        end
      else // find next
        if PrevPos >= Length(V) - 1 then
          Result := -1
        else
          if V[PrevPos + 1] = Find then
            Result := PrevPos + 1
          else
            Result := -1;
    end
  else
    begin // linear search
      For I := MaxI(PrevPos + 1, 0) to Length(V) - 1 do
        if V[I] = Find then
          begin
            Result := I;
            exit;
          end;
      Result := -1;
    end;
end;

function PosNext(const Find: Int64; const V: Int64Array; const PrevPos: Integer;
    const IsSortedAscending: Boolean): Integer;
var I, L, H : Integer;
    D       : Int64;
begin
  if IsSortedAscending then // binary search
    begin
      if MaxI(PrevPos + 1, 0) = 0 then // find first
        begin
          L := 0;
          H := Length(V) - 1;
          While L <= H do
            begin
              I := (L + H) div 2;
              D := V[I];
              if Find = D then
                begin
                  While (I > 0) and (V[I - 1] = Find) do
                    Dec(I);
                  Result := I;
                  exit;
                end else
              if D > Find then
                H := I - 1
              else
                L := I + 1;
            end;
          Result := -1;
        end
      else // find next
        if PrevPos >= Length(V) - 1 then
          Result := -1
        else
          if V[PrevPos + 1] = Find then
            Result := PrevPos + 1
          else
            Result := -1;
    end
  else
    begin // linear search
      For I := MaxI(PrevPos + 1, 0) to Length(V) - 1 do
        if V[I] = Find then
          begin
            Result := I;
            exit;
          end;
      Result := -1;
    end;
end;

function PosNext(const Find: Single; const V: SingleArray; const PrevPos: Integer;
    const IsSortedAscending: Boolean): Integer;
var I, L, H : Integer;
    D       : Single;
begin
  if IsSortedAscending then // binary search
    begin
      if MaxI(PrevPos + 1, 0) = 0 then // find first
        begin
          L := 0;
          H := Length(V) - 1;
          While L <= H do
            begin
              I := (L + H) div 2;
              D := V[I];
              if Find = D then
                begin
                  While (I > 0) and (V[I - 1] = Find) do
                    Dec(I);
                  Result := I;
                  exit;
                end else
              if D > Find then
                H := I - 1
              else
                L := I + 1;
            end;
          Result := -1;
        end
      else // find next
        if PrevPos >= Length(V) - 1 then
          Result := -1
        else
          if V[PrevPos + 1] = Find then
            Result := PrevPos + 1
          else
            Result := -1;
    end
  else
    begin // linear search
      For I := MaxI(PrevPos + 1, 0) to Length(V) - 1 do
        if V[I] = Find then
          begin
            Result := I;
            exit;
          end;
      Result := -1;
    end;
end;

function PosNext(const Find: Double; const V: DoubleArray; const PrevPos: Integer;
    const IsSortedAscending: Boolean): Integer;
var I, L, H : Integer;
    D       : Double;
begin
  if IsSortedAscending then // binary search
    begin
      if MaxI(PrevPos + 1, 0) = 0 then // find first
        begin
          L := 0;
          H := Length(V) - 1;
          While L <= H do
            begin
              I := (L + H) div 2;
              D := V[I];
              if Find = D then
                begin
                  While (I > 0) and (V[I - 1] = Find) do
                    Dec(I);
                  Result := I;
                  exit;
                end else
              if D > Find then
                H := I - 1
              else
                L := I + 1;
            end;
          Result := -1;
        end
      else // find next
        if PrevPos >= Length(V) - 1 then
          Result := -1
        else
          if V[PrevPos + 1] = Find then
            Result := PrevPos + 1
          else
            Result := -1;
    end
  else
    begin // linear search
      For I := MaxI(PrevPos + 1, 0) to Length(V) - 1 do
        if V[I] = Find then
          begin
            Result := I;
            exit;
          end;
      Result := -1;
    end;
end;

function PosNext(const Find: Extended; const V: ExtendedArray; const PrevPos: Integer;
    const IsSortedAscending: Boolean): Integer;
var I, L, H : Integer;
    D       : Extended;
begin
  if IsSortedAscending then // binary search
    begin
      if MaxI(PrevPos + 1, 0) = 0 then // find first
        begin
          L := 0;
          H := Length(V) - 1;
          While L <= H do
            begin
              I := (L + H) div 2;
              D := V[I];
              if Find = D then
                begin
                  While (I > 0) and (V[I - 1] = Find) do
                    Dec(I);
                  Result := I;
                  exit;
                end else
              if D > Find then
                H := I - 1
              else
                L := I + 1;
            end;
          Result := -1;
        end
      else // find next
        if PrevPos >= Length(V) - 1 then
          Result := -1
        else
          if V[PrevPos + 1] = Find then
            Result := PrevPos + 1
          else
            Result := -1;
    end
  else
    begin // linear search
      For I := MaxI(PrevPos + 1, 0) to Length(V) - 1 do
        if V[I] = Find then
          begin
            Result := I;
            exit;
          end;
      Result := -1;
    end;
end;

function PosNext(const Find: Boolean; const V: BooleanArray; const PrevPos: Integer;
    const IsSortedAscending: Boolean): Integer;
var I, L, H : Integer;
    D       : Boolean;
begin
  if IsSortedAscending then // binary search
    begin
      if MaxI(PrevPos + 1, 0) = 0 then // find first
        begin
          L := 0;
          H := Length(V) - 1;
          While L <= H do
            begin
              I := (L + H) div 2;
              D := V[I];
              if Find = D then
                begin
                  While (I > 0) and (V[I - 1] = Find) do
                    Dec(I);
                  Result := I;
                  exit;
                end else
              if D > Find then
                H := I - 1
              else
                L := I + 1;
            end;
          Result := -1;
        end
      else // find next
        if PrevPos >= Length(V) - 1 then
          Result := -1
        else
          if V[PrevPos + 1] = Find then
            Result := PrevPos + 1
          else
            Result := -1;
    end
  else
    begin // linear search
      For I := MaxI(PrevPos + 1, 0) to Length(V) - 1 do
        if V[I] = Find then
          begin
            Result := I;
            exit;
          end;
      Result := -1;
    end;
end;

function PosNext(const Find: String; const V: StringArray; const PrevPos: Integer;
    const IsSortedAscending: Boolean): Integer;
var I, L, H : Integer;
    D       : String;
begin
  if IsSortedAscending then // binary search
    begin
      if MaxI(PrevPos + 1, 0) = 0 then // find first
        begin
          L := 0;
          H := Length(V) - 1;
          While L <= H do
            begin
              I := (L + H) div 2;
              D := V[I];
              if Find = D then
                begin
                  While (I > 0) and (V[I - 1] = Find) do
                    Dec(I);
                  Result := I;
                  exit;
                end else
              if D > Find then
                H := I - 1
              else
                L := I + 1;
            end;
          Result := -1;
        end
      else // find next
        if PrevPos >= Length(V) - 1 then
          Result := -1
        else
          if V[PrevPos + 1] = Find then
            Result := PrevPos + 1
          else
            Result := -1;
    end
  else
    begin // linear search
      For I := MaxI(PrevPos + 1, 0) to Length(V) - 1 do
        if V[I] = Find then
          begin
            Result := I;
            exit;
          end;
      Result := -1;
    end;
end;

function PosNext(const Find: TObject; const V: ObjectArray; const PrevPos: Integer): Integer;
var I : Integer;
begin
  For I := MaxI(PrevPos + 1, 0) to Length(V) - 1 do
    if V[I] = Find then
      begin
        Result := I;
        exit;
       end;
  Result := -1;
end;

function PosNext(const ClassType: TClass; const V: ObjectArray; const PrevPos: Integer): Integer;
var I : Integer;
begin
  For I := MaxI(PrevPos + 1, 0) to Length(V) - 1 do
    if V[I] is ClassType then
      begin
        Result := I;
        exit;
       end;
  Result := -1;
end;

function PosNext(const ClassName: String; const V: ObjectArray; const PrevPos: Integer): Integer;
var I : Integer;
    T : TObject;
begin
  For I := MaxI(PrevPos + 1, 0) to Length(V) - 1 do
    begin
      T := V[I];
      if Assigned(T) and (T.ClassName = ClassName) then
        begin
          Result := I;
          exit;
         end;
    end;
  Result := -1;
end;

function PosNext(const Find: Pointer; const V: PointerArray; const PrevPos: Integer): Integer;
var I : Integer;
begin
  For I := MaxI(PrevPos + 1, 0) to Length(V) - 1 do
    if V[I] = Find then
      begin
        Result := I;
        exit;
       end;
  Result := -1;
end;



{                                                                              }
{ Count                                                                        }
{                                                                              }
function Count(const Find: Byte; const V: ByteArray; const IsSortedAscending: Boolean = False): Integer;
var I, J : Integer;
begin
  if IsSortedAscending then
    begin
      I := PosNext(Find, V, -1, True);
      if I = -1 then
        Result := 0 else
        begin
          Result := 1;
          J := Length(V);
          While (I + Result < J) and (V[I + Result] = Find) do
            Inc(Result);
        end;
    end
  else
    begin
      J := -1;
      Result := 0;
      Repeat
        I := PosNext(Find, V, J, False);
        if I >= 0 then
          begin
            Inc(Result);
            J := I;
          end;
      Until I < 0;
    end;
end;

function Count(const Find: Word; const V: WordArray; const IsSortedAscending: Boolean = False): Integer;
var I, J : Integer;
begin
  if IsSortedAscending then
    begin
      I := PosNext(Find, V, -1, True);
      if I = -1 then
        Result := 0 else
        begin
          Result := 1;
          J := Length(V);
          While (I + Result < J) and (V[I + Result] = Find) do
            Inc(Result);
        end;
    end
  else
    begin
      J := -1;
      Result := 0;
      Repeat
        I := PosNext(Find, V, J, False);
        if I >= 0 then
          begin
            Inc(Result);
            J := I;
          end;
      Until I < 0;
    end;
end;

function Count(const Find: LongWord; const V: LongWordArray; const IsSortedAscending: Boolean = False): Integer;
var I, J : Integer;
begin
  if IsSortedAscending then
    begin
      I := PosNext(Find, V, -1, True);
      if I = -1 then
        Result := 0 else
        begin
          Result := 1;
          J := Length(V);
          While (I + Result < J) and (V[I + Result] = Find) do
            Inc(Result);
        end;
    end
  else
    begin
      J := -1;
      Result := 0;
      Repeat
        I := PosNext(Find, V, J, False);
        if I >= 0 then
          begin
            Inc(Result);
            J := I;
          end;
      Until I < 0;
    end;
end;

function Count(const Find: ShortInt; const V: ShortIntArray; const IsSortedAscending: Boolean = False): Integer;
var I, J : Integer;
begin
  if IsSortedAscending then
    begin
      I := PosNext(Find, V, -1, True);
      if I = -1 then
        Result := 0 else
        begin
          Result := 1;
          J := Length(V);
          While (I + Result < J) and (V[I + Result] = Find) do
            Inc(Result);
        end;
    end
  else
    begin
      J := -1;
      Result := 0;
      Repeat
        I := PosNext(Find, V, J, False);
        if I >= 0 then
          begin
            Inc(Result);
            J := I;
          end;
      Until I < 0;
    end;
end;

function Count(const Find: SmallInt; const V: SmallIntArray; const IsSortedAscending: Boolean = False): Integer;
var I, J : Integer;
begin
  if IsSortedAscending then
    begin
      I := PosNext(Find, V, -1, True);
      if I = -1 then
        Result := 0 else
        begin
          Result := 1;
          J := Length(V);
          While (I + Result < J) and (V[I + Result] = Find) do
            Inc(Result);
        end;
    end
  else
    begin
      J := -1;
      Result := 0;
      Repeat
        I := PosNext(Find, V, J, False);
        if I >= 0 then
          begin
            Inc(Result);
            J := I;
          end;
      Until I < 0;
    end;
end;

function Count(const Find: LongInt; const V: LongIntArray; const IsSortedAscending: Boolean = False): Integer;
var I, J : Integer;
begin
  if IsSortedAscending then
    begin
      I := PosNext(Find, V, -1, True);
      if I = -1 then
        Result := 0 else
        begin
          Result := 1;
          J := Length(V);
          While (I + Result < J) and (V[I + Result] = Find) do
            Inc(Result);
        end;
    end
  else
    begin
      J := -1;
      Result := 0;
      Repeat
        I := PosNext(Find, V, J, False);
        if I >= 0 then
          begin
            Inc(Result);
            J := I;
          end;
      Until I < 0;
    end;
end;

function Count(const Find: Int64; const V: Int64Array; const IsSortedAscending: Boolean = False): Integer;
var I, J : Integer;
begin
  if IsSortedAscending then
    begin
      I := PosNext(Find, V, -1, True);
      if I = -1 then
        Result := 0 else
        begin
          Result := 1;
          J := Length(V);
          While (I + Result < J) and (V[I + Result] = Find) do
            Inc(Result);
        end;
    end
  else
    begin
      J := -1;
      Result := 0;
      Repeat
        I := PosNext(Find, V, J, False);
        if I >= 0 then
          begin
            Inc(Result);
            J := I;
          end;
      Until I < 0;
    end;
end;

function Count(const Find: Single; const V: SingleArray; const IsSortedAscending: Boolean = False): Integer;
var I, J : Integer;
begin
  if IsSortedAscending then
    begin
      I := PosNext(Find, V, -1, True);
      if I = -1 then
        Result := 0 else
        begin
          Result := 1;
          J := Length(V);
          While (I + Result < J) and (V[I + Result] = Find) do
            Inc(Result);
        end;
    end
  else
    begin
      J := -1;
      Result := 0;
      Repeat
        I := PosNext(Find, V, J, False);
        if I >= 0 then
          begin
            Inc(Result);
            J := I;
          end;
      Until I < 0;
    end;
end;

function Count(const Find: Double; const V: DoubleArray; const IsSortedAscending: Boolean = False): Integer;
var I, J : Integer;
begin
  if IsSortedAscending then
    begin
      I := PosNext(Find, V, -1, True);
      if I = -1 then
        Result := 0 else
        begin
          Result := 1;
          J := Length(V);
          While (I + Result < J) and (V[I + Result] = Find) do
            Inc(Result);
        end;
    end
  else
    begin
      J := -1;
      Result := 0;
      Repeat
        I := PosNext(Find, V, J, False);
        if I >= 0 then
          begin
            Inc(Result);
            J := I;
          end;
      Until I < 0;
    end;
end;

function Count(const Find: Extended; const V: ExtendedArray; const IsSortedAscending: Boolean = False): Integer;
var I, J : Integer;
begin
  if IsSortedAscending then
    begin
      I := PosNext(Find, V, -1, True);
      if I = -1 then
        Result := 0 else
        begin
          Result := 1;
          J := Length(V);
          While (I + Result < J) and (V[I + Result] = Find) do
            Inc(Result);
        end;
    end
  else
    begin
      J := -1;
      Result := 0;
      Repeat
        I := PosNext(Find, V, J, False);
        if I >= 0 then
          begin
            Inc(Result);
            J := I;
          end;
      Until I < 0;
    end;
end;

function Count(const Find: String; const V: StringArray; const IsSortedAscending: Boolean = False): Integer;
var I, J : Integer;
begin
  if IsSortedAscending then
    begin
      I := PosNext(Find, V, -1, True);
      if I = -1 then
        Result := 0 else
        begin
          Result := 1;
          J := Length(V);
          While (I + Result < J) and (V[I + Result] = Find) do
            Inc(Result);
        end;
    end
  else
    begin
      J := -1;
      Result := 0;
      Repeat
        I := PosNext(Find, V, J, False);
        if I >= 0 then
          begin
            Inc(Result);
            J := I;
          end;
      Until I < 0;
    end;
end;

function Count(const Find: Boolean; const V: BooleanArray; const IsSortedAscending: Boolean = False): Integer;
var I, J : Integer;
begin
  if IsSortedAscending then
    begin
      I := PosNext(Find, V, -1, True);
      if I = -1 then
        Result := 0 else
        begin
          Result := 1;
          J := Length(V);
          While (I + Result < J) and (V[I + Result] = Find) do
            Inc(Result);
        end;
    end
  else
    begin
      J := -1;
      Result := 0;
      Repeat
        I := PosNext(Find, V, J, False);
        if I >= 0 then
          begin
            Inc(Result);
            J := I;
          end;
      Until I < 0;
    end;
end;



{                                                                              }
{ RemoveAll                                                                    }
{                                                                              }
procedure RemoveAll(const Find: Byte; var V: ByteArray; const IsSortedAscending: Boolean = False);
var I, J : Integer;
begin
  I := PosNext(Find, V, -1, IsSortedAscending);
  While I >= 0 do
    begin
      J := 1;
      While (I + J < Length(V)) and (V[I + J] = Find) do
        Inc(J);
      Remove(V, I, J);
      I := PosNext(Find, V, I, IsSortedAscending);
    end;
end;

procedure RemoveAll(const Find: Word; var V: WordArray; const IsSortedAscending: Boolean = False);
var I, J : Integer;
begin
  I := PosNext(Find, V, -1, IsSortedAscending);
  While I >= 0 do
    begin
      J := 1;
      While (I + J < Length(V)) and (V[I + J] = Find) do
        Inc(J);
      Remove(V, I, J);
      I := PosNext(Find, V, I, IsSortedAscending);
    end;
end;

procedure RemoveAll(const Find: LongWord; var V: LongWordArray; const IsSortedAscending: Boolean = False);
var I, J : Integer;
begin
  I := PosNext(Find, V, -1, IsSortedAscending);
  While I >= 0 do
    begin
      J := 1;
      While (I + J < Length(V)) and (V[I + J] = Find) do
        Inc(J);
      Remove(V, I, J);
      I := PosNext(Find, V, I, IsSortedAscending);
    end;
end;

procedure RemoveAll(const Find: ShortInt; var V: ShortIntArray; const IsSortedAscending: Boolean = False);
var I, J : Integer;
begin
  I := PosNext(Find, V, -1, IsSortedAscending);
  While I >= 0 do
    begin
      J := 1;
      While (I + J < Length(V)) and (V[I + J] = Find) do
        Inc(J);
      Remove(V, I, J);
      I := PosNext(Find, V, I, IsSortedAscending);
    end;
end;

procedure RemoveAll(const Find: SmallInt; var V: SmallIntArray; const IsSortedAscending: Boolean = False);
var I, J : Integer;
begin
  I := PosNext(Find, V, -1, IsSortedAscending);
  While I >= 0 do
    begin
      J := 1;
      While (I + J < Length(V)) and (V[I + J] = Find) do
        Inc(J);
      Remove(V, I, J);
      I := PosNext(Find, V, I, IsSortedAscending);
    end;
end;

procedure RemoveAll(const Find: LongInt; var V: LongIntArray; const IsSortedAscending: Boolean = False);
var I, J : Integer;
begin
  I := PosNext(Find, V, -1, IsSortedAscending);
  While I >= 0 do
    begin
      J := 1;
      While (I + J < Length(V)) and (V[I + J] = Find) do
        Inc(J);
      Remove(V, I, J);
      I := PosNext(Find, V, I, IsSortedAscending);
    end;
end;

procedure RemoveAll(const Find: Int64; var V: Int64Array; const IsSortedAscending: Boolean = False);
var I, J : Integer;
begin
  I := PosNext(Find, V, -1, IsSortedAscending);
  While I >= 0 do
    begin
      J := 1;
      While (I + J < Length(V)) and (V[I + J] = Find) do
        Inc(J);
      Remove(V, I, J);
      I := PosNext(Find, V, I, IsSortedAscending);
    end;
end;

procedure RemoveAll(const Find: Single; var V: SingleArray; const IsSortedAscending: Boolean = False);
var I, J : Integer;
begin
  I := PosNext(Find, V, -1, IsSortedAscending);
  While I >= 0 do
    begin
      J := 1;
      While (I + J < Length(V)) and (V[I + J] = Find) do
        Inc(J);
      Remove(V, I, J);
      I := PosNext(Find, V, I, IsSortedAscending);
    end;
end;

procedure RemoveAll(const Find: Double; var V: DoubleArray; const IsSortedAscending: Boolean = False);
var I, J : Integer;
begin
  I := PosNext(Find, V, -1, IsSortedAscending);
  While I >= 0 do
    begin
      J := 1;
      While (I + J < Length(V)) and (V[I + J] = Find) do
        Inc(J);
      Remove(V, I, J);
      I := PosNext(Find, V, I, IsSortedAscending);
    end;
end;

procedure RemoveAll(const Find: Extended; var V: ExtendedArray; const IsSortedAscending: Boolean = False);
var I, J : Integer;
begin
  I := PosNext(Find, V, -1, IsSortedAscending);
  While I >= 0 do
    begin
      J := 1;
      While (I + J < Length(V)) and (V[I + J] = Find) do
        Inc(J);
      Remove(V, I, J);
      I := PosNext(Find, V, I, IsSortedAscending);
    end;
end;

procedure RemoveAll(const Find: String; var V: StringArray; const IsSortedAscending: Boolean = False);
var I, J : Integer;
begin
  I := PosNext(Find, V, -1, IsSortedAscending);
  While I >= 0 do
    begin
      J := 1;
      While (I + J < Length(V)) and (V[I + J] = Find) do
        Inc(J);
      Remove(V, I, J);
      I := PosNext(Find, V, I, IsSortedAscending);
    end;
end;



{                                                                              }
{ Intersection                                                                 }
{   If both arrays are sorted ascending then time is o(n) instead of o(n^2).   }
{                                                                              }
function Intersection(const V1, V2: SingleArray; const IsSortedAscending: Boolean): SingleArray;
var I, J, L, LV : Integer;
begin
  SetLength(Result, 0);
  if IsSortedAscending then
    begin
      I := 0;
      J := 0;
      L := Length(V1);
      LV := Length(V2);
      While (I < L) and (J < LV) do
        begin
          While (I < L) and (V1[I] < V2[J]) do
            Inc(I);
          if I < L then
            begin
              if V1[I] = V2[J] then
                Append(Result, V1[I]);
              While (J < LV) and (V2[J] <= V1[I]) do
                Inc(J);
            end;
        end;
    end
  else
    For I := 0 to Length(V1) - 1 do
      if (PosNext(V1[I], V2) >= 0) and (PosNext(V1[I], Result) = -1) then
        Append(Result, V1[I]);
end;

function Intersection(const V1, V2: DoubleArray; const IsSortedAscending: Boolean): DoubleArray;
var I, J, L, LV : Integer;
begin
  SetLength(Result, 0);
  if IsSortedAscending then
    begin
      I := 0;
      J := 0;
      L := Length(V1);
      LV := Length(V2);
      While (I < L) and (J < LV) do
        begin
          While (I < L) and (V1[I] < V2[J]) do
            Inc(I);
          if I < L then
            begin
              if V1[I] = V2[J] then
                Append(Result, V1[I]);
              While (J < LV) and (V2[J] <= V1[I]) do
                Inc(J);
            end;
        end;
    end
  else
    For I := 0 to Length(V1) - 1 do
      if (PosNext(V1[I], V2) >= 0) and (PosNext(V1[I], Result) = -1) then
        Append(Result, V1[I]);
end;

function Intersection(const V1, V2: ExtendedArray; const IsSortedAscending: Boolean): ExtendedArray;
var I, J, L, LV : Integer;
begin
  SetLength(Result, 0);
  if IsSortedAscending then
    begin
      I := 0;
      J := 0;
      L := Length(V1);
      LV := Length(V2);
      While (I < L) and (J < LV) do
        begin
          While (I < L) and (V1[I] < V2[J]) do
            Inc(I);
          if I < L then
            begin
              if V1[I] = V2[J] then
                Append(Result, V1[I]);
              While (J < LV) and (V2[J] <= V1[I]) do
                Inc(J);
            end;
        end;
    end
  else
    For I := 0 to Length(V1) - 1 do
      if (PosNext(V1[I], V2) >= 0) and (PosNext(V1[I], Result) = -1) then
        Append(Result, V1[I]);
end;

function Intersection(const V1, V2: ByteArray; const IsSortedAscending: Boolean): ByteArray;
var I, J, L, LV : Integer;
begin
  SetLength(Result, 0);
  if IsSortedAscending then
    begin
      I := 0;
      J := 0;
      L := Length(V1);
      LV := Length(V2);
      While (I < L) and (J < LV) do
        begin
          While (I < L) and (V1[I] < V2[J]) do
            Inc(I);
          if I < L then
            begin
              if V1[I] = V2[J] then
                Append(Result, V1[I]);
              While (J < LV) and (V2[J] <= V1[I]) do
                Inc(J);
            end;
        end;
    end
  else
    For I := 0 to Length(V1) - 1 do
      if (PosNext(V1[I], V2) >= 0) and (PosNext(V1[I], Result) = -1) then
        Append(Result, V1[I]);
end;

function Intersection(const V1, V2: WordArray; const IsSortedAscending: Boolean): WordArray;
var I, J, L, LV : Integer;
begin
  SetLength(Result, 0);
  if IsSortedAscending then
    begin
      I := 0;
      J := 0;
      L := Length(V1);
      LV := Length(V2);
      While (I < L) and (J < LV) do
        begin
          While (I < L) and (V1[I] < V2[J]) do
            Inc(I);
          if I < L then
            begin
              if V1[I] = V2[J] then
                Append(Result, V1[I]);
              While (J < LV) and (V2[J] <= V1[I]) do
                Inc(J);
            end;
        end;
    end
  else
    For I := 0 to Length(V1) - 1 do
      if (PosNext(V1[I], V2) >= 0) and (PosNext(V1[I], Result) = -1) then
        Append(Result, V1[I]);
end;

function Intersection(const V1, V2: LongWordArray; const IsSortedAscending: Boolean): LongWordArray;
var I, J, L, LV : Integer;
begin
  SetLength(Result, 0);
  if IsSortedAscending then
    begin
      I := 0;
      J := 0;
      L := Length(V1);
      LV := Length(V2);
      While (I < L) and (J < LV) do
        begin
          While (I < L) and (V1[I] < V2[J]) do
            Inc(I);
          if I < L then
            begin
              if V1[I] = V2[J] then
                Append(Result, V1[I]);
              While (J < LV) and (V2[J] <= V1[I]) do
                Inc(J);
            end;
        end;
    end
  else
    For I := 0 to Length(V1) - 1 do
      if (PosNext(V1[I], V2) >= 0) and (PosNext(V1[I], Result) = -1) then
        Append(Result, V1[I]);
end;

function Intersection(const V1, V2: ShortIntArray; const IsSortedAscending: Boolean): ShortIntArray;
var I, J, L, LV : Integer;
begin
  SetLength(Result, 0);
  if IsSortedAscending then
    begin
      I := 0;
      J := 0;
      L := Length(V1);
      LV := Length(V2);
      While (I < L) and (J < LV) do
        begin
          While (I < L) and (V1[I] < V2[J]) do
            Inc(I);
          if I < L then
            begin
              if V1[I] = V2[J] then
                Append(Result, V1[I]);
              While (J < LV) and (V2[J] <= V1[I]) do
                Inc(J);
            end;
        end;
    end
  else
    For I := 0 to Length(V1) - 1 do
      if (PosNext(V1[I], V2) >= 0) and (PosNext(V1[I], Result) = -1) then
        Append(Result, V1[I]);
end;

function Intersection(const V1, V2: SmallIntArray; const IsSortedAscending: Boolean): SmallIntArray;
var I, J, L, LV : Integer;
begin
  SetLength(Result, 0);
  if IsSortedAscending then
    begin
      I := 0;
      J := 0;
      L := Length(V1);
      LV := Length(V2);
      While (I < L) and (J < LV) do
        begin
          While (I < L) and (V1[I] < V2[J]) do
            Inc(I);
          if I < L then
            begin
              if V1[I] = V2[J] then
                Append(Result, V1[I]);
              While (J < LV) and (V2[J] <= V1[I]) do
                Inc(J);
            end;
        end;
    end
  else
    For I := 0 to Length(V1) - 1 do
      if (PosNext(V1[I], V2) >= 0) and (PosNext(V1[I], Result) = -1) then
        Append(Result, V1[I]);
end;

function Intersection(const V1, V2: LongIntArray; const IsSortedAscending: Boolean): LongIntArray;
var I, J, L, LV : Integer;
begin
  SetLength(Result, 0);
  if IsSortedAscending then
    begin
      I := 0;
      J := 0;
      L := Length(V1);
      LV := Length(V2);
      While (I < L) and (J < LV) do
        begin
          While (I < L) and (V1[I] < V2[J]) do
            Inc(I);
          if I < L then
            begin
              if V1[I] = V2[J] then
                Append(Result, V1[I]);
              While (J < LV) and (V2[J] <= V1[I]) do
                Inc(J);
            end;
        end;
    end
  else
    For I := 0 to Length(V1) - 1 do
      if (PosNext(V1[I], V2) >= 0) and (PosNext(V1[I], Result) = -1) then
        Append(Result, V1[I]);
end;

function Intersection(const V1, V2: Int64Array; const IsSortedAscending: Boolean): Int64Array;
var I, J, L, LV : Integer;
begin
  SetLength(Result, 0);
  if IsSortedAscending then
    begin
      I := 0;
      J := 0;
      L := Length(V1);
      LV := Length(V2);
      While (I < L) and (J < LV) do
        begin
          While (I < L) and (V1[I] < V2[J]) do
            Inc(I);
          if I < L then
            begin
              if V1[I] = V2[J] then
                Append(Result, V1[I]);
              While (J < LV) and (V2[J] <= V1[I]) do
                Inc(J);
            end;
        end;
    end
  else
    For I := 0 to Length(V1) - 1 do
      if (PosNext(V1[I], V2) >= 0) and (PosNext(V1[I], Result) = -1) then
        Append(Result, V1[I]);
end;

function Intersection(const V1, V2: StringArray; const IsSortedAscending: Boolean): StringArray;
var I, J, L, LV : Integer;
begin
  SetLength(Result, 0);
  if IsSortedAscending then
    begin
      I := 0;
      J := 0;
      L := Length(V1);
      LV := Length(V2);
      While (I < L) and (J < LV) do
        begin
          While (I < L) and (V1[I] < V2[J]) do
            Inc(I);
          if I < L then
            begin
              if V1[I] = V2[J] then
                Append(Result, V1[I]);
              While (J < LV) and (V2[J] <= V1[I]) do
                Inc(J);
            end;
        end;
    end
  else
    For I := 0 to Length(V1) - 1 do
      if (PosNext(V1[I], V2) >= 0) and (PosNext(V1[I], Result) = -1) then
        Append(Result, V1[I]);
end;



{                                                                              }
{ Difference                                                                   }
{   Returns elements in V1 but not in V2.                                      }
{   If both arrays are sorted ascending then time is o(n) instead of o(n^2).   }
{                                                                              }
function Difference(const V1, V2: SingleArray; const IsSortedAscending: Boolean): SingleArray;
var I, J, L, LV : Integer;
begin
  SetLength(Result, 0);
  if IsSortedAscending then
    begin
      I := 0;
      J := 0;
      L := Length(V1);
      LV := Length(V2);
      While (I < L) and (J < LV) do
        begin
          While (I < L) and (V1[I] < V2[J]) do
            Inc(I);
          if I < L then
            begin
              if V1[I] <> V2[J] then
                Append(Result, V1[I]);
              While (J < LV) and (V2[J] <= V1[I]) do
                Inc(J);
            end;
        end;
    end
  else
    For I := 0 to Length(V1) - 1 do
      if (PosNext(V1[I], V2) = -1) and (PosNext(V1[I], Result) = -1) then
        Append(Result, V1[I]);
end;

function Difference(const V1, V2: DoubleArray; const IsSortedAscending: Boolean): DoubleArray;
var I, J, L, LV : Integer;
begin
  SetLength(Result, 0);
  if IsSortedAscending then
    begin
      I := 0;
      J := 0;
      L := Length(V1);
      LV := Length(V2);
      While (I < L) and (J < LV) do
        begin
          While (I < L) and (V1[I] < V2[J]) do
            Inc(I);
          if I < L then
            begin
              if V1[I] <> V2[J] then
                Append(Result, V1[I]);
              While (J < LV) and (V2[J] <= V1[I]) do
                Inc(J);
            end;
        end;
    end
  else
    For I := 0 to Length(V1) - 1 do
      if (PosNext(V1[I], V2) = -1) and (PosNext(V1[I], Result) = -1) then
        Append(Result, V1[I]);
end;

function Difference(const V1, V2: ExtendedArray; const IsSortedAscending: Boolean): ExtendedArray;
var I, J, L, LV : Integer;
begin
  SetLength(Result, 0);
  if IsSortedAscending then
    begin
      I := 0;
      J := 0;
      L := Length(V1);
      LV := Length(V2);
      While (I < L) and (J < LV) do
        begin
          While (I < L) and (V1[I] < V2[J]) do
            Inc(I);
          if I < L then
            begin
              if V1[I] <> V2[J] then
                Append(Result, V1[I]);
              While (J < LV) and (V2[J] <= V1[I]) do
                Inc(J);
            end;
        end;
    end
  else
    For I := 0 to Length(V1) - 1 do
      if (PosNext(V1[I], V2) = -1) and (PosNext(V1[I], Result) = -1) then
        Append(Result, V1[I]);
end;

function Difference(const V1, V2: ByteArray; const IsSortedAscending: Boolean): ByteArray;
var I, J, L, LV : Integer;
begin
  SetLength(Result, 0);
  if IsSortedAscending then
    begin
      I := 0;
      J := 0;
      L := Length(V1);
      LV := Length(V2);
      While (I < L) and (J < LV) do
        begin
          While (I < L) and (V1[I] < V2[J]) do
            Inc(I);
          if I < L then
            begin
              if V1[I] <> V2[J] then
                Append(Result, V1[I]);
              While (J < LV) and (V2[J] <= V1[I]) do
                Inc(J);
            end;
        end;
    end
  else
    For I := 0 to Length(V1) - 1 do
      if (PosNext(V1[I], V2) = -1) and (PosNext(V1[I], Result) = -1) then
        Append(Result, V1[I]);
end;

function Difference(const V1, V2: WordArray; const IsSortedAscending: Boolean): WordArray;
var I, J, L, LV : Integer;
begin
  SetLength(Result, 0);
  if IsSortedAscending then
    begin
      I := 0;
      J := 0;
      L := Length(V1);
      LV := Length(V2);
      While (I < L) and (J < LV) do
        begin
          While (I < L) and (V1[I] < V2[J]) do
            Inc(I);
          if I < L then
            begin
              if V1[I] <> V2[J] then
                Append(Result, V1[I]);
              While (J < LV) and (V2[J] <= V1[I]) do
                Inc(J);
            end;
        end;
    end
  else
    For I := 0 to Length(V1) - 1 do
      if (PosNext(V1[I], V2) = -1) and (PosNext(V1[I], Result) = -1) then
        Append(Result, V1[I]);
end;

function Difference(const V1, V2: LongWordArray; const IsSortedAscending: Boolean): LongWordArray;
var I, J, L, LV : Integer;
begin
  SetLength(Result, 0);
  if IsSortedAscending then
    begin
      I := 0;
      J := 0;
      L := Length(V1);
      LV := Length(V2);
      While (I < L) and (J < LV) do
        begin
          While (I < L) and (V1[I] < V2[J]) do
            Inc(I);
          if I < L then
            begin
              if V1[I] <> V2[J] then
                Append(Result, V1[I]);
              While (J < LV) and (V2[J] <= V1[I]) do
                Inc(J);
            end;
        end;
    end
  else
    For I := 0 to Length(V1) - 1 do
      if (PosNext(V1[I], V2) = -1) and (PosNext(V1[I], Result) = -1) then
        Append(Result, V1[I]);
end;

function Difference(const V1, V2: ShortIntArray; const IsSortedAscending: Boolean): ShortIntArray;
var I, J, L, LV : Integer;
begin
  SetLength(Result, 0);
  if IsSortedAscending then
    begin
      I := 0;
      J := 0;
      L := Length(V1);
      LV := Length(V2);
      While (I < L) and (J < LV) do
        begin
          While (I < L) and (V1[I] < V2[J]) do
            Inc(I);
          if I < L then
            begin
              if V1[I] <> V2[J] then
                Append(Result, V1[I]);
              While (J < LV) and (V2[J] <= V1[I]) do
                Inc(J);
            end;
        end;
    end
  else
    For I := 0 to Length(V1) - 1 do
      if (PosNext(V1[I], V2) = -1) and (PosNext(V1[I], Result) = -1) then
        Append(Result, V1[I]);
end;

function Difference(const V1, V2: SmallIntArray; const IsSortedAscending: Boolean): SmallIntArray;
var I, J, L, LV : Integer;
begin
  SetLength(Result, 0);
  if IsSortedAscending then
    begin
      I := 0;
      J := 0;
      L := Length(V1);
      LV := Length(V2);
      While (I < L) and (J < LV) do
        begin
          While (I < L) and (V1[I] < V2[J]) do
            Inc(I);
          if I < L then
            begin
              if V1[I] <> V2[J] then
                Append(Result, V1[I]);
              While (J < LV) and (V2[J] <= V1[I]) do
                Inc(J);
            end;
        end;
    end
  else
    For I := 0 to Length(V1) - 1 do
      if (PosNext(V1[I], V2) = -1) and (PosNext(V1[I], Result) = -1) then
        Append(Result, V1[I]);
end;

function Difference(const V1, V2: LongIntArray; const IsSortedAscending: Boolean): LongIntArray;
var I, J, L, LV : Integer;
begin
  SetLength(Result, 0);
  if IsSortedAscending then
    begin
      I := 0;
      J := 0;
      L := Length(V1);
      LV := Length(V2);
      While (I < L) and (J < LV) do
        begin
          While (I < L) and (V1[I] < V2[J]) do
            Inc(I);
          if I < L then
            begin
              if V1[I] <> V2[J] then
                Append(Result, V1[I]);
              While (J < LV) and (V2[J] <= V1[I]) do
                Inc(J);
            end;
        end;
    end
  else
    For I := 0 to Length(V1) - 1 do
      if (PosNext(V1[I], V2) = -1) and (PosNext(V1[I], Result) = -1) then
        Append(Result, V1[I]);
end;

function Difference(const V1, V2: Int64Array; const IsSortedAscending: Boolean): Int64Array;
var I, J, L, LV : Integer;
begin
  SetLength(Result, 0);
  if IsSortedAscending then
    begin
      I := 0;
      J := 0;
      L := Length(V1);
      LV := Length(V2);
      While (I < L) and (J < LV) do
        begin
          While (I < L) and (V1[I] < V2[J]) do
            Inc(I);
          if I < L then
            begin
              if V1[I] <> V2[J] then
                Append(Result, V1[I]);
              While (J < LV) and (V2[J] <= V1[I]) do
                Inc(J);
            end;
        end;
    end
  else
    For I := 0 to Length(V1) - 1 do
      if (PosNext(V1[I], V2) = -1) and (PosNext(V1[I], Result) = -1) then
        Append(Result, V1[I]);
end;

function Difference(const V1, V2: StringArray; const IsSortedAscending: Boolean): StringArray;
var I, J, L, LV : Integer;
begin
  SetLength(Result, 0);
  if IsSortedAscending then
    begin
      I := 0;
      J := 0;
      L := Length(V1);
      LV := Length(V2);
      While (I < L) and (J < LV) do
        begin
          While (I < L) and (V1[I] < V2[J]) do
            Inc(I);
          if I < L then
            begin
              if V1[I] <> V2[J] then
                Append(Result, V1[I]);
              While (J < LV) and (V2[J] <= V1[I]) do
                Inc(J);
            end;
        end;
    end
  else
    For I := 0 to Length(V1) - 1 do
      if (PosNext(V1[I], V2) = -1) and (PosNext(V1[I], Result) = -1) then
        Append(Result, V1[I]);
end;



{                                                                              }
{ Reverse                                                                      }
{                                                                              }
procedure Reverse(var V: ByteArray);
var I, L : Integer;
begin
  L := Length(V);
  For I := 1 to L div 2 do
    Swap(V[I - 1], V[L - I]);
end;

procedure Reverse(var V: WordArray);
var I, L : Integer;
begin
  L := Length(V);
  For I := 1 to L div 2 do
    Swap(V[I - 1], V[L - I]);
end;

procedure Reverse(var V: LongWordArray);
var I, L : Integer;
begin
  L := Length(V);
  For I := 1 to L div 2 do
    Swap(V[I - 1], V[L - I]);
end;

procedure Reverse(var V: ShortIntArray);
var I, L : Integer;
begin
  L := Length(V);
  For I := 1 to L div 2 do
    Swap(V[I - 1], V[L - I]);
end;

procedure Reverse(var V: SmallIntArray);
var I, L : Integer;
begin
  L := Length(V);
  For I := 1 to L div 2 do
    Swap(V[I - 1], V[L - I]);
end;

procedure Reverse(var V: LongIntArray);
var I, L : Integer;
begin
  L := Length(V);
  For I := 1 to L div 2 do
    Swap(V[I - 1], V[L - I]);
end;

procedure Reverse(var V: Int64Array);
var I, L : Integer;
begin
  L := Length(V);
  For I := 1 to L div 2 do
    Swap(V[I - 1], V[L - I]);
end;

procedure Reverse(var V: StringArray);
var I, L : Integer;
begin
  L := Length(V);
  For I := 1 to L div 2 do
    Swap(V[I - 1], V[L - I]);
end;

procedure Reverse(var V: PointerArray);
var I, L : Integer;
begin
  L := Length(V);
  For I := 1 to L div 2 do
    Swap(V[I - 1], V[L - I]);
end;

procedure Reverse(var V: ObjectArray);
var I, L : Integer;
begin
  L := Length(V);
  For I := 1 to L div 2 do
    Swap(V[I - 1], V[L - I]);
end;

procedure Reverse(var V: SingleArray);
var I, L : Integer;
begin
  L := Length(V);
  For I := 1 to L div 2 do
    Swap(V[I - 1], V[L - I]);
end;

procedure Reverse(var V: DoubleArray);
var I, L : Integer;
begin
  L := Length(V);
  For I := 1 to L div 2 do
    Swap(V[I - 1], V[L - I]);
end;

procedure Reverse(var V: ExtendedArray);
var I, L : Integer;
begin
  L := Length(V);
  For I := 1 to L div 2 do
    Swap(V[I - 1], V[L - I]);
end;



{                                                                              }
{ Returns an open array (V) as a dynamic array.                                }
{                                                                              }
function AsBooleanArray(const V: Array of Boolean): BooleanArray;
var I : Integer;
begin
  SetLength(Result, High(V) + 1);
  For I := 0 to High(V) do
    Result[I] := V[I];
end;

function AsByteArray(const V: Array of Byte): ByteArray;
var I : Integer;
begin
  SetLength(Result, High(V) + 1);
  For I := 0 to High(V) do
    Result[I] := V[I];
end;

function AsWordArray(const V: Array of Word): WordArray;
var I : Integer;
begin
  SetLength(Result, High(V) + 1);
  For I := 0 to High(V) do
    Result[I] := V[I];
end;

function AsLongWordArray(const V: Array of LongWord): LongWordArray;
var I : Integer;
begin
  SetLength(Result, High(V) + 1);
  For I := 0 to High(V) do
    Result[I] := V[I];
end;

function AsCardinalArray(const V: Array of Cardinal): CardinalArray;
var I : Integer;
begin
  SetLength(Result, High(V) + 1);
  For I := 0 to High(V) do
    Result[I] := V[I];
end;

function AsShortIntArray(const V: Array of ShortInt): ShortIntArray;
var I : Integer;
begin
  SetLength(Result, High(V) + 1);
  For I := 0 to High(V) do
    Result[I] := V[I];
end;

function AsSmallIntArray(const V: Array of SmallInt): SmallIntArray;
var I : Integer;
begin
  SetLength(Result, High(V) + 1);
  For I := 0 to High(V) do
    Result[I] := V[I];
end;

function AsLongIntArray(const V: Array of LongInt): LongIntArray;
var I : Integer;
begin
  SetLength(Result, High(V) + 1);
  For I := 0 to High(V) do
    Result[I] := V[I];
end;

function AsIntegerArray(const V: Array of Integer): IntegerArray;
var I : Integer;
begin
  SetLength(Result, High(V) + 1);
  For I := 0 to High(V) do
    Result[I] := V[I];
end;

function AsInt64Array(const V: Array of Int64): Int64Array;
var I : Integer;
begin
  SetLength(Result, High(V) + 1);
  For I := 0 to High(V) do
    Result[I] := V[I];
end;

function AsSingleArray(const V: Array of Single): SingleArray;
var I : Integer;
begin
  SetLength(Result, High(V) + 1);
  For I := 0 to High(V) do
    Result[I] := V[I];
end;

function AsDoubleArray(const V: Array of Double): DoubleArray;
var I : Integer;
begin
  SetLength(Result, High(V) + 1);
  For I := 0 to High(V) do
    Result[I] := V[I];
end;

function AsExtendedArray(const V: Array of Extended): ExtendedArray;
var I : Integer;
begin
  SetLength(Result, High(V) + 1);
  For I := 0 to High(V) do
    Result[I] := V[I];
end;

function AsCurrencyArray(const V: Array of Currency): CurrencyArray;
var I : Integer;
begin
  SetLength(Result, High(V) + 1);
  For I := 0 to High(V) do
    Result[I] := V[I];
end;

function AsStringArray(const V: Array of String): StringArray;
var I : Integer;
begin
  SetLength(Result, High(V) + 1);
  For I := 0 to High(V) do
    Result[I] := V[I];
end;

function AsWideStringArray(const V: Array of WideString): WideStringArray;
var I : Integer;
begin
  SetLength(Result, High(V) + 1);
  For I := 0 to High(V) do
    Result[I] := V[I];
end;

function AsPointerArray(const V: Array of Pointer): PointerArray;
var I : Integer;
begin
  SetLength(Result, High(V) + 1);
  For I := 0 to High(V) do
    Result[I] := V[I];
end;

function AsCharSetArray(const V: Array of CharSet): CharSetArray;
var I : Integer;
begin
  SetLength(Result, High(V) + 1);
  For I := 0 to High(V) do
    Result[I] := V[I];
end;

function AsObjectArray(const V: Array of TObject): ObjectArray;
var I : Integer;
begin
  SetLength(Result, High(V) + 1);
  For I := 0 to High(V) do
    Result[I] := V[I];
end;

function AsInterfaceArray(const V: Array of IInterface): InterfaceArray;
var I : Integer;
begin
  SetLength(Result, High(V) + 1);
  For I := 0 to High(V) do
    Result[I] := V[I];
end;



function RangeByte(const First: Byte; const Count: Integer; const Increment: Byte): ByteArray;
var I : Integer;
    J : Byte;
begin
  SetLength(Result, Count);
  J := First;
  For I := 0 to Count - 1 do
    begin
      Result[I] := J;
      J := J + Increment;
    end;
end;

function RangeWord(const First: Word; const Count: Integer; const Increment: Word): WordArray;
var I : Integer;
    J : Word;
begin
  SetLength(Result, Count);
  J := First;
  For I := 0 to Count - 1 do
    begin
      Result[I] := J;
      J := J + Increment;
    end;
end;

function RangeLongWord(const First: LongWord; const Count: Integer; const Increment: LongWord): LongWordArray;
var I : Integer;
    J : LongWord;
begin
  SetLength(Result, Count);
  J := First;
  For I := 0 to Count - 1 do
    begin
      Result[I] := J;
      J := J + Increment;
    end;
end;

function RangeCardinal(const First: Cardinal; const Count: Integer; const Increment: Cardinal): CardinalArray;
var I : Integer;
    J : Cardinal;
begin
  SetLength(Result, Count);
  J := First;
  For I := 0 to Count - 1 do
    begin
      Result[I] := J;
      J := J + Increment;
    end;
end;

function RangeShortInt(const First: ShortInt; const Count: Integer; const Increment: ShortInt): ShortIntArray;
var I : Integer;
    J : ShortInt;
begin
  SetLength(Result, Count);
  J := First;
  For I := 0 to Count - 1 do
    begin
      Result[I] := J;
      J := J + Increment;
    end;
end;

function RangeSmallInt(const First: SmallInt; const Count: Integer; const Increment: SmallInt): SmallIntArray;
var I : Integer;
    J : SmallInt;
begin
  SetLength(Result, Count);
  J := First;
  For I := 0 to Count - 1 do
    begin
      Result[I] := J;
      J := J + Increment;
    end;
end;

function RangeLongInt(const First: LongInt; const Count: Integer; const Increment: LongInt): LongIntArray;
var I : Integer;
    J : LongInt;
begin
  SetLength(Result, Count);
  J := First;
  For I := 0 to Count - 1 do
    begin
      Result[I] := J;
      J := J + Increment;
    end;
end;

function RangeInteger(const First: Integer; const Count: Integer; const Increment: Integer): IntegerArray;
var I : Integer;
    J : Integer;
begin
  SetLength(Result, Count);
  J := First;
  For I := 0 to Count - 1 do
    begin
      Result[I] := J;
      J := J + Increment;
    end;
end;

function RangeInt64(const First: Int64; const Count: Integer; const Increment: Int64): Int64Array;
var I : Integer;
    J : Int64;
begin
  SetLength(Result, Count);
  J := First;
  For I := 0 to Count - 1 do
    begin
      Result[I] := J;
      J := J + Increment;
    end;
end;

function RangeSingle(const First: Single; const Count: Integer; const Increment: Single): SingleArray;
var I : Integer;
    J : Single;
begin
  SetLength(Result, Count);
  J := First;
  For I := 0 to Count - 1 do
    begin
      Result[I] := J;
      J := J + Increment;
    end;
end;

function RangeDouble(const First: Double; const Count: Integer; const Increment: Double): DoubleArray;
var I : Integer;
    J : Double;
begin
  SetLength(Result, Count);
  J := First;
  For I := 0 to Count - 1 do
    begin
      Result[I] := J;
      J := J + Increment;
    end;
end;

function RangeExtended(const First: Extended; const Count: Integer; const Increment: Extended): ExtendedArray;
var I : Integer;
    J : Extended;
begin
  SetLength(Result, Count);
  J := First;
  For I := 0 to Count - 1 do
    begin
      Result[I] := J;
      J := J + Increment;
    end;
end;



{                                                                              }
{ Dup                                                                          }
{                                                                              }
function DupByte(const V: Byte; const Count: Integer): ByteArray;
begin
  SetLength(Result, Count);
  FillChar(Result[0], Count, V);
end;

function DupWord(const V: Word; const Count: Integer): WordArray;
var I : Integer;
begin
  SetLength(Result, Count);
  For I := 0 to Count - 1 do
    Result[I] := V;
end;

function DupLongWord(const V: LongWord; const Count: Integer): LongWordArray;
var I : Integer;
begin
  SetLength(Result, Count);
  For I := 0 to Count - 1 do
    Result[I] := V;
end;

function DupCardinal(const V: Cardinal; const Count: Integer): CardinalArray;
var I : Integer;
begin
  SetLength(Result, Count);
  For I := 0 to Count - 1 do
    Result[I] := V;
end;

function DupShortInt(const V: ShortInt; const Count: Integer): ShortIntArray;
var I : Integer;
begin
  SetLength(Result, Count);
  For I := 0 to Count - 1 do
    Result[I] := V;
end;

function DupSmallInt(const V: SmallInt; const Count: Integer): SmallIntArray;
var I : Integer;
begin
  SetLength(Result, Count);
  For I := 0 to Count - 1 do
    Result[I] := V;
end;

function DupLongInt(const V: LongInt; const Count: Integer): LongIntArray;
var I : Integer;
begin
  SetLength(Result, Count);
  For I := 0 to Count - 1 do
    Result[I] := V;
end;

function DupInteger(const V: Integer; const Count: Integer): IntegerArray;
var I : Integer;
begin
  SetLength(Result, Count);
  For I := 0 to Count - 1 do
    Result[I] := V;
end;

function DupInt64(const V: Int64; const Count: Integer): Int64Array;
var I : Integer;
begin
  SetLength(Result, Count);
  For I := 0 to Count - 1 do
    Result[I] := V;
end;

function DupSingle(const V: Single; const Count: Integer): SingleArray;
var I : Integer;
begin
  SetLength(Result, Count);
  For I := 0 to Count - 1 do
    Result[I] := V;
end;

function DupDouble(const V: Double; const Count: Integer): DoubleArray;
var I : Integer;
begin
  SetLength(Result, Count);
  For I := 0 to Count - 1 do
    Result[I] := V;
end;

function DupExtended(const V: Extended; const Count: Integer): ExtendedArray;
var I : Integer;
begin
  SetLength(Result, Count);
  For I := 0 to Count - 1 do
    Result[I] := V;
end;

function DupCurrency(const V: Currency; const Count: Integer): CurrencyArray;
var I : Integer;
begin
  SetLength(Result, Count);
  For I := 0 to Count - 1 do
    Result[I] := V;
end;

function DupString(const V: String; const Count: Integer): StringArray;
var I : Integer;
begin
  SetLength(Result, Count);
  For I := 0 to Count - 1 do
    Result[I] := V;
end;

function DupCharSet(const V: CharSet; const Count: Integer): CharSetArray;
var I : Integer;
begin
  SetLength(Result, Count);
  For I := 0 to Count - 1 do
    Result[I] := V;
end;

function DupObject(const V: TObject; const Count: Integer): ObjectArray;
var I : Integer;
begin
  SetLength(Result, Count);
  For I := 0 to Count - 1 do
    Result[I] := V;
end;



{                                                                              }
{ SetLengthAndZero                                                             }
{                                                                              }
procedure SetLengthAndZero(var V: ByteArray; const NewLength: Integer);
var OldLen, NewLen : Integer;
begin
  NewLen := NewLength;
  if NewLen < 0 then
    NewLen := 0;
  OldLen := Length(V);
  if OldLen = NewLen then
    exit;
  SetLength(V, NewLen);
  if OldLen > NewLen then
    exit;
  FillChar(Pointer(@V[OldLen])^, Sizeof(Byte) * (NewLen - OldLen), #0);
end;

procedure SetLengthAndZero(var V: WordArray; const NewLength: Integer);
var OldLen, NewLen : Integer;
begin
  NewLen := NewLength;
  if NewLen < 0 then
    NewLen := 0;
  OldLen := Length(V);
  if OldLen = NewLen then
    exit;
  SetLength(V, NewLen);
  if OldLen > NewLen then
    exit;
  FillChar(Pointer(@V[OldLen])^, Sizeof(Word) * (NewLen - OldLen), #0);
end;

procedure SetLengthAndZero(var V: LongWordArray; const NewLength: Integer);
var OldLen, NewLen : Integer;
begin
  NewLen := NewLength;
  if NewLen < 0 then
    NewLen := 0;
  OldLen := Length(V);
  if OldLen = NewLen then
    exit;
  SetLength(V, NewLen);
  if OldLen > NewLen then
    exit;
  FillChar(Pointer(@V[OldLen])^, Sizeof(LongWord) * (NewLen - OldLen), #0);
end;

procedure SetLengthAndZero(var V: ShortIntArray; const NewLength: Integer);
var OldLen, NewLen : Integer;
begin
  NewLen := NewLength;
  if NewLen < 0 then
    NewLen := 0;
  OldLen := Length(V);
  if OldLen = NewLen then
    exit;
  SetLength(V, NewLen);
  if OldLen > NewLen then
    exit;
  FillChar(Pointer(@V[OldLen])^, Sizeof(ShortInt) * (NewLen - OldLen), #0);
end;

procedure SetLengthAndZero(var V: SmallIntArray; const NewLength: Integer);
var OldLen, NewLen : Integer;
begin
  NewLen := NewLength;
  if NewLen < 0 then
    NewLen := 0;
  OldLen := Length(V);
  if OldLen = NewLen then
    exit;
  SetLength(V, NewLen);
  if OldLen > NewLen then
    exit;
  FillChar(Pointer(@V[OldLen])^, Sizeof(SmallInt) * (NewLen - OldLen), #0);
end;

procedure SetLengthAndZero(var V: LongIntArray; const NewLength: Integer);
var OldLen, NewLen : Integer;
begin
  NewLen := NewLength;
  if NewLen < 0 then
    NewLen := 0;
  OldLen := Length(V);
  if OldLen = NewLen then
    exit;
  SetLength(V, NewLen);
  if OldLen > NewLen then
    exit;
  FillChar(Pointer(@V[OldLen])^, Sizeof(LongInt) * (NewLen - OldLen), #0);
end;

procedure SetLengthAndZero(var V: Int64Array; const NewLength: Integer);
var OldLen, NewLen : Integer;
begin
  NewLen := NewLength;
  if NewLen < 0 then
    NewLen := 0;
  OldLen := Length(V);
  if OldLen = NewLen then
    exit;
  SetLength(V, NewLen);
  if OldLen > NewLen then
    exit;
  FillChar(Pointer(@V[OldLen])^, Sizeof(Int64) * (NewLen - OldLen), #0);
end;

procedure SetLengthAndZero(var V: SingleArray; const NewLength: Integer);
var OldLen, NewLen : Integer;
begin
  NewLen := NewLength;
  if NewLen < 0 then
    NewLen := 0;
  OldLen := Length(V);
  if OldLen = NewLen then
    exit;
  SetLength(V, NewLen);
  if OldLen > NewLen then
    exit;
  FillChar(Pointer(@V[OldLen])^, Sizeof(Single) * (NewLen - OldLen), #0);
end;

procedure SetLengthAndZero(var V: DoubleArray; const NewLength: Integer);
var OldLen, NewLen : Integer;
begin
  NewLen := NewLength;
  if NewLen < 0 then
    NewLen := 0;
  OldLen := Length(V);
  if OldLen = NewLen then
    exit;
  SetLength(V, NewLen);
  if OldLen > NewLen then
    exit;
  FillChar(Pointer(@V[OldLen])^, Sizeof(Double) * (NewLen - OldLen), #0);
end;

procedure SetLengthAndZero(var V: ExtendedArray; const NewLength: Integer);
var OldLen, NewLen : Integer;
begin
  NewLen := NewLength;
  if NewLen < 0 then
    NewLen := 0;
  OldLen := Length(V);
  if OldLen = NewLen then
    exit;
  SetLength(V, NewLen);
  if OldLen > NewLen then
    exit;
  FillChar(Pointer(@V[OldLen])^, Sizeof(Extended) * (NewLen - OldLen), #0);
end;

procedure SetLengthAndZero(var V: CurrencyArray; const NewLength: Integer);
var OldLen, NewLen : Integer;
begin
  NewLen := NewLength;
  if NewLen < 0 then
    NewLen := 0;
  OldLen := Length(V);
  if OldLen = NewLen then
    exit;
  SetLength(V, NewLen);
  if OldLen > NewLen then
    exit;
  FillChar(Pointer(@V[OldLen])^, Sizeof(Currency) * (NewLen - OldLen), #0);
end;

procedure SetLengthAndZero(var V: CharSetArray; const NewLength: Integer);
var OldLen, NewLen : Integer;
begin
  NewLen := NewLength;
  if NewLen < 0 then
    NewLen := 0;
  OldLen := Length(V);
  if OldLen = NewLen then
    exit;
  SetLength(V, NewLen);
  if OldLen > NewLen then
    exit;
  FillChar(Pointer(@V[OldLen])^, Sizeof(CharSet) * (NewLen - OldLen), #0);
end;

procedure SetLengthAndZero(var V: BooleanArray; const NewLength: Integer);
var OldLen, NewLen : Integer;
begin
  NewLen := NewLength;
  if NewLen < 0 then
    NewLen := 0;
  OldLen := Length(V);
  if OldLen = NewLen then
    exit;
  SetLength(V, NewLen);
  if OldLen > NewLen then
    exit;
  FillChar(Pointer(@V[OldLen])^, Sizeof(Boolean) * (NewLen - OldLen), #0);
end;

procedure SetLengthAndZero(var V: PointerArray; const NewLength: Integer);
var OldLen, NewLen : Integer;
begin
  NewLen := NewLength;
  if NewLen < 0 then
    NewLen := 0;
  OldLen := Length(V);
  if OldLen = NewLen then
    exit;
  SetLength(V, NewLen);
  if OldLen > NewLen then
    exit;
  FillChar(Pointer(@V[OldLen])^, Sizeof(Pointer) * (NewLen - OldLen), #0);
end;

procedure SetLengthAndZero(var V: ObjectArray; const NewLength: Integer;
    const FreeObjects: Boolean);
var I, L : Integer;
begin
  L := Length(V);
  if L = NewLength then
    exit;
  if (L > NewLength) and FreeObjects then
    For I := NewLength to L - 1 do
      FreeAndNil(V[I]);
  SetLength(V, NewLength);
  if L > NewLength then
    exit;
  FillChar(V[L], Sizeof(Pointer) * (NewLength - L), #0);
end;



{                                                                              }
{ IsEqual                                                                      }
{                                                                              }
function IsEqual(const V1, V2: ByteArray): Boolean;
var L : Integer;
begin
  L := Length(V1);
  if L <> Length(V2) then
    begin
      Result := False;
      exit;
    end;
  Result := CompareMem(Pointer(V1)^, Pointer(V2)^, Sizeof(Byte) * L);
end;

function IsEqual(const V1, V2: WordArray): Boolean;
var L : Integer;
begin
  L := Length(V1);
  if L <> Length(V2) then
    begin
      Result := False;
      exit;
    end;
  Result := CompareMem(Pointer(V1)^, Pointer(V2)^, Sizeof(Word) * L);
end;

function IsEqual(const V1, V2: LongWordArray): Boolean;
var L : Integer;
begin
  L := Length(V1);
  if L <> Length(V2) then
    begin
      Result := False;
      exit;
    end;
  Result := CompareMem(Pointer(V1)^, Pointer(V2)^, Sizeof(LongWord) * L);
end;

function IsEqual(const V1, V2: ShortIntArray): Boolean;
var L : Integer;
begin
  L := Length(V1);
  if L <> Length(V2) then
    begin
      Result := False;
      exit;
    end;
  Result := CompareMem(Pointer(V1)^, Pointer(V2)^, Sizeof(ShortInt) * L);
end;

function IsEqual(const V1, V2: SmallIntArray): Boolean;
var L : Integer;
begin
  L := Length(V1);
  if L <> Length(V2) then
    begin
      Result := False;
      exit;
    end;
  Result := CompareMem(Pointer(V1)^, Pointer(V2)^, Sizeof(SmallInt) * L);
end;

function IsEqual(const V1, V2: LongIntArray): Boolean;
var L : Integer;
begin
  L := Length(V1);
  if L <> Length(V2) then
    begin
      Result := False;
      exit;
    end;
  Result := CompareMem(Pointer(V1)^, Pointer(V2)^, Sizeof(LongInt) * L);
end;

function IsEqual(const V1, V2: Int64Array): Boolean;
var L : Integer;
begin
  L := Length(V1);
  if L <> Length(V2) then
    begin
      Result := False;
      exit;
    end;
  Result := CompareMem(Pointer(V1)^, Pointer(V2)^, Sizeof(Int64) * L);
end;

function IsEqual(const V1, V2: SingleArray): Boolean;
var L : Integer;
begin
  L := Length(V1);
  if L <> Length(V2) then
    begin
      Result := False;
      exit;
    end;
  Result := CompareMem(Pointer(V1)^, Pointer(V2)^, Sizeof(Single) * L);
end;

function IsEqual(const V1, V2: DoubleArray): Boolean;
var L : Integer;
begin
  L := Length(V1);
  if L <> Length(V2) then
    begin
      Result := False;
      exit;
    end;
  Result := CompareMem(Pointer(V1)^, Pointer(V2)^, Sizeof(Double) * L);
end;

function IsEqual(const V1, V2: ExtendedArray): Boolean;
var L : Integer;
begin
  L := Length(V1);
  if L <> Length(V2) then
    begin
      Result := False;
      exit;
    end;
  Result := CompareMem(Pointer(V1)^, Pointer(V2)^, Sizeof(Extended) * L);
end;

function IsEqual(const V1, V2: CurrencyArray): Boolean;
var L : Integer;
begin
  L := Length(V1);
  if L <> Length(V2) then
    begin
      Result := False;
      exit;
    end;
  Result := CompareMem(Pointer(V1)^, Pointer(V2)^, Sizeof(Currency) * L);
end;

function IsEqual(const V1, V2: StringArray): Boolean;
var I, L : Integer;
begin
  L := Length(V1);
  if L <> Length(V2) then
    begin
      Result := False;
      exit;
    end;
  For I := 0 to L - 1 do
    if V1[I] <> V2[I] then
      begin
        Result := False;
        exit;
      end;
  Result := True;
end;

function IsEqual(const V1, V2: CharSetArray): Boolean;
var I, L : Integer;
begin
  L := Length(V1);
  if L <> Length(V2) then
    begin
      Result := False;
      exit;
    end;
  For I := 0 to L - 1 do
    if V1[I] <> V2[I] then
      begin
        Result := False;
        exit;
      end;
  Result := True;
end;



{                                                                              }
{ Dynamic array to Dynamic array                                               }
{                                                                              }
function ByteArrayToLongIntArray(const V: ByteArray): LongIntArray;
var I, L : Integer;
begin
  L := Length(V);
  SetLength(Result, L);
  For I := 0 to L - 1 do
    Result[I] := V[I];
end;

function WordArrayToLongIntArray(const V: WordArray): LongIntArray;
var I, L : Integer;
begin
  L := Length(V);
  SetLength(Result, L);
  For I := 0 to L - 1 do
    Result[I] := V[I];
end;

function ShortIntArrayToLongIntArray(const V: ShortIntArray): LongIntArray;
var I, L : Integer;
begin
  L := Length(V);
  SetLength(Result, L);
  For I := 0 to L - 1 do
    Result[I] := V[I];
end;

function SmallIntArrayToLongIntArray(const V: SmallIntArray): LongIntArray;
var I, L : Integer;
begin
  L := Length(V);
  SetLength(Result, L);
  For I := 0 to L - 1 do
    Result[I] := V[I];
end;

function LongIntArrayToInt64Array(const V: LongIntArray): Int64Array;
var I, L : Integer;
begin
  L := Length(V);
  SetLength(Result, L);
  For I := 0 to L - 1 do
    Result[I] := V[I];
end;

function LongIntArrayToSingleArray(const V: LongIntArray): SingleArray;
var I, L : Integer;
begin
  L := Length(V);
  SetLength(Result, L);
  For I := 0 to L - 1 do
    Result[I] := V[I];
end;

function LongIntArrayToDoubleArray(const V: LongIntArray): DoubleArray;
var I, L : Integer;
begin
  L := Length(V);
  SetLength(Result, L);
  For I := 0 to L - 1 do
    Result[I] := V[I];
end;

function LongIntArrayToExtendedArray(const V: LongIntArray): ExtendedArray;
var I, L : Integer;
begin
  L := Length(V);
  SetLength(Result, L);
  For I := 0 to L - 1 do
    Result[I] := V[I];
end;

function SingleArrayToDoubleArray(const V: SingleArray): DoubleArray;
var I, L : Integer;
begin
  L := Length(V);
  SetLength(Result, L);
  For I := 0 to L - 1 do
    Result[I] := V[I];
end;

function SingleArrayToExtendedArray(const V: SingleArray): ExtendedArray;
var I, L : Integer;
begin
  L := Length(V);
  SetLength(Result, L);
  For I := 0 to L - 1 do
    Result[I] := V[I];
end;

function SingleArrayToCurrencyArray(const V: SingleArray): CurrencyArray;
var I, L : Integer;
begin
  L := Length(V);
  SetLength(Result, L);
  For I := 0 to L - 1 do
    Result[I] := V[I];
end;

function SingleArrayToLongIntArray(const V: SingleArray): LongIntArray;
var I, L : Integer;
begin
  L := Length(V);
  SetLength(Result, L);
  For I := 0 to L - 1 do
    Result[I] := LongInt(Trunc(V[I]));
end;

function SingleArrayToInt64Array(const V: SingleArray): Int64Array;
var I, L : Integer;
begin
  L := Length(V);
  SetLength(Result, L);
  For I := 0 to L - 1 do
    Result[I] := Trunc(V[I]);
end;

function DoubleArrayToExtendedArray(const V: DoubleArray): ExtendedArray;
var I, L : Integer;
begin
  L := Length(V);
  SetLength(Result, L);
  For I := 0 to L - 1 do
    Result[I] := V[I];
end;

function DoubleArrayToCurrencyArray(const V: DoubleArray): CurrencyArray;
var I, L : Integer;
begin
  L := Length(V);
  SetLength(Result, L);
  For I := 0 to L - 1 do
    Result[I] := V[I];
end;

function DoubleArrayToLongIntArray(const V: DoubleArray): LongIntArray;
var I, L : Integer;
begin
  L := Length(V);
  SetLength(Result, L);
  For I := 0 to L - 1 do
    Result[I] := LongInt(Trunc(V[I]));
end;

function DoubleArrayToInt64Array(const V: DoubleArray): Int64Array;
var I, L : Integer;
begin
  L := Length(V);
  SetLength(Result, L);
  For I := 0 to L - 1 do
    Result[I] := Trunc(V[I]);
end;

function ExtendedArrayToCurrencyArray(const V: ExtendedArray): CurrencyArray;
var I, L : Integer;
begin
  L := Length(V);
  SetLength(Result, L);
  For I := 0 to L - 1 do
    Result[I] := V[I];
end;

function ExtendedArrayToLongIntArray(const V: ExtendedArray): LongIntArray;
var I, L : Integer;
begin
  L := Length(V);
  SetLength(Result, L);
  For I := 0 to L - 1 do
    Result[I] := LongInt(Trunc(V[I]));
end;

function ExtendedArrayToInt64Array(const V: ExtendedArray): Int64Array;
var I, L : Integer;
begin
  L := Length(V);
  SetLength(Result, L);
  For I := 0 to L - 1 do
    Result[I] := Trunc(V[I]);
end;



{                                                                              }
{ Array from indexes                                                           }
{                                                                              }
function ByteArrayFromIndexes(const V: ByteArray; const Indexes: IntegerArray): ByteArray;
var I, L : Integer;
begin
  L := Length(Indexes);
  SetLength(Result, L);
  For I := 0 to L - 1 do
    Result[I] := V[Indexes[I]];
end;

function WordArrayFromIndexes(const V: WordArray; const Indexes: IntegerArray): WordArray;
var I, L : Integer;
begin
  L := Length(Indexes);
  SetLength(Result, L);
  For I := 0 to L - 1 do
    Result[I] := V[Indexes[I]];
end;

function LongWordArrayFromIndexes(const V: LongWordArray; const Indexes: IntegerArray): LongWordArray;
var I, L : Integer;
begin
  L := Length(Indexes);
  SetLength(Result, L);
  For I := 0 to L - 1 do
    Result[I] := V[Indexes[I]];
end;

function CardinalArrayFromIndexes(const V: CardinalArray; const Indexes: IntegerArray): CardinalArray;
var I, L : Integer;
begin
  L := Length(Indexes);
  SetLength(Result, L);
  For I := 0 to L - 1 do
    Result[I] := V[Indexes[I]];
end;

function ShortIntArrayFromIndexes(const V: ShortIntArray; const Indexes: IntegerArray): ShortIntArray;
var I, L : Integer;
begin
  L := Length(Indexes);
  SetLength(Result, L);
  For I := 0 to L - 1 do
    Result[I] := V[Indexes[I]];
end;

function SmallIntArrayFromIndexes(const V: SmallIntArray; const Indexes: IntegerArray): SmallIntArray;
var I, L : Integer;
begin
  L := Length(Indexes);
  SetLength(Result, L);
  For I := 0 to L - 1 do
    Result[I] := V[Indexes[I]];
end;

function LongIntArrayFromIndexes(const V: LongIntArray; const Indexes: IntegerArray): LongIntArray;
var I, L : Integer;
begin
  L := Length(Indexes);
  SetLength(Result, L);
  For I := 0 to L - 1 do
    Result[I] := V[Indexes[I]];
end;

function IntegerArrayFromIndexes(const V: IntegerArray; const Indexes: IntegerArray): IntegerArray;
var I, L : Integer;
begin
  L := Length(Indexes);
  SetLength(Result, L);
  For I := 0 to L - 1 do
    Result[I] := V[Indexes[I]];
end;

function Int64ArrayFromIndexes(const V: Int64Array; const Indexes: IntegerArray): Int64Array;
var I, L : Integer;
begin
  L := Length(Indexes);
  SetLength(Result, L);
  For I := 0 to L - 1 do
    Result[I] := V[Indexes[I]];
end;

function SingleArrayFromIndexes(const V: SingleArray; const Indexes: IntegerArray): SingleArray;
var I, L : Integer;
begin
  L := Length(Indexes);
  SetLength(Result, L);
  For I := 0 to L - 1 do
    Result[I] := V[Indexes[I]];
end;

function DoubleArrayFromIndexes(const V: DoubleArray; const Indexes: IntegerArray): DoubleArray;
var I, L : Integer;
begin
  L := Length(Indexes);
  SetLength(Result, L);
  For I := 0 to L - 1 do
    Result[I] := V[Indexes[I]];
end;

function ExtendedArrayFromIndexes(const V: ExtendedArray; const Indexes: IntegerArray): ExtendedArray;
var I, L : Integer;
begin
  L := Length(Indexes);
  SetLength(Result, L);
  For I := 0 to L - 1 do
    Result[I] := V[Indexes[I]];
end;

function StringArrayFromIndexes(const V: StringArray; const Indexes: IntegerArray): StringArray;
var I, L : Integer;
begin
  L := Length(Indexes);
  SetLength(Result, L);
  For I := 0 to L - 1 do
    Result[I] := V[Indexes[I]];
end;



{                                                                              }
{ Dynamic array sort                                                           }
{                                                                              }
procedure Sort(const V: ByteArray);

  procedure QuickSort(L, R: Integer);
  var I, J, M : Integer;
      W, T    : Byte;
      P, Q    : PByte;
  begin
    Repeat
      I := L;
      P := @V[I];
      J := R;
      Q := @V[J];
      M := (L + R) shr 1;
      W := V[M];
      Repeat
        While P^ < W do
          begin
            Inc(P);
            Inc(I);
          end;
        While Q^ > W do
          begin
            Dec(Q);
            Dec(J);
          end;
        if I <= J then
          begin
            T := P^;
            P^ := Q^;
            Q^ := T;
            if M = I then
              begin
                M := J;
                W := Q^;
              end else
              if M = J then
                begin
                  M := I;
                  W := P^;
                end;
            Inc(P);
            Inc(I);
            Dec(Q);
            Dec(J);
          end;
      Until I > J;
      if L < J then
        QuickSort(L, J);
      L := I;
    Until I >= R;
  end;

var I : Integer;
begin
  I := Length(V);
  if I > 0 then
    QuickSort(0, I - 1);
end;

procedure Sort(const V: WordArray);

  procedure QuickSort(L, R: Integer);
  var I, J, M : Integer;
      W, T    : Word;
      P, Q    : PWord;
  begin
    Repeat
      I := L;
      P := @V[I];
      J := R;
      Q := @V[J];
      M := (L + R) shr 1;
      W := V[M];
      Repeat
        While P^ < W do
          begin
            Inc(P);
            Inc(I);
          end;
        While Q^ > W do
          begin
            Dec(Q);
            Dec(J);
          end;
        if I <= J then
          begin
            T := P^;
            P^ := Q^;
            Q^ := T;
            if M = I then
              begin
                M := J;
                W := Q^;
              end else
              if M = J then
                begin
                  M := I;
                  W := P^;
                end;
            Inc(P);
            Inc(I);
            Dec(Q);
            Dec(J);
          end;
      Until I > J;
      if L < J then
        QuickSort(L, J);
      L := I;
    Until I >= R;
  end;

var I : Integer;
begin
  I := Length(V);
  if I > 0 then
    QuickSort(0, I - 1);
end;

procedure Sort(const V: LongWordArray);

  procedure QuickSort(L, R: Integer);
  var I, J, M : Integer;
      W, T    : LongWord;
      P, Q    : PLongWord;
  begin
    Repeat
      I := L;
      P := @V[I];
      J := R;
      Q := @V[J];
      M := (L + R) shr 1;
      W := V[M];
      Repeat
        While P^ < W do
          begin
            Inc(P);
            Inc(I);
          end;
        While Q^ > W do
          begin
            Dec(Q);
            Dec(J);
          end;
        if I <= J then
          begin
            T := P^;
            P^ := Q^;
            Q^ := T;
            if M = I then
              begin
                M := J;
                W := Q^;
              end else
              if M = J then
                begin
                  M := I;
                  W := P^;
                end;
            Inc(P);
            Inc(I);
            Dec(Q);
            Dec(J);
          end;
      Until I > J;
      if L < J then
        QuickSort(L, J);
      L := I;
    Until I >= R;
  end;

var I : Integer;
begin
  I := Length(V);
  if I > 0 then
    QuickSort(0, I - 1);
end;

procedure Sort(const V: ShortIntArray);

  procedure QuickSort(L, R: Integer);
  var I, J, M : Integer;
      W, T    : ShortInt;
      P, Q    : PShortInt;
  begin
    Repeat
      I := L;
      P := @V[I];
      J := R;
      Q := @V[J];
      M := (L + R) shr 1;
      W := V[M];
      Repeat
        While P^ < W do
          begin
            Inc(P);
            Inc(I);
          end;
        While Q^ > W do
          begin
            Dec(Q);
            Dec(J);
          end;
        if I <= J then
          begin
            T := P^;
            P^ := Q^;
            Q^ := T;
            if M = I then
              begin
                M := J;
                W := Q^;
              end else
              if M = J then
                begin
                  M := I;
                  W := P^;
                end;
            Inc(P);
            Inc(I);
            Dec(Q);
            Dec(J);
          end;
      Until I > J;
      if L < J then
        QuickSort(L, J);
      L := I;
    Until I >= R;
  end;

var I : Integer;
begin
  I := Length(V);
  if I > 0 then
    QuickSort(0, I - 1);
end;

procedure Sort(const V: SmallIntArray);

  procedure QuickSort(L, R: Integer);
  var I, J, M : Integer;
      W, T    : SmallInt;
      P, Q    : PSmallInt;
  begin
    Repeat
      I := L;
      P := @V[I];
      J := R;
      Q := @V[J];
      M := (L + R) shr 1;
      W := V[M];
      Repeat
        While P^ < W do
          begin
            Inc(P);
            Inc(I);
          end;
        While Q^ > W do
          begin
            Dec(Q);
            Dec(J);
          end;
        if I <= J then
          begin
            T := P^;
            P^ := Q^;
            Q^ := T;
            if M = I then
              begin
                M := J;
                W := Q^;
              end else
              if M = J then
                begin
                  M := I;
                  W := P^;
                end;
            Inc(P);
            Inc(I);
            Dec(Q);
            Dec(J);
          end;
      Until I > J;
      if L < J then
        QuickSort(L, J);
      L := I;
    Until I >= R;
  end;

var I : Integer;
begin
  I := Length(V);
  if I > 0 then
    QuickSort(0, I - 1);
end;

procedure Sort(const V: LongIntArray);

  procedure QuickSort(L, R: Integer);
  var I, J, M : Integer;
      W, T    : LongInt;
      P, Q    : PLongInt;
  begin
    Repeat
      I := L;
      P := @V[I];
      J := R;
      Q := @V[J];
      M := (L + R) shr 1;
      W := V[M];
      Repeat
        While P^ < W do
          begin
            Inc(P);
            Inc(I);
          end;
        While Q^ > W do
          begin
            Dec(Q);
            Dec(J);
          end;
        if I <= J then
          begin
            T := P^;
            P^ := Q^;
            Q^ := T;
            if M = I then
              begin
                M := J;
                W := Q^;
              end else
              if M = J then
                begin
                  M := I;
                  W := P^;
                end;
            Inc(P);
            Inc(I);
            Dec(Q);
            Dec(J);
          end;
      Until I > J;
      if L < J then
        QuickSort(L, J);
      L := I;
    Until I >= R;
  end;

var I : Integer;
begin
  I := Length(V);
  if I > 0 then
    QuickSort(0, I - 1);
end;

procedure Sort(const V: Int64Array);

  procedure QuickSort(L, R: Integer);
  var I, J, M : Integer;
      W, T    : Int64;
      P, Q    : PInt64;
  begin
    Repeat
      I := L;
      P := @V[I];
      J := R;
      Q := @V[J];
      M := (L + R) shr 1;
      W := V[M];
      Repeat
        While P^ < W do
          begin
            Inc(P);
            Inc(I);
          end;
        While Q^ > W do
          begin
            Dec(Q);
            Dec(J);
          end;
        if I <= J then
          begin
            T := P^;
            P^ := Q^;
            Q^ := T;
            if M = I then
              begin
                M := J;
                W := Q^;
              end else
              if M = J then
                begin
                  M := I;
                  W := P^;
                end;
            Inc(P);
            Inc(I);
            Dec(Q);
            Dec(J);
          end;
      Until I > J;
      if L < J then
        QuickSort(L, J);
      L := I;
    Until I >= R;
  end;

var I : Integer;
begin
  I := Length(V);
  if I > 0 then
    QuickSort(0, I - 1);
end;

procedure Sort(const V: SingleArray);

  procedure QuickSort(L, R: Integer);
  var I, J, M : Integer;
      W, T    : Single;
      P, Q    : PSingle;
  begin
    Repeat
      I := L;
      P := @V[I];
      J := R;
      Q := @V[J];
      M := (L + R) shr 1;
      W := V[M];
      Repeat
        While P^ < W do
          begin
            Inc(P);
            Inc(I);
          end;
        While Q^ > W do
          begin
            Dec(Q);
            Dec(J);
          end;
        if I <= J then
          begin
            T := P^;
            P^ := Q^;
            Q^ := T;
            if M = I then
              begin
                M := J;
                W := Q^;
              end else
              if M = J then
                begin
                  M := I;
                  W := P^;
                end;
            Inc(P);
            Inc(I);
            Dec(Q);
            Dec(J);
          end;
      Until I > J;
      if L < J then
        QuickSort(L, J);
      L := I;
    Until I >= R;
  end;

var I : Integer;
begin
  I := Length(V);
  if I > 0 then
    QuickSort(0, I - 1);
end;

procedure Sort(const V: DoubleArray);

  procedure QuickSort(L, R: Integer);
  var I, J, M : Integer;
      W, T    : Double;
      P, Q    : PDouble;
  begin
    Repeat
      I := L;
      P := @V[I];
      J := R;
      Q := @V[J];
      M := (L + R) shr 1;
      W := V[M];
      Repeat
        While P^ < W do
          begin
            Inc(P);
            Inc(I);
          end;
        While Q^ > W do
          begin
            Dec(Q);
            Dec(J);
          end;
        if I <= J then
          begin
            T := P^;
            P^ := Q^;
            Q^ := T;
            if M = I then
              begin
                M := J;
                W := Q^;
              end else
              if M = J then
                begin
                  M := I;
                  W := P^;
                end;
            Inc(P);
            Inc(I);
            Dec(Q);
            Dec(J);
          end;
      Until I > J;
      if L < J then
        QuickSort(L, J);
      L := I;
    Until I >= R;
  end;

var I : Integer;
begin
  I := Length(V);
  if I > 0 then
    QuickSort(0, I - 1);
end;

procedure Sort(const V: ExtendedArray);

  procedure QuickSort(L, R: Integer);
  var I, J, M : Integer;
      W, T    : Extended;
      P, Q    : PExtended;
  begin
    Repeat
      I := L;
      P := @V[I];
      J := R;
      Q := @V[J];
      M := (L + R) shr 1;
      W := V[M];
      Repeat
        While P^ < W do
          begin
            Inc(P);
            Inc(I);
          end;
        While Q^ > W do
          begin
            Dec(Q);
            Dec(J);
          end;
        if I <= J then
          begin
            T := P^;
            P^ := Q^;
            Q^ := T;
            if M = I then
              begin
                M := J;
                W := Q^;
              end else
              if M = J then
                begin
                  M := I;
                  W := P^;
                end;
            Inc(P);
            Inc(I);
            Dec(Q);
            Dec(J);
          end;
      Until I > J;
      if L < J then
        QuickSort(L, J);
      L := I;
    Until I >= R;
  end;

var I : Integer;
begin
  I := Length(V);
  if I > 0 then
    QuickSort(0, I - 1);
end;

procedure Sort(const V: StringArray);

  procedure QuickSort(L, R: Integer);
  var I, J, M : Integer;
      W, T    : String;
      P, Q    : PString;
  begin
    Repeat
      I := L;
      P := @V[I];
      J := R;
      Q := @V[J];
      M := (L + R) shr 1;
      W := V[M];
      Repeat
        While P^ < W do
          begin
            Inc(P);
            Inc(I);
          end;
        While Q^ > W do
          begin
            Dec(Q);
            Dec(J);
          end;
        if I <= J then
          begin
            T := P^;
            P^ := Q^;
            Q^ := T;
            if M = I then
              begin
                M := J;
                W := Q^;
              end else
              if M = J then
                begin
                  M := I;
                  W := P^;
                end;
            Inc(P);
            Inc(I);
            Dec(Q);
            Dec(J);
          end;
      Until I > J;
      if L < J then
        QuickSort(L, J);
      L := I;
    Until I >= R;
  end;

var I : Integer;
begin
  I := Length(V);
  if I > 0 then
    QuickSort(0, I - 1);
end;



procedure Sort(const Key: IntegerArray; const Data: IntegerArray);

  procedure QuickSort(L, R: Integer);
  var I, J, M : Integer;
      W, T    : Integer;
      P, Q    : PInteger;
      A       : Integer;
  begin
    Repeat
      I := L;
      P := @Key[I];
      J := R;
      Q := @Key[J];
      M := (L + R) shr 1;
      W := Key[M];
      Repeat
        While P^ < W do
          begin
            Inc(P);
            Inc(I);
          end;
        While Q^ > W do
          begin
            Dec(Q);
            Dec(J);
          end;
        if I <= J then
          begin
            T := P^;
            P^ := Q^;
            Q^ := T;
            A := Data[I];
            Data[I] := Data[J];
            Data[J] := A;
            if M = I then
              begin
                M := J;
                W := Q^;
              end else
              if M = J then
                begin
                  M := I;
                  W := P^;
                end;
            Inc(P);
            Inc(I);
            Dec(Q);
            Dec(J);
          end;
      Until I > J;
      if L < J then
        QuickSort(L, J);
      L := I;
    Until I >= R;
  end;

var I : Integer;
begin
  Assert(Length(Key) = Length(Data), 'Sort pair must be of equal length');
  I := Length(Key);
  if I > 0 then
    QuickSort(0, I - 1);
end;

procedure Sort(const Key: IntegerArray; const Data: Int64Array);

  procedure QuickSort(L, R: Integer);
  var I, J, M : Integer;
      W, T    : Integer;
      P, Q    : PInteger;
      A       : Int64;
  begin
    Repeat
      I := L;
      P := @Key[I];
      J := R;
      Q := @Key[J];
      M := (L + R) shr 1;
      W := Key[M];
      Repeat
        While P^ < W do
          begin
            Inc(P);
            Inc(I);
          end;
        While Q^ > W do
          begin
            Dec(Q);
            Dec(J);
          end;
        if I <= J then
          begin
            T := P^;
            P^ := Q^;
            Q^ := T;
            A := Data[I];
            Data[I] := Data[J];
            Data[J] := A;
            if M = I then
              begin
                M := J;
                W := Q^;
              end else
              if M = J then
                begin
                  M := I;
                  W := P^;
                end;
            Inc(P);
            Inc(I);
            Dec(Q);
            Dec(J);
          end;
      Until I > J;
      if L < J then
        QuickSort(L, J);
      L := I;
    Until I >= R;
  end;

var I : Integer;
begin
  Assert(Length(Key) = Length(Data), 'Sort pair must be of equal length');
  I := Length(Key);
  if I > 0 then
    QuickSort(0, I - 1);
end;

procedure Sort(const Key: IntegerArray; const Data: StringArray);

  procedure QuickSort(L, R: Integer);
  var I, J, M : Integer;
      W, T    : Integer;
      P, Q    : PInteger;
      A       : String;
  begin
    Repeat
      I := L;
      P := @Key[I];
      J := R;
      Q := @Key[J];
      M := (L + R) shr 1;
      W := Key[M];
      Repeat
        While P^ < W do
          begin
            Inc(P);
            Inc(I);
          end;
        While Q^ > W do
          begin
            Dec(Q);
            Dec(J);
          end;
        if I <= J then
          begin
            T := P^;
            P^ := Q^;
            Q^ := T;
            A := Data[I];
            Data[I] := Data[J];
            Data[J] := A;
            if M = I then
              begin
                M := J;
                W := Q^;
              end else
              if M = J then
                begin
                  M := I;
                  W := P^;
                end;
            Inc(P);
            Inc(I);
            Dec(Q);
            Dec(J);
          end;
      Until I > J;
      if L < J then
        QuickSort(L, J);
      L := I;
    Until I >= R;
  end;

var I : Integer;
begin
  Assert(Length(Key) = Length(Data), 'Sort pair must be of equal length');
  I := Length(Key);
  if I > 0 then
    QuickSort(0, I - 1);
end;

procedure Sort(const Key: IntegerArray; const Data: ExtendedArray);

  procedure QuickSort(L, R: Integer);
  var I, J, M : Integer;
      W, T    : Integer;
      P, Q    : PInteger;
      A       : Extended;
  begin
    Repeat
      I := L;
      P := @Key[I];
      J := R;
      Q := @Key[J];
      M := (L + R) shr 1;
      W := Key[M];
      Repeat
        While P^ < W do
          begin
            Inc(P);
            Inc(I);
          end;
        While Q^ > W do
          begin
            Dec(Q);
            Dec(J);
          end;
        if I <= J then
          begin
            T := P^;
            P^ := Q^;
            Q^ := T;
            A := Data[I];
            Data[I] := Data[J];
            Data[J] := A;
            if M = I then
              begin
                M := J;
                W := Q^;
              end else
              if M = J then
                begin
                  M := I;
                  W := P^;
                end;
            Inc(P);
            Inc(I);
            Dec(Q);
            Dec(J);
          end;
      Until I > J;
      if L < J then
        QuickSort(L, J);
      L := I;
    Until I >= R;
  end;

var I : Integer;
begin
  Assert(Length(Key) = Length(Data), 'Sort pair must be of equal length');
  I := Length(Key);
  if I > 0 then
    QuickSort(0, I - 1);
end;

procedure Sort(const Key: IntegerArray; const Data: PointerArray);

  procedure QuickSort(L, R: Integer);
  var I, J, M : Integer;
      W, T    : Integer;
      P, Q    : PInteger;
      A       : Pointer;
  begin
    Repeat
      I := L;
      P := @Key[I];
      J := R;
      Q := @Key[J];
      M := (L + R) shr 1;
      W := Key[M];
      Repeat
        While P^ < W do
          begin
            Inc(P);
            Inc(I);
          end;
        While Q^ > W do
          begin
            Dec(Q);
            Dec(J);
          end;
        if I <= J then
          begin
            T := P^;
            P^ := Q^;
            Q^ := T;
            A := Data[I];
            Data[I] := Data[J];
            Data[J] := A;
            if M = I then
              begin
                M := J;
                W := Q^;
              end else
              if M = J then
                begin
                  M := I;
                  W := P^;
                end;
            Inc(P);
            Inc(I);
            Dec(Q);
            Dec(J);
          end;
      Until I > J;
      if L < J then
        QuickSort(L, J);
      L := I;
    Until I >= R;
  end;

var I : Integer;
begin
  Assert(Length(Key) = Length(Data), 'Sort pair must be of equal length');
  I := Length(Key);
  if I > 0 then
    QuickSort(0, I - 1);
end;

procedure Sort(const Key: StringArray; const Data: IntegerArray);

  procedure QuickSort(L, R: Integer);
  var I, J, M : Integer;
      W, T    : String;
      P, Q    : PString;
      A       : Integer;
  begin
    Repeat
      I := L;
      P := @Key[I];
      J := R;
      Q := @Key[J];
      M := (L + R) shr 1;
      W := Key[M];
      Repeat
        While P^ < W do
          begin
            Inc(P);
            Inc(I);
          end;
        While Q^ > W do
          begin
            Dec(Q);
            Dec(J);
          end;
        if I <= J then
          begin
            T := P^;
            P^ := Q^;
            Q^ := T;
            A := Data[I];
            Data[I] := Data[J];
            Data[J] := A;
            if M = I then
              begin
                M := J;
                W := Q^;
              end else
              if M = J then
                begin
                  M := I;
                  W := P^;
                end;
            Inc(P);
            Inc(I);
            Dec(Q);
            Dec(J);
          end;
      Until I > J;
      if L < J then
        QuickSort(L, J);
      L := I;
    Until I >= R;
  end;

var I : Integer;
begin
  Assert(Length(Key) = Length(Data), 'Sort pair must be of equal length');
  I := Length(Key);
  if I > 0 then
    QuickSort(0, I - 1);
end;

procedure Sort(const Key: StringArray; const Data: Int64Array);

  procedure QuickSort(L, R: Integer);
  var I, J, M : Integer;
      W, T    : String;
      P, Q    : PString;
      A       : Int64;
  begin
    Repeat
      I := L;
      P := @Key[I];
      J := R;
      Q := @Key[J];
      M := (L + R) shr 1;
      W := Key[M];
      Repeat
        While P^ < W do
          begin
            Inc(P);
            Inc(I);
          end;
        While Q^ > W do
          begin
            Dec(Q);
            Dec(J);
          end;
        if I <= J then
          begin
            T := P^;
            P^ := Q^;
            Q^ := T;
            A := Data[I];
            Data[I] := Data[J];
            Data[J] := A;
            if M = I then
              begin
                M := J;
                W := Q^;
              end else
              if M = J then
                begin
                  M := I;
                  W := P^;
                end;
            Inc(P);
            Inc(I);
            Dec(Q);
            Dec(J);
          end;
      Until I > J;
      if L < J then
        QuickSort(L, J);
      L := I;
    Until I >= R;
  end;

var I : Integer;
begin
  Assert(Length(Key) = Length(Data), 'Sort pair must be of equal length');
  I := Length(Key);
  if I > 0 then
    QuickSort(0, I - 1);
end;

procedure Sort(const Key: StringArray; const Data: StringArray);

  procedure QuickSort(L, R: Integer);
  var I, J, M : Integer;
      W, T    : String;
      P, Q    : PString;
      A       : String;
  begin
    Repeat
      I := L;
      P := @Key[I];
      J := R;
      Q := @Key[J];
      M := (L + R) shr 1;
      W := Key[M];
      Repeat
        While P^ < W do
          begin
            Inc(P);
            Inc(I);
          end;
        While Q^ > W do
          begin
            Dec(Q);
            Dec(J);
          end;
        if I <= J then
          begin
            T := P^;
            P^ := Q^;
            Q^ := T;
            A := Data[I];
            Data[I] := Data[J];
            Data[J] := A;
            if M = I then
              begin
                M := J;
                W := Q^;
              end else
              if M = J then
                begin
                  M := I;
                  W := P^;
                end;
            Inc(P);
            Inc(I);
            Dec(Q);
            Dec(J);
          end;
      Until I > J;
      if L < J then
        QuickSort(L, J);
      L := I;
    Until I >= R;
  end;

var I : Integer;
begin
  Assert(Length(Key) = Length(Data), 'Sort pair must be of equal length');
  I := Length(Key);
  if I > 0 then
    QuickSort(0, I - 1);
end;

procedure Sort(const Key: StringArray; const Data: ExtendedArray);

  procedure QuickSort(L, R: Integer);
  var I, J, M : Integer;
      W, T    : String;
      P, Q    : PString;
      A       : Extended;
  begin
    Repeat
      I := L;
      P := @Key[I];
      J := R;
      Q := @Key[J];
      M := (L + R) shr 1;
      W := Key[M];
      Repeat
        While P^ < W do
          begin
            Inc(P);
            Inc(I);
          end;
        While Q^ > W do
          begin
            Dec(Q);
            Dec(J);
          end;
        if I <= J then
          begin
            T := P^;
            P^ := Q^;
            Q^ := T;
            A := Data[I];
            Data[I] := Data[J];
            Data[J] := A;
            if M = I then
              begin
                M := J;
                W := Q^;
              end else
              if M = J then
                begin
                  M := I;
                  W := P^;
                end;
            Inc(P);
            Inc(I);
            Dec(Q);
            Dec(J);
          end;
      Until I > J;
      if L < J then
        QuickSort(L, J);
      L := I;
    Until I >= R;
  end;

var I : Integer;
begin
  Assert(Length(Key) = Length(Data), 'Sort pair must be of equal length');
  I := Length(Key);
  if I > 0 then
    QuickSort(0, I - 1);
end;

procedure Sort(const Key: StringArray; const Data: PointerArray);

  procedure QuickSort(L, R: Integer);
  var I, J, M : Integer;
      W, T    : String;
      P, Q    : PString;
      A       : Pointer;
  begin
    Repeat
      I := L;
      P := @Key[I];
      J := R;
      Q := @Key[J];
      M := (L + R) shr 1;
      W := Key[M];
      Repeat
        While P^ < W do
          begin
            Inc(P);
            Inc(I);
          end;
        While Q^ > W do
          begin
            Dec(Q);
            Dec(J);
          end;
        if I <= J then
          begin
            T := P^;
            P^ := Q^;
            Q^ := T;
            A := Data[I];
            Data[I] := Data[J];
            Data[J] := A;
            if M = I then
              begin
                M := J;
                W := Q^;
              end else
              if M = J then
                begin
                  M := I;
                  W := P^;
                end;
            Inc(P);
            Inc(I);
            Dec(Q);
            Dec(J);
          end;
      Until I > J;
      if L < J then
        QuickSort(L, J);
      L := I;
    Until I >= R;
  end;

var I : Integer;
begin
  Assert(Length(Key) = Length(Data), 'Sort pair must be of equal length');
  I := Length(Key);
  if I > 0 then
    QuickSort(0, I - 1);
end;

procedure Sort(const Key: ExtendedArray; const Data: IntegerArray);

  procedure QuickSort(L, R: Integer);
  var I, J, M : Integer;
      W, T    : Extended;
      P, Q    : PExtended;
      A       : Integer;
  begin
    Repeat
      I := L;
      P := @Key[I];
      J := R;
      Q := @Key[J];
      M := (L + R) shr 1;
      W := Key[M];
      Repeat
        While P^ < W do
          begin
            Inc(P);
            Inc(I);
          end;
        While Q^ > W do
          begin
            Dec(Q);
            Dec(J);
          end;
        if I <= J then
          begin
            T := P^;
            P^ := Q^;
            Q^ := T;
            A := Data[I];
            Data[I] := Data[J];
            Data[J] := A;
            if M = I then
              begin
                M := J;
                W := Q^;
              end else
              if M = J then
                begin
                  M := I;
                  W := P^;
                end;
            Inc(P);
            Inc(I);
            Dec(Q);
            Dec(J);
          end;
      Until I > J;
      if L < J then
        QuickSort(L, J);
      L := I;
    Until I >= R;
  end;

var I : Integer;
begin
  Assert(Length(Key) = Length(Data), 'Sort pair must be of equal length');
  I := Length(Key);
  if I > 0 then
    QuickSort(0, I - 1);
end;

procedure Sort(const Key: ExtendedArray; const Data: Int64Array);

  procedure QuickSort(L, R: Integer);
  var I, J, M : Integer;
      W, T    : Extended;
      P, Q    : PExtended;
      A       : Int64;
  begin
    Repeat
      I := L;
      P := @Key[I];
      J := R;
      Q := @Key[J];
      M := (L + R) shr 1;
      W := Key[M];
      Repeat
        While P^ < W do
          begin
            Inc(P);
            Inc(I);
          end;
        While Q^ > W do
          begin
            Dec(Q);
            Dec(J);
          end;
        if I <= J then
          begin
            T := P^;
            P^ := Q^;
            Q^ := T;
            A := Data[I];
            Data[I] := Data[J];
            Data[J] := A;
            if M = I then
              begin
                M := J;
                W := Q^;
              end else
              if M = J then
                begin
                  M := I;
                  W := P^;
                end;
            Inc(P);
            Inc(I);
            Dec(Q);
            Dec(J);
          end;
      Until I > J;
      if L < J then
        QuickSort(L, J);
      L := I;
    Until I >= R;
  end;

var I : Integer;
begin
  Assert(Length(Key) = Length(Data), 'Sort pair must be of equal length');
  I := Length(Key);
  if I > 0 then
    QuickSort(0, I - 1);
end;

procedure Sort(const Key: ExtendedArray; const Data: StringArray);

  procedure QuickSort(L, R: Integer);
  var I, J, M : Integer;
      W, T    : Extended;
      P, Q    : PExtended;
      A       : String;
  begin
    Repeat
      I := L;
      P := @Key[I];
      J := R;
      Q := @Key[J];
      M := (L + R) shr 1;
      W := Key[M];
      Repeat
        While P^ < W do
          begin
            Inc(P);
            Inc(I);
          end;
        While Q^ > W do
          begin
            Dec(Q);
            Dec(J);
          end;
        if I <= J then
          begin
            T := P^;
            P^ := Q^;
            Q^ := T;
            A := Data[I];
            Data[I] := Data[J];
            Data[J] := A;
            if M = I then
              begin
                M := J;
                W := Q^;
              end else
              if M = J then
                begin
                  M := I;
                  W := P^;
                end;
            Inc(P);
            Inc(I);
            Dec(Q);
            Dec(J);
          end;
      Until I > J;
      if L < J then
        QuickSort(L, J);
      L := I;
    Until I >= R;
  end;

var I : Integer;
begin
  Assert(Length(Key) = Length(Data), 'Sort pair must be of equal length');
  I := Length(Key);
  if I > 0 then
    QuickSort(0, I - 1);
end;

procedure Sort(const Key: ExtendedArray; const Data: ExtendedArray);

  procedure QuickSort(L, R: Integer);
  var I, J, M : Integer;
      W, T    : Extended;
      P, Q    : PExtended;
      A       : Extended;
  begin
    Repeat
      I := L;
      P := @Key[I];
      J := R;
      Q := @Key[J];
      M := (L + R) shr 1;
      W := Key[M];
      Repeat
        While P^ < W do
          begin
            Inc(P);
            Inc(I);
          end;
        While Q^ > W do
          begin
            Dec(Q);
            Dec(J);
          end;
        if I <= J then
          begin
            T := P^;
            P^ := Q^;
            Q^ := T;
            A := Data[I];
            Data[I] := Data[J];
            Data[J] := A;
            if M = I then
              begin
                M := J;
                W := Q^;
              end else
              if M = J then
                begin
                  M := I;
                  W := P^;
                end;
            Inc(P);
            Inc(I);
            Dec(Q);
            Dec(J);
          end;
      Until I > J;
      if L < J then
        QuickSort(L, J);
      L := I;
    Until I >= R;
  end;

var I : Integer;
begin
  Assert(Length(Key) = Length(Data), 'Sort pair must be of equal length');
  I := Length(Key);
  if I > 0 then
    QuickSort(0, I - 1);
end;

procedure Sort(const Key: ExtendedArray; const Data: PointerArray);

  procedure QuickSort(L, R: Integer);
  var I, J, M : Integer;
      W, T    : Extended;
      P, Q    : PExtended;
      A       : Pointer;
  begin
    Repeat
      I := L;
      P := @Key[I];
      J := R;
      Q := @Key[J];
      M := (L + R) shr 1;
      W := Key[M];
      Repeat
        While P^ < W do
          begin
            Inc(P);
            Inc(I);
          end;
        While Q^ > W do
          begin
            Dec(Q);
            Dec(J);
          end;
        if I <= J then
          begin
            T := P^;
            P^ := Q^;
            Q^ := T;
            A := Data[I];
            Data[I] := Data[J];
            Data[J] := A;
            if M = I then
              begin
                M := J;
                W := Q^;
              end else
              if M = J then
                begin
                  M := I;
                  W := P^;
                end;
            Inc(P);
            Inc(I);
            Dec(Q);
            Dec(J);
          end;
      Until I > J;
      if L < J then
        QuickSort(L, J);
      L := I;
    Until I >= R;
  end;

var I : Integer;
begin
  Assert(Length(Key) = Length(Data), 'Sort pair must be of equal length');
  I := Length(Key);
  if I > 0 then
    QuickSort(0, I - 1);
end;




{                                                                              }
{ Test cases                                                                   }
{                                                                              }
{$ASSERTIONS ON}
procedure Test_Misc;
var A, B : String;
    L, H : Cardinal;
    I, J : Integer;
    V    : Boolean;
begin
  // Clip
  Assert(SumClipI(1, 2) = 3, 'SumClipI');
  Assert(SumClipI(1, -2) = -1, 'SumClipI');
  Assert(SumClipI(MaxInteger - 1, 0) = MaxInteger - 1, 'SumClipI');
  Assert(SumClipI(MaxInteger - 1, 1) = MaxInteger, 'SumClipI');
  Assert(SumClipI(MaxInteger - 1, 2) = MaxInteger, 'SumClipI');
  Assert(SumClipI(MinInteger + 1, 0) = MinInteger + 1, 'SumClipI');
  Assert(SumClipI(MinInteger + 1, -1) = MinInteger, 'SumClipI');
  Assert(SumClipI(MinInteger + 1, -2) = MinInteger, 'SumClipI');
  Assert(SumClipC(1, 2) = 3, 'SumClipC');
  Assert(SumClipC(3, -2) = 1, 'SumClipC');
  Assert(SumClipC(MaxCardinal - 1, 0) = MaxCardinal - 1, 'SumClipC');
  Assert(SumClipC(MaxCardinal - 1, 1) = MaxCardinal, 'SumClipC');
  Assert(SumClipC(MaxCardinal - 1, 2) = MaxCardinal, 'SumClipC');
  Assert(SumClipC(1, 0) = 1, 'SumClipC');
  Assert(SumClipC(1, -1) = 0, 'SumClipC');
  Assert(SumClipC(1, -2) = 0, 'SumClipC');

  // Ranges
  L := 10;
  H := 20;
  Assert(CardinalRangeIncludeElementRange(L, H, 10, 20), 'RangeInclude');
  Assert((L = 10) and (H = 20), 'RangeInclude');
  Assert(CardinalRangeIncludeElementRange(L, H, 9, 21), 'RangeInclude');
  Assert((L = 9) and (H = 21), 'RangeInclude');
  Assert(CardinalRangeIncludeElementRange(L, H, 7, 10), 'RangeInclude');
  Assert((L = 7) and (H = 21), 'RangeInclude');
  Assert(CardinalRangeIncludeElementRange(L, H, 5, 6), 'RangeInclude');
  Assert((L = 5) and (H = 21), 'RangeInclude');
  Assert(not CardinalRangeIncludeElementRange(L, H, 1, 3), 'RangeInclude');
  Assert((L = 5) and (H = 21), 'RangeInclude');
  Assert(CardinalRangeIncludeElementRange(L, H, 20, 22), 'RangeInclude');
  Assert((L = 5) and (H = 22), 'RangeInclude');
  Assert(CardinalRangeIncludeElementRange(L, H, 23, 24), 'RangeInclude');
  Assert((L = 5) and (H = 24), 'RangeInclude');
  Assert(not CardinalRangeIncludeElementRange(L, H, 26, 27), 'RangeInclude');
  Assert((L = 5) and (H = 24), 'RangeInclude');

  // iif
  Assert(iif(True, 1, 2) = 1, 'iif');
  Assert(iif(False, 1, 2) = 2, 'iif');
  Assert(iif(True, -1, -2) = -1, 'iif');
  Assert(iif(False, -1, -2) = -2, 'iif');
  Assert(iif(True, '1', '2') = '1', 'iif');
  Assert(iif(False, '1', '2') = '2', 'iif');
  Assert(iif(True, 1.1, 2.2) = 1.1, 'iif');
  Assert(iif(False, 1.1, 2.2) = 2.2, 'iif');

  // CharSet
  Assert(CharCount([]) = 0, 'CharCount');
  Assert(CharCount(['a'..'z']) = 26, 'CharCount');
  Assert(CharCount([#0, #255]) = 2, 'CharCount');

  // Compare
  Assert(Compare(1, 1) = crEqual, 'Compare');
  Assert(Compare(1, 2) = crLess, 'Compare');
  Assert(Compare(1, 0) = crGreater, 'Compare');
  Assert(Compare(1.0, 1.0) = crEqual, 'Compare');
  Assert(Compare(1.0, 1.1) = crLess, 'Compare');
  Assert(Compare(1.0, 0.9) = crGreater, 'Compare');
  Assert(Compare(False, False) = crEqual, 'Compare');
  Assert(Compare(True, True) = crEqual, 'Compare');
  Assert(Compare(False, True) = crLess, 'Compare');
  Assert(Compare(True, False) = crGreater, 'Compare');
  Assert(Compare('', '') = crEqual, 'Compare');
  Assert(Compare('a', 'a') = crEqual, 'Compare');
  Assert(Compare('a', 'b') = crLess, 'Compare');
  Assert(Compare('b', 'a') = crGreater, 'Compare');
  Assert(Compare('', 'a') = crLess, 'Compare');
  Assert(Compare('a', '') = crGreater, 'Compare');
  Assert(Compare('aa', 'a') = crGreater, 'Compare');

  Assert(not FloatZero(1e-1, 1e-2), 'FloatZero');
  Assert(FloatZero(1e-2, 1e-2), 'FloatZero');
  Assert(not FloatZero(1e-1, 1e-9), 'FloatZero');
  Assert(not FloatZero(1e-8, 1e-9), 'FloatZero');
  Assert(FloatZero(1e-9, 1e-9), 'FloatZero');
  Assert(FloatZero(1e-10, 1e-9), 'FloatZero');
  Assert(not FloatZero(0.2, 1e-1), 'FloatZero');
  Assert(FloatZero(0.09, 1e-1), 'FloatZero');

  Assert(FloatOne(1.0, 1e-1), 'FloatOne');
  Assert(FloatOne(1.09999, 1e-1), 'FloatOne');
  Assert(FloatOne(0.90001, 1e-1), 'FloatOne');
  Assert(not FloatOne(1.10001, 1e-1), 'FloatOne');
  Assert(not FloatOne(1.2, 1e-1), 'FloatOne');
  Assert(not FloatOne(0.89999, 1e-1), 'FloatOne');

  Assert(not FloatsEqual(2.0, -2.0, 1e-1), 'FloatsEqual');
  Assert(not FloatsEqual(1.0, 0.0, 1e-1), 'FloatsEqual');
  Assert(FloatsEqual(2.0, 2.0, 1e-1), 'FloatsEqual');
  Assert(FloatsEqual(2.0, 2.09, 1e-1), 'FloatsEqual');
  Assert(FloatsEqual(2.0, 1.90000001, 1e-1), 'FloatsEqual');
  Assert(not FloatsEqual(2.0, 2.10001, 1e-1), 'FloatsEqual');
  Assert(not FloatsEqual(2.0, 2.2, 1e-1), 'FloatsEqual');
  Assert(not FloatsEqual(2.0, 1.8999999, 1e-1), 'FloatsEqual');
  Assert(FloatsEqual(2.00000000011, 2.0, 1e-2), 'FloatsEqual');
  Assert(FloatsEqual(2.00000000011, 2.0, 1e-9), 'FloatsEqual');
  Assert(not FloatsEqual(2.00000000011, 2.0, 1e-10), 'FloatsEqual');
  Assert(not FloatsEqual(2.00000000011, 2.0, 1e-11), 'FloatsEqual');

  Assert(FloatsCompare(0.0, 0.0, MinExtended) = crEqual, 'FloatsCompare');
  Assert(FloatsCompare(1.2, 1.2, MinExtended) = crEqual, 'FloatsCompare');
  Assert(FloatsCompare(1.23456789e-300, 1.23456789e-300, MinExtended) = crEqual, 'FloatsCompare');
  Assert(FloatsCompare(1.23456780e-300, 1.23456789e-300, MinExtended) = crLess, 'FloatsCompare');
  Assert(FloatsCompare(1.4e-5, 1.5e-5, 1e-4) = crEqual, 'FloatsCompare');
  Assert(FloatsCompare(1.4e-5, 1.5e-5, 1e-5) = crEqual, 'FloatsCompare');
  Assert(FloatsCompare(1.4e-5, 1.5e-5, 1e-6) = crLess, 'FloatsCompare');
  Assert(FloatsCompare(1.4e-5, 1.5e-5, 1e-7) = crLess, 'FloatsCompare');
  Assert(FloatsCompare(0.5003, 0.5001, 1e-1) = crEqual, 'FloatsCompare');
  Assert(FloatsCompare(0.5003, 0.5001, 1e-2) = crEqual, 'FloatsCompare');
  Assert(FloatsCompare(0.5003, 0.5001, 1e-3) = crEqual, 'FloatsCompare');
  Assert(FloatsCompare(0.5003, 0.5001, 1e-4) = crGreater, 'FloatsCompare');
  Assert(FloatsCompare(0.5003, 0.5001, 1e-5) = crGreater, 'FloatsCompare');

  Assert(ApproxEqual(0.0, 0.0), 'ApproxEqual');
  Assert(not ApproxEqual(0.0, 1e-100, 1e-10), 'ApproxEqual');
  Assert(not ApproxEqual(1.0, 1e-100, 1e-10), 'ApproxEqual');
  Assert(ApproxEqual(1.0, 1.0), 'ApproxEqual');
  Assert(ApproxEqual(-1.0, -1.0), 'ApproxEqual');
  Assert(not ApproxEqual(1.0, -1.0), 'ApproxEqual');
  Assert(ApproxEqual(1e-100, 1e-100, 1e-10), 'ApproxEqual');
  Assert(not ApproxEqual(0.0, 1.0, 1e-9), 'ApproxEqual');
  Assert(not ApproxEqual(-1.0, 1.0, 1e-9), 'ApproxEqual');
  Assert(ApproxEqual(0.12345, 0.12349, 1e-3), 'ApproxEqual');
  Assert(not ApproxEqual(0.12345, 0.12349, 1e-4), 'ApproxEqual');
  Assert(not ApproxEqual(0.12345, 0.12349, 1e-5), 'ApproxEqual');
  Assert(ApproxEqual(1.2345e+100, 1.2349e+100, 1e-3), 'ApproxEqual');
  Assert(not ApproxEqual(1.2345e+100, 1.2349e+100, 1e-4), 'ApproxEqual');
  Assert(not ApproxEqual(1.2345e+100, 1.2349e+100, 1e-5), 'ApproxEqual');
  Assert(ApproxEqual(1.2345e-100, 1.2349e-100, 1e-3), 'ApproxEqual');
  Assert(not ApproxEqual(1.2345e-100, 1.2349e-100, 1e-4), 'ApproxEqual');
  Assert(not ApproxEqual(1.2345e-100, 1.2349e-100, 1e-5), 'ApproxEqual');
  Assert(not ApproxEqual(1.0e+20, 1.00000001E+20, 1e-8), 'ApproxEqual');
  Assert(ApproxEqual(1.0e+20, 1.000000001E+20, 1e-8), 'ApproxEqual');
  Assert(not ApproxEqual(1.0e+20, 1.000000001E+20, 1e-9), 'ApproxEqual');
  Assert(ApproxEqual(1.0e+20, 1.0000000001E+20, 1e-9), 'ApproxEqual');
  Assert(not ApproxEqual(1.0e+20, 1.0000000001E+20, 1e-10), 'ApproxEqual');

  Assert(ApproxCompare(0.0, 0.0) = crEqual, 'ApproxCompare');
  Assert(ApproxCompare(0.0, 1.0) = crLess, 'ApproxCompare');
  Assert(ApproxCompare(1.0, 0.0) = crGreater, 'ApproxCompare');
  Assert(ApproxCompare(-1.0, 1.0) = crLess, 'ApproxCompare');
  Assert(ApproxCompare(1.2345e+10, 1.2349e+10, 1e-3) = crEqual, 'ApproxCompare');
  Assert(ApproxCompare(1.2345e+10, 1.2349e+10, 1e-4) = crLess, 'ApproxCompare');
  Assert(ApproxCompare(-1.2345e-10, -1.2349e-10, 1e-3) = crEqual, 'ApproxCompare');
  Assert(ApproxCompare(-1.2345e-10, -1.2349e-10, 1e-4) = crGreater, 'ApproxCompare');

  Assert(ReverseCompareResult(crLess) = crGreater, 'ReverseCompareResult');
  Assert(ReverseCompareResult(crGreater) = crLess, 'ReverseCompareResult');

  // MoveMem
  For I := -8 to 16 do
    begin
      A := '0123456789ABCDEFGHIJ';
      B := '                    ';
      MoveMem(A[1], B[1], I);
      For J := 1 to MinI(I, 10) do
        Assert(B[J] = Char(48 + J - 1), 'MoveMem');
      For J := 11 to MinI(I, 16) do
        Assert(B[J] = Char(65 + J - 11), 'MoveMem');
      For J := MaxI(I + 1, 1) to 20 do
        Assert(B[J] = ' ');
    end;

  // ZeroMem
  For I := -8 to 16 do
    begin
      A := '0123456789ABCDEFGHIJ';
      ZeroMem(A[1], I);
      For J := 1 to I do
        Assert(A[J] = #0, 'ZeroMem');
      For J := MaxI(I + 1, 1) to 10 do
        Assert(A[J] = Char(48 + J - 1), 'ZeroMem');
      For J := MaxI(I + 1, 11) to 20 do
        Assert(A[J] = Char(65 + J - 11), 'ZeroMem');
    end;

  // FillMem
  For I := -8 to 16 do
    begin
      A := '0123456789ABCDEFGHIJ';
      FillMem(A[1], I, Ord('Z'));
      For J := 1 to I do
        Assert(A[J] = 'Z', 'FillMem');
      For J := MaxI(I + 1, 1) to 10 do
        Assert(A[J] = Char(48 + J - 1), 'FillMem');
      For J := MaxI(I + 1, 11) to 20 do
        Assert(A[J] = Char(65 + J - 11), 'FillMem');
    end;

  // Hash
  Assert(HashStr('Fundamentals') = $3FB7796E, 'HashStr');

  // Encodings
  Assert(HexCharValue('A') = 10, 'HexCharValue');
  Assert(HexCharValue('a') = 10, 'HexCharValue');
  Assert(HexCharValue('1') = 1, 'HexCharValue');
  Assert(HexCharValue('G') = $FF, 'HexCharValue');

  Assert(LongWordToStr(123) = '123', 'LongWordToStr');
  Assert(LongWordToStr(0) = '0', 'LongWordToStr');
  Assert(LongWordToStr($FFFFFFFF) = '4294967295', 'LongWordToStr');
  Assert(LongWordToStr(10000) = '10000', 'LongWordToStr');
  Assert(LongWordToStr(99999) = '99999', 'LongWordToStr');
  Assert(LongWordToStr(1, 1) = '1', 'LongWordToStr');
  Assert(LongWordToStr(1, 3) = '001', 'LongWordToStr');
  Assert(LongWordToStr(1234, 3) = '1234', 'LongWordToStr');

  Assert(DecStrToLongWord('', V) = 0, 'DecStrToLongWord');
  Assert(V = False, 'DecStrToLongWord');
  Assert(DecStrToLongWord('123', V) = 123, 'DecStrToLongWord');
  Assert(V = True, 'DecStrToLongWord');
  Assert(DecStrToLongWord('4294967295', V) = $FFFFFFFF, 'DecStrToLongWord');
  Assert(V = True, 'DecStrToLongWord');
  Assert(DecStrToLongWord('99999', V) = 99999, 'DecStrToLongWord');

  Assert(LongWordToHex(0) = '0', 'LongWordToHex');
  Assert(LongWordToHex($FFFFFFFF) = 'FFFFFFFF', 'LongWordToHex');
  Assert(LongWordToHex($10000) = '10000', 'LongWordToHex');
  Assert(LongWordToHex($12345678) = '12345678', 'LongWordToHex');
  Assert(LongWordToHex($AB, 4) = '00AB', 'LongWordToHex');
  Assert(LongWordToHex($ABCD, 8) = '0000ABCD', 'LongWordToHex');
  Assert(LongWordToHex(0, 8) = '00000000', 'LongWordToHex');
  Assert(LongWordToHex($CDEF, 2) = 'CDEF', 'LongWordToHex');

  Assert(HexStrToLongWord('FFFFFFFF', V) = $FFFFFFFF, 'HexStrToLongWord');
  Assert(V = True, 'HexStrToLongWord');
  Assert(HexStrToLongWord('0', V) = 0, 'HexStrToLongWord');
  Assert(V = True, 'HexStrToLongWord');
  Assert(HexStrToLongWord('123456', V) = $123456, 'HexStrToLongWord');
  Assert(HexStrToLongWord('ABC', V) = $ABC, 'HexStrToLongWord');
  Assert(HexStrToLongWord('', V) = 0, 'HexStrToLongWord');
  Assert(V = False, 'HexStrToLongWord');
  Assert(HexStrToLongWord('x', V) = 0, 'HexStrToLongWord');
  Assert(V = False, 'HexStrToLongWord');
  Assert(HexStrToLongWord('1000', V) = $1000, 'HexStrToLongWord');
end;

procedure Test_BitFunctions;
begin
  Assert(SetBit($100F, 5) = $102F, 'SetBit');
  Assert(ClearBit($102F, 5) = $100F, 'ClearBit');
  Assert(ToggleBit($102F, 5) = $100F, 'ToggleBit');
  Assert(ToggleBit($100F, 5) = $102F, 'ToggleBit');
  Assert(IsBitSet($102F, 5), 'IsBitSet');
  Assert(not IsBitSet($100F, 5), 'IsBitSet');

  Assert(SetBitScanForward(0) = -1, 'SetBitScanForward');
  Assert(SetBitScanForward($1020) = 5, 'SetBitScanForward');
  Assert(SetBitScanReverse($1020) = 12, 'SetBitScanForward');
  Assert(SetBitScanForward($1020, 6) = 12, 'SetBitScanForward');
  Assert(SetBitScanReverse($1020, 11) = 5, 'SetBitScanForward');
  Assert(ClearBitScanForward($FFFFFFFF) = -1, 'ClearBitScanForward');
  Assert(ClearBitScanForward($1020) = 0, 'ClearBitScanForward');
  Assert(ClearBitScanReverse($1020) = 31, 'ClearBitScanForward');
  Assert(ClearBitScanForward($1020, 5) = 6, 'ClearBitScanForward');
  Assert(ClearBitScanReverse($1020, 12) = 11, 'ClearBitScanForward');

  Assert(ReverseBits($12345678) = $1E6A2C48, 'ReverseBits');
  Assert(SwapEndian($12345678) = $78563412, 'SwapEndian');

  Assert(BitCount($12341234) = 10, 'BitCount');

  Assert(LowBitMask(10) = $3FF, 'LowBitMask');
  Assert(HighBitMask(28) = $F0000000, 'HighBitMask');
  Assert(RangeBitMask(2, 6) = $7C, 'RangeBitMask');

  Assert(SetBitRange($101, 2, 6) = $17D, 'SetBitRange');
  Assert(ClearBitRange($17D, 2, 6) = $101, 'ClearBitRange');
  Assert(ToggleBitRange($17D, 2, 6) = $101, 'ToggleBitRange');
  Assert(IsBitRangeSet($17D, 2, 6), 'IsBitRangeSet');
  Assert(not IsBitRangeSet($101, 2, 6), 'IsBitRangeSet');
  Assert(not IsBitRangeClear($17D, 2, 6), 'IsBitRangeClear');
  Assert(IsBitRangeClear($101, 2, 6), 'IsBitRangeClear');
end;

procedure Test_IntegerArray;
var S, T : IntegerArray;
    F    : Integer;
begin
  S := nil;
  For F := 1 to 100 do
    begin
      Append(S, F);
      Assert(Length(S) = F,                 'Append');
      Assert(S[F - 1] = F,                  'Append');
    end;

  T := Copy(S);
  AppendIntegerArray(S, T);
  For F := 1 to 100 do
    Assert(S[F + 99] = F,                   'Append');
  Assert(PosNext(60, S) = 59,               'PosNext');
  Assert(PosNext(60, T) = 59,               'PosNext');
  Assert(PosNext(60, S, 59) = 159,          'PosNext');
  Assert(PosNext(60, T, 59) = -1,           'PosNext');
  Assert(PosNext(60, T, -1, True) = 59,     'PosNext');
  Assert(PosNext(60, T, 59, True) = -1,     'PosNext');

  For F := 1 to 100 do
    begin
      Remove(S, PosNext(F, S), 1);
      Assert(Length(S) = 200 - F,           'Remove');
    end;
  For F := 99 downto 0 do
    begin
      Remove(S, PosNext(F xor 3 + 1, S), 1);
      Assert(Length(S) = F,                 'Remove');
    end;

  S := AsIntegerArray([3, 1, 2, 5, 4]);
  Sort(S);
  Assert(S[0] = 1, 'Sort');
  Assert(S[1] = 2, 'Sort');
  Assert(S[2] = 3, 'Sort');
  Assert(S[3] = 4, 'Sort');
  Assert(S[4] = 5, 'Sort');

  S := AsIntegerArray([3, 5, 5, 2, 5, 5, 1]);
  Sort(S);
  Assert(S[0] = 1, 'Sort');
  Assert(S[1] = 2, 'Sort');
  Assert(S[2] = 3, 'Sort');
  Assert(S[3] = 5, 'Sort');
  Assert(S[4] = 5, 'Sort');
  Assert(S[5] = 5, 'Sort');
  Assert(S[6] = 5, 'Sort');

  SetLength(S, 1000);
  For F := 0 to 999 do
    S[F] := F mod 5;
  Sort(S);
  For F := 0 to 999 do
    Assert(S[F] = F div 200, 'Sort');

  S := AsIntegerArray([6, 3, 5, 1]);
  T := AsIntegerArray([1, 2, 3, 4]);
  Sort(S, T);
  Assert(S[0] = 1, 'Sort');
  Assert(S[1] = 3, 'Sort');
  Assert(S[2] = 5, 'Sort');
  Assert(S[3] = 6, 'Sort');
  Assert(T[0] = 4, 'Sort');
  Assert(T[1] = 2, 'Sort');
  Assert(T[2] = 3, 'Sort');
  Assert(T[3] = 1, 'Sort');
end;

procedure SelfTest;
begin
  Test_Misc;
  Test_BitFunctions;
  Test_IntegerArray;
end;



end.