├── .gitignore ├── CHANGELOG.md ├── Cargo.toml ├── LICENSE ├── Readme.md ├── examples └── sudoku.rs └── src ├── arithmetics.rs ├── conversions.rs ├── holder.rs ├── iter.rs ├── lib.rs ├── tests.rs └── value_check.rs /.gitignore: -------------------------------------------------------------------------------- 1 | # Generated by Cargo 2 | # will have compiled files and executables 3 | /target/ 4 | 5 | # Remove Cargo.lock from gitignore if creating an executable, leave it for libraries 6 | # More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html 7 | Cargo.lock 8 | 9 | # These are backup files generated by rustfmt 10 | **/*.rs.bk 11 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | ## 0.9.0 4 | 5 | Works on nightly-2024-10-12. 6 | - **Removed** const arithmetics to be supported by the current version of Rust (nightly-2024-10-12). 7 | 8 | ## 0.8.0 9 | 10 | Works on nightly-2024-02-04 11 | - **Changed** updated to the new version of Rust (nightly-2024-02-04). 12 | - (**Changed** to prevent ICEs, the internals were reverted to the old byte array-based version; 13 | the specialization feature was disabled and an additional trait bound was added to Ranged type out of necessity). 14 | 15 | ## 0.7.1 16 | 17 | Works on nightly-2022-11-26 18 | - **Changed** const-ify array indexing traits. 19 | - **Added** type constraining comparisons `fit_less_than`, `fit_less_eq`, `fit_greater_than`, `fit_greater_eq`. 20 | - (**Changed** the naming of internals). 21 | 22 | ## 0.7.0 23 | 24 | Works on nightly-2022-11-26 25 | - **Changed** updated to the new version of Nightly rust. 26 | - **Changed** globally revamped the internal structure of Ranged. 27 | - **Added** `rmatch` macro and pattern matching over Ranged. 28 | 29 | ## 0.6.0 30 | 31 | - **Removed** `range` function. 32 | - **Changed** `try_expand` into `fit` function (deprecated `try_expand`). 33 | - **Added** `fit_min` and `fit_max` functions. 34 | - **Added** comparisons. 35 | - **Added** `ConstInclusiveRange` zero-size structure with `IntoIterator` instead of `fn range()`. 36 | - **Added** slicing arrays by `ConstInclusiveRange` with fixed-size array reference output. 37 | - **Fixed** `r!` revamp and support of ranges (MIN..END and MIN..=MAX) 38 | 39 | ## 0.5.1 40 | 41 | - **Added** `r!` support of `range`. 42 | - **Added** `FromStr` trait for ranged. 43 | - **Added** Comparisons `i128` vs `Ranged`. 44 | - **Fixed** Auto Trait Implementations 45 | 46 | ## 0.5.0 47 | 48 | - **Changed** updated to the new version of Nightly rust. 49 | - **Added** `abs`, `div_euclid`, `rem_euclid` functions. 50 | 51 | ## 0.4.2 52 | 53 | - **Added** iterator over ranges, `iter_from` method and `range` function, indexing arrays by `Ranged`. 54 | - **Added** [sudoku](examples/sudoku.rs) example. 55 | 56 | ## 0.4.1 57 | 58 | - **Added** to and from `isize` and `usize` conversions 59 | 60 | ## 0.4.0 61 | 62 | - **Changed**: the "constant" `Ranged` types made zero-sized 63 | 64 | ## 0.3.1 65 | 66 | - **Fixed** `r!` macro failure with `const_evaluatable_checked` enabled 67 | - **Added** `min` and `max` functions 68 | 69 | ## 0.3.0 70 | 71 | First release 72 | 73 | ## 0.1 - 0.2 74 | 75 | Not published, built for the old versions of nightly Rust 76 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | edition = "2021" 3 | name = "ranged_integers" 4 | version = "0.9.0" 5 | authors = ["disiamylborane"] 6 | description = "An integer restricted to a compile-time defined bounds driven by const generics" 7 | readme = "Readme.md" 8 | keywords = ["integer","nightly","bounded","refinement"] 9 | categories = ["data-structures", "no-std", "rust-patterns"] 10 | license = "MIT" 11 | repository = "https://github.com/disiamylborane/ranged_integers" -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 disiamylborane 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Readme.md: -------------------------------------------------------------------------------- 1 | # Ranged integers [nightly only] 2 | 3 | **Note: the library relies on the incomplete Rust features:** 4 | - It causes ICEson some Rust toolchains, 5 | - The usage may increase the time compilation time heavily. 6 | - The current version (0.9.0) was tested on nightly-2024-10-12. 7 | 8 | [Documentation at docs.rs](https://docs.rs/ranged_integers) 9 | 10 | [Sudoku example](https://github.com/disiamylborane/ranged_integers/blob/master/examples/sudoku.rs) 11 | 12 | [Changelog](https://github.com/disiamylborane/ranged_integers/blob/master/CHANGELOG.md) 13 | 14 | Provides a generic type `Ranged` representing an integer 15 | within a specified range. It automatically chooses the data size guided by 16 | the range specified (so `Ranged<-50, 50>` is of 1 byte while 17 | `Ranged<-20_000, 100_000>` is of 4 bytes) and supports the arithmetic operations 18 | with automatic bound recalculation and range iteration. The fixed sized arrays can be 19 | indexed with a ranged integer having the fitting bounds. 20 | 21 | The conversion and arithmetic functions catch the errors such as possible overflow 22 | and zero division at compile time. 23 | 24 | ## Example 25 | 26 | ```rust 27 | #![allow(incomplete_features)] 28 | #![feature(adt_const_params, generic_const_exprs)] 29 | 30 | extern crate ranged_integers; 31 | use ranged_integers::*; 32 | 33 | // Consider a simple race game. The player rolls a 34 | // die and then moves forward, backward or forward 35 | // with the double speed according to some rules. 36 | 37 | enum MoveType {MoveForward, DoubleSpeed, MoveBackward} 38 | 39 | // Get a die roll using a standard random number generator 40 | fn roll_die(rng: &mut dyn rand::RngCore) -> Ranged<1, 6> { 41 | let random: u8 = rng.gen(); 42 | // The consistency is proved at compile time: 43 | // r!(6) means Ranged<6,6> with the value 6 44 | // r!(1) means Ranged<1,1> with the value 1 45 | // u8 % Ranged<6, 6> = Ranged<0, 5> 46 | // Ranged<0, 5> + Ranged<1, 1> = Ranged<1, 6> 47 | random % r!(6) + r!(1) 48 | } 49 | 50 | // Calculate where the player must move 51 | // The result fits the range -6..=12 52 | fn move_player( 53 | mtype: MoveType, 54 | dice_points: Ranged<1, 6> 55 | ) -> Ranged<-6, 12> 56 | { 57 | match move_type { 58 | MoveType::MoveForward => { 59 | // Expand 1..=6 to -6..=12 60 | dice_points.expand() 61 | } 62 | MoveType::DoubleSpeed => { 63 | let mv = dice_points*r!(2); // Ranged<2, 12> 64 | mv.expand() // Expand to -6..=12 65 | } 66 | MoveType::MoveBackward => { 67 | let mv = -dice_points; // Ranged<-6, -1> 68 | mv.expand() // Expand to -6..=12 69 | } 70 | } 71 | } 72 | ``` 73 | -------------------------------------------------------------------------------- /examples/sudoku.rs: -------------------------------------------------------------------------------- 1 | //! A Sudoku problem from RosettaCode (https://rosettacode.org/wiki/Sudoku#Rust) 2 | //! rewritten with the use of ranged_integers library 3 | 4 | #![allow(incomplete_features)] 5 | #![feature(adt_const_params,generic_const_exprs)] 6 | 7 | use ranged_integers::*; 8 | 9 | pub const ROW_SIZE : usize = 9; 10 | 11 | pub type Val = Ranged<0, 9>; 12 | pub type RowIndex = Ranged<0, {({ROW_SIZE - 1}) as _}>; 13 | pub type Sudoku = [[Val; 9]; 9]; 14 | 15 | pub fn is_valid(val: Val, x: RowIndex, y: RowIndex, sudoku_ar: &Sudoku) -> bool { 16 | r!(0..9).into_iter().all( |i| 17 | sudoku_ar[x][i] != val && 18 | sudoku_ar[i][y] != val && { 19 | r!(0..3).into_iter().all(|i| r!(0..3).into_iter().all(|j| 20 | sudoku_ar[x / r!(3) * r!(3) + i][y / r!(3) * r!(3) + j] != val 21 | )) 22 | } 23 | ) 24 | } 25 | 26 | pub fn place_number(pos: Ranged<0, 80>, sudoku_ar: &mut Sudoku) -> bool { 27 | pos.iter_up() 28 | .find_map(|p| { 29 | let (x, y) = (p % r!(9), p / r!(9)); 30 | if sudoku_ar[x][y] == r!(0) {Some((x,y))} else {None} 31 | }) 32 | .map_or(true, |(x, y)| { 33 | for n in r!(1..10) { 34 | if is_valid(n.expand(), x, y, sudoku_ar) { 35 | sudoku_ar[x][y] = n.expand(); 36 | let next = if let Some(next) = (pos + r!(1)).fit() {next} else {return true}; 37 | if place_number(next,sudoku_ar) { 38 | return true; 39 | } 40 | sudoku_ar[x][y] = r!([]0); 41 | } 42 | } 43 | false 44 | }) 45 | } 46 | 47 | pub fn pretty_print(sudoku_ar: Sudoku) { 48 | let line_sep = "------+-------+------"; 49 | println!("{}", line_sep); 50 | for (i, row) in sudoku_ar.iter().enumerate() { 51 | for (j, val) in row.iter().enumerate() { 52 | print!("{} ", val); 53 | if j == 2 || j == 5 { 54 | print!("| "); 55 | } 56 | } 57 | println!(); 58 | if i % 3 == 2 { 59 | println!("{}", line_sep); 60 | } 61 | } 62 | } 63 | 64 | fn solve(sudoku_ar: &mut Sudoku)->bool { 65 | place_number(r!([] 0), sudoku_ar) 66 | } 67 | 68 | macro_rules! rangedarr { 69 | ($($e:literal),* $(,)?) => { [$( r!([] $e) ),*] }; 70 | } 71 | 72 | fn main() { 73 | let mut sudoku_ar: Sudoku = [ 74 | rangedarr![8, 5, 0, 0, 0, 2, 4, 0, 0], 75 | rangedarr![7, 2, 0, 0, 0, 0, 0, 0, 9], 76 | rangedarr![0, 0, 4, 0, 0, 0, 0, 0, 0], 77 | rangedarr![0, 0, 0, 1, 0, 7, 0, 0, 2], 78 | rangedarr![3, 0, 5, 0, 0, 0, 9, 0, 0], 79 | rangedarr![0, 4, 0, 0, 0, 0, 0, 0, 0], 80 | rangedarr![0, 0, 0, 0, 8, 0, 0, 7, 0], 81 | rangedarr![0, 1, 7, 0, 0, 0, 0, 0, 0], 82 | rangedarr![0, 0, 0, 0, 3, 6, 0, 4, 0], 83 | ]; 84 | 85 | if solve(&mut sudoku_ar) { 86 | pretty_print(sudoku_ar); 87 | } 88 | else { 89 | println!("Unsolvable"); 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /src/arithmetics.rs: -------------------------------------------------------------------------------- 1 | // Any arithmetic operation are first of all to recalculate the bounds 2 | 3 | use crate::{holder::{AlignWrap, Aligner}, irang, max_irang, memlayout, min_irang, Assert, IsAllowed, OperationPossibility, Ranged}; 4 | 5 | impl 6 | core::ops::Add> for Ranged 7 | where 8 | [(); memlayout(AMIN, AMAX).bytes()]:, 9 | [(); memlayout(BMIN, BMAX).bytes()]:, 10 | AlignWrap<{memlayout(AMIN, AMAX)}>: Aligner, 11 | AlignWrap<{memlayout(BMIN, BMAX)}>: Aligner, 12 | [(); memlayout(AMIN + BMIN, AMAX + BMAX).bytes()]:, 13 | AlignWrap<{memlayout(AMIN + BMIN, AMAX + BMAX)}>: Aligner, 14 | { 15 | type Output = Ranged<{ AMIN + BMIN }, { AMAX + BMAX }>; 16 | 17 | fn add(self, rhs: Ranged) -> Self::Output { 18 | unsafe { Ranged::__unsafe_new(self.get() + rhs.get()) } 19 | } 20 | } 21 | 22 | impl 23 | core::ops::Sub> for Ranged 24 | where 25 | [(); memlayout(AMIN, AMAX).bytes()]:, 26 | [(); memlayout(BMIN, BMAX).bytes()]:, 27 | [(); memlayout(AMIN - BMAX, AMAX - BMIN).bytes()]:, 28 | AlignWrap<{memlayout(AMIN, AMAX)}>: Aligner, 29 | AlignWrap<{memlayout(BMIN, BMAX)}>: Aligner, 30 | AlignWrap<{memlayout(AMIN - BMAX, AMAX - BMIN)}>: Aligner, 31 | { 32 | type Output = Ranged<{ AMIN - BMAX }, { AMAX - BMIN }>; 33 | 34 | fn sub(self, rhs: Ranged) -> Self::Output { 35 | unsafe { Ranged::__unsafe_new(self.get() - rhs.get()) } 36 | } 37 | } 38 | 39 | // The current (10.2022) of Rust are not capable to work with iterators out-of-the-box 40 | // in const environments. The following macros and functions are the ad-hoc replacers 41 | // for min/max functions 42 | macro_rules! reduce { 43 | ($fn:ident, $a:expr) => ( $a ); 44 | ($fn:ident, $a:expr, $($args:expr),+) => { 45 | { 46 | $fn($a, reduce!($fn, $($args),+ )) 47 | } 48 | }; 49 | } 50 | 51 | const fn max_4(vals: (irang, irang, irang, irang)) -> irang { 52 | reduce!(max_irang, vals.0, vals.1, vals.2, vals.3) 53 | } 54 | const fn min_4(vals: (irang, irang, irang, irang)) -> irang { 55 | reduce!(min_irang, vals.0, vals.1, vals.2, vals.3) 56 | } 57 | #[must_use] 58 | #[doc(hidden)] 59 | pub const fn max_cross(a_min: irang, a_max: irang, b_min: irang, b_max: irang) -> irang { 60 | max_4((a_min * b_min, a_min * b_max, a_max * b_min, a_max * b_max)) 61 | } 62 | #[must_use] 63 | #[doc(hidden)] 64 | pub const fn min_cross(a_min: irang, a_max: irang, b_min: irang, b_max: irang) -> irang { 65 | min_4((a_min * b_min, a_min * b_max, a_max * b_min, a_max * b_max)) 66 | } 67 | 68 | impl 69 | core::ops::Mul> for Ranged 70 | where 71 | [(); memlayout(AMIN, AMAX).bytes()]: , 72 | [(); memlayout(BMIN, BMAX).bytes()]: , 73 | [(); memlayout( 74 | min_cross(AMIN, AMAX, BMIN, BMAX), 75 | max_cross(AMIN, AMAX, BMIN, BMAX), 76 | ) 77 | .bytes()]: , 78 | AlignWrap<{memlayout(AMIN, AMAX)}>: Aligner, 79 | AlignWrap<{memlayout(BMIN, BMAX)}>: Aligner, 80 | AlignWrap<{memlayout( 81 | min_cross(AMIN, AMAX, BMIN, BMAX), 82 | max_cross(AMIN, AMAX, BMIN, BMAX), 83 | )}>: Aligner, 84 | { 85 | type Output = 86 | Ranged<{ min_cross(AMIN, AMAX, BMIN, BMAX) }, { max_cross(AMIN, AMAX, BMIN, BMAX) }>; 87 | 88 | fn mul(self, rhs: Ranged) -> Self::Output { 89 | unsafe { Ranged::__unsafe_new(self.get() * rhs.get()) } 90 | } 91 | } 92 | 93 | #[must_use] 94 | #[doc(hidden)] 95 | pub const fn allow_division(b_min: irang, b_max: irang) -> OperationPossibility { 96 | if ((b_min > 0) && (b_max > 0)) || ((b_min < 0) && (b_max < 0)) { 97 | OperationPossibility::Allowed 98 | } else { 99 | OperationPossibility::Forbidden 100 | } 101 | } 102 | 103 | #[must_use] 104 | #[doc(hidden)] 105 | pub const fn singleside_div_min(a_min: irang, a_max: irang, b_min: irang, b_max: irang) -> irang { 106 | min_4((a_min / b_min, a_min / b_max, a_max / b_min, a_max / b_max)) 107 | } 108 | #[must_use] 109 | #[doc(hidden)] 110 | pub const fn singleside_div_max(a_min: irang, a_max: irang, b_min: irang, b_max: irang) -> irang { 111 | max_4((a_min / b_min, a_min / b_max, a_max / b_min, a_max / b_max)) 112 | } 113 | 114 | impl 115 | core::ops::Div> for Ranged 116 | where 117 | [(); memlayout(AMIN, AMAX).bytes()]: , 118 | [(); memlayout(BMIN, BMAX).bytes()]: , 119 | [(); memlayout( 120 | singleside_div_min(AMIN, AMAX, BMIN, BMAX), 121 | singleside_div_max(AMIN, AMAX, BMIN, BMAX), 122 | ) 123 | .bytes()]: , 124 | AlignWrap<{memlayout(AMIN, AMAX)}>: Aligner, 125 | AlignWrap<{memlayout(BMIN, BMAX)}>: Aligner, 126 | AlignWrap<{memlayout( 127 | singleside_div_min(AMIN, AMAX, BMIN, BMAX), 128 | singleside_div_max(AMIN, AMAX, BMIN, BMAX), 129 | )}>: Aligner, 130 | 131 | Assert<{ allow_division(BMIN, BMAX) }>: IsAllowed, 132 | { 133 | type Output = Ranged< 134 | { singleside_div_min(AMIN, AMAX, BMIN, BMAX) }, 135 | { singleside_div_max(AMIN, AMAX, BMIN, BMAX) }, 136 | >; 137 | 138 | fn div(self, rhs: Ranged) -> Self::Output { 139 | unsafe { Ranged::__unsafe_new(self.get() / rhs.get()) } 140 | } 141 | } 142 | 143 | #[must_use] 144 | #[doc(hidden)] 145 | pub const fn singleside_rem_min(a_min: irang, a_max: irang, b_min: irang, b_max: irang) -> irang { 146 | // Note that b_min..=b_max must never include 0 147 | if b_min == b_max { 148 | if a_min == a_max {return a_min % b_min} 149 | else if a_min > 0 { 150 | let base = a_max / b_min.abs() * b_min.abs(); 151 | if a_min >= base { 152 | return a_min % b_min; 153 | } 154 | } 155 | else if a_max < 0 { 156 | let base = a_min / b_min.abs() * b_min.abs(); 157 | if a_max <= base { 158 | return a_min % b_min; 159 | } 160 | } 161 | } 162 | 163 | if a_min >= 0 {0} 164 | else { 165 | max_irang(1 - max_irang(b_max.abs(), b_min.abs()), a_min) 166 | } 167 | } 168 | 169 | 170 | #[must_use] 171 | #[doc(hidden)] 172 | pub const fn singleside_rem_max(a_min: irang, a_max: irang, b_min: irang, b_max: irang) -> irang { 173 | if b_min == b_max { 174 | if a_min == a_max {return a_min % b_min} 175 | else if a_min > 0 { 176 | let base = a_max / b_min.abs() * b_min.abs(); 177 | if a_min >= base { 178 | return a_max % b_min; 179 | } 180 | } 181 | else if a_max < 0 { 182 | let base = a_min / b_min.abs() * b_min.abs(); 183 | if a_max <= base { 184 | return a_max % b_min; 185 | } 186 | } 187 | } 188 | 189 | if a_max <= 0 {0} 190 | else { 191 | min_irang(max_irang(b_max.abs(), b_min.abs()) - 1, a_max) 192 | } 193 | } 194 | 195 | 196 | 197 | 198 | 199 | impl 200 | core::ops::Rem> for Ranged 201 | where 202 | [(); memlayout(AMIN, AMAX).bytes()]: , 203 | [(); memlayout(BMIN, BMAX).bytes()]: , 204 | [(); memlayout( 205 | singleside_rem_min(AMIN, AMAX, BMIN, BMAX), 206 | singleside_rem_max(AMIN, AMAX, BMIN, BMAX), 207 | ) 208 | .bytes()]: , 209 | AlignWrap<{memlayout(AMIN, AMAX)}>: Aligner, 210 | AlignWrap<{memlayout(BMIN, BMAX)}>: Aligner, 211 | AlignWrap<{memlayout( 212 | singleside_rem_min(AMIN, AMAX, BMIN, BMAX), 213 | singleside_rem_max(AMIN, AMAX, BMIN, BMAX), 214 | )}>: Aligner, 215 | 216 | Assert<{ allow_division(BMIN, BMAX) }>: IsAllowed, 217 | { 218 | type Output = Ranged< 219 | { singleside_rem_min(AMIN, AMAX, BMIN, BMAX) }, 220 | { singleside_rem_max(AMIN, AMAX, BMIN, BMAX) }, 221 | >; 222 | 223 | fn rem(self, rhs: Ranged) -> Self::Output { 224 | unsafe { Ranged::__unsafe_new(self.get() % rhs.get()) } 225 | } 226 | } 227 | 228 | impl core::ops::Neg for Ranged 229 | where 230 | [(); memlayout(MIN, MAX).bytes()]: , 231 | [(); memlayout(-MAX, -MIN).bytes()]: , 232 | AlignWrap<{memlayout(MIN, MAX)}>: Aligner, 233 | AlignWrap<{memlayout(-MAX, -MIN)}>: Aligner, 234 | { 235 | type Output = Ranged<{ -MAX }, { -MIN }>; 236 | 237 | fn neg(self) -> Self::Output { 238 | unsafe { Ranged::__unsafe_new(-self.get()) } 239 | } 240 | } 241 | 242 | 243 | #[allow(clippy::use_self)] // False positive clippy lint 244 | impl 245 | core::cmp::PartialEq> for irang 246 | where 247 | [(); memlayout(MIN, MAX).bytes()]: , 248 | AlignWrap<{memlayout(MIN, MAX)}>: Aligner, 249 | { 250 | fn eq(&self, other: &Ranged) -> bool { 251 | *self == other.get() 252 | } 253 | 254 | #[allow(clippy::partialeq_ne_impl)] // Clippy makes a row, but it's mandatory in const trait impl to implement it 255 | fn ne(&self, other: &Ranged) -> bool { 256 | !self.eq(other) 257 | } 258 | } 259 | 260 | impl 261 | core::cmp::PartialEq for Ranged 262 | where 263 | [(); memlayout(MIN, MAX).bytes()]: , 264 | AlignWrap<{memlayout(MIN, MAX)}>: Aligner, 265 | { 266 | fn eq(&self, other: &irang) -> bool { 267 | self.get() == *other 268 | } 269 | 270 | #[allow(clippy::partialeq_ne_impl)] // Clippy makes a row, but it's mandatory in const trait impl to implement it 271 | fn ne(&self, other: &irang) -> bool { 272 | !self.eq(other) 273 | } 274 | } 275 | 276 | 277 | impl 278 | core::cmp::PartialEq> for Ranged 279 | where 280 | [(); memlayout(AMIN, AMAX).bytes()]: , 281 | [(); memlayout(BMIN, BMAX).bytes()]: , 282 | AlignWrap<{memlayout(AMIN, AMAX)}>: Aligner, 283 | AlignWrap<{memlayout(BMIN, BMAX)}>: Aligner, 284 | { 285 | fn eq(&self, rhs: &Ranged) -> bool { 286 | self.get() == rhs.get() 287 | } 288 | 289 | #[allow(clippy::partialeq_ne_impl)] // Clippy makes a row, but it's mandatory in const trait impl to implement it 290 | fn ne(&self, other: &Ranged) -> bool { 291 | !self.eq(other) 292 | } 293 | } 294 | 295 | impl core::cmp::Eq for Ranged 296 | where [(); memlayout(AMIN, AMAX).bytes()]: , 297 | AlignWrap<{memlayout(AMIN, AMAX)}>: Aligner, 298 | {} 299 | 300 | 301 | impl 302 | core::cmp::PartialOrd> for Ranged 303 | where 304 | [(); memlayout(AMIN, AMAX).bytes()]: , 305 | [(); memlayout(BMIN, BMAX).bytes()]: , 306 | AlignWrap<{memlayout(AMIN, AMAX)}>: Aligner, 307 | AlignWrap<{memlayout(BMIN, BMAX)}>: Aligner, 308 | { 309 | fn partial_cmp(&self, other: &Ranged) -> Option { 310 | let s = self.get(); 311 | let o = other.get(); 312 | #[allow(clippy::comparison_chain)] 313 | Some(if s>o {core::cmp::Ordering::Greater} 314 | else if s==o {core::cmp::Ordering::Equal} 315 | else {core::cmp::Ordering::Less}) 316 | } 317 | 318 | fn lt(&self, other: &Ranged) -> bool { 319 | self.get() < other.get() 320 | } 321 | fn le(&self, other: &Ranged) -> bool { 322 | self.get() <= other.get() 323 | } 324 | fn gt(&self, other: &Ranged) -> bool { 325 | self.get() > other.get() 326 | } 327 | fn ge(&self, other: &Ranged) -> bool { 328 | self.get() >= other.get() 329 | } 330 | } 331 | impl core::cmp::Ord for Ranged 332 | where 333 | [(); memlayout(AMIN, AMAX).bytes()]: , 334 | AlignWrap<{memlayout(AMIN, AMAX)}>: Aligner, 335 | { 336 | fn cmp(&self, other: &Self) -> core::cmp::Ordering { 337 | let s = self.get(); 338 | let o = other.get(); 339 | #[allow(clippy::comparison_chain)] 340 | if s>o {core::cmp::Ordering::Greater} 341 | else if s==o {core::cmp::Ordering::Equal} 342 | else {core::cmp::Ordering::Less} 343 | } 344 | fn max(self, other: Self) -> Self where Self: Sized { 345 | unsafe { Self::__unsafe_new(max_irang(self.get(), other.get() )) } 346 | } 347 | fn min(self, other: Self) -> Self where Self: Sized { 348 | unsafe { Self::__unsafe_new(min_irang(self.get(), other.get() )) } 349 | } 350 | fn clamp(self, min: Self, max: Self) -> Self where Self: Sized { 351 | ::min(::max(self, min), max) 352 | } 353 | } 354 | 355 | impl 356 | core::cmp::PartialOrd for Ranged 357 | where 358 | [(); memlayout(AMIN, AMAX).bytes()]: , 359 | AlignWrap<{memlayout(AMIN, AMAX)}>: Aligner, 360 | { 361 | fn partial_cmp(&self, other: &irang) -> Option { 362 | let s = self.get(); 363 | #[allow(clippy::comparison_chain)] 364 | Some(if s>*other {core::cmp::Ordering::Greater} 365 | else if s==*other {core::cmp::Ordering::Equal} 366 | else {core::cmp::Ordering::Less}) 367 | } 368 | 369 | fn lt(&self, other: &irang) -> bool { 370 | matches!(self.partial_cmp(other), Some(core::cmp::Ordering::Less)) 371 | } 372 | fn le(&self, other: &irang) -> bool { 373 | !matches!(self.partial_cmp(other), None | Some(core::cmp::Ordering::Greater)) 374 | } 375 | fn gt(&self, other: &irang) -> bool { 376 | matches!(self.partial_cmp(other), Some(core::cmp::Ordering::Greater)) 377 | } 378 | fn ge(&self, other: &irang) -> bool { 379 | matches!(self.partial_cmp(other), Some(core::cmp::Ordering::Greater | core::cmp::Ordering::Equal)) 380 | } 381 | } 382 | 383 | #[allow(clippy::use_self)] // False positive clippy lint 384 | impl 385 | core::cmp::PartialOrd> for irang 386 | where 387 | [(); memlayout(AMIN, AMAX).bytes()]: , 388 | AlignWrap<{memlayout(AMIN, AMAX)}>: Aligner, 389 | { 390 | fn partial_cmp(&self, other: &Ranged) -> Option { 391 | let o = other.get(); 392 | #[allow(clippy::comparison_chain)] 393 | Some(if *self>o {core::cmp::Ordering::Greater} 394 | else if *self==o {core::cmp::Ordering::Equal} 395 | else {core::cmp::Ordering::Less}) 396 | } 397 | 398 | fn lt(&self, other: &Ranged) -> bool { 399 | matches!(self.partial_cmp(other), Some(core::cmp::Ordering::Less)) 400 | } 401 | fn le(&self, other: &Ranged) -> bool { 402 | !matches!(self.partial_cmp(other), None | Some(core::cmp::Ordering::Greater)) 403 | } 404 | fn gt(&self, other: &Ranged) -> bool { 405 | matches!(self.partial_cmp(other), Some(core::cmp::Ordering::Greater)) 406 | } 407 | fn ge(&self, other: &Ranged) -> bool { 408 | matches!(self.partial_cmp(other), Some(core::cmp::Ordering::Greater | core::cmp::Ordering::Equal)) 409 | } 410 | } 411 | 412 | #[must_use] #[doc(hidden)] 413 | pub const fn abs_min(min: irang, max: irang) -> irang { 414 | if min.signum() == max.signum() {min_irang(min.abs(), max.abs())} 415 | else {0} 416 | } 417 | 418 | #[must_use] #[doc(hidden)] 419 | pub const fn abs_max(min: irang, max: irang) -> irang { 420 | max_irang(min.abs(), max.abs()) 421 | } 422 | 423 | 424 | 425 | #[must_use] 426 | #[doc(hidden)] 427 | pub const fn singleside_div_euclid_min(a_min: irang, a_max: irang, b_min: irang, b_max: irang) -> irang { 428 | min_4((a_min.div_euclid(b_min), a_min.div_euclid(b_max), a_max.div_euclid(b_min), a_max.div_euclid(b_max))) 429 | } 430 | #[must_use] 431 | #[doc(hidden)] 432 | pub const fn singleside_div_euclid_max(a_min: irang, a_max: irang, b_min: irang, b_max: irang) -> irang { 433 | max_4((a_min.div_euclid(b_min), a_min.div_euclid(b_max), a_max.div_euclid(b_min), a_max.div_euclid(b_max))) 434 | } 435 | 436 | 437 | #[must_use] 438 | #[doc(hidden)] 439 | pub const fn singleside_rem_euclid_min(a_min: irang, a_max: irang, b_min: irang, b_max: irang) -> irang { 440 | // Note that b_min..=b_max must never include 0 441 | if b_min == b_max { 442 | if a_min == a_max {return a_min.rem_euclid(b_min)} 443 | 444 | let base_min = a_min.div_euclid(b_min); 445 | let base_max = a_max.div_euclid(b_min); 446 | if base_min==base_max { 447 | return a_min.rem_euclid(b_min); 448 | } 449 | } 450 | 451 | 0 452 | } 453 | 454 | #[must_use] 455 | #[doc(hidden)] 456 | pub const fn singleside_rem_euclid_max(a_min: irang, a_max: irang, b_min: irang, b_max: irang) -> irang { 457 | // Note that b_min..=b_max must never include 0 458 | if b_min == b_max { 459 | if a_min == a_max {return a_min.rem_euclid(b_min)} 460 | 461 | let base_min = a_min.div_euclid(b_min); 462 | let base_max = a_max.div_euclid(b_min); 463 | if base_min==base_max { 464 | return a_max.rem_euclid(b_min); 465 | } 466 | } 467 | 468 | let absb_max = max_irang(b_min.abs(), b_max.abs()); 469 | 470 | if a_min > 0 && a_max < absb_max {a_max} 471 | else {absb_max-1} 472 | } 473 | 474 | 475 | impl Ranged 476 | where [u8; memlayout(MIN, MAX).bytes()]:, 477 | AlignWrap<{memlayout(MIN, MAX)}>: Aligner, 478 | { 479 | /// Returns the minimum of two values 480 | pub const fn min(self, other: Ranged) 481 | -> Ranged< {min_irang(MIN, BMIN)}, {min_irang(MAX, BMAX)} > 482 | where 483 | [u8; memlayout(BMIN, BMAX).bytes()]:, 484 | [u8; memlayout(min_irang(MIN, BMIN), min_irang(MAX, BMAX)).bytes()]:, 485 | AlignWrap<{memlayout(BMIN, BMAX)}>: Aligner, 486 | AlignWrap<{memlayout(min_irang(MIN, BMIN), min_irang(MAX, BMAX))}>: Aligner, 487 | { 488 | unsafe { Ranged::__unsafe_new(min_irang(self.get(), other.get() )) } 489 | } 490 | 491 | /// Returns the maximum of two values 492 | pub const fn max(self, other: Ranged) 493 | -> Ranged< {max_irang(MIN, BMIN)}, {max_irang(MAX, BMAX)} > 494 | where 495 | [u8; memlayout(BMIN, BMAX).bytes()]:, 496 | [u8; memlayout(max_irang(MIN, BMIN), max_irang(MAX, BMAX)).bytes()]:, 497 | AlignWrap<{memlayout(BMIN, BMAX)}>: Aligner, 498 | AlignWrap<{memlayout(max_irang(MIN, BMIN), max_irang(MAX, BMAX))}>: Aligner, 499 | { 500 | unsafe { Ranged::__unsafe_new(max_irang(self.get(), other.get() )) } 501 | } 502 | 503 | /// Computes the absolute value of `self` 504 | pub const fn abs(self) -> Ranged< {abs_min(MIN, MAX)}, {abs_max(MIN, MAX)} > 505 | where [u8; memlayout(abs_min(MIN, MAX), abs_max(MIN, MAX)).bytes()]:, 506 | AlignWrap<{memlayout(abs_min(MIN, MAX), abs_max(MIN, MAX))}>: Aligner, 507 | { 508 | unsafe { Ranged::__unsafe_new(self.get().abs()) } 509 | } 510 | 511 | /// Calculates the quotient of Euclidean division of self by rhs 512 | pub const fn div_euclid(self, rhs: Ranged) 513 | -> Ranged< {singleside_div_euclid_min(MIN, MAX, BMIN, BMAX)}, {singleside_div_euclid_max(MIN, MAX, BMIN, BMAX)} > 514 | where 515 | [u8; memlayout(BMIN, BMAX).bytes()]:, 516 | [u8; memlayout(singleside_div_euclid_min(MIN, MAX, BMIN, BMAX), singleside_div_euclid_max(MIN, MAX, BMIN, BMAX)).bytes()]:, 517 | AlignWrap<{memlayout(BMIN, BMAX)}>: Aligner, 518 | AlignWrap<{memlayout(singleside_div_euclid_min(MIN, MAX, BMIN, BMAX), singleside_div_euclid_max(MIN, MAX, BMIN, BMAX))}>: Aligner, 519 | Assert<{ allow_division(BMIN, BMAX) }>: IsAllowed, 520 | { 521 | unsafe { Ranged::__unsafe_new(self.get().div_euclid(rhs.get())) } 522 | } 523 | 524 | /// Calculates the Euclidean mod of self by rhs 525 | pub const fn rem_euclid(self, rhs: Ranged) 526 | -> Ranged< {singleside_rem_euclid_min(MIN, MAX, BMIN, BMAX)}, {singleside_rem_euclid_max(MIN, MAX, BMIN, BMAX)} > 527 | where 528 | [u8; memlayout(BMIN, BMAX).bytes()]:, 529 | [u8; memlayout(singleside_rem_euclid_min(MIN, MAX, BMIN, BMAX), singleside_rem_euclid_max(MIN, MAX, BMIN, BMAX)).bytes()]:, 530 | AlignWrap<{memlayout(BMIN, BMAX)}>: Aligner, 531 | AlignWrap<{memlayout(singleside_rem_euclid_min(MIN, MAX, BMIN, BMAX), singleside_rem_euclid_max(MIN, MAX, BMIN, BMAX))}>: Aligner, 532 | Assert<{ allow_division(BMIN, BMAX) }>: IsAllowed, 533 | { 534 | unsafe { Ranged::__unsafe_new(self.get().rem_euclid(rhs.get())) } 535 | } 536 | } 537 | -------------------------------------------------------------------------------- /src/conversions.rs: -------------------------------------------------------------------------------- 1 | use core::str::FromStr; 2 | 3 | use crate::{Assert, IsAllowed, OperationPossibility, Ranged, irang, memlayout, arithmetics::allow_division}; 4 | use crate::{AlignWrap, Aligner}; 5 | /// Convert an integer value to Ranged according to its own bounds. 6 | /// 7 | /// Implemented for integer primitives. 8 | /// 9 | /// ``` 10 | /// use ranged_integers::*; 11 | /// 12 | /// let a = 42_u8; 13 | /// let ra = a.as_ranged(); 14 | /// let check_ra: Ranged<0, 255> = ra; 15 | /// ``` 16 | pub trait AsRanged: Copy { 17 | /// Conversion output 18 | type Res; 19 | 20 | /// Convert to Ranged type 21 | fn as_ranged(self) -> Self::Res; 22 | } 23 | 24 | macro_rules! int_ranged_converters { 25 | ($($t: ident)+) => { 26 | #[doc(hidden)] 27 | pub mod converter_checkers { 28 | use super::OperationPossibility; 29 | use super::irang; 30 | 31 | $( 32 | #[must_use] #[doc(hidden)] pub const fn $t (min: irang, max: irang)->OperationPossibility { 33 | OperationPossibility::allow_if(min>=$t::MIN as irang && max<=$t::MAX as irang) 34 | } 35 | )+ 36 | } 37 | 38 | impl Ranged 39 | where [u8; memlayout(MIN, MAX).bytes()]:, 40 | AlignWrap<{memlayout(MIN, MAX)}>: Aligner, 41 | { 42 | $( 43 | #[doc=concat!("Convert a Ranged into `", stringify!($t), "` value. Accessible if fits.")] 44 | pub const fn $t(self) -> $t 45 | where Assert<{converter_checkers::$t(MIN, MAX)}>: IsAllowed 46 | { 47 | #![allow(clippy::cast_possible_truncation)] 48 | #![allow(clippy::cast_sign_loss)] 49 | self.get() as $t 50 | } 51 | )+ 52 | } 53 | 54 | $( 55 | impl From> for $t 56 | where 57 | [u8; memlayout(MIN, MAX).bytes()]:, 58 | AlignWrap<{memlayout(MIN, MAX)}>: Aligner, 59 | Assert<{converter_checkers::$t(MIN, MAX)}>: IsAllowed, 60 | { 61 | fn from(a: Ranged) -> Self { a.$t() } 62 | } 63 | 64 | impl AsRanged for $t { 65 | type Res = Ranged<{$t::MIN as irang},{$t::MAX as irang}>; 66 | fn as_ranged(self) -> Self::Res { 67 | #![allow(clippy::cast_lossless)] 68 | unsafe {Self::Res::__unsafe_new(self as irang)} 69 | } 70 | } 71 | 72 | )+ 73 | }; 74 | } 75 | 76 | int_ranged_converters! {i8 u8 i16 u16 i32 u32 i64 u64 i128 isize usize} 77 | 78 | macro_rules! signed_ranged_rem { 79 | ($($t: ident)+) => { 80 | $( 81 | impl core::ops::Rem> for $t 82 | where 83 | [(); memlayout(VAL, VAL).bytes()]:, 84 | [(); memlayout(1-VAL.abs(), VAL.abs()-1).bytes()]:, 85 | AlignWrap<{memlayout(VAL, VAL)}>: Aligner, 86 | AlignWrap<{memlayout(1-VAL.abs(), VAL.abs()-1)}>: Aligner, 87 | Assert<{ allow_division(VAL, VAL) }>: IsAllowed, 88 | { 89 | type Output = Ranged<{1-VAL.abs()}, {VAL.abs()-1}>; 90 | 91 | fn rem(self, _rhs: Ranged) -> Self::Output { 92 | #![allow(clippy::cast_lossless)] 93 | unsafe { Ranged::__unsafe_new(self as irang % VAL) } 94 | } 95 | } 96 | )+ 97 | }; 98 | } 99 | signed_ranged_rem! {i8 i16 i32 i64 i128 isize} 100 | 101 | macro_rules! unsigned_ranged_rem { 102 | ($($t: ident)+) => { 103 | $( 104 | impl core::ops::Rem> for $t 105 | where 106 | [(); memlayout(VAL, VAL).bytes()]:, 107 | [(); memlayout(0, VAL.abs()-1).bytes()]:, 108 | AlignWrap<{memlayout(VAL, VAL)}>: Aligner, 109 | AlignWrap<{memlayout(0, VAL.abs()-1)}>: Aligner, 110 | { 111 | type Output = Ranged<0, {VAL.abs()-1}>; 112 | 113 | fn rem(self, _rhs: Ranged) -> Self::Output { 114 | #![allow(clippy::cast_lossless)] 115 | unsafe { Ranged::__unsafe_new(self as irang % VAL) } 116 | } 117 | } 118 | )+ 119 | }; 120 | } 121 | unsigned_ranged_rem! {u8 u16 u32 u64 usize} 122 | 123 | #[must_use] 124 | #[doc(hidden)] 125 | pub const fn expansion_possible(s_min: irang, s_max: irang, r_min: irang, r_max: irang) -> OperationPossibility { 126 | OperationPossibility::allow_if(r_min <= s_min && r_max >= s_max) 127 | } 128 | 129 | #[must_use] 130 | #[doc(hidden)] 131 | pub const fn lessthan(a: irang, b: irang) -> OperationPossibility { 132 | OperationPossibility::allow_if(a < b) 133 | } 134 | #[must_use] 135 | #[doc(hidden)] 136 | pub const fn lesseq(a: irang, b: irang) -> OperationPossibility { 137 | OperationPossibility::allow_if(a <= b) 138 | } 139 | 140 | impl Ranged 141 | where [u8; memlayout(MIN, MAX).bytes()]:, 142 | AlignWrap<{memlayout(MIN, MAX)}>: Aligner, 143 | { 144 | /// Convert to the Ranged with the wider bounds 145 | #[inline] 146 | pub const fn expand(self) -> Ranged 147 | where 148 | [u8; memlayout(RMIN, RMAX).bytes()]: , 149 | AlignWrap<{memlayout(RMIN, RMAX)}>: Aligner, 150 | Assert<{ expansion_possible(MIN, MAX, RMIN, RMAX) }>: IsAllowed, 151 | { 152 | unsafe { Ranged::__unsafe_new(self.get()) } 153 | } 154 | 155 | /// Convert to the other `Ranged`, returning `None` if the value is out of range 156 | #[inline] 157 | pub const fn fit(self) -> Option> 158 | where [u8; memlayout(RMIN, RMAX).bytes()]:, 159 | AlignWrap<{memlayout(RMIN, RMAX)}>: Aligner, { 160 | Ranged::::new(self.get()) 161 | } 162 | 163 | /// Change the `MIN` value of Ranged bounds, returning `None` if the value is out of range 164 | #[inline] 165 | pub const fn fit_max(self) -> Option> 166 | where [u8; memlayout(MIN, RMAX).bytes()]:, 167 | AlignWrap<{memlayout(MIN, RMAX)}>: Aligner, { 168 | Ranged::::new(self.get()) 169 | } 170 | 171 | /// Change the `MAX` value of Ranged bounds, returning `None` if the value is out of range 172 | #[inline] 173 | pub const fn fit_min(self) -> Option> 174 | where [u8; memlayout(RMIN, MAX).bytes()]:, 175 | AlignWrap<{memlayout(RMIN, MAX)}>: Aligner, { 176 | Ranged::::new(self.get()) 177 | } 178 | 179 | /// Compares two `Ranged` values. If self is less than the other, it 180 | /// returns a Ranged with the same value and shrinked bounds. 181 | /// 182 | /// Allowed only if the ranges interleave. 183 | #[inline] 184 | pub const fn fit_less_than(self, other: Ranged) -> Option> 185 | where 186 | [u8; memlayout(RMIN, RMAX).bytes()]:, 187 | [u8; memlayout(MIN, RMAX).bytes()]:, 188 | AlignWrap<{memlayout(RMIN, RMAX)}>: Aligner, 189 | AlignWrap<{memlayout(MIN, RMAX)}>: Aligner, 190 | Assert<{lessthan(RMAX, MAX)}>: IsAllowed, 191 | Assert<{lessthan(MIN, RMAX)}>: IsAllowed, 192 | { 193 | if self.get() < other.get() { 194 | Some(unsafe{ Ranged::__unsafe_new(self.get()) }) 195 | } else {None} 196 | } 197 | 198 | /// Compares two `Ranged` values. If self is less than or equal to the other, it 199 | /// returns a Ranged with the same value and shrinked bounds. 200 | /// 201 | /// Allowed only if the ranges interleave. 202 | #[inline] 203 | pub const fn fit_less_eq(self, other: Ranged) -> Option> 204 | where 205 | [u8; memlayout(RMIN, RMAX).bytes()]:, 206 | [u8; memlayout(MIN, RMAX).bytes()]:, 207 | AlignWrap<{memlayout(RMIN, RMAX)}>: Aligner, 208 | AlignWrap<{memlayout(MIN, RMAX)}>: Aligner, 209 | Assert<{lessthan(RMAX, MAX)}>: IsAllowed, 210 | Assert<{lesseq(MIN, RMAX)}>: IsAllowed, 211 | { 212 | if self.get() <= other.get() { 213 | Some(unsafe{ Ranged::__unsafe_new(self.get()) }) 214 | } else {None} 215 | } 216 | 217 | /// Compares two `Ranged` values. If self is greater than the other, it 218 | /// returns a Ranged with the same value and shrinked bounds. 219 | /// 220 | /// Allowed only if the ranges interleave. 221 | #[inline] 222 | pub const fn fit_greater_than(self, other: Ranged) -> Option> 223 | where 224 | [u8; memlayout(RMIN, RMAX).bytes()]:, 225 | [u8; memlayout(RMIN, MAX).bytes()]:, 226 | AlignWrap<{memlayout(RMIN, RMAX)}>: Aligner, 227 | AlignWrap<{memlayout(RMIN, MAX)}>: Aligner, 228 | Assert<{lessthan(MIN, RMIN)}>: IsAllowed, 229 | Assert<{lessthan(RMIN, MAX)}>: IsAllowed, 230 | { 231 | if self.get() > other.get() { 232 | Some(unsafe{ Ranged::__unsafe_new(self.get()) }) 233 | } else {None} 234 | } 235 | 236 | /// Compares two `Ranged` values. If self is greater than or equal to the other, it 237 | /// returns a Ranged with the same value and shrinked bounds. 238 | /// 239 | /// Allowed only if the ranges interleave. 240 | #[inline] 241 | pub const fn fit_greater_eq(self, other: Ranged) -> Option> 242 | where 243 | [u8; memlayout(RMIN, RMAX).bytes()]:, 244 | [u8; memlayout(RMIN, MAX).bytes()]:, 245 | AlignWrap<{memlayout(RMIN, RMAX)}>: Aligner, 246 | AlignWrap<{memlayout(RMIN, MAX)}>: Aligner, 247 | Assert<{lessthan(MIN, RMIN)}>: IsAllowed, 248 | Assert<{lesseq(RMIN, MAX)}>: IsAllowed, 249 | { 250 | if self.get() >= other.get() { 251 | Some(unsafe{ Ranged::__unsafe_new(self.get()) }) 252 | } else {None} 253 | } 254 | 255 | #[deprecated(note = "use method fit() instead")] 256 | /// Convert to the other Ranged, returning None if the value is out of range 257 | pub const fn try_expand(self) -> Option> 258 | where [u8; memlayout(RMIN, RMAX).bytes()]:, 259 | AlignWrap<{memlayout(RMIN, RMAX)}>: Aligner, { 260 | Ranged::::new(self.get()) 261 | } 262 | } 263 | 264 | #[derive(Debug)] 265 | pub struct ParseRangedError; 266 | 267 | impl FromStr for Ranged 268 | where [(); memlayout(MIN, MAX).bytes()]:, 269 | AlignWrap<{memlayout(MIN, MAX)}>: Aligner, 270 | { 271 | type Err = ParseRangedError; 272 | 273 | fn from_str(s: &str) -> Result { 274 | let a : irang = s.parse().ok().ok_or(ParseRangedError)?; 275 | Self::new(a).ok_or(ParseRangedError) 276 | } 277 | } 278 | -------------------------------------------------------------------------------- /src/holder.rs: -------------------------------------------------------------------------------- 1 | // The internals for `Ranged` type 2 | 3 | // The internal representation of `Ranged` is struct `RangedRepr`. It contains 4 | // a needed-size byte array and a zero-size type to align it. 5 | 6 | //! The internals for `Ranged` type 7 | 8 | #![doc(hidden)] 9 | #![allow(clippy::inline_always)] 10 | 11 | use super::irang; 12 | 13 | // A helper trait specifying the alignment 14 | pub trait Aligner 15 | { 16 | type A: Copy; 17 | } 18 | 19 | // A helper enum specifying the amount of bytes needed for a Ranged 20 | #[derive(PartialEq, Eq, Copy, Clone, core::marker::ConstParamTy)] 21 | #[allow(non_camel_case_types)] 22 | pub enum IntLayout { 23 | i8, u8, i16, u16, i32, u32, i64, u64, i128, Trivial 24 | } 25 | impl IntLayout { 26 | #[doc(hidden)] 27 | pub const fn bytes(self) -> usize { 28 | match self { 29 | Self::u8 | Self::i8 => 1, 30 | Self::i16 | Self::u16 => 2, 31 | Self::i32 | Self::u32 => 4, 32 | Self::i64 | Self::u64 => 8, 33 | Self::i128 => 16, 34 | Self::Trivial => 0, 35 | } 36 | } 37 | } 38 | 39 | 40 | // Convert the IntLayout into the corresponding type 41 | #[doc(hidden)] 42 | #[derive(Copy, Clone)] 43 | pub struct AlignWrap; 44 | // the internal type must have the alignment of the corresponding integer 45 | impl Aligner for AlignWrap<{IntLayout::i8}> { type A = i8; } 46 | impl Aligner for AlignWrap<{IntLayout::u8}> { type A = u8; } 47 | impl Aligner for AlignWrap<{IntLayout::i16}> { type A = i16; } 48 | impl Aligner for AlignWrap<{IntLayout::u16}> { type A = u16; } 49 | impl Aligner for AlignWrap<{IntLayout::i32}> { type A = i32; } 50 | impl Aligner for AlignWrap<{IntLayout::u32}> { type A = u32; } 51 | impl Aligner for AlignWrap<{IntLayout::i64}> { type A = i64; } 52 | impl Aligner for AlignWrap<{IntLayout::u64}> { type A = u64; } 53 | impl Aligner for AlignWrap<{IntLayout::i128}> { type A = i128; } 54 | impl Aligner for AlignWrap<{IntLayout::Trivial}> { type A = u8; } 55 | 56 | // The internal representation of Ranged: an array of bytes with the length and alignmemt ensured 57 | #[derive(Clone, Copy)] 58 | pub struct RangedRepr 59 | where 60 | AlignWrap: Aligner, 61 | [u8; LAYOUT.bytes()]:, 62 | { 63 | // Ensure the alignment 64 | _align: [ as Aligner>::A; 0], 65 | // Bytewise access 66 | bytes: [u8; LAYOUT.bytes()], 67 | } 68 | 69 | // Convert NumberBytes to and from integers. 70 | // This code heavily relies on optimization 71 | impl RangedRepr 72 | where 73 | AlignWrap : Aligner, 74 | [(); LAYOUT.bytes()]: 75 | { 76 | const fn new() -> Self { Self{_align: [], bytes: [0; LAYOUT.bytes()]} } 77 | 78 | #[inline(always)] 79 | pub(crate) const fn from_irang(v: irang) -> Self { 80 | let mut x = Self::new(); 81 | let bytes = v.to_ne_bytes(); 82 | match LAYOUT { 83 | IntLayout::Trivial => { 84 | } 85 | IntLayout::i8 | IntLayout::u8 => { 86 | x.bytes[0] = bytes[0]; 87 | } 88 | IntLayout::i16 | IntLayout::u16 => { 89 | x.bytes[0] = bytes[0]; 90 | x.bytes[1] = bytes[1]; 91 | } 92 | IntLayout::i32 | IntLayout::u32 => { 93 | x.bytes[0] = bytes[0]; 94 | x.bytes[1] = bytes[1]; 95 | x.bytes[2] = bytes[2]; 96 | x.bytes[3] = bytes[3]; 97 | } 98 | IntLayout::i64 | IntLayout::u64 => { 99 | x.bytes[0] = bytes[0]; 100 | x.bytes[1] = bytes[1]; 101 | x.bytes[2] = bytes[2]; 102 | x.bytes[3] = bytes[3]; 103 | x.bytes[4] = bytes[4]; 104 | x.bytes[5] = bytes[5]; 105 | x.bytes[6] = bytes[6]; 106 | x.bytes[7] = bytes[7]; 107 | } 108 | IntLayout::i128 => { 109 | x.bytes[0] = bytes[0]; 110 | x.bytes[1] = bytes[1]; 111 | x.bytes[2] = bytes[2]; 112 | x.bytes[3] = bytes[3]; 113 | x.bytes[4] = bytes[4]; 114 | x.bytes[5] = bytes[5]; 115 | x.bytes[6] = bytes[6]; 116 | x.bytes[7] = bytes[7]; 117 | x.bytes[8] = bytes[8]; 118 | x.bytes[9] = bytes[9]; 119 | x.bytes[10] = bytes[10]; 120 | x.bytes[11] = bytes[11]; 121 | x.bytes[12] = bytes[12]; 122 | x.bytes[13] = bytes[13]; 123 | x.bytes[14] = bytes[14]; 124 | x.bytes[15] = bytes[15]; 125 | } 126 | } 127 | 128 | x 129 | } 130 | 131 | #[inline(always)] 132 | pub(crate) const fn to_irang(self) -> irang { 133 | let b = self.bytes; 134 | match LAYOUT { 135 | IntLayout::Trivial => { 136 | 0 137 | } 138 | IntLayout::i8 => { 139 | i8::from_ne_bytes([b[0]]) as irang 140 | } 141 | IntLayout::u8 => { 142 | u8::from_ne_bytes([b[0]]) as irang 143 | } 144 | IntLayout::i16 => { 145 | i16::from_ne_bytes([b[0], b[1]]) as irang 146 | } 147 | IntLayout::u16 => { 148 | u16::from_ne_bytes([b[0], b[1]]) as irang 149 | } 150 | IntLayout::i32 => { 151 | i32::from_ne_bytes([b[0], b[1], b[2], b[3]]) as irang 152 | } 153 | IntLayout::u32 => { 154 | u32::from_ne_bytes([b[0], b[1], b[2], b[3]]) as irang 155 | } 156 | IntLayout::i64 => { 157 | i64::from_ne_bytes([b[0], b[1], b[2], b[3], b[4], b[5], b[6], b[7]]) as irang 158 | } 159 | IntLayout::u64 => { 160 | u64::from_ne_bytes([b[0], b[1], b[2], b[3], b[4], b[5], b[6], b[7]]) as irang 161 | } 162 | IntLayout::i128 => { 163 | i128::from_ne_bytes([b[0], b[1], b[2], b[3], b[4], b[5], b[6], b[7], b[8], b[9], b[10], b[11], b[12], b[13], b[14], b[15]]) as irang 164 | } 165 | } 166 | } 167 | } 168 | 169 | -------------------------------------------------------------------------------- /src/iter.rs: -------------------------------------------------------------------------------- 1 | 2 | use crate::holder::{AlignWrap, Aligner}; 3 | 4 | use super::{Assert, IsAllowed, OperationPossibility, Ranged, irang, memlayout}; 5 | 6 | use core::convert::TryFrom; 7 | 8 | /// An iterator through given range 9 | pub struct Iter 10 | where [u8; memlayout(MIN, MAX).bytes()]:,AlignWrap<{memlayout(MIN, MAX)}>: Aligner, 11 | { 12 | pub(crate) current: Option> 13 | } 14 | 15 | /// Const range for iterators with `Ranged` output and array indexing 16 | /// 17 | /// Do not use directly, use [`r!`](macro.r.html) macro instead 18 | /// 19 | /// # Example 20 | /// 21 | /// ``` 22 | /// # #![feature(adt_const_params, generic_const_exprs)] use ranged_integers::*; 23 | /// let mut fibonacci = [0; 10]; 24 | /// fibonacci[0] = 1; 25 | /// fibonacci[1] = 1; 26 | /// for i in r!(2..10) { 27 | /// fibonacci[i.expand()] = fibonacci[(i-r!(1)).expand()] + fibonacci[(i-r!(2)).expand()]; 28 | /// } 29 | /// 30 | /// let fib234: [_; 3] = fibonacci[r!(2..5)]; 31 | /// assert_eq!(fib234, [2,3,5]); 32 | /// 33 | /// ``` 34 | #[derive(Clone, Copy)] 35 | pub struct ConstRange 36 | where [(); memlayout(MIN, MAX).bytes()]:, AlignWrap<{memlayout(MIN, MAX)}>: Aligner, ; 37 | 38 | impl IntoIterator for ConstRange 39 | where [(); memlayout(MIN, MAX).bytes()]:, AlignWrap<{memlayout(MIN, MAX)}>: Aligner, 40 | { 41 | type Item = Ranged; 42 | type IntoIter = Iter; 43 | fn into_iter(self) -> Self::IntoIter { 44 | Self::IntoIter{current: Some(unsafe{Ranged::__unsafe_new(MIN)})} 45 | } 46 | } 47 | 48 | impl Iterator for Iter 49 | where [u8; memlayout(MIN, MAX).bytes()]:, AlignWrap<{memlayout(MIN, MAX)}>: Aligner, 50 | { 51 | type Item = Ranged; 52 | 53 | fn next(&mut self) -> Option { 54 | unsafe{ 55 | let curr = self.current?; 56 | let cval = curr.get(); 57 | self.current = if cval == MAX {None} else {Ranged::__unsafe_new(cval+1).into()}; 58 | Some(curr) 59 | } 60 | } 61 | 62 | fn size_hint(&self) -> (usize, Option) { 63 | let range = MAX-MIN; 64 | usize::try_from(range).map_or((usize::MAX, None), |rangeus| (rangeus, Some(rangeus))) 65 | } 66 | 67 | #[inline] 68 | fn nth(&mut self, n: usize) -> Option { 69 | let cval = self.current?; 70 | if let Some(out) = Ranged::new(cval.get() + n as i128) { 71 | self.current = Some(out); 72 | self.next() 73 | } else { 74 | self.current = None; 75 | None 76 | } 77 | } 78 | 79 | fn last(self) -> Option { 80 | self.current?; 81 | Some(unsafe{Ranged::__unsafe_new(MAX)}) 82 | } 83 | 84 | fn min(mut self) -> Option { 85 | self.next() 86 | } 87 | 88 | fn max(self) -> Option { 89 | self.last() 90 | } 91 | } 92 | 93 | #[doc(hidden)] 94 | pub const fn range_fits_usize(min: irang, max: irang) -> OperationPossibility { 95 | OperationPossibility::allow_if((max-min) < (usize::MAX as i128)) 96 | } 97 | 98 | impl ExactSizeIterator for Iter 99 | where 100 | [u8; memlayout(MIN, MAX).bytes()]:, 101 | AlignWrap<{memlayout(MIN, MAX)}>: Aligner, 102 | Assert<{range_fits_usize(MAX, MIN)}>: IsAllowed, 103 | {} 104 | 105 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | //! # Ranged integers [nightly only] 2 | //! 3 | //! The crate provides [an integer type](struct.Ranged.html) restricted to a compile time defined range with 4 | //! automatic data size selection, automatic bounds calulation for arithmetics and the possibility 5 | //! of fixed-size array indexing and range iteration. 6 | //! 7 | //! # Version info 8 | //! 9 | //! In the version 0.9.0 the compile-time arithmetics were removed, because the compiler 10 | //! stopped supporting it. The version 0.9.0 was adapted for nightly-2024-10-12. 11 | //! Check out the [changelog](https://github.com/disiamylborane/ranged_integers/blob/master/CHANGELOG.md) 12 | //! for the list of features in the previous versions. 13 | //! 14 | //! # Prerequisites 15 | //! 16 | //! The library usage requires the following Rust features enabled in the user crate or application: 17 | //! 18 | //! ``` 19 | //! // Without this rustc generates errors and sometimes panics. 20 | //! #![feature(adt_const_params, generic_const_exprs)] 21 | //! ``` 22 | //! 23 | //! Note that the features needed depend on the package version. 24 | //! 25 | //! # Usage and examples 26 | //! 27 | //! ## Ranged semantics 28 | //! 29 | //! Use `Ranged` type to be sure of the value range: 30 | //! 31 | //! ``` 32 | //! # use ranged_integers::*; 33 | //! fn move_player(dice_roll: Ranged<1, 6>) { 34 | //! let x : i32 = dice_roll.into(); // Conversion is allowed, i32 can store 1..=6 35 | //! } 36 | //! ``` 37 | //! 38 | //! ## Contents 39 | //! 40 | //! * [Data layout paradigm](#data-layout-paradigm) 41 | //! * [Ranged and integer primitives](#ranged-and-integer-primitives) 42 | //! - [Creation of Ranged at compile time](#creation-of-ranged-at-compile-time) 43 | //! - [Ranged -> Ranged conversion](#ranged---ranged-conversion) 44 | //! - [int -> Ranged conversion](#int---ranged-conversion) 45 | //! - [Ranged -> int conversion](#ranged---int-conversion) 46 | //! * [Array indexing, slicing and iteration](#array-indexing-slicing-and-iteration) 47 | //! * [Comparison](#comparison) 48 | //! * [Arithmetics](#arithmetics) 49 | //! * [Pattern matching](#pattern-matching) 50 | //! 51 | //! ## Data layout paradigm 52 | //! 53 | //! The [Ranged] automatically chooses the smallest size possible according 54 | //! to `MIN..=MAX` range. 55 | //! It supports i8, u8, i16, u16, i32, u32, i64, u64 and i128 layouts (u128 is not supported), 56 | //! and a special zero-size layout for "constant" values with `MIN==MAX`. 57 | //! 58 | //! ``` 59 | //! # use ranged_integers::*; fn main(){ 60 | //! use core::mem::size_of; 61 | //! assert_eq!(size_of::< Ranged<42, 42> >(), 0); // It's always 42, no need to store it 62 | //! 63 | //! assert_eq!(size_of::< Ranged<-1, 127> >(), 1); // Fits i8 64 | //! assert_eq!(size_of::< Ranged<0, 200> >(), 1); // Fits u8 65 | //! assert_eq!(size_of::< Ranged<-1, 200> >(), 2); // Fits i16, doesn't fit i8 or u8 66 | //! 67 | //! assert_eq!(size_of::< Ranged<0, 90000> >(), 4); // The range fits i32 68 | //! # } 69 | //! ``` 70 | //! 71 | //! The implementation heavily relies on the optimizer. 72 | //! 73 | //! ## Ranged and integer primitives 74 | //! 75 | //! ### Creation of `Ranged` at compile time 76 | //! 77 | //! The [`Ranged::create_const`] can be used to create a 78 | //! [`Ranged`] value checking it at compile time. 79 | //! The macro [`r!`] does the same but a bit shorter. 80 | //! 81 | //! ``` 82 | //! # #![feature(adt_const_params, generic_const_exprs)] use ranged_integers::*; fn move_player(dice_roll: Ranged<1, 6>) {} 83 | //! // Way 1: specify the bounds explicitly 84 | //! move_player(Ranged::<1,6>::create_const::<4>()); 85 | //! move_player(r!([1 6] 4)); // Same thing 86 | //! 87 | //! // Way 2: do not specify the bounds when possible 88 | //! move_player(Ranged::create_const::<4>()); 89 | //! move_player(r!([] 4)); // Same thing 90 | //! let x: Ranged::<0, 100> = Ranged::create_const::<42>(); 91 | //! let y: Ranged::<0, 100> = r!([] 42); // Same thing 92 | //! 93 | //! // Way 3: a special case with the single possible value 94 | //! let x = Ranged::<4, 4>::create_const::<4>(); 95 | //! let y = r!(4); // Same thing 96 | //! ``` 97 | //! 98 | //! It fails if the bounds are corrupted: 99 | //! 100 | //! ```compile_fail 101 | //! # #![feature(adt_const_params, generic_const_exprs)] use ranged_integers::*; fn move_player(dice_roll: Ranged<1, 6>) {} 102 | //! move_player(r!([] 7)); // Error: Can't store 7 in [1 6] inverval 103 | //! ``` 104 | //! ```compile_fail 105 | //! move_player(r!([1 7] 7)); // Error: type mismatch, move_player() requires Ranged<1, 6> 106 | //! ``` 107 | //! 108 | //! ### `Ranged` -> `Ranged` conversion 109 | //! 110 | //! The `Ranged` can be converted to the type with different bounds using 111 | //! [`expand()`](struct.Ranged.html#method.expand) generic method (compile-time check) 112 | //! and the methods [`fit()`](struct.Ranged.html#method.fit), [`fit_min()`](struct.Ranged.html#method.fit_min), 113 | //! [`fit_max()`](struct.Ranged.html#method.fit_max) for runtime check. The `Ranged` values may be compared 114 | //! to each other yielding the shrinked bounds using 115 | //! [`fit_less_than()`](struct.Ranged.html#method.fit_less_than), 116 | //! [`fit_less_eq()`](struct.Ranged.html#method.fit_less_eq), 117 | //! [`fit_greater_than()`](struct.Ranged.html#method.fit_greater_than), and 118 | //! [`fit_greater_eq()`](struct.Ranged.html#method.fit_greater_eq) methods. 119 | //! 120 | //! ``` 121 | //! # #![feature(adt_const_params, generic_const_exprs)] use ranged_integers::*; fn move_player(dice_roll: Ranged<1, 6>) {} 122 | //! let expandable: Ranged<4, 5> = r!([] 5); // Fits Ranged<1,6> accepted by move_player 123 | //! let overlapping: Ranged<4, 9> = r!([] 5); // Doesn't fit, but the value 5 is acceptable 124 | //! move_player(expandable.expand()); 125 | //! move_player(overlapping.fit().unwrap()); 126 | //! ``` 127 | //! 128 | //! Shrinking with `expand()` is forbidden: 129 | //! 130 | //! ```compile_fail 131 | //! # #![feature(adt_const_params, generic_const_exprs)] use ranged_integers::*; fn move_player(dice_roll: Ranged<1, 6>) {} 132 | //! # let overlapping: Ranged<4, 9> = r!([] 5); 133 | //! move_player(overlapping.expand()); // Error: the bounds 4..=9 can't fit in 1..=6 134 | //! ``` 135 | //! 136 | //! ### `int` -> `Ranged` conversion 137 | //! 138 | //! Way 1: ensure the bounds with [`Ranged::new(i128) -> Option`](struct.Ranged.html#method.new) function. 139 | //! 140 | //! **Note**: due to incomplete features limitations, the function may fail to compile with 141 | //! the 'trait bound is not satisfied' if the bounds are not explicitly specified. 142 | //! 143 | //! ``` 144 | //! # #![feature(adt_const_params, generic_const_exprs)] use ranged_integers::*; fn move_player(dice_roll: Ranged<1, 6>) {} 145 | //! let some_int = 4; 146 | //! let some_wrong_int = 8; 147 | //! assert!(Ranged::<0, 6>::new(some_int) == Some(r!([0 6] 4))); 148 | //! assert!(Ranged::<0, 6>::new(some_wrong_int) == None); 149 | //! 150 | //! move_player(Ranged::new(some_int).unwrap()); // this call may fail to compile 151 | //! // use Ranged::<0, 6>::new instead 152 | //! ``` 153 | //! 154 | //! Way 2: use the [`Remainder operation`](struct.Ranged.html#impl-Rem>) with the "const" divisor 155 | //! 156 | //! ``` 157 | //! # #![feature(adt_const_params, generic_const_exprs)] use ranged_integers::*; fn move_player(dice_roll: Ranged<1, 6>) {} 158 | //! let x: Ranged<-9, 9> = 15_i32 % r!(10); 159 | //! let y: Ranged<0, 9> = 15_u32 % r!(10); 160 | //! assert!(x == r!(5)); 161 | //! assert!(y == r!(5)); 162 | //! ``` 163 | //! 164 | //! Way 3: Convert the primitive types to `Ranged` with their native bounds using [`AsRanged`] 165 | //! 166 | //! ``` 167 | //! # #![feature(adt_const_params, generic_const_exprs)] use ranged_integers::*; fn move_player(dice_roll: Ranged<1, 6>) {} 168 | //! use ranged_integers::AsRanged; 169 | //! let x = 15_u8.as_ranged(); // Ranged<0, 255> 170 | //! let y = 15_i16.as_ranged(); // Ranged<-32768, 32767> 171 | //! ``` 172 | //! 173 | //! ### `Ranged` -> `int` conversion 174 | //! 175 | //! `int::From` trait is implemented when the value is proved to 176 | //! fit into the result type: 177 | //! 178 | //! ``` 179 | //! # #![feature(adt_const_params, generic_const_exprs)] use ranged_integers::*; 180 | //! let x = r!([0 200] 20); 181 | //! let y: u8 = x.into(); // 0..=200 fits u8 182 | //! ``` 183 | //! 184 | //! ```compile_fail 185 | //! # #![feature(adt_const_params, generic_const_exprs)] use ranged_integers::*; 186 | //! let x = r!([0 200] 20); 187 | //! let y: i8 = x.into(); // 0..=200 doesn't fit i8 188 | //! ``` 189 | //! 190 | //! `From` and `Into` operations can't be used in const context. 191 | //! A set of [`const fn`](struct.Ranged.html#method.i8)s allows const conversions to 192 | //! any integer primitive except for `u128`: 193 | //! 194 | //! ``` 195 | //! # #![feature(adt_const_params, generic_const_exprs)] use ranged_integers::*; fn move_player(dice_roll: Ranged<1, 6>) {} 196 | //! let x = r!([0 200] 20); 197 | //! let y = x.u8(); // y is u8 198 | //! let z = x.i16(); // z is i16 199 | //! let w = x.usize(); // w is usize 200 | //! ``` 201 | //! 202 | //! ```compile_fail 203 | //! # #![feature(adt_const_params, generic_const_exprs)] use ranged_integers::*; fn move_player(dice_roll: Ranged<1, 6>) {} 204 | //! let x = r!([0 200] 20); 205 | //! let err = x.i8(); // Error: 0..=200 doesn't fit i8 206 | //! ``` 207 | //! 208 | //! ## Array indexing, slicing and iteration 209 | //! 210 | //! The [`ConstInclusiveRange`] zero-size type is a range `MIN..=MAX` 211 | //! capable to create the iterator ([`IntoIterator`] trait implemented) 212 | //! with `Ranged` output type. The [`r!`] macro can be used instead. 213 | //! 214 | //! The [`Ranged::iter_up`](struct.Ranged.html#method.iter_up) method creates an 215 | //! iterator from the current value up to `MAX`. 216 | //! 217 | //! The arrays `[T; N]` may be indexed with `Ranged<0, {N-1}>` and sliced 218 | //! with `r!(MIN..END)` range with a reference to fixed-size array output. 219 | //! 220 | //! ``` 221 | //! # #![feature(adt_const_params, generic_const_exprs)] use ranged_integers::*; fn move_player(dice_roll: Ranged<1, 6>) {} 222 | //! let arr = [r!([1 6] 2), r!([] 3), r!([] 4), r!([] 5)]; 223 | //! 224 | //! assert_eq!(arr[r!(1..3)], [3,4]); // Slicing with array reference output 225 | //! assert_eq!(arr[r!([0 3] 1)], 3); // Slicing with array reference output 226 | //! 227 | //! // Not recommended to use this: 228 | //! for i in ConstInclusiveRange::<0, 3> { 229 | //! move_player(arr[i]) // iters through 0,1,2,3 230 | //! } 231 | //! for i in r!(0..4) { 232 | //! move_player(arr[i]) // iters through 0,1,2,3 233 | //! } 234 | //! for i in r!(0..=3) { 235 | //! move_player(arr[i]) // iters through 0,1,2,3 236 | //! } 237 | //! for mv in r!([1 6] 3).iter_up() { 238 | //! move_player(mv) // calls with 3,4,5,6 239 | //! } 240 | //! ``` 241 | //! 242 | //! ## Comparison 243 | //! 244 | //! All `Eq` and `Ord` operations between different Ranged types are allowed, 245 | //! so as `Ranged` vs integer comparisons: 246 | //! 247 | //! ``` 248 | //! # #![feature(adt_const_params, generic_const_exprs)] use ranged_integers::*; fn move_player(dice_roll: Ranged<1, 6>) {} 249 | //! assert!(r!([1 6] 4) == r!([1 10] 4)); 250 | //! assert!(r!([1 6] 4) != r!([1 6] 5)); 251 | //! assert!(r!(4) == 4); 252 | //! assert!(5 != r!([1 6] 4)); 253 | //! 254 | //! assert!(r!(5) > r!([1 6] 4)); 255 | //! assert!(4 < r!([1 6] 5)); 256 | //! ``` 257 | //! 258 | //! To constrain the output type ruled by comparison, one may use [`Ranged::fit_less_than`] 259 | //! function and its siblings [`Ranged::fit_less_eq`], [`Ranged::fit_greater_than`], and [`Ranged::fit_greater_eq`]. 260 | //! 261 | //! ## Arithmetics 262 | //! 263 | //! The bounds of arithmetic operations results are automatically recalculated. 264 | //! 265 | //! Currently supported: 266 | //! * The basic arithmetic operations (+, -, *, /) 267 | //! * [`div_euclid()`](struct.Ranged.html#method.div_euclid) and [`rem_euclid()`](struct.Ranged.html#method.rem_euclid) 268 | //! * [`min()`](struct.Ranged.html#method.min) and [`max()`](struct.Ranged.html#method.max) 269 | //! * [`abs()`](struct.Ranged.html#method.abs) 270 | //! 271 | //! ``` 272 | //! # #![feature(adt_const_params, generic_const_exprs)] use ranged_integers::*; fn move_player(dice_roll: Ranged<1, 6>) {} 273 | //! let x = r!([1 6] 5); 274 | //! let y = r!([1 6] 4); 275 | //! 276 | //! let a = x + y; // The minimum is (1+1)=2, the maximum is (6+6)=12 277 | //! let check_add: Ranged<2, 12> = a; // Range assertion assignment 278 | //! assert_eq!(check_add, r!(9)); 279 | //! 280 | //! let s = x - y; // The minimum is (1-6)=-5, the maximum is (6-1)=5 281 | //! let check_sub: Ranged<-5, 5> = s; // Range assertion assignment 282 | //! assert_eq!(check_sub, r!(1)); 283 | //! 284 | //! let m = x * y; // The minimum is (1*1)=1, the maximum is (6*6)=36 285 | //! let check_mul: Ranged<1, 36> = m; // Range assertion assignment 286 | //! assert_eq!(check_mul, r!(20)); 287 | //! 288 | //! let d = x / y; // The minimum is (1/6)=0, the maximum is (6/1)=6 289 | //! let check_div: Ranged<0, 6> = d; // Range assertion assignment 290 | //! assert_eq!(check_div, r!(1)); 291 | //! 292 | //! let r = x % y; 293 | //! let check_rem: Ranged<0, 5> = r; // Range assertion assignment 294 | //! assert_eq!(check_rem, r!(1)); 295 | //! 296 | //! let n = -x; 297 | //! let check_neg: Ranged<-6, -1> = n; // Range assertion assignment 298 | //! assert_eq!(check_neg, r!(-5)); 299 | //! 300 | //! let min: Ranged<1,6> = x.min(a); 301 | //! let max: Ranged<2,12> = x.max(a); 302 | //! let abs: Ranged<0,6> = r!([-1 6] -1).abs(); 303 | //! ``` 304 | //! 305 | //! The division and remainder are allowed only if it's impossible to store "0" in the divisor: 306 | //! 307 | //! ```compile_fail 308 | //! # #![feature(adt_const_params, generic_const_exprs)] use ranged_integers::*; fn move_player(dice_roll: Ranged<1, 6>) {} 309 | //! let x = r!([1 6] 4); 310 | //! let y = r!([0 6] 5); 311 | //! let z = r!([-1 6] 5); 312 | //! 313 | //! let d = x / y; // Error: y can be 0 314 | //! let e = x % z; // Error: z can be 0 315 | //! ``` 316 | //! 317 | //! The true bounds calculation routine for `Rem` operation is far too complex. 318 | //! In this library the calculated bounds will never exceed `1-DMAXABS..=DMAXABS-1` where `DMAXABS` is the 319 | //! maximum of the divisor absolute value. 320 | //! 321 | //! This kind of `Rem` followed by `expand` is available for any dividend: 322 | //! ``` 323 | //! # #![feature(adt_const_params, generic_const_exprs)] use ranged_integers::*; 324 | //! let x = r!([-1000 1000] 500); 325 | //! let y = r!([-1 1000] 500); 326 | //! let d = r!([1 10] 7); 327 | //! 328 | //! let r: Ranged<-9, 9> = (x%d).expand(); 329 | //! // In this case, it expands just from Ranged<-9, 9> to itself 330 | //! 331 | //! let r: Ranged<-9, 9> = (y%d).expand(); 332 | //! // In this case, it expands from Ranged<-1, 9> 333 | //! ``` 334 | //! 335 | //! But the actual calculation routine can produce smaller bounds: 336 | //! 337 | //! ``` 338 | //! # #![feature(adt_const_params, generic_const_exprs)] use ranged_integers::*; fn move_player(dice_roll: Ranged<1, 6>) {} 339 | //! // In the general case the output is Ranged<1-MAX, MAX-1>, MAX from divisor (by absolute value) 340 | //! let x: Ranged<-9, 9> = (r!([-1000 1000] 500) % r!([1 10] 7)); 341 | //! let x: Ranged<-9, 9> = (r!([-1000 1000] 500) % r!([-10 -1] -7)); 342 | //! 343 | //! // If the dividend is nonnegative or nonpositive, 344 | //! // the output range is limited to 0. 345 | //! let x: Ranged<0, 9> = r!([0 100] 15) % r!(10); 346 | //! let x: Ranged<-9, 0> = r!([-100 0] -15) % r!(10); 347 | //! 348 | //! // The limit can't exceed the dividend's MIN(if negative) or MAX(if positive): 349 | //! let x: Ranged<-10, 10> = r!([-10 10] 4) % r!([1 1000] 70); 350 | //! 351 | //! // If the divisor is "constant", the output bounds are the true bounds: 352 | //! let x: Ranged<4, 7> = r!([14 17] 15) % r!(10); 353 | //! 354 | //! // In particular, if both operands are "constant", the result is "constant" 355 | //! let x: Ranged<5, 5> = r!(15) % r!(10); 356 | //! ``` 357 | //! 358 | //! Following these rules, the calculated bounds may be wider than the true ones, like 359 | //! `Ranged<36289, 36292> % Ranged<6, 9> = Ranged<0, 8>` while the 360 | //! result never exceeds `Ranged<1, 4>`. 361 | //! 362 | //! ## Pattern matching 363 | //! 364 | //! A limited version is implemented with [`rmatch!`] macro. 365 | //! 366 | 367 | 368 | #![no_std] 369 | #![allow(incomplete_features)] 370 | 371 | #![feature(adt_const_params)] 372 | #![feature(generic_const_exprs)] 373 | 374 | #![feature(const_trait_impl)] 375 | 376 | #![deny(missing_docs)] 377 | #![deny(clippy::nursery)] 378 | #![warn(clippy::pedantic)] 379 | 380 | #[cfg(test)] 381 | #[macro_use] 382 | extern crate std; 383 | 384 | #[cfg(test)] 385 | mod tests; 386 | 387 | #[doc(hidden)] 388 | pub mod value_check; 389 | use holder::{AlignWrap, Aligner}; 390 | use value_check::{Assert, IsAllowed, OperationPossibility}; 391 | 392 | mod holder; 393 | 394 | // An alias integer representing the largest possible ranged integer 395 | #[allow(non_camel_case_types)] 396 | type irang = i128; 397 | 398 | /// Pick out the "smallest" IntLayout that fits the min..=max range. 399 | /// To be evaluated at compile time. 400 | /// Panics to emit an error when min>max. 401 | #[must_use] 402 | #[doc(hidden)] 403 | pub const fn memlayout(min: irang, max: irang) -> holder::IntLayout { 404 | macro_rules! layout_variants { 405 | ($($t:ident)+) => { 406 | $( if $t::MIN as irang <= min && max <= $t::MAX as irang {return holder::IntLayout::$t} )+ 407 | } 408 | } 409 | if min == max { 410 | return holder::IntLayout::Trivial; 411 | } 412 | assert!(min <= max, "Ranged error: MIN cannot be greater than MAX"); 413 | layout_variants! {i8 u8 i16 u16 i32 u32 i64 u64} 414 | holder::IntLayout::i128 415 | } 416 | 417 | /// A value restricted to the given bounds 418 | #[repr(transparent)] 419 | #[derive(Clone, Copy)] 420 | pub struct Ranged 421 | where [u8; memlayout(MIN, MAX).bytes()]:,AlignWrap<{memlayout(MIN, MAX)}>: Aligner, 422 | { 423 | v: holder::RangedRepr<{ memlayout(MIN, MAX) }>, 424 | } 425 | 426 | #[must_use] 427 | #[doc(hidden)] 428 | pub const fn allow_creation(min: irang, v: irang, max: irang) -> bool { 429 | min <= v && v <= max 430 | } 431 | 432 | impl Ranged 433 | where 434 | [u8; memlayout(MIN, MAX).bytes()]: ,AlignWrap<{memlayout(MIN, MAX)}>: Aligner, 435 | { 436 | #[allow(clippy::inline_always)] #[must_use] #[inline(always)] 437 | const unsafe fn __unsafe_new(n: irang) -> Self { 438 | Self { 439 | v: holder::RangedRepr::from_irang(n), 440 | } 441 | } 442 | 443 | #[allow(clippy::inline_always)] #[must_use] #[inline(always)] 444 | /// Convert Ranged to a primitive 445 | const fn get(self) -> irang { 446 | if MIN == MAX {MIN} 447 | else {self.v.to_irang()} 448 | } 449 | 450 | /// Create a Ranged value checking the bounds at runtime 451 | /// 452 | /// **Note**: due to incomplete features limitations, the function may fail to compile with 453 | /// the 'trait bound is not satisfied' if the bounds are not explicitly specified. 454 | /// 455 | /// # Example 456 | /// 457 | /// ``` 458 | /// # #![feature(adt_const_params, generic_const_exprs)] use ranged_integers::*; 459 | /// # fn main() -> Result<(), Box> { 460 | /// let input = "42".to_string(); 461 | /// let user_input = input.parse()?; 462 | /// if let Some(input) = Ranged::<1, 100>::new(user_input){ 463 | /// println!("The value is in range 1..=100") 464 | /// } 465 | /// else { 466 | /// println!("The value is too high :(") 467 | /// } 468 | /// # Ok(()) } 469 | /// ``` 470 | #[must_use] 471 | pub const fn new(n: irang) -> Option { 472 | if (MIN <= n) && (n <= MAX) { 473 | Some(unsafe { Self::__unsafe_new(n) }) 474 | } else { 475 | None 476 | } 477 | } 478 | 479 | /// Create a Ranged constant checking the bounds at compile time 480 | /// 481 | /// Consider using [`r!`] macro instead 482 | /// 483 | /// # Example 484 | /// 485 | /// ``` 486 | /// # #![feature(adt_const_params, generic_const_exprs)] use ranged_integers::*; 487 | /// let a = Ranged::<0, 100>::create_const::<42>(); 488 | /// let a = r!([0 100] 42); 489 | /// ``` 490 | #[must_use] 491 | pub const fn create_const() -> Self 492 | where Assert<{ OperationPossibility::allow_if(allow_creation(MIN, V, MAX)) }>: IsAllowed, 493 | { 494 | unsafe { Self::__unsafe_new(V) } 495 | } 496 | 497 | /// Iterate up from current value to `Self::MAX` (inclusively) using `Self` as output 498 | #[must_use] 499 | pub const fn iter_up(self) -> iter::Iter { 500 | iter::Iter::{current: Some(self)} 501 | } 502 | } 503 | 504 | mod conversions; 505 | pub use conversions::AsRanged; 506 | mod arithmetics; 507 | mod iter; 508 | pub use iter::ConstRange as ConstInclusiveRange; 509 | 510 | 511 | /// Create a ranged value or a range at compile time 512 | /// 513 | /// **Warning**: ensure `#![feature(adt_const_params)]` is enabled. 514 | /// 515 | /// # Example 516 | /// 517 | /// ``` 518 | /// # #![feature(adt_const_params, generic_const_exprs)] use ranged_integers::*; 519 | /// // Explicit bounds: 520 | /// let a = r!([0 42] 23); // Ranged<0, 42> with a value 23 521 | /// // Type inference: 522 | /// let b: Ranged<0, 100> = r!([] 42); // Ranged<0, 100> with a value 42 523 | /// // "Constant" value: 524 | /// let c = r!(10); // Zero-sized Ranged<10, 10> with a value 10 525 | /// //Range: 526 | /// for i in r!(0..10){ 527 | /// let v: Ranged<0,9> = i; 528 | /// } 529 | /// ``` 530 | #[macro_export] 531 | macro_rules! r { 532 | ([$min:literal $max:literal] $x:expr) => { 533 | $crate::Ranged::<$min, $max>::create_const::<$x>() 534 | }; 535 | ([] $v:expr) => { 536 | $crate::Ranged::create_const::<$v>() 537 | }; 538 | ($min:tt..$end:tt) => { 539 | $crate::ConstInclusiveRange::<{$min}, {$end-1}> 540 | }; 541 | (-$min:tt..-$end:tt) => { 542 | $crate::ConstInclusiveRange::<{-$min}, {-$end-1}> 543 | }; 544 | (-$min:tt..$end:tt) => { 545 | $crate::ConstInclusiveRange::<{-$min}, {$end-1}> 546 | }; 547 | (-$min:tt..=$max:tt) => { 548 | $crate::ConstInclusiveRange::<{-$min}, {$max}> 549 | }; 550 | (-$min:tt..=-$max:tt) => { 551 | $crate::ConstInclusiveRange::<{-$min}, {-$max}> 552 | }; 553 | ($min:tt..=$max:tt) => { 554 | $crate::ConstInclusiveRange::<{$min}, {$max}> 555 | }; 556 | ($v:literal) => { 557 | $crate::Ranged::<$v, $v>::create_const::<$v>() 558 | }; 559 | ($v:tt) => { 560 | $crate::Ranged::<$v, $v>::create_const::<{$v}>() 561 | }; 562 | } 563 | 564 | impl core::fmt::Display for Ranged 565 | where [(); memlayout(MIN, MAX).bytes()]: ,AlignWrap<{memlayout(MIN, MAX)}>: Aligner, 566 | { 567 | fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { 568 | write!(f, "{}", self.get()) 569 | } 570 | } 571 | 572 | impl core::fmt::Debug for Ranged 573 | where [(); memlayout(MIN, MAX).bytes()]: ,AlignWrap<{memlayout(MIN, MAX)}>: Aligner, 574 | { 575 | fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { 576 | if MIN == MAX { 577 | write!(f, "r!({MIN})") 578 | } else { 579 | write!(f, "r!([{MIN} {MAX}] {})", self.get()) 580 | } 581 | } 582 | } 583 | 584 | #[doc(hidden)] #[must_use] 585 | pub const fn min_irang(x: irang, y: irang) -> irang { 586 | if x < y {x} else {y} 587 | } 588 | #[doc(hidden)] #[must_use] 589 | pub const fn max_irang(x: irang, y: irang) -> irang { 590 | if x > y {x} else {y} 591 | } 592 | 593 | #[allow(clippy::cast_sign_loss)] 594 | impl core::ops::Index> for [T; N] 595 | where 596 | [u8; memlayout(0, N as i128 - 1).bytes()]:,AlignWrap<{memlayout(0, N as i128 - 1)}>: Aligner, 597 | Assert<{conversions::converter_checkers::usize(0, N as i128 - 1)}>: IsAllowed 598 | { 599 | type Output = T; 600 | fn index(&self, index: Ranged<0, {N as i128 - 1}>) -> &Self::Output { 601 | unsafe{self.get_unchecked(index.usize())} 602 | } 603 | } 604 | 605 | #[allow(clippy::cast_sign_loss)] 606 | impl core::ops::IndexMut> for [T; N] 607 | where 608 | [u8; memlayout(0, N as i128 - 1).bytes()]:,AlignWrap<{memlayout(0, N as i128 - 1)}>: Aligner, 609 | Assert<{conversions::converter_checkers::usize(0, N as i128 - 1)}>: IsAllowed 610 | { 611 | fn index_mut(&mut self, index: Ranged<0, {N as i128 - 1}>) -> &mut Self::Output { 612 | unsafe{self.get_unchecked_mut(index.usize())} 613 | } 614 | } 615 | 616 | #[allow(clippy::cast_sign_loss)] 617 | impl 618 | core::ops::Index> for [T; N] 619 | where 620 | [(); memlayout(MIN, MAX).bytes()]:, 621 | AlignWrap<{memlayout(MIN, MAX)}>: Aligner, 622 | [T; (MAX-MIN+1) as usize]:, 623 | Assert<{conversions::converter_checkers::usize(MIN, MAX)}>: IsAllowed 624 | { 625 | type Output = [T; (MAX-MIN+1) as usize]; 626 | fn index(&self, _index: iter::ConstRange) -> &Self::Output { 627 | unsafe{ 628 | let ptr = self.get_unchecked((MIN as usize)..=(MAX as usize)).as_ptr().cast(); 629 | &*ptr 630 | } 631 | } 632 | } 633 | #[allow(clippy::cast_sign_loss)] 634 | impl 635 | core::ops::IndexMut> for [T; N] 636 | where 637 | [(); memlayout(MIN, MAX).bytes()]:, 638 | AlignWrap<{memlayout(MIN, MAX)}>: Aligner, 639 | [T; (MAX-MIN+1) as usize]:, 640 | Assert<{conversions::converter_checkers::usize(MIN, MAX)}>: IsAllowed 641 | { 642 | fn index_mut(&mut self, _index: iter::ConstRange) -> &mut Self::Output { 643 | unsafe{ 644 | let ptr = self.get_unchecked_mut((MIN as usize)..=(MAX as usize)).as_mut_ptr().cast(); 645 | &mut *ptr 646 | } 647 | } 648 | } 649 | 650 | /// Ranged pattern matching macro 651 | /// 652 | /// Allows to match a [`Ranged`] value over a range it covers. The feature has 653 | /// the following limitations: 654 | /// - The bounds must be explicitly specified; they are checked, but not inferred 655 | /// - The macro syntax supports a subset of Rust pattern matching syntax 656 | /// - `i128::MIN` and `i128::MAX` values must not be in range 657 | /// - The unclear error reporting 658 | /// 659 | /// ``` 660 | /// # #![feature(adt_const_params)] use ranged_integers::*; 661 | /// fn ranged_to_bool(r: Ranged<0,1>) -> bool { 662 | /// rmatch!{[0 1] r // Bounds and expression (token tree, 663 | /// // complex expressions must be in parentheses) 664 | /// 0 => {false} 665 | /// 1 => {true} // Complex patterns like 1..5 | 12..14 are supported 666 | /// } 667 | /// } 668 | /// ``` 669 | #[macro_export] 670 | macro_rules! rmatch { 671 | ([$min:literal $max:literal] $val:tt 672 | $( 673 | $p:pat => { $e:expr } 674 | )* 675 | ) => { 676 | { 677 | #[allow(renamed_and_removed_lints)] 678 | #[deny(const_err)] 679 | const _MINF: i128 = $min - 1; 680 | #[allow(renamed_and_removed_lints)] 681 | #[deny(const_err)] 682 | const _PINF: i128 = $max + 1; 683 | let _v: Ranged<$min, $max> = $val; 684 | match _v.i128() { 685 | i128::MIN..=_MINF => unsafe {core::hint::unreachable_unchecked()} 686 | _PINF..=i128::MAX => unsafe {core::hint::unreachable_unchecked()} 687 | $( $p => { $e } )* 688 | } 689 | } 690 | }; 691 | } 692 | 693 | 694 | #[allow(dead_code)] 695 | #[doc(hidden)] 696 | /** 697 | ``` 698 | # #![feature(adt_const_params)] use ranged_integers::*; 699 | u8::from(r!(0)); 700 | ``` 701 | 702 | ```compile_fail 703 | # #![feature(adt_const_params)] use ranged_integers::*; 704 | u8::from(r!(-1)); 705 | ``` 706 | 707 | ``` 708 | # #![feature(adt_const_params)] use ranged_integers::*; 709 | u8::from(r!(255)); 710 | ``` 711 | 712 | ```compile_fail 713 | # #![feature(adt_const_params)] use ranged_integers::*; 714 | u8::from(r!(256)); 715 | ``` 716 | 717 | ``` 718 | # #![feature(adt_const_params)] use ranged_integers::*; 719 | i8::from(r!(-128)); 720 | ``` 721 | 722 | 723 | ```compile_fail 724 | # #![feature(adt_const_params)] use ranged_integers::*; 725 | i8::from(r!(-129)); 726 | ``` 727 | 728 | ``` 729 | # #![feature(adt_const_params)] use ranged_integers::*; 730 | i8::from(r!(127)); 731 | ``` 732 | 733 | 734 | ```compile_fail 735 | # #![feature(adt_const_params)] use ranged_integers::*; 736 | i8::from(r!(128)); 737 | ``` 738 | 739 | 740 | ``` 741 | # #![feature(adt_const_params)] #![feature(generic_const_exprs)] 742 | # #[macro_use] extern crate ranged_integers; use ranged_integers::*; 743 | let a = r![[100 1000] 500] / r![[1 6] 5]; 744 | ``` 745 | ```compile_fail 746 | # #![feature(adt_const_params)] #![feature(generic_const_exprs)] 747 | # #[macro_use] extern crate ranged_integers; use ranged_integers::*; 748 | let a = r![[100 1000] 500] / r![[0 6] 5]; 749 | ``` 750 | ```compile_fail 751 | # #![feature(adt_const_params)] #![feature(generic_const_exprs)] 752 | # #[macro_use] extern crate ranged_integers; use ranged_integers::*; 753 | let a = r![[100 1000] 500] / r![[-1 6] 5]; 754 | ``` 755 | 756 | ``` 757 | # #![feature(adt_const_params)] use ranged_integers::*; 758 | Ranged::<0,1>::new(1); 759 | ``` 760 | 761 | 762 | ```compile_fail 763 | # #![feature(adt_const_params)] use ranged_integers::*; 764 | Ranged::<1,0>::new(1); 765 | ``` 766 | 767 | 768 | ``` 769 | # #![feature(adt_const_params)] use ranged_integers::*; 770 | let x: Ranged::<0,1> = Ranged::<0,1>::new(1).unwrap(); 771 | ``` 772 | 773 | 774 | ``` 775 | # #![feature(adt_const_params)] use ranged_integers::*; 776 | let x: Ranged::<0,1> = Ranged::<0,1>::new(1).unwrap(); 777 | ``` 778 | 779 | 780 | ``` 781 | # #![feature(adt_const_params)] use ranged_integers::*; 782 | let a = r!([-10 10] 0); 783 | rmatch!{[-10 10] a 784 | _ => {()} 785 | } 786 | ``` 787 | 788 | ```compile_fail 789 | # #![feature(adt_const_params)] use ranged_integers::*; 790 | let a = r!([-170141183460469231731687303715884105728 10] 0); 791 | rmatch!{[-170141183460469231731687303715884105728 10] a 792 | _ => {()} 793 | } 794 | ``` 795 | 796 | ```compile_fail 797 | # #![feature(adt_const_params)] use ranged_integers::*; 798 | let a = r!([-10 170141183460469231731687303715884105727] 0); 799 | rmatch!{[-10 170141183460469231731687303715884105727] a 800 | _ => {()} 801 | } 802 | ``` 803 | 804 | ``` 805 | # #![feature(generic_const_exprs)] use ranged_integers::*; 806 | assert!( r!([10 50] 30).fit_less_than(r!([10 45] 40)) == Some(r!([10 45] 30)) ); 807 | ``` 808 | 809 | ``` 810 | # #![feature(generic_const_exprs)] use ranged_integers::*; 811 | assert_eq!(r!([10 50] 39).fit_less_than( r!([0 45] 40) ), Some(r!([10 45] 39))); 812 | ``` 813 | 814 | ``` 815 | # #![feature(generic_const_exprs)] use ranged_integers::*; 816 | assert_eq!(r!([10 50] 39).fit_less_than( r!([30 45] 40) ), Some(r!([10 45] 39))); 817 | ``` 818 | 819 | ``` 820 | # #![feature(generic_const_exprs)] use ranged_integers::*; 821 | assert_eq!(r!([10 50] 40).fit_less_than( r!([30 45] 40) ), None); 822 | ``` 823 | 824 | ```compile_fail 825 | # #![feature(generic_const_exprs)] use ranged_integers::*; 826 | assert_eq!(r!([10 40] 39).fit_less_than( r!([30 45] 40) ), Some(r!([10 45] 39))); 827 | ``` 828 | 829 | ```compile_fail 830 | # #![feature(generic_const_exprs)] use ranged_integers::*; 831 | assert_eq!(r!([45 50] 47).fit_less_than( r!([0 45] 40) ), None); 832 | ``` 833 | 834 | ```compile_fail 835 | # #![feature(generic_const_exprs)] use ranged_integers::*; 836 | assert_eq!(r!([10 20] 10).fit_less_than( r!([30 45] 40) ), Some(r!([10 45] 10))); 837 | ``` 838 | 839 | 840 | 841 | ``` 842 | # #![feature(generic_const_exprs)] use ranged_integers::*; 843 | assert!( r!([10 50] 40).fit_less_eq(r!([10 45] 40)) == Some(r!([10 45] 40)) ); 844 | ``` 845 | 846 | ``` 847 | # #![feature(generic_const_exprs)] use ranged_integers::*; 848 | assert_eq!(r!([10 50] 40).fit_less_eq( r!([0 45] 40) ), Some(r!([10 45] 40))); 849 | ``` 850 | 851 | ``` 852 | # #![feature(generic_const_exprs)] use ranged_integers::*; 853 | assert_eq!(r!([10 50] 40).fit_less_eq( r!([30 45] 40) ), Some(r!([10 45] 40))); 854 | ``` 855 | 856 | ``` 857 | # #![feature(generic_const_exprs)] use ranged_integers::*; 858 | assert_eq!(r!([10 50] 40).fit_less_eq( r!([40 45] 40) ), Some(r!([10 45] 40))); 859 | ``` 860 | 861 | ``` 862 | # #![feature(generic_const_exprs)] use ranged_integers::*; 863 | assert_eq!(r!([10 50] 41).fit_less_eq( r!([30 45] 40) ), None); 864 | ``` 865 | 866 | ```compile_fail 867 | # #![feature(generic_const_exprs)] use ranged_integers::*; 868 | assert!( r!([10 40] 40).fit_less_eq(r!([40 45] 40)) == Some(r!([10 40] 40)) ); 869 | ``` 870 | ``` 871 | # #![feature(generic_const_exprs)] use ranged_integers::*; 872 | assert!( r!([10 50] 10).fit_less_eq(r!([5 10] 10)) == Some(r!([10 10] 10)) ); 873 | ``` 874 | 875 | 876 | ``` 877 | # #![feature(generic_const_exprs)] use ranged_integers::*; 878 | assert!( r!([10 50] 40).fit_greater_than(r!([20 40] 39)) == Some(r!([20 50] 40)) ); 879 | ``` 880 | 881 | ``` 882 | # #![feature(generic_const_exprs)] use ranged_integers::*; 883 | assert!( r!([10 50] 40).fit_greater_than(r!([20 50] 39)) == Some(r!([20 50] 40)) ); 884 | ``` 885 | 886 | ``` 887 | # #![feature(generic_const_exprs)] use ranged_integers::*; 888 | assert!( r!([10 50] 40).fit_greater_than(r!([20 100] 39)) == Some(r!([20 50] 40)) ); 889 | ``` 890 | 891 | ``` 892 | # #![feature(generic_const_exprs)] use ranged_integers::*; 893 | assert_eq!(r!([10 50] 40).fit_greater_than( r!([30 45] 40) ), None); 894 | ``` 895 | 896 | ```compile_fail 897 | # #![feature(generic_const_exprs)] use ranged_integers::*; 898 | assert!( r!([10 50] 40).fit_greater_than(r!([10 40] 39)) == Some(r!([10 50] 40)) ); 899 | ``` 900 | 901 | ```compile_fail 902 | # #![feature(generic_const_exprs)] use ranged_integers::*; 903 | assert!( r!([10 50] 50).fit_greater_than(r!([50 55] 50)) == Some(r!([50 50] 50)) ); 904 | ``` 905 | 906 | ```compile_fail 907 | # #![feature(generic_const_exprs)] use ranged_integers::*; 908 | assert!( r!([10 50] 40).fit_greater_than(r!([0 10] 5)) == Some(r!([10 50] 40)) ); 909 | ``` 910 | 911 | 912 | ``` 913 | # #![feature(generic_const_exprs)] use ranged_integers::*; 914 | assert!( r!([10 50] 40).fit_greater_eq(r!([20 40] 40)) == Some(r!([20 50] 40)) ); 915 | ``` 916 | 917 | ``` 918 | # #![feature(generic_const_exprs)] use ranged_integers::*; 919 | assert!( r!([10 50] 40).fit_greater_eq(r!([20 50] 40)) == Some(r!([20 50] 40)) ); 920 | ``` 921 | 922 | ``` 923 | # #![feature(generic_const_exprs)] use ranged_integers::*; 924 | assert!( r!([10 50] 40).fit_greater_eq(r!([20 100] 40)) == Some(r!([20 50] 40)) ); 925 | ``` 926 | 927 | ``` 928 | # #![feature(generic_const_exprs)] use ranged_integers::*; 929 | assert_eq!(r!([10 50] 40).fit_greater_eq( r!([30 45] 41) ), None); 930 | ``` 931 | 932 | ```compile_fail 933 | # #![feature(generic_const_exprs)] use ranged_integers::*; 934 | assert!( r!([10 50] 40).fit_greater_eq(r!([10 40] 39)) == Some(r!([10 50] 40)) ); 935 | ``` 936 | 937 | ``` 938 | # #![feature(generic_const_exprs)] use ranged_integers::*; 939 | assert!( r!([10 50] 50).fit_greater_eq(r!([50 55] 50)) == Some(r!([50 50] 50)) ); 940 | ``` 941 | 942 | ```compile_fail 943 | # #![feature(generic_const_exprs)] use ranged_integers::*; 944 | assert!( r!([10 50] 40).fit_greater_eq(r!([0 10] 5)) == Some(r!([10 50] 40)) ); 945 | ``` 946 | 947 | 948 | 949 | */ 950 | struct Failtests; 951 | -------------------------------------------------------------------------------- /src/tests.rs: -------------------------------------------------------------------------------- 1 | #![allow(clippy::missing_const_for_fn)] 2 | #![allow(clippy::cognitive_complexity)] 3 | 4 | use std::prelude::v1::*; 5 | 6 | use super::*; 7 | use core::ops::Add; 8 | use core::ops::Div; 9 | use core::ops::Mul; 10 | use core::ops::Sub; 11 | 12 | #[test] 13 | fn sizes() { 14 | use core::mem::{align_of, size_of}; 15 | 16 | macro_rules! sz_align { 17 | ($sz:ty, $t:ty) => { 18 | assert_eq!(size_of::<$t>(), size_of::<$sz>()); 19 | assert_eq!(align_of::<$t>(), align_of::<$sz>()); 20 | }; 21 | } 22 | 23 | sz_align!((), Ranged<0,0>); 24 | sz_align!((), Ranged<1,1>); 25 | sz_align!((), Ranged<-1,-1>); 26 | sz_align!((), Ranged<100, 100>); 27 | sz_align!((), Ranged<-100, -100>); 28 | sz_align!((), Ranged<500, 500>); 29 | sz_align!((), Ranged<-500, -500>); 30 | sz_align!((), Ranged<100_000, 100_000>); 31 | sz_align!((), Ranged<-100_000, -100_000>); 32 | sz_align!((), Ranged<10_000_000_000, 10_000_000_000>); 33 | sz_align!((), Ranged<-10_000_000_000, -10_000_000_000>); 34 | sz_align!((), Ranged<18_446_744_073_709_551_616, 18_446_744_073_709_551_616>); 35 | sz_align!((), Ranged<-18_446_744_073_709_551_616, -18_446_744_073_709_551_616>); 36 | 37 | sz_align!((), Ranged<-32768, -32768>); 38 | sz_align!((), Ranged<32767, 32767>); 39 | sz_align!((), Ranged<65535, 65535>); 40 | sz_align!((), Ranged<65536, 65536>); 41 | 42 | sz_align!(i8, Ranged<10,11>); 43 | sz_align!(i8, Ranged<254,255>); 44 | sz_align!(i8, Ranged<126,127>); 45 | sz_align!(i8, Ranged<-128, -127>); 46 | sz_align!(i8, Ranged<0,10>); 47 | sz_align!(i8, Ranged<0,127>); 48 | sz_align!(i8, Ranged<0,255>); 49 | sz_align!(i8, Ranged<127,255>); 50 | sz_align!(i8, Ranged<-128, 127>); 51 | 52 | sz_align!(i16, Ranged<-128, 128>); 53 | 54 | sz_align!(i16, Ranged<-32768, 32767>); 55 | sz_align!(i16, Ranged<0, 32768>); 56 | sz_align!(i16, Ranged<0, 65535>); 57 | sz_align!(i16, Ranged<-32768, -32767>); 58 | sz_align!(i16, Ranged<32766, 32767>); 59 | sz_align!(i16, Ranged<65534, 65535>); 60 | 61 | sz_align!(i32, Ranged<-32768, 32768>); 62 | sz_align!(i32, Ranged<0, 65536>); 63 | 64 | sz_align!(i32, Ranged<0, 4_294_967_295>); 65 | sz_align!(i32, Ranged<-2_147_483_648, 2_147_483_647>); 66 | sz_align!(i32, Ranged<100, 10_000_000>); 67 | sz_align!(i32, Ranged<-100, 10_000_000>); 68 | sz_align!(i32, Ranged<100, 2_147_483_647>); 69 | sz_align!(i32, Ranged<-100, 2_147_483_647>); 70 | 71 | sz_align!(i64, Ranged<-1, 4_294_967_295>); 72 | sz_align!(i64, Ranged<0, 4_294_967_296>); 73 | sz_align!(i64, Ranged<-2_147_483_649, 2_147_483_647>); 74 | sz_align!(i64, Ranged<-2_147_483_648, 2_147_483_648>); 75 | 76 | sz_align!(i64, Ranged<0, 18_446_744_073_709_551_615>); 77 | sz_align!(i64, Ranged<-9_223_372_036_854_775_808, 9_223_372_036_854_775_807>); 78 | 79 | sz_align!(i128, Ranged<-1, 18_446_744_073_709_551_615>); 80 | 81 | sz_align!(i128, Ranged<0, 18_446_744_073_709_551_616>); 82 | sz_align!(i128, Ranged<-9_223_372_036_854_775_809, 9_223_372_036_854_775_807>); 83 | sz_align!(i128, Ranged<-9_223_372_036_854_775_808, 9_223_372_036_854_775_808>); 84 | } 85 | 86 | #[test] 87 | fn print_val() { 88 | macro_rules! assert_val { 89 | ([$min:literal $max:literal] $x:literal) => { 90 | assert_eq!(r!([$min $max] $x).get(), $x); 91 | } 92 | } 93 | 94 | for i in 0..126 { 95 | let w = 2_i128.pow(i); 96 | assert_eq!(w.as_ranged().get(), w); 97 | assert_eq!((-w).as_ranged().get(), -w); 98 | } 99 | 100 | assert_val!([-128 127] -128); 101 | assert_val!([-128 127] -127); 102 | assert_val!([-128 127] -30); 103 | assert_val!([-128 127] -1); 104 | assert_val!([-128 127] 0); 105 | assert_val!([-128 127] 1); 106 | assert_val!([-128 127] 30); 107 | assert_val!([-128 127] 127); 108 | 109 | assert_val!([0 200] 0); 110 | assert_val!([0 200] 1); 111 | assert_val!([0 200] 30); 112 | assert_val!([0 200] 127); 113 | assert_val!([0 200] 128); 114 | assert_val!([0 200] 200); 115 | 116 | assert_val!([-1000 1000] -500); 117 | assert_val!([-1000 1000] -100); 118 | assert_val!([-1000 1000] 0); 119 | assert_val!([-1000 1000] 100); 120 | assert_val!([-1000 1000] 500); 121 | 122 | assert_val!([0 1000] 0); 123 | assert_val!([0 1000] 100); 124 | assert_val!([0 1000] 500); 125 | 126 | assert_val!([0 65535] 0); 127 | assert_val!([0 65535] 1); 128 | assert_val!([0 65535] 65534); 129 | assert_val!([0 65535] 65535); 130 | 131 | let x = r!(42); 132 | assert_eq!(format!("{x}"), "42"); 133 | assert_eq!(format!("{x:?}"), "r!(42)"); 134 | 135 | let x = r!(400_000); 136 | assert_eq!(format!("{x}"), "400000"); 137 | 138 | let x = r!(4000); 139 | assert_eq!(format!("{x}"), "4000"); 140 | 141 | let x = r!(40); 142 | assert_eq!(format!("{x}"), "40"); 143 | 144 | let x = r!(0); 145 | assert_eq!(format!("{x}"), "0"); 146 | 147 | let x = r!(-400_000); 148 | assert_eq!(format!("{x}"), "-400000"); 149 | 150 | let x = r!(-4000); 151 | assert_eq!(format!("{x}"), "-4000"); 152 | 153 | let x = r!(-40); 154 | assert_eq!(format!("{x}"), "-40"); 155 | } 156 | 157 | #[test] 158 | fn ranged_macro() { 159 | let x = r! {[0 4] 2}; 160 | assert_eq!(format!("{x}"), "2"); 161 | assert_eq!(format!("{x:?}"), "r!([0 4] 2)"); 162 | 163 | let x: Ranged<2, 15> = r!([] 10); 164 | assert_eq!(format!("{x}"), "10"); 165 | assert_eq!(format!("{x:?}"), "r!([2 15] 10)"); 166 | 167 | let x = r!(10); 168 | assert_eq!(format!("{x}"), "10"); 169 | assert_eq!(format!("{x:?}"), "r!(10)"); 170 | } 171 | 172 | #[test] 173 | fn addsub() { 174 | let a = r!(20) + r!(22); 175 | assert_eq!(format!("{a}"), "42"); 176 | assert_eq!(format!("{a:?}"), "r!(42)"); 177 | 178 | let a = r!(20).add(r!(22)); 179 | assert_eq!(format!("{a}"), "42"); 180 | assert_eq!(format!("{a:?}"), "r!(42)"); 181 | 182 | let a = 15_u32 % r!(20); 183 | let b = a + a; 184 | assert_eq!(format!("{b}"), "30"); 185 | assert_eq!(format!("{b:?}"), "r!([0 38] 30)"); 186 | 187 | let c = r!(22) - r!(20); 188 | assert_eq!(format!("{c}"), "2"); 189 | assert_eq!(format!("{c:?}"), "r!(2)"); 190 | 191 | let c = r!(22).sub(r!(20)); 192 | assert_eq!(format!("{c}"), "2"); 193 | assert_eq!(format!("{c:?}"), "r!(2)"); 194 | } 195 | 196 | #[test] 197 | fn mul() { 198 | let a = r!(20) * r!(20); 199 | assert_eq!(format!("{a}"), "400"); 200 | assert_eq!(format!("{a:?}"), "r!(400)"); 201 | 202 | let b = r! {[-3 3] 1} * r! {[-3 3] 2}; 203 | assert_eq!(format!("{b}"), "2"); 204 | assert_eq!(format!("{b:?}"), "r!([-9 9] 2)"); 205 | 206 | let c = Ranged::<{ -3 }, 0>::new(-1).unwrap() * Ranged::<0, 3>::new(2).unwrap(); 207 | assert_eq!(format!("{c}"), "-2"); 208 | assert_eq!(format!("{c:?}"), "r!([-9 0] -2)"); 209 | 210 | let b = r! {[-30000 30000] 1} * r! {[-3 3] 2}; 211 | assert_eq!(format!("{b}"), "2"); 212 | assert_eq!(format!("{b:?}"), "r!([-90000 90000] 2)"); 213 | 214 | let a = r!(20).mul(r!(20)); 215 | assert_eq!(format!("{a}"), "400"); 216 | assert_eq!(format!("{a:?}"), "r!(400)"); 217 | 218 | let b = r! {[-3 3] 1}.mul(r! {[-3 3] 2}); 219 | assert_eq!(format!("{b}"), "2"); 220 | assert_eq!(format!("{b:?}"), "r!([-9 9] 2)"); 221 | 222 | let c = Ranged::<{ -3 }, 0>::new(-1) 223 | .unwrap() 224 | .mul(Ranged::<0, 3>::new(2).unwrap()); 225 | assert_eq!(format!("{c}"), "-2"); 226 | assert_eq!(format!("{c:?}"), "r!([-9 0] -2)"); 227 | 228 | let b = r! {[-30000 30000] 1}.mul(r! {[-3 3] 2}); 229 | assert_eq!(format!("{b}"), "2"); 230 | assert_eq!(format!("{b:?}"), "r!([-90000 90000] 2)"); 231 | } 232 | 233 | #[test] 234 | fn div() { 235 | let a = r!(20) / r!(20); 236 | assert_eq!(format!("{a}"), "1"); 237 | assert_eq!(format!("{a:?}"), "r!(1)"); 238 | 239 | let a = r!(20).div(r!(20)); 240 | assert_eq!(format!("{a}"), "1"); 241 | assert_eq!(format!("{a:?}"), "r!(1)"); 242 | 243 | let a = r!([0 100] 20) / r!([1 10] 5); 244 | assert_eq!(format!("{a}"), "4"); 245 | assert_eq!(format!("{a:?}"), "r!([0 100] 4)"); 246 | 247 | let a = r!([0 100] 20) / r![[-10 -1] - 5]; 248 | assert_eq!(format!("{a}"), "-4"); 249 | assert_eq!(format!("{a:?}"), "r!([-100 0] -4)"); 250 | 251 | let a = r!([-100 0] -20).div_euclid(r![[-10 - 1] - 5]); 252 | assert_eq!(format!("{a}"), "4"); 253 | assert_eq!(format!("{a:?}"), "r!([0 100] 4)"); 254 | 255 | let a = r!([-100 0] -20) / r!([1 10] 5); 256 | assert_eq!(format!("{a}"), "-4"); 257 | assert_eq!(format!("{a:?}"), "r!([-100 0] -4)"); 258 | 259 | let a = r!([100 1000] 500) / r!([1 6] 5); 260 | assert_eq!(format!("{a}"), "100"); 261 | assert_eq!(format!("{a:?}"), "r!([16 1000] 100)"); 262 | 263 | let a = r!([100 1000] 500) / r![[-6 - 1] - 5]; 264 | assert_eq!(format!("{a}"), "-100"); 265 | assert_eq!(format!("{a:?}"), "r!([-1000 -16] -100)"); 266 | 267 | let _: Ranged<0, 50> = r!([0 101] 17) / r![[2 10] 5]; 268 | let _: Ranged<0, 50> = r!([0 101] 17).div_euclid(r![[2 10] 5]); 269 | let _: Ranged<-50, 0> = r!([-101 0] -17) / r![[2 10] 5]; 270 | let _: Ranged<-51, 0> = r!([-101 0] -17).div_euclid(r![[2 10] 5]); 271 | 272 | let _: Ranged<-50, 0> = r!([0 101] 17) / r![[-10 -2] -5]; 273 | let _: Ranged<-50, 0> = r!([0 101] 17).div_euclid(r![[-10 -2] -5]); 274 | let _: Ranged<0, 50> = r!([-101 0] -17) / r![[-10 -2] -5]; 275 | let _: Ranged<0, 51> = r!([-101 0] -17).div_euclid(r![[-10 -2] -5]); 276 | } 277 | 278 | #[test] 279 | fn rem() { 280 | macro_rules! chrem{ 281 | ($a:literal / $b:literal) => {{ 282 | assert_eq!(r!($a)/r!($b)*r!($b) + r!($a)%r!($b), r!($a)); 283 | assert_eq!(r!($a).div_euclid(r!($b))*r!($b) + r!($a).rem_euclid(r!($b)), r!($a)); 284 | }} 285 | } 286 | 287 | chrem!(9 / 5); 288 | chrem!(10 / 5); 289 | chrem!(11 / 5); 290 | chrem!(9 / -5); 291 | chrem!(10 / -5); 292 | chrem!(11 / -5); 293 | chrem!(-9 / 5); 294 | chrem!(-10 / 5); 295 | chrem!(-11 / 5); 296 | chrem!(-9 / -5); 297 | chrem!(-10 / -5); 298 | chrem!(-11 / -5); 299 | 300 | // Rem operation for primitives 301 | let a: Ranged<0, 2> = 7_u8 % r!(3); 302 | assert_eq!(a, 1); 303 | let a: Ranged<0, 2> = 7_u8 % r!(-3); 304 | assert_eq!(a, 1); 305 | let a: Ranged<-2, 2> = 7_i8 % r!(3); 306 | assert_eq!(a, 1); 307 | let a: Ranged<-2, 2> = 7_i8 % r!(-3); 308 | assert_eq!(a, 1); 309 | let a: Ranged<-2, 2> = -7_i8 % r!(3); 310 | assert_eq!(a, -1); 311 | let a: Ranged<-2, 2> = -7_i8 % r!(-3); 312 | assert_eq!(a, -1); 313 | 314 | // Value checks 315 | assert_eq!(r!(25) % r!(20), 5); 316 | assert_eq!(r!(25) % r!(-20), 5); 317 | assert_eq!(r!(-25) % r!(20), -5); 318 | assert_eq!(r!(-25) % r!(-20), -5); 319 | assert_eq!(r!(25).rem_euclid(r!(20)), 5); 320 | assert_eq!(r!(25).rem_euclid(r!(-20)), 5); 321 | assert_eq!(r!(-25).rem_euclid(r!(20)), 15); 322 | assert_eq!(r!(-25).rem_euclid(r!(-20)), 15); 323 | 324 | // Range checks, Tier 1/5: constant values 325 | let _: Ranged<0, 0> = r!(25) % r!(25); 326 | let _: Ranged<5, 5> = r!(25) % r!(20); 327 | let _: Ranged<5, 5> = r!(25) % r!(-20); 328 | let _: Ranged<-5, -5> = r!(-25) % r!(20); 329 | let _: Ranged<-5, -5> = r!(-25) % r!(-20); 330 | 331 | let _: Ranged<0, 0> = r!(25).rem_euclid(r!(25)); 332 | let _: Ranged<5, 5> = r!(25).rem_euclid(r!(20)); 333 | let _: Ranged<5, 5> = r!(25).rem_euclid(r!(-20)); 334 | let _: Ranged<15, 15> = r!(-25).rem_euclid(r!(20)); 335 | let _: Ranged<15, 15> = r!(-25).rem_euclid(r!(-20)); 336 | 337 | // Range checks, Tier 2/5: small range by constant value 338 | let _: Ranged<3, 6> = r!([23 26] 23) % r!(20); 339 | let _: Ranged<3, 6> = r!([23 26] 23) % r!(-20); 340 | let _: Ranged<-6, -3> = r!([-26 -23] -23) % r!(20); 341 | let _: Ranged<-6, -3> = r!([-26 -23] -23) % r!(-20); 342 | 343 | let _: Ranged<3, 6> = r!([23 26] 23).rem_euclid(r!(20)); 344 | let _: Ranged<3, 6> = r!([23 26] 23).rem_euclid(r!(-20)); 345 | let _: Ranged<14, 17> = r!([-26 -23] -23).rem_euclid(r!(20)); 346 | let _: Ranged<14, 17> = r!([-26 -23] -23).rem_euclid(r!(-20)); 347 | 348 | // Range checks, Tier 2a/5: small range must fail 349 | let _: Ranged<0, 19> = r!([19 21] 21) % r!(20); 350 | let _: Ranged<0, 19> = r!([19 21] 21) % r!(-20); 351 | let _: Ranged<-19, 0> = r!([-21 -19] -21) % r!(20); 352 | let _: Ranged<-19, 0> = r!([-21 -19] -21) % r!(-20); 353 | 354 | let _: Ranged<0, 19> = r!([19 21] 21).rem_euclid(r!(20)); 355 | let _: Ranged<0, 19> = r!([19 21] 21).rem_euclid(r!(-20)); 356 | let _: Ranged<0, 19> = r!([-21 -19] -21).rem_euclid(r!(20)); 357 | let _: Ranged<0, 19> = r!([-21 -19] -21).rem_euclid(r!(-20)); 358 | 359 | // Range checks, Tier 3/5: positive dividend 360 | // 3a: large dividend, small divisor 361 | let _: Ranged<0, 39> = r!([2 400] 21) % r!([1 40] 10); 362 | let _: Ranged<0, 39> = r!([2 400] 21) % r!([-40 -1] -10); 363 | let _: Ranged<0, 39> = r!([2 400] 21).rem_euclid(r!([1 40] 10)); 364 | let _: Ranged<0, 39> = r!([2 400] 21).rem_euclid(r!([-40 -1] -10)); 365 | // 3b: large divisor, small dividend 366 | let _: Ranged<0, 20> = r!([2 20] 17) % r!([1 400] 10); 367 | let _: Ranged<0, 20> = r!([2 20] 17) % r!([-400 -1] -10); 368 | let _: Ranged<0, 20> = r!([2 20] 17).rem_euclid(r!([1 400] 10)); 369 | let _: Ranged<0, 20> = r!([2 20] 17).rem_euclid(r!([-400 -1] -10)); 370 | 371 | // Range checks, Tier 4/5: negative dividend 372 | // 4a: large dividend, small divisor 373 | let _: Ranged<-39, 0> = r!([-400 -2] -21) % r!([1 40] 10); 374 | let _: Ranged<-39, 0> = r!([-400 -2] -21) % r!([-40 -1] -10); 375 | let _: Ranged<0, 39> = r!([-400 -2] -21).rem_euclid(r!([1 40] 10)); 376 | let _: Ranged<0, 39> = r!([-400 -2] -21).rem_euclid(r!([-40 -1] -10)); 377 | // 4b: large divisor, small dividend 378 | let _: Ranged<-20, 0> = r!([-20 -2] -17) % r!([1 400] 10); 379 | let _: Ranged<-20, 0> = r!([-20 -2] -17) % r!([-400 -1] -10); 380 | let _: Ranged<0, 399> = r!([-20 -2] -17).rem_euclid(r!([1 400] 10)); // FIXME: Something is possible to be done 381 | let _: Ranged<0, 399> = r!([-20 -2] -17).rem_euclid(r!([-400 -1] -10)); 382 | 383 | // Range checks, Tier 5/5: wide dividend 384 | let _: Ranged<-39, 39> = r!([-400 400] 17) % r!([1 40] 10); 385 | let _: Ranged<-39, 39> = r!([-400 400] 17) % r!([-40 -1] -10); 386 | let _: Ranged<-20, 39> = r!([-20 400] 17) % r!([1 40] 10); 387 | let _: Ranged<-20, 39> = r!([-20 400] 17) % r!([-40 -1] -10); 388 | let _: Ranged<-39, 20> = r!([-400 20] 17) % r!([1 40] 10); 389 | let _: Ranged<-39, 20> = r!([-400 20] 17) % r!([-40 -1] -10); 390 | let _: Ranged<-20, 20> = r!([-20 20] 17) % r!([1 40] 10); 391 | let _: Ranged<-20, 20> = r!([-20 20] 17) % r!([-40 -1] -10); 392 | 393 | let _: Ranged<0, 39> = r!([-400 400] 17).rem_euclid(r!([1 40] 10)); 394 | let _: Ranged<0, 39> = r!([-400 400] 17).rem_euclid(r!([-40 -1] -10)); 395 | let _: Ranged<0, 39> = r!([-20 400] 17).rem_euclid(r!([1 40] 10)); 396 | let _: Ranged<0, 39> = r!([-20 400] 17).rem_euclid(r!([-40 -1] -10)); 397 | let _: Ranged<0, 39> = r!([-400 20] 17).rem_euclid(r!([1 40] 10)); 398 | let _: Ranged<0, 39> = r!([-400 20] 17).rem_euclid(r!([-40 -1] -10)); 399 | let _: Ranged<0, 39> = r!([-20 20] 17).rem_euclid(r!([1 40] 10)); 400 | let _: Ranged<0, 39> = r!([-20 20] 17).rem_euclid(r!([-40 -1] -10)); 401 | } 402 | 403 | #[test] 404 | fn eq() { 405 | let a = r!(20); 406 | let b = r!(40); 407 | assert_eq!(a, a); 408 | assert_eq!(15 % a, 15 % b); 409 | assert!(10 % a + 15 % b == 15 % a + 10 % b); 410 | assert!(11 % a + 15 % b != 15 % a + 10 % b); 411 | assert!(a != b); 412 | 413 | assert!(r!(40) > r!(20)); 414 | assert!(r!(40) >= r!(20)); 415 | assert!(r!(10) < r!(20)); 416 | assert!(r!(10) <= r!(20)); 417 | 418 | assert!(r!(40) > 20); 419 | assert!(r!(40) >= 20); 420 | assert!(r!(10) < 20); 421 | assert!(r!(10) <= 20); 422 | 423 | assert!(40 > r!(20)); 424 | assert!(40 >= r!(20)); 425 | assert!(10 < r!(20)); 426 | assert!(10 <= r!(20)); 427 | } 428 | 429 | #[test] 430 | fn eqz() { 431 | let some_i32 = 4; 432 | let some_wrong_i32 = 8; 433 | assert!(Ranged::<0, 6>::new(some_i32) == Some(r!([] 4))); 434 | assert!(Ranged::<0, 6>::new(some_wrong_i32).is_none()); 435 | } 436 | 437 | #[test] 438 | fn convert() { 439 | assert_eq!(20, i8::from(r!([0 100] 20))); 440 | assert_eq!(20, u8::from(r!([0 100] 20))); 441 | assert_eq!(20, i16::from(r!([0 100] 20))); 442 | assert_eq!(20, u16::from(r!([0 100] 20))); 443 | assert_eq!(20, i32::from(r!([0 100] 20))); 444 | assert_eq!(20, u32::from(r!([0 100] 20))); 445 | assert_eq!(20, i64::from(r!([0 100] 20))); 446 | assert_eq!(20, u64::from(r!([0 100] 20))); 447 | assert_eq!(20, i128::from(r!([0 100] 20))); 448 | assert_eq!(20, isize::from(r!([0 100] 20))); 449 | assert_eq!(20, usize::from(r!([0 100] 20))); 450 | 451 | let x: Ranged<-128, 127> = 10_i8.as_ranged(); 452 | assert_eq!(r!(10), x); 453 | let x: Ranged<0, 255> = 10_u8.as_ranged(); 454 | assert_eq!(r!(10), x); 455 | let x: Ranged<-32768, 32767> = 10_i16.as_ranged(); 456 | assert_eq!(r!(10), x); 457 | let x: Ranged<0, 65535> = 10_u16.as_ranged(); 458 | assert_eq!(r!(10), x); 459 | let x: Ranged<-2_147_483_648, 2_147_483_647> = 10_i32.as_ranged(); 460 | assert_eq!(r!(10), x); 461 | let x: Ranged<0, 4_294_967_295> = 10_u32.as_ranged(); 462 | assert_eq!(r!(10), x); 463 | let x: Ranged<-9_223_372_036_854_775_808, 9_223_372_036_854_775_807> = 10_i64.as_ranged(); 464 | assert_eq!(r!(10), x); 465 | let x: Ranged<0, 18_446_744_073_709_551_615> = 10_u64.as_ranged(); 466 | assert_eq!(r!(10), x); 467 | let x: Ranged< 468 | -170_141_183_460_469_231_731_687_303_715_884_105_728, 469 | 170_141_183_460_469_231_731_687_303_715_884_105_727, 470 | > = 10_i128.as_ranged(); 471 | assert_eq!(r!(10), x); 472 | let x: Ranged<{usize::MIN as i128}, {usize::MAX as i128}> = 10_usize.as_ranged(); 473 | assert_eq!(r!(10), x); 474 | let x: Ranged<{isize::MIN as i128}, {isize::MAX as i128}> = 10_isize.as_ranged(); 475 | assert_eq!(r!(10), x); 476 | } 477 | 478 | #[test] 479 | fn expand() { 480 | let x = r!([0 100] 20); 481 | assert_eq!(x, r!(20)); 482 | 483 | let y: Ranged<{ -5 }, 200> = x.expand(); 484 | assert_eq!(y, r!(20)); 485 | 486 | let z: Ranged<{ -5 }, 200> = y; 487 | assert_eq!(z, r!(20)); 488 | 489 | let x_ex: Ranged<0, 600> = 10_u8.as_ranged().expand(); 490 | assert_eq!(x_ex, r!(10)); 491 | } 492 | 493 | #[test] 494 | fn minmax() { 495 | let x = r!([-100 100] 15); 496 | let y = r!([-20 20] 3); 497 | 498 | let min = x.min(y); 499 | let max = x.max(y); 500 | 501 | let _: Ranged<-100,20> = min; 502 | let _: Ranged<-20,100> = max; 503 | 504 | assert_eq!(min, r!(3)); 505 | assert_eq!(max, r!(15)); 506 | } 507 | 508 | #[test] 509 | fn abs() { 510 | let x: Ranged<0, 100> = r!([-100 100] 15).abs(); 511 | assert_eq!(x, r!(15)); 512 | let x: Ranged<0, 100> = r!([-100 100] -15).abs(); 513 | assert_eq!(x, r!(15)); 514 | let x: Ranged<50, 100> = r!([50 100] 75).abs(); 515 | assert_eq!(x, r!(75)); 516 | let x: Ranged<50, 100> = r!([-100 -50] -75).abs(); 517 | assert_eq!(x, r!(75)); 518 | } 519 | 520 | 521 | #[test] 522 | fn iter() { 523 | use core::fmt::Write; 524 | 525 | let mut s = String::new(); 526 | for r in crate::ConstInclusiveRange::<0, 9> { 527 | write!(&mut s, "{r} ").unwrap(); 528 | } 529 | assert_eq!(s, "0 1 2 3 4 5 6 7 8 9 "); 530 | 531 | assert_eq!(r!(0..10).into_iter().step_by(2).collect::>(), vec![r!([0 8] 0), r!([] 2), r!([] 4), r!([] 6), r!([] 8)]); 532 | assert_eq!(r!(0..11).into_iter().step_by(2).collect::>(), vec![r!([0 10] 0), r!([] 2), r!([] 4), r!([] 6), r!([] 8), r!([] 10)]); 533 | 534 | let mut fibonacci = [0; 10]; 535 | fibonacci[0] = 1; 536 | fibonacci[1] = 1; 537 | for i in r!(2..10) { 538 | fibonacci[i.expand()] = fibonacci[(i-r!(1)).expand()] + fibonacci[(i-r!(2)).expand()]; 539 | } 540 | assert_eq!(fibonacci, [1,1,2,3,5,8,13,21,34,55]); 541 | 542 | let mut fib234: [_; 3] = fibonacci[r!(2..5)]; 543 | assert_eq!(fib234, [2,3,5]); 544 | 545 | fib234[r!(1..3)] = [10,15]; 546 | assert_eq!(fib234, [2,10,15]); 547 | 548 | let rfib = &mut fib234; 549 | rfib[r!(0..2)] = [0,5]; 550 | assert_eq!(fib234, [0,5,15]); 551 | } 552 | 553 | 554 | #[test] 555 | fn fromstr() { 556 | let x = "42".parse::>().unwrap(); 557 | assert_eq!(x, 42); 558 | 559 | let x = "333".parse::>().ok(); 560 | assert_eq!(x, None); 561 | 562 | let x = "-42".parse::>().unwrap(); 563 | assert_eq!(x, -42); 564 | 565 | let x = "-333".parse::>().ok(); 566 | assert_eq!(x, None); 567 | 568 | let x = r!(16).to_string(); 569 | assert_eq!(x, "16"); 570 | } 571 | 572 | 573 | const fn rmatch_example(val: Ranged<1, 20>) -> &'static str { 574 | rmatch!{[1 20] val 575 | 1..=5 | 16..=20 => {"Fail"} 576 | 6..=15 => {"Success"} 577 | } 578 | } 579 | 580 | const fn rmatch_digit(val: Ranged<0, 9>) -> &'static str { 581 | rmatch!{[0 9] val 582 | 0 => {"Zero"} 583 | 1 => {"One"} 584 | 2 => {"Two"} 585 | 3 => {"Three"} 586 | 4 => {"Four"} 587 | 5 => {"Five"} 588 | 6 => {"Six"} 589 | 7 => {"Seven"} 590 | 8 => {"Eight"} 591 | 9 => {"Nine"} 592 | } 593 | } 594 | 595 | #[test] 596 | fn test_rmatch() { 597 | assert_eq!(rmatch_example(r!([] 5)), "Fail"); 598 | assert_eq!(rmatch_example(r!([] 10)), "Success"); 599 | assert_eq!(rmatch_example(r!([] 15)), "Success"); 600 | assert_eq!(rmatch_example(r!([] 20)), "Fail"); 601 | 602 | let all_digits = r!(0..=9).into_iter().map(rmatch_digit).collect::>().join(" "); 603 | assert_eq!(all_digits, "Zero One Two Three Four Five Six Seven Eight Nine"); 604 | } 605 | 606 | 607 | #[test] 608 | fn type_constraining_comparisons() { 609 | { 610 | let _fit = r!([10 50] 30).fit_less_than(r!([10 45] 40)); 611 | 612 | let a: Ranged<20, 100> = r!([] 30); 613 | let b: Ranged<10, 50> = r!([] 40); 614 | 615 | let x: Option> = a.fit_less_than(b); 616 | let y: Option> = b.fit_greater_than(a); 617 | 618 | assert_eq!(x.unwrap(), 30); 619 | assert_eq!(y.unwrap(), 40); 620 | } 621 | 622 | { 623 | let a: Ranged<20, 100> = r!([] 40); 624 | let b: Ranged<10, 50> = r!([] 30); 625 | 626 | let x: Option> = a.fit_less_than(b); 627 | let y: Option> = b.fit_greater_than(a); 628 | 629 | assert_eq!(x, None); 630 | assert_eq!(y, None); 631 | } 632 | 633 | { 634 | assert_eq!(r!([10 50] 39).fit_less_than( r!([0 45] 40) ), Some(r!([] 39))); 635 | assert_eq!(r!([10 50] 39).fit_less_than( r!([15 45] 40) ), Some(r!([] 39))); 636 | } 637 | } 638 | -------------------------------------------------------------------------------- /src/value_check.rs: -------------------------------------------------------------------------------- 1 | //! The module facilitates the compile-time value checking in `where` clauses. 2 | //! 3 | //! Usage example. Allow `myfunction` only for positive values of SOME_PARAM: 4 | //! ``` 5 | //! #![feature(generic_const_exprs)] 6 | //! #![feature(adt_const_params)] 7 | //! 8 | //! # use ranged_integers::value_check::*; 9 | //! 10 | //! const fn myfunction_allowed(some_param: i32) -> OperationPossibility { 11 | //! OperationPossibility::allow_if(some_param>0) 12 | //! } 13 | //! 14 | //! fn myfunction() 15 | //! where 16 | //! Assert<{myfunction_allowed(SOME_PARAM)}>: IsAllowed, 17 | //! { 18 | //! // code 19 | //! } 20 | //! ``` 21 | //! 22 | 23 | #[doc(hidden)] pub enum Assert {} 24 | #[doc(hidden)] pub trait IsAllowed {} 25 | impl IsAllowed for Assert<{OperationPossibility::Allowed}> {} 26 | 27 | // This enum is used instead of `bool` for better compile error handling 28 | #[doc(hidden)] 29 | #[derive(PartialEq, Eq, core::marker::ConstParamTy)] 30 | pub enum OperationPossibility { 31 | Forbidden, 32 | Allowed 33 | } 34 | 35 | impl OperationPossibility { 36 | #[must_use] 37 | #[doc(hidden)] 38 | pub const fn allow_if(cond: bool)->Self { 39 | if cond { Self::Allowed } else { Self::Forbidden } 40 | } 41 | } 42 | --------------------------------------------------------------------------------