unit Myc.Data.Types.Float;

interface

uses
    System.SysUtils,
    Myc.Data.Types;

type
    // Implements the float data type.
    TImplDataFloatType = class(TInterfacedObject, IDataType, IDataFloatType)
    strict private
        class var
            FSingleton: IDataFloatType;
            FZero: IDataFloatValue;
            FNaN: IDataFloatValue;
        class constructor CreateClass;
    public
        function GetName: String;
        function GetKind: TDataKind;
        function CreateValue(Init: Double): IDataFloatValue;
        class property Singleton: IDataFloatType read FSingleton;
    end;

implementation

uses
    System.Math;

type
    // Implements the float data value.
    TImplDataFloatValue = class(TInterfacedObject, IDataValue, IDataFloatValue)
    private
        FValue: Double;
    public
        constructor Create(const AValue: Double);

        // IDataValue
        function GetDataType: IDataType;
        function GetAsString: string;

        // IDataFloatValue
        function GetValue: Double;
    end;

    // Special implementation for the value 0.0 (Singleton)
    TImplDataFloatValueZero = class(TInterfacedObject, IDataValue, IDataFloatValue)
    public
        function GetDataType: IDataType;
        function GetAsString: string;
        function GetValue: Double;
    end;

    // Special implementation for NaN (Not a Number) (Singleton)
    TImplDataFloatValueNaN = class(TInterfacedObject, IDataValue, IDataFloatValue)
    public
        function GetDataType: IDataType;
        function GetAsString: string;
        function GetValue: Double;
    end;

{ TImplDataFloatValue }

constructor TImplDataFloatValue.Create(const AValue: Double);
begin
    inherited Create;
    FValue := AValue;
end;

function TImplDataFloatValue.GetDataType: IDataType;
begin
    Result := TImplDataFloatType.Singleton;
end;

function TImplDataFloatValue.GetAsString: string;
begin
    Result := FloatToStr(FValue);
end;

function TImplDataFloatValue.GetValue: Double;
begin
    Result := FValue;
end;

function TImplDataFloatValueZero.GetAsString: string;
begin
    Result := '0';
end;

function TImplDataFloatValueZero.GetDataType: IDataType;
begin
    Result := TImplDataFloatType.Singleton;
end;

function TImplDataFloatValueZero.GetValue: Double;
begin
    Result := 0.0;
end;

function TImplDataFloatValueNaN.GetAsString: string;
begin
    Result := 'NaN';
end;

function TImplDataFloatValueNaN.GetDataType: IDataType;
begin
    Result := TImplDataFloatType.Singleton;
end;

function TImplDataFloatValueNaN.GetValue: Double;
begin
    Result := NaN;
end;

{ TImplDataFloatType }

class constructor TImplDataFloatType.CreateClass;
begin
    FSingleton := TImplDataFloatType.Create;
    FZero := TImplDataFloatValueZero.Create;
    FNaN := TImplDataFloatValueNaN.Create;
end;

function TImplDataFloatType.GetName: String;
begin
    Result := 'Float';
end;

function TImplDataFloatType.GetKind: TDataKind;
begin
    Result := dkFloat;
end;

function TImplDataFloatType.CreateValue(Init: Double): IDataFloatValue;
begin
    // Return singleton instances for 0.0 and NaN to avoid unnecessary allocations.
    if (Init = 0.0) then
        Result := FZero
    else if IsNaN(Init) then
        Result := FNaN
    else
        Result := TImplDataFloatValue.Create(Init);
end;

end.
