arithmetic_series/
lib.rs

1/// 初項 `a`, 項数 `n`, 公差 `d` の等差数列の和を求めます。
2///
3/// # Panics
4/// if `n` is negative.
5///
6/// # Examples
7/// ```
8/// use arithmetic_series::arithmetic_series;
9///
10/// // 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 10
11/// assert_eq!(arithmetic_series(1, 10, 1), Some(55));
12/// // 1 + 3 + 5 + 7 + 9
13/// assert_eq!(arithmetic_series(1, 5, 2), Some(25));
14/// // 5 + 2 + (-1) + (-4) + (-7) + (-10)
15/// assert_eq!(arithmetic_series(5, 6, -3), Some(-15));
16/// ```
17pub fn arithmetic_series<T: Int>(a: T, n: T, d: T) -> Option<T> {
18    if n == T::zero() {
19        return Some(T::zero());
20    }
21
22    assert!(n.is_positive());
23
24    let last = d.checked_mul(n.decrement())?.checked_add(a)?;
25    a.checked_add(last)?.checked_mul(n)?.checked_div(T::two())
26}
27
28pub trait Int: Copy + Ord {
29    fn is_positive(self) -> bool;
30    fn decrement(self) -> Self;
31    fn checked_add(self, rhs: Self) -> Option<Self>;
32    fn checked_mul(self, rhs: Self) -> Option<Self>;
33    fn checked_div(self, rhs: Self) -> Option<Self>;
34    fn zero() -> Self;
35    fn two() -> Self;
36}
37
38macro_rules! impl_int {
39    ($($t:ty),+) => {
40        $(
41            impl Int for $t {
42                fn is_positive(self) -> bool {
43                    self >= 1
44                }
45                fn decrement(self) -> Self {
46                    self - 1
47                }
48                fn checked_add(self, rhs: Self) -> Option<Self> {
49                    self.checked_add(rhs)
50                }
51                fn checked_mul(self, rhs: Self) -> Option<Self> {
52                    self.checked_mul(rhs)
53                }
54                fn checked_div(self, rhs: Self) -> Option<Self> {
55                    self.checked_div(rhs)
56                }
57                fn zero() -> Self {
58                    0
59                }
60                fn two() -> Self {
61                    2
62                }
63            }
64        )+
65    };
66}
67
68impl_int!(i32, i64, i128, u32, u64, u128, usize);
69
70#[cfg(test)]
71mod tests {
72    use crate::arithmetic_series;
73
74    #[test]
75    fn test_sum_of_1_2_3_to_10() {
76        assert_eq!(arithmetic_series(1, 10, 1), Some(55));
77    }
78
79    #[test]
80    fn test_single() {
81        assert_eq!(arithmetic_series(42, 1, 3), Some(42));
82    }
83
84    #[test]
85    fn test_decrease_sequence() {
86        assert_eq!(
87            arithmetic_series(8, 6, -3),
88            Some(8 + 5 + 2 + (-1) + (-4) + (-7))
89        );
90    }
91
92    #[test]
93    fn test_empty() {
94        assert_eq!(arithmetic_series(42, 0, 3), Some(0));
95    }
96
97    #[test]
98    fn test_too_large() {
99        assert_eq!(arithmetic_series(1, std::i64::MAX, 1), None);
100    }
101
102    #[test]
103    #[should_panic]
104    fn test_negative_length() {
105        arithmetic_series(42, -4, 3);
106    }
107}