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
use core::{
    cmp::Ordering,
    convert::TryFrom,
    fmt,
    hash::{Hash, Hasher},
};

use serde::{Deserialize, Serialize};

use crate::ClosedUnitF64;

#[derive(Debug)]
#[allow(clippy::module_name_repetitions)]
pub struct ClosedOpenUnitF64Error(f64);

impl fmt::Display for ClosedOpenUnitF64Error {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
        write!(fmt, "{} is not in [0.0, 1.0).", self.0)
    }
}

#[allow(clippy::unsafe_derive_deserialize)]
#[derive(Copy, Clone, Deserialize, Serialize, TypeLayout)]
#[repr(transparent)]
#[serde(try_from = "f64", into = "f64")]
pub struct ClosedOpenUnitF64(f64);

impl fmt::Display for ClosedOpenUnitF64 {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
        fmt::Display::fmt(&self.0, fmt)
    }
}

impl TryFrom<f64> for ClosedOpenUnitF64 {
    type Error = ClosedOpenUnitF64Error;

    fn try_from(value: f64) -> Result<Self, Self::Error> {
        Self::new(value)
    }
}

impl From<ClosedOpenUnitF64> for f64 {
    fn from(val: ClosedOpenUnitF64) -> Self {
        val.get()
    }
}

impl fmt::Debug for ClosedOpenUnitF64 {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
        struct ClosedOpenUnitF64Range(f64);

        impl fmt::Debug for ClosedOpenUnitF64Range {
            fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
                write!(fmt, "0.0 <= {} < 1.0", self.0)
            }
        }

        fmt.debug_tuple("ClosedOpenUnitF64")
            .field(&ClosedOpenUnitF64Range(self.0))
            .finish()
    }
}

impl ClosedOpenUnitF64 {
    /// # Errors
    ///
    /// Returns `ClosedOpenUnitF64Error` if not `0.0 <= value < 1.0`
    pub const fn new(value: f64) -> Result<Self, ClosedOpenUnitF64Error> {
        if value >= 0.0 && value < 1.0 {
            Ok(Self(value))
        } else {
            Err(ClosedOpenUnitF64Error(value))
        }
    }

    /// # Safety
    ///
    /// Only safe iff `0.0 <= value < 1.0`
    #[must_use]
    pub const unsafe fn new_unchecked(value: f64) -> Self {
        Self(value)
    }

    #[must_use]
    pub const fn get(self) -> f64 {
        self.0
    }
}

impl PartialEq for ClosedOpenUnitF64 {
    #[allow(clippy::unconditional_recursion)]
    fn eq(&self, other: &Self) -> bool {
        self.0.eq(&other.0)
    }
}

impl Eq for ClosedOpenUnitF64 {}

impl PartialOrd for ClosedOpenUnitF64 {
    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
        Some(self.cmp(other))
    }
}

impl Ord for ClosedOpenUnitF64 {
    fn cmp(&self, other: &Self) -> Ordering {
        self.0.total_cmp(&other.0)
    }
}

impl Hash for ClosedOpenUnitF64 {
    fn hash<H: Hasher>(&self, state: &mut H) {
        self.0.to_bits().hash(state);
    }
}

impl PartialEq<f64> for ClosedOpenUnitF64 {
    fn eq(&self, other: &f64) -> bool {
        self.0.eq(other)
    }
}

impl PartialOrd<f64> for ClosedOpenUnitF64 {
    fn partial_cmp(&self, other: &f64) -> Option<Ordering> {
        self.0.partial_cmp(other)
    }
}

impl PartialEq<ClosedUnitF64> for ClosedOpenUnitF64 {
    fn eq(&self, other: &ClosedUnitF64) -> bool {
        self.0.eq(&other.get())
    }
}

impl PartialOrd<ClosedUnitF64> for ClosedOpenUnitF64 {
    fn partial_cmp(&self, other: &ClosedUnitF64) -> Option<Ordering> {
        self.0.partial_cmp(&other.get())
    }
}