Componentes.Terceros.jcl/official/1.100/source/common/JclMath.pas

4468 lines
116 KiB
ObjectPascal

{**************************************************************************************************}
{ }
{ Project JEDI Code Library (JCL) }
{ }
{ The contents of this file are subject to the Mozilla Public License Version 1.1 (the "License"); }
{ you may not use this file except in compliance with the License. You may obtain a copy of the }
{ License at http://www.mozilla.org/MPL/ }
{ }
{ Software distributed under the License is distributed on an "AS IS" basis, WITHOUT WARRANTY OF }
{ ANY KIND, either express or implied. See the License for the specific language governing rights }
{ and limitations under the License. }
{ }
{ The Original Code is JclMath.pas. }
{ }
{ The Initial Developers of the Original Code are Clayton Collie, David Butler, ESB Consultancy, }
{ Jean Debord, Marcel van Brakel and Michael Schnell. }
{ Portions created by these individuals are Copyright (C) of these individuals. }
{ All Rights Reserved. }
{ }
{ Contributors: }
{ Ernesto Benestante }
{ Marcel van Brakel }
{ Aleksei Koudinov }
{ Robert Marquardt (marquardt) }
{ Robert Rossmair (rrossmair) }
{ Matthias Thoma (mthoma) }
{ Mark Vaughan }
{ Andreas Hausladen }
{ unknown }
{ }
{**************************************************************************************************}
{ }
{ Various mathematics classes and routines. Includes prime numbers, rational }
{ numbers, generic floating point routines, hyperbolic and transcendenatal }
{ routines, NAN and INF support and more. }
{ }
{ Unit owner: Matthias Thoma }
{ }
{**************************************************************************************************}
// Last modified: $Date: 2006-08-11 17:24:18 +0200 (ven., 11 août 2006) $
unit JclMath;
{$I jcl.inc}
interface
uses
{$IFDEF UNITVERSIONING}
JclUnitVersioning,
{$ENDIF UNITVERSIONING}
Classes, SysUtils,
JclBase;
{ Mathematical constants }
const
Bernstein: Float = 0.2801694990238691330364364912307; // Bernstein constant
Cbrt2: Float = 1.2599210498948731647672106072782; // CubeRoot(2)
Cbrt3: Float = 1.4422495703074083823216383107801; // CubeRoot(3)
Cbrt10: Float = 2.1544346900318837217592935665194; // CubeRoot(10)
Cbrt100: Float = 4.6415888336127788924100763509194; // CubeRoot(100)
CbrtPi: Float = 1.4645918875615232630201425272638; // CubeRoot(PI)
Catalan: Float = 0.9159655941772190150546035149324; // Catalan constant
Pi: Float = 3.1415926535897932384626433832795; // PI
PiOn2: Float = 1.5707963267948966192313216916398; // PI / 2
PiOn3: Float = 1.0471975511965977461542144610932; // PI / 3
PiOn4: Float = 0.78539816339744830961566084581988; // PI / 4
Sqrt2: Float = 1.4142135623730950488016887242097; // Sqrt(2)
Sqrt3: Float = 1.7320508075688772935274463415059; // Sqrt(3)
Sqrt5: Float = 2.2360679774997896964091736687313; // Sqrt(5)
Sqrt10: Float = 3.1622776601683793319988935444327; // Sqrt(10)
SqrtPi: Float = 1.7724538509055160272981674833411; // Sqrt(PI)
Sqrt2Pi: Float = 2.506628274631000502415765284811; // Sqrt(2 * PI)
TwoPi: Float = 6.283185307179586476925286766559; // 2 * PI
ThreePi: Float = 9.4247779607693797153879301498385; // 3 * PI
Ln2: Float = 0.69314718055994530941723212145818; // Ln(2)
Ln10: Float = 2.3025850929940456840179914546844; // Ln(10)
LnPi: Float = 1.1447298858494001741434273513531; // Ln(PI)
Log2: Float = 0.30102999566398119521373889472449; // Log10(2)
Log3: Float = 0.47712125471966243729502790325512; // Log10(3)
LogPi: Float = 0.4971498726941338543512682882909; // Log10(PI)
LogE: Float = 0.43429448190325182765112891891661; // Log10(E)
E: Float = 2.7182818284590452353602874713527; // Natural constant
hLn2Pi: Float = 0.91893853320467274178032973640562; // Ln(2*PI)/2
inv2Pi: Float = 0.159154943091895; // 0.5 / Pi
TwoToPower63: Float = 9223372036854775808.0; // 2^63
GoldenMean: Float = 1.618033988749894848204586834365638; // GoldenMean
EulerMascheroni: Float = 0.5772156649015328606065120900824; // Euler GAMMA
const
MaxAngle: Float = 9223372036854775808.0; // 2^63 Rad
{$IFDEF MATH_EXTENDED_PRECISION}
MaxTanH: Float = 5678.2617031470719747459655389854; // Ln(2^16384)/2
MaxFactorial = 1754;
MaxFloatingPoint: Float = 1.189731495357231765085759326628E+4932; // 2^16384
MinFloatingPoint: Float = 3.3621031431120935062626778173218E-4932; // 2^(-16382)
{$ENDIF MATH_EXTENDED_PRECISION}
{$IFDEF MATH_DOUBLE_PRECISION}
MaxTanH: Float = 354.89135644669199842162284618659; // Ln(2^1024)/2
MaxFactorial = 170;
MaxFloatingPoint: Float = 1.797693134862315907729305190789E+308; // 2^1024
MinFloatingPoint: Float = 2.2250738585072013830902327173324E-308; // 2^(-1022)
{$ENDIF MATH_DOUBLE_PRECISION}
{$IFDEF MATH_SINGLE_PRECISION}
MaxTanH: Float = 44.361419555836499802702855773323; // Ln(2^128)/2
MaxFactorial = 33;
MaxFloatingPoint: Float = 3.4028236692093846346337460743177E+38; // 2^128
MinFloatingPoint: Float = 1.1754943508222875079687365372222E-38; // 2^(-126)
{$ENDIF MATH_SINGLE_PRECISION}
const
PiExt = 3.1415926535897932384626433832795;
RatioDegToRad : Extended = PiExt / 180.0;
RatioRadToDeg : Extended = 180.0 / PiExt;
RatioGradToRad : Extended = PiExt / 200.0;
RatioRadToGrad : Extended = 200.0 / PiExt;
RatioDegToGrad : Extended = 200.0 / 180.0;
RatioGradToDeg : Extended = 180.0 / 200.0;
var
PrecisionTolerance: Float = 0.0000001;
EpsSingle: Single;
EpsDouble: Double;
EpsExtended: Extended;
Epsilon: Float;
ThreeEpsSingle: Single;
ThreeEpsDouble: Double;
ThreeEpsExtended: Extended;
ThreeEpsilon: Float;
type
TPrimalityTestMethod = (ptTrialDivision, ptRabinMiller);
// swaps 2 bytes
procedure SwapOrd(var X, Y: Integer); {$IFDEF SUPPORTS_INLINE}inline;{$ENDIF}
// converts double to hex
function DoubleToHex(const D: Double): string; {$IFDEF SUPPORTS_INLINE}inline;{$ENDIF}
// converts hex to double
function HexToDouble(const Hex: string): Double;
// Converts degrees to radians.
function DegToRad(const Value: Extended): Extended; overload; {$IFDEF SUPPORTS_INLINE}inline;{$ENDIF}
function DegToRad(const Value: Double): Double; overload; {$IFDEF SUPPORTS_INLINE}inline;{$ENDIF}
function DegToRad(const Value: Single): Single; overload; {$IFDEF SUPPORTS_INLINE}inline;{$ENDIF}
{$IFDEF CPU386}
procedure FastDegToRad;
{$ENDIF CPU386}
// Converts radians to degrees.
function RadToDeg(const Value: Extended): Extended; overload; {$IFDEF SUPPORTS_INLINE}inline;{$ENDIF}
function RadToDeg(const Value: Double): Double; overload; {$IFDEF SUPPORTS_INLINE}inline;{$ENDIF}
function RadToDeg(const Value: Single): Single; overload; {$IFDEF SUPPORTS_INLINE}inline;{$ENDIF}
{$IFDEF CPU386}
procedure FastRadToDeg;
{$ENDIF CPU386}
// Converts grads to radians.
function GradToRad(const Value: Extended): Extended; overload; {$IFDEF SUPPORTS_INLINE}inline;{$ENDIF}
function GradToRad(const Value: Double): Double; overload; {$IFDEF SUPPORTS_INLINE}inline;{$ENDIF}
function GradToRad(const Value: Single): Single; overload; {$IFDEF SUPPORTS_INLINE}inline;{$ENDIF}
{$IFDEF CPU386}
procedure FastGradToRad;
{$ENDIF CPU386}
// Converts radians to grads.
function RadToGrad(const Value: Extended): Extended; overload; {$IFDEF SUPPORTS_INLINE}inline;{$ENDIF}
function RadToGrad(const Value: Double): Double; overload; {$IFDEF SUPPORTS_INLINE}inline;{$ENDIF}
function RadToGrad(const Value: Single): Single; overload; {$IFDEF SUPPORTS_INLINE}inline;{$ENDIF}
{$IFDEF CPU386}
procedure FastRadToGrad;
{$ENDIF CPU386}
// Converts degrees to grads.
function DegToGrad(const Value: Extended): Extended; overload; {$IFDEF SUPPORTS_INLINE}inline;{$ENDIF}
function DegToGrad(const Value: Double): Double; overload; {$IFDEF SUPPORTS_INLINE}inline;{$ENDIF}
function DegToGrad(const Value: Single): Single; overload; {$IFDEF SUPPORTS_INLINE}inline;{$ENDIF}
{$IFDEF CPU386}
procedure FastDegToGrad;
{$ENDIF CPU386}
// Converts grads to degrees.
function GradToDeg(const Value: Extended): Extended; overload; {$IFDEF SUPPORTS_INLINE}inline;{$ENDIF}
function GradToDeg(const Value: Double): Double; overload; {$IFDEF SUPPORTS_INLINE}inline;{$ENDIF}
function GradToDeg(const Value: Single): Single; overload; {$IFDEF SUPPORTS_INLINE}inline;{$ENDIF}
{$IFDEF CPU386}
procedure FastGradToDeg;
{$ENDIF CPU386}
{ Logarithmic }
function LogBase10(X: Float): Float;
function LogBase2(X: Float): Float;
function LogBaseN(Base, X: Float): Float;
{ Transcendental }
function ArcCos(X: Float): Float;
function ArcCot(X: Float): Float; {$IFDEF SUPPORTS_INLINE}inline;{$ENDIF}
function ArcCsc(X: Float): Float; {$IFDEF SUPPORTS_INLINE}inline;{$ENDIF}
function ArcSec(X: Float): Float;
function ArcSin(X: Float): Float;
function ArcTan(X: Float): Float;
function ArcTan2(Y, X: Float): Float;
function Cos(X: Float): Float; overload;
function Cot(X: Float): Float; overload;
function Coversine(X: Float): Float; {$IFDEF SUPPORTS_INLINE}inline;{$ENDIF}
function Csc(X: Float): Float; overload;
function Exsecans(X: Float): Float; {$IFDEF SUPPORTS_INLINE}inline;{$ENDIF}
function Haversine(X: Float): Float; {$IFDEF SUPPORTS_INLINE}inline;{$ENDIF}
function Sec(X: Float): Float; overload;
function Sin(X: Float): Float; overload;
procedure SinCos(X: Float; var Sin, Cos: Float);
function Tan(X: Float): Float; overload;
function Versine(X: Float): Float; {$IFDEF SUPPORTS_INLINE}inline;{$ENDIF}
{ Hyperbolic }
function ArcCosH(X: Float): Float;
function ArcCotH(X: Float): Float;
function ArcCscH(X: Float): Float;
function ArcSecH(X: Float): Float;
function ArcSinH(X: Float): Float;
function ArcTanH(X: Float): Float;
function CosH(X: Float): Float; overload;
function CotH(X: Float): Float; overload; {$IFDEF SUPPORTS_INLINE}inline;{$ENDIF}
function CscH(X: Float): Float; overload;
function SecH(X: Float): Float; overload;
function SinH(X: Float): Float; overload; {$IFDEF CLR}{$IFDEF SUPPORTS_INLINE}inline;{$ENDIF}{$ENDIF}
function TanH(X: Float): Float; overload;
{ Coordinate conversion }
function DegMinSecToFloat(const Degs, Mins, Secs: Float): Float; // obsolete (see JclUnitConv)
procedure FloatToDegMinSec(const X: Float; var Degs, Mins, Secs: Float); // obsolete (see JclUnitConv)
{ Exponential }
function Exp(const X: Float): Float; overload;
function Power(const Base, Exponent: Float): Float; overload;
function PowerInt(const X: Float; N: Integer): Float; overload;
function TenToY(const Y: Float): Float; {$IFDEF SUPPORTS_INLINE}inline;{$ENDIF}
function TruncPower(const Base, Exponent: Float): Float; {$IFDEF SUPPORTS_INLINE}inline;{$ENDIF}
function TwoToY(const Y: Float): Float; {$IFDEF SUPPORTS_INLINE}inline;{$ENDIF}
{ Floating point numbers support routines }
function IsFloatZero(const X: Float): Boolean; {$IFDEF SUPPORTS_INLINE}inline;{$ENDIF}
function FloatsEqual(const X, Y: Float): Boolean;
function MaxFloat(const X, Y: Float): Float; {$IFDEF SUPPORTS_INLINE}inline;{$ENDIF}
function MinFloat(const X, Y: Float): Float; {$IFDEF SUPPORTS_INLINE}inline;{$ENDIF}
function ModFloat(const X, Y: Float): Float;
function RemainderFloat(const X, Y: Float): Float; {$IFDEF SUPPORTS_INLINE}inline;{$ENDIF}
function SetPrecisionTolerance(NewTolerance: Float): Float; {$IFDEF SUPPORTS_INLINE}inline;{$ENDIF}
procedure SwapFloats(var X, Y: Float); {$IFDEF SUPPORTS_INLINE}inline;{$ENDIF}
procedure CalcMachineEpsSingle;
procedure CalcMachineEpsDouble;
procedure CalcMachineEpsExtended;
procedure CalcMachineEps;
procedure SetPrecisionToleranceToEpsilon; {$IFDEF SUPPORTS_INLINE}inline;{$ENDIF}
{ Miscellaneous }
function Ackermann(const A, B: Integer): Integer;
function Ceiling(const X: Float): Integer;
function CommercialRound(const X: Float): Int64;
function Factorial(const N: Integer): Float;
function Fibonacci(const N: Integer): Integer;
function Floor(const X: Float): Integer;
function GCD(X, Y: Cardinal): Cardinal;
function ISqrt(const I: Smallint): Smallint;
function LCM(const X, Y: Cardinal): Cardinal;
function NormalizeAngle(const Angle: Float): Float;
function Pythagoras(const X, Y: Float): Float;
function Sgn(const X: Float): Integer;
function Signe(const X, Y: Float): Float;
{ Ranges }
function EnsureRange(const AValue, AMin, AMax: Integer): Integer; overload;
function EnsureRange(const AValue, AMin, AMax: Int64): Int64; overload;
function EnsureRange(const AValue, AMin, AMax: Double): Double; overload;
{ Prime numbers }
function IsRelativePrime(const X, Y: Cardinal): Boolean; {$IFDEF SUPPORTS_INLINE}inline;{$ENDIF}
function IsPrimeTD(N: Cardinal): Boolean;
{$IFDEF CPU386}
function IsPrimeRM(N: Cardinal): Boolean;
{$ENDIF CPU386}
function IsPrimeFactor(const F, N: Cardinal): Boolean; {$IFDEF SUPPORTS_INLINE}inline;{$ENDIF}
function PrimeFactors(N: Cardinal): TDynCardinalArray;
var
IsPrime: function(N: Cardinal): Boolean = IsPrimeTD;
{$IFDEF CPU386}
procedure SetPrimalityTest(const Method: TPrimalityTestMethod);
{$ENDIF CPU386}
{$IFDEF CPU386}
{ Floating point value classification }
type
TFloatingPointClass =
(
fpZero, // zero
fpNormal, // normal finite <> 0
fpDenormal, // denormalized finite
fpInfinite, // infinite
fpNaN, // not a number
fpInvalid // unsupported floating point format
);
function FloatingPointClass(const Value: Single): TFloatingPointClass; overload;
function FloatingPointClass(const Value: Double): TFloatingPointClass; overload;
function FloatingPointClass(const Value: Extended): TFloatingPointClass; overload;
{$ENDIF CPU386}
{ NaN and INF support }
type
TNaNTag = -$3FFFFF..$3FFFFE;
const
Infinity = 1/0; // tricky
{$EXTERNALSYM Infinity}
NaN = 0/0; // tricky
{$EXTERNALSYM NaN}
NegInfinity = -Infinity;
{$EXTERNALSYM NegInfinity}
{$HPPEMIT 'static const Infinity = 1.0 / 0.0;'}
{$HPPEMIT 'static const NaN = 0.0 / 0.0;'}
{$HPPEMIT 'static const NegInfinity = -1.0 / 0.0;'}
function IsInfinite(const Value: Single): Boolean; overload; {$IFDEF SUPPORTS_INLINE}inline;{$ENDIF}
function IsInfinite(const Value: Double): Boolean; overload; {$IFDEF SUPPORTS_INLINE}inline;{$ENDIF}
function IsInfinite(const Value: Extended): Boolean; overload; {$IFDEF SUPPORTS_INLINE}inline;{$ENDIF}
function IsNaN(const Value: Single): Boolean; overload; {$IFDEF SUPPORTS_INLINE}inline;{$ENDIF}
function IsNaN(const Value: Double): Boolean; overload; {$IFDEF SUPPORTS_INLINE}inline;{$ENDIF}
function IsNaN(const Value: Extended): Boolean; overload; {$IFDEF SUPPORTS_INLINE}inline;{$ENDIF}
function IsSpecialValue(const X: Float): Boolean; {$IFDEF SUPPORTS_INLINE}inline;{$ENDIF}
procedure MakeQuietNaN(var X: Single; Tag: TNaNTag = 0); overload;
procedure MakeQuietNaN(var X: Double; Tag: TNaNTag = 0); overload;
procedure MakeQuietNaN(var X: Extended; Tag: TNaNTag = 0); overload;
procedure MakeSignalingNaN(var X: Single; Tag: TNaNTag = 0); overload;
procedure MakeSignalingNaN(var X: Double; Tag: TNaNTag = 0); overload;
procedure MakeSignalingNaN(var X: Extended; Tag: TNaNTag = 0); overload;
{$IFNDEF CLR}
{ Mine*Buffer fills "Buffer" with consecutive tagged signaling NaNs.
This allows for real number arrays which enforce initialization: any attempt
to load an uninitialized array element into the FPU will raise an exception
either of class EInvalidOp (Windows 9x/ME) or EJclNaNSignal (Windows NT).
Under Windows NT it is thus possible to derive the violating array index from
the EJclNaNSignal object's Tag property. }
procedure MineSingleBuffer(var Buffer; Count: Integer; StartTag: TNaNTag = 0);
procedure MineDoubleBuffer(var Buffer; Count: Integer; StartTag: TNaNTag = 0);
function MinedSingleArray(Length: Integer): TDynSingleArray;
function MinedDoubleArray(Length: Integer): TDynDoubleArray;
{$ENDIF ~CLR}
function GetNaNTag(const NaN: Single): TNaNTag; overload;
function GetNaNTag(const NaN: Double): TNaNTag; overload;
function GetNaNTag(const NaN: Extended): TNaNTag; overload;
{ Set support }
type
TJclASet = class(TObject)
{$IFDEF CLR}
public
{$ELSE}
protected
{$ENDIF}
function GetBit(const Idx: Integer): Boolean; virtual; abstract;
procedure SetBit(const Idx: Integer; const Value: Boolean); virtual; abstract;
procedure Clear; virtual; abstract;
procedure Invert; virtual; abstract;
function GetRange(const Low, High: Integer; const Value: Boolean): Boolean; virtual; abstract;
procedure SetRange(const Low, High: Integer; const Value: Boolean); virtual; abstract;
end;
type
TJclFlatSet = class(TJclASet)
private
FBits: TBits;
public
constructor Create;
destructor Destroy; override;
procedure Clear; override;
procedure Invert; override;
procedure SetRange(const Low, High: Integer; const Value: Boolean); override;
function GetBit(const Idx: Integer): Boolean; override;
function GetRange(const Low, High: Integer; const Value: Boolean): Boolean; override;
procedure SetBit(const Idx: Integer; const Value: Boolean); override;
end;
type
{$IFNDEF CLR}
TPointerArray = array [0..MaxLongint div 256] of Pointer;
PPointerArray = ^TPointerArray;
{$ENDIF ~CLR}
TDelphiSet = set of Byte; // 256 elements
PDelphiSet = ^TDelphiSet;
const
EmptyDelphiSet: TDelphiSet = [];
CompleteDelphiSet: TDelphiSet = [0..255];
{$IFNDEF CLR}
type
TJclSparseFlatSet = class(TJclASet)
private
FSetList: PPointerArray;
FSetListEntries: Integer;
public
destructor Destroy; override;
procedure Clear; override;
procedure Invert; override;
function GetBit(const Idx: Integer): Boolean; override;
procedure SetBit(const Idx: Integer; const Value: Boolean); override;
procedure SetRange(const Low, High: Integer; const Value: Boolean); override;
function GetRange(const Low, High: Integer; const Value: Boolean): Boolean; override;
end;
{$ENDIF ~CLR}
{ Rational numbers }
type
TJclRational = class(TObject)
private
FT: Integer;
FN: Integer;
function GetAsString: string;
procedure SetAsString(const S: string);
function GetAsFloat: Float;
procedure SetAsFloat(const R: Float);
protected
procedure Simplify;
public
constructor Create; overload;
constructor Create(const R: Float); overload;
constructor Create(const Numerator: Integer; const Denominator: Integer = 1); overload;
property Numerator: Integer read FT;
property Denominator: Integer read FN;
property AsString: string read GetAsString write SetAsString;
property AsFloat: Float read GetAsFloat write SetAsFloat;
procedure Assign(const R: TJclRational); overload; {$IFDEF SUPPORTS_INLINE}inline;{$ENDIF}
procedure Assign(const R: Float); overload;
procedure Assign(const Numerator: Integer; const Denominator: Integer = 1); overload;
procedure AssignZero; {$IFDEF SUPPORTS_INLINE}inline;{$ENDIF}
procedure AssignOne; {$IFDEF SUPPORTS_INLINE}inline;{$ENDIF}
function Duplicate: TJclRational; {$IFDEF SUPPORTS_INLINE}inline;{$ENDIF}
function IsEqual(const R: TJclRational): Boolean; reintroduce; overload; {$IFDEF SUPPORTS_INLINE}inline;{$ENDIF}
function IsEqual(const Numerator: Integer; const Denominator: Integer = 1) : Boolean; reintroduce; overload; {$IFDEF SUPPORTS_INLINE}inline;{$ENDIF}
function IsEqual(const R: Float): Boolean; reintroduce; overload; {$IFDEF SUPPORTS_INLINE}inline;{$ENDIF}
function IsZero: Boolean; {$IFDEF SUPPORTS_INLINE}inline;{$ENDIF}
function IsOne: Boolean; {$IFDEF SUPPORTS_INLINE}inline;{$ENDIF}
procedure Add(const R: TJclRational); overload;
procedure Add(const V: Float); overload; {$IFDEF SUPPORTS_INLINE}inline;{$ENDIF}
procedure Add(const V: Integer); overload; {$IFDEF SUPPORTS_INLINE}inline;{$ENDIF}
procedure Subtract(const R: TJclRational); overload;
procedure Subtract(const V: Float); overload; {$IFDEF SUPPORTS_INLINE}inline;{$ENDIF}
procedure Subtract(const V: Integer); overload; {$IFDEF SUPPORTS_INLINE}inline;{$ENDIF}
procedure Negate; {$IFDEF SUPPORTS_INLINE}inline;{$ENDIF}
procedure Abs;
function Sgn: Integer;
procedure Multiply(const R: TJclRational); overload;
procedure Multiply(const V: Float); overload; {$IFDEF SUPPORTS_INLINE}inline;{$ENDIF}
procedure Multiply(const V: Integer); overload; {$IFDEF SUPPORTS_INLINE}inline;{$ENDIF}
procedure Reciprocal;
procedure Divide(const R: TJclRational); overload;
procedure Divide(const V: Float); overload; {$IFDEF SUPPORTS_INLINE}inline;{$ENDIF}
procedure Divide(const V: Integer); overload;
procedure Sqrt; {$IFDEF SUPPORTS_INLINE}inline;{$ENDIF}
procedure Sqr; {$IFDEF SUPPORTS_INLINE}inline;{$ENDIF}
procedure Power(const R: TJclRational); overload;
procedure Power(const V: Integer); overload;
procedure Power(const V: Float); overload;
end;
type
EJclMathError = class(EJclError);
{$IFNDEF CLR}
EJclNaNSignal = class(EJclMathError)
private
FTag: TNaNTag;
public
constructor Create(ATag: TNaNTag; Dummy: Boolean = False);
property Tag: TNaNTag read FTag;
end;
{$ENDIF ~CLR}
procedure DomainCheck(Err: Boolean);
{ Checksums }
{$IFDEF CLR}
function GetParity(Buffer: TDynByteArray; Len: Integer): Boolean;
{$ELSE}
function GetParity(Buffer: PByte; Len: Integer): Boolean;
{$ENDIF CLR}
{ CRC }
function Crc16_P(X: PJclByteArray; N: Integer; Crc: Word = 0): Word;
function Crc16(const X: array of Byte; N: Integer; Crc: Word = 0): Word;
function Crc16_A(const X: array of Byte; Crc: Word = 0): Word;
function CheckCrc16_P(X: PJclByteArray; N: Integer; Crc: Word): Integer;
function CheckCrc16(var X: array of Byte; N: Integer; Crc: Word): Integer;
function CheckCrc16_A(var X: array of Byte; Crc: Word): Integer;
function Crc32_P(X: PJclByteArray; N: Integer; Crc: Cardinal = 0): Cardinal;
function Crc32(const X: array of Byte; N: Integer; Crc: Cardinal = 0): Cardinal;
function Crc32_A(const X: array of Byte; Crc: Cardinal = 0): Cardinal;
function CheckCrc32_P(X: PJclByteArray; N: Integer; Crc: Cardinal): Integer;
function CheckCrc32(var X: array of Byte; N: Integer; Crc: Cardinal): Integer;
function CheckCrc32_A(var X: array of Byte; Crc: Cardinal): Integer;
{$IFDEF CRCINIT}
procedure InitCrc32(Polynom, Start: Cardinal);
procedure InitCrc16(Polynom, Start: Word);
{$ENDIF CRCINIT}
{ Complex numbers }
type
TPolarComplex = record
Radius: Float;
Angle: Float;
end;
TRectComplex = record
Re: Float;
Im: Float;
{$IFDEF SUPPORTS_CLASS_OPERATORS}
class operator Implicit(const Value: Float): TRectComplex;
class operator Implicit(const Value: Integer): TRectComplex;
class operator Implicit(const Value: Int64): TRectComplex;
class operator Implicit(const Z: TPolarComplex): TRectComplex;
class operator Equal(const Z1, Z2: TRectComplex): Boolean;
class operator NotEqual(const Z1, Z2: TRectComplex): Boolean;
class operator Add(const Z1, Z2: TRectComplex): TRectComplex; inline;
class operator Subtract(const Z1, Z2: TRectComplex): TRectComplex;
class operator Multiply(const Z1, Z2: TRectComplex): TRectComplex;
class operator Divide(const Z1, Z2: TRectComplex): TRectComplex;
class operator Negative(const Z: TRectComplex): TRectComplex;
class function Exp(const Z: TRectComplex): TPolarComplex; static;
{$ENDIF SUPPORTS_CLASS_OPERATORS}
end;
function RectComplex(const Re: Float; const Im: Float = 0): TRectComplex; overload;
function RectComplex(const Z: TPolarComplex): TRectComplex; overload;
function PolarComplex(const Radius: Float; const Angle: Float = 0): TPolarComplex; overload;
function PolarComplex(const Z: TRectComplex): TPolarComplex; overload;
function Equal(const Z1, Z2: TRectComplex): Boolean; overload;
function Equal(const Z1, Z2: TPolarComplex): Boolean; overload;
function IsZero(const Z: TRectComplex): Boolean; overload;
function IsZero(const Z: TPolarComplex): Boolean; overload;
function IsInfinite(const Z: TRectComplex): Boolean; overload;
function IsInfinite(const Z: TPolarComplex): Boolean; overload;
function Norm(const Z: TRectComplex): Float; overload;
function Norm(const Z: TPolarComplex): Float; overload;
function AbsSqr(const Z: TRectComplex): Float; overload;
function AbsSqr(const Z: TPolarComplex): Float; overload;
function Conjugate(const Z: TRectComplex): TRectComplex; overload;
function Conjugate(const Z: TPolarComplex): TPolarComplex; overload;
function Inv(const Z: TRectComplex): TRectComplex; overload;
function Inv(const Z: TPolarComplex): TPolarComplex; overload;
function Neg(const Z: TRectComplex): TRectComplex; overload;
function Neg(const Z: TPolarComplex): TPolarComplex; overload;
function Sum(const Z1, Z2: TRectComplex): TRectComplex; overload;
function Sum(const Z: array of TRectComplex): TRectComplex; overload;
function Diff(const Z1, Z2: TRectComplex): TRectComplex;
function Product(const Z1, Z2: TRectComplex): TRectComplex; overload;
function Product(const Z1, Z2: TPolarComplex): TPolarComplex; overload;
function Quotient(const Z1, Z2: TRectComplex): TRectComplex;
function Ln(const Z: TPolarComplex): TRectComplex;
function Exp(const Z: TRectComplex): TPolarComplex; overload;
function Power(const Z: TPolarComplex; const Exponent: TRectComplex): TPolarComplex; overload;
function Power(const Z: TPolarComplex; const Exponent: Float): TPolarComplex; overload;
function PowerInt(const Z: TPolarComplex; const Exponent: Integer): TPolarComplex; overload;
function Root(const Z: TPolarComplex; const K, N: Cardinal): TPolarComplex;
function Cos(const Z: TRectComplex): TRectComplex; overload;
function Sin(const Z: TRectComplex): TRectComplex; overload;
function Tan(const Z: TRectComplex): TRectComplex; overload;
function Cot(const Z: TRectComplex): TRectComplex; overload;
function Sec(const Z: TRectComplex): TRectComplex; overload;
function Csc(const Z: TRectComplex): TRectComplex; overload;
function CosH(const Z: TRectComplex): TRectComplex; overload;
function SinH(const Z: TRectComplex): TRectComplex; overload;
function TanH(const Z: TRectComplex): TRectComplex; overload;
function CotH(const Z: TRectComplex): TRectComplex; overload;
function SecH(const Z: TRectComplex): TRectComplex; overload;
function CscH(const Z: TRectComplex): TRectComplex; overload;
{$IFDEF UNITVERSIONING}
const
UnitVersioning: TUnitVersionInfo = (
RCSfile: '$URL: https://jcl.svn.sourceforge.net:443/svnroot/jcl/tags/JCL-1.100-Build2646/jcl/source/common/JclMath.pas $';
Revision: '$Revision: 1726 $';
Date: '$Date: 2006-08-11 17:24:18 +0200 (ven., 11 août 2006) $';
LogPath: 'JCL\source\common'
);
{$ENDIF UNITVERSIONING}
implementation
uses
{$IFDEF Win32API}
Windows,
{$ENDIF Win32API}
{$IFDEF CPU386}
Jcl8087,
{$ENDIF CPU386}
JclResources;
// Internal helper routines
// Linux: Get Global Offset Table (GOT) adress for Position Independent Code
// (PIC, used by shared objects)
{$IFNDEF CLR}
{$IFDEF PIC}
function GetGOT: Pointer; export;
begin
asm
MOV Result, EBX
end;
end;
{$ENDIF PIC}
{$ENDIF ~CLR}
// to keep name space usage low
const
JclMathSgn: function(const X: Float): Integer = Sgn;
JclMathPower: function(const Base, Exponent: Float): Float = Power;
// to be independent from JclLogic
function Min(const X, Y: Integer): Integer;
begin
if X < Y then
Result := X
else
Result := Y;
end;
// to be independent from JCLLogic
procedure SwapOrd(var X, Y: Integer);
var
Temp: Integer;
begin
Temp := X;
X := Y;
Y := Temp;
end;
function DoubleToHex(const D: Double): string;
{$IFDEF CLR}
begin
Result := IntToHex(BitConverter.DoubleToInt64Bits(D), 16);
end;
{$ELSE}
var
Overlay: array [1..2] of Longint absolute D;
begin
// Look at element 2 before element 1 because of "Little Endian" order.
Result := IntToHex(Overlay[2], 8) + IntToHex(Overlay[1], 8);
end;
{$ENDIF CLR}
function HexToDouble(const Hex: string): Double;
{$IFDEF CLR}
begin
if Length(Hex) <> 16 then
raise EJclMathError.Create(RsUnexpectedValue);
Result := BitConverter.Int64BitsToDouble(StrToInt64('$' + Hex))
end;
{$ELSE}
var
D: Double;
Overlay: array [1..2] of Longint absolute D;
begin
if Length(Hex) <> 16 then
raise EJclMathError.CreateRes(@RsUnexpectedValue);
Overlay[1] := StrToInt('$' + Copy(Hex, 9, 8));
Overlay[2] := StrToInt('$' + Copy(Hex, 1, 8));
Result := D;
end;
{$ENDIF CLR}
// Converts degrees to radians.
function DegToRad(const Value: Extended): Extended;
begin
Result := Value * RatioDegToRad;
end;
function DegToRad(const Value: Double): Double;
begin
Result := Value * RatioDegToRad;
end;
function DegToRad(const Value: Single): Single;
begin
Result := Value * RatioDegToRad;
end;
{$IFDEF CPU386}
// Expects degrees in ST(0), leaves radians in ST(0)
// ST(0) := ST(0) * PI / 180
procedure FastDegToRad; assembler;
asm
{$IFDEF PIC}
CALL GetGOT
FLD [EAX][RatioDegToRad]
{$ELSE}
FLD [RatioDegToRad]
{$ENDIF PIC}
FMULP
FWAIT
end;
{$ENDIF CPU386}
// Converts radians to degrees.
function RadToDeg(const Value: Extended): Extended;
begin
Result := Value * RatioRadToDeg;
end;
function RadToDeg(const Value: Double): Double;
begin
Result := Value * RatioRadToDeg;
end;
function RadToDeg(const Value: Single): Single;
begin
Result := Value * RatioRadToDeg;
end;
{$IFDEF CPU386}
// Expects radians in ST(0), leaves degrees in ST(0)
// ST(0) := ST(0) * (180 / PI);
procedure FastRadToDeg; assembler;
asm
{$IFDEF PIC}
CALL GetGOT
FLD [EAX][RatioRadToDeg]
{$ELSE}
FLD [RatioRadToDeg]
{$ENDIF PIC}
FMULP
FWAIT
end;
{$ENDIF CPU386}
// Converts grads to radians.
function GradToRad(const Value: Extended): Extended;
begin
Result := Value * RatioGradToRad;
end;
function GradToRad(const Value: Double): Double;
begin
Result := Value * RatioGradToRad;
end;
function GradToRad(const Value: Single): Single;
begin
Result := Value * RatioGradToRad;
end;
{$IFDEF CPU386}
// Expects grads in ST(0), leaves radians in ST(0)
// ST(0) := ST(0) * PI / 200
procedure FastGradToRad; assembler;
asm
{$IFDEF PIC}
CALL GetGOT
FLD [EAX][RatioGradToRad]
{$ELSE}
FLD [RatioGradToRad]
{$ENDIF PIC}
FMULP
FWAIT
end;
{$ENDIF CPU386}
// Converts radians to grads.
function RadToGrad(const Value: Extended): Extended;
begin
Result := Value * RatioRadToGrad;
end;
function RadToGrad(const Value: Double): Double;
begin
Result := Value * RatioRadToGrad;
end;
function RadToGrad(const Value: Single): Single;
begin
Result := Value * RatioRadToGrad;
end;
{$IFDEF CPU386}
// Expects radians in ST(0), leaves grads in ST(0)
// ST(0) := ST(0) * (200 / PI);
procedure FastRadToGrad; assembler;
asm
{$IFDEF PIC}
CALL GetGOT
FLD [EAX][RatioRadToGrad]
{$ELSE}
FLD [RatioRadToGrad]
{$ENDIF PIC}
FMULP
FWAIT
end;
{$ENDIF CPU386}
// Converts degrees to grads.
function DegToGrad(const Value: Extended): Extended;
begin
Result := Value * RatioDegToGrad;
end;
function DegToGrad(const Value: Double): Double;
begin
Result := Value * RatioDegToGrad;
end;
function DegToGrad(const Value: Single): Single;
begin
Result := Value * RatioDegToGrad;
end;
{$IFDEF CPU386}
// Expects Degrees in ST(0), leaves grads in ST(0)
// ST(0) := ST(0) * (200 / 180);
procedure FastDegToGrad; assembler;
asm
{$IFDEF PIC}
CALL GetGOT
FLD [EAX][RatioDegToGrad]
{$ELSE}
FLD [RatioDegToGrad]
{$ENDIF PIC}
FMULP
FWAIT
end;
{$ENDIF CPU386}
// Converts grads to degrees.
function GradToDeg(const Value: Extended): Extended;
begin
Result := Value * RatioGradToDeg;
end;
function GradToDeg(const Value: Double): Double;
begin
Result := Value * RatioGradToDeg;
end;
function GradToDeg(const Value: Single): Single;
begin
Result := Value * RatioGradToDeg;
end;
{$IFDEF CPU386}
// Expects grads in ST(0), leaves radians in ST(0)
// ST(0) := ST(0) * PI / 200
procedure FastGradToDeg; assembler;
asm
{$IFDEF PIC}
CALL GetGOT
FLD [EAX][RatioGradToDeg]
{$ELSE}
FLD [RatioGradToDeg]
{$ENDIF PIC}
FMULP
FWAIT
end;
{$ENDIF CPU386}
procedure DomainCheck(Err: Boolean);
begin
if Err then
{$IFDEF CLR}
raise EJclMathError.Create(RsMathDomainError);
{$ELSE}
raise EJclMathError.CreateRes(@RsMathDomainError);
{$ENDIF CLR}
end;
//=== Logarithmic ============================================================
function LogBase10(X: Float): Float;
{$IFDEF CPU386}
function FLogBase10(X: Float): Float; assembler;
asm
FLDLG2
FLD X
FYL2X
FWAIT
end;
{$ENDIF CPU386}
begin
DomainCheck(X <= 0.0);
{$IFDEF CLR}
Result := System.Math.Log10(X)
{$ELSE}
Result := FLogBase10(X);
{$ENDIF CLR}
end;
function LogBase2(X: Float): Float;
{$IFDEF CPU386}
function FLogBase2(X: Float): Float; assembler;
asm
FLD1
FLD X
FYL2X
FWAIT
end;
{$ENDIF CPU386}
begin
DomainCheck(X <= 0.0);
{$IFDEF CLR}
Result := System.Math.Log(X, 2);
{$ELSE}
Result := FLogBase2(X);
{$ENDIF CLR}
end;
function LogBaseN(Base, X: Float): Float;
{$IFDEF CPU386}
function FLogBaseN(Base, X: Float): Float; assembler;
asm
FLD1
FLD X
FYL2X
FLD1
FLD Base
FYL2X
FDIV
FWAIT
end;
{$ENDIF CPU386}
begin
DomainCheck((X <= 0.0) or (Base <= 0.0) or (Base = 1.0));
{$IFDEF CLR}
Result := System.Math.Log(X);
{$ELSE}
Result := FLogBaseN(Base, X);
{$ENDIF CLR}
end;
//=== Transcendental =========================================================
function ArcCos(X: Float): Float;
{$IFDEF CPU386}
function FArcCos(X: Float): Float; assembler;
asm
FLD X
FLD ST(0)
FMUL ST(0), ST
FLD1
FSUBRP ST(1), ST
FSQRT
FXCH
FPATAN
FWAIT
end;
{$ENDIF CPU386}
begin
DomainCheck(Abs(X) > 1.0);
{$IFDEF CLR}
Result := System.Math.Acos(X);
{$ELSE}
Result := FArcCos(X);
{$ENDIF CLR}
end;
function ArcCot(X: Float): Float;
begin
DomainCheck(X = 0);
Result := ArcTan(1 / X);
end;
function ArcCsc(X: Float): Float;
begin
Result := ArcSec(X / Sqrt(X * X -1));
end;
function ArcSec(X: Float): Float;
{$IFDEF CPU386}
function FArcTan(X: Float): Float; assembler;
asm
FLD X
FLD1
FPATAN
FWAIT
end;
{$ENDIF CPU386}
begin
{$IFDEF CLR}
Result := System.Math.Atan(Sqrt(X*X - 1))
{$ELSE}
Result := FArcTan(Sqrt(X*X - 1));
{$ENDIF CLR}
end;
function ArcSin(X: Float): Float;
{$IFDEF CPU386}
function FArcSin(X: Float): Float; assembler;
asm
FLD X
FLD ST(0)
FMUL ST(0), ST
FLD1
FSUBRP ST(1), ST
FSQRT
FPATAN
FWAIT
end;
{$ENDIF CPU386}
begin
DomainCheck(Abs(X) > 1.0);
{$IFDEF CPU386}
Result := FArcSin(X);
{$ELSE}
Result := System.Math.Asin(X);
{$ENDIF}
end;
function ArcTan(X: Float): Float;
{$IFDEF PUREPASCAL}
begin
Result := ArcTan2(X, 1);
end;
{$ELSE ~PUREPASCAL}
assembler;
asm
FLD X
FLD1
FPATAN
FWAIT
end;
{$ENDIF ~PUREPASCAL}
{$IFDEF CLR}
function ArcTan2(Y, X: Float): Float;
begin
Result := System.Math.ATan2(Y, X);
end;
{$ELSE}
function ArcTan2(Y, X: Float): Float; assembler;
asm
FLD Y
FLD X
FPATAN
FWAIT
end;
{$ENDIF CLR}
function Cos(X: Float): Float;
{$IFDEF CPU386}
function FCos(X: Float): Float; assembler;
asm
FLD X
FCOS
FWAIT
end;
{$ENDIF CPU386}
begin
DomainCheck(Abs(X) > MaxAngle);
{$IFDEF CLR}
Result := System.Math.Cos(X);
{$ELSE}
Result := FCos(X);
{$ENDIF CLR}
end;
function Cot(X: Float): Float;
{$IFDEF CPU386}
function FCot(X: Float): Float; assembler;
asm
FLD X
FPTAN
FDIVRP
FWAIT
end;
{$ENDIF CPU386}
begin
DomainCheck(Abs(X) > MaxAngle);
{ TODO : Cot = 1 / Tan -> Tan(X) <> 0.0 }
{$IFDEF CLR}
Result := 1 / System.Math.Tan(X);
{$ELSE}
Result := FCot(X);
{$ENDIF CLR}
end;
function Coversine(X: Float): Float;
begin
Result := 1 - Sin(X);
end;
function Csc(X: Float): Float;
var
Y: Float;
begin
DomainCheck(Abs(X) > MaxAngle);
Y := Sin(X);
DomainCheck(Y = 0.0);
Result := 1.0 / Y;
end;
function Exsecans(X: Float): Float;
begin
Result := Sec(X) - 1;
end;
function Haversine(X: Float): Float;
begin
Result := 0.5 * (1 - Cos(X));
end;
function Sec(X: Float): Float;
{$IFDEF CPU386}
function FSec(X: Float): Float; assembler;
asm
FLD X
FCOS
FLD1
FDIVRP
FWAIT
end;
{$ENDIF CPU386}
begin
DomainCheck(Abs(X) > MaxAngle);
{ TODO : Sec = 1 / Cos -> Cos(X) <> 0! }
{$IFDEF CLR}
Result := 1 / System.Math.Cos(X);
{$ELSE}
Result := FSec(X);
{$ENDIF CLR}
end;
function Sin(X: Float): Float;
{$IFDEF CPU386}
function FSin(X: Float): Float; assembler;
asm
FLD X
FSIN
FWAIT
end;
{$ENDIF CPU386}
begin
{$IFNDEF MATH_EXT_SPECIALVALUES}
DomainCheck(Abs(X) > MaxAngle);
{$ENDIF ~MATH_EXT_SPECIALVALUES}
{$IFDEF CLR}
Result := System.Math.Sin(X);
{$ELSE}
Result := FSin(X);
{$ENDIF CLR}
end;
procedure SinCos(X: Float; var Sin, Cos: Float);
{$IFDEF CPU386}
procedure FSinCos(X: Float; var Sin, Cos: Float); assembler;
asm
FLD X
FSINCOS
FSTP Float PTR [EDX]
FSTP Float PTR [EAX]
FWAIT
end;
{$ENDIF CPU386}
begin
DomainCheck(Abs(X) > MaxAngle);
{$IFDEF CLR}
Sin := System.Math.Sin(X);
Cos := System.Math.Cos(X);
{$ELSE}
FSinCos(X, Sin, Cos);
{$ENDIF CLR}
end;
function Tan(X: Float): Float;
{$IFDEF CPU386}
function FTan(X: Float): Float; assembler;
asm
FLD X
FPTAN
FSTP ST(0)
FWAIT
end;
{$ENDIF CPU386}
begin
DomainCheck(Abs(X) > MaxAngle);
{$IFDEF CLR}
Result := System.Math.Tan(X);
{$ELSE}
Result := FTan(X);
{$ENDIF CLR}
end;
function Versine(X: Float): Float;
begin
Result := 1 - Cos(X);
end;
//=== Hyperbolic =============================================================
function ArcCosH(X: Float): Float;
{$IFDEF CPU386}
function FArcCosH(X: Float): Float; assembler;
asm
FLDLN2
FLD X
FLD ST(0)
FMUL ST(0), ST
FLD1
FSUBP ST(1), ST
FSQRT
FADDP ST(1), ST
FYL2X
end;
{$ENDIF CPU386}
begin
DomainCheck(X < 1.0);
{$IFDEF CLR}
Result := System.Math.Log(X, 2);
{$ELSE}
Result := FArcCosH(X);
{$ENDIF CLR}
end;
function ArcCotH(X: Float): Float;
begin
DomainCheck(Abs(X) = 1.0);
{$IFDEF CLR}
Result := 0.5 * System.Math.Log((X + 1.0) / (X - 1.0));
{$ELSE}
Result := 0.5 * System.Ln((X + 1.0) / (X - 1.0));
{$ENDIF CLR}
end;
function ArcCscH(X: Float): Float;
begin
DomainCheck(X = 0);
{$IFDEF CLR}
Result := System.Math.Log((Sgn(X) * Sqrt(Sqr(X) + 1.0) + 1.0) / X);
{$ELSE}
Result := System.Ln((Sgn(X) * Sqrt(Sqr(X) + 1.0) + 1.0) / X);
{$ENDIF CLR}
end;
function ArcSecH(X: Float): Float;
begin
DomainCheck(Abs(X) > 1.0);
{$IFDEF CLR}
Result := System.Math.Log((Sqrt(1.0 - Sqr(X)) + 1.0) / X);
{$ELSE}
Result := System.Ln((Sqrt(1.0 - Sqr(X)) + 1.0) / X);
{$ENDIF CLR}
end;
function ArcSinH(X: Float): Float;
{$IFDEF CLR}
begin
Result := System.Math.Log(X + Sqrt(1 + X * X));
end;
{$ELSE}
assembler;
asm
FLDLN2
FLD X
FLD ST(0)
FMUL ST(0), ST
FLD1
FADDP ST(1), ST
FSQRT
FADDP ST(1), ST
FYL2X
end;
{$ENDIF CLR}
function ArcTanH(X: Float): Float;
{$IFDEF CPU386}
function FArcTanH(X: Float): Float; assembler;
asm
FLDLN2
FLD X
FLD ST(0)
FLD1
FADDP ST(1), ST
FXCH
FLD1
FSUBRP ST(1), ST
FDIVP ST(1), ST
FSQRT
FYL2X
FWAIT
end;
{$ENDIF CPU386}
begin
DomainCheck(Abs(X) >= 1.0);
{$IFDEF CLR}
Result := System.Math.Log((1 + X) / (1 - X)) / 2;
{$ELSE}
Result := FArcTanH(X);
{$ENDIF CLR}
end;
function CosH(X: Float): Float;
{$IFDEF PUREPASCAL}
begin
Result := 0.5 * (Exp(X) + Exp(-X));
end;
{$ELSE ~PUREPASCAL}
const
RoundDown: Word = $177F;
OneHalf: Float = 0.5;
var
ControlWW: Word;
asm
{$IFDEF PIC}
CALL GetGOT
{$ENDIF PIC}
FLD X { TODO : Legal values for X? }
FLDL2E
FMULP ST(1), ST
FSTCW ControlWW
{$IFDEF PIC}
FLDCW [EAX].RoundDown
{$ELSE}
FLDCW RoundDown
{$ENDIF PIC}
FLD ST(0)
FRNDINT
FLDCW ControlWW
FXCH
FSUB ST, ST(1)
F2XM1
FLD1
FADDP ST(1), ST
FSCALE
FST ST(1)
FLD1
FDIVRP ST(1), ST
FADDP ST(1), ST
{$IFDEF PIC}
FLD [EAX].OneHalf
{$ELSE}
FLD OneHalf
{$ENDIF PIC}
FMULP ST(1), ST
FWAIT
end;
{$ENDIF ~PUREPASCAL}
function CotH(X: Float): Float;
begin
Result := 1 / TanH(X);
end;
function CscH(X: Float): Float;
begin
Result := Exp(X) - Exp(-X);
DomainCheck(Result = 0.0);
Result := 2.0 / Result;
end;
function SecH(X: Float): Float;
begin
Result := Exp(X) + Exp(-X);
DomainCheck(Result = 0.0);
Result := 2.0 / Result;
end;
function SinH(X: Float): Float;
{$IFDEF CLR}
begin
Result := System.Math.Sinh(X);
end;
{$ELSE ~CLR}
assembler;
const
RoundDown: Word = $177F;
OneHalf: Float = 0.5;
var
ControlWW: Word;
asm
{$IFDEF PIC}
CALL GetGOT
{$ENDIF PIC}
FLD X { TODO : Legal values for X? }
FLDL2E
FMULP ST(1), ST
FSTCW ControlWW
{$IFDEF PIC}
FLDCW [EAX].RoundDown
{$ELSE}
FLDCW RoundDown
{$ENDIF PIC}
FLD ST(0)
FRNDINT
FLDCW ControlWW
FXCH
FSUB ST, ST(1)
F2XM1
FLD1
FADDP ST(1), ST
FSCALE
FST ST(1)
FLD1
FDIVRP ST(1), ST
FSUBP ST(1), ST
{$IFDEF PIC}
FLD [EAX].OneHalf
{$ELSE}
FLD OneHalf
{$ENDIF PIC}
FMULP ST(1), ST
FWAIT
end;
{$ENDIF ~CLR}
function TanH(X: Float): Float;
begin
if X > MaxTanH then
Result := 1.0
else
begin
if X < -MaxTanH then
Result := -1.0
else
begin
Result := Exp(X);
Result := Result * Result;
Result := (Result - 1.0) / (Result + 1.0);
end;
end;
end;
//=== Coordinate conversion ==================================================
function DegMinSecToFloat(const Degs, Mins, Secs: Float): Float; // obsolete
begin
Result := Degs + (Mins / 60.0) + (Secs / 3600.0);
end;
procedure FloatToDegMinSec(const X: Float; var Degs, Mins, Secs: Float); // obsolete
var
Y: Float;
begin
Degs := {$IFDEF CLR}Borland.Delphi.{$ENDIF}System.Int(X);
Y := Frac(X) * 60;
Mins := {$IFDEF CLR}Borland.Delphi.{$ENDIF}System.Int(Y);
Secs := Frac(Y) * 60;
end;
//=== Exponential ============================================================
function Exp(const X: Float): Float;
begin
{$IFDEF MATH_EXT_EXTREMEVALUES}
if IsSpecialValue(X) then
begin
if IsNaN(X) or (X = Infinity) then
Result := X
else
Result := 0;
Exit;
end;
{$ENDIF MATH_EXT_EXTREMEVALUES}
Result := {$IFDEF CLR}Borland.Delphi.{$ENDIF}System.Exp(X);
end;
function Power(const Base, Exponent: Float): Float;
var
IsAnInteger, IsOdd: Boolean;
begin
if (Exponent = 0.0) or (Base = 1.0) then
Result := 1
else
if Base = 0.0 then
begin
if Exponent > 0.0 then
Result := 0.0
else
{$IFDEF MATH_EXT_EXTREMEVALUES}
Result := Infinity;
{$ELSE}
{$IFDEF CLR}
raise EJclMathError.Create(RsPowerInfinite);
{$ELSE}
raise EJclMathError.CreateRes(@RsPowerInfinite);
{$ENDIF CLR}
{$ENDIF MATH_EXT_EXTREMEVALUES}
end
else
if Base > 0.0 then
Result := Exp(Exponent * {$IFDEF CLR}Borland.Delphi.{$ENDIF}System.Ln(Base))
else
begin
IsAnInteger := (Frac(Exponent) = 0.0);
if IsAnInteger then
begin
Result := Exp(Exponent * {$IFDEF CLR}Borland.Delphi.{$ENDIF}System.Ln(Abs(Base)));
IsOdd := Abs(Round(ModFloat(Exponent, 2))) = 1;
if IsOdd then
Result := -Result;
end
else
{$IFDEF CLR}
raise EJclMathError.Create(RsPowerComplex);
{$ELSE}
raise EJclMathError.CreateRes(@RsPowerComplex);
{$ENDIF CLR}
end;
end;
function PowerInt(const X: Float; N: Integer): Float;
var
M: Integer;
T: Float;
Xc: Float;
begin
if X = 0.0 then
begin
if N = 0 then
Result := 1.0
else
if N > 0 then
Result := 0.0
else
Result := MaxFloatingPoint;
Exit;
end;
if N = 0 then
begin
Result := 1.0;
Exit;
end;
// Legendre's algorithm for minimizing the number of multiplications
T := 1.0;
M := Abs(N);
Xc := X;
repeat
if Odd(M) then
begin
Dec(M);
T := T * Xc;
end
else
begin
M := M div 2;
Xc := Sqr(Xc);
end;
until M = 0;
if N > 0 then
Result := T
else
Result := 1.0 / T;
end;
function TenToY(const Y: Float): Float;
begin
if Y = 0.0 then
Result := 1.0
else
Result := Exp(Y * Ln10);
end;
function TruncPower(const Base, Exponent: Float): Float;
begin
if Base > 0 then
Result := Power(Base, Exponent)
else
Result := 0;
end;
function TwoToY(const Y: Float): Float;
begin
if Y = 0.0 then
Result := 1.0
else
Result := Exp(Y * Ln2);
end;
//=== Floating point support routines ========================================
function IsFloatZero(const X: Float): Boolean;
begin
Result := Abs(X) < PrecisionTolerance;
end;
function FloatsEqual(const X, Y: Float): Boolean;
begin
try
if Y = 0 then
// catch exact equality
Result := (X = Y) or (Abs(1 - Y/X ) <= PrecisionTolerance)
else
// catch exact equality
Result := (X = Y) or (Abs(1 - X/Y ) <= PrecisionTolerance);
except
Result := False; // catch real rare overflow e.g. 1.0e3000/1.0e-3000
end
end;
function MaxFloat(const X, Y: Float): Float;
begin
if X < Y then
Result := Y
else
Result := X;
end;
function MinFloat(const X, Y: Float): Float;
begin
if X > Y then
Result := Y
else
Result := X;
end;
function ModFloat(const X, Y: Float): Float;
var
Z: Float;
begin
Result := X / Y;
Z := {$IFDEF CLR}Borland.Delphi.{$ENDIF}System.Int(Result);
if Frac(Result) < 0.0 then
Z := Z - 1.0;
Result := X - Y * Z;
end;
function RemainderFloat(const X, Y: Float): Float;
begin
Result := X - {$IFDEF CLR}Borland.Delphi.{$ENDIF}System.Int(X / Y) * Y;
end;
procedure SwapFloats(var X, Y: Float);
var
T: Float;
begin
T := X;
X := Y;
Y := T;
end;
procedure CalcMachineEpsSingle;
var
One: Single;
T: Single;
begin
One := 1.0;
EpsSingle := One;
repeat
EpsSingle := 0.5 * EpsSingle;
T := One + EpsSingle;
until One = T;
EpsSingle := 2.0 * EpsSingle;
ThreeEpsSingle := 3.0 * EpsSingle;
end;
procedure CalcMachineEpsDouble;
var
One: Double;
T: Double;
begin
One := 1.0;
EpsDouble := One;
repeat
EpsDouble := 0.5 * EpsDouble;
T := One + EpsDouble;
until One = T;
EpsDouble := 2.0 * EpsDouble;
ThreeEpsDouble := 3.0 * EpsDouble;
end;
procedure CalcMachineEpsExtended;
var
One: Extended;
T: Extended;
begin
One := 1.0;
EpsExtended := One;
repeat
EpsExtended := 0.5 * EpsExtended;
T := One + EpsExtended;
until One = T;
EpsExtended := 2.0 * EpsExtended;
ThreeEpsExtended := 3.0 * EpsExtended;
end;
procedure CalcMachineEps;
begin
{$IFDEF MATH_EXTENDED_PRECISION}
CalcMachineEpsExtended;
Epsilon := EpsExtended;
ThreeEpsilon := ThreeEpsExtended;
{$ENDIF MATH_EXTENDED_PRECISION}
{$IFDEF MATH_DOUBLE_PRECISION}
CalcMachineEpsDouble;
Epsilon := EpsDouble;
ThreeEpsilon := ThreeEpsDouble;
{$ENDIF MATH_DOUBLE_PRECISION}
{$IFDEF MATH_SINGLE_PRECISION}
CalcMachineEpsSingle;
Epsilon := EpsSingle;
ThreeEpsilon := ThreeEpsSingle;
{$ENDIF MATH_SINGLE_PRECISION}
end;
procedure SetPrecisionToleranceToEpsilon;
begin
CalcMachineEps;
PrecisionTolerance := Epsilon;
end;
function SetPrecisionTolerance(NewTolerance: Float): Float;
begin
Result := PrecisionTolerance;
PrecisionTolerance := NewTolerance;
end;
//=== Miscellaneous ==========================================================
function Ceiling(const X: Float): Integer;
begin
Result := Integer(Trunc(X));
if Frac(X) > 0 then
Inc(Result);
end;
function CommercialRound(const X: Float): Int64;
begin
Result := Trunc(X);
if Frac(Abs(X)) >= 0.5 then
Result := Result + Sgn(X);
end;
const
PreCompFactsCount = 33; // all factorials that fit in a Single
{$IFDEF MATH_SINGLE_PRECISION}
PreCompFacts: array [0..PreCompFactsCount] of Float =
(
1.0,
1.0,
2.0,
6.0,
24.0,
120.0,
720.0,
5040.0,
40320.0,
362880.0,
3628800.0,
39916800.0,
479001600.0,
6227020800.0,
87178289152.0,
1307674279936.0,
20922788478976.0,
355687414628352.0,
6.4023735304192E15,
1.21645096004223E17,
2.43290202316367E18,
5.10909408371697E19,
1.12400072480601E21,
2.58520174445945E22,
6.20448454699065E23,
1.55112110792462E25,
4.03291499589617E26,
1.08888704151327E28,
3.04888371623715E29,
8.8417630793192E30,
2.65252889961724E32,
8.22283968552752E33,
2.63130869936881E35,
8.68331850984666E36
);
{$ENDIF MATH_SINGLE_PRECISION}
{$IFDEF MATH_DOUBLE_PRECISION}
PreCompFacts: array [0..PreCompFactsCount] of Float =
(
1.0,
1.0,
2.0,
6.0,
24.0,
120.0,
720.0,
5040.0,
40320.0,
362880.0,
3628800.0,
39916800.0,
479001600.0,
6227020800.0,
87178291200.0,
1307674368000.0,
20922789888000.0,
355687428096000.0,
6.402373705728E15,
1.21645100408832E17,
2.43290200817664E18,
5.10909421717094E19,
1.12400072777761E21,
2.5852016738885E22,
6.20448401733239E23,
1.5511210043331E25,
4.03291461126606E26,
1.08888694504184E28,
3.04888344611714E29,
8.8417619937397E30,
2.65252859812191E32,
8.22283865417792E33,
2.63130836933694E35,
8.68331761881189E36
);
{$ENDIF MATH_DOUBLE_PRECISION}
{$IFDEF MATH_EXTENDED_PRECISION}
PreCompFacts: array [0..PreCompFactsCount] of Float =
(
1.0,
1.0,
2.0,
6.0,
24.0,
120.0,
720.0,
5040.0,
40320.0,
362880.0,
3628800.0,
39916800.0,
479001600.0,
6227020800.0,
87178291200.0,
1307674368000.0,
20922789888000.0,
355687428096000.0,
6.402373705728E15,
1.21645100408832E17,
2.43290200817664E18,
5.10909421717094E19,
1.12400072777761E21,
2.5852016738885E22,
6.20448401733239E23,
1.5511210043331E25,
4.03291461126606E26,
1.08888694504184E28,
3.04888344611714E29,
8.8417619937397E30,
2.65252859812191E32,
8.22283865417792E33,
2.63130836933694E35,
8.68331761881189E36
);
{$ENDIF MATH_EXTENDED_PRECISION}
function Factorial(const N: Integer): Float;
var
I: Integer;
begin
if (N < 0) or (N > MaxFactorial) then
Result := 0.0
else
begin
if N <= PreCompFactsCount then
Result := PreCompFacts[N]
else
begin { TODO : Change following by: Gamma(N + 1) }
Result := PreCompFacts[PreCompFactsCount];
for I := PreCompFactsCount + 1 to N do
Result := Result * I;
end;
end;
end;
function Floor(const X: Float): Integer;
begin
Result := Integer(Trunc(X));
if Frac(X) < 0 then
Dec(Result);
end;
function GCD(X, Y: Cardinal): Cardinal;
{$IFDEF PUREPASCAL}
begin
Result := X;
while Y <> 0 do
begin
X := Result;
Result := Y;
Y := X mod Y;
end;
end;
{$ELSE ~PUREPASCAL}
assembler;
{ Euclid's algorithm }
asm
JMP @01 // We start with EAX <- X, EDX <- Y, and check to see if Y=0
@00:
MOV ECX, EDX // ECX <- EDX prepare for division
XOR EDX, EDX // clear EDX for Division
DIV ECX // EAX <- EDX:EAX div ECX, EDX <- EDX:EAX mod ECX
MOV EAX, ECX // EAX <- ECX, and repeat if EDX <> 0
@01:
AND EDX, EDX // test to see if EDX is zero, without changing EDX
JNZ @00 // when EDX is zero EAX has the Result
end;
{$ENDIF ~PUREPASCAL}
function ISqrt(const I: Smallint): Smallint;
{$IFDEF PUREPASCAL}
var
b, d: Smallint;
begin
Result := -1;
d := -1;
b := 0;
repeat
Inc(Result);
Inc(d, 2);
b := b + d;
until b > I;
end;
{$ELSE ~PUREPASCAL}
assembler;
asm
PUSH EBX
MOV CX, AX // load argument
MOV AX, -1 // init Result
CWD // init odd numbers to -1
XOR BX, BX // init perfect squares to 0
@LOOP:
INC AX // increment Result
INC DX // compute
INC DX // next odd number
ADD BX, DX // next perfect square
CMP BX, CX // perfect square > argument ?
JBE @LOOP // until square greater than argument
POP EBX
end;
{$ENDIF ~PUREPASCAL}
function LCM(const X, Y: Cardinal): Cardinal;
var
E: Cardinal;
begin
E := GCD(X, Y);
if E > 0 then
Result := (X div E) * Y
else
Result := 0;
end;
function NormalizeAngle(const Angle: Float): Float;
begin
Result := Angle;
{$IFDEF MATH_ANGLE_DEGREES}
Result := DegToRad(Result);
{$ENDIF MATH_ANGLE_DEGREES}
{$IFDEF MATH_ANGLE_GRADS}
Result := GradToRad(Result);
{$ENDIF MATH_ANGLE_GRADS}
Result := Frac(Result * Inv2Pi);
if Result < -0.5 then
Result := Result + 1.0
else
if Result >= 0.5 then
Result := Result - 1.0;
Result := Result * TwoPi;
{$IFDEF MATH_ANGLE_DEGREES}
Result := RadToDeg(Result);
{$ENDIF MATH_ANGLE_DEGREES}
{$IFDEF MATH_ANGLE_GRADS}
Result := RadToGrad(Result);
{$ENDIF MATH_ANGLE_GRADS}
end;
function Pythagoras(const X, Y: Float): Float;
var
AbsX, AbsY: Float;
begin
AbsX := Abs(X);
AbsY := Abs(Y);
if AbsX > AbsY then
Result := AbsX * Sqrt(1.0 + Sqr(AbsY / AbsX))
else
if AbsY = 0.0 then
Result := 0.0
else
Result := AbsY * Sqrt(1.0 + Sqr(AbsX / AbsY));
end;
function Sgn(const X: Float): Integer;
begin
if X > 0.0 then
Result := 1
else
if X < 0.0 then
Result := -1
else
Result := 0;
end;
function Signe(const X, Y: Float): Float;
begin
if X > 0.0 then
begin
if Y > 0.0 then
Result := X
else
Result := -X;
end
else
begin
if Y < 0.0 then
Result := X
else
Result := -X;
end;
end;
function Ackermann(const A, B: Integer): Integer;
begin
if A = 0 then
begin
Result := B + 1;
Exit;
end;
if B = 0 then
Result := Ackermann(A - 1, 1)
else
Result := Ackermann(A - 1, Ackermann(A, B - 1));
end;
function Fibonacci(const N: Integer): Integer;
var
I: Integer;
P1, P2: Integer;
begin
Assert(N >= 0);
Result := 0;
P1 := 1;
P2 := 1;
if (N = 1) or (N = 2) then
Result := 1
else
for I := 3 to N do
begin
Result := P1 + P2;
P1 := P2;
P2 := Result;
end;
end;
//=== { TJclFlatSet } ========================================================
constructor TJclFlatSet.Create;
begin
inherited Create;
FBits := TBits.Create;
end;
destructor TJclFlatSet.Destroy;
begin
FBits.Free;
FBits := nil;
inherited Destroy;
end;
procedure TJclFlatSet.Clear;
begin
FBits.Size := 0;
end;
procedure TJclFlatSet.Invert;
var
I: Integer;
begin
for I := 0 to FBits.Size - 1 do
FBits[I] := not FBits[I];
end;
procedure TJclFlatSet.SetRange(const Low, High: Integer; const Value: Boolean);
var
I: Integer;
begin
for I := High downto Low do
FBits[I] := Value;
end;
function TJclFlatSet.GetBit(const Idx: Integer): Boolean;
begin
Result := FBits[Idx];
end;
function TJclFlatSet.GetRange(const Low, High: Integer; const Value: Boolean): Boolean;
var
I: Integer;
begin
if not Value and (High >= FBits.Size) then
begin
Result := False;
Exit;
end;
for I := Low to Min(High, FBits.Size - 1) do
if FBits[I] <> Value then
begin
Result := False;
Exit;
end;
Result := True;
end;
procedure TJclFlatSet.SetBit(const Idx: Integer; const Value: Boolean);
begin
FBits[Idx] := Value;
end;
{$IFNDEF CLR}
//== { TJclSparseFlatSet } ===================================================
destructor TJclSparseFlatSet.Destroy;
begin
Clear;
inherited Destroy;
end;
procedure TJclSparseFlatSet.Clear;
var
F: Integer;
begin
if FSetList <> nil then
begin
for F := 0 to FSetListEntries - 1 do
if FSetList^[F] <> nil then
Dispose(PDelphiSet(FSetList^[F]));
FreeMem(FSetList, FSetListEntries * SizeOf(Pointer));
FSetList := nil;
FSetListEntries := 0;
end;
end;
procedure TJclSparseFlatSet.Invert;
var
F: Integer;
begin
for F := 0 to FSetListEntries - 1 do
if FSetList^[F] <> nil then
PDelphiSet(FSetList^[F])^ := CompleteDelphiSet - PDelphiSet(FSetList^[F])^;
end;
function TJclSparseFlatSet.GetBit(const Idx: Integer): Boolean;
var
SetIdx: Integer;
begin
SetIdx := Idx shr 8;
Result := (SetIdx < FSetListEntries) and (FSetList^[SetIdx] <> nil) and
(Byte(Idx and $FF) in PDelphiSet(FSetList^[SetIdx])^);
end;
procedure TJclSparseFlatSet.SetBit(const Idx: Integer; const Value: Boolean);
var
I, SetIdx: Integer;
S: PDelphiSet;
begin
SetIdx := Idx shr 8;
if SetIdx >= FSetListEntries then
if Value then
begin
I := FSetListEntries;
FSetListEntries := SetIdx + 1;
ReallocMem(FSetList, FSetListEntries * SizeOf(Pointer));
FillChar(FSetList^[I], (FSetListEntries - I) * SizeOf(Pointer), #0);
end
else
Exit;
S := FSetList^[SetIdx];
if S = nil then
if Value then
begin
New(S);
S^ := [];
FSetList^[SetIdx] := S;
end
else
Exit;
Include(S^, Byte(Idx and $FF));
end;
procedure TJclSparseFlatSet.SetRange(const Low, High: Integer; const Value: Boolean);
var
I, LowSet, HighSet: Integer;
procedure SetValue(const S: TDelphiSet; const SetIdx: Integer);
var
D: PDelphiSet;
begin
D := FSetList^[SetIdx];
if D = nil then
begin
if Value then
begin
New(D);
D^ := S;
FSetList^[SetIdx] := D;
end;
end
else
if Value then
D^ := D^ + S
else
D^ := D^ - S;
end;
begin
LowSet := Low shr 8;
HighSet := High shr 8;
if HighSet >= FSetListEntries then
begin
I := FSetListEntries;
FSetListEntries := HighSet + 1;
ReallocMem(FSetList, FSetListEntries * SizeOf(Pointer));
FillChar(FSetList^[I], (FSetListEntries - I) * SizeOf(Pointer), #0);
end;
if LowSet = HighSet then
SetValue([Byte(Low and $FF)..Byte(High and $FF)], LowSet)
else
begin
SetValue([Byte(Low and $FF)..$FF], LowSet);
SetValue([0..Byte(High and $FF)], HighSet);
for I := LowSet + 1 to HighSet - 1 do
SetValue(CompleteDelphiSet, I);
end;
end;
function TJclSparseFlatSet.GetRange(const Low, High: Integer; const Value: Boolean): Boolean;
var
I: Integer;
begin
if not Value and (High >= FSetListEntries) then
begin
Result := False;
Exit;
end;
for I := Low to Min(High, FSetListEntries) do
if GetBit(I) <> Value then
begin
Result := False;
Exit;
end;
Result := True;
end;
{$ENDIF ~CLR}
//=== Ranges =================================================================
function EnsureRange(const AValue, AMin, AMax: Integer): Integer;
begin
Result := AValue;
Assert(AMin <= AMax);
if Result < AMin then
Result := AMin;
if Result > AMax then
Result := AMax;
end;
function EnsureRange(const AValue, AMin, AMax: Int64): Int64;
begin
Result := AValue;
Assert(AMin <= AMax);
if Result < AMin then
Result := AMin;
if Result > AMax then
Result := AMax;
end;
function EnsureRange(const AValue, AMin, AMax: Double): Double;
begin
Result := AValue;
Assert(AMin <= AMax);
if Result < AMin then
Result := AMin;
if Result > AMax then
Result := AMax;
end;
//=== Prime numbers ==========================================================
const
PrimeCacheLimit = 65537; // 4K lookup table. Note: Sqr(65537) > MaxLongint
var
PrimeSet: TJclFlatSet = nil;
procedure InitPrimeSet;
var
I, J, MaxI, MaxJ : Integer;
begin
PrimeSet := TJclFlatSet.Create;
PrimeSet.SetRange(1, PrimeCacheLimit div 2, True);
PrimeSet.SetBit(0, False); // 1 is no prime
MaxI := Trunc(Sqrt(PrimeCacheLimit));
I := 3;
repeat
if PrimeSet.GetBit(I div 2) then
begin
MaxJ := PrimeCacheLimit div I;
J := 3;
repeat
PrimeSet.SetBit((I*J) div 2, False);
Inc(J,2);
until J > MaxJ;
end;
Inc(I, 2);
until I > MaxI;
end;
function IsPrimeTD(N: Cardinal): Boolean;
{ Trial Division Algorithm }
var
I, Max: Cardinal;
R: Extended;
begin
if N = 2 then
begin
Result := True;
Exit;
end;
if (N and 1) = 0 then //Zero or even
begin
Result := False;
Exit;
end;
if PrimeSet = nil then // initialize look-up table
InitPrimeSet;
if N <= PrimeCacheLimit then // do look-up
Result := PrimeSet.GetBit(N div 2)
else
begin // calculate
R := N;
Max := Round(Sqrt (R));
if Max > PrimeCacheLimit then
begin
{$IFDEF CLR}
raise EJclMathError.Create(RsUnexpectedValue);
{$ELSE}
raise EJclMathError.CreateRes(@RsUnexpectedValue);
{$ENDIF CLR}
Exit;
end;
I := 1;
repeat
Inc(I,2);
if PrimeSet.GetBit(I div 2) then
if N mod I = 0 then
begin
Result := False;
Exit;
end;
until I >= Max;
Result := True;
end;
end;
{$IFDEF CPU386}
{ Rabin-Miller Strong Primality Test }
function IsPrimeRM(N: Cardinal): Boolean;
asm
TEST EAX,1 // Odd(N) ??
JNZ @@1
CMP EAX,2 // N == 2 ??
SETE AL
RET
@@1: CMP EAX,73
JBE @@C
PUSH ESI
PUSH EDI
PUSH EBX
PUSH EBP
PUSH EAX // save N as Param for @@5
LEA EBP,[EAX - 1] // M == N -1, Exponent
MOV ECX,32 // calc remaining Bits of M and shift M'
MOV ESI,EBP
@@2: DEC ECX
SHL ESI,1
JNC @@2
PUSH ECX // save Bits as Param for @@5
PUSH ESI // save M' as Param for @@5
CMP EAX,08A8D7Fh // N >= 9080191 ??
JAE @@3
// now if (N < 9080191) and SPP(31, N) and SPP(73, N) then N is prime
MOV EAX,31
CALL @@5
JC @@4
MOV EAX,73
PUSH OFFSET @@4
JMP @@5
// now if (N < 4759123141) and SPP(2, N) and SPP(7, N) and SPP(61, N) then N is prime
@@3: MOV EAX,2
CALL @@5
JC @@4
MOV EAX,7
CALL @@5
JC @@4
MOV EAX,61
CALL @@5
@@4: SETNC AL
ADD ESP,4 * 3
POP EBP
POP EBX
POP EDI
POP ESI
RET
// do a Strong Pseudo Prime Test
@@5: MOV EBX,[ESP + 12] // N on stack
MOV ECX,[ESP + 8] // remaining Bits
MOV ESI,[ESP + 4] // M'
MOV EDI,EAX // T = b, temp. Base
@@6: DEC ECX
MUL EAX
DIV EBX
MOV EAX,EDX
SHL ESI,1
JNC @@7
MUL EDI
DIV EBX
AND ESI,ESI
MOV EAX,EDX
@@7: JNZ @@6
CMP EAX,1 // b^((N -1)(2^s)) mod N == 1 mod N ??
JE @@A
@@8: CMP EAX,EBP // b^((N -1)(2^s)) mod N == -1 mod N ??
JE @@A
DEC ECX // second part to 2^s
JNG @@9
MUL EAX
DIV EBX
CMP EDX,1
MOV EAX,EDX
JNE @@8
@@9: STC
@@A: RET
@@B: DB 3,5,7,11,13,17,19,23,29,31,37,41,43,47,53,59,61,67,71,73
@@C: MOV EDX,OFFSET @@B
MOV ECX,19
@@D: CMP AL,[EDX + ECX]
JE @@E
DEC ECX
JNL @@D
@@E: SETE AL
end;
{$ENDIF CPU386}
function PrimeFactors(N: Cardinal): TDynCardinalArray;
var
I, L, Max: Cardinal;
R: Extended;
begin
SetLength(Result, 0);
if N <= 1 then
Exit
else
begin
if PrimeSet = nil then
InitPrimeSet;
L := 0;
R := N;
R := Sqrt(R);
Max := Round(R); // only one factor can be > Sqrt (N)
if N mod 2 = 0 then // test even at first
begin // 2 is a prime factor
Inc(L);
SetLength(Result, L);
Result[L - 1] := 2;
repeat
N := N div 2;
if N = 1 then // no more factors
Exit;
until N mod 2 <> 0;
end;
I := 3; // test all odd factors
repeat
if (N mod I = 0) and IsPrime(I) then
begin // I is a prime factor
Inc(L);
SetLength(Result, L);
Result[L - 1] := I;
repeat
N := N div I;
if N = 1 then // no more factors
Exit;
until N mod I <> 0;
end;
Inc(I, 2);
until I > Max;
Inc(L); // final factor (> Sqrt(N))
SetLength(Result, L);
Result[L - 1] := N;
end;
end;
function IsPrimeFactor(const F, N: Cardinal): Boolean;
begin
Result := (N mod F = 0) and IsPrime(F);
end;
function IsRelativePrime(const X, Y: Cardinal): Boolean;
begin
Result := GCD(X, Y) = 1;
end;
{$IFNDEF CLR}
procedure SetPrimalityTest(const Method: TPrimalityTestMethod);
begin
case Method of
ptTrialDivision:
IsPrime := IsPrimeTD;
ptRabinMiller:
IsPrime := IsPrimeRM;
end;
end;
{$ENDIF ~CLR}
{$IFDEF CPU386}
//=== Floating point value classification ====================================
const
fpEmpty = TFloatingPointClass(Ord(High(TFloatingPointClass))+1);
FPClasses: array [0..6] of TFloatingPointClass =
(
fpInvalid,
fpNaN,
fpNormal,
fpInfinite,
fpZero,
fpEmpty, // should not happen
fpDenormal
);
function _FPClass: TFloatingPointClass;
// In: ST(0) Value to examine
// ECX address of GOT (PIC only)
asm
FXAM
XOR EDX, EDX
FNSTSW AX
FFREE ST(0)
FINCSTP
BT EAX, 14 // C3
RCL EDX, 1
BT EAX, 10 // C2
RCL EDX, 1
BT EAX, 8 // C0
RCL EDX, 1
{$IFDEF PIC}
MOVZX EAX, TFloatingPointClass([ECX].FPClasses[EDX])
{$ELSE}
MOVZX EAX, TFloatingPointClass(FPClasses[EDX])
{$ENDIF PIC}
end;
function FloatingPointClass(const Value: Single): TFloatingPointClass; overload;
asm
{$IFDEF PIC}
CALL GetGOT
MOV ECX, EAX
{$ENDIF PIC}
FLD Value
CALL _FPClass
end;
function FloatingPointClass(const Value: Double): TFloatingPointClass; overload;
asm
{$IFDEF PIC}
CALL GetGOT
MOV ECX, EAX
{$ENDIF PIC}
FLD Value
CALL _FPClass
end;
function FloatingPointClass(const Value: Extended): TFloatingPointClass; overload;
asm
{$IFDEF PIC}
CALL GetGOT
MOV ECX, EAX
{$ENDIF PIC}
FLD Value
CALL _FPClass
end;
{$ENDIF CPU386}
//=== NaN and Infinity support ===============================================
function IsInfinite(const Value: Single): Boolean; overload;
begin
{$IFDEF CLR}
Result := System.Single.IsInfinity(Value);
{$ELSE}
Result := FloatingPointClass(Value) = fpInfinite;
{$ENDIF CLR}
end;
function IsInfinite(const Value: Double): Boolean; overload;
begin
{$IFDEF CLR}
Result := System.Double.IsInfinity(Value);
{$ELSE}
Result := FloatingPointClass(Value) = fpInfinite;
{$ENDIF CLR}
end;
function IsInfinite(const Value: Extended): Boolean; overload;
begin
{$IFDEF CLR}
Result := System.Double.IsInfinity(Value);
{$ELSE}
Result := FloatingPointClass(Value) = fpInfinite;
{$ENDIF CLR}
end;
const
sSignBit = 31;
dSignBit = 63;
xSignBit = 79;
type
TSingleBits = set of 0..sSignBit;
TDoubleBits = set of 0..dSignBit;
TExtendedBits = set of 0..xSignBit;
sFractionBits = 0..22; // Single type fraction bits
dFractionBits = 0..51; // Double type fraction bits
xFractionBits = 0..62; // Extended type fraction bits
sExponentBits = 23..sSignBit-1;
dExponentBits = 52..dSignBit-1;
xExponentBits = 64..xSignBit-1;
QWord = Int64;
PExtendedRec = ^TExtendedRec;
TExtendedRec = packed record
Significand: QWord;
Exponent: Word;
end;
const
ZeroTag = $3FFFFF;
InvalidTag = TNaNTag($80000000);
NaNTagMask = $3FFFFF;
sNaNQuietFlag = High(sFractionBits);
dNaNQuietFlag = High(dFractionBits);
xNaNQuietFlag = High(xFractionBits);
dNaNTagShift = High(dFractionBits) - High(sFractionBits);
xNaNTagShift = High(xFractionBits) - High(sFractionBits);
sNaNBits = $7F800000;
dNaNBits = $7FF0000000000000;
sQuietNaNBits = sNaNBits or (1 shl sNaNQuietFlag);
dQuietNaNBits = dNaNBits or (Int64(1) shl dNaNQuietFlag);
function IsNaN(const Value: Single): Boolean; overload;
begin
{$IFDEF CLR}
Result := System.Single.IsNaN(Value);
{$ELSE}
Result := FloatingPointClass(Value) = fpNaN;
{$ENDIF CLR}
end;
function IsNaN(const Value: Double): Boolean; overload;
begin
{$IFDEF CLR}
Result := System.Double.IsNaN(Value);
{$ELSE}
Result := FloatingPointClass(Value) = fpNaN;
{$ENDIF CLR}
end;
function IsNaN(const Value: Extended): Boolean; overload;
begin
{$IFDEF CLR}
Result := System.Double.IsNaN(Value);
{$ELSE}
Result := FloatingPointClass(Value) = fpNaN;
{$ENDIF CLR}
end;
procedure CheckNaN(const Value: Single); overload;
{$IFDEF CLR}
begin
if not IsNaN(Value) then
raise EJclMathError.Create(RsNoNaN);
end;
{$ELSE}
var
SaveExMask: T8087Exceptions;
begin
SaveExMask := Mask8087Exceptions([emInvalidOp]);
try
if FloatingPointClass(Value) <> fpNaN then
raise EJclMathError.CreateRes(@RsNoNaN);
finally
SetMasked8087Exceptions(SaveExMask);
end;
end;
{$ENDIF CLR}
procedure CheckNaN(const Value: Double); overload;
{$IFDEF CLR}
begin
if not IsNaN(Value) then
raise EJclMathError.Create(RsNoNaN);
end;
{$ELSE}
var
SaveExMask: T8087Exceptions;
begin
SaveExMask := Mask8087Exceptions([emInvalidOp]);
try
if FloatingPointClass(Value) <> fpNaN then
raise EJclMathError.CreateRes(@RsNoNaN);
finally
SetMasked8087Exceptions(SaveExMask);
end;
end;
{$ENDIF CLR}
procedure CheckNaN(const Value: Extended); overload;
{$IFDEF CLR}
begin
if not IsNaN(Value) then
raise EJclMathError.Create(RsNoNaN);
end;
{$ELSE}
var
SaveExMask: T8087Exceptions;
begin
SaveExMask := Mask8087Exceptions([emInvalidOp]);
try
if FloatingPointClass(Value) <> fpNaN then
raise EJclMathError.CreateRes(@RsNoNaN);
finally
SetMasked8087Exceptions(SaveExMask);
end;
end;
{$ENDIF CLR}
function GetNaNTag(const NaN: Single): TNaNTag;
var
Temp: Integer;
{$IFDEF CLR}
Bytes: Int32;
{$ENDIF CLR}
begin
CheckNaN(NaN);
{$IFDEF CLR}
Bytes := BitConverter.ToInt32(BitConverter.GetBytes(NaN), 0);
Temp := Bytes and NaNTagMask;
if Bytes and (1 shl sSignBit) <> 0 then
{$ELSE}
Temp := PLongint(@NaN)^ and NaNTagMask;
if sSignBit in TSingleBits(NaN) then
{$ENDIF CLR}
Result := -Temp
else
if Temp = ZeroTag then
Result := 0
else
Result := Temp;
end;
function GetNaNTag(const NaN: Double): TNaNTag;
var
Temp: Integer;
{$IFDEF CLR}
Bytes: Int64;
{$ENDIF CLR}
begin
CheckNaN(NaN);
{$IFDEF CLR}
Bytes := BitConverter.DoubleToInt64Bits(NaN);
Temp := (Bytes shr dNanTagShift) and NaNTagMask;
if Bytes and (1 shl dSignBit) <> 0 then
{$ELSE}
Temp := (PInt64(@NaN)^ shr dNaNTagShift) and NaNTagMask;
{$IFDEF FPC}
if Int64(NaN) < 0 then
{$ELSE}
if dSignBit in TDoubleBits(NaN) then
{$ENDIF FPC}
{$ENDIF CLR}
Result := -Temp
else
if Temp = ZeroTag then
Result := 0
else
Result := Temp;
end;
function GetNaNTag(const NaN: Extended): TNaNTag;
{$IFNDEF CLR}
var
Temp: Integer;
{$ENDIF ~CLR}
begin
{$IFDEF CLR}
Result := GetNaNTag(Double(NaN));
{$ELSE}
CheckNaN(NaN);
Temp := (PExtendedRec(@NaN)^.Significand shr xNaNTagShift) and NaNTagMask;
{$IFDEF FPC}
if (TExtendedRec(NaN).Exponent and $8000) <> 0 then
{$ELSE}
if xSignBit in TExtendedBits(NaN) then
{$ENDIF FPC}
Result := -Temp
else
if Temp = ZeroTag then
Result := 0
else
Result := Temp;
{$ENDIF CLR}
end;
{$IFNDEF CLR}
{$IFDEF MSWINDOWS}
type
TRealType = (rtUndef, rtSingle, rtDouble, rtExtended);
{ ExceptionInformation record for FPU exceptions under WinNT,
where documented? }
PFPUExceptionInfo = ^TFPUExceptionInfo;
TFPUExceptionInfo = packed record
Unknown: array [0..7] of Longint;
ControlWord: Word;
Dummy1: Word;
StatusWord: Word;
Dummy2: Word;
TagWord: Word;
Dummy3: Word;
InstructionPtr: Pointer;
UnknownW: Word;
OpCode: Word; // Note: 5 most significant bits of first opcode byte
// (always 11011b) not stored in FPU opcode register
OperandPtr: Pointer;
UnknownL: Longint;
end;
TExceptObjProc = function(P: PExceptionRecord): Exception;
var
PrevExceptObjProc: TExceptObjProc;
ExceptObjProcInitialized: Boolean = False;
function GetExceptionObject(P: PExceptionRecord): Exception;
var
Tag: TNaNTag;
FPUExceptInfo: PFPUExceptionInfo;
OPtr: Pointer;
OType: TRealType;
function GetOperandType(OpCode: Word): TRealType;
var
NNN: 0..7;
begin
Result := rtUndef;
NNN := (Lo(OpCode) shr 3) and 7; // NNN field of ModR/M byte
if Lo(OpCode) <= $BF then
case Hi(OpCode) of // 3 least significant bits of first opcode byte
0:
Result := rtSingle;
1:
if NNN < 4 then
Result := rtSingle;
// Extended signaling NaNs don't cause exceptions on FLD/FST(P) ?!
3:
if NNN = 5 then
Result := rtExtended;
4:
Result := rtDouble;
5:
if NNN = 0 then
Result := rtDouble;
end;
end;
begin
Tag := InvalidTag; // shut up compiler warning
OType := rtUndef;
if P^.ExceptionCode = STATUS_FLOAT_INVALID_OPERATION then
begin
FPUExceptInfo := @P^.ExceptionInformation;
OPtr := FPUExceptInfo^.OperandPtr;
OType := GetOperandType(FPUExceptInfo^.OpCode);
case OType of
rtSingle:
Tag := GetNaNTag(PSingle(OPtr)^);
rtDouble:
Tag := GetNaNTag(PDouble(OPtr)^);
rtExtended:
Tag := GetNaNTag(PExtended(OPtr)^);
end;
end;
if OType = rtUndef then
Result := PrevExceptObjProc(P)
else
Result := EJclNaNSignal.Create(Tag);
end;
{$ENDIF MSWINDOWS}
{$IFDEF MSWINDOWS}
{$IFNDEF FPC}
procedure InitExceptObjProc;
function IsInitialized: Boolean;
asm
MOV AL, True
LOCK XCHG AL, ExceptObjProcInitialized
end;
begin
if not IsInitialized then
if Win32Platform = VER_PLATFORM_WIN32_NT then
PrevExceptObjProc := Pointer(InterlockedExchange(Integer(ExceptObjProc), Integer(@GetExceptionObject)));
end;
{$ENDIF ~FPC}
{$ENDIF MSWINDOWS}
{$ENDIF ~CLR}
procedure CheckTag(Tag: TNaNTag);
begin
if (Tag < Low(TNaNTag)) or (Tag > High(TNaNTag)) then
{$IFDEF CLR}
raise EJclMathError.CreateFmt(RsNaNTagError, [Tag]);
{$ELSE}
raise EJclMathError.CreateResFmt(@RsNaNTagError, [Tag]);
{$ENDIF CLR}
end;
procedure MakeQuietNaN(var X: Single; Tag: TNaNTag);
var
Bits: LongWord;
begin
CheckTag(Tag);
if Tag = 0 then
Bits := ZeroTag or sQuietNaNBits
else
Bits := Abs(Tag) or sQuietNaNBits;
if Tag < 0 then
{$IFDEF CLR}
Bits := Bits or (LongWord(1) shl sSignBit);
X := BitConverter.ToSingle(BitConverter.GetBytes(Bits), 0);
{$ELSE}
Include(TSingleBits(Bits), sSignBit);
PLongWord(@X)^ := Bits;
{$ENDIF CLR}
end;
procedure MakeQuietNaN(var X: Double; Tag: TNaNTag);
var
Bits: Int64;
begin
CheckTag(Tag);
if Tag = 0 then
Bits := ZeroTag
else
Bits := Abs(Tag);
{$IFDEF CLR}
X := BitConverter.Int64BitsToDouble((Bits shl dNaNTagShift) or dQuietNaNBits);
{$ELSE}
PInt64(@X)^ := (Bits shl dNaNTagShift) or dQuietNaNBits;
{$ENDIF CLR}
if Tag < 0 then
{$IFDEF CLR}
X := BitConverter.Int64BitsToDouble(BitConverter.DoubleToInt64Bits(X) or (Int64(1) shl dSignBit));
{$ELSE}
{$IFDEF FPC}
QWord(X) := QWord(X) or (1 shl dSignBit);
{$ELSE}
Include(TDoubleBits(X), dSignBit);
{$ENDIF FPC}
{$ENDIF CLR}
end;
procedure MakeQuietNaN(var X: Extended; Tag: TNaNTag);
{$IFDEF CLR}
var
d: Double;
{$ELSE}
const
QuietNaNSignificand = $C000000000000000;
QuietNaNExponent = $7FFF;
var
Bits: Int64;
{$ENDIF CLR}
begin
{$IFDEF CLR}
d := X;
MakeQuietNaN(d);
X := d;
{$ELSE}
CheckTag(Tag);
if Tag = 0 then
Bits := ZeroTag
else
Bits := Abs(Tag);
TExtendedRec(X).Significand := (Bits shl xNaNTagShift) or QuietNaNSignificand;
TExtendedRec(X).Exponent := QuietNaNExponent;
if Tag < 0 then
{$IFDEF FPC}
TExtendedRec(X).Exponent := TExtendedRec(X).Exponent or $8000;
{$ELSE}
Include(TExtendedBits(X), xSignBit);
{$ENDIF FPC}
{$ENDIF CLR}
end;
procedure MakeSignalingNaN(var X: Single; Tag: TNaNTag);
begin
{$IFDEF ClR}
MakeQuietNaN(X, Tag);
BitConverter.ToSingle(
BitConverter.GetBytes(
BitConverter.ToInt32(
BitConverter.GetBytes(X), 0) and not (1 shl sNaNQuietFlag)), 0);
{$ELSE}
{$IFDEF MSWINDOWS}
{$IFNDEF FPC}
InitExceptObjProc;
{$ENDIF ~FPC}
{$ENDIF MSWINDOWS}
MakeQuietNaN(X, Tag);
Exclude(TSingleBits(X), sNaNQuietFlag);
{$ENDIF CLR}
end;
procedure MakeSignalingNaN(var X: Double; Tag: TNaNTag);
begin
{$IFDEF ClR}
MakeQuietNaN(X, Tag);
BitConverter.Int64BitsToDouble(
BitConverter.DoubleToInt64Bits(X) and not (1 shl sNaNQuietFlag));
{$ELSE}
{$IFDEF FPC}
MakeQuietNaN(X, Tag);
QWord(X) := QWord(X) and not (1 shl dNaNQuietFlag);
{$ELSE}
{$IFDEF MSWINDOWS}
InitExceptObjProc;
{$ENDIF MSWINDOWS}
MakeQuietNaN(X, Tag);
Exclude(TDoubleBits(X), dNaNQuietFlag);
{$ENDIF FPC}
{$ENDIF CLR}
end;
procedure MakeSignalingNaN(var X: Extended; Tag: TNaNTag);
{$IFDEF CLR}
var
d: Double;
{$ENDIF CLR}
begin
{$IFDEF CLR}
d := X;
MakeSignalingNaN(d, Tag);
X := d;
{$ELSE}
{$IFDEF FPC}
MakeQuietNaN(X, Tag);
TExtendedRec(X).Significand := TExtendedRec(X).Significand and not (1 shl xNaNQuietFlag);
{$ELSE}
{$IFDEF MSWINDOWS}
//InitExceptObjProc;
{$ENDIF MSWINDOWS}
MakeQuietNaN(X, Tag);
Exclude(TExtendedBits(X), xNaNQuietFlag);
{$ENDIF FPC}
{$ENDIF CLR}
end;
{$IFNDEF CLR}
procedure MineSingleBuffer(var Buffer; Count: Integer; StartTag: TNaNTag);
var
Tag, StopTag: TNaNTag;
P: PLongint;
begin
{$IFDEF MSWINDOWS}
{$IFNDEF FPC}
InitExceptObjProc;
{$ENDIF ~FPC}
{$ENDIF MSWINDOWS}
StopTag := StartTag + Count - 1;
CheckTag(StartTag);
CheckTag(StopTag);
P := @Buffer;
for Tag := StartTag to StopTag do
begin
if Tag > 0 then
P^ := sNaNBits or Tag
else
if Tag < 0 then
P^ := sNaNBits or Longint($80000000) or -Tag
else
P^ := sNaNBits or ZeroTag;
Inc(P);
end;
end;
procedure MineDoubleBuffer(var Buffer; Count: Integer; StartTag: TNaNTag);
var
Tag, StopTag: TNaNTag;
P: PInt64;
begin
{$IFDEF MSWINDOWS}
{$IFNDEF FPC}
InitExceptObjProc;
{$ENDIF ~FPC}
{$ENDIF MSWINDOWS}
StopTag := StartTag + Count - 1;
CheckTag(StartTag);
CheckTag(StopTag);
P := @Buffer;
for Tag := StartTag to StopTag do
begin
if Tag > 0 then
P^ := dNaNBits or (Int64(Tag) shl dNaNTagShift)
else
if Tag < 0 then
P^ := dNaNBits or $8000000000000000 or (Int64(-Tag) shl dNaNTagShift)
else
P^ := dNaNBits or (Int64(ZeroTag) shl dNaNTagShift);
Inc(P);
end;
end;
function MinedSingleArray(Length: Integer): TDynSingleArray;
begin
SetLength(Result, Length);
MineSingleBuffer(Result[0], Length, 0);
end;
function MinedDoubleArray(Length: Integer): TDynDoubleArray;
begin
SetLength(Result, Length);
MineDoubleBuffer(Result[0], Length, 0);
end;
{$ENDIF ~CLR}
function IsSpecialValue(const X: Float): Boolean;
begin
Result := IsNaN(X) or IsInfinite(X);
end;
//=== { EJclNaNSignal } ======================================================
{$IFNDEF CLR}
constructor EJclNaNSignal.Create(ATag: TNaNTag; Dummy: Boolean);
begin
FTag := ATag;
CreateResFmt(@RsNaNSignal, [ATag]);
end;
{$ENDIF ~CLR}
//=== { TJclRational } =======================================================
constructor TJclRational.Create(const Numerator: Integer; const Denominator: Integer);
begin
inherited Create;
Assign(Numerator, Denominator);
end;
constructor TJclRational.Create;
begin
inherited Create;
AssignZero;
end;
constructor TJclRational.Create(const R: Float);
begin
inherited Create;
Assign(R);
end;
procedure TJclRational.Simplify;
var
I: Integer;
begin
if FN < 0 then
begin
FT := -FT;
FN := -FN;
end;
if (FT = 1) or (FN = 1) or (FT = 0) then
Exit;
I := GCD({$IFDEF CLR}Borland.Delphi.{$ENDIF}System.Abs(FT), FN);
FT := FT div I;
FN := FN div I;
end;
procedure TJclRational.Assign(const Numerator: Integer; const Denominator: Integer);
begin
if Denominator = 0 then
{$IFDEF CLR}
raise EJclMathError.Create(RsInvalidRational);
{$ELSE}
raise EJclMathError.CreateRes(@RsInvalidRational);
{$ENDIF CLR}
FT := Numerator;
FN := Denominator;
if FN <> 1 then
Simplify;
end;
procedure TJclRational.Assign(const R: TJclRational);
begin
FT := R.FT;
FN := R.FN;
end;
procedure TJclRational.Assign(const R: Float);
var
T: TJclRational;
Z: Integer;
function CalcFrac(const R: Float; const Level: Integer): TJclRational;
var
I: Float;
Z: Integer;
begin
if IsFloatZero(R) or (Level = 12) then // 0 (if Level = 12 we get an approximation)
Result := TJclRational.Create
else
if FloatsEqual(R, 1.0) then // 1
begin
Result := TJclRational.Create;
Result.AssignOne;
end
else
if IsFloatZero(Frac(R * 1E8)) then // terminating decimal (<8)
Result := TJclRational.Create(Trunc(R * 1E8), 100000000)
else
begin // recursive process
I := 1.0 / R;
Result := CalcFrac(Frac(I), Level + 1);
Z := Trunc(I);
Result.Add(Z);
Result.Reciprocal;
end;
end;
begin
T := CalcFrac(Frac(R), 1);
try
Z := Trunc(R);
T.Add(Z);
Assign(T);
finally
T.Free;
end;
end;
procedure TJclRational.AssignOne;
begin
FT := 1;
FN := 1;
end;
procedure TJclRational.AssignZero;
begin
FT := 0;
FN := 1;
end;
function TJclRational.IsEqual(const Numerator: Integer; const Denominator: Integer): Boolean;
var
R: TJclRational;
begin
R := TJclRational.Create(Numerator, Denominator);
Result := IsEqual(R);
R.Free;
end;
function TJclRational.IsEqual(const R: TJclRational): Boolean;
begin
Result := (FT = R.FT) and (FN = R.FN);
end;
function TJclRational.IsEqual(const R: Float): Boolean;
begin
Result := FloatsEqual(R, GetAsFloat);
end;
function TJclRational.IsOne: Boolean;
begin
Result := (FT = 1) and (FN = 1);
end;
function TJclRational.IsZero: Boolean;
begin
Result := FT = 0;
end;
function TJclRational.Duplicate: TJclRational;
begin
Result := TJclRational.Create(FT, FN);
end;
procedure TJclRational.SetAsFloat(const R: Float);
begin
Assign(R);
end;
procedure TJclRational.SetAsString(const S: string);
var
F: Integer;
begin
F := Pos('/', S);
if F = 0 then
Assign(StrToFloat(S))
else
Assign(StrToInt(Trim(Copy(S,1,F - 1))), StrToInt(Trim(Copy(S, F + 1,Length(s)))));
end;
function TJclRational.GetAsFloat: Float;
begin
Result := FT / FN;
end;
function TJclRational.GetAsString: string;
begin
Result := IntToStr(FT) + '/' + IntToStr(FN);
end;
procedure TJclRational.Add(const R: TJclRational);
begin
FT := FT * R.FN + R.FT * FN;
FN := FN * R.FN;
Simplify;
end;
procedure TJclRational.Add(const V: Integer);
begin
Inc(FT, FN * V);
end;
procedure TJclRational.Add(const V: Float);
begin
Assign(GetAsFloat + V);
end;
procedure TJclRational.Subtract(const V: Float);
begin
Assign(GetAsFloat - V);
end;
procedure TJclRational.Subtract(const R: TJclRational);
begin
FT := FT * R.FN - R.FT * FN;
FN := FN * R.FN;
Simplify;
end;
procedure TJclRational.Subtract(const V: Integer);
begin
Dec(FT, FN * V);
end;
procedure TJclRational.Negate;
begin
FT := -FT;
end;
procedure TJclRational.Abs;
begin
FT := {$IFDEF CLR}Borland.Delphi.{$ENDIF}System.Abs(FT);
FN := {$IFDEF CLR}Borland.Delphi.{$ENDIF}System.Abs(FN);
end;
function TJclRational.Sgn: Integer;
begin
if FT = 0 then
Result := 0
else
begin
if JclMathSgn(FT) = JclMathSgn(FN) then
Result := 1
else
Result := -1;
end;
end;
procedure TJclRational.Divide(const V: Integer);
begin
if V = 0 then
{$IFDEF CLR}
raise EJclMathError.Create(RsDivByZero);
{$ELSE}
raise EJclMathError.CreateRes(@RsDivByZero);
{$ENDIF CLR}
FN := FN * V;
Simplify;
end;
procedure TJclRational.Divide(const R: TJclRational);
begin
if R.FT = 0 then
{$IFDEF CLR}
raise EJclMathError.Create(RsRationalDivByZero);
{$ELSE}
raise EJclMathError.CreateRes(@RsRationalDivByZero);
{$ENDIF CLR}
FT := FT * R.FN;
FN := FN * R.FT;
Simplify;
end;
procedure TJclRational.Divide(const V: Float);
begin
Assign(GetAsFloat / V);
end;
procedure TJclRational.Reciprocal;
begin
if FT = 0 then
{$IFDEF CLR}
raise EJclMathError.Create(RsRationalDivByZero);
{$ELSE}
raise EJclMathError.CreateRes(@RsRationalDivByZero);
{$ENDIF CLR}
SwapOrd(FT, FN);
end;
procedure TJclRational.Multiply(const R: TJclRational);
begin
FT := FT * R.FT;
FN := FN * R.FN;
Simplify;
end;
procedure TJclRational.Multiply(const V: Integer);
begin
FT := FT * V;
Simplify;
end;
procedure TJclRational.Multiply(const V: Float);
begin
Assign(GetAsFloat * V);
end;
procedure TJclRational.Power(const R: TJclRational);
begin
Assign(JclMathPower(GetAsFloat, R.GetAsFloat));
end;
procedure TJclRational.Power(const V: Integer);
var
T, N: Extended;
begin
T := FT;
N := FN;
FT := Round(JclMathPower(T, V));
FN := Round(JclMathPower(N, V));
end;
procedure TJclRational.Power(const V: Float);
begin
Assign(JclMathPower(FT, V) / JclMathPower(FN, V));
end;
procedure TJclRational.Sqrt;
begin
Assign({$IFDEF CLR}Borland.Delphi.{$ENDIF}System.Sqrt(FT / FN));
end;
procedure TJclRational.Sqr;
begin
FT := {$IFDEF CLR}Borland.Delphi.{$ENDIF}System.Sqr(FT);
FN := {$IFDEF CLR}Borland.Delphi.{$ENDIF}System.Sqr(FN);
end;
//=== Checksums ==============================================================
// See also: CountBitsSet in JclLogic (bug fixing etc.) - similar algorithm!
{$IFDEF CLR}
function GetParity(Buffer: TDynByteArray; Len: Integer): Boolean;
{$ELSE}
function GetParity(Buffer: PByte; Len: Integer): Boolean;
{$ENDIF CLR}
const
lu: packed array [0..15] of Byte =
(0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4);
var
b: Byte;
BitsSet: Cardinal;
{$IFDEF CLR}
Index: Cardinal;
{$ENDIF CLR}
begin
BitsSet := 0;
{$IFDEF CLR}
Index := 0;
if Len > Length(Buffer) then
Len := Length(Buffer);
{$ENDIF CLR}
while Len > 0 do
begin
{$IFDEF CLR}
b := Buffer[Index];
{$ELSE}
b := PByte(Buffer)^;
{$ENDIF CLR}
// lower Nibble
Inc(BitsSet, lu[b and $0F]);
// upper Nibble
Inc(BitsSet, lu[b shr 4]);
Dec(Len);
{$IFDEF CLR}
Inc(Index);
{$ELSE}
Inc(PByte(Buffer));
{$ENDIF CLR}
end;
Result := (BitsSet mod 2) = 0;
end;
// CRC
{ TODO : check for the correct polynom and init, exit values }
// CRC 16
{$IFDEF CRCINIT}
var
{$ELSE}
const
{$ENDIF CRCINIT}
// CRC16Polynom = $1021;
Crc16Table: array [0..255] of Word = (
$0000, $1021, $2042, $3063, $4084, $50A5, $60C6, $70E7,
$8108, $9129, $A14A, $B16B, $C18C, $D1AD, $E1CE, $F1EF,
$1231, $0210, $3273, $2252, $52B5, $4294, $72F7, $62D6,
$9339, $8318, $B37B, $A35A, $D3BD, $C39C, $F3FF, $E3DE,
$2462, $3443, $0420, $1401, $64E6, $74C7, $44A4, $5485,
$A56A, $B54B, $8528, $9509, $E5EE, $F5CF, $C5AC, $D58D,
$3653, $2672, $1611, $0630, $76D7, $66F6, $5695, $46B4,
$B75B, $A77A, $9719, $8738, $F7DF, $E7FE, $D79D, $C7BC,
$48C4, $58E5, $6886, $78A7, $0840, $1861, $2802, $3823,
$C9CC, $D9ED, $E98E, $F9AF, $8948, $9969, $A90A, $B92B,
$5AF5, $4AD4, $7AB7, $6A96, $1A71, $0A50, $3A33, $2A12,
$DBFD, $CBDC, $FBBF, $EB9E, $9B79, $8B58, $BB3B, $AB1A,
$6CA6, $7C87, $4CE4, $5CC5, $2C22, $3C03, $0C60, $1C41,
$EDAE, $FD8F, $CDEC, $DDCD, $AD2A, $BD0B, $8D68, $9D49,
$7E97, $6EB6, $5ED5, $4EF4, $3E13, $2E32, $1E51, $0E70,
$FF9F, $EFBE, $DFDD, $CFFC, $BF1B, $AF3A, $9F59, $8F78,
$9188, $81A9, $B1CA, $A1EB, $D10C, $C12D, $F14E, $E16F,
$1080, $00A1, $30C2, $20E3, $5004, $4025, $7046, $6067,
$83B9, $9398, $A3FB, $B3DA, $C33D, $D31C, $E37F, $F35E,
$02B1, $1290, $22F3, $32D2, $4235, $5214, $6277, $7256,
$B5EA, $A5CB, $95A8, $8589, $F56E, $E54F, $D52C, $C50D,
$34E2, $24C3, $14A0, $0481, $7466, $6447, $5424, $4405,
$A7DB, $B7FA, $8799, $97B8, $E75F, $F77E, $C71D, $D73C,
$26D3, $36F2, $0691, $16B0, $6657, $7676, $4615, $5634,
$D94C, $C96D, $F90E, $E92F, $99C8, $89E9, $B98A, $A9AB,
$5844, $4865, $7806, $6827, $18C0, $08E1, $3882, $28A3,
$CB7D, $DB5C, $EB3F, $FB1E, $8BF9, $9BD8, $ABBB, $BB9A,
$4A75, $5A54, $6A37, $7A16, $0AF1, $1AD0, $2AB3, $3A92,
$FD2E, $ED0F, $DD6C, $CD4D, $BDAA, $AD8B, $9DE8, $8DC9,
$7C26, $6C07, $5C64, $4C45, $3CA2, $2C83, $1CE0, $0CC1,
$EF1F, $FF3E, $CF5D, $DF7C, $AF9B, $BFBA, $8FD9, $9FF8,
$6E17, $7E36, $4E55, $5E74, $2E93, $3EB2, $0ED1, $1EF0
);
Crc16Start: Cardinal = $FFFF;
const
Crc16Bits = 16;
Crc16Bytes = 2;
Crc16HighBit = $8000;
NotCrc16HighBit = $7FFF;
function Crc16Corr(Crc: Word; N: Integer): Integer;
var
I: Integer;
// CrcX : Cardinal;
begin
// calculate Syndrome
// CrcX := CrC;
for I := 1 to Crc16Bytes do
// a 16 bit value shr 8 is a Byte, explictit type conversion to Byte adds an ASM instruction
Crc := Crc16Table[Crc shr (CRC16Bits - 8)] xor Word(Crc shl 8);
I := -1;
repeat
Inc(I);
if (Crc and 1) <> 0 then
Crc := ((Crc xor Crc16Table[1]) shr 1) or Crc16HighBit
// Crc16Table[1] = Crc16Polynom
else
Crc := (Crc shr 1) and NotCrc16HighBit;
until (Crc = Crc16HighBit) or (I = (N + Crc16Bytes) * 8);
if Crc <> Crc16HighBit then
Result := -1000 // not correctable
else
// I = No. of single faulty bit
// (high bit first,
// starting from lowest with CRC bits)
Result := I - Crc16Bits;
// Result < 0 faulty CRC-bit
// Result >= 0 No. of faulty data bit
end;
function Crc16_P(X: PJclByteArray; N: Integer; Crc: Word = 0): Word;
var
I: Integer;
begin
Result := Crc16Start;
for I := 0 to N - 1 do // The CRC Bytes are located at the end of the information
// a 16 bit value shr 8 is a Byte, explictit type conversion to Byte adds an ASM instruction
Result := Crc16Table[Result shr (CRC16Bits - 8)] xor Word((Result shl 8)) xor X[I];
for I := 0 to Crc16Bytes - 1 do
begin
// a 16 bit value shr 8 is a Byte, explictit type conversion to Byte adds an ASM instruction
Result := Crc16Table[Result shr (CRC16Bits-8)] xor Word((Result shl 8)) xor (Crc shr (CRC16Bits-8));
Crc := Word(Crc shl 8);
end;
end;
function CheckCrc16_P(X: PJclByteArray; N: Integer; Crc: Word): Integer;
// checks and corrects a single bit in up to 2^15-16 Bit -> 2^12-2 = 4094 Byte
var
I, J: Integer;
C: Byte;
begin
Crc := Crc16_P(X, N, Crc);
if Crc = 0 then
Result := 0 // No CRC-error
else
begin
J := Crc16Corr(Crc, N);
if J < -(Crc16Bytes * 8 + 1) then
Result := -1 // non-correctable error (more than one wrong bit)
else
begin
if J < 0 then
Result := 1 // one faulty Bit in CRC itself
else
begin // Bit J is faulty
I := J and 7; // I <= 7 (faulty Bit in Byte)
C := 1 shl I; // C <= 128
I := J shr 3; // I: Index of faulty Byte
X[N - 1 - I] := X[N - 1 - I] xor C; // correct faulty bit
Result := 1; // Correctable error
end;
end;
end;
end;
function Crc16(const X: array of Byte; N: Integer; Crc: Word = 0): Word;
begin
{$IFDEF CLR}
Result := Crc16_P(X, N, Crc);
{$ELSE}
Result := Crc16_P(@X, N, Crc);
{$ENDIF CLR}
end;
function CheckCrc16(var X: array of Byte; N: Integer; Crc: Word): Integer;
begin
{$IFDEF CLR}
Result := CheckCRC16_P(X, N, CRC);
{$ELSE}
Result := CheckCRC16_P(@X, N, CRC);
{$ENDIF CLR}
end;
function Crc16_A(const X: array of Byte; Crc: Word = 0): Word;
begin
{$IFDEF CLR}
Result := Crc16_P(X, Length(X), Crc);
{$ELSE}
Result := Crc16_P(@X, Length(X), Crc);
{$ENDIF CLR}
end;
function CheckCrc16_A(var X: array of Byte; Crc: Word): Integer;
begin
{$IFDEF CLR}
Result := CheckCrc16_P(X, Length(X), Crc);
{$ELSE}
Result := CheckCrc16_P(@X, Length(X), Crc);
{$ENDIF CLR}
end;
{$IFDEF CRCINIT}
// The CRC Table can be generated like this:
// const Crc16Start0 = 0; !!
function Crc16_Bitwise(X: PJclByteArray; N: Integer; Crc: Word; Polynom: Word): Word;
const
Crc16Start0 = 0; //Generating the table
var
I, J: Integer;
Sr, SrHighBit: Word;
B: Byte;
begin
Sr := Crc16Start0;
SrHighBit := 0;
for I := 0 to N - 1 + Crc16Bytes do
begin
if I >= N then
begin
B := Crc shr (Crc16Bits - 8);
Crc := Crc shl 8;
end
else
B := X[I];
for J := 1 to 8 do
begin
if SrHighBit <> 0 then
Sr := Sr xor Polynom;
SrHighBit := Sr and Crc16HighBit;
Sr := (Word (Sr shl 1)) or ((B shr 7) and 1);
B := Byte(B shl 1);
end;
end;
if SrHighBit <> 0 then
Sr := Sr xor Polynom;
Result := Sr;
end;
procedure InitCrc16(Polynom, Start: Word);
var
X: array [0..0] of Byte;
I: Integer;
begin
for I := 0 to 255 do
begin
X[0] := I;
Crc16Table[I] := Crc16_Bitwise(@X, 1, 0, Polynom); { only with crcstart=0 !!!!}
end;
Crc16Start := Start;
end;
{$ENDIF CRCINIT}
// CRC 32
{$IFDEF CRCINIT}
var
{$ELSE}
const
{$ENDIF CRCINIT}
// CRC32Polynom = $04C11DB7;
Crc32Table: array [0..255] of Cardinal = (
$00000000, $04C11DB7, $09823B6E, $0D4326D9, $130476DC, $17C56B6B, $1A864DB2, $1E475005,
$2608EDB8, $22C9F00F, $2F8AD6D6, $2B4BCB61, $350C9B64, $31CD86D3, $3C8EA00A, $384FBDBD,
$4C11DB70, $48D0C6C7, $4593E01E, $4152FDA9, $5F15ADAC, $5BD4B01B, $569796C2, $52568B75,
$6A1936C8, $6ED82B7F, $639B0DA6, $675A1011, $791D4014, $7DDC5DA3, $709F7B7A, $745E66CD,
$9823B6E0, $9CE2AB57, $91A18D8E, $95609039, $8B27C03C, $8FE6DD8B, $82A5FB52, $8664E6E5,
$BE2B5B58, $BAEA46EF, $B7A96036, $B3687D81, $AD2F2D84, $A9EE3033, $A4AD16EA, $A06C0B5D,
$D4326D90, $D0F37027, $DDB056FE, $D9714B49, $C7361B4C, $C3F706FB, $CEB42022, $CA753D95,
$F23A8028, $F6FB9D9F, $FBB8BB46, $FF79A6F1, $E13EF6F4, $E5FFEB43, $E8BCCD9A, $EC7DD02D,
$34867077, $30476DC0, $3D044B19, $39C556AE, $278206AB, $23431B1C, $2E003DC5, $2AC12072,
$128E9DCF, $164F8078, $1B0CA6A1, $1FCDBB16, $018AEB13, $054BF6A4, $0808D07D, $0CC9CDCA,
$7897AB07, $7C56B6B0, $71159069, $75D48DDE, $6B93DDDB, $6F52C06C, $6211E6B5, $66D0FB02,
$5E9F46BF, $5A5E5B08, $571D7DD1, $53DC6066, $4D9B3063, $495A2DD4, $44190B0D, $40D816BA,
$ACA5C697, $A864DB20, $A527FDF9, $A1E6E04E, $BFA1B04B, $BB60ADFC, $B6238B25, $B2E29692,
$8AAD2B2F, $8E6C3698, $832F1041, $87EE0DF6, $99A95DF3, $9D684044, $902B669D, $94EA7B2A,
$E0B41DE7, $E4750050, $E9362689, $EDF73B3E, $F3B06B3B, $F771768C, $FA325055, $FEF34DE2,
$C6BCF05F, $C27DEDE8, $CF3ECB31, $CBFFD686, $D5B88683, $D1799B34, $DC3ABDED, $D8FBA05A,
$690CE0EE, $6DCDFD59, $608EDB80, $644FC637, $7A089632, $7EC98B85, $738AAD5C, $774BB0EB,
$4F040D56, $4BC510E1, $46863638, $42472B8F, $5C007B8A, $58C1663D, $558240E4, $51435D53,
$251D3B9E, $21DC2629, $2C9F00F0, $285E1D47, $36194D42, $32D850F5, $3F9B762C, $3B5A6B9B,
$0315D626, $07D4CB91, $0A97ED48, $0E56F0FF, $1011A0FA, $14D0BD4D, $19939B94, $1D528623,
$F12F560E, $F5EE4BB9, $F8AD6D60, $FC6C70D7, $E22B20D2, $E6EA3D65, $EBA91BBC, $EF68060B,
$D727BBB6, $D3E6A601, $DEA580D8, $DA649D6F, $C423CD6A, $C0E2D0DD, $CDA1F604, $C960EBB3,
$BD3E8D7E, $B9FF90C9, $B4BCB610, $B07DABA7, $AE3AFBA2, $AAFBE615, $A7B8C0CC, $A379DD7B,
$9B3660C6, $9FF77D71, $92B45BA8, $9675461F, $8832161A, $8CF30BAD, $81B02D74, $857130C3,
$5D8A9099, $594B8D2E, $5408ABF7, $50C9B640, $4E8EE645, $4A4FFBF2, $470CDD2B, $43CDC09C,
$7B827D21, $7F436096, $7200464F, $76C15BF8, $68860BFD, $6C47164A, $61043093, $65C52D24,
$119B4BE9, $155A565E, $18197087, $1CD86D30, $029F3D35, $065E2082, $0B1D065B, $0FDC1BEC,
$3793A651, $3352BBE6, $3E119D3F, $3AD08088, $2497D08D, $2056CD3A, $2D15EBE3, $29D4F654,
$C5A92679, $C1683BCE, $CC2B1D17, $C8EA00A0, $D6AD50A5, $D26C4D12, $DF2F6BCB, $DBEE767C,
$E3A1CBC1, $E760D676, $EA23F0AF, $EEE2ED18, $F0A5BD1D, $F464A0AA, $F9278673, $FDE69BC4,
$89B8FD09, $8D79E0BE, $803AC667, $84FBDBD0, $9ABC8BD5, $9E7D9662, $933EB0BB, $97FFAD0C,
$AFB010B1, $AB710D06, $A6322BDF, $A2F33668, $BCB4666D, $B8757BDA, $B5365D03, $B1F740B4
);
Crc32Start: Cardinal = $FFFFFFFF;
const
Crc32Bits = 32;
Crc32Bytes = 4;
Crc32HighBit = $80000000;
NotCrc32HighBit = $7FFFFFFF;
function Crc32Corr(Crc: Cardinal; N: Integer): Integer;
var
I: Integer;
begin
// calculate Syndrome
for I := 1 to Crc32Bytes do
Crc := Crc32Table[Crc shr (CRC32Bits - 8)] xor (Crc shl 8);
I := -1;
repeat
Inc(I);
if (Crc and 1) <> 0 then
Crc := ((Crc xor Crc32Table[1]) shr 1) or Crc32HighBit
// Crc32Table[1] = Crc32Polynom
else
Crc := (Crc shr 1) and NotCrc32HighBit;
until (Crc = Crc32HighBit) or (I = (N + Crc32Bytes) * 8);
if Crc <> Crc32HighBit then
Result := -1000 // not correctable
else
// I = No. of single faulty bit
// (high bit first,
// starting from lowest with CRC bits)
Result := I - Crc32Bits;
// Result < 0 faulty CRC-bit
// Result >= 0 No. of faulty data bit
end;
function Crc32_P(X: PJclByteArray; N: Integer; Crc: Cardinal = 0): Cardinal;
var
I: Integer;
begin
Result := Crc32Start;
for I := 0 to N - 1 do // The CRC Bytes are located at the end of the information
begin
// a 32 bit value shr 24 is a Byte, explictit type conversion to Byte adds an ASM instruction
Result := Crc32Table[Result shr (CRC32Bits-8)] xor (Result shl 8) xor X[I];
end;
for I := 0 to Crc32Bytes - 1 do
begin
// a 32 bit value shr 24 is a Byte, explictit type conversion to Byte adds an ASM instruction
Result := Crc32Table[Result shr (CRC32Bits-8)] xor (Result shl 8) xor (Crc shr (CRC32Bits-8));
Crc := Crc shl 8;
end;
end;
function CheckCrc32_P(X: PJclByteArray; N: Integer; Crc: Cardinal): Integer;
// checks and corrects a single bit in up to 2^31-32 Bit -> 2^28-4 = 268435452 Byte
var
I, J: Integer;
C: Byte;
begin
Crc := Crc32_P(X, N, Crc);
if Crc = 0 then
Result := 0 // No CRC-error
else
begin
J := Crc32Corr(Crc, N);
if J < -(Crc32Bytes * 8 + 1) then
Result := -1 // non-correctable error (more than one wrong bit)
else
begin
if J < 0 then
Result := 1 // one faulty Bit in CRC itself
else
begin // Bit J is faulty
I := J and 7; // I <= 7 (faulty Bit in Byte)
C := 1 shl I; // C <= 128
I := J shr 3; // I: Index of faulty Byte
X[N - 1 - I] := X[N - 1 - I] xor C; // correct faulty bit
Result := 1; // Correctable error
end;
end;
end;
end;
function Crc32(const X: array of Byte; N: Integer; Crc: Cardinal = 0): Cardinal;
begin
{$IFDEF CLR}
Result := Crc32_P(X, N, Crc);
{$ELSE}
Result := Crc32_P(@X, N, Crc);
{$ENDIF CLR}
end;
function CheckCrc32(var X: array of Byte; N: Integer; Crc: Cardinal): Integer;
begin
{$IFDEF CLR}
Result := CheckCRC32_P(X, N, CRC);
{$ELSE}
Result := CheckCRC32_P(@X, N, CRC);
{$ENDIF CLR}
end;
function Crc32_A(const X: array of Byte; Crc: Cardinal = 0): Cardinal;
begin
{$IFDEF CLR}
Result := Crc32_P(X, Length(X), Crc);
{$ELSE}
Result := Crc32_P(@X, Length(X), Crc);
{$ENDIF CLR}
end;
function CheckCrc32_A(var X: array of Byte; Crc: Cardinal): Integer;
begin
{$IFDEF CLR}
Result := CheckCrc32_P(X, Length(X), Crc);
{$ELSE}
Result := CheckCrc32_P(@X, Length(X), Crc);
{$ENDIF CLR}
end;
{$IFDEF CRCINIT}
// The CRC Table can be generated like this:
// const Crc32Start0 = 0; !!
function Crc32_Bitwise(X: PJclByteArray; N: Integer; Crc: Cardinal; Polynom: Cardinal) : Cardinal;
const
Crc32Start0 = 0; //Generating the table
var
I, J: Integer;
Sr, SrHighBit: Cardinal;
B: Byte;
begin
Sr := Crc32Start0;
SrHighBit := 0;
for I := 0 to N - 1 + Crc32Bytes do
begin
if I >= N then
begin
B := Crc shr (Crc32Bits - 8);
Crc := Crc shl 8;
end
else
B := X[I];
for J := 1 to 8 do
begin
if SrHighBit <> 0 then
Sr := Sr xor Polynom;
SrHighBit := Sr and Crc32HighBit;
Sr := (Sr shl 1) or ((B shr 7) and 1);
B := Byte(B shl 1);
end
end;
if SrHighBit <> 0 then
Sr := Sr xor Polynom;
Result := Sr;
end;
procedure InitCrc32(Polynom, Start: Cardinal);
var
X: array [0..0] of Byte;
I: Integer;
begin
for I := 0 to 255 do
begin
X[0] := I;
Crc32Table[I] := Crc32_Bitwise(@X, 1, 0, Polynom);
end;
Crc32Start := Start;
end;
{$ENDIF CRCINIT}
//=== complex numbers support ================================================
const
RectOne: TRectComplex = (Re: 1.0; Im: 0.0);
RectZero: TRectComplex = (Re: 0.0; Im: 0.0);
//RectInfinity: TRectComplex = (Re: Infinity; Im: Infinity);
function RectComplex(const Re: Float; const Im: Float = 0): TRectComplex;
begin
Result.Re := Re;
Result.Im := Im;
end;
function RectComplex(const Z: TPolarComplex): TRectComplex;
var
ASin, ACos: Float;
begin
SinCos(Z.Angle, ASin, ACos);
Result.Re := Z.Radius * ACos;
Result.Im := Z.Radius * ASin;
end;
function PolarComplex(const Radius: Float; const Angle: Float = 0): TPolarComplex;
begin
Result.Radius := Radius;
Result.Angle := Angle;
end;
function PolarComplex(const Z: TRectComplex): TPolarComplex;
begin
Result.Radius := Sqrt(Sqr(Z.Re) + Sqr(Z.Im));
Result.Angle := ArcTan2(Z.Im, Z.Re);
end;
function Equal(const Z1, Z2: TRectComplex): Boolean;
begin
Result := (Z1.Re = Z2.Re) and (Z1.Im = Z2.Im);
end;
function Equal(const Z1, Z2: TPolarComplex): Boolean;
begin
Result := (Z1.Radius = Z2.Radius) and IsFloatZero(NormalizeAngle(Z1.Angle - Z2.Angle));
end;
function IsZero(const Z: TRectComplex): Boolean;
begin
Result := IsFloatZero(Z.Re) and IsFloatZero(Z.Im);
end;
function IsZero(const Z: TPolarComplex): Boolean;
begin
Result := IsFloatZero(Z.Radius);
end;
function IsInfinite(const Z: TRectComplex): Boolean;
begin
Result := IsInfinite(Z.Re) or IsInfinite(Z.Im);
end;
function IsInfinite(const Z: TPolarComplex): Boolean;
begin
Result := IsInfinite(Z.Radius);
end;
function Norm(const Z: TRectComplex): Float;
begin
Result := Sqrt(Sqr(Z.Re) + Sqr(Z.Im));
end;
function Norm(const Z: TPolarComplex): Float;
begin
Result := Z.Radius;
end;
function AbsSqr(const Z: TRectComplex): Float;
begin
Result := Sqr(Z.Re) + Sqr(Z.Im);
end;
function AbsSqr(const Z: TPolarComplex): Float;
begin
Result := Sqr(Z.Radius);
end;
function Conjugate(const Z: TRectComplex): TRectComplex; overload;
begin
Result.Re := Z.Re;
Result.Im := -Z.Im;
end;
function Conjugate(const Z: TPolarComplex): TPolarComplex; overload;
begin
Result.Radius := Z.Radius;
Result.Angle := -Z.Angle;
end;
function Inv(const Z: TRectComplex): TRectComplex;
var
Denom: Float;
begin
Denom := Sqr(Z.Re) + Sqr(Z.Im);
Result.Re := Z.Re / Denom;
Result.Im := -Z.Im / Denom;
end;
function Inv(const Z: TPolarComplex): TPolarComplex;
begin
Result.Radius := 1 / Z.Radius;
Result.Angle := - Z.Angle;
end;
function Neg(const Z: TRectComplex): TRectComplex; overload;
begin
Result.Re := -Z.Re;
Result.Im := -Z.Im;
end;
function Neg(const Z: TPolarComplex): TPolarComplex; overload;
begin
Result.Radius := Z.Radius;
Result.Angle := NormalizeAngle(Z.Angle + Pi);
end;
function Sum(const Z1, Z2: TRectComplex): TRectComplex;
begin
Result.Re := Z1.Re + Z2.Re;
Result.Im := Z1.Im + Z2.Im;
end;
function Sum(const Z: array of TRectComplex): TRectComplex;
var
I: Integer;
begin
Result := RectZero;
for I := Low(Z) to High(Z) do
begin
Result.Re := Result.Re + Z[I].Re;
Result.Im := Result.Im + Z[I].Im;
end;
end;
function Diff(const Z1, Z2: TRectComplex): TRectComplex;
begin
Result.Re := Z1.Re - Z2.Re;
Result.Im := Z1.Im - Z2.Im;
end;
function Product(const Z1, Z2: TRectComplex): TRectComplex;
begin
Result.Re := Z1.Re * Z2.Re - Z1.Im * Z2.Im;
Result.Im := Z1.Re * Z2.Im + Z1.Im * Z2.Re;
end;
function Product(const Z1, Z2: TPolarComplex): TPolarComplex;
begin
Result.Radius := Z1.Radius * Z2.Radius;
Result.Angle := Z1.Angle + Z2.Angle;
end;
function Quotient(const Z1, Z2: TRectComplex): TRectComplex;
var
Denom: Float;
begin
Denom := Sqr(Z2.Re) + Sqr(Z2.Im);
Result.Re := (Z1.Re * Z2.Re + Z1.Im * Z2.Im) / Denom;
Result.Im := (Z1.Im * Z2.Re - Z1.Re * Z2.Im) / Denom;
end;
function Ln(const Z: TPolarComplex): TRectComplex;
begin
Result.Re := {$IFDEF CLR}Borland.Delphi.{$ENDIF}System.Ln(Z.Radius);
Result.Im := NormalizeAngle(Z.Angle);
end;
function Exp(const Z: TRectComplex): TPolarComplex;
begin
Result.Radius := {$IFDEF CLR}Borland.Delphi.{$ENDIF}System.Exp(Z.Re);
Result.Angle := Z.Im;
end;
function Power(const Z: TPolarComplex; const Exponent: Float): TPolarComplex;
begin
Result.Radius := Power(Z.Radius, Exponent);
Result.Angle := NormalizeAngle(Exponent * Z.Angle);
end;
function Power(const Z: TPolarComplex; const Exponent: TRectComplex): TPolarComplex;
begin
Result := Exp(Product(Exponent, Ln(Z)));
end;
function PowerInt(const Z: TPolarComplex; const Exponent: Integer): TPolarComplex;
begin
Result.Radius := PowerInt(Z.Radius, Exponent);
Result.Angle := NormalizeAngle(Exponent * Z.Angle);
end;
function Root(const Z: TPolarComplex; const K, N: Cardinal): TPolarComplex;
begin
Result.Radius := Power(Z.Radius, 1.0 / N);
Result.Angle := NormalizeAngle((Z.Angle + K * TwoPi) / N);
end;
//=== complex trigonometric functions ========================================
function Cos(const Z: TRectComplex): TRectComplex;
var
ACos, ASin: Float;
begin
SinCos(Z.Re, ASin, ACos);
Result.Re := ACos * CosH(Z.Im);
Result.Im := -ASin * SinH(Z.Im);
end;
function Sin(const Z: TRectComplex): TRectComplex;
var
ACos, ASin: Float;
begin
SinCos(Z.Re, ASin, ACos);
Result.Re := ASin * CosH(Z.Im);
Result.Im := ACos * SinH(Z.Im);
end;
function Tan(const Z: TRectComplex): TRectComplex;
var
Denom: Float;
ACos, ASin: Float;
begin
SinCos(2.0 * Z.Re, ASin, ACos);
Denom := ACos + CosH(2.0 * Z.Im);
Result.Re := ASin / Denom;
Result.Im := SinH(2.0 * Z.Im) / Denom;
end;
function Cot(const Z: TRectComplex): TRectComplex;
var
Denom: Float;
ACos, ASin: Float;
begin
SinCos(2.0 * Z.Re, ASin, ACos);
Denom := CosH(2.0 * Z.Im) - ACos;
Result.Re := ASin / Denom;
Result.Im := -SinH(2.0 * Z.Im) / Denom;
end;
function Sec(const Z: TRectComplex): TRectComplex;
begin
Result := Quotient(RectOne, Cos(Z));
end;
function Csc(const Z: TRectComplex): TRectComplex;
begin
Result := Quotient(RectOne, Sin(Z));
end;
//=== complex hyperbolic functions ===========================================
function CosH(const Z: TRectComplex): TRectComplex;
var
ACos, ASin: Float;
begin
SinCos(Z.Im, ASin, ACos);
Result.Re := CosH(Z.Re) * ACos;
Result.Im := SinH(Z.Re) * ASin;
end;
function SinH(const Z: TRectComplex): TRectComplex;
var
ACos, ASin: Float;
begin
SinCos(Z.Im, ASin, ACos);
Result.Re := SinH(Z.Re) * ACos;
Result.Im := CosH(Z.Re) * ASin;
end;
function TanH(const Z: TRectComplex): TRectComplex;
var
Denom: Float;
ACos, ASin: Float;
begin
SinCos(2.0 * Z.Im, ASin, ACos);
Denom := CosH(2.0 * Z.Re) + ACos;
Result.Re := SinH(2.0 * Z.Re) / Denom;
Result.Im := ASin / Denom;
end;
function CotH(const Z: TRectComplex): TRectComplex;
var
Denom: Float;
ACos, ASin: Float;
begin
SinCos(2.0 * Z.Im, ASin, ACos);
Denom := CosH(2.0 * Z.Re) - ACos;
Result.Re := SinH(2.0 * Z.Re) / Denom;
Result.Im := -ASin / Denom;
end;
function SecH(const Z: TRectComplex): TRectComplex;
begin
Result := Quotient(RectOne, CosH(Z));
end;
function CscH(const Z: TRectComplex): TRectComplex;
begin
Result := Quotient(RectOne, SinH(Z));
end;
{$IFDEF SUPPORTS_CLASS_OPERATORS}
class operator TRectComplex.Implicit(const Value: Float): TRectComplex;
begin
Result.Re := Value;
Result.Im := 0;
end;
class operator TRectComplex.Implicit(const Value: Integer): TRectComplex;
begin
Result.Re := Value;
end;
class operator TRectComplex.Implicit(const Value: Int64): TRectComplex;
begin
Result.Re := Value;
end;
class operator TRectComplex.Implicit(const Z: TPolarComplex): TRectComplex;
var
ASin, ACos: Float;
begin
SinCos(Z.Angle, ASin, ACos);
Result.Re := Z.Radius * ACos;
Result.Im := Z.Radius * ASin;
end;
class operator TRectComplex.Equal(const Z1, Z2: TRectComplex): Boolean;
begin
Result := (Z1.Re = Z2.Re) and (Z1.Im = Z2.Im);
end;
class operator TRectComplex.NotEqual(const Z1, Z2: TRectComplex): Boolean;
begin
Result := not Equal(Z1, Z2);
end;
class operator TRectComplex.Add(const Z1, Z2: TRectComplex): TRectComplex;
begin
Result.Re := Z1.Re + Z2.Re;
Result.Im := Z1.Im + Z2.Im;
end;
class operator TRectComplex.Subtract(const Z1, Z2: TRectComplex): TRectComplex;
begin
Result.Re := Z1.Re - Z2.Re;
Result.Im := Z1.Im - Z2.Im;
end;
class operator TRectComplex.Multiply(const Z1, Z2: TRectComplex): TRectComplex;
begin
Result.Re := Z1.Re * Z2.Re - Z1.Im * Z2.Im;
Result.Im := Z1.Re * Z2.Im + Z1.Im * Z2.Re;
end;
class operator TRectComplex.Divide(const Z1, Z2: TRectComplex): TRectComplex;
var
Denom: Float;
begin
Denom := Sqr(Z2.Re) + Sqr(Z2.Im);
Result.Re := (Z1.Re * Z2.Re + Z1.Im * Z2.Im) / Denom;
Result.Im := (Z1.Im * Z2.Re - Z1.Re * Z2.Im) / Denom;
end;
class operator TRectComplex.Negative(const Z: TRectComplex): TRectComplex;
begin
Result.Re := -Z.Re;
Result.Im := -Z.Im;
end;
class function TRectComplex.Exp(const Z: TRectComplex): TPolarComplex;
begin
Result.Radius := {$IFDEF CLR}Borland.Delphi.{$ENDIF}System.Exp(Z.Re);
Result.Angle := Z.Im;
end;
{$ENDIF SUPPORTS_CLASS_OPERATORS}
{$IFDEF UNITVERSIONING}
initialization
RegisterUnitVersion(HInstance, UnitVersioning);
finalization
UnregisterUnitVersion(HInstance);
{$ENDIF UNITVERSIONING}
end.