using System;
using System.Globalization;
using System.Numerics;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Text;
using Scriban;
using Scriban.Parsing;
using Scriban.Runtime;
using Scriban.Syntax;
namespace Kalk.Core
{
///
/// Represents a IEE-754 half-float 16-bit
///
public readonly struct KalkHalf : IScriptCustomType, IKalkSpannable, IFormattable, IScriptTransformable, IKalkDisplayable
{
private readonly Half _value;
public KalkHalf(float value)
{
_value = (Half) value;
}
public KalkHalf(Half value)
{
_value = value;
}
public string TypeName => "half";
internal Half Value => _value;
public static float[] ToFloatValues(KalkHalf[] values)
{
if (values == null) return null;
var floatValues = new float[values.Length];
for (int i = 0; i < values.Length; i++)
{
floatValues[i] = (float)values[i];
}
return floatValues;
}
public static KalkHalf[] ToHalfValues(float[] values)
{
if (values == null) return null;
var halfValues = new KalkHalf[values.Length];
for (int i = 0; i < values.Length; i++)
{
halfValues[i] = (KalkHalf)values[i];
}
return halfValues;
}
public bool TryEvaluate(TemplateContext context, SourceSpan span, ScriptBinaryOperator op, SourceSpan leftSpan, object leftValue, SourceSpan rightSpan, object rightValue, out object result)
{
result = null;
if (leftValue is KalkHalf leftHalf && rightValue is KalkHalf rightHalf)
{
result = (KalkHalf)(float)ScriptBinaryExpression.Evaluate(context, span, op, leftSpan, (float) leftHalf, rightSpan, (float)rightHalf);
return true;
}
if (leftValue is KalkHalf leftHalf1)
{
result = (KalkHalf)(float)ScriptBinaryExpression.Evaluate(context, span, op, leftSpan, (float)leftHalf1, rightSpan, rightValue);
return true;
}
if (rightValue is KalkHalf rightHalf1)
{
result = (KalkHalf)(float)ScriptBinaryExpression.Evaluate(context, span, op, leftSpan, leftValue, rightSpan, rightHalf1);
return true;
}
return false;
}
public bool TryEvaluate(TemplateContext context, SourceSpan span, ScriptUnaryOperator op, object rightValue, out object result)
{
result = ScriptUnaryExpression.Evaluate(context, span, op, (float)(KalkHalf)rightValue);
return true;
}
public static implicit operator KalkHalf(Half half)
{
return new KalkHalf(half);
}
public static explicit operator KalkHalf(float value)
{
return new KalkHalf(value);
}
public static explicit operator float(KalkHalf half)
{
return (float)half._value;
}
public override string ToString()
{
return ToString(null, CultureInfo.InvariantCulture);
}
public void Display(KalkEngine engine, KalkDisplayMode mode)
{
switch (mode)
{
case KalkDisplayMode.Raw:
case KalkDisplayMode.Standard:
break;
case KalkDisplayMode.Developer:
DisplayDetailed(engine);
break;
}
}
private void DisplayDetailed(KalkEngine engine)
{
var u16 = Unsafe.As(ref Unsafe.AsRef(_value));
engine.WriteHighlightLine($" # IEEE 754 - half float - 16-bit");
engine.WriteHighlightLine($" #");
engine.WriteInt16(u16, true);
// Prints the 16 bits indices
engine.WriteHighlightLine($" # 15 8 0");
engine.WriteHighlightLine($" #");
engine.WriteHighlightLine($" # sign exponent |-- fraction -|");
// # 1 * 2 ^ (15 - 15) * 0b1.1000000000
var sign = (u16 >> 15);
var exponent = (u16 >> 10) & 0b11111;
var fraction = u16 & 0b1111111111;
var builder = new StringBuilder();
builder.Append(sign != 0 ? " -1" : " 1");
builder.Append(" * 2 ^ (");
builder.Append($"{(exponent == 0 ? 1 : exponent),2} - 15) * 0b{(exponent == 0 ? '0' : '1')}.");
for (int i = 9; i >= 0; i--)
{
builder.Append(((fraction >> i) & 1) == 0 ? '0' : '1');
}
engine.WriteHighlightLine($" = {builder}f");
}
public string ToString(string? format, IFormatProvider? formatProvider)
{
return _value.ToString(format, formatProvider);
}
public Span AsSpan()
{
return MemoryMarshal.CreateSpan(ref Unsafe.As(ref Unsafe.AsRef(this)), Unsafe.SizeOf());
}
public bool TryConvertTo(TemplateContext context, SourceSpan span, Type type, out object value)
{
if (type == typeof(KalkDoubleValue))
{
value = new KalkDoubleValue(this);
return true;
}
if (type == typeof(KalkTrigDoubleValue))
{
value = new KalkTrigDoubleValue(this);
return true;
}
if (type == typeof(object))
{
value = this;
return true;
}
if (type == typeof(int))
{
value = (int)(float)_value;
return true;
}
if (type == typeof(float))
{
value = (float)_value;
return true;
}
if (type == typeof(long))
{
value = (long)(float)_value;
return true;
}
if (type == typeof(double))
{
value = (double)(float)_value;
return true;
}
if (type == typeof(decimal))
{
value = (decimal) (float) _value;
return true;
}
throw new ScriptRuntimeException(span, $"Cannot convert {this} to an {type} as it has an imaginary part.");
}
public bool CanTransform(Type transformType)
{
return transformType == typeof(float) || transformType == typeof(double) || transformType == typeof(int);
}
public bool Visit(TemplateContext context, SourceSpan span, Func