use_arithmetic/
division.rs1#[must_use]
19pub fn checked_div_floor(dividend: i64, divisor: i64) -> Option<i64> {
20 let quotient = floor_quotient(dividend, divisor)?;
21
22 i64::try_from(quotient).ok()
23}
24
25#[must_use]
40pub fn checked_div_ceil(dividend: i64, divisor: i64) -> Option<i64> {
41 let quotient = ceil_quotient(dividend, divisor)?;
42
43 i64::try_from(quotient).ok()
44}
45
46#[must_use]
63pub fn checked_mod_floor(dividend: i64, divisor: i64) -> Option<i64> {
64 let quotient = i128::from(checked_div_floor(dividend, divisor)?);
65 let remainder = i128::from(dividend) - (i128::from(divisor) * quotient);
66
67 i64::try_from(remainder).ok()
68}
69
70#[must_use]
77pub fn div_floor(dividend: i64, divisor: i64) -> i64 {
78 checked_div_floor(dividend, divisor)
79 .unwrap_or_else(|| panic!("div_floor requires a non-zero divisor and an in-range quotient"))
80}
81
82#[must_use]
89pub fn div_ceil(dividend: i64, divisor: i64) -> i64 {
90 checked_div_ceil(dividend, divisor)
91 .unwrap_or_else(|| panic!("div_ceil requires a non-zero divisor and an in-range quotient"))
92}
93
94#[must_use]
101pub fn mod_floor(dividend: i64, divisor: i64) -> i64 {
102 checked_mod_floor(dividend, divisor)
103 .unwrap_or_else(|| panic!("mod_floor requires a non-zero divisor and an in-range quotient"))
104}
105
106fn floor_quotient(dividend: i64, divisor: i64) -> Option<i128> {
107 if divisor == 0 {
108 return None;
109 }
110
111 let dividend = i128::from(dividend);
112 let divisor = i128::from(divisor);
113 let quotient = dividend / divisor;
114 let remainder = dividend % divisor;
115
116 if remainder != 0 && ((remainder > 0) != (divisor > 0)) {
117 Some(quotient - 1)
118 } else {
119 Some(quotient)
120 }
121}
122
123fn ceil_quotient(dividend: i64, divisor: i64) -> Option<i128> {
124 if divisor == 0 {
125 return None;
126 }
127
128 let dividend = i128::from(dividend);
129 let divisor = i128::from(divisor);
130 let quotient = dividend / divisor;
131 let remainder = dividend % divisor;
132
133 if remainder != 0 && ((remainder > 0) == (divisor > 0)) {
134 Some(quotient + 1)
135 } else {
136 Some(quotient)
137 }
138}
139
140#[cfg(test)]
141mod tests {
142 use super::{
143 checked_div_ceil, checked_div_floor, checked_mod_floor, div_ceil, div_floor, mod_floor,
144 };
145
146 #[test]
147 fn matches_floor_semantics_for_negative_values() {
148 assert_eq!(checked_div_floor(-7, 3), Some(-3));
149 assert_eq!(checked_div_floor(7, -3), Some(-3));
150 assert_eq!(checked_div_floor(-7, -3), Some(2));
151 assert_eq!(checked_div_ceil(-7, 3), Some(-2));
152 assert_eq!(checked_div_ceil(7, -3), Some(-2));
153 assert_eq!(checked_mod_floor(-7, 3), Some(2));
154 assert_eq!(checked_mod_floor(7, -3), Some(-2));
155 assert_eq!(div_floor(-7, 3), -3);
156 assert_eq!(div_ceil(-7, 3), -2);
157 assert_eq!(mod_floor(-7, 3), 2);
158 }
159
160 #[test]
161 fn rejects_zero_divisors_and_overflowing_quotients() {
162 assert_eq!(checked_div_floor(7, 0), None);
163 assert_eq!(checked_div_ceil(7, 0), None);
164 assert_eq!(checked_mod_floor(7, 0), None);
165 assert_eq!(checked_div_floor(i64::MIN, -1), None);
166 assert_eq!(checked_div_ceil(i64::MIN, -1), None);
167 assert_eq!(checked_mod_floor(i64::MIN, -1), None);
168 }
169
170 #[test]
171 #[should_panic(expected = "div_floor requires a non-zero divisor and an in-range quotient")]
172 fn plain_div_floor_panics_on_zero_divisor() {
173 let _ = div_floor(7, 0);
174 }
175
176 #[test]
177 #[should_panic(expected = "div_ceil requires a non-zero divisor and an in-range quotient")]
178 fn plain_div_ceil_panics_on_zero_divisor() {
179 let _ = div_ceil(7, 0);
180 }
181
182 #[test]
183 #[should_panic(expected = "mod_floor requires a non-zero divisor and an in-range quotient")]
184 fn plain_mod_floor_panics_on_zero_divisor() {
185 let _ = mod_floor(7, 0);
186 }
187}