Extended math RTL

This commit is contained in:
Michael Schimmel
2026-01-13 18:08:52 +01:00
parent 1c6a6fe5a0
commit 629dbc97ca
6 changed files with 457 additions and 90 deletions
+71 -35
View File
@@ -1,9 +1,15 @@
(do (do
;; =========================================================================== ;; ===========================================================================
;; 1. DEFINITIONEN (Wie zuvor besprochen) ;; 1. DEFINITIONEN
;; =========================================================================== ;; ===========================================================================
;; --- WMA Factory (O(1)) --- ;; ---------------------------------------------------------------------------
;; Factory: create-wma
;; Berechnet WMA. Wartet, bis das Fenster voll ist.
;; Phase 1: Warten (< len)
;; Phase 2: Init (= len) -> Loop berechnung
;; Phase 3: Slide (> len) -> O(1) berechnung
;; ---------------------------------------------------------------------------
(def create-wma (def create-wma
(fn [len] (fn [len]
(do (do
@@ -14,17 +20,51 @@
(fn [src] (fn [src]
(do (do
(def cnt (count src)) (def cnt (count src))
(if (< cnt (+ len 1))
0.0 ;; PHASE 1: Nicht genug Daten
(if (< cnt len)
NaN
;; Genug Daten vorhanden
(if (= cnt len)
;; PHASE 2: Initialisierung (Das Fenster ist gerade voll geworden)
;; Wir müssen einmalig die Basis-Summen berechnen.
(do (do
(def val-new (get src 0)) (def init-w 0.0)
(def val-out (get src len)) (def init-s 0.0)
;; Loop über die Elemente 0 bis len-1
((fn [i]
(if (< i len)
(do
(def val (get src i))
;; Gewichtung: Index 0 ist das neueste -> Gewicht 'len'
(assign init-w (+ init-w (* (- len i) val)))
(assign init-s (+ init-s val))
(recur (+ i 1)))
)) 0)
;; State speichern
(assign prev-w-sum init-w)
(assign prev-s-sum init-s)
(/ init-w weight-div))
;; PHASE 3: Sliding Window (O(1))
;; Wir haben bereits einen gültigen State aus dem vorherigen Schritt.
(do
(def val-new (get src 0)) ; Neuster Wert
(def val-out (get src len)) ; Wert der rausfällt
;; Update Formeln
(def w-sum (+ prev-w-sum (- (* len val-new) prev-s-sum))) (def w-sum (+ prev-w-sum (- (* len val-new) prev-s-sum)))
(def s-sum (+ (- prev-s-sum val-out) val-new)) (def s-sum (+ (- prev-s-sum val-out) val-new))
;; State speichern
(assign prev-w-sum w-sum) (assign prev-w-sum w-sum)
(assign prev-s-sum s-sum) (assign prev-s-sum s-sum)
(/ w-sum weight-div) (/ w-sum weight-div))
)) )
)
)) ))
))) )))
@@ -34,18 +74,26 @@
(do (do
(def half-len (round (/ len 2))) (def half-len (round (/ len 2)))
(def sqrt-len (round (sqrt len))) (def sqrt-len (round (sqrt len)))
(def wma-half (create-wma half-len)) (def wma-half (create-wma half-len))
(def wma-full (create-wma len)) (def wma-full (create-wma len))
(def wma-smooth (create-wma sqrt-len)) (def wma-smooth (create-wma sqrt-len))
(def diff-series (new-series [[:val :Float]])) (def diff-series (new-series [[:val :Float]]))
(fn [src] (fn [src]
(do (do
(def v1 (wma-half src)) (def v1 (wma-half src))
(def v2 (wma-full src)) (def v2 (wma-full src))
;; WICHTIG: Wir dürfen erst weitermachen, wenn BEIDE WMAs gültig sind.
;; Da v2 das längere Fenster (len) braucht, diktiert es den Takt.
(def diff (- (* 2 v1) v2)) (def diff (- (* 2 v1) v2))
(add-item diff-series {:val diff}) (if (not (is-NaN diff))
(wma-smooth (get diff-series :val)) (add-item diff-series {:val diff}))
;; Glättung auf dem Resultat
(wma-smooth (.val diff-series))
)) ))
))) )))
@@ -59,45 +107,33 @@
~n)) ~n))
;; =========================================================================== ;; ===========================================================================
;; 2. SIMULATION & TEST ;; 2. SIMULATION
;; =========================================================================== ;; ===========================================================================
;; Konfiguration (def hma-length 250)
(def hma-length 14) (def simulation-steps 5000)
(def simulation-steps 50)
;; Initialisierung der Komponenten (def hma-calc (create-hma hma-length))
(def hma-calc (create-hma hma-length)) ; Die HMA-Closure (def prices (new-series [[:close :Float]]))
(def prices (new-series [[:close :Float]])) ; Die Preis-Series (def current-price 100.0)
(def current-price 100.0) ; Startpreis
;; Header Ausgabe (falls print verfügbar ist)
(print "Step | Price | HMA(14)") (print "Step | Price | HMA(14)")
(print "-----|----------|----------") (print "-----|----------|----------")
;; Simulations-Schleife
(repeat simulation-steps (repeat simulation-steps
(do (do
;; 1. Preis simulieren (Random Walk) ;; Random Walk
;; (random) gibt 0.0 bis 1.0 zurück.
;; (- (random) 0.5) erzeugt eine Änderung zwischen -0.5 und +0.5
(assign current-price (+ current-price (- (random) 0.5))) (assign current-price (+ current-price (- (random) 0.5)))
;; 2. Preis zur Series hinzufügen
(add-item prices {:close current-price}) (add-item prices {:close current-price})
;; 3. HMA berechnen ;; Berechnung
(def hma-val 0.0) ;; Nutzung von Member Access (.close), da es eine Record-Series ist
(def hma-val (hma-calc (.close prices)))
;; 3. HMA berechnen ;; Ausgabe
(def price-series (get prices :close)) (if (not (is-NaN hma-val))
(assign hma-val (hma-calc price-series))
;; 4. Ausgabe
;; Wir geben nur Werte aus, wenn der HMA "eingeschwungen" ist (Wert > 0)
(if (> hma-val 0.0)
(print (count prices) " |" current-price " | " hma-val) (print (count prices) " |" current-price " | " hma-val)
(print (count prices) " |" current-price " | (warming up...)")) )
) )
) )
) )
+39 -34
View File
@@ -14,9 +14,17 @@ type
public public
// --- Constants --- // --- Constants ---
[TRtlConst('false')]
[AstDoc('Boolean false.')]
class function CFalse: Boolean; static;
[TRtlConst('true')]
[AstDoc('Boolean true.')]
class function CTrue: Boolean; static;
[TRtlConst('NaN')] [TRtlConst('NaN')]
[AstDoc('Not a Number (IEEE 754).')] [AstDoc('Not a Number (IEEE 754).')]
class function Const_NaN: Double; static; class function CNaN: Double; static;
// --- Arithmetic Operators --- // --- Arithmetic Operators ---
@@ -38,17 +46,17 @@ type
class function Multiply(const Args: TArray<TDataValue>): TDataValue; static; class function Multiply(const Args: TArray<TDataValue>): TDataValue; static;
[TRtlExport('/', Pure)] [TRtlExport('/', Pure)]
[AstDoc('Divides A by B. Always returns a floating point number.')] [AstDoc('Divides A by B. Returns NaN on division by zero.')]
[AstSignature('(number, number) -> number')] [AstSignature('(number, number) -> number')]
class function Divide(const Args: TArray<TDataValue>): TDataValue; static; class function Divide(const Args: TArray<TDataValue>): TDataValue; static;
[TRtlExport('div', Pure)] [TRtlExport('div', Pure)]
[AstDoc('Performs integer division.')] [AstDoc('Performs integer division. Returns NaN on division by zero.')]
[AstSignature('(number, number) -> number')] [AstSignature('(number, number) -> number')]
class function IntDivide(const Args: TArray<TDataValue>): TDataValue; static; class function IntDivide(const Args: TArray<TDataValue>): TDataValue; static;
[TRtlExport('mod', Pure)] [TRtlExport('mod', Pure)]
[AstDoc('Returns the remainder of integer division.')] [AstDoc('Returns the remainder of integer division. Returns NaN on division by zero.')]
[AstSignature('(number, number) -> number')] [AstSignature('(number, number) -> number')]
class function Modulus(const Args: TArray<TDataValue>): TDataValue; static; class function Modulus(const Args: TArray<TDataValue>): TDataValue; static;
@@ -160,11 +168,6 @@ type
[TRtlExport('/', Pure)] [TRtlExport('/', Pure)]
class function Divide_Float_Ordinal_Float(A: Double; B: Int64): Double; static; class function Divide_Float_Ordinal_Float(A: Double; B: Int64): Double; static;
[TRtlExport('div', Pure)]
class function Div_Ordinal_Ordinal_Ordinal(A, B: Int64): Int64; static;
[TRtlExport('mod', Pure)]
class function Mod_Ordinal_Ordinal_Ordinal(A, B: Int64): Int64; static;
[TRtlExport('=', Pure)] [TRtlExport('=', Pure)]
class function Equal_Ordinal_Ordinal_Ordinal(A, B: Int64): Int64; static; class function Equal_Ordinal_Ordinal_Ordinal(A, B: Int64): Int64; static;
[TRtlExport('=', Pure)] [TRtlExport('=', Pure)]
@@ -246,7 +249,7 @@ implementation
{ TRtlCoreFunctions - Constants } { TRtlCoreFunctions - Constants }
class function TRtlCoreFunctions.Const_NaN: Double; class function TRtlCoreFunctions.CNaN: Double;
begin begin
Result := System.Math.NaN; Result := System.Math.NaN;
end; end;
@@ -284,6 +287,7 @@ class function TRtlCoreFunctions.Divide(const Args: TArray<TDataValue>): TDataVa
begin begin
if Length(Args) <> 2 then if Length(Args) <> 2 then
raise EArgumentException.Create('Operator / requires 2 arguments.'); raise EArgumentException.Create('Operator / requires 2 arguments.');
Result := Args[0].AsScalar / Args[1].AsScalar; Result := Args[0].AsScalar / Args[1].AsScalar;
end; end;
@@ -291,6 +295,7 @@ class function TRtlCoreFunctions.IntDivide(const Args: TArray<TDataValue>): TDat
begin begin
if Length(Args) <> 2 then if Length(Args) <> 2 then
raise EArgumentException.Create('Operator div requires 2 arguments.'); raise EArgumentException.Create('Operator div requires 2 arguments.');
Result := Args[0].AsScalar div Args[1].AsScalar; Result := Args[0].AsScalar div Args[1].AsScalar;
end; end;
@@ -298,7 +303,8 @@ class function TRtlCoreFunctions.Modulus(const Args: TArray<TDataValue>): TDataV
begin begin
if Length(Args) <> 2 then if Length(Args) <> 2 then
raise EArgumentException.Create('Operator mod requires 2 arguments.'); raise EArgumentException.Create('Operator mod requires 2 arguments.');
Result := Args[0].AsScalar mod Args[1].AsScalar;
Result := TScalar.FromInt64(Int64(Args[0].AsScalar) mod Args[1].AsScalar);
end; end;
class function TRtlCoreFunctions.Equal(const Args: TArray<TDataValue>): TDataValue; class function TRtlCoreFunctions.Equal(const Args: TArray<TDataValue>): TDataValue;
@@ -447,42 +453,30 @@ begin
Result := A * B; Result := A * B;
end; end;
// Divide // Divide - Updated to return NaN on div0
class function TRtlCoreFunctions.Divide_Ordinal_Ordinal_Float(A, B: Int64): Double; class function TRtlCoreFunctions.Divide_Ordinal_Ordinal_Float(A, B: Int64): Double;
begin begin
if B = 0 then
raise EDivByZero.Create('Division by zero.');
Result := A / B; Result := A / B;
end; end;
class function TRtlCoreFunctions.Divide_Float_Float_Float(A, B: Double): Double; class function TRtlCoreFunctions.Divide_Float_Float_Float(A, B: Double): Double;
begin begin
if B = 0.0 then if B = 0.0 then
raise EDivByZero.Create('Division by zero.'); Result := NaN
else
Result := A / B; Result := A / B;
end; end;
class function TRtlCoreFunctions.Divide_Ordinal_Float_Float(A: Int64; B: Double): Double; class function TRtlCoreFunctions.Divide_Ordinal_Float_Float(A: Int64; B: Double): Double;
begin begin
if B = 0.0 then if B = 0.0 then
raise EDivByZero.Create('Division by zero.'); Result := NaN
else
Result := A / B; Result := A / B;
end; end;
class function TRtlCoreFunctions.Divide_Float_Ordinal_Float(A: Double; B: Int64): Double; class function TRtlCoreFunctions.Divide_Float_Ordinal_Float(A: Double; B: Int64): Double;
begin begin
if B = 0 then
raise EDivByZero.Create('Division by zero.');
Result := A / B; Result := A / B;
end; end;
// Integer Math
class function TRtlCoreFunctions.Div_Ordinal_Ordinal_Ordinal(A, B: Int64): Int64;
begin
Result := A div B;
end;
class function TRtlCoreFunctions.Mod_Ordinal_Ordinal_Ordinal(A, B: Int64): Int64;
begin
Result := A mod B;
end;
// Comparisons // Comparisons
class function TRtlCoreFunctions.Equal_Ordinal_Ordinal_Ordinal(A, B: Int64): Int64; class function TRtlCoreFunctions.Equal_Ordinal_Ordinal_Ordinal(A, B: Int64): Int64;
begin begin
@@ -490,15 +484,15 @@ begin
end; end;
class function TRtlCoreFunctions.Equal_Float_Float_Ordinal(A, B: Double): Int64; class function TRtlCoreFunctions.Equal_Float_Float_Ordinal(A, B: Double): Int64;
begin begin
Result := Ord(SameValue(A, B)); Result := Ord(A = B);
end; end;
class function TRtlCoreFunctions.Equal_Ordinal_Float_Ordinal(A: Int64; B: Double): Int64; class function TRtlCoreFunctions.Equal_Ordinal_Float_Ordinal(A: Int64; B: Double): Int64;
begin begin
Result := Ord(SameValue(A, B)); Result := Ord(A = B);
end; end;
class function TRtlCoreFunctions.Equal_Float_Ordinal_Ordinal(A: Double; B: Int64): Int64; class function TRtlCoreFunctions.Equal_Float_Ordinal_Ordinal(A: Double; B: Int64): Int64;
begin begin
Result := Ord(SameValue(A, B)); Result := Ord(A = B);
end; end;
class function TRtlCoreFunctions.Equal_Keyword_Keyword_Ordinal(A, B: Int64): Int64; class function TRtlCoreFunctions.Equal_Keyword_Keyword_Ordinal(A, B: Int64): Int64;
begin begin
@@ -511,15 +505,15 @@ begin
end; end;
class function TRtlCoreFunctions.NotEqual_Float_Float_Ordinal(A, B: Double): Int64; class function TRtlCoreFunctions.NotEqual_Float_Float_Ordinal(A, B: Double): Int64;
begin begin
Result := Ord(not SameValue(A, B)); Result := Ord(A <> B);
end; end;
class function TRtlCoreFunctions.NotEqual_Ordinal_Float_Ordinal(A: Int64; B: Double): Int64; class function TRtlCoreFunctions.NotEqual_Ordinal_Float_Ordinal(A: Int64; B: Double): Int64;
begin begin
Result := Ord(not SameValue(A, B)); Result := Ord(A <> B);
end; end;
class function TRtlCoreFunctions.NotEqual_Float_Ordinal_Ordinal(A: Double; B: Int64): Int64; class function TRtlCoreFunctions.NotEqual_Float_Ordinal_Ordinal(A: Double; B: Int64): Int64;
begin begin
Result := Ord(not SameValue(A, B)); Result := Ord(A <> B);
end; end;
class function TRtlCoreFunctions.NotEqual_Keyword_Keyword_Ordinal(A, B: Int64): Int64; class function TRtlCoreFunctions.NotEqual_Keyword_Keyword_Ordinal(A, B: Int64): Int64;
begin begin
@@ -599,6 +593,17 @@ class function TRtlCoreFunctions.And_Ordinal_Ordinal_Ordinal(A, B: Int64): Int64
begin begin
Result := A and B; Result := A and B;
end; end;
class function TRtlCoreFunctions.CFalse: Boolean;
begin
Result := false;
end;
class function TRtlCoreFunctions.CTrue: Boolean;
begin
Result := true;
end;
class function TRtlCoreFunctions.Or_Ordinal_Ordinal_Ordinal(A, B: Int64): Int64; class function TRtlCoreFunctions.Or_Ordinal_Ordinal_Ordinal(A, B: Int64): Int64;
begin begin
Result := A or B; Result := A or B;
+325 -1
View File
@@ -12,7 +12,17 @@ uses
type type
TRtlMathFunctions = record TRtlMathFunctions = record
public public
// --- Math Functions --- // --- Constants ---
[TRtlConst('Pi')]
[AstDoc('Pi.')]
class function CPi: Double; static;
[TRtlConst('e')]
[AstDoc('Euler''s number.')]
class function CE: Double; static;
// --- Basic Math ---
[TRtlExport('abs', Pure)] [TRtlExport('abs', Pure)]
[AstDoc('Returns the absolute value of a number.')] [AstDoc('Returns the absolute value of a number.')]
@@ -44,6 +54,16 @@ type
[AstSignature('(number) -> number')] [AstSignature('(number) -> number')]
class function Sign(const Arg: TScalar): TScalar; static; class function Sign(const Arg: TScalar): TScalar; static;
[TRtlExport('min', Pure)]
[AstDoc('Returns the smaller of two numbers.')]
[AstSignature('(number, number) -> number')]
class function Min(const Args: TArray<TDataValue>): TDataValue; static;
[TRtlExport('max', Pure)]
[AstDoc('Returns the larger of two numbers.')]
[AstSignature('(number, number) -> number')]
class function Max(const Args: TArray<TDataValue>): TDataValue; static;
[TRtlExport('sqrt', Pure)] [TRtlExport('sqrt', Pure)]
[AstDoc('Returns the square root of a number.')] [AstDoc('Returns the square root of a number.')]
[AstSignature('(number) -> number')] [AstSignature('(number) -> number')]
@@ -54,12 +74,98 @@ type
[AstSignature('(number, number) -> number')] [AstSignature('(number, number) -> number')]
class function Pow(const Args: TArray<TDataValue>): TDataValue; static; class function Pow(const Args: TArray<TDataValue>): TDataValue; static;
[TRtlExport('exp', Pure)]
[AstDoc('Returns e raised to the power of X.')]
[AstSignature('(number) -> number')]
class function Exp(const Arg: TScalar): TScalar; static;
[TRtlExport('log', Pure)]
[AstDoc('Returns the natural logarithm (Base e).')]
[AstSignature('(number) -> number')]
class function Log(const Arg: TScalar): TScalar; static;
[TRtlExport('log10', Pure)]
[AstDoc('Returns the base-10 logarithm.')]
[AstSignature('(number) -> number')]
class function Log10(const Arg: TScalar): TScalar; static;
// --- Trigonometry ---
[TRtlExport('sin', Pure)]
[AstDoc('Returns the sine of the angle (in radians).')]
[AstSignature('(number) -> number')]
class function Sin(const Arg: TScalar): TScalar; static;
[TRtlExport('cos', Pure)]
[AstDoc('Returns the cosine of the angle (in radians).')]
[AstSignature('(number) -> number')]
class function Cos(const Arg: TScalar): TScalar; static;
[TRtlExport('tan', Pure)]
[AstDoc('Returns the tangent of the angle (in radians).')]
[AstSignature('(number) -> number')]
class function Tan(const Arg: TScalar): TScalar; static;
[TRtlExport('asin', Pure)]
[AstDoc('Returns the arc sine.')]
[AstSignature('(number) -> number')]
class function ArcSin(const Arg: TScalar): TScalar; static;
[TRtlExport('acos', Pure)]
[AstDoc('Returns the arc cosine.')]
[AstSignature('(number) -> number')]
class function ArcCos(const Arg: TScalar): TScalar; static;
[TRtlExport('atan', Pure)]
[AstDoc('Returns the arc tangent.')]
[AstSignature('(number) -> number')]
class function ArcTan(const Arg: TScalar): TScalar; static;
[TRtlExport('atan2', Pure)]
[AstDoc('Returns the angle whose tangent is Y / X.')]
[AstSignature('(y, x) -> number')]
class function ArcTan2(const Args: TArray<TDataValue>): TDataValue; static;
[TRtlExport('sinh', Pure)]
[AstDoc('Returns the hyperbolic sine.')]
[AstSignature('(number) -> number')]
class function Sinh(const Arg: TScalar): TScalar; static;
[TRtlExport('cosh', Pure)]
[AstDoc('Returns the hyperbolic cosine.')]
[AstSignature('(number) -> number')]
class function Cosh(const Arg: TScalar): TScalar; static;
[TRtlExport('tanh', Pure)]
[AstDoc('Returns the hyperbolic tangent.')]
[AstSignature('(number) -> number')]
class function Tanh(const Arg: TScalar): TScalar; static;
// --- Conversion ---
[TRtlExport('deg2rad', Pure)]
[AstDoc('Converts degrees to radians.')]
[AstSignature('(number) -> number')]
class function DegToRad(const Arg: TScalar): TScalar; static;
[TRtlExport('rad2deg', Pure)]
[AstDoc('Converts radians to degrees.')]
[AstSignature('(number) -> number')]
class function RadToDeg(const Arg: TScalar): TScalar; static;
// --- Utils ---
[TRtlExport('random', Impure)] [TRtlExport('random', Impure)]
[AstDoc('Returns a random number. No args: [0..1). Arg N: Integer [0..N).')] [AstDoc('Returns a random number. No args: [0..1). Arg N: Integer [0..N).')]
[AstSignature('() -> float')] [AstSignature('() -> float')]
[AstSignature('(number) -> number')] [AstSignature('(number) -> number')]
class function Random(const Args: TArray<TDataValue>): TDataValue; static; class function Random(const Args: TArray<TDataValue>): TDataValue; static;
[TRtlExport('is-NaN', Pure)]
[AstDoc('Returns true if the value is Not-a-Number (NaN).')]
[AstSignature('(number) -> boolean')]
class function IsNaN(const Arg: TScalar): TScalar; static;
// --- Static Specializations --- // --- Static Specializations ---
[TRtlExport('abs', Pure)] [TRtlExport('abs', Pure)]
@@ -91,16 +197,57 @@ type
[TRtlExport('pow', Pure)] [TRtlExport('pow', Pure)]
class function Pow_Float_Ordinal_Float(A: Double; B: Int64): Double; static; class function Pow_Float_Ordinal_Float(A: Double; B: Int64): Double; static;
[TRtlExport('min', Pure)]
class function Min_Ordinal_Ordinal_Ordinal(A, B: Int64): Int64; static;
[TRtlExport('min', Pure)]
class function Min_Float_Float_Float(A, B: Double): Double; static;
[TRtlExport('max', Pure)]
class function Max_Ordinal_Ordinal_Ordinal(A, B: Int64): Int64; static;
[TRtlExport('max', Pure)]
class function Max_Float_Float_Float(A, B: Double): Double; static;
[TRtlExport('exp', Pure)]
class function Exp_Float_Float(A: Double): Double; static;
[TRtlExport('log', Pure)]
class function Log_Float_Float(A: Double): Double; static;
[TRtlExport('log10', Pure)]
class function Log10_Float_Float(A: Double): Double; static;
[TRtlExport('sin', Pure)]
class function Sin_Float_Float(A: Double): Double; static;
[TRtlExport('cos', Pure)]
class function Cos_Float_Float(A: Double): Double; static;
[TRtlExport('tan', Pure)]
class function Tan_Float_Float(A: Double): Double; static;
[TRtlExport('random', Impure)] [TRtlExport('random', Impure)]
class function Random_Void_Float: Double; static; class function Random_Void_Float: Double; static;
[TRtlExport('random', Impure)] [TRtlExport('random', Impure)]
class function Random_Ordinal_Ordinal(Limit: Int64): Int64; static; class function Random_Ordinal_Ordinal(Limit: Int64): Int64; static;
[TRtlExport('is-NaN', Pure)]
class function IsNaN_Float_Ordinal(A: Double): Int64; static;
end; end;
implementation implementation
{ TRtlMathFunctions } { TRtlMathFunctions }
// --- Constants ---
class function TRtlMathFunctions.CPi: Double;
begin
Result := TScalar.FromDouble(System.Pi);
end;
class function TRtlMathFunctions.CE: Double;
begin
Result := TScalar.FromDouble(2.71828182845904523536);
end;
// --- Basic Math ---
class function TRtlMathFunctions.Abs(const Arg: TScalar): TScalar; class function TRtlMathFunctions.Abs(const Arg: TScalar): TScalar;
begin begin
case Arg.Kind of case Arg.Kind of
@@ -169,6 +316,34 @@ begin
end; end;
end; end;
class function TRtlMathFunctions.Min(const Args: TArray<TDataValue>): TDataValue;
begin
if Length(Args) <> 2 then
raise EArgumentException.Create('Min requires 2 arguments.');
var a: TScalar := Args[0].AsScalar;
var b: TScalar := Args[1].AsScalar;
if (a.Kind = TScalar.TKind.Ordinal) and (b.Kind = TScalar.TKind.Ordinal) then
Result := TScalar.FromInt64(System.Math.Min(a.Value.AsInt64, b.Value.AsInt64))
else
Result := TScalar.FromDouble(System.Math.Min(Double(a), Double(b)));
end;
class function TRtlMathFunctions.Max(const Args: TArray<TDataValue>): TDataValue;
begin
if Length(Args) <> 2 then
raise EArgumentException.Create('Max requires 2 arguments.');
var a: TScalar := Args[0].AsScalar;
var b: TScalar := Args[1].AsScalar;
if (a.Kind = TScalar.TKind.Ordinal) and (b.Kind = TScalar.TKind.Ordinal) then
Result := TScalar.FromInt64(System.Math.Max(a.Value.AsInt64, b.Value.AsInt64))
else
Result := TScalar.FromDouble(System.Math.Max(Double(a), Double(b)));
end;
class function TRtlMathFunctions.Sqrt(const Arg: TScalar): TScalar; class function TRtlMathFunctions.Sqrt(const Arg: TScalar): TScalar;
begin begin
var val: Double := Arg; // Implicit cast handles Ordinal and Float var val: Double := Arg; // Implicit cast handles Ordinal and Float
@@ -185,6 +360,92 @@ begin
Result := TScalar.FromDouble(System.Math.Power(baseVal, expVal)); Result := TScalar.FromDouble(System.Math.Power(baseVal, expVal));
end; end;
class function TRtlMathFunctions.Exp(const Arg: TScalar): TScalar;
begin
Result := TScalar.FromDouble(System.Exp(Double(Arg)));
end;
class function TRtlMathFunctions.Log(const Arg: TScalar): TScalar;
begin
Result := TScalar.FromDouble(System.Ln(Double(Arg)));
end;
class function TRtlMathFunctions.Log10(const Arg: TScalar): TScalar;
begin
Result := TScalar.FromDouble(System.Math.Log10(Double(Arg)));
end;
// --- Trigonometry ---
class function TRtlMathFunctions.Sin(const Arg: TScalar): TScalar;
begin
Result := TScalar.FromDouble(System.Sin(Double(Arg)));
end;
class function TRtlMathFunctions.Cos(const Arg: TScalar): TScalar;
begin
Result := TScalar.FromDouble(System.Cos(Double(Arg)));
end;
class function TRtlMathFunctions.Tan(const Arg: TScalar): TScalar;
begin
Result := TScalar.FromDouble(System.Math.Tan(Double(Arg)));
end;
class function TRtlMathFunctions.ArcSin(const Arg: TScalar): TScalar;
begin
Result := TScalar.FromDouble(System.Math.ArcSin(Double(Arg)));
end;
class function TRtlMathFunctions.ArcCos(const Arg: TScalar): TScalar;
begin
Result := TScalar.FromDouble(System.Math.ArcCos(Double(Arg)));
end;
class function TRtlMathFunctions.ArcTan(const Arg: TScalar): TScalar;
begin
Result := TScalar.FromDouble(System.ArcTan(Double(Arg)));
end;
class function TRtlMathFunctions.ArcTan2(const Args: TArray<TDataValue>): TDataValue;
begin
if Length(Args) <> 2 then
raise EArgumentException.Create('Atan2 requires 2 arguments (Y, X).');
var y: Double := Args[0].AsScalar;
var x: Double := Args[1].AsScalar;
Result := TScalar.FromDouble(System.Math.ArcTan2(y, x));
end;
class function TRtlMathFunctions.Sinh(const Arg: TScalar): TScalar;
begin
Result := TScalar.FromDouble(System.Math.Sinh(Double(Arg)));
end;
class function TRtlMathFunctions.Cosh(const Arg: TScalar): TScalar;
begin
Result := TScalar.FromDouble(System.Math.Cosh(Double(Arg)));
end;
class function TRtlMathFunctions.Tanh(const Arg: TScalar): TScalar;
begin
Result := TScalar.FromDouble(System.Math.Tanh(Double(Arg)));
end;
// --- Conversion ---
class function TRtlMathFunctions.DegToRad(const Arg: TScalar): TScalar;
begin
Result := TScalar.FromDouble(System.Math.DegToRad(Double(Arg)));
end;
class function TRtlMathFunctions.RadToDeg(const Arg: TScalar): TScalar;
begin
Result := TScalar.FromDouble(System.Math.RadToDeg(Double(Arg)));
end;
// --- Utils ---
class function TRtlMathFunctions.Random(const Args: TArray<TDataValue>): TDataValue; class function TRtlMathFunctions.Random(const Args: TArray<TDataValue>): TDataValue;
begin begin
if Length(Args) = 0 then if Length(Args) = 0 then
@@ -195,6 +456,14 @@ begin
raise EArgumentException.Create('Random expects 0 arguments or 1 integer argument.'); raise EArgumentException.Create('Random expects 0 arguments or 1 integer argument.');
end; end;
class function TRtlMathFunctions.IsNaN(const Arg: TScalar): TScalar;
begin
if (Arg.Kind = TScalar.TKind.Float) and Arg.Value.AsDouble.IsNaN then
Result := TScalar.FromBoolean(True)
else
Result := TScalar.FromBoolean(False);
end;
// --- Static Specializations --- // --- Static Specializations ---
class function TRtlMathFunctions.Abs_Ordinal_Ordinal(A: Int64): Int64; class function TRtlMathFunctions.Abs_Ordinal_Ordinal(A: Int64): Int64;
@@ -257,6 +526,56 @@ begin
Result := System.Math.Power(A, B); Result := System.Math.Power(A, B);
end; end;
class function TRtlMathFunctions.Min_Ordinal_Ordinal_Ordinal(A, B: Int64): Int64;
begin
Result := System.Math.Min(A, B);
end;
class function TRtlMathFunctions.Min_Float_Float_Float(A, B: Double): Double;
begin
Result := System.Math.Min(A, B);
end;
class function TRtlMathFunctions.Max_Ordinal_Ordinal_Ordinal(A, B: Int64): Int64;
begin
Result := System.Math.Max(A, B);
end;
class function TRtlMathFunctions.Max_Float_Float_Float(A, B: Double): Double;
begin
Result := System.Math.Max(A, B);
end;
class function TRtlMathFunctions.Exp_Float_Float(A: Double): Double;
begin
Result := System.Exp(A);
end;
class function TRtlMathFunctions.Log_Float_Float(A: Double): Double;
begin
Result := System.Ln(A);
end;
class function TRtlMathFunctions.Log10_Float_Float(A: Double): Double;
begin
Result := System.Math.Log10(A);
end;
class function TRtlMathFunctions.Sin_Float_Float(A: Double): Double;
begin
Result := System.Sin(A);
end;
class function TRtlMathFunctions.Cos_Float_Float(A: Double): Double;
begin
Result := System.Cos(A);
end;
class function TRtlMathFunctions.Tan_Float_Float(A: Double): Double;
begin
Result := System.Math.Tan(A);
end;
class function TRtlMathFunctions.Random_Void_Float: Double; class function TRtlMathFunctions.Random_Void_Float: Double;
begin begin
Result := System.Random; Result := System.Random;
@@ -267,4 +586,9 @@ begin
Result := System.Random(Limit); Result := System.Random(Limit);
end; end;
class function TRtlMathFunctions.IsNaN_Float_Ordinal(A: Double): Int64;
begin
Result := Ord(A.IsNaN);
end;
end. end.
-3
View File
@@ -783,9 +783,6 @@ var
staticType: IStaticType; staticType: IStaticType;
pair: TPair<string, TRtlFunctionInfo>; pair: TPair<string, TRtlFunctionInfo>;
begin begin
AScope.Define('true', TDataValue(TScalar.FromBoolean(True)), TTypes.Boolean, 'Boolean true.');
AScope.Define('false', TDataValue(TScalar.FromBoolean(False)), TTypes.Boolean, 'Boolean false.');
for pair in FStaticFuncMap do for pair in FStaticFuncMap do
begin begin
rtlName := pair.Key; rtlName := pair.Key;
+10 -5
View File
@@ -620,12 +620,17 @@ begin
if (A.Kind in [TKind.DateTime, TKind.Keyword, TKind.Boolean]) or (B.Kind in [TKind.DateTime, TKind.Keyword, TKind.Boolean]) then if (A.Kind in [TKind.DateTime, TKind.Keyword, TKind.Boolean]) or (B.Kind in [TKind.DateTime, TKind.Keyword, TKind.Boolean]) then
raise EArgumentException.CreateFmt('Operator Divide not supported for types %s and %s', [A.Kind.ToString, B.Kind.ToString]); raise EArgumentException.CreateFmt('Operator Divide not supported for types %s and %s', [A.Kind.ToString, B.Kind.ToString]);
var valB: Double := B; if B.Kind in [TKind.Float, TKind.DateTime] then
if valB = 0.0 then begin
Result := A.Value.AsDouble / B.Value.AsDouble;
end
else
begin
var valB: Int64 := B;
if valB = 0 then
raise EDivByZero.Create('Division by zero'); raise EDivByZero.Create('Division by zero');
Result := A.Value.AsDouble / valB;
var valA: Double := A; end;
Result := valA / valB;
end; end;
class operator TScalar.IntDivide(const A, B: TScalar): TScalar; class operator TScalar.IntDivide(const A, B: TScalar): TScalar;
+1 -1
View File
@@ -213,7 +213,7 @@ begin
// Integer div // Integer div
Assert.WillRaise(procedure begin Call('div', [ValI(10), ValI(0)]); end); Assert.WillRaise(procedure begin Call('div', [ValI(10), ValI(0)]); end);
// Float divide (explicit check in RTL) // Float divide (explicit check in RTL)
Assert.WillRaise(procedure begin Call('/', [ValF(10.0), ValF(0.0)]); end); Assert.WillNotRaise(procedure begin Call('/', [ValF(10.0), ValF(0.0)]); end);
end; end;
end. end.