use crate::prelude::*;
use core::cmp::Ordering;
use core::hash::{Hash, Hasher};
use core::str::FromStr;
#[derive(Debug, Clone, Copy)]
pub enum Number {
Integer(i64),
Float(f64),
}
impl Number {
#[must_use]
pub fn as_i64(&self) -> Option<i64> {
match self {
Number::Integer(n) => Some(*n),
Number::Float(_) => None,
}
}
#[must_use]
pub fn as_u64(&self) -> Option<u64> {
match self {
Number::Integer(n) if *n >= 0 => Some(*n as u64),
_ => None,
}
}
#[must_use]
pub fn as_f64(&self) -> f64 {
match self {
Number::Integer(n) => *n as f64,
Number::Float(n) => *n,
}
}
#[must_use]
pub fn is_integer(&self) -> bool {
matches!(self, Number::Integer(_))
}
#[must_use]
pub fn is_float(&self) -> bool {
matches!(self, Number::Float(_))
}
#[must_use]
pub fn is_i64(&self) -> bool {
matches!(self, Number::Integer(_))
}
#[must_use]
pub fn is_u64(&self) -> bool {
matches!(self, Number::Integer(n) if *n >= 0)
}
#[must_use]
pub fn is_f64(&self) -> bool {
true
}
#[must_use]
pub fn is_nan(&self) -> bool {
match self {
Number::Float(n) => n.is_nan(),
Number::Integer(_) => false,
}
}
#[must_use]
pub fn is_infinite(&self) -> bool {
match self {
Number::Float(n) => n.is_infinite(),
Number::Integer(_) => false,
}
}
#[must_use]
pub fn is_finite(&self) -> bool {
match self {
Number::Float(n) => n.is_finite(),
Number::Integer(_) => true,
}
}
}
impl fmt::Display for Number {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Number::Integer(n) => write!(f, "{n}"),
Number::Float(n) => write!(f, "{n}"),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct ParseNumberError {
_private: (),
}
impl fmt::Display for ParseNumberError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "invalid number")
}
}
#[cfg(feature = "std")]
impl std::error::Error for ParseNumberError {}
impl FromStr for Number {
type Err = ParseNumberError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let s = s.trim();
match s {
".nan" | ".NaN" | ".NAN" => return Ok(Number::Float(f64::NAN)),
".inf" | ".Inf" | ".INF" => return Ok(Number::Float(f64::INFINITY)),
"+.inf" | "+.Inf" | "+.INF" => return Ok(Number::Float(f64::INFINITY)),
"-.inf" | "-.Inf" | "-.INF" => return Ok(Number::Float(f64::NEG_INFINITY)),
_ => {}
}
if let Ok(n) = s.parse::<i64>() {
return Ok(Number::Integer(n));
}
if s.len() > 2 {
let (prefix, rest) = s.split_at(2);
match prefix {
"0x" | "0X" => {
if let Ok(n) = i64::from_str_radix(rest, 16) {
return Ok(Number::Integer(n));
}
}
"0o" | "0O" => {
if let Ok(n) = i64::from_str_radix(rest, 8) {
return Ok(Number::Integer(n));
}
}
"0b" | "0B" => {
if let Ok(n) = i64::from_str_radix(rest, 2) {
return Ok(Number::Integer(n));
}
}
_ => {}
}
}
if let Ok(n) = s.parse::<f64>() {
return Ok(Number::Float(n));
}
Err(ParseNumberError { _private: () })
}
}
impl PartialEq for Number {
fn eq(&self, other: &Self) -> bool {
match (self, other) {
(Number::Integer(a), Number::Integer(b)) => a == b,
(Number::Float(a), Number::Float(b)) => {
(a.is_nan() && b.is_nan()) || a == b
}
_ => false,
}
}
}
impl Eq for Number {}
impl Hash for Number {
fn hash<H: Hasher>(&self, state: &mut H) {
match self {
Number::Integer(n) => {
0u8.hash(state);
n.hash(state);
}
Number::Float(n) => {
1u8.hash(state);
let bits = if n.is_nan() {
0x7FF8_0000_0000_0001
} else if *n == 0.0 {
0
} else {
n.to_bits()
};
bits.hash(state);
}
}
}
}
impl PartialOrd for Number {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl Ord for Number {
fn cmp(&self, other: &Self) -> Ordering {
match (self, other) {
(Number::Integer(a), Number::Integer(b)) => a.cmp(b),
(Number::Float(a), Number::Float(b)) => {
match (a.is_nan(), b.is_nan()) {
(true, true) => Ordering::Equal,
(true, false) => Ordering::Greater,
(false, true) => Ordering::Less,
(false, false) => a.partial_cmp(b).unwrap_or(Ordering::Equal),
}
}
(Number::Integer(a), Number::Float(b)) => {
if b.is_nan() {
Ordering::Less
} else if *a > (1_i64 << 53) || *a < -(1_i64 << 53) {
let a_f = *a as f64;
if (a_f as i64) == *a {
a_f.partial_cmp(b).unwrap_or(Ordering::Equal)
} else {
if *a > 0 {
if *b < (1_i64 << 53) as f64 {
Ordering::Greater
} else {
(*a as f64).partial_cmp(b).unwrap_or(Ordering::Equal)
}
} else if *b > -(1_i64 << 53) as f64 {
Ordering::Less
} else {
(*a as f64).partial_cmp(b).unwrap_or(Ordering::Equal)
}
}
} else {
(*a as f64).partial_cmp(b).unwrap_or(Ordering::Equal)
}
}
(Number::Float(a), Number::Integer(b)) => {
match Number::Integer(*b).cmp(&Number::Float(*a)) {
Ordering::Less => Ordering::Greater,
Ordering::Greater => Ordering::Less,
Ordering::Equal => Ordering::Equal,
}
}
}
}
}
impl From<i8> for Number {
fn from(v: i8) -> Self {
Number::Integer(i64::from(v))
}
}
impl From<i16> for Number {
fn from(v: i16) -> Self {
Number::Integer(i64::from(v))
}
}
impl From<i32> for Number {
fn from(v: i32) -> Self {
Number::Integer(i64::from(v))
}
}
impl From<i64> for Number {
fn from(v: i64) -> Self {
Number::Integer(v)
}
}
impl From<isize> for Number {
fn from(v: isize) -> Self {
Number::Integer(v as i64)
}
}
impl From<u8> for Number {
fn from(v: u8) -> Self {
Number::Integer(i64::from(v))
}
}
impl From<u16> for Number {
fn from(v: u16) -> Self {
Number::Integer(i64::from(v))
}
}
impl From<u32> for Number {
fn from(v: u32) -> Self {
Number::Integer(i64::from(v))
}
}
impl From<u64> for Number {
fn from(v: u64) -> Self {
if v <= i64::MAX as u64 {
Number::Integer(v as i64)
} else {
Number::Float(v as f64)
}
}
}
impl From<usize> for Number {
fn from(v: usize) -> Self {
Number::from(v as u64)
}
}
impl From<f32> for Number {
fn from(v: f32) -> Self {
Number::Float(f64::from(v))
}
}
impl From<f64> for Number {
fn from(v: f64) -> Self {
Number::Float(v)
}
}