use core::convert::Infallible;
use core::fmt::{self, Debug, Display, Formatter};
use core::marker::PhantomData;
use thiserror::Error;
use crate::cmp::EmptyInhabitant;
use crate::divergence::{Divergence, OrPanic, OutputFor};
use crate::proxy::{Constrained, ConstrainedProxy};
use crate::sealed::{Sealed, StaticDebug};
use crate::{NanEncoding, Primitive};
pub(crate) mod sealed {
use crate::proxy::Constrained;
use crate::Primitive;
pub trait FromEmpty: Sized {
type Empty<T>;
fn empty<T>() -> Self::Empty<T>
where
T: Primitive;
fn from_empty<T>(empty: Self::Empty<T>) -> Constrained<T, Self>
where
T: Primitive;
fn is_empty<T>(primitive: &T) -> bool
where
T: Primitive;
}
}
use sealed::FromEmpty;
#[derive(Clone, Copy, Debug, Error)]
pub enum ConstraintError {
#[error(transparent)]
NotExtendedReal(NotExtendedRealError),
#[error(transparent)]
NotReal(NotRealError),
}
impl From<NotExtendedRealError> for ConstraintError {
fn from(error: NotExtendedRealError) -> Self {
ConstraintError::NotExtendedReal(error)
}
}
impl From<NotRealError> for ConstraintError {
fn from(error: NotRealError) -> Self {
ConstraintError::NotReal(error)
}
}
#[derive(Clone, Copy, Debug, Error)]
#[error("{}", "floating-point value must be an extended real")]
pub struct NotExtendedRealError;
impl EmptyInhabitant for NotExtendedRealError {
fn empty() -> Self {
NotExtendedRealError
}
}
#[derive(Clone, Copy, Debug, Error)]
#[error("{}", "floating-point value must be a real")]
pub struct NotRealError;
impl EmptyInhabitant for NotRealError {
fn empty() -> Self {
NotRealError
}
}
pub(crate) trait ExpectConstrained<T>: Sized {
fn expect_constrained(self) -> T;
}
impl<T, E> ExpectConstrained<T> for Result<T, E>
where
E: Debug,
{
fn expect_constrained(self) -> T {
self.unwrap()
}
}
pub enum RealSet {}
pub enum InfinitySet {}
pub enum NanSet {}
pub trait Member<T>: Sealed {}
pub trait SupersetOf<C>: Sealed {}
pub trait SubsetOf<C>: Sealed {}
impl<C1, C2> SubsetOf<C2> for C1
where
C1: Sealed,
C2: SupersetOf<C1>,
{
}
pub trait Constraint: FromEmpty + Member<RealSet> + StaticDebug {
type Divergence: Divergence;
type Error: Debug + Display;
fn check<T>(inner: T) -> Result<(), Self::Error>
where
T: Primitive;
fn map<T, U, F>(inner: T, f: F) -> OutputFor<Self::Divergence, U, Self::Error>
where
T: Primitive,
U: ConstrainedProxy<Constraint = Self, Primitive = T>,
F: FnOnce(T) -> U,
{
Self::Divergence::diverge(Self::check(inner).map(|_| f(inner)))
}
}
#[derive(Debug)]
pub enum IsFloat {}
impl Constraint for IsFloat {
type Divergence = OrPanic;
type Error = Infallible;
#[inline(always)]
fn check<T>(_inner: T) -> Result<(), Self::Error>
where
T: Primitive,
{
Ok(())
}
#[inline(always)]
fn map<T, U, F>(inner: T, f: F) -> U
where
T: Primitive,
U: ConstrainedProxy<Constraint = Self, Primitive = T>,
F: FnOnce(T) -> U,
{
f(inner)
}
}
impl FromEmpty for IsFloat {
type Empty<T> = Constrained<T, Self>;
#[inline(always)]
fn empty<T>() -> Self::Empty<T>
where
T: Primitive,
{
Constrained::NAN
}
#[inline(always)]
fn from_empty<T>(empty: Self::Empty<T>) -> Constrained<T, Self>
where
T: Primitive,
{
empty
}
#[inline(always)]
fn is_empty<T>(primitive: &T) -> bool
where
T: Primitive,
{
primitive.is_nan()
}
}
impl Member<InfinitySet> for IsFloat {}
impl Member<NanSet> for IsFloat {}
impl Member<RealSet> for IsFloat {}
impl Sealed for IsFloat {}
impl StaticDebug for IsFloat {
fn fmt(formatter: &mut Formatter<'_>) -> fmt::Result {
write!(formatter, "IsFloat")
}
}
impl<D> SupersetOf<IsReal<D>> for IsFloat {}
impl<D> SupersetOf<IsExtendedReal<D>> for IsFloat {}
#[derive(Debug)]
pub struct IsExtendedReal<D>(PhantomData<fn() -> D>, Infallible);
pub type IsNotNan<D> = IsExtendedReal<D>;
impl<D> Constraint for IsExtendedReal<D>
where
D: Divergence,
{
type Divergence = D;
type Error = NotExtendedRealError;
fn check<T>(inner: T) -> Result<(), Self::Error>
where
T: Primitive,
{
if inner.is_nan() {
Err(NotExtendedRealError)
}
else {
Ok(())
}
}
}
impl<D> FromEmpty for IsExtendedReal<D>
where
D: Divergence,
{
type Empty<T> = Infallible;
fn empty<T>() -> Self::Empty<T>
where
T: Primitive,
{
unreachable!()
}
fn from_empty<T>(_: Self::Empty<T>) -> Constrained<T, Self>
where
T: Primitive,
{
unreachable!()
}
#[inline(always)]
fn is_empty<T>(_: &T) -> bool
where
T: Primitive,
{
false
}
}
impl<D> Member<InfinitySet> for IsExtendedReal<D> {}
impl<D> Member<RealSet> for IsExtendedReal<D> {}
impl<D> Sealed for IsExtendedReal<D> {}
impl<D> StaticDebug for IsExtendedReal<D>
where
D: StaticDebug,
{
fn fmt(formatter: &mut Formatter<'_>) -> fmt::Result {
write!(formatter, "IsExtendedReal<")?;
D::fmt(formatter)?;
write!(formatter, ">")
}
}
impl<D> SupersetOf<IsReal<D>> for IsExtendedReal<D> {}
#[derive(Debug)]
pub struct IsReal<D>(PhantomData<fn() -> D>, Infallible);
impl<D> Constraint for IsReal<D>
where
D: Divergence,
{
type Divergence = D;
type Error = NotRealError;
fn check<T>(inner: T) -> Result<(), Self::Error>
where
T: Primitive,
{
if inner.is_nan() || inner.is_infinite() {
Err(NotRealError)
}
else {
Ok(())
}
}
}
impl<D> FromEmpty for IsReal<D>
where
D: Divergence,
{
type Empty<T> = Infallible;
fn empty<T>() -> Self::Empty<T>
where
T: Primitive,
{
unreachable!()
}
fn from_empty<T>(_: Self::Empty<T>) -> Constrained<T, Self>
where
T: Primitive,
{
unreachable!()
}
#[inline(always)]
fn is_empty<T>(_: &T) -> bool
where
T: Primitive,
{
false
}
}
impl<D> Member<RealSet> for IsReal<D> {}
impl<D> Sealed for IsReal<D> {}
impl<D> StaticDebug for IsReal<D>
where
D: StaticDebug,
{
fn fmt(formatter: &mut Formatter<'_>) -> fmt::Result {
write!(formatter, "IsReal<")?;
D::fmt(formatter)?;
write!(formatter, ">")
}
}