use rlx_ir::{DType, Graph, GraphExt, NodeId};
pub(crate) const MAX_EXACT_INT: i128 = 1 << 53;
pub(crate) fn promote_scalar(g: &mut Graph, value: Scalar, dtype_hint: DType) -> NodeId {
match value {
Scalar::Bool(b) => g.constant(if b { 1.0 } else { 0.0 }, DType::Bool),
Scalar::Int(v) => {
let fv = int_to_f64(v).expect("integer literal exceeds exact f64 range");
if dtype_hint.is_integral() {
g.constant(fv, dtype_hint)
} else {
g.constant(fv, DType::F32)
}
}
Scalar::Float(v) => {
if dtype_hint.is_integral() {
g.constant(v, dtype_hint)
} else {
g.constant(v, DType::F32)
}
}
}
}
fn int_to_f64(v: i64) -> Result<f64, ()> {
if v.unsigned_abs() > MAX_EXACT_INT as u64 {
return Err(());
}
Ok(v as f64)
}
#[derive(Debug, Clone, Copy)]
pub(crate) enum Scalar {
Bool(bool),
Int(i64),
Float(f64),
}
impl From<bool> for Scalar {
fn from(v: bool) -> Self {
Self::Bool(v)
}
}
impl From<i32> for Scalar {
fn from(v: i32) -> Self {
Self::Int(v as i64)
}
}
impl From<i64> for Scalar {
fn from(v: i64) -> Self {
Self::Int(v)
}
}
impl From<f32> for Scalar {
fn from(v: f32) -> Self {
Self::Float(v as f64)
}
}
impl From<f64> for Scalar {
fn from(v: f64) -> Self {
Self::Float(v)
}
}
trait IntegralDType {
fn is_integral(&self) -> bool;
}
impl IntegralDType for DType {
fn is_integral(&self) -> bool {
matches!(
self,
DType::I8 | DType::I16 | DType::I32 | DType::I64 | DType::U8 | DType::U32
)
}
}