1use super::poly::{
4 Poly,
5 coef::Coef,
6 mono::{Mono, Pow, Var},
7};
8
9#[derive(Clone, PartialEq, Eq, Hash, Debug)]
52pub struct Count {
53 num: Poly,
54 den: Coef,
55}
56
57impl Count {
58 pub fn zero() -> Self {
59 Self {
60 num: Poly::zero(),
61 den: 1.into(),
62 }
63 }
64
65 pub fn constant<T: Into<Coef>>(c: T) -> Self {
66 Self {
67 num: Poly::constant(c),
68 den: 1.into(),
69 }
70 }
71
72 pub fn var<T: Into<Var>>(v: T, p: Pow) -> Self {
73 Self {
74 num: Poly::var(v, p),
75 den: 1.into(),
76 }
77 }
78
79 pub fn ratio<T: Into<Coef>>(num: Poly, den: T) -> Self {
80 let den = den.into();
81 assert!(!den.is_zero());
82
83 let (num, den) = if !den.is_positive() {
84 (num.mul_scalar(-1), -den)
85 } else {
86 (num, den)
87 };
88 let g = num.coef_gcd().gcd(&den);
89 let num = num.divide_coefs(g).unwrap();
90
91 Self { num, den: den / g }.debug_checked()
92 }
93
94 pub fn num(&self) -> &Poly {
95 &self.num
96 }
97
98 pub fn den(&self) -> Coef {
99 self.den
100 }
101
102 pub fn is_zero(&self) -> bool {
103 self.num.is_zero()
104 }
105
106 pub fn as_poly(&self) -> Option<&Poly> {
107 (self.den == 1.into()).then_some(&self.num)
108 }
109
110 pub fn as_constant(&self) -> Option<(Coef, Coef)> {
111 self.num.as_constant().map(|c| (c, self.den))
112 }
113
114 pub fn add(&self, rhs: &Self) -> Self {
115 let g = self.den.gcd(&rhs.den);
116 let lcm = self.den / g * rhs.den;
117 let num = self
118 .num
119 .mul_scalar(lcm / self.den)
120 .add(&rhs.num.mul_scalar(lcm / rhs.den));
121 Self::ratio(num, lcm).debug_checked()
122 }
123
124 pub fn neg(&self) -> Self {
125 Self {
126 num: self.num.neg(),
127 den: self.den,
128 }
129 .debug_checked()
130 }
131
132 pub fn sub(&self, rhs: &Self) -> Self {
133 self.add(&rhs.neg())
134 }
135
136 pub fn mul(&self, rhs: &Self) -> Self {
137 Self::ratio(self.num.mul(&rhs.num), self.den * rhs.den)
138 }
139
140 pub fn mul_scalar<T: Into<Coef>>(&self, c: T) -> Self {
141 Self::ratio(self.num.mul_scalar(c), self.den)
142 }
143
144 pub fn div_scalar<T: Into<Coef>>(&self, d: T) -> Self {
145 Self::ratio(self.num.clone(), self.den * d.into())
146 }
147
148 pub fn eval(&self, point: impl FnMut(Var) -> i128) -> (Coef, Coef) {
149 let n = self.num.eval(point);
150 let g = n.gcd(&self.den);
151 (n / g, self.den / g)
152 }
153
154 pub fn eval_int(&self, point: impl FnMut(Var) -> i128) -> Option<Coef> {
155 let (num, den) = self.eval(point);
156 if den == 1.into() { Some(num) } else { None }
157 }
158
159 pub fn substitute(&self, v: Var, q: &Poly) -> Self {
160 Self::ratio(self.num.substitute(v, q), self.den)
161 }
162
163 pub fn assert_canonical(&self) {
164 self.num.assert_canonical();
165 assert!(
166 self.den.is_positive(),
167 "denominator {:?} not positive",
168 self.den
169 );
170 assert_eq!(
171 self.num.coef_gcd().gcd(&self.den),
172 1.into(),
173 "fraction not reduced: content {:?} vs den {:?}",
174 self.num.coef_gcd(),
175 self.den
176 );
177 }
178
179 fn debug_checked(self) -> Self {
180 #[cfg(debug_assertions)]
181 self.assert_canonical();
182 self
183 }
184
185 pub fn sum_below<T: Into<Var>>(&self, v: T, hi: &Poly) -> Self {
186 let v = v.into();
187 assert_eq!(hi.degree_in(v), 0);
188 if self.is_zero() {
189 return Self::zero();
190 }
191
192 let kmax = self.num().degree_in(v) as usize;
194
195 let mut groups: Vec<Vec<(Coef, Mono)>> = vec![Vec::new(); kmax + 1];
196 for (c, m) in self.num().terms().iter().rev() {
197 let k = m.degree_in(v) as usize;
198 let rest = Mono::new(m.exps().iter().copied().filter(|&(vi, _)| vi != v));
199 groups[k].push((*c, rest));
200 }
201
202 let mut falling_factorials: Vec<Poly> = Vec::with_capacity(kmax + 1);
203 falling_factorials.push(hi.clone());
204 for j in 1..=kmax {
205 let factor = hi.sub(&Poly::constant(Coef::from_size(j)));
206 falling_factorials.push(falling_factorials[j - 1].mul(&factor));
207 }
208
209 let s2 = stirling2(kmax);
210 let mut total = Self::zero();
211 for (k, group) in groups.into_iter().enumerate() {
212 let c_k = Poly::from_terms(group);
213 if c_k.is_zero() {
214 continue;
215 }
216
217 let mut a_k = Self::zero();
218 for (j, &s) in s2[k].iter().enumerate() {
219 if !s.is_zero() {
220 a_k = a_k.add(&Self::ratio(
221 falling_factorials[j].mul_scalar(s),
222 Coef::from_size(j + 1),
223 ));
224 }
225 }
226 total = total.add(&Self::ratio(c_k, 1).mul(&a_k));
227 }
228 total.div_scalar(self.den())
229 }
230
231 pub fn sum_range(&self, var: Var, lo: &Poly, hi: &Poly) -> Self {
232 self.sum_below(var, hi).sub(&self.sum_below(var, lo))
233 }
234}
235
236fn stirling2(kmax: usize) -> Vec<Vec<Coef>> {
237 let mut s2: Vec<Vec<Coef>> = Vec::with_capacity(kmax + 1);
238 s2.push(vec![Coef::new(1)]);
239 for k in 1..=kmax {
240 let mut row = Vec::with_capacity(k + 1);
241 row.push(Coef::new(0));
242 for j in 1..=k {
243 let above = if j < k { s2[k - 1][j] } else { Coef::new(0) };
244 let diag = s2[k - 1][j - 1];
245 row.push((Coef::from_size(j) * above) + diag);
246 }
247 s2.push(row);
248 }
249 s2
250}
251
252#[cfg(test)]
253mod tests {
254 use super::{Count, Var, stirling2};
255 use crate::solver::poly::{coef::Coef, poly};
256
257 #[test]
258 fn ratio_reduce() {
259 let half_n = Count::ratio(poly!(2 * x), 4);
260 assert_eq!(half_n.num(), &poly!(x));
261 assert_eq!(half_n.den(), 2.into());
262 assert_eq!(
263 Count::ratio(poly!(2 * x + 2), 2),
264 Count::ratio(poly!(x + 1), 1)
265 );
266 assert_eq!(
267 Count::ratio(poly!(2 * x), -1),
268 Count::ratio(poly!(-2 * x), 1)
269 );
270 }
271
272 #[test]
273 fn arithmetic_sanity() {
274 let (a, b) = (Count::ratio(poly!(x), 2), Count::ratio(poly!(x), 3));
276 assert_eq!(a.add(&b), Count::ratio(poly!(5 * x), 6));
277 assert_eq!(a.sub(&a), Count::ratio(poly!(), 1))
278 }
279
280 #[test]
281 fn stirling_triangle() {
282 assert_eq!(
284 stirling2(5),
285 vec![
286 Coef::vec(&[1]),
287 Coef::vec(&[0, 1]),
288 Coef::vec(&[0, 1, 1]),
289 Coef::vec(&[0, 1, 3, 1]),
290 Coef::vec(&[0, 1, 7, 6, 1]),
291 Coef::vec(&[0, 1, 15, 25, 10, 1]),
292 ]
293 );
294 }
295
296 #[test]
297 fn constant_summand() {
298 let one = Count::ratio(poly!(1), 1);
300 assert_eq!(one.sum_below('i', &poly!(b)), Count::ratio(poly!(b), 1));
301
302 let three = Count::ratio(poly!(3), 1);
304 assert_eq!(
305 three.sum_range('i' as Var, &poly!(2), &poly!(b)),
306 Count::ratio(poly!(3 * b - 6), 1)
307 );
308 }
309
310 #[test]
311 fn faulhaber() {
312 let cube = Count::ratio(poly!(i ^ 3), 1);
314 let s = cube.sum_below('i', &poly!(b));
315 let b2 = poly!(b ^ 2);
316 let bm1 = poly!(b - 1);
317 assert_eq!(s, Count::ratio(b2.mul(&bm1.pow(2)), 4));
318
319 let triangle = Count::ratio(poly!(i), 1).sum_below('i', &poly!(b));
321 assert_eq!(triangle.mul(&triangle), s);
322 }
323}