1 {=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
3 KKKKK KKKKK OOOOOOOOO LLLLL
4 KKKKK KKKKK OOOOOOOOOOOOO LLLLL
5 KKKKK KKKKK OOOOO OOOOO LLLLL
6 KKKKK KKKKK OOOOO OOOOO LLLLL
7 KKKKKKKKKK OOOOO OOOOO LLLLL
8 KKKKK KKKKK OOOOO OOOOO LLLLL
9 KKKKK KKKKK OOOOO OOOOO LLLLL
10 KKKKK KKKKK OOOOOOOOOOOOO LLLLLLLLLLLLL
11 KKKKK KKKKK OOOOOOOOO LLLLLLLLLLLLL
13 Key Objects Library (C) 2000 by Kladov Vladimir.
15 mailto: bonanzas@xcl.cjb.net
16 Home: http://kol.nm.ru
20 =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-}
22 This code is grabbed from standard math.pas unit,
23 provided by Borland Delphi. This unit is for working with
24 engineering (mathematical) functions. The main difference
25 is that err unit specially designed to handle exceptions
26 for KOL is used instead of SysUtils. This allows to make
27 size of the executable smaller for about 5K. though this
28 value is insignificant for project made with VCL, it can
29 be more than 15% of executable file size made with KOL.
32 {*******************************************************}
34 { Borland Delphi Runtime Library }
37 { Copyright (C) 1996,99 Inprise Corporation }
39 {*******************************************************}
43 { This unit contains high-performance arithmetic, trigonometric, logorithmic,
44 statistical and financial calculation routines which supplement the math
45 routines that are part of the Delphi language or System unit. }
55 const { Ranges of the IEEE floating point types, including denormals }
60 MinExtended
= 3.4e-4932;
61 MaxExtended
= 1.1e+4932;
62 MinComp
= -9.223372036854775807e+18;
63 MaxComp
= 9.223372036854775807e+18;
65 {-----------------------------------------------------------------------
68 1) P.J. Plauger, "The Standard C Library", Prentice-Hall, 1992, Ch. 7.
69 2) W.J. Cody, Jr., and W. Waite, "Software Manual For the Elementary
70 Functions", Prentice-Hall, 1980.
71 3) Namir Shammas, "C/C++ Mathematical Algorithms for Scientists and Engineers",
72 McGraw-Hill, 1995, Ch 8.
73 4) H.T. Lau, "A Numerical Library in C for Scientists and Engineers",
74 CRC Press, 1994, Ch. 6.
75 5) "Pentium(tm) Processor User's Manual, Volume 3: Architecture
76 and Programming Manual", Intel, 1994
78 All angle parameters and results of trig functions are in radians.
80 Most of the following trig and log routines map directly to Intel 80387 FPU
81 floating point machine instructions. Input domains, output ranges, and
82 error handling are determined largely by the FPU hardware.
83 Routines coded in assembler favor the Pentium FPU pipeline architecture.
84 -----------------------------------------------------------------------}
86 function EAbs( D
: Double ): Double;
87 function EMax( const Values
: array of Double ): Double;
88 function EMin( const Values
: array of Double ): Double;
89 function iMax( const Values
: array of Integer ): Integer;
90 function iMin( const Values
: array of Integer ): Integer;
92 { Trigonometric functions }
93 function ArcCos(X
: Extended
): Extended
; { IN: |X| <= 1 OUT: [0..PI] radians }
94 function ArcSin(X
: Extended
): Extended
; { IN: |X| <= 1 OUT: [-PI/2..PI/2] radians }
96 { ArcTan2 calculates ArcTan(Y/X), and returns an angle in the correct quadrant.
97 IN: |Y| < 2^64, |X| < 2^64, X <> 0 OUT: [-PI..PI] radians }
98 function ArcTan2(Y
, X
: Extended
): Extended
;
100 { SinCos is 2x faster than calling Sin and Cos separately for the same angle }
101 procedure SinCos(Theta
: Extended
; var Sin
, Cos
: Extended
) register;
102 function Tan(X
: Extended
): Extended
;
103 function Cotan(X
: Extended
): Extended
; { 1 / tan(X), X <> 0 }
104 function Hypot(X
, Y
: Extended
): Extended
; { Sqrt(X**2 + Y**2) }
106 { Angle unit conversion routines }
107 function DegToRad(Degrees
: Extended
): Extended
; { Radians := Degrees * PI / 180}
108 function RadToDeg(Radians
: Extended
): Extended
; { Degrees := Radians * 180 / PI }
109 function GradToRad(Grads
: Extended
): Extended
; { Radians := Grads * PI / 200 }
110 function RadToGrad(Radians
: Extended
): Extended
; { Grads := Radians * 200 / PI }
111 function CycleToRad(Cycles
: Extended
): Extended
; { Radians := Cycles * 2PI }
112 function RadToCycle(Radians
: Extended
): Extended
;{ Cycles := Radians / 2PI }
114 { Hyperbolic functions and inverses }
115 function Cosh(X
: Extended
): Extended
;
116 function Sinh(X
: Extended
): Extended
;
117 function Tanh(X
: Extended
): Extended
;
118 function ArcCosh(X
: Extended
): Extended
; { IN: X >= 1 }
119 function ArcSinh(X
: Extended
): Extended
;
120 function ArcTanh(X
: Extended
): Extended
; { IN: |X| <= 1 }
122 { Logorithmic functions }
123 function LnXP1(X
: Extended
): Extended
; { Ln(X + 1), accurate for X near zero }
124 function Log10(X
: Extended
): Extended
; { Log base 10 of X}
125 function Log2(X
: Extended
): Extended
; { Log base 2 of X }
126 function LogN(Base
, X
: Extended
): Extended
; { Log base N of X }
128 { Exponential functions }
130 { IntPower: Raise base to an integral power. Fast. }
131 //function IntPower(Base: Extended; Exponent: Integer): Extended register;
132 // -- already defined in kol.pas
134 { Power: Raise base to any power.
135 For fractional exponents, or |exponents| > MaxInt, base must be > 0. }
136 function Power(Base
, Exponent
: Extended
): Extended
;
139 { Miscellaneous Routines }
141 { Frexp: Separates the mantissa and exponent of X. }
142 procedure Frexp(X
: Extended
; var Mantissa
: Extended
; var Exponent
: Integer) register;
144 { Ldexp: returns X*2**P }
145 function Ldexp(X
: Extended
; P
: Integer): Extended
register;
147 { Ceil: Smallest integer >= X, |X| < MaxInt }
148 function Ceil(X
: Extended
):Integer;
150 { Floor: Largest integer <= X, |X| < MaxInt }
151 function Floor(X
: Extended
): Integer;
153 { Poly: Evaluates a uniform polynomial of one variable at value X.
154 The coefficients are ordered in increasing powers of X:
155 Coefficients[0] + Coefficients[1]*X + ... + Coefficients[N]*(X**N) }
156 function Poly(X
: Extended
; const Coefficients
: array of Double): Extended
;
158 {-----------------------------------------------------------------------
159 Statistical functions.
161 Common commercial spreadsheet macro names for these statistical and
162 financial functions are given in the comments preceding each function.
163 -----------------------------------------------------------------------}
165 { Mean: Arithmetic average of values. (AVG): SUM / N }
166 function Mean(const Data
: array of Double): Extended
;
168 { Sum: Sum of values. (SUM) }
169 function Sum(const Data
: array of Double): Extended
register;
170 function SumInt(const Data
: array of Integer): Integer register;
171 function SumOfSquares(const Data
: array of Double): Extended
;
172 procedure SumsAndSquares(const Data
: array of Double;
173 var Sum
, SumOfSquares
: Extended
) register;
175 { MinValue: Returns the smallest signed value in the data array (MIN) }
176 function MinValue(const Data
: array of Double): Double;
177 function MinIntValue(const Data
: array of Integer): Integer;
179 function Min(A
,B
: Integer): Integer;
182 function Min(A
,B
: I64
): I64
; overload
;
183 function Min(A
,B
: Single): Single; overload
;
184 function Min(A
,B
: Double): Double; overload
;
185 function Min(A
,B
: Extended
): Extended
; overload
;
188 { MaxValue: Returns the largest signed value in the data array (MAX) }
189 function MaxValue(const Data
: array of Double): Double;
190 function MaxIntValue(const Data
: array of Integer): Integer;
192 function Max(A
,B
: Integer): Integer;
195 function Max(A
,B
: I64
): I64
; overload
;
196 function Max(A
,B
: Single): Single; overload
;
197 function Max(A
,B
: Double): Double; overload
;
198 function Max(A
,B
: Extended
): Extended
; overload
;
201 { Standard Deviation (STD): Sqrt(Variance). aka Sample Standard Deviation }
202 function StdDev(const Data
: array of Double): Extended
;
204 { MeanAndStdDev calculates Mean and StdDev in one call. }
205 procedure MeanAndStdDev(const Data
: array of Double; var Mean
, StdDev
: Extended
);
207 { Population Standard Deviation (STDP): Sqrt(PopnVariance).
208 Used in some business and financial calculations. }
209 function PopnStdDev(const Data
: array of Double): Extended
;
211 { Variance (VARS): TotalVariance / (N-1). aka Sample Variance }
212 function Variance(const Data
: array of Double): Extended
;
214 { Population Variance (VAR or VARP): TotalVariance/ N }
215 function PopnVariance(const Data
: array of Double): Extended
;
217 { Total Variance: SUM(i=1,N)[(X(i) - Mean)**2] }
218 function TotalVariance(const Data
: array of Double): Extended
;
220 { Norm: The Euclidean L2-norm. Sqrt(SumOfSquares) }
221 function Norm(const Data
: array of Double): Extended
;
223 { MomentSkewKurtosis: Calculates the core factors of statistical analysis:
224 the first four moments plus the coefficients of skewness and kurtosis.
225 M1 is the Mean. M2 is the Variance.
226 Skew reflects symmetry of distribution: M3 / (M2**(3/2))
227 Kurtosis reflects flatness of distribution: M4 / Sqr(M2) }
228 procedure MomentSkewKurtosis(const Data
: array of Double;
229 var M1
, M2
, M3
, M4
, Skew
, Kurtosis
: Extended
);
231 { RandG produces random numbers with Gaussian distribution about the mean.
232 Useful for simulating data with sampling errors. }
233 function RandG(Mean
, StdDev
: Extended
): Extended
;
235 {-----------------------------------------------------------------------
236 Financial functions. Standard set from Quattro Pro.
238 Parameter conventions:
240 From the point of view of A, amounts received by A are positive and
241 amounts disbursed by A are negative (e.g. a borrower's loan repayments
242 are regarded by the borrower as negative).
244 Interest rates are per payment period. 11% annual percentage rate on a
245 loan with 12 payments per year would be (11 / 100) / 12 = 0.00916667
247 -----------------------------------------------------------------------}
250 TPaymentTime
= (ptEndOfPeriod
, ptStartOfPeriod
);
252 { Double Declining Balance (DDB) }
253 function DoubleDecliningBalance(Cost
, Salvage
: Extended
;
254 Life
, Period
: Integer): Extended
;
256 { Future Value (FVAL) }
257 function FutureValue(Rate
: Extended
; NPeriods
: Integer; Payment
, PresentValue
:
258 Extended
; PaymentTime
: TPaymentTime
): Extended
;
260 { Interest Payment (IPAYMT) }
261 function InterestPayment(Rate
: Extended
; Period
, NPeriods
: Integer; PresentValue
,
262 FutureValue
: Extended
; PaymentTime
: TPaymentTime
): Extended
;
264 { Interest Rate (IRATE) }
265 function InterestRate(NPeriods
: Integer;
266 Payment
, PresentValue
, FutureValue
: Extended
; PaymentTime
: TPaymentTime
): Extended
;
268 { Internal Rate of Return. (IRR) Needs array of cash flows. }
269 function InternalRateOfReturn(Guess
: Extended
;
270 const CashFlows
: array of Double): Extended
;
272 { Number of Periods (NPER) }
273 function NumberOfPeriods(Rate
, Payment
, PresentValue
, FutureValue
: Extended
;
274 PaymentTime
: TPaymentTime
): Extended
;
276 { Net Present Value. (NPV) Needs array of cash flows. }
277 function NetPresentValue(Rate
: Extended
; const CashFlows
: array of Double;
278 PaymentTime
: TPaymentTime
): Extended
;
281 function Payment(Rate
: Extended
; NPeriods
: Integer;
282 PresentValue
, FutureValue
: Extended
; PaymentTime
: TPaymentTime
): Extended
;
284 { Period Payment (PPAYMT) }
285 function PeriodPayment(Rate
: Extended
; Period
, NPeriods
: Integer;
286 PresentValue
, FutureValue
: Extended
; PaymentTime
: TPaymentTime
): Extended
;
288 { Present Value (PVAL) }
289 function PresentValue(Rate
: Extended
; NPeriods
: Integer;
290 Payment
, FutureValue
: Extended
; PaymentTime
: TPaymentTime
): Extended
;
292 { Straight Line depreciation (SLN) }
293 function SLNDepreciation(Cost
, Salvage
: Extended
; Life
: Integer): Extended
;
295 { Sum-of-Years-Digits depreciation (SYD) }
296 function SYDDepreciation(Cost
, Salvage
: Extended
; Life
, Period
: Integer): Extended
;
299 EInvalidArgument = class(EMathError) end;}
307 function EAbs( D
: Double ): Double;
314 function EMax( const Values
: array of Double ): Double;
317 Result
:= Values
[ 0 ];
318 for I
:= 1 to High( Values
) do
319 if Result
< Values
[ I
] then Result
:= Values
[ I
];
322 function EMin( const Values
: array of Double ): Double;
325 Result
:= Values
[ 0 ];
326 for I
:= 1 to High( Values
) do
327 if Result
> Values
[ I
] then Result
:= Values
[ I
];
330 function iMax( const Values
: array of Integer ): Integer;
333 Result
:= Values
[ 0 ];
334 for I
:= 1 to High( Values
) do
335 if Result
< Values
[ I
] then Result
:= Values
[ I
];
338 function iMin( const Values
: array of Integer ): Integer;
341 Result
:= Values
[ 0 ];
342 for I
:= 1 to High( Values
) do
343 if Result
> Values
[ I
] then Result
:= Values
[ I
];
346 function Annuity2(R
: Extended
; N
: Integer; PaymentTime
: TPaymentTime
;
347 var CompoundRN
: Extended
): Extended
; Forward;
348 function Compound(R
: Extended
; N
: Integer): Extended
; Forward;
349 function RelSmall(X
, Y
: Extended
): Boolean; Forward;
353 Neg
, Pos
, DNeg
, DPos
: Extended
359 procedure ArgError(const Msg
: string);
361 raise Exception
.Create(e_Math_InvalidArgument
, Msg
);
364 function DegToRad(Degrees
: Extended
): Extended
; { Radians := Degrees * PI / 180 }
366 Result
:= Degrees
* (PI
/ 180);
369 function RadToDeg(Radians
: Extended
): Extended
; { Degrees := Radians * 180 / PI }
371 Result
:= Radians
* (180 / PI
);
374 function GradToRad(Grads
: Extended
): Extended
; { Radians := Grads * PI / 200 }
376 Result
:= Grads
* (PI
/ 200);
379 function RadToGrad(Radians
: Extended
): Extended
; { Grads := Radians * 200 / PI}
381 Result
:= Radians
* (200 / PI
);
384 function CycleToRad(Cycles
: Extended
): Extended
; { Radians := Cycles * 2PI }
386 Result
:= Cycles
* (2 * PI
);
389 function RadToCycle(Radians
: Extended
): Extended
;{ Cycles := Radians / 2PI }
391 Result
:= Radians
/ (2 * PI
);
394 function LnXP1(X
: Extended
): Extended
;
395 { Return ln(1 + X). Accurate for X near 0. }
398 MOV AX,WORD PTR X
+8 { exponent }
400 CMP AX,$3FFD { .4225 }
412 { Invariant: Y >= 0 & Result*X**Y = X**I. Init Y = I and Result = 1. }
413 {function IntPower(X: Extended; I: Integer): Extended;
428 if I < 0 then Result := 1.0 / Result
431 (* -- already defined in kol.pas
432 function IntPower(Base: Extended; Exponent: Integer): Extended;
438 sub eax, edx { eax := Abs(Exponent) }
442 @@1: fmul ST, ST { X := Base * Base }
445 fmul ST(1),ST { Result := Result * X }
447 fstp st { pop X from FPU stack }
451 fdivrp { Result := 1 / Result }
457 function Compound(R: Extended; N: Integer): Extended;
458 { Return (1 + R)**N. }
460 Result := IntPower(1.0 + R, N)
463 function Annuity2(R: Extended; N: Integer; PaymentTime: TPaymentTime;
464 var CompoundRN: Extended): Extended;
465 { Set CompoundRN to Compound(R, N),
466 return (1+Rate*PaymentTime)*(Compound(R,N)-1)/R;
476 { 6.1E-5 approx= 2**-14 }
477 if EAbs(R) < 6.1E-5 then
479 CompoundRN := Exp(N * LnXP1(R));
480 Result := N*(1+(N-1)*R/2);
484 CompoundRN := Compound(R, N);
485 Result := (CompoundRN-1) / R
487 if PaymentTime = ptStartOfPeriod then
488 Result := Result * (1 + R);
493 procedure PolyX(const A: array of Double; X: Extended; var Poly: TPoly);
494 { Compute A[0] + A[1]*X + ... + A[N]*X**N and X * its derivative.
495 Accumulate positive and negative terms separately. }
498 Neg, Pos, DNeg, DPos: Extended;
504 for I := High(A) downto Low(A) do
506 DNeg := X * DNeg + Neg;
508 DPos := X * DPos + Pos;
517 Poly.DNeg := DNeg * X;
518 Poly.DPos := DPos * X;
522 function RelSmall(X, Y: Extended): Boolean;
523 { Returns True if X is small relative to Y }
528 Result := EAbs(X) < (C1 + C2 * EAbs(Y))
533 function ArcCos(X: Extended): Extended;
535 Result := ArcTan2(Sqrt(1 - X*X), X);
538 function ArcSin(X: Extended): Extended;
540 Result := ArcTan2(X, Sqrt(1 - X*X))
543 function ArcTan2(Y, X: Extended): Extended;
551 function Tan(X: Extended): Extended;
552 { Tan := Sin(X) / Cos(X) }
556 FSTP ST(0) { FPTAN pushes 1.0 after result }
560 function CoTan(X: Extended): Extended;
561 { CoTan := Cos(X) / Sin(X) = 1 / Tan(X) }
569 function Hypot(X, Y: Extended): Extended;
570 { formula: Sqrt(X*X + Y*Y)
571 implemented as: |Y|*Sqrt(1+Sqr(X/Y)), |X| < |Y| for greater precision
585 else // Y > X, X <> 0, so Y > 0
586 Result := Y * Sqrt(1 + Sqr(X/Y));
597 JNZ @@1 // if ST > ST(1) then swap
598 FXCH ST(1) // put larger number in ST(1)
602 TEST AH,$40 // if ST = 0, return ST(1)
606 @@2: FDIV ST,ST(1) // ST := ST / ST(1)
607 FMUL ST,ST // ST := ST * ST
610 FSQRT // ST := Sqrt(ST)
611 FMUL // ST(1) := ST * ST(1); Pop ST
616 procedure SinCos(Theta: Extended; var Sin, Cos: Extended);
620 FSTP tbyte ptr [edx] // Cos
621 FSTP tbyte ptr [eax] // Sin
625 { Extract exponent and mantissa from X }
626 procedure Frexp(X: Extended; var Mantissa: Extended; var Exponent: Integer);
627 { Mantissa ptr in EAX, Exponent ptr in EDX }
631 MOV dword ptr [edx], 0 { if X = 0, return 0 }
639 FXTRACT // ST(1) = exponent, (pushed) ST = fraction
642 // The FXTRACT instruction normalizes the fraction 1 bit higher than
643 // wanted for the definition of frexp() so we need to tweak the result
644 // by scaling the fraction down and incrementing the exponent.
646 FISTP dword ptr [edx]
650 FSCALE // scale fraction
651 INC dword ptr [edx] // exponent biased to match
652 FSTP ST(1) // discard -1, leave fraction as TOS
660 function Ldexp(X: Extended; P: Integer): Extended;
661 { Result := X * (2^P) }
672 function Ceil(X: Extended): Integer;
674 Result := Integer(Trunc(X));
679 function Floor(X: Extended): Integer;
681 Result := Integer(Trunc(X));
686 { Conversion of bases: Log.b(X) = Log.a(X) / Log.a(b) }
688 function Log10(X: Extended): Extended;
689 { Log.10(X) := Log.2(X) * Log.10(2) }
691 FLDLG2 { Log base ten of 2 }
697 function Log2(X: Extended): Extended;
705 function LogN(Base, X: Extended): Extended;
706 { Log.N(X) := Log.2(X) / Log.2(N) }
718 function Poly(X: Extended; const Coefficients: array of Double): Extended;
723 Result := Coefficients[High(Coefficients)];
724 for I := High(Coefficients)-1 downto Low(Coefficients) do
725 Result := Result * X + Coefficients[I];
728 function Power(Base, Exponent: Extended): Extended;
730 if Exponent = 0.0 then
731 Result := 1.0 { n**0 = 1 }
732 else if (Base = 0.0) and (Exponent > 0.0) then
733 Result := 0.0 { 0**n = 0, n > 0 }
734 else if (Frac(Exponent) = 0.0) and (EAbs(Exponent) <= MaxInt) then
735 Result := IntPower(Base, Integer(Trunc(Exponent)))
737 Result := Exp(Exponent * Ln(Base))
740 { Hyperbolic functions }
742 function CoshSinh(X: Extended; Factor: Double): Extended;
744 Result := Exp(X) / 2;
745 Result := Result + Factor / Result;
748 function Cosh(X: Extended): Extended;
750 Result := CoshSinh(X, 0.25)
753 function Sinh(X: Extended): Extended;
755 Result := CoshSinh(X, -0.25)
759 MaxTanhDomain = 5678.22249441322; // Ln(MaxExtended)/2
761 function Tanh(X: Extended): Extended;
763 if X > MaxTanhDomain then
765 else if X < -MaxTanhDomain then
770 Result := Result * Result;
771 Result := (Result - 1.0) / (Result + 1.0)
775 function ArcCosh(X: Extended): Extended;
779 else if X > 1.0e10 then
780 Result := Ln(2) + Ln(X)
782 Result := Ln(X + Sqrt((X - 1.0) * (X + 1.0)));
785 function ArcSinh(X: Extended): Extended;
796 Result := Ln(2) + Ln(X)
800 Result := LnXP1(X + Result / (1 + Sqrt(1 + Result)));
802 if Neg then Result := -Result;
806 function ArcTanh(X: Extended): Extended;
817 Result := MaxExtended
819 Result := 0.5 * LnXP1((2.0 * X) / (1.0 - X));
820 if Neg then Result := -Result;
824 { Statistical functions }
826 function Mean(const Data: array of Double): Extended;
828 Result := SUM(Data) / (High(Data) - Low(Data) + 1)
831 function MinValue(const Data: array of Double): Double;
835 Result := Data[Low(Data)];
836 for I := Low(Data) + 1 to High(Data) do
837 if Result > Data[I] then
841 function MinIntValue(const Data: array of Integer): Integer;
845 Result := Data[Low(Data)];
846 for I := Low(Data) + 1 to High(Data) do
847 if Result > Data[I] then
851 function Min(A,B: Integer): Integer;
860 function Min(A,B: I64): I64;
862 if Cmp64( A, B ) < 0 then
868 function Min(A,B: Single): Single;
876 function Min(A,B: Double): Double;
884 function Min(A,B: Extended): Extended;
893 function MaxValue(const Data: array of Double): Double;
897 Result := Data[Low(Data)];
898 for I := Low(Data) + 1 to High(Data) do
899 if Result < Data[I] then
903 function MaxIntValue(const Data: array of Integer): Integer;
907 Result := Data[Low(Data)];
908 for I := Low(Data) + 1 to High(Data) do
909 if Result < Data[I] then
913 function Max(A,B: Integer): Integer;
922 function Max(A,B: I64): I64;
924 if Cmp64( A, B ) > 0 then
930 function Max(A,B: Single): Single;
938 function Max(A,B: Double): Double;
946 function Max(A,B: Extended): Extended;
955 procedure MeanAndStdDev(const Data: array of Double; var Mean, StdDev: Extended);
960 N := High(Data)- Low(Data) + 1;
967 Mean := Sum(Data) / N;
968 S := 0; // sum differences from the mean, for greater accuracy
969 for I := Low(Data) to High(Data) do
970 S := S + Sqr(Mean - Data[I]);
971 StdDev := Sqrt(S / (N - 1));
974 procedure MomentSkewKurtosis(const Data: array of Double;
975 var M1, M2, M3, M4, Skew, Kurtosis: Extended);
977 Sum, SumSquares, SumCubes, SumQuads, OverN, Accum, M1Sqr, S2N, S3N: Extended;
980 OverN := 1 / (High(Data) - Low(Data) + 1);
985 for I := Low(Data) to High(Data) do
987 Sum := Sum + Data[I];
988 Accum := Sqr(Data[I]);
989 SumSquares := SumSquares + Accum;
990 Accum := Accum*Data[I];
991 SumCubes := SumCubes + Accum;
992 SumQuads := SumQuads + Accum*Data[I];
996 S2N := SumSquares * OverN;
997 S3N := SumCubes * OverN;
999 M3 := S3N - (M1 * 3 * S2N) + 2*M1Sqr*M1;
1000 M4 := (SumQuads * OverN) - (M1 * 4 * S3N) + (M1Sqr*6*S2N - 3*Sqr(M1Sqr));
1001 Skew := M3 * Power(M2, -3/2); // = M3 / Power(M2, 3/2)
1002 Kurtosis := M4 / Sqr(M2);
1005 function Norm(const Data: array of Double): Extended;
1007 Result := Sqrt(SumOfSquares(Data));
1010 function PopnStdDev(const Data: array of Double): Extended;
1012 Result := Sqrt(PopnVariance(Data))
1015 function PopnVariance(const Data: array of Double): Extended;
1017 Result := TotalVariance(Data) / (High(Data) - Low(Data) + 1)
1020 function RandG(Mean, StdDev: Extended): Extended;
1021 { Marsaglia-Bray algorithm }
1027 S2 := Sqr(U1) + Sqr(2*Random-1);
1029 Result := Sqrt(-2*Ln(S2)/S2) * U1 * StdDev + Mean;
1032 function StdDev(const Data: array of Double): Extended;
1034 Result := Sqrt(Variance(Data))
1037 procedure RaiseOverflowError; forward;
1039 function SumInt(const Data: array of Integer): Integer;
1044 for I := Low(Data) to High(Data) do
1045 Result := Result + Data[I]
1047 asm // IN: EAX = ptr to Data, EDX = High(Data) = Count - 1
1048 // loop unrolled 4 times, 5 clocks per loop, 1.2 clocks per datum
1050 MOV ECX, EAX // ecx = ptr to data
1056 JMP @Vector.Pointer[EBX*4]
1063 ADD EAX, [ECX+12+EDX]
1064 JO RaiseOverflowError
1066 ADD EAX, [ECX+8+EDX]
1067 JO RaiseOverflowError
1069 ADD EAX, [ECX+4+EDX]
1070 JO RaiseOverflowError
1073 JO RaiseOverflowError
1079 procedure RaiseOverflowError;
1081 raise Exception.Create(e_IntOverflow, SIntOverflow);
1084 function SUM(const Data: array of Double): Extended;
1089 for I := Low(Data) to High(Data) do
1090 Result := Result + Data[I]
1092 asm // IN: EAX = ptr to Data, EDX = High(Data) = Count - 1
1093 // Uses 4 accumulators to minimize read-after-write delays and loop overhead
1094 // 5 clocks per loop, 4 items per loop = 1.2 clocks per item
1102 SHL EDX, 3 // count * sizeof(Double) = count * 8
1103 JMP @Vector.Pointer[ECX*4]
1109 @@4: FADD qword ptr [EAX+EDX+24] // 1
1111 @@3: FADD qword ptr [EAX+EDX+16] // 1
1113 @@2: FADD qword ptr [EAX+EDX+8] // 1
1115 @@1: FADD qword ptr [EAX+EDX] // 1
1119 FADDP ST(3),ST // ST(3) := ST + ST(3); Pop ST
1120 FADD // ST(1) := ST + ST(1); Pop ST
1121 FADD // ST(1) := ST + ST(1); Pop ST
1125 function SumOfSquares(const Data: array of Double): Extended;
1130 for I := Low(Data) to High(Data) do
1131 Result := Result + Sqr(Data[I]);
1134 procedure SumsAndSquares(const Data: array of Double; var Sum, SumOfSquares: Extended);
1140 for I := Low(Data) to High(Data) do
1142 Sum := Sum + Data[I];
1143 SumOfSquares := SumOfSquares + Data[I]*Data[I];
1146 asm // IN: EAX = ptr to Data
1147 // EDX = High(Data) = Count - 1
1149 // Est. 17 clocks per loop, 4 items per loop = 4.5 clocks per data item
1150 FLDZ // init Sum accumulator
1153 FLD ST(0) // init Sqr1 accum.
1155 FLD ST(0) // init Sqr2 accum.
1157 FLD ST(0) // init/simulate last data item left in ST
1158 SHL EDX, 3 // count * sizeof(Double) = count * 8
1159 JMP @Vector.Pointer[ECX*4]
1165 @@4: FADD // Sqr2 := Sqr2 + Sqr(Data4); Pop Data4
1166 FLD qword ptr [EAX+EDX+24] // Load Data1
1167 FADD ST(3),ST // Sum := Sum + Data1
1168 FMUL ST,ST // Data1 := Sqr(Data1)
1169 @@3: FLD qword ptr [EAX+EDX+16] // Load Data2
1170 FADD ST(4),ST // Sum := Sum + Data2
1171 FMUL ST,ST // Data2 := Sqr(Data2)
1172 FXCH // Move Sqr(Data1) into ST(0)
1173 FADDP ST(3),ST // Sqr1 := Sqr1 + Sqr(Data1); Pop Data1
1174 @@2: FLD qword ptr [EAX+EDX+8] // Load Data3
1175 FADD ST(4),ST // Sum := Sum + Data3
1176 FMUL ST,ST // Data3 := Sqr(Data3)
1177 FXCH // Move Sqr(Data2) into ST(0)
1178 FADDP ST(3),ST // Sqr1 := Sqr1 + Sqr(Data2); Pop Data2
1179 @@1: FLD qword ptr [EAX+EDX] // Load Data4
1180 FADD ST(4),ST // Sum := Sum + Data4
1181 FMUL ST,ST // Sqr(Data4)
1182 FXCH // Move Sqr(Data3) into ST(0)
1183 FADDP ST(3),ST // Sqr1 := Sqr1 + Sqr(Data3); Pop Data3
1186 FADD // Sqr2 := Sqr2 + Sqr(Data4); Pop Data4
1188 FADD // Sqr1 := Sqr2 + Sqr1; Pop Sqr2
1189 FXCH // Move Sum1 into ST(0)
1190 MOV EAX, SumOfSquares
1191 FSTP tbyte ptr [ECX] // Sum := Sum1; Pop Sum1
1192 FSTP tbyte ptr [EAX] // SumOfSquares := Sum1; Pop Sum1
1196 function TotalVariance(const Data: array of Double): Extended;
1198 Sum, SumSquares: Extended;
1200 SumsAndSquares(Data, Sum, SumSquares);
1201 Result := SumSquares - Sqr(Sum)/(High(Data) - Low(Data) + 1);
1204 function Variance(const Data: array of Double): Extended;
1206 Result := TotalVariance(Data) / (High(Data) - Low(Data))
1210 { Depreciation functions. }
1212 function DoubleDecliningBalance(Cost, Salvage: Extended; Life, Period: Integer): Extended;
1213 { dv := cost * (1 - 2/life)**(period - 1)
1215 if DDB > dv - salvage then DDB := dv - salvage
1216 if DDB < 0 then DDB := 0
1219 DepreciatedVal, Factor: Extended;
1222 if (Period < 1) or (Life < Period) or (Life < 1) or (Cost <= Salvage) then
1225 {depreciate everything in period 1 if life is only one or two periods}
1226 if ( Life <= 2 ) then
1228 if ( Period = 1 ) then
1229 DoubleDecliningBalance:=Cost-Salvage
1231 DoubleDecliningBalance:=0; {all depreciation occurred in first period}
1234 Factor := 2.0 / Life;
1236 DepreciatedVal := Cost * IntPower((1.0 - Factor), Period - 1);
1237 {DepreciatedVal is Cost-(sum of previous depreciation results)}
1239 Result := Factor * DepreciatedVal;
1240 {Nominal computed depreciation for this period. The rest of the
1241 function applies limits to this nominal value. }
1243 {Only depreciate until total depreciation equals cost-salvage.}
1244 if Result > DepreciatedVal - Salvage then
1245 Result := DepreciatedVal - Salvage;
1247 {No more depreciation after salvage value is reached. This is mostly a nit.
1248 If Result is negative at this point, it's very close to zero.}
1249 if Result < 0.0 then
1253 function SLNDepreciation(Cost, Salvage: Extended; Life: Integer): Extended;
1254 { Spreads depreciation linearly over life. }
1256 if Life < 1 then ArgError('SLNDepreciation');
1257 Result := (Cost - Salvage) / Life
1260 function SYDDepreciation(Cost, Salvage: Extended; Life, Period: Integer): Extended;
1261 { SYD = (cost - salvage) * (life - period + 1) / (life*(life + 1)/2) }
1262 { Note: life*(life+1)/2 = 1+2+3+...+life "sum of years"
1263 The depreciation factor varies from life/sum_of_years in first period = 1
1264 downto 1/sum_of_years in last period = life.
1265 Total depreciation over life is cost-salvage.}
1270 if (Period < 1) or (Life < Period) or (Cost <= Salvage) then Exit;
1271 X1 := 2 * (Life - Period + 1);
1272 X2 := Life * (Life + 1);
1273 Result := (Cost - Salvage) * X1 / X2
1276 { Discounted cash flow functions. }
1278 function InternalRateOfReturn(Guess: Extended; const CashFlows: array of Double): Extended;
1280 Use Newton's method to solve NPV = 0, where NPV is a polynomial in
1281 x = 1/(1+rate). Split the coefficients into negative and postive sets:
1282 neg + pos = 0, so pos = -neg, so -neg/pos = 1
1286 Let t = log(1/(1+r) = -LnXP1(r)
1287 then r = exp(-t) - 1
1288 Iterate on t, then use the last equation to compute r.
1295 function ConditionP(const CashFlows: array of Double): Integer;
1296 { Guarantees existence and uniqueness of root. The sign of payments
1297 must change exactly once, the net payout must be always > 0 for
1298 first portion, then each payment must be >= 0.
1299 Returns: 0 if condition not satisfied, > 0 if condition satisfied
1300 and this is the index of the first value considered a payback. }
1305 K := High(CashFlows);
1306 while (K >= 0) and (CashFlows[K] >= 0.0) do Dec(K);
1312 while I < K do begin
1313 X := X + CashFlows[I];
1326 InternalRateOfReturn := 0;
1327 K := ConditionP(CashFlows);
1328 if K < 0 then ArgError('InternalRateOfReturn');
1331 if Guess <= -1.0 then ArgError('InternalRateOfReturn');
1335 for Count := 1 to MaxIterations do
1337 PolyX(CashFlows, Exp(T), Poly);
1338 if Poly.Pos <= Poly.Neg then ArgError('InternalRateOfReturn');
1339 if (Poly.Neg >= 0.0) or (Poly.Pos <= 0.0) then
1341 InternalRateOfReturn := -1.0;
1345 Y := Ln(-Neg / Pos) / (DNeg / Neg - DPos / Pos);
1347 if RelSmall(Y, T) then
1349 InternalRateOfReturn := Exp(-T) - 1.0;
1353 ArgError('InternalRateOfReturn');
1356 function NetPresentValue(Rate: Extended; const CashFlows: array of Double;
1357 PaymentTime: TPaymentTime): Extended;
1358 { Caution: The sign of NPV is reversed from what would be expected for standard
1364 if Rate <= -1.0 then ArgError('NetPresentValue');
1367 for I := High(CashFlows) downto Low(CashFlows) do
1368 result := rr * result + CashFlows[I];
1369 if PaymentTime = ptEndOfPeriod then result := rr * result;
1372 { Annuity functions. }
1375 From the point of view of A, amounts received by A are positive and
1376 amounts disbursed by A are negative (e.g. a borrower's loan repayments
1377 are regarded by the borrower as negative).
1379 Given interest rate r, number of periods n:
1380 compound(r, n) = (1 + r)**n "Compounding growth factor"
1381 annuity(r, n) = (compound(r, n)-1) / r "Annuity growth factor"
1383 Given future value fv, periodic payment pmt, present value pv and type
1384 of payment (start, 1 , or end of period, 0) pmtTime, financial variables satisfy:
1386 fv = -pmt*(1 + r*pmtTime)*annuity(r, n) - pv*compound(r, n)
1391 A := (1 + r*pmtTime)*annuity(r, n)
1392 Compute both at once in Annuity2.
1394 if C > 1E16 then A = C/r, so:
1396 pv := -pmt*(pmtTime+1/r)
1397 pmt := -pv*r/(1 + r*pmtTime)
1399 fv := -pmt(1+r*pmtTime)*A - pv*C
1400 pv := (-pmt(1+r*pmtTime)*A - fv)/C
1401 pmt := (-pv*C-fv)/((1+r*pmtTime)*A)
1404 function PaymentParts(Period, NPeriods: Integer; Rate, PresentValue,
1405 FutureValue: Extended; PaymentTime: TPaymentTime; var IntPmt: Extended):
1408 Crn:extended; { =Compound(Rate,NPeriods) }
1409 Crp:extended; { =Compound(Rate,Period-1) }
1410 Arn:extended; { =AnnuityF(Rate,NPeriods) }
1413 if Rate <= -1.0 then ArgError('PaymentParts');
1414 Crp:=Compound(Rate,Period-1);
1415 Arn:=Annuity2(Rate,NPeriods,PaymentTime,Crn);
1416 IntPmt:=(FutureValue*(Crp-1)-PresentValue*(Crn-Crp))/Arn;
1417 PaymentParts:=(-FutureValue-PresentValue)*Crp/Arn;
1420 function FutureValue(Rate: Extended; NPeriods: Integer; Payment, PresentValue:
1421 Extended; PaymentTime: TPaymentTime): Extended;
1423 Annuity, CompoundRN: Extended;
1425 if Rate <= -1.0 then ArgError('FutureValue');
1426 Annuity := Annuity2(Rate, NPeriods, PaymentTime, CompoundRN);
1427 if CompoundRN > 1.0E16 then ArgError('FutureValue');
1428 FutureValue := -Payment * Annuity - PresentValue * CompoundRN
1431 function InterestPayment(Rate: Extended; Period, NPeriods: Integer; PresentValue,
1432 FutureValue: Extended; PaymentTime: TPaymentTime): Extended;
1434 Crp:extended; { compound(rate,period-1)}
1435 Crn:extended; { compound(rate,nperiods)}
1436 Arn:extended; { annuityf(rate,nperiods)}
1439 or (Period < 1) or (Period > NPeriods) then ArgError('InterestPayment');
1440 Crp:=Compound(Rate,Period-1);
1441 Arn:=Annuity2(Rate,Nperiods,PaymentTime,Crn);
1442 InterestPayment:=(FutureValue*(Crp-1)-PresentValue*(Crn-Crp))/Arn;
1445 function InterestRate(NPeriods: Integer;
1446 Payment, PresentValue, FutureValue: Extended; PaymentTime: TPaymentTime): Extended;
1449 First and last payments are non-zero and of opposite signs.
1450 Number of periods N >= 2.
1451 Convert data into cash flow of first, N-1 payments, last with
1452 first < 0, payment > 0, last > 0.
1453 Compute the IRR of this cash flow:
1454 0 = first + pmt*x + pmt*x**2 + ... + pmt*x**(N-1) + last*x**N
1455 where x = 1/(1 + rate).
1456 Substitute x = exp(t) and apply Newton's method to
1457 f(t) = log(pmt*x + ... + last*x**N) / -first
1458 which has a unique root given the above hypotheses.
1461 X, Y, Z, First, Pmt, Last, T, ET, EnT, ET1: Extended;
1465 function LostPrecision(X: Extended): Boolean;
1480 if NPeriods <= 0 then ArgError('InterestRate');
1482 if PaymentTime = ptEndOfPeriod then
1485 Y := FutureValue + Payment
1489 X := PresentValue + Payment;
1495 if First * Payment > 0.0 then
1508 if (First = 0.0) or (Last < 0.0) then ArgError('InterestRate');
1509 T := 0.0; { Guess at solution }
1510 for Count := 1 to MaxIterations do
1512 EnT := Exp(NPeriods * T);
1513 if {LostPrecision(EnT)} ent=(ent+1) then
1515 Result := -Pmt / First;
1517 Result := Exp(-LnXP1(Result)) - 1.0;
1525 Y := X * (X - 1.0) / 2.0
1529 X := ET * (Exp((NPeriods - 1) * T)-1.0) / ET1;
1530 Y := (NPeriods * EnT - ET - X * ET) / ET1
1532 Z := Pmt * X + Last * EnT;
1533 Y := Ln(Z / -First) / ((Pmt * Y + Last * NPeriods *EnT) / Z);
1535 if RelSmall(Y, T) then
1537 if not Reverse then T := -T;
1538 InterestRate := Exp(T)-1.0;
1542 ArgError('InterestRate');
1545 function NumberOfPeriods(Rate, Payment, PresentValue, FutureValue: Extended;
1546 PaymentTime: TPaymentTime): Extended;
1548 { If Rate = 0 then nper := -(pv + fv) / pmt
1549 else cf := pv + pmt * (1 + rate*pmtTime) / rate
1550 nper := LnXP1(-(pv + fv) / cf) / LnXP1(rate) }
1553 PVRPP: Extended; { =PV*Rate+Payment } {"initial cash flow"}
1558 if Rate <= -1.0 then ArgError('NumberOfPeriods');
1560 {whenever both Payment and PaymentTime are given together, the PaymentTime has the effect
1561 of modifying the effective Payment by the interest accrued on the Payment}
1563 if ( PaymentTime=ptStartOfPeriod ) then
1564 Payment:=Payment*(1+Rate);
1566 {if the payment exactly matches the interest accrued periodically on the
1567 presentvalue, then an infinite number of payments are going to be
1568 required to effect a change from presentvalue to futurevalue. The
1569 following catches that specific error where payment is exactly equal,
1570 but opposite in sign to the interest on the present value. If PVRPP
1571 ("initial cash flow") is simply close to zero, the computation will
1572 be numerically unstable, but not as likely to cause an error.}
1574 PVRPP:=PresentValue*Rate+Payment;
1575 if PVRPP=0 then ArgError('NumberOfPeriods');
1577 { 6.1E-5 approx= 2**-14 }
1578 if ( EAbs(Rate)<6.1E-5 ) then
1579 Result:=-(PresentValue+FutureValue)/PVRPP
1583 {starting with the initial cash flow, each compounding period cash flow
1584 should result in the current value approaching the final value. The
1585 following test combines a number of simultaneous conditions to ensure
1586 reasonableness of the cashflow before computing the NPER.}
1588 T:= -(PresentValue+FutureValue)*Rate/PVRPP;
1589 if T<=-1.0 then ArgError('NumberOfPeriods');
1590 Result := LnXP1(T) / LnXP1(Rate)
1592 NumberOfPeriods:=Result;
1595 function Payment(Rate: Extended; NPeriods: Integer; PresentValue, FutureValue:
1596 Extended; PaymentTime: TPaymentTime): Extended;
1598 Annuity, CompoundRN: Extended;
1600 if Rate <= -1.0 then ArgError('Payment');
1601 Annuity := Annuity2(Rate, NPeriods, PaymentTime, CompoundRN);
1602 if CompoundRN > 1.0E16 then
1603 Payment := -PresentValue * Rate / (1 + Integer(PaymentTime) * Rate)
1605 Payment := (-PresentValue * CompoundRN - FutureValue) / Annuity
1608 function PeriodPayment(Rate: Extended; Period, NPeriods: Integer;
1609 PresentValue, FutureValue: Extended; PaymentTime: TPaymentTime): Extended;
1613 if (Rate <= -1.0) or (Period < 1) or (Period > NPeriods) then ArgError('PeriodPayment');
1614 PeriodPayment := PaymentParts(Period, NPeriods, Rate, PresentValue,
1615 FutureValue, PaymentTime, Junk);
1618 function PresentValue(Rate: Extended; NPeriods: Integer; Payment, FutureValue:
1619 Extended; PaymentTime: TPaymentTime): Extended;
1621 Annuity, CompoundRN: Extended;
1623 if Rate <= -1.0 then ArgError('PresentValue');
1624 Annuity := Annuity2(Rate, NPeriods, PaymentTime, CompoundRN);
1625 if CompoundRN > 1.0E16 then
1626 PresentValue := -(Payment / Rate * Integer(PaymentTime) * Payment)
1628 PresentValue := (-Payment * Annuity - FutureValue) / CompoundRN