Scalar operators

This commit is contained in:
Michael Schimmel
2025-09-15 16:10:01 +02:00
parent 3ad004a895
commit 101dbec760
2 changed files with 794 additions and 0 deletions
+76
View File
@@ -20,6 +20,13 @@ type
class operator Equal(const A, B: TDecimal): Boolean; inline; class operator Equal(const A, B: TDecimal): Boolean; inline;
class operator NotEqual(const A, B: TDecimal): Boolean; inline; class operator NotEqual(const A, B: TDecimal): Boolean; inline;
class operator GreaterThan(const A, B: TDecimal): Boolean; inline;
class operator GreaterThanOrEqual(const A, B: TDecimal): Boolean; inline;
class operator LessThan(const A, B: TDecimal): Boolean; inline;
class operator LessThanOrEqual(const A, B: TDecimal): Boolean; inline;
class operator Negative(const A: TDecimal): TDecimal; inline;
class operator Round(const A: TDecimal): TDecimal;
class operator Trunc(const A: TDecimal): TDecimal;
class operator Implicit(const A: Int64): TDecimal; inline; class operator Implicit(const A: Int64): TDecimal; inline;
class operator Explicit(const A: TDecimal): Double; class operator Explicit(const A: TDecimal): Double;
@@ -246,6 +253,75 @@ begin
Result := not (A = B); Result := not (A = B);
end; end;
class operator TDecimal.GreaterThan(const A, B: TDecimal): Boolean;
begin
var aScale := A.GetScale;
var bScale := B.GetScale;
// Fast path for identical scales
if aScale = bScale then
begin
Result := (A.GetValue > B.GetValue);
exit;
end;
// Slower path: align scales before comparing values
if aScale > bScale then
begin
var scaledB := TDecimal.Create(B, aScale);
Result := (A.GetValue > scaledB.GetValue);
end
else // bScale > aScale
begin
var scaledA := TDecimal.Create(A, bScale);
Result := (scaledA.GetValue > B.GetValue);
end;
end;
class operator TDecimal.GreaterThanOrEqual(const A, B: TDecimal): Boolean;
begin
// Reuse existing operators for consistency
Result := not (A < B);
end;
class operator TDecimal.LessThan(const A, B: TDecimal): Boolean;
begin
// Reuse existing operators for consistency
Result := (B > A);
end;
class operator TDecimal.LessThanOrEqual(const A, B: TDecimal): Boolean;
begin
// Reuse existing operators for consistency
Result := not (A > B);
end;
class operator TDecimal.Negative(const A: TDecimal): TDecimal;
begin
// Negate the value, keep the scale
Result := TDecimal.Create(-A.GetValue, A.GetScale);
end;
class operator TDecimal.Round(const A: TDecimal): TDecimal;
var
value: Int64;
divisor: Int64;
begin
// Rounds to the nearest whole number (scale 0) using arithmetic rounding.
value := A.GetValue;
divisor := PowersOf10[A.GetScale];
if value >= 0 then
Result := TDecimal.Create((value + divisor div 2) div divisor, 0)
else
Result := TDecimal.Create((value - divisor div 2) div divisor, 0);
end;
class operator TDecimal.Trunc(const A: TDecimal): TDecimal;
begin
// Truncates the fractional part, resulting in a decimal with scale 0
Result := TDecimal.Create(A.GetValue div PowersOf10[A.GetScale], 0);
end;
class operator TDecimal.Implicit(const A: Int64): TDecimal; class operator TDecimal.Implicit(const A: Int64): TDecimal;
begin begin
// Correctly create a TDecimal with scale 0 // Correctly create a TDecimal with scale 0
+718
View File
@@ -93,6 +93,22 @@ type
class function StringToKind(const AName: string): TScalarKind; static; class function StringToKind(const AName: string): TScalarKind; static;
function ToString: String; function ToString: String;
class operator Add(const A, B: TScalar): TScalar;
class operator Divide(const A, B: TScalar): TScalar;
class operator Equal(const A, B: TScalar): Boolean;
//TODO implementieren
class operator GreaterThan(const A, B: TScalar): Boolean;
class operator GreaterThanOrEqual(const A, B: TScalar): Boolean;
class operator LessThan(const A, B: TScalar): Boolean;
class operator LessThanOrEqual(const A, B: TScalar): Boolean;
class operator Multiply(const A, B: TScalar): TScalar;
class operator Negative(const A: TScalar): TScalar;
class operator NotEqual(const A, B: TScalar): Boolean;
class operator Round(const A: TScalar): TScalar;
class operator Subtract(const A, B: TScalar): TScalar;
class operator Trunc(const A: TScalar): TScalar;
end; end;
TScalarKindHelper = record helper for TScalarKind TScalarKindHelper = record helper for TScalarKind
@@ -443,6 +459,708 @@ begin
end; end;
end; end;
class operator TScalar.Add(const A, B: TScalar): TScalar;
begin
// Fast path for identical types
if (A.Kind = B.Kind) then
begin
Result.Kind := A.Kind;
case A.Kind of
skInteger: Result.Value.AsInteger := A.Value.AsInteger + B.Value.AsInteger;
skInt64: Result.Value.AsInt64 := A.Value.AsInt64 + B.Value.AsInt64;
skUInt64: Result.Value.AsUInt64 := A.Value.AsUInt64 + B.Value.AsUInt64;
skSingle: Result.Value.AsSingle := A.Value.AsSingle + B.Value.AsSingle;
skDouble: Result.Value.AsDouble := A.Value.AsDouble + B.Value.AsDouble;
skDecimal: Result.Value.AsDecimal := A.Value.AsDecimal + B.Value.AsDecimal;
else
raise EArgumentException.CreateFmt('Operator Add not supported for type %s', [A.Kind.ToString]);
end;
exit;
end;
// Slow path for mixed types with compatible conversion
const FLOAT_KINDS: set of TScalarKind = [skSingle, skDouble];
const ORDINAL_KINDS: set of TScalarKind = [skInteger, skInt64, skUInt64];
const NUMERIC_KINDS: set of TScalarKind = FLOAT_KINDS + ORDINAL_KINDS + [skDecimal];
if not (((A.Kind in NUMERIC_KINDS) and (B.Kind in NUMERIC_KINDS))
or ((A.Kind = skDateTime) and (B.Kind in ORDINAL_KINDS + FLOAT_KINDS))
or ((B.Kind = skDateTime) and (A.Kind in ORDINAL_KINDS + FLOAT_KINDS))) then
raise EArgumentException.CreateFmt('Operator Add requires compatible types, but got %s and %s', [A.Kind.ToString, B.Kind.ToString]);
if (A.Kind = skDecimal) or (B.Kind = skDecimal) then
begin
if (A.Kind in FLOAT_KINDS) or (B.Kind in FLOAT_KINDS) then
raise EArgumentException
.CreateFmt('Operator Add cannot mix Decimal and floating-point types (%s, %s)', [A.Kind.ToString, B.Kind.ToString]);
var valA, valB: TDecimal;
case A.Kind of
skInteger: valA := TDecimal.Create(A.Value.AsInteger, 0);
skInt64: valA := TDecimal.Create(A.Value.AsInt64, 0);
skUInt64: valA := TDecimal.Create(Int64(A.Value.AsUInt64), 0);
skDecimal: valA := A.Value.AsDecimal;
end;
case B.Kind of
skInteger: valB := TDecimal.Create(B.Value.AsInteger, 0);
skInt64: valB := TDecimal.Create(B.Value.AsInt64, 0);
skUInt64: valB := TDecimal.Create(Int64(B.Value.AsUInt64), 0);
skDecimal: valB := B.Value.AsDecimal;
end;
Result := TScalar.FromDecimal(valA + valB);
end
else if (A.Kind = skDateTime) or (B.Kind = skDateTime) then
begin
if A.Kind = skDateTime then
begin
var dtPart := A.Value.AsDateTime;
var numPart: Double := 0;
case B.Kind of
skInteger: numPart := B.Value.AsInteger;
skInt64: numPart := B.Value.AsInt64;
skUInt64: numPart := B.Value.AsUInt64;
skSingle: numPart := B.Value.AsSingle;
skDouble: numPart := B.Value.AsDouble;
end;
Result := TScalar.FromDateTime(dtPart + numPart);
end
else
begin
var dtPart := B.Value.AsDateTime;
var numPart: Double := 0;
case A.Kind of
skInteger: numPart := A.Value.AsInteger;
skInt64: numPart := A.Value.AsInt64;
skUInt64: numPart := A.Value.AsUInt64;
skSingle: numPart := A.Value.AsSingle;
skDouble: numPart := A.Value.AsDouble;
end;
Result := TScalar.FromDateTime(dtPart + numPart);
end;
end
else if (A.Kind = skDouble) or (B.Kind = skDouble) then
begin
var dblA: Double := 0;
var dblB: Double := 0;
case A.Kind of
skInteger: dblA := A.Value.AsInteger;
skInt64: dblA := A.Value.AsInt64;
skUInt64: dblA := A.Value.AsUInt64;
skSingle: dblA := A.Value.AsSingle;
skDouble: dblA := A.Value.AsDouble;
end;
case B.Kind of
skInteger: dblB := B.Value.AsInteger;
skInt64: dblB := B.Value.AsInt64;
skUInt64: dblB := B.Value.AsUInt64;
skSingle: dblB := B.Value.AsSingle;
skDouble: dblB := B.Value.AsDouble;
end;
Result := TScalar.FromDouble(dblA + dblB);
end
else if (A.Kind = skSingle) or (B.Kind = skSingle) then
begin
var sngA: Single := 0;
var sngB: Single := 0;
case A.Kind of
skInteger: sngA := A.Value.AsInteger;
skInt64: sngA := A.Value.AsInt64;
skUInt64: sngA := A.Value.AsUInt64;
skSingle: sngA := A.Value.AsSingle;
end;
case B.Kind of
skInteger: sngB := B.Value.AsInteger;
skInt64: sngB := B.Value.AsInt64;
skUInt64: sngB := B.Value.AsUInt64;
skSingle: sngB := B.Value.AsSingle;
end;
Result := TScalar.FromSingle(sngA + sngB);
end
else if (A.Kind = skUInt64) or (B.Kind = skUInt64) then
begin
var u64A: UInt64 := 0;
var u64B: UInt64 := 0;
case A.Kind of
skInteger: u64A := UInt64(A.Value.AsInteger);
skInt64: u64A := UInt64(A.Value.AsInt64);
skUInt64: u64A := A.Value.AsUInt64;
end;
case B.Kind of
skInteger: u64B := UInt64(B.Value.AsInteger);
skInt64: u64B := UInt64(B.Value.AsInt64);
skUInt64: u64B := B.Value.AsUInt64;
end;
Result := TScalar.FromUInt64(u64A + u64B);
end
else if (A.Kind = skInt64) or (B.Kind = skInt64) then
begin
var i64A: Int64 := 0;
var i64B: Int64 := 0;
case A.Kind of
skInteger: i64A := A.Value.AsInteger;
skInt64: i64A := A.Value.AsInt64;
end;
case B.Kind of
skInteger: i64B := B.Value.AsInteger;
skInt64: i64B := B.Value.AsInt64;
end;
Result := TScalar.FromInt64(i64A + i64B);
end;
end;
class operator TScalar.Divide(const A, B: TScalar): TScalar;
begin
// Fast path for identical types
if (A.Kind = B.Kind) then
begin
case A.Kind of
skInteger: Result := TScalar.FromDouble(A.Value.AsInteger / B.Value.AsInteger);
skInt64: Result := TScalar.FromDouble(A.Value.AsInt64 / B.Value.AsInt64);
skUInt64: Result := TScalar.FromDouble(A.Value.AsUInt64 / B.Value.AsUInt64);
skSingle: Result := TScalar.FromSingle(A.Value.AsSingle / B.Value.AsSingle);
skDouble: Result := TScalar.FromDouble(A.Value.AsDouble / B.Value.AsDouble);
skDecimal: Result := TScalar.FromDecimal(A.Value.AsDecimal / B.Value.AsDecimal);
else
raise EArgumentException.CreateFmt('Operator Divide not supported for type %s', [A.Kind.ToString]);
end;
exit;
end;
// Slow path for mixed types
const FLOAT_KINDS: set of TScalarKind = [skSingle, skDouble];
const NUMERIC_KINDS: set of TScalarKind = FLOAT_KINDS + [skInteger, skInt64, skUInt64, skDecimal];
if not ((A.Kind in NUMERIC_KINDS) and (B.Kind in NUMERIC_KINDS)) then
raise EArgumentException.CreateFmt('Operator Divide not supported for types %s and %s', [A.Kind.ToString, B.Kind.ToString]);
if (A.Kind = skDecimal) or (B.Kind = skDecimal) then
begin
if (A.Kind in FLOAT_KINDS) or (B.Kind in FLOAT_KINDS) then
raise EArgumentException
.CreateFmt('Operator Divide cannot mix Decimal and floating-point types (%s, %s)', [A.Kind.ToString, B.Kind.ToString]);
var valA, valB: TDecimal;
case A.Kind of
skInteger: valA := TDecimal.Create(A.Value.AsInteger, 0);
skInt64: valA := TDecimal.Create(A.Value.AsInt64, 0);
skUInt64: valA := TDecimal.Create(Int64(A.Value.AsUInt64), 0);
skDecimal: valA := A.Value.AsDecimal;
end;
case B.Kind of
skInteger: valB := TDecimal.Create(B.Value.AsInteger, 0);
skInt64: valB := TDecimal.Create(B.Value.AsInt64, 0);
skUInt64: valB := TDecimal.Create(Int64(B.Value.AsUInt64), 0);
skDecimal: valB := B.Value.AsDecimal;
end;
Result := TScalar.FromDecimal(valA / valB);
end
else
begin
var dblA: Double := 0.0;
var dblB: Double := 0.0;
case A.Kind of
skInteger: dblA := A.Value.AsInteger;
skInt64: dblA := A.Value.AsInt64;
skUInt64: dblA := A.Value.AsUInt64;
skSingle: dblA := A.Value.AsSingle;
skDouble: dblA := A.Value.AsDouble;
end;
case B.Kind of
skInteger: dblB := B.Value.AsInteger;
skInt64: dblB := B.Value.AsInt64;
skUInt64: dblB := B.Value.AsUInt64;
skSingle: dblB := B.Value.AsSingle;
skDouble: dblB := B.Value.AsDouble;
end;
Result := TScalar.FromDouble(dblA / dblB);
end;
end;
class operator TScalar.Equal(const A, B: TScalar): Boolean;
begin
// Fast path for identical types
if (A.Kind = B.Kind) then
begin
case A.Kind of
skInteger: Result := (A.Value.AsInteger = B.Value.AsInteger);
skInt64: Result := (A.Value.AsInt64 = B.Value.AsInt64);
skUInt64: Result := (A.Value.AsUInt64 = B.Value.AsUInt64);
skSingle: Result := (A.Value.AsSingle = B.Value.AsSingle);
skDouble: Result := (A.Value.AsDouble = B.Value.AsDouble);
skDateTime: Result := (A.Value.AsDateTime = B.Value.AsDateTime);
skTimestamp:
Result := (A.Value.AsTimestamp.Time = B.Value.AsTimestamp.Time) and (A.Value.AsTimestamp.Date = B.Value.AsTimestamp.Date);
skBoolean: Result := (A.Value.AsBoolean = B.Value.AsBoolean);
skChar: Result := (A.Value.AsChar = B.Value.AsChar);
skPChar: Result := (StrComp(PChar(@A.Value.AsPChar), PChar(@B.Value.AsPChar)) = 0);
skString: Result := (A.Value.AsString = B.Value.AsString);
skBytes: Result := CompareMem(@A.Value.AsBytes, @B.Value.AsBytes, SizeOf(TScalarBytes));
skDecimal: Result := (A.Value.AsDecimal = B.Value.AsDecimal);
else
Result := False;
end;
exit;
end;
// Slow path for mixed, but compatible, types
const FLOAT_KINDS: set of TScalarKind = [skSingle, skDouble];
const NUMERIC_KINDS: set of TScalarKind = FLOAT_KINDS + [skInteger, skInt64, skUInt64, skDecimal];
if not ((A.Kind in NUMERIC_KINDS) and (B.Kind in NUMERIC_KINDS)) then
begin
Result := False;
exit;
end;
if ((A.Kind = skDecimal) and (B.Kind in FLOAT_KINDS)) or ((B.Kind = skDecimal) and (A.Kind in FLOAT_KINDS)) then
begin
Result := False;
exit;
end;
if (A.Kind = skDecimal) or (B.Kind = skDecimal) then
begin
var valA, valB: TDecimal;
case A.Kind of
skInteger: valA := TDecimal.Create(A.Value.AsInteger, 0);
skInt64: valA := TDecimal.Create(A.Value.AsInt64, 0);
skUInt64: valA := TDecimal.Create(Int64(A.Value.AsUInt64), 0);
skDecimal: valA := A.Value.AsDecimal;
else
Result := False;
exit;
end;
case B.Kind of
skInteger: valB := TDecimal.Create(B.Value.AsInteger, 0);
skInt64: valB := TDecimal.Create(B.Value.AsInt64, 0);
skUInt64: valB := TDecimal.Create(Int64(B.Value.AsUInt64), 0);
skDecimal: valB := B.Value.AsDecimal;
else
Result := False;
exit;
end;
Result := (valA = valB);
end
else
begin
var dblA: Double := 0.0;
var dblB: Double := 0.0;
case A.Kind of
skInteger: dblA := A.Value.AsInteger;
skInt64: dblA := A.Value.AsInt64;
skUInt64: dblA := A.Value.AsUInt64;
skSingle: dblA := A.Value.AsSingle;
skDouble: dblA := A.Value.AsDouble;
end;
case B.Kind of
skInteger: dblB := B.Value.AsInteger;
skInt64: dblB := B.Value.AsInt64;
skUInt64: dblB := B.Value.AsUInt64;
skSingle: dblB := B.Value.AsSingle;
skDouble: dblB := B.Value.AsDouble;
end;
Result := (dblA = dblB);
end;
end;
class operator TScalar.GreaterThan(const A, B: TScalar): Boolean;
begin
// Fast path for identical types
if (A.Kind = B.Kind) then
begin
case A.Kind of
skInteger: Result := (A.Value.AsInteger > B.Value.AsInteger);
skInt64: Result := (A.Value.AsInt64 > B.Value.AsInt64);
skUInt64: Result := (A.Value.AsUInt64 > B.Value.AsUInt64);
skSingle: Result := (A.Value.AsSingle > B.Value.AsSingle);
skDouble: Result := (A.Value.AsDouble > B.Value.AsDouble);
skDateTime: Result := (A.Value.AsDateTime > B.Value.AsDateTime);
skDecimal: Result := (A.Value.AsDecimal > B.Value.AsDecimal);
skChar: Result := (A.Value.AsChar > B.Value.AsChar);
skString: Result := (A.Value.AsString > B.Value.AsString);
else
raise EArgumentException.CreateFmt('Operator GreaterThan not supported for type %s', [A.Kind.ToString]);
end;
exit;
end;
// Slow path for mixed, but compatible, types
const FLOAT_KINDS: set of TScalarKind = [skSingle, skDouble];
const NUMERIC_KINDS: set of TScalarKind = FLOAT_KINDS + [skInteger, skInt64, skUInt64, skDecimal];
if not ((A.Kind in NUMERIC_KINDS) and (B.Kind in NUMERIC_KINDS)) then
raise EArgumentException.CreateFmt('Cannot compare %s and %s', [A.Kind.ToString, B.Kind.ToString]);
if ((A.Kind = skDecimal) and (B.Kind in FLOAT_KINDS)) or ((B.Kind = skDecimal) and (A.Kind in FLOAT_KINDS)) then
raise EArgumentException.CreateFmt('Cannot compare Decimal and floating-point types (%s, %s)', [A.Kind.ToString, B.Kind.ToString]);
if (A.Kind = skDecimal) or (B.Kind = skDecimal) then
begin
var valA, valB: TDecimal;
case A.Kind of
skInteger: valA := TDecimal.Create(A.Value.AsInteger, 0);
skInt64: valA := TDecimal.Create(A.Value.AsInt64, 0);
skUInt64: valA := TDecimal.Create(Int64(A.Value.AsUInt64), 0);
skDecimal: valA := A.Value.AsDecimal;
else
raise EArgumentException.CreateFmt('Internal error comparing %s', [A.Kind.ToString]);
end;
case B.Kind of
skInteger: valB := TDecimal.Create(B.Value.AsInteger, 0);
skInt64: valB := TDecimal.Create(B.Value.AsInt64, 0);
skUInt64: valB := TDecimal.Create(Int64(B.Value.AsUInt64), 0);
skDecimal: valB := B.Value.AsDecimal;
else
raise EArgumentException.CreateFmt('Internal error comparing %s', [B.Kind.ToString]);
end;
Result := (valA > valB);
end
else
begin
var dblA, dblB: Double;
dblA := 0.0;
dblB := 0.0;
case A.Kind of
skInteger: dblA := A.Value.AsInteger;
skInt64: dblA := A.Value.AsInt64;
skUInt64: dblA := A.Value.AsUInt64;
skSingle: dblA := A.Value.AsSingle;
skDouble: dblA := A.Value.AsDouble;
end;
case B.Kind of
skInteger: dblB := B.Value.AsInteger;
skInt64: dblB := B.Value.AsInt64;
skUInt64: dblB := B.Value.AsUInt64;
skSingle: dblB := B.Value.AsSingle;
skDouble: dblB := B.Value.AsDouble;
end;
Result := (dblA > dblB);
end;
end;
class operator TScalar.GreaterThanOrEqual(const A, B: TScalar): Boolean;
begin
Result := (A > B) or (A = B);
end;
class operator TScalar.LessThan(const A, B: TScalar): Boolean;
begin
// Re-use GreaterThan logic to avoid code duplication
Result := B > A;
end;
class operator TScalar.LessThanOrEqual(const A, B: TScalar): Boolean;
begin
Result := (A < B) or (A = B);
end;
class operator TScalar.Multiply(const A, B: TScalar): TScalar;
begin
// Fast path for identical types
if (A.Kind = B.Kind) then
begin
Result.Kind := A.Kind;
case A.Kind of
skInteger: Result.Value.AsInteger := A.Value.AsInteger * B.Value.AsInteger;
skInt64: Result.Value.AsInt64 := A.Value.AsInt64 * B.Value.AsInt64;
skUInt64: Result.Value.AsUInt64 := A.Value.AsUInt64 * B.Value.AsUInt64;
skSingle: Result.Value.AsSingle := A.Value.AsSingle * B.Value.AsSingle;
skDouble: Result.Value.AsDouble := A.Value.AsDouble * B.Value.AsDouble;
skDecimal: Result.Value.AsDecimal := A.Value.AsDecimal * B.Value.AsDecimal;
else
raise EArgumentException.CreateFmt('Operator Multiply not supported for type %s', [A.Kind.ToString]);
end;
exit;
end;
// Slow path for mixed types
const FLOAT_KINDS: set of TScalarKind = [skSingle, skDouble];
const NUMERIC_KINDS: set of TScalarKind = FLOAT_KINDS + [skInteger, skInt64, skUInt64, skDecimal];
if not ((A.Kind in NUMERIC_KINDS) and (B.Kind in NUMERIC_KINDS)) then
raise EArgumentException.CreateFmt('Operator Multiply not supported for types %s and %s', [A.Kind.ToString, B.Kind.ToString]);
if (A.Kind = skDecimal) or (B.Kind = skDecimal) then
begin
if (A.Kind in FLOAT_KINDS) or (B.Kind in FLOAT_KINDS) then
raise EArgumentException
.CreateFmt('Operator Multiply cannot mix Decimal and floating-point types (%s, %s)', [A.Kind.ToString, B.Kind.ToString]);
var valA, valB: TDecimal;
case A.Kind of
skInteger: valA := TDecimal.Create(A.Value.AsInteger, 0);
skInt64: valA := TDecimal.Create(A.Value.AsInt64, 0);
skUInt64: valA := TDecimal.Create(Int64(A.Value.AsUInt64), 0);
skDecimal: valA := A.Value.AsDecimal;
end;
case B.Kind of
skInteger: valB := TDecimal.Create(B.Value.AsInteger, 0);
skInt64: valB := TDecimal.Create(B.Value.AsInt64, 0);
skUInt64: valB := TDecimal.Create(Int64(B.Value.AsUInt64), 0);
skDecimal: valB := B.Value.AsDecimal;
end;
Result := TScalar.FromDecimal(valA * valB);
end
else if (A.Kind = skDouble) or (B.Kind = skDouble) then
begin
var dblA: Double := 0.0;
var dblB: Double := 0.0;
case A.Kind of
skInteger: dblA := A.Value.AsInteger;
skInt64: dblA := A.Value.AsInt64;
skUInt64: dblA := A.Value.AsUInt64;
skSingle: dblA := A.Value.AsSingle;
skDouble: dblA := A.Value.AsDouble;
end;
case B.Kind of
skInteger: dblB := B.Value.AsInteger;
skInt64: dblB := B.Value.AsInt64;
skUInt64: dblB := B.Value.AsUInt64;
skSingle: dblB := B.Value.AsSingle;
skDouble: dblB := B.Value.AsDouble;
end;
Result := TScalar.FromDouble(dblA * dblB);
end
else if (A.Kind = skSingle) or (B.Kind = skSingle) then
begin
var sngA: Single := 0.0;
var sngB: Single := 0.0;
case A.Kind of
skInteger: sngA := A.Value.AsInteger;
skInt64: sngA := A.Value.AsInt64;
skUInt64: sngA := A.Value.AsUInt64;
skSingle: sngA := A.Value.AsSingle;
end;
case B.Kind of
skInteger: sngB := B.Value.AsInteger;
skInt64: sngB := B.Value.AsInt64;
skUInt64: sngB := B.Value.AsUInt64;
skSingle: sngB := B.Value.AsSingle;
end;
Result := TScalar.FromSingle(sngA * sngB);
end
else if (A.Kind = skUInt64) or (B.Kind = skUInt64) then
begin
var u64A: UInt64 := 0;
var u64B: UInt64 := 0;
case A.Kind of
skInteger: u64A := UInt64(A.Value.AsInteger);
skInt64: u64A := UInt64(A.Value.AsInt64);
skUInt64: u64A := A.Value.AsUInt64;
end;
case B.Kind of
skInteger: u64B := UInt64(B.Value.AsInteger);
skInt64: u64B := UInt64(B.Value.AsInt64);
skUInt64: u64B := B.Value.AsUInt64;
end;
Result := TScalar.FromUInt64(u64A * u64B);
end
else if (A.Kind = skInt64) or (B.Kind = skInt64) then
begin
var i64A: Int64 := 0;
var i64B: Int64 := 0;
case A.Kind of
skInteger: i64A := A.Value.AsInteger;
skInt64: i64A := A.Value.AsInt64;
end;
case B.Kind of
skInteger: i64B := B.Value.AsInteger;
skInt64: i64B := B.Value.AsInt64;
end;
Result := TScalar.FromInt64(i64A * i64B);
end
else
begin
Result := TScalar.FromInteger(A.Value.AsInteger * B.Value.AsInteger);
end;
end;
class operator TScalar.Negative(const A: TScalar): TScalar;
begin
Result.Kind := A.Kind;
case A.Kind of
skInteger: Result.Value.AsInteger := -A.Value.AsInteger;
skInt64: Result.Value.AsInt64 := -A.Value.AsInt64;
skSingle: Result.Value.AsSingle := -A.Value.AsSingle;
skDouble: Result.Value.AsDouble := -A.Value.AsDouble;
skDecimal: Result.Value.AsDecimal := -A.Value.AsDecimal;
else
raise EArgumentException.CreateFmt('Operator Negative not supported for type %s', [A.Kind.ToString]);
end;
end;
class operator TScalar.NotEqual(const A, B: TScalar): Boolean;
begin
Result := not (A = B);
end;
class operator TScalar.Round(const A: TScalar): TScalar;
begin
case A.Kind of
skSingle: Result := TScalar.FromInt64(System.Round(A.Value.AsSingle));
skDouble: Result := TScalar.FromInt64(System.Round(A.Value.AsDouble));
skDateTime: Result := TScalar.FromInt64(System.Round(A.Value.AsDateTime));
else
raise EArgumentException.CreateFmt('Operator Round not supported for type %s', [A.Kind.ToString]);
end;
end;
class operator TScalar.Subtract(const A, B: TScalar): TScalar;
begin
// Fast path for identical types
if (A.Kind = B.Kind) then
begin
if (A.Kind = skDateTime) then
begin
Result := TScalar.FromDouble(A.Value.AsDateTime - B.Value.AsDateTime);
exit;
end;
Result.Kind := A.Kind;
case A.Kind of
skInteger: Result.Value.AsInteger := A.Value.AsInteger - B.Value.AsInteger;
skInt64: Result.Value.AsInt64 := A.Value.AsInt64 - B.Value.AsInt64;
skUInt64: Result.Value.AsUInt64 := A.Value.AsUInt64 - B.Value.AsUInt64;
skSingle: Result.Value.AsSingle := A.Value.AsSingle - B.Value.AsSingle;
skDouble: Result.Value.AsDouble := A.Value.AsDouble - B.Value.AsDouble;
skDecimal: Result.Value.AsDecimal := A.Value.AsDecimal - B.Value.AsDecimal;
else
raise EArgumentException.CreateFmt('Operator Subtract not supported for type %s', [A.Kind.ToString]);
end;
exit;
end;
// Slow path for mixed types
const FLOAT_KINDS: set of TScalarKind = [skSingle, skDouble];
const ORDINAL_KINDS: set of TScalarKind = [skInteger, skInt64, skUInt64];
const NUMERIC_KINDS: set of TScalarKind = FLOAT_KINDS + ORDINAL_KINDS + [skDecimal];
if not (((A.Kind in NUMERIC_KINDS) and (B.Kind in NUMERIC_KINDS))
or ((A.Kind = skDateTime) and (B.Kind in NUMERIC_KINDS + [skDateTime]))) then
raise EArgumentException.CreateFmt('Operator Subtract not supported for types %s and %s', [A.Kind.ToString, B.Kind.ToString]);
if (A.Kind = skDecimal) or (B.Kind = skDecimal) then
begin
if (A.Kind in FLOAT_KINDS) or (B.Kind in FLOAT_KINDS) then
raise EArgumentException
.CreateFmt('Operator Subtract cannot mix Decimal and floating-point types (%s, %s)', [A.Kind.ToString, B.Kind.ToString]);
var valA, valB: TDecimal;
case A.Kind of
skInteger: valA := TDecimal.Create(A.Value.AsInteger, 0);
skInt64: valA := TDecimal.Create(A.Value.AsInt64, 0);
skUInt64: valA := TDecimal.Create(Int64(A.Value.AsUInt64), 0);
skDecimal: valA := A.Value.AsDecimal;
end;
case B.Kind of
skInteger: valB := TDecimal.Create(B.Value.AsInteger, 0);
skInt64: valB := TDecimal.Create(B.Value.AsInt64, 0);
skUInt64: valB := TDecimal.Create(Int64(B.Value.AsUInt64), 0);
skDecimal: valB := B.Value.AsDecimal;
end;
Result := TScalar.FromDecimal(valA - valB);
end
else if (A.Kind = skDateTime) then
begin
var numPart: Double := 0.0;
case B.Kind of
skInteger: numPart := B.Value.AsInteger;
skInt64: numPart := B.Value.AsInt64;
skUInt64: numPart := B.Value.AsUInt64;
skSingle: numPart := B.Value.AsSingle;
skDouble: numPart := B.Value.AsDouble;
skDateTime: numPart := B.Value.AsDateTime;
end;
if B.Kind = skDateTime then
Result := TScalar.FromDouble(A.Value.AsDateTime - numPart)
else
Result := TScalar.FromDateTime(A.Value.AsDateTime - numPart);
end
else if (B.Kind = skDateTime) then
begin
raise EArgumentException.CreateFmt('Operator Subtract not supported for types %s and %s', [A.Kind.ToString, B.Kind.ToString]);
end
else if (A.Kind = skDouble) or (B.Kind = skDouble) then
begin
var dblA: Double := 0.0;
var dblB: Double := 0.0;
case A.Kind of
skInteger: dblA := A.Value.AsInteger;
skInt64: dblA := A.Value.AsInt64;
skUInt64: dblA := A.Value.AsUInt64;
skSingle: dblA := A.Value.AsSingle;
skDouble: dblA := A.Value.AsDouble;
end;
case B.Kind of
skInteger: dblB := B.Value.AsInteger;
skInt64: dblB := B.Value.AsInt64;
skUInt64: dblB := B.Value.AsUInt64;
skSingle: dblB := B.Value.AsSingle;
skDouble: dblB := B.Value.AsDouble;
end;
Result := TScalar.FromDouble(dblA - dblB);
end
else if (A.Kind = skSingle) or (B.Kind = skSingle) then
begin
var sngA: Single := 0.0;
var sngB: Single := 0.0;
case A.Kind of
skInteger: sngA := A.Value.AsInteger;
skInt64: sngA := A.Value.AsInt64;
skUInt64: sngA := A.Value.AsUInt64;
skSingle: sngA := A.Value.AsSingle;
end;
case B.Kind of
skInteger: sngB := B.Value.AsInteger;
skInt64: sngB := B.Value.AsInt64;
skUInt64: sngB := B.Value.AsUInt64;
skSingle: sngB := B.Value.AsSingle;
end;
Result := TScalar.FromSingle(sngA - sngB);
end
else if (A.Kind = skUInt64) or (B.Kind = skUInt64) then
begin
var u64A: UInt64 := 0;
var u64B: UInt64 := 0;
case A.Kind of
skInteger: u64A := UInt64(A.Value.AsInteger);
skInt64: u64A := UInt64(A.Value.AsInt64);
skUInt64: u64A := A.Value.AsUInt64;
end;
case B.Kind of
skInteger: u64B := UInt64(B.Value.AsInteger);
skInt64: u64B := UInt64(B.Value.AsInt64);
skUInt64: u64B := B.Value.AsUInt64;
end;
Result := TScalar.FromUInt64(u64A - u64B);
end
else if (A.Kind = skInt64) or (B.Kind = skInt64) then
begin
var i64A: Int64 := 0;
var i64B: Int64 := 0;
case A.Kind of
skInteger: i64A := A.Value.AsInteger;
skInt64: i64A := A.Value.AsInt64;
end;
case B.Kind of
skInteger: i64B := B.Value.AsInteger;
skInt64: i64B := B.Value.AsInt64;
end;
Result := TScalar.FromInt64(i64A - i64B);
end
else
begin
Result := TScalar.FromInteger(A.Value.AsInteger - B.Value.AsInteger);
end;
end;
class operator TScalar.Trunc(const A: TScalar): TScalar;
begin
case A.Kind of
skSingle: Result := TScalar.FromInt64(System.Trunc(A.Value.AsSingle));
skDouble: Result := TScalar.FromInt64(System.Trunc(A.Value.AsDouble));
skDateTime: Result := TScalar.FromInt64(System.Trunc(A.Value.AsDateTime));
else
raise EArgumentException.CreateFmt('Operator Trunc not supported for type %s', [A.Kind.ToString]);
end;
end;
{ TScalarArray } { TScalarArray }
constructor TScalarArray.Create(AKind: TScalarKind; const AItems: TArray<TScalarValue>); constructor TScalarArray.Create(AKind: TScalarKind; const AItems: TArray<TScalarValue>);