Keywords as basic scalar type
This commit is contained in:
+129
-132
@@ -6,6 +6,7 @@ uses
|
||||
System.SysUtils,
|
||||
System.Generics.Collections,
|
||||
System.Generics.Defaults,
|
||||
Myc.Utils,
|
||||
Myc.Data.Decimal,
|
||||
Myc.Data.Series,
|
||||
Myc.Data.Keyword;
|
||||
@@ -17,11 +18,13 @@ type
|
||||
TScalar = record
|
||||
public
|
||||
type
|
||||
TKind = (Ordinal, Float);
|
||||
// Defines the underlying storage kinds for scalar values
|
||||
TKind = (Ordinal, Float, Keyword);
|
||||
|
||||
// The 8-byte storage for the scalar value
|
||||
TValue = record
|
||||
case TKind of
|
||||
TKind.Ordinal: (AsInt64: Int64);
|
||||
TKind.Ordinal, TKind.Keyword: (AsInt64: Int64); // Ordinal and Keyword index
|
||||
TKind.Float: (AsDouble: Double);
|
||||
end;
|
||||
|
||||
@@ -51,10 +54,12 @@ type
|
||||
// Factory methods for core types.
|
||||
class function FromInt64(AValue: Int64): TScalar; static; inline;
|
||||
class function FromDouble(AValue: Double): TScalar; static; inline;
|
||||
class function FromKeyword(const AValue: IKeyword): TScalar; static; inline;
|
||||
|
||||
// Implicit casts for core types.
|
||||
class operator Implicit(AValue: Int64): TScalar; overload; inline;
|
||||
class operator Implicit(AValue: Double): TScalar; overload; inline;
|
||||
class operator Implicit(const AValue: IKeyword): TScalar; overload; inline;
|
||||
class operator Implicit(const A: TScalar): Double; overload;
|
||||
class operator Implicit(const A: TScalar): Int64; overload;
|
||||
|
||||
@@ -159,19 +164,17 @@ type
|
||||
// A time series of scalar records, optimized for memory and access speed.
|
||||
TScalarRecordSeries = class(TInterfacedObject, IRecordSeries)
|
||||
type
|
||||
TMemberSeries = class(TObject, ISeries)
|
||||
TMemberSeries = class(TGenericContainedObject<TScalarRecordSeries>, ISeries)
|
||||
private
|
||||
FRecordSeries: TScalarRecordSeries;
|
||||
FKind: TScalar.TKind;
|
||||
FOffset: Integer;
|
||||
function GetCount: Int64;
|
||||
function GetItems(Idx: Integer): TScalar;
|
||||
function GetRecordSeries: TScalarRecordSeries; inline;
|
||||
function GetTotalCount: Int64;
|
||||
function QueryInterface(const IID: TGUID; out Obj): HResult; stdcall;
|
||||
function _AddRef: Integer; stdcall;
|
||||
function _Release: Integer; stdcall;
|
||||
public
|
||||
constructor Create(ARecordSeries: TScalarRecordSeries; AElementIdx: Integer);
|
||||
property RecordSeries: TScalarRecordSeries read GetRecordSeries;
|
||||
end;
|
||||
private
|
||||
FDef: IScalarRecordDefinition;
|
||||
@@ -219,6 +222,20 @@ begin
|
||||
Result.Value.AsDouble := AValue;
|
||||
end;
|
||||
|
||||
class function TScalar.FromInt64(AValue: Int64): TScalar;
|
||||
begin
|
||||
Result.Kind := TKind.Ordinal;
|
||||
Result.Value.AsInt64 := AValue;
|
||||
end;
|
||||
|
||||
class function TScalar.FromKeyword(const AValue: IKeyword): TScalar;
|
||||
begin
|
||||
Result.Kind := TKind.Keyword;
|
||||
if not Assigned(AValue) then
|
||||
raise EArgumentException.Create('Keyword must not be nil.');
|
||||
Result.Value.AsInt64 := AValue.Idx
|
||||
end;
|
||||
|
||||
class operator TScalar.Implicit(AValue: Double): TScalar;
|
||||
begin
|
||||
Result.Kind := TKind.Float;
|
||||
@@ -231,39 +248,58 @@ begin
|
||||
Result.Value.AsInt64 := AValue;
|
||||
end;
|
||||
|
||||
class operator TScalar.Implicit(const AValue: IKeyword): TScalar;
|
||||
begin
|
||||
Result.Kind := TKind.Keyword;
|
||||
if not Assigned(AValue) then
|
||||
raise EArgumentException.Create('Keyword must not be nil.');
|
||||
Result.Value.AsInt64 := AValue.Idx
|
||||
end;
|
||||
|
||||
class operator TScalar.Implicit(const A: TScalar): Double;
|
||||
begin
|
||||
if A.Kind = TKind.Ordinal then
|
||||
Result := A.Value.AsInt64
|
||||
case A.Kind of
|
||||
TKind.Ordinal: Result := A.Value.AsInt64;
|
||||
TKind.Float: Result := A.Value.AsDouble;
|
||||
else
|
||||
Result := A.Value.AsDouble;
|
||||
raise EInvalidCast.Create('Cannot implicitly convert Keyword to Double');
|
||||
end;
|
||||
end;
|
||||
|
||||
class operator TScalar.Implicit(const A: TScalar): Int64;
|
||||
begin
|
||||
if A.Kind = TKind.Ordinal then
|
||||
Result := A.Value.AsInt64
|
||||
case A.Kind of
|
||||
TKind.Ordinal: Result := A.Value.AsInt64;
|
||||
else
|
||||
raise EInvalidCast.Create('Cannot implicitly convert a Float to an Int64');
|
||||
end;
|
||||
|
||||
class function TScalar.FromInt64(AValue: Int64): TScalar;
|
||||
begin
|
||||
Result.Kind := TKind.Ordinal;
|
||||
Result.Value.AsInt64 := AValue;
|
||||
raise EInvalidCast.CreateFmt('Cannot implicitly convert %s to Int64', [A.Kind.ToString]);
|
||||
end;
|
||||
end;
|
||||
|
||||
class function TScalar.IsBinaryOperatorSupported(Op: TBinaryOp; A, B: TKind): Boolean;
|
||||
begin
|
||||
// Deny all operations if either operand is Keyword...
|
||||
if (A = TKind.Keyword) or (B = TKind.Keyword) then
|
||||
begin
|
||||
// ...except for Equality checks
|
||||
Result := (Op in [TBinaryOp.Equal, TBinaryOp.NotEqual]);
|
||||
exit;
|
||||
end;
|
||||
|
||||
// Default for Ordinal/Float
|
||||
Result := True;
|
||||
end;
|
||||
|
||||
class function TScalar.IsUnaryOperatorSupported(Op: TUnaryOp; A: TKind): Boolean;
|
||||
begin
|
||||
// Deny all unary ops for Keywords
|
||||
if (A = TKind.Keyword) then
|
||||
exit(False);
|
||||
|
||||
// Deny 'not' for Float
|
||||
if (Op = TUnaryOp.Not) and (A = TKind.Float) then
|
||||
Result := False
|
||||
else
|
||||
Result := True;
|
||||
exit(False);
|
||||
|
||||
Result := True;
|
||||
end;
|
||||
|
||||
class function TScalar.StringToKind(const AName: string): TKind;
|
||||
@@ -272,6 +308,8 @@ begin
|
||||
Result := TKind.Ordinal
|
||||
else if SameText(AName, 'Float') then
|
||||
Result := TKind.Float
|
||||
else if SameText(AName, 'Keyword') then
|
||||
Result := TKind.Keyword
|
||||
else
|
||||
raise EArgumentException.CreateFmt('Unknown scalar type name: "%s"', [AName]);
|
||||
end;
|
||||
@@ -281,6 +319,7 @@ begin
|
||||
case Kind of
|
||||
TKind.Ordinal: Result := IntToStr(Value.AsInt64);
|
||||
TKind.Float: Result := FloatToStr(Value.AsDouble);
|
||||
TKind.Keyword: Result := TKeywordRegistry.GetName(Value.AsInt64);
|
||||
else
|
||||
Result := '[Unknown Scalar]';
|
||||
end;
|
||||
@@ -288,6 +327,9 @@ end;
|
||||
|
||||
class function TScalar.TryBinaryOperation(Op: TBinaryOp; const A, B: TScalar; out Res: TScalar): Boolean;
|
||||
begin
|
||||
if not IsBinaryOperatorSupported(Op, A.Kind, B.Kind) then
|
||||
exit(False);
|
||||
|
||||
try
|
||||
case Op of
|
||||
TBinaryOp.Add: Res := A + B;
|
||||
@@ -313,10 +355,7 @@ end;
|
||||
class function TScalar.TryUnaryOperation(Op: TUnaryOp; const A: TScalar; out Res: TScalar): Boolean;
|
||||
begin
|
||||
if not IsUnaryOperatorSupported(Op, A.Kind) then
|
||||
begin
|
||||
Result := False;
|
||||
exit;
|
||||
end;
|
||||
exit(False);
|
||||
try
|
||||
case Op of
|
||||
TUnaryOp.Negate: Res := -A;
|
||||
@@ -335,51 +374,40 @@ class operator TScalar.Add(const A, B: TScalar): TScalar;
|
||||
begin
|
||||
if (A.Kind = TKind.Ordinal) and (B.Kind = TKind.Ordinal) then
|
||||
Result := A.Value.AsInt64 + B.Value.AsInt64
|
||||
else
|
||||
else if (A.Kind = TKind.Float) or (B.Kind = TKind.Float) then
|
||||
begin
|
||||
var valA, valB: Double;
|
||||
if A.Kind = TKind.Ordinal then
|
||||
valA := A.Value.AsInt64
|
||||
else
|
||||
valA := A.Value.AsDouble;
|
||||
if B.Kind = TKind.Ordinal then
|
||||
valB := B.Value.AsInt64
|
||||
else
|
||||
valB := B.Value.AsDouble;
|
||||
valA := A; // Use implicit cast
|
||||
valB := B; // Use implicit cast
|
||||
Result := valA + valB;
|
||||
end;
|
||||
end
|
||||
else
|
||||
raise EArgumentException.CreateFmt('Operator Add not supported for types %s and %s', [A.Kind.ToString, B.Kind.ToString]);
|
||||
end;
|
||||
|
||||
class operator TScalar.Divide(const A, B: TScalar): TScalar;
|
||||
begin
|
||||
// Division *always* promotes to Float, unless types are invalid
|
||||
if (A.Kind = TKind.Keyword) or (B.Kind = TKind.Keyword) then
|
||||
raise EArgumentException.CreateFmt('Operator Divide not supported for types %s and %s', [A.Kind.ToString, B.Kind.ToString]);
|
||||
|
||||
var valA, valB: Double;
|
||||
if A.Kind = TKind.Ordinal then
|
||||
valA := A.Value.AsInt64
|
||||
else
|
||||
valA := A.Value.AsDouble;
|
||||
if B.Kind = TKind.Ordinal then
|
||||
valB := B.Value.AsInt64
|
||||
else
|
||||
valB := B.Value.AsDouble;
|
||||
valA := A; // Use implicit cast
|
||||
valB := B; // Use implicit cast
|
||||
Result := valA / valB;
|
||||
end;
|
||||
|
||||
class operator TScalar.Equal(const A, B: TScalar): Boolean;
|
||||
begin
|
||||
if (A.Kind = TKind.Ordinal) and (B.Kind = TKind.Ordinal) then
|
||||
Result := A.Value.AsInt64 = B.Value.AsInt64
|
||||
// Must be same kind to be equal (e.g., Ordinal(5) <> Keyword(5))
|
||||
if A.Kind <> B.Kind then
|
||||
exit(False);
|
||||
|
||||
case A.Kind of
|
||||
TKind.Ordinal, TKind.Keyword: Result := A.Value.AsInt64 = B.Value.AsInt64;
|
||||
TKind.Float: Result := A.Value.AsDouble = B.Value.AsDouble;
|
||||
else
|
||||
begin
|
||||
var valA, valB: Double;
|
||||
if A.Kind = TKind.Ordinal then
|
||||
valA := A.Value.AsInt64
|
||||
else
|
||||
valA := A.Value.AsDouble;
|
||||
if B.Kind = TKind.Ordinal then
|
||||
valB := B.Value.AsInt64
|
||||
else
|
||||
valB := B.Value.AsDouble;
|
||||
Result := valA = valB;
|
||||
Result := False;
|
||||
end;
|
||||
end;
|
||||
|
||||
@@ -387,19 +415,15 @@ class operator TScalar.GreaterThan(const A, B: TScalar): Boolean;
|
||||
begin
|
||||
if (A.Kind = TKind.Ordinal) and (B.Kind = TKind.Ordinal) then
|
||||
Result := A.Value.AsInt64 > B.Value.AsInt64
|
||||
else
|
||||
else if (A.Kind = TKind.Float) or (B.Kind = TKind.Float) then
|
||||
begin
|
||||
var valA, valB: Double;
|
||||
if A.Kind = TKind.Ordinal then
|
||||
valA := A.Value.AsInt64
|
||||
else
|
||||
valA := A.Value.AsDouble;
|
||||
if B.Kind = TKind.Ordinal then
|
||||
valB := B.Value.AsInt64
|
||||
else
|
||||
valB := B.Value.AsDouble;
|
||||
valA := A;
|
||||
valB := B;
|
||||
Result := valA > valB;
|
||||
end;
|
||||
end
|
||||
else
|
||||
raise EArgumentException.CreateFmt('Operator GreaterThan not supported for types %s and %s', [A.Kind.ToString, B.Kind.ToString]);
|
||||
end;
|
||||
|
||||
class operator TScalar.GreaterThanOrEqual(const A, B: TScalar): Boolean;
|
||||
@@ -411,19 +435,15 @@ class operator TScalar.LessThan(const A, B: TScalar): Boolean;
|
||||
begin
|
||||
if (A.Kind = TKind.Ordinal) and (B.Kind = TKind.Ordinal) then
|
||||
Result := A.Value.AsInt64 < B.Value.AsInt64
|
||||
else
|
||||
else if (A.Kind = TKind.Float) or (B.Kind = TKind.Float) then
|
||||
begin
|
||||
var valA, valB: Double;
|
||||
if A.Kind = TKind.Ordinal then
|
||||
valA := A.Value.AsInt64
|
||||
else
|
||||
valA := A.Value.AsDouble;
|
||||
if B.Kind = TKind.Ordinal then
|
||||
valB := B.Value.AsInt64
|
||||
else
|
||||
valB := B.Value.AsDouble;
|
||||
valA := A;
|
||||
valB := B;
|
||||
Result := valA < valB;
|
||||
end;
|
||||
end
|
||||
else
|
||||
raise EArgumentException.CreateFmt('Operator LessThan not supported for types %s and %s', [A.Kind.ToString, B.Kind.ToString]);
|
||||
end;
|
||||
|
||||
class operator TScalar.LessThanOrEqual(const A, B: TScalar): Boolean;
|
||||
@@ -434,7 +454,7 @@ end;
|
||||
class operator TScalar.LogicalNot(const A: TScalar): TScalar;
|
||||
begin
|
||||
if A.Kind <> TKind.Ordinal then
|
||||
raise EArgumentException.Create('Operator Not not supported for type Float');
|
||||
raise EArgumentException.CreateFmt('Operator Not not supported for type %s', [A.Kind.ToString]);
|
||||
|
||||
if A.Value.AsInt64 = 0 then
|
||||
Result := 1
|
||||
@@ -446,19 +466,15 @@ class operator TScalar.Multiply(const A, B: TScalar): TScalar;
|
||||
begin
|
||||
if (A.Kind = TKind.Ordinal) and (B.Kind = TKind.Ordinal) then
|
||||
Result := A.Value.AsInt64 * B.Value.AsInt64
|
||||
else
|
||||
else if (A.Kind = TKind.Float) or (B.Kind = TKind.Float) then
|
||||
begin
|
||||
var valA, valB: Double;
|
||||
if A.Kind = TKind.Ordinal then
|
||||
valA := A.Value.AsInt64
|
||||
else
|
||||
valA := A.Value.AsDouble;
|
||||
if B.Kind = TKind.Ordinal then
|
||||
valB := B.Value.AsInt64
|
||||
else
|
||||
valB := B.Value.AsDouble;
|
||||
valA := A;
|
||||
valB := B;
|
||||
Result := valA * valB;
|
||||
end;
|
||||
end
|
||||
else
|
||||
raise EArgumentException.CreateFmt('Operator Multiply not supported for types %s and %s', [A.Kind.ToString, B.Kind.ToString]);
|
||||
end;
|
||||
|
||||
class operator TScalar.Negative(const A: TScalar): TScalar;
|
||||
@@ -467,6 +483,8 @@ begin
|
||||
case A.Kind of
|
||||
TKind.Ordinal: Result.Value.AsInt64 := -A.Value.AsInt64;
|
||||
TKind.Float: Result.Value.AsDouble := -A.Value.AsDouble;
|
||||
else
|
||||
raise EArgumentException.CreateFmt('Operator Negative not supported for type %s', [A.Kind.ToString]);
|
||||
end;
|
||||
end;
|
||||
|
||||
@@ -478,10 +496,7 @@ end;
|
||||
class operator TScalar.Round(const A: TScalar): TScalar;
|
||||
begin
|
||||
var val: Double;
|
||||
if A.Kind = TKind.Ordinal then
|
||||
val := A.Value.AsInt64
|
||||
else
|
||||
val := A.Value.AsDouble;
|
||||
val := A; // Implicit cast handles Ordinal or Float
|
||||
Result := System.Round(val);
|
||||
end;
|
||||
|
||||
@@ -489,28 +504,21 @@ class operator TScalar.Subtract(const A, B: TScalar): TScalar;
|
||||
begin
|
||||
if (A.Kind = TKind.Ordinal) and (B.Kind = TKind.Ordinal) then
|
||||
Result := A.Value.AsInt64 - B.Value.AsInt64
|
||||
else
|
||||
else if (A.Kind = TKind.Float) or (B.Kind = TKind.Float) then
|
||||
begin
|
||||
var valA, valB: Double;
|
||||
if A.Kind = TKind.Ordinal then
|
||||
valA := A.Value.AsInt64
|
||||
else
|
||||
valA := A.Value.AsDouble;
|
||||
if B.Kind = TKind.Ordinal then
|
||||
valB := B.Value.AsInt64
|
||||
else
|
||||
valB := B.Value.AsDouble;
|
||||
valA := A;
|
||||
valB := B;
|
||||
Result := valA - valB;
|
||||
end;
|
||||
end
|
||||
else
|
||||
raise EArgumentException.CreateFmt('Operator Subtract not supported for types %s and %s', [A.Kind.ToString, B.Kind.ToString]);
|
||||
end;
|
||||
|
||||
class operator TScalar.Trunc(const A: TScalar): TScalar;
|
||||
begin
|
||||
var val: Double;
|
||||
if A.Kind = TKind.Ordinal then
|
||||
val := A.Value.AsInt64
|
||||
else
|
||||
val := A.Value.AsDouble;
|
||||
val := A; // Implicit cast handles Ordinal or Float
|
||||
Result := System.Trunc(val);
|
||||
end;
|
||||
|
||||
@@ -607,17 +615,20 @@ begin
|
||||
Result := FTotalCount;
|
||||
end;
|
||||
|
||||
{ TScalar.TKindHelper }
|
||||
|
||||
function TScalar.TKindHelper.ToString: string;
|
||||
begin
|
||||
case Self of
|
||||
TScalar.TKind.Ordinal: Result := 'Ordinal';
|
||||
TScalar.TKind.Float: Result := 'Float';
|
||||
TScalar.TKind.Keyword: Result := 'Keyword';
|
||||
else
|
||||
Result := 'unknown';
|
||||
end;
|
||||
end;
|
||||
|
||||
{ TScalar_TBinaryOpHelper }
|
||||
{ TScalar.TBinaryOpHelper }
|
||||
|
||||
function TScalar.TBinaryOpHelper.ToString: string;
|
||||
begin
|
||||
@@ -637,7 +648,7 @@ begin
|
||||
end;
|
||||
end;
|
||||
|
||||
{ TScalar_TUnaryOpHelper }
|
||||
{ TScalar.TUnaryOpHelper }
|
||||
|
||||
function TScalar.TUnaryOpHelper.ToString: string;
|
||||
begin
|
||||
@@ -653,47 +664,33 @@ end;
|
||||
|
||||
constructor TScalarRecordSeries.TMemberSeries.Create(ARecordSeries: TScalarRecordSeries; AElementIdx: Integer);
|
||||
begin
|
||||
inherited Create;
|
||||
FRecordSeries := ARecordSeries;
|
||||
FKind := FRecordSeries.FDef.Fields[AElementIdx].Value;
|
||||
inherited Create(ARecordSeries);
|
||||
FKind := ARecordSeries.FDef.Fields[AElementIdx].Value;
|
||||
FOffset := sizeof(TScalar.TValue) * AElementIdx;
|
||||
end;
|
||||
|
||||
function TScalarRecordSeries.TMemberSeries.GetCount: Int64;
|
||||
begin
|
||||
Result := FRecordSeries.Count;
|
||||
Result := RecordSeries.Count;
|
||||
end;
|
||||
|
||||
function TScalarRecordSeries.TMemberSeries.GetItems(Idx: Integer): TScalar;
|
||||
var
|
||||
P: TChunkArray<TScalar.TValue>.PT;
|
||||
begin
|
||||
P := FRecordSeries.GetItemRef(Idx);
|
||||
P := RecordSeries.GetItemRef(Idx);
|
||||
inc(PByte(P), FOffset);
|
||||
Result.Create(FKind, P^);
|
||||
end;
|
||||
|
||||
function TScalarRecordSeries.TMemberSeries.GetRecordSeries: TScalarRecordSeries;
|
||||
begin
|
||||
Result := TScalarRecordSeries(Controller);
|
||||
end;
|
||||
|
||||
function TScalarRecordSeries.TMemberSeries.GetTotalCount: Int64;
|
||||
begin
|
||||
Result := FRecordSeries.TotalCount;
|
||||
end;
|
||||
|
||||
function TScalarRecordSeries.TMemberSeries.QueryInterface(const IID: TGUID; out Obj): HResult;
|
||||
begin
|
||||
if GetInterface(IID, Obj) then
|
||||
Result := S_OK
|
||||
else
|
||||
Result := E_NOINTERFACE;
|
||||
end;
|
||||
|
||||
function TScalarRecordSeries.TMemberSeries._AddRef: Integer;
|
||||
begin
|
||||
Result := FRecordSeries._AddRef;
|
||||
end;
|
||||
|
||||
function TScalarRecordSeries.TMemberSeries._Release: Integer;
|
||||
begin
|
||||
Result := FRecordSeries._Release;
|
||||
Result := RecordSeries.TotalCount;
|
||||
end;
|
||||
|
||||
{ TScalarSeries }
|
||||
|
||||
Reference in New Issue
Block a user