logo
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
//! Making floating-point behave: ordering, equivalence, hashing, and
//! constraints for floating-point types.
//!
//! Decorum provides traits that describe types using floating-point
//! representations and provides proxy types that wrap primitive floating-point
//! types. Proxy types implement a total ordering and constraints on the classes
//! of values that they may represent.
//!
//! # Floating-Point Classes
//!
//! Traits, proxy types, and constraints are based on three classes or subsets
//! of floating-point values:
//!
//! | Class        | Trait      |
//! |--------------|------------|
//! | real number  | `Real`     |
//! | infinity     | `Infinite` |
//! | not-a-number | `Nan`      |
//!
//! Primitive floating-point values directly expose IEEE-754 and therefore the
//! complete set of values (and traits). Proxy types implement traits that are
//! compatible with their constraints, so types that disallow `NaN`s do not
//! implement the `Nan` trait, for example.
//!
//! # Proxy Types
//!
//! Proxy types wrap primitive floating-point types and constrain the classes of
//! values that they can represent:
//!
//! | Type     | Aliases      | Trait Implementations                      | Disallowed Values     |
//! |----------|--------------|--------------------------------------------|-----------------------|
//! | `Total`  |              | `Encoding + Real + Infinite + Nan + Float` |                       |
//! | `NotNan` | `N32`, `N64` | `Encoding + Real + Infinite`               | `NaN`                 |
//! | `Finite` | `R32`, `R64` | `Encoding + Real`                          | `NaN`, `-INF`, `+INF` |
//!
//! The `NotNan` and `Finite` types disallow values that represent `NaN`,
//! $\infin$, and $-\infin$. **Operations that emit values that violate these
//! constraints will panic**. The `Total` type applies no constraints and
//! exposes all classes of floating-point values.
//!
//! # Total Ordering
//!
//! The following total ordering is implemented by all proxy types and is
//! provided by traits in the `cmp` module:
//!
//! $$-\infin<\cdots<0<\cdots<\infin<\text{NaN}$$
//!
//! Note that all zero and `NaN` representations are considered equivalent. See
//! the `cmp` module documentation for more details.
//!
//! # Equivalence
//!
//! Floating-point `NaN`s have numerous representations and are incomparable.
//! Decorum considers all `NaN` representations equal to all other `NaN`
//! representations and any and all `NaN` representations are unequal to
//! non-`NaN` values.
//!
//! See the `cmp` module documentation for more details.

#![doc(
    html_favicon_url = "https://raw.githubusercontent.com/olson-sean-k/decorum/master/doc/decorum-favicon.ico"
)]
#![doc(
    html_logo_url = "https://raw.githubusercontent.com/olson-sean-k/decorum/master/doc/decorum.svg?sanitize=true"
)]
#![no_std]

#[cfg(feature = "std")]
extern crate std;

use core::num::FpCategory;
use core::ops::Neg;
#[allow(unused_imports)]
use num_traits::{Num, NumCast, Signed};

#[cfg(not(feature = "std"))]
pub(in crate) use num_traits::float::FloatCore as ForeignFloat;
#[cfg(feature = "std")]
pub(in crate) use num_traits::real::Real as ForeignReal;
#[cfg(feature = "std")]
pub(in crate) use num_traits::Float as ForeignFloat;

mod canonical;
pub mod cmp;
mod constraint;
pub mod hash;
mod primitive;
mod proxy;

use crate::cmp::IntrinsicOrd;
use crate::constraint::{FiniteConstraint, NotNanConstraint, UnitConstraint};

pub use crate::canonical::ToCanonicalBits;
pub use crate::primitive::Primitive;
pub use crate::proxy::ConstrainedFloat;

/// Floating-point representation with total ordering.
pub type Total<T> = ConstrainedFloat<T, UnitConstraint<T>>;

/// Floating-point representation that cannot be `NaN`.
///
/// If an operation emits `NaN`, then a panic will occur. Like `Total`, this
/// type implements a total ordering.
pub type NotNan<T> = ConstrainedFloat<T, NotNanConstraint<T>>;

/// 32-bit floating-point representation that cannot be `NaN`.
pub type N32 = NotNan<f32>;
/// 64-bit floating-point representation that cannot be `NaN`.
pub type N64 = NotNan<f64>;

/// Floating-point representation that must be a real number.
///
/// If an operation emits `NaN` or infinities, then a panic will occur. Like
/// `Total`, this type implements a total ordering.
pub type Finite<T> = ConstrainedFloat<T, FiniteConstraint<T>>;

/// 32-bit floating-point representation that must be a real number.
///
/// The prefix "R" for _real_ is used instead of "F" for _finite_, because if
/// "F" were used, then this name would be very similar to `f32`.
pub type R32 = Finite<f32>;
/// 64-bit floating-point representation that must be a real number.
///
/// The prefix "R" for _real_ is used instead of "F" for _finite_, because if
/// "F" were used, then this name would be very similar to `f64`.
pub type R64 = Finite<f64>;

/// Floating-point representations that expose infinities.
pub trait Infinite: Encoding {
    const INFINITY: Self;
    const NEG_INFINITY: Self;

    fn is_infinite(self) -> bool;
    fn is_finite(self) -> bool;
}

/// Floating-point representations that expose `NaN`s.
pub trait Nan: Encoding {
    /// A representation of `NaN`.
    ///
    /// For primitive floating-point types, `NaN` is incomparable. Therefore,
    /// prefer the `is_nan` predicate over direct comparisons with `NaN`.
    const NAN: Self;

    fn is_nan(self) -> bool;
}

/// Floating-point encoding.
///
/// Provides values and operations that describe the encoding of an IEEE-754
/// floating-point value. Infinities and `NaN`s are described by the `Infinite`
/// and `NaN` sub-traits.
pub trait Encoding: Copy {
    const MAX: Self;
    const MIN: Self;
    const MIN_POSITIVE: Self;
    const EPSILON: Self;

    fn classify(self) -> FpCategory;
    fn is_normal(self) -> bool;

    fn is_sign_positive(self) -> bool;
    fn is_sign_negative(self) -> bool;

    fn integer_decode(self) -> (u64, i16, i8);
}

/// Types that can represent real numbers.
///
/// Provides values and operations that generally apply to real numbers. As
/// such, this trait is implemented by types using floating-point
/// representations, but this trait is a general numeric trait and can be
/// implemented by other numeric types as well.
///
/// Some members of this trait depend on the standard library and the `std`
/// feature.
pub trait Real: Copy + Neg<Output = Self> + Num + PartialOrd + Signed {
    const E: Self;
    const PI: Self;
    const FRAC_1_PI: Self;
    const FRAC_2_PI: Self;
    const FRAC_2_SQRT_PI: Self;
    const FRAC_PI_2: Self;
    const FRAC_PI_3: Self;
    const FRAC_PI_4: Self;
    const FRAC_PI_6: Self;
    const FRAC_PI_8: Self;
    const SQRT_2: Self;
    const FRAC_1_SQRT_2: Self;
    const LN_2: Self;
    const LN_10: Self;
    const LOG2_E: Self;
    const LOG10_E: Self;

    fn floor(self) -> Self;
    fn ceil(self) -> Self;
    fn round(self) -> Self;
    fn trunc(self) -> Self;
    fn fract(self) -> Self;
    fn recip(self) -> Self;

    #[cfg(feature = "std")]
    fn mul_add(self, a: Self, b: Self) -> Self;

    #[cfg(feature = "std")]
    fn powi(self, n: i32) -> Self;
    #[cfg(feature = "std")]
    fn powf(self, n: Self) -> Self;
    #[cfg(feature = "std")]
    fn sqrt(self) -> Self;
    #[cfg(feature = "std")]
    fn cbrt(self) -> Self;
    #[cfg(feature = "std")]
    fn exp(self) -> Self;
    #[cfg(feature = "std")]
    fn exp2(self) -> Self;
    #[cfg(feature = "std")]
    fn exp_m1(self) -> Self;
    #[cfg(feature = "std")]
    fn log(self, base: Self) -> Self;
    #[cfg(feature = "std")]
    fn ln(self) -> Self;
    #[cfg(feature = "std")]
    fn log2(self) -> Self;
    #[cfg(feature = "std")]
    fn log10(self) -> Self;
    #[cfg(feature = "std")]
    fn ln_1p(self) -> Self;

    #[cfg(feature = "std")]
    fn hypot(self, other: Self) -> Self;
    #[cfg(feature = "std")]
    fn sin(self) -> Self;
    #[cfg(feature = "std")]
    fn cos(self) -> Self;
    #[cfg(feature = "std")]
    fn tan(self) -> Self;
    #[cfg(feature = "std")]
    fn asin(self) -> Self;
    #[cfg(feature = "std")]
    fn acos(self) -> Self;
    #[cfg(feature = "std")]
    fn atan(self) -> Self;
    #[cfg(feature = "std")]
    fn atan2(self, other: Self) -> Self;
    #[cfg(feature = "std")]
    fn sin_cos(self) -> (Self, Self);
    #[cfg(feature = "std")]
    fn sinh(self) -> Self;
    #[cfg(feature = "std")]
    fn cosh(self) -> Self;
    #[cfg(feature = "std")]
    fn tanh(self) -> Self;
    #[cfg(feature = "std")]
    fn asinh(self) -> Self;
    #[cfg(feature = "std")]
    fn acosh(self) -> Self;
    #[cfg(feature = "std")]
    fn atanh(self) -> Self;
}

/// Floating-point representations.
///
/// Types that implement this trait are represented using IEEE-754 encoding and
/// expose the details of that encoding, including infinities, `NaN`, and
/// operations on real numbers. This trait is implemented by primitive
/// floating-point types and the `Total` proxy type.
pub trait Float: Encoding + Infinite + IntrinsicOrd + Nan + Real {}

impl<T> Float for T where T: Encoding + Infinite + IntrinsicOrd + Nan + Real {}