use std::cmp::Ordering;
use qubit_datatype::{
NumberRef,
NumericComparisonPolicy,
};
use super::{
Value,
ValueRepr,
};
use crate::NumericComparisonError;
macro_rules! project_number_ref {
(number_copy, $value:expr) => {
Some(NumberRef::from(*$value))
};
(number_ref, $value:expr) => {
Some(NumberRef::from($value))
};
(not_number, $value:expr) => {{
let _ = $value;
None
}};
}
macro_rules! value_number_ref_match {
($value:expr; $(([$($cfg:meta),*], $variant:ident, $type:ty, $data_type:expr, $materialization:ident, $json_class:ident, $number_projection:ident, $value_doc:literal, $multi_doc:literal)),+ $(,)?) => {
match &$value.repr {
ValueRepr::Unset(_) => None,
$(
$(#[$cfg])*
ValueRepr::$variant(value) => {
project_number_ref!($number_projection, value)
}
)+
}
};
}
impl Value {
#[inline(always)]
#[must_use]
pub fn is_nan(&self) -> bool {
self.as_number_ref().is_some_and(|value| value.is_nan())
}
pub fn numeric_cmp(
&self,
other: &Self,
policy: NumericComparisonPolicy,
) -> Result<Ordering, NumericComparisonError> {
if let ValueRepr::Unset(declared) = &self.repr {
return Err(NumericComparisonError::LeftMissing {
declared: *declared,
});
}
if let ValueRepr::Unset(declared) = &other.repr {
return Err(NumericComparisonError::RightMissing {
declared: *declared,
});
}
let left = self.as_number_ref().ok_or_else(|| {
NumericComparisonError::LeftNotNumeric {
actual: self.data_type(),
}
})?;
let right = other.as_number_ref().ok_or_else(|| {
NumericComparisonError::RightNotNumeric {
actual: other.data_type(),
}
})?;
match (left.is_nan(), right.is_nan()) {
(true, true) => return Err(NumericComparisonError::BothNaN),
(true, false) => return Err(NumericComparisonError::LeftNaN),
(false, true) => return Err(NumericComparisonError::RightNaN),
(false, false) => {}
}
match left.compare(right, policy) {
Some(ordering) => Ok(ordering),
None => unreachable!(
"validated non-NaN numeric values must be orderable"
),
}
}
fn as_number_ref(&self) -> Option<NumberRef<'_>> {
for_each_value_type!(value_number_ref_match, self)
}
}