use_series/
progression.rs1use crate::error::SeriesError;
2
3pub const fn arithmetic_nth_term(first: i128, step: i128, index: u64) -> Result<i128, SeriesError> {
19 let Some(offset) = step.checked_mul(index as i128) else {
20 return Err(SeriesError::Overflow {
21 operation: "arithmetic_nth_term",
22 });
23 };
24
25 match first.checked_add(offset) {
26 Some(value) => Ok(value),
27 None => Err(SeriesError::Overflow {
28 operation: "arithmetic_nth_term",
29 }),
30 }
31}
32
33pub const fn arithmetic_sum(first: i128, step: i128, terms: u64) -> Result<i128, SeriesError> {
49 let mut sum = 0_i128;
50 let mut index = 0_u64;
51
52 while index < terms {
53 let Ok(term) = arithmetic_nth_term(first, step, index) else {
54 return Err(SeriesError::Overflow {
55 operation: "arithmetic_sum",
56 });
57 };
58
59 sum = match sum.checked_add(term) {
60 Some(value) => value,
61 None => {
62 return Err(SeriesError::Overflow {
63 operation: "arithmetic_sum",
64 });
65 },
66 };
67
68 index += 1;
69 }
70
71 Ok(sum)
72}
73
74pub const fn geometric_nth_term(first: i128, ratio: i128, index: u64) -> Result<i128, SeriesError> {
90 let mut value = first;
91 let mut exponent = 0_u64;
92
93 while exponent < index {
94 value = match value.checked_mul(ratio) {
95 Some(next) => next,
96 None => {
97 return Err(SeriesError::Overflow {
98 operation: "geometric_nth_term",
99 });
100 },
101 };
102
103 exponent += 1;
104 }
105
106 Ok(value)
107}
108
109pub const fn geometric_sum(first: i128, ratio: i128, terms: u64) -> Result<i128, SeriesError> {
125 let mut sum = 0_i128;
126 let mut term = first;
127 let mut index = 0_u64;
128
129 while index < terms {
130 sum = match sum.checked_add(term) {
131 Some(value) => value,
132 None => {
133 return Err(SeriesError::Overflow {
134 operation: "geometric_sum",
135 });
136 },
137 };
138
139 term = match term.checked_mul(ratio) {
140 Some(next) => next,
141 None if index + 1 == terms => term,
142 None => {
143 return Err(SeriesError::Overflow {
144 operation: "geometric_sum",
145 });
146 },
147 };
148
149 index += 1;
150 }
151
152 Ok(sum)
153}
154
155#[cfg(test)]
156mod tests {
157 use super::{arithmetic_nth_term, arithmetic_sum, geometric_nth_term, geometric_sum};
158 use crate::SeriesError;
159
160 #[test]
161 fn computes_arithmetic_progressions() {
162 assert_eq!(arithmetic_nth_term(3, 2, 4), Ok(11));
163 assert_eq!(arithmetic_sum(3, 2, 5), Ok(35));
164 assert_eq!(arithmetic_sum(10, -3, 4), Ok(22));
165 assert_eq!(arithmetic_sum(8, 5, 0), Ok(0));
166 }
167
168 #[test]
169 fn computes_geometric_progressions() {
170 assert_eq!(geometric_nth_term(2, 3, 4), Ok(162));
171 assert_eq!(geometric_sum(2, 3, 4), Ok(80));
172 assert_eq!(geometric_sum(5, 1, 4), Ok(20));
173 assert_eq!(geometric_sum(4, -2, 4), Ok(-20));
174 assert_eq!(geometric_sum(7, 3, 0), Ok(0));
175 }
176
177 #[test]
178 fn reports_overflow() {
179 assert!(matches!(
180 arithmetic_nth_term(i128::MAX, 1, 1),
181 Err(SeriesError::Overflow {
182 operation: "arithmetic_nth_term"
183 })
184 ));
185 assert!(matches!(
186 arithmetic_sum(i128::MAX, 1, 2),
187 Err(SeriesError::Overflow {
188 operation: "arithmetic_sum"
189 })
190 ));
191 assert!(matches!(
192 geometric_nth_term(i128::MAX, 2, 1),
193 Err(SeriesError::Overflow {
194 operation: "geometric_nth_term"
195 })
196 ));
197 assert!(matches!(
198 geometric_sum(i128::MAX, 2, 2),
199 Err(SeriesError::Overflow {
200 operation: "geometric_sum"
201 })
202 ));
203 }
204}