aurum_numeric/
clamp.rs

1// Copyright (c) 2016-2017 <daggerbot@gmail.com>
2// This software is available under the terms of the zlib license.
3// See COPYING.md for more information.
4
5use std::{i8, i16, i32, i64, isize, u8, u16, u32, u64, usize, f32, f64};
6use std::fmt::{self, Display, Formatter};
7use std::mem;
8
9/// Error which may occur when attempting to interpolate between clamped values of different types.
10#[derive(Clone, Copy, Debug, Eq, PartialEq)]
11pub enum ClampError {
12    Nan,
13    Min,
14    Max,
15}
16
17impl ClampError {
18    fn as_str (self) -> &'static str {
19        match self {
20            ClampError::Nan => "value is not a number",
21            ClampError::Min => "value is below clamped range",
22            ClampError::Max => "value is above clamped range",
23        }
24    }
25}
26
27impl Display for ClampError {
28    fn fmt (&self, f: &mut Formatter) -> fmt::Result {
29        f.write_str(self.as_str())
30    }
31}
32
33#[cfg(feature = "std")]
34impl ::std::error::Error for ClampError {
35    fn description (&self) -> &str { self.as_str() }
36}
37
38/// Trait for scalar values which have a "clamped" range.
39///
40/// For integer values, the clamped range is from 0 to `$ty::MAX`.
41/// For floating point values, the clamped range is from 0.0 to 1.0.
42pub trait Clamp: Sized + PartialOrd {
43    fn clamp_min () -> Self;
44    fn clamp_max () -> Self;
45
46    fn clamp (self) -> Self {
47        let min = Self::clamp_min();
48
49        if self.is_clamp_nan() || self < min {
50            return min;
51        }
52
53        let max = Self::clamp_max();
54
55        if self > max {
56            max
57        } else {
58            self
59        }
60    }
61
62    fn is_clamp_nan (&self) -> bool;
63}
64
65macro_rules! impl_clamp_int {
66    ($($ty:ident),*) => { $(
67        impl Clamp for $ty {
68            #[inline(always)]
69            fn clamp_min () -> $ty { 0 }
70            
71            #[inline(always)]
72            fn clamp_max () -> $ty { $ty::MAX }
73
74            #[inline(always)]
75            fn clamp (self) -> $ty { if self < 0 { 0 } else { self }}
76
77            #[inline(always)]
78            fn is_clamp_nan (&self) -> bool { false }
79        }
80    )* };
81}
82
83macro_rules! impl_clamp_uint {
84    ($($ty:ident),*) => { $(
85        impl Clamp for $ty {
86            #[inline(always)]
87            fn clamp_min () -> $ty { 0 }
88            
89            #[inline(always)]
90            fn clamp_max () -> $ty { $ty::MAX }
91
92            #[inline(always)]
93            fn clamp (self) -> $ty { self }
94
95            #[inline(always)]
96            fn is_clamp_nan (&self) -> bool { false }
97        }
98    )* };
99}
100
101macro_rules! impl_clamp_float {
102    ($($ty:ident),*) => { $(
103        impl Clamp for $ty {
104            #[inline(always)]
105            fn clamp_min () -> $ty { 0.0 }
106            
107            #[inline(always)]
108            fn clamp_max () -> $ty { 1.0 }
109
110            #[inline(always)]
111            fn is_clamp_nan (&self) -> bool { self.is_nan() }
112        }
113    )* };
114}
115
116impl_clamp_int!(i8, i16, i32, i64, isize);
117impl_clamp_uint!(u8, u16, u32, u64, usize);
118impl_clamp_float!(f32, f64);
119
120/// Trait for interpolation of clamped values between different types.
121pub trait ClampFrom<F: Sized>: Sized {
122    fn clamp_from (other: F) -> Self;
123    fn saturating_clamp_from (other: F) -> Self;
124    fn try_clamp_from (other: F) -> Result<Self, ClampError>;
125}
126
127impl<T: Clamp> ClampFrom<T> for T {
128    #[inline(always)]
129    fn clamp_from (other: T) -> T { other }
130
131    fn saturating_clamp_from (other: T) -> T {
132        let min = T::clamp_min();
133
134        if other.is_clamp_nan() || other < min {
135            return min;
136        }
137
138        let max = T::clamp_max();
139
140        if other > max {
141            max
142        } else {
143            other
144        }
145    }
146
147    #[inline(always)]
148    fn try_clamp_from (other: T) -> Result<T, ClampError> {
149        if other.is_clamp_nan() {
150            Err(ClampError::Nan)
151        } else if other < T::clamp_min() {
152            Err(ClampError::Min)
153        } else if other > T::clamp_max() {
154            Err(ClampError::Max)
155        } else {
156            Ok(other)
157        }
158    }
159}
160
161macro_rules! impl_clamp_from_int_to_float {
162    { $($f:ident => $t:ty,)* } => { $(
163        impl ClampFrom<$f> for $t {
164            #[inline(always)]
165            fn clamp_from (other: $f) -> $t { other as $t / $f::MAX as $t }
166
167            fn saturating_clamp_from (other: $f) -> $t {
168                if other < 0 {
169                    0.0
170                } else {
171                    other as $t / $f::MAX as $t
172                }
173            }
174
175            fn try_clamp_from (other: $f) -> Result<$t, ClampError> {
176                if other < 0 {
177                    Err(ClampError::Min)
178                } else {
179                    Ok(other as $t / $f::MAX as $t)
180                }
181            }
182        }
183    )* };
184}
185
186macro_rules! impl_clamp_from_uint_to_float {
187    { $($f:ident => $t:ty,)* } => { $(
188        impl ClampFrom<$f> for $t {
189            #[inline(always)]
190            fn clamp_from (other: $f) -> $t { other as $t / $f::MAX as $t }
191
192            #[inline(always)]
193            fn saturating_clamp_from (other: $f) -> $t { other as $t / $f::MAX as $t }
194
195            #[inline(always)]
196            fn try_clamp_from (other: $f) -> Result<$t, ClampError> {
197                Ok(other as $t / $f::MAX as $t)
198            }
199        }
200    )* };
201}
202
203macro_rules! impl_clamp_from_float_to_int {
204    { $($f:ty => $t:ident,)* } => { $(
205        impl ClampFrom<$f> for $t {
206            #[inline(always)]
207            fn clamp_from (other: $f) -> $t { (other * $t::MAX as $f) as $t }
208
209            fn saturating_clamp_from (other: $f) -> $t {
210                if other.is_clamp_nan() || other < 0.0 {
211                    0
212                } else if other > 1.0 {
213                    $t::MAX
214                } else {
215                    (other * $t::MAX as $f) as $t
216                }
217            }
218
219            fn try_clamp_from (other: $f) -> Result<$t, ClampError> {
220                if other.is_clamp_nan() {
221                    Err(ClampError::Nan)
222                } else if other < 0.0 {
223                    Err(ClampError::Min)
224                } else if other > 1.0 {
225                    Err(ClampError::Max)
226                } else {
227                    Ok((other * $t::MAX as $f) as $t)
228                }
229            }
230        }
231    )* };
232}
233
234macro_rules! impl_clamp_from_float_to_float {
235    { $($f:ty => $t:ty,)* } => { $(
236        impl ClampFrom<$f> for $t {
237            #[inline(always)]
238            fn clamp_from (other: $f) -> $t { other as $t }
239
240            fn saturating_clamp_from (other: $f) -> $t {
241                if other.is_clamp_nan() || other < 0.0 {
242                    0.0
243                } else if other > 1.0 {
244                    1.0
245                } else {
246                    other as $t
247                }
248            }
249
250            fn try_clamp_from (other: $f) -> Result<$t, ClampError> {
251                if other.is_clamp_nan() {
252                    Err(ClampError::Nan)
253                } else if other < 0.0 {
254                    Err(ClampError::Min)
255                } else if other > 1.0 {
256                    Err(ClampError::Max)
257                } else {
258                    Ok(other as $t)
259                }
260            }
261        }
262    )* };
263}
264
265macro_rules! impl_clamp_from_uint_expand {
266    { $($f:ident($mul:expr) => $t:ty,)* } => { $(
267        impl ClampFrom<$f> for $t {
268            #[inline(always)]
269            fn clamp_from (other: $f) -> $t { other as $t * ($mul) }
270
271            #[inline(always)]
272            fn saturating_clamp_from (other: $f) -> $t { other as $t * ($mul) }
273
274            #[inline(always)]
275            fn try_clamp_from (other: $f) -> Result<$t, ClampError> { Ok(other as $t * ($mul)) }
276        }
277    )* };
278}
279
280macro_rules! impl_clamp_from_uint_shrink {
281    { $($f:ident($shift:expr) => $t:ty,)* } => { $(
282        impl ClampFrom<$f> for $t {
283            #[inline(always)]
284            fn clamp_from (other: $f) -> $t { (other >> ($shift)) as $t }
285
286            #[inline(always)]
287            fn saturating_clamp_from (other: $f) -> $t { (other >> ($shift)) as $t }
288
289            #[inline(always)]
290            fn try_clamp_from (other: $f) -> Result<$t, ClampError> {
291                Ok((other >> ($shift)) as $t)
292            }
293        }
294    )* };
295}
296
297impl_clamp_from_int_to_float! {
298    i8 => f32,
299    i8 => f64,
300    i16 => f32,
301    i16 => f64,
302    i32 => f32,
303    i32 => f64,
304    i64 => f32,
305    i64 => f64,
306    isize => f32,
307    isize => f64,
308}
309
310impl_clamp_from_uint_to_float! {
311    u8 => f32,
312    u8 => f64,
313    u16 => f32,
314    u16 => f64,
315    u32 => f32,
316    u32 => f64,
317    u64 => f32,
318    u64 => f64,
319    usize => f32,
320    usize => f64,
321}
322
323impl_clamp_from_float_to_int! {
324    f32 => i8,
325    f32 => i16,
326    f32 => i32,
327    f32 => i64,
328    f32 => isize,
329    f32 => u8,
330    f32 => u16,
331    f32 => u32,
332    f32 => u64,
333    f32 => usize,
334    f64 => i8,
335    f64 => i16,
336    f64 => i32,
337    f64 => i64,
338    f64 => isize,
339    f64 => u8,
340    f64 => u16,
341    f64 => u32,
342    f64 => u64,
343    f64 => usize,
344}
345
346impl_clamp_from_float_to_float! {
347    f32 => f64,
348    f64 => f32,
349}
350
351// TODO: impl_clamp_from_int_expand
352// TODO: impl_clamp_from_int_shrink
353
354impl_clamp_from_uint_expand! {
355    u8(0x0101) => u16,
356    u8(0x0101_0101) => u32,
357    u8(0x0101_0101_0101_0101) => u64,
358    u8(0x0101_0101_0101_0101u64 as usize) => usize,
359    u16(0x0001_0001) => u32,
360    u16(0x0001_0001_0001_0001) => u64,
361    u16(0x0001_0001_0001_0001u64 as usize) => usize,
362    u32(0x0000_0001_0000_0001) => u64,
363    u32(0x0000_0001_0000_0001u64 as usize) => usize,
364    usize((usize::MAX as u64).wrapping_add(2)) => u64,
365}
366
367impl_clamp_from_uint_shrink! {
368    u16(8) => u8,
369    u32(24) => u8,
370    u32(16) => u16,
371    u64(56) => u8,
372    u64(48) => u16,
373    u64(32) => u32,
374    u64(64 - mem::size_of::<usize>() * 8) => usize,
375    usize(mem::size_of::<usize>() * 8 - 8) => u8,
376    usize(mem::size_of::<usize>() * 8 - 16) => u16,
377    usize(mem::size_of::<usize>() * 8 - 32) => u32,
378}
379
380/// Blanket impl over `ClampFrom`.
381pub trait ClampInto<T: Sized> : Sized {
382    fn clamp_into (self) -> T;
383    fn saturating_clamp_into (self) -> T;
384    fn try_clamp_into (self) -> Result<T, ClampError>;
385}
386
387impl<F: Sized, T: ClampFrom<F>> ClampInto<T> for F {
388    #[inline(always)]
389    fn clamp_into (self) -> T { T::clamp_from(self) }
390
391    #[inline(always)]
392    fn saturating_clamp_into (self) -> T { T::saturating_clamp_from(self) }
393
394    #[inline(always)]
395    fn try_clamp_into (self) -> Result<T, ClampError> {
396        T::try_clamp_from(self)
397    }
398}