spl_math/checked_ceil_div.rs
1//! Defines performing checked ceiling division for different types
2
3use crate::uint::U256;
4
5/// Perform a division that does not truncate value from either side, returning
6/// the (quotient, divisor) as a tuple
7///
8/// When dividing integers, we are often left with a remainder, which can
9/// cause information to be lost. By checking for a remainder, adjusting
10/// the quotient, and recalculating the divisor, this provides the most fair
11/// calculation.
12///
13/// For example, 400 / 32 = 12, with a remainder cutting off 0.5 of amount.
14/// If we simply ceiling the quotient to 13, then we're saying 400 / 32 = 13,
15/// which also cuts off value. To improve this result, we calculate the other
16/// way around and again check for a remainder: 400 / 13 = 30, with a remainder
17/// of 0.77, and we ceiling that value again. This gives us a final calculation
18/// of 400 / 31 = 13, which provides a ceiling calculation without cutting off
19/// more value than needed.
20///
21/// This calculation fails if the divisor is larger than the dividend, to avoid
22/// having a result like: 1 / 1000 = 1.
23pub trait CheckedCeilDiv: Sized {
24 /// Perform ceiling division
25 fn checked_ceil_div(&self, rhs: Self) -> Option<(Self, Self)>;
26}
27
28impl CheckedCeilDiv for u128 {
29 fn checked_ceil_div(&self, mut rhs: Self) -> Option<(Self, Self)> {
30 let mut quotient = self.checked_div(rhs)?;
31 // Avoid dividing a small number by a big one and returning 1, and instead
32 // fail.
33 if quotient == 0 {
34 return None;
35 }
36
37 // Ceiling the destination amount if there's any remainder, which will
38 // almost always be the case.
39 let remainder = self.checked_rem(rhs)?;
40 if remainder > 0 {
41 quotient = quotient.checked_add(1)?;
42 // calculate the minimum amount needed to get the dividend amount to
43 // avoid truncating too much
44 rhs = self.checked_div(quotient)?;
45 let remainder = self.checked_rem(quotient)?;
46 if remainder > 0 {
47 rhs = rhs.checked_add(1)?;
48 }
49 }
50 Some((quotient, rhs))
51 }
52}
53
54impl CheckedCeilDiv for U256 {
55 fn checked_ceil_div(&self, mut rhs: Self) -> Option<(Self, Self)> {
56 let mut quotient = self.checked_div(rhs)?;
57 let zero = U256::from(0);
58 let one = U256::from(1);
59 // Avoid dividing a small number by a big one and returning 1, and instead
60 // fail.
61 if quotient == zero {
62 return None;
63 }
64
65 // Ceiling the destination amount if there's any remainder, which will
66 // almost always be the case.
67 let remainder = self.checked_rem(rhs)?;
68 if remainder > zero {
69 quotient = quotient.checked_add(one)?;
70 // calculate the minimum amount needed to get the dividend amount to
71 // avoid truncating too much
72 rhs = self.checked_div(quotient)?;
73 let remainder = self.checked_rem(quotient)?;
74 if remainder > zero {
75 rhs = rhs.checked_add(one)?;
76 }
77 }
78 Some((quotient, rhs))
79 }
80}