#![cfg_attr(not(test), no_std)]
use core::cmp::Ordering;
use core::fmt;
use core::fmt::{Display, Formatter};
use core::ops::{Add, Div, Mul, Neg, Sub};
use num_traits::Zero;
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
pub enum Infinitable<T> {
Finite(T),
Infinity,
NegativeInfinity,
}
pub use Infinitable::{Finite, Infinity, NegativeInfinity};
impl<T> Infinitable<T> {
#[must_use]
pub fn is_finite(&self) -> bool {
match self {
Finite(_) => true,
_ => false,
}
}
#[must_use]
pub fn finite(self) -> Option<T> {
match self {
Finite(x) => Some(x),
_ => None,
}
}
#[must_use]
pub fn finite_or_infinity(option: Option<T>) -> Infinitable<T> {
match option {
Some(x) => Finite(x),
None => Infinity,
}
}
#[must_use]
pub fn finite_or_negative_infinity(option: Option<T>) -> Infinitable<T> {
match option {
Some(x) => Finite(x),
None => NegativeInfinity,
}
}
#[must_use]
pub fn convert_into<U>(self) -> Infinitable<U>
where
T: Into<U>,
{
match self {
Finite(x) => Finite(x.into()),
Infinity => Infinity,
NegativeInfinity => NegativeInfinity,
}
}
#[must_use]
pub fn try_convert_into<U>(self) -> Result<Infinitable<U>, T::Error>
where
T: TryInto<U>,
{
match self {
Finite(x) => x.try_into().map(|y| Finite(y)),
Infinity => Ok(Infinity),
NegativeInfinity => Ok(NegativeInfinity),
}
}
}
impl<T> From<T> for Infinitable<T> {
fn from(value: T) -> Infinitable<T> {
Finite(value)
}
}
impl<T> PartialOrd for Infinitable<T>
where
T: PartialOrd,
{
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
match cmp_initial(self, other) {
CmpInitialResult::Infinite(o) => Some(o),
CmpInitialResult::Finite(x, y) => x.partial_cmp(y),
}
}
}
impl<T> Ord for Infinitable<T>
where
T: Ord,
{
fn cmp(&self, other: &Self) -> Ordering {
match cmp_initial(self, other) {
CmpInitialResult::Infinite(o) => o,
CmpInitialResult::Finite(x, y) => x.cmp(y),
}
}
}
enum CmpInitialResult<'a, T> {
Infinite(Ordering),
Finite(&'a T, &'a T),
}
fn cmp_initial<'a, T>(x: &'a Infinitable<T>, y: &'a Infinitable<T>) -> CmpInitialResult<'a, T> {
match (x, y) {
(Infinity, Infinity) | (NegativeInfinity, NegativeInfinity) => {
CmpInitialResult::Infinite(Ordering::Equal)
}
(Infinity, _) | (_, NegativeInfinity) => CmpInitialResult::Infinite(Ordering::Greater),
(NegativeInfinity, _) | (_, Infinity) => CmpInitialResult::Infinite(Ordering::Less),
(Finite(xf), Finite(yf)) => CmpInitialResult::Finite(xf, yf),
}
}
impl<T> Add for Infinitable<T>
where
T: Add,
{
type Output = Infinitable<T::Output>;
fn add(self, rhs: Infinitable<T>) -> Infinitable<T::Output> {
match (self, rhs) {
(Infinity, NegativeInfinity) | (NegativeInfinity, Infinity) => {
panic!("Cannot add infinity and negative infinity")
}
(Finite(lf), Finite(rf)) => Finite(lf.add(rf)),
(Infinity, _) | (_, Infinity) => Infinity,
(NegativeInfinity, _) | (_, NegativeInfinity) => NegativeInfinity,
}
}
}
impl<T> Sub for Infinitable<T>
where
T: Sub,
{
type Output = Infinitable<T::Output>;
fn sub(self, rhs: Infinitable<T>) -> Infinitable<T::Output> {
match (self, rhs) {
(Infinity, Infinity) | (NegativeInfinity, NegativeInfinity) => {
panic!("Cannot subtract infinite value from itself")
}
(Finite(lf), Finite(rf)) => Finite(lf.sub(rf)),
(Infinity, _) | (_, NegativeInfinity) => Infinity,
(NegativeInfinity, _) | (_, Infinity) => NegativeInfinity,
}
}
}
impl<T> Mul for Infinitable<T>
where
T: Mul + Zero + PartialOrd,
{
type Output = Infinitable<<T as Mul>::Output>;
fn mul(self, rhs: Infinitable<T>) -> Infinitable<<T as Mul>::Output> {
match (self, rhs) {
(Infinity, Infinity) | (NegativeInfinity, NegativeInfinity) => Infinity,
(Infinity, NegativeInfinity) | (NegativeInfinity, Infinity) => NegativeInfinity,
(Infinity, Finite(x)) | (Finite(x), Infinity) => match x.partial_cmp(&T::zero()) {
Some(Ordering::Greater) => Infinity,
Some(Ordering::Less) => NegativeInfinity,
_ => panic!("Cannot multiply infinite value and zero or unordered value"),
},
(NegativeInfinity, Finite(x)) | (Finite(x), NegativeInfinity) => {
match x.partial_cmp(&T::zero()) {
Some(Ordering::Greater) => NegativeInfinity,
Some(Ordering::Less) => Infinity,
_ => panic!("Cannot multiply infinite value and zero or unordered value"),
}
}
(Finite(lf), Finite(rf)) => Finite(lf.mul(rf)),
}
}
}
impl<T> Div for Infinitable<T>
where
T: Div + Zero + PartialOrd,
<T as Div>::Output: Zero,
{
type Output = Infinitable<<T as Div>::Output>;
fn div(self, rhs: Infinitable<T>) -> Infinitable<<T as Div>::Output> {
match (self, rhs) {
(Infinity, Infinity)
| (NegativeInfinity, NegativeInfinity)
| (Infinity, NegativeInfinity)
| (NegativeInfinity, Infinity) => panic!("Cannot divide two infinite values"),
(Infinity, Finite(x)) => match x.partial_cmp(&T::zero()) {
Some(Ordering::Less) => NegativeInfinity,
_ => Infinity,
},
(NegativeInfinity, Finite(x)) => match x.partial_cmp(&T::zero()) {
Some(Ordering::Less) => Infinity,
_ => NegativeInfinity,
},
(Finite(_), Infinity) | (Finite(_), NegativeInfinity) => {
Finite(<T as Div>::Output::zero())
}
(Finite(lf), Finite(rf)) => match rf.partial_cmp(&T::zero()) {
Some(Ordering::Greater) | Some(Ordering::Less) => Finite(lf.div(rf)),
_ => match lf.partial_cmp(&T::zero()) {
Some(Ordering::Greater) => Infinity,
Some(Ordering::Less) => NegativeInfinity,
_ => panic!("Cannot divide two zeros or unordered values"),
},
},
}
}
}
impl<T> Neg for Infinitable<T>
where
T: Neg,
{
type Output = Infinitable<T::Output>;
fn neg(self) -> Infinitable<T::Output> {
match self {
Finite(x) => Finite(-x),
Infinity => NegativeInfinity,
NegativeInfinity => Infinity,
}
}
}
impl<T> Display for Infinitable<T>
where
T: Display,
{
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
match self {
Finite(x) => write!(f, "{}", x),
Infinity => write!(f, "inf"),
NegativeInfinity => write!(f, "-inf"),
}
}
}
pub fn from_f32(value: f32) -> Option<Infinitable<f32>> {
if value.is_finite() {
Some(Finite(value))
} else if value.is_nan() {
None
} else if value.is_sign_positive() {
Some(Infinity)
} else {
Some(NegativeInfinity)
}
}
pub fn from_f64(value: f64) -> Option<Infinitable<f64>> {
if value.is_finite() {
Some(Finite(value))
} else if value.is_nan() {
None
} else if value.is_sign_positive() {
Some(Infinity)
} else {
Some(NegativeInfinity)
}
}