Skip to main content

oxilean_std/combinatorics/
types.rs

1//! Auto-generated module
2//!
3//! 🤖 Generated with [SplitRS](https://github.com/cool-japan/splitrs)
4use super::functions::*;
5
6/// Verifies the symmetric Lovász Local Lemma condition.
7///
8/// Given n bad events, each with probability at most p, and each
9/// depending on at most d others, LLL guarantees a positive probability
10/// avoidance if p * (d + 1) ≤ 1 (symmetric version, ep(d+1) ≤ 1 in general).
11#[derive(Debug, Clone)]
12pub struct LovaszLocalLemma {
13    /// Number of bad events.
14    pub num_events: usize,
15    /// Upper bound on the probability of each event (as 1/denom).
16    pub prob_numerator: u64,
17    pub prob_denominator: u64,
18    /// Maximum dependency degree.
19    pub max_degree: usize,
20}
21impl LovaszLocalLemma {
22    /// Create a new LLL instance.
23    pub fn new(
24        num_events: usize,
25        prob_numerator: u64,
26        prob_denominator: u64,
27        max_degree: usize,
28    ) -> Self {
29        Self {
30            num_events,
31            prob_numerator,
32            prob_denominator,
33            max_degree,
34        }
35    }
36    /// Check the symmetric LLL condition: p * (d + 1) ≤ 1/4.
37    ///
38    /// Returns true if the condition holds (4 * p * (d+1) ≤ 1).
39    pub fn symmetric_condition_holds(&self) -> bool {
40        let lhs = 4u64
41            .saturating_mul(self.prob_numerator)
42            .saturating_mul((self.max_degree + 1) as u64);
43        lhs <= self.prob_denominator
44    }
45    /// Check the asymmetric LLL condition: p * e * (d + 1) ≤ 1.
46    ///
47    /// Uses e ≈ 271828/100000 ≈ 2.71828.
48    pub fn asymmetric_condition_holds(&self) -> bool {
49        let numerator = 271828u64
50            .saturating_mul(self.prob_numerator)
51            .saturating_mul((self.max_degree + 1) as u64);
52        let denominator = self.prob_denominator.saturating_mul(100000);
53        numerator <= denominator
54    }
55    /// Return the slack: how far the condition is from the boundary.
56    pub fn slack(&self) -> i64 {
57        let lhs = 4i64 * self.prob_numerator as i64 * (self.max_degree as i64 + 1);
58        self.prob_denominator as i64 - lhs
59    }
60}
61/// A polynomial over i64 stored as coefficient vector: `poly[i]` = coefficient of x^i.
62#[derive(Debug, Clone, PartialEq, Eq)]
63pub struct Poly(pub Vec<i64>);
64impl Poly {
65    /// Zero polynomial.
66    pub fn zero() -> Self {
67        Poly(vec![])
68    }
69    /// Constant polynomial.
70    pub fn constant(c: i64) -> Self {
71        if c == 0 {
72            Poly::zero()
73        } else {
74            Poly(vec![c])
75        }
76    }
77    /// Degree (returns 0 for the zero polynomial).
78    pub fn degree(&self) -> usize {
79        self.0.len().saturating_sub(1)
80    }
81    /// Evaluate at x using Horner's method.
82    pub fn eval(&self, x: i64) -> i64 {
83        self.0.iter().rev().fold(0i64, |acc, &c| acc * x + c)
84    }
85    /// Trim trailing zero coefficients.
86    pub fn trim(mut self) -> Self {
87        while self.0.last() == Some(&0) {
88            self.0.pop();
89        }
90        self
91    }
92    /// Add two polynomials.
93    pub fn add(&self, other: &Self) -> Self {
94        let len = self.0.len().max(other.0.len());
95        let mut result = vec![0i64; len];
96        for (i, &c) in self.0.iter().enumerate() {
97            result[i] += c;
98        }
99        for (i, &c) in other.0.iter().enumerate() {
100            result[i] += c;
101        }
102        Poly(result).trim()
103    }
104    /// Multiply two polynomials (naive O(n²)).
105    pub fn mul(&self, other: &Self) -> Self {
106        if self.0.is_empty() || other.0.is_empty() {
107            return Poly::zero();
108        }
109        let len = self.0.len() + other.0.len() - 1;
110        let mut result = vec![0i64; len];
111        for (i, &a) in self.0.iter().enumerate() {
112            for (j, &b) in other.0.iter().enumerate() {
113                result[i + j] += a * b;
114            }
115        }
116        Poly(result).trim()
117    }
118    /// Compute the first `n+1` terms of the OGF product (Cauchy convolution).
119    pub fn conv_truncated(a: &[i64], b: &[i64], n: usize) -> Vec<i64> {
120        let mut c = vec![0i64; n + 1];
121        for i in 0..=n {
122            for j in 0..=i {
123                if j < a.len() && (i - j) < b.len() {
124                    c[i] += a[j] * b[i - j];
125                }
126            }
127        }
128        c
129    }
130}
131/// Computes Turán densities and exact Turán numbers for small graphs.
132#[derive(Debug, Clone)]
133pub struct TuranDensityComputer {
134    /// The forbidden graph (number of vertices in K_{r+1}).
135    pub forbidden_clique: usize,
136    /// Precomputed upper bounds for various n.
137    pub bounds: Vec<(usize, u64)>,
138}
139impl TuranDensityComputer {
140    /// Create a Turán density computer forbidding K_{r+1}.
141    pub fn new(r: usize) -> Self {
142        Self {
143            forbidden_clique: r + 1,
144            bounds: Vec::new(),
145        }
146    }
147    /// Compute ex(n, K_{r+1}) = number of edges in the Turán graph T(n,r).
148    ///
149    /// T(n,r) has n vertices partitioned into r equal parts with all edges between parts.
150    /// |E(T(n,r))| = (r-1)/(2r) * n² (asymptotically).
151    pub fn turan_edges(&self, n: usize) -> u64 {
152        let r = self.forbidden_clique - 1;
153        if r == 0 {
154            return 0;
155        }
156        let q = n / r;
157        let rem = n % r;
158        let total_edges = (n as u64 * (n as u64 - 1)) / 2;
159        let intra: u64 = (0..r)
160            .map(|i| {
161                let part_size = if i < rem { q + 1 } else { q } as u64;
162                part_size * (part_size - 1) / 2
163            })
164            .sum();
165        total_edges - intra
166    }
167    /// Compute the Turán density π(K_{r+1}) = (r-1)/r.
168    ///
169    /// Returns the numerator and denominator.
170    pub fn density(&self) -> (usize, usize) {
171        let r = self.forbidden_clique - 1;
172        if r <= 1 {
173            return (0, 1);
174        }
175        let g = gcd_usize(r - 1, r);
176        ((r - 1) / g, r / g)
177    }
178    /// Cache a precomputed bound ex(n, K_{r+1}) = bound.
179    pub fn cache_bound(&mut self, n: usize, bound: u64) {
180        self.bounds.push((n, bound));
181    }
182}
183/// Solves the matroid intersection problem for two partition matroids.
184///
185/// A partition matroid is defined by a partition of the ground set E into
186/// blocks B_1, …, B_k, with rank bounds r_1, …, r_k.
187#[derive(Debug, Clone)]
188pub struct MatroidIntersectionSolver {
189    /// Ground set size |E|.
190    pub ground_set_size: usize,
191    /// Block assignments: block[i] = block index for element i.
192    pub block_assignment_1: Vec<usize>,
193    /// Rank bounds for matroid 1 (indexed by block).
194    pub rank_bounds_1: Vec<usize>,
195    /// Block assignments for matroid 2.
196    pub block_assignment_2: Vec<usize>,
197    /// Rank bounds for matroid 2.
198    pub rank_bounds_2: Vec<usize>,
199}
200impl MatroidIntersectionSolver {
201    /// Create a new solver for two partition matroids on ground set {0..n-1}.
202    pub fn new(
203        n: usize,
204        block_assignment_1: Vec<usize>,
205        rank_bounds_1: Vec<usize>,
206        block_assignment_2: Vec<usize>,
207        rank_bounds_2: Vec<usize>,
208    ) -> Self {
209        Self {
210            ground_set_size: n,
211            block_assignment_1,
212            rank_bounds_1,
213            block_assignment_2,
214            rank_bounds_2,
215        }
216    }
217    /// Check if a set (represented as a bitmask) is independent in matroid 1.
218    pub fn is_independent_1(&self, mask: u64) -> bool {
219        let mut counts = vec![0usize; self.rank_bounds_1.len()];
220        for i in 0..self.ground_set_size {
221            if mask & (1 << i) != 0 {
222                let b = self.block_assignment_1[i];
223                if b < counts.len() {
224                    counts[b] += 1;
225                    if counts[b] > self.rank_bounds_1[b] {
226                        return false;
227                    }
228                }
229            }
230        }
231        true
232    }
233    /// Check if a set is independent in matroid 2.
234    pub fn is_independent_2(&self, mask: u64) -> bool {
235        let mut counts = vec![0usize; self.rank_bounds_2.len()];
236        for i in 0..self.ground_set_size {
237            if mask & (1 << i) != 0 {
238                let b = self.block_assignment_2[i];
239                if b < counts.len() {
240                    counts[b] += 1;
241                    if counts[b] > self.rank_bounds_2[b] {
242                        return false;
243                    }
244                }
245            }
246        }
247        true
248    }
249    /// Find the maximum common independent set by brute force (for small |E|).
250    ///
251    /// Returns (max_size, mask) where mask encodes the optimal set.
252    pub fn solve_brute_force(&self) -> (usize, u64) {
253        let n = self.ground_set_size.min(20);
254        let mut best_size = 0;
255        let mut best_mask = 0u64;
256        for mask in 0u64..(1 << n) {
257            if self.is_independent_1(mask) && self.is_independent_2(mask) {
258                let size = mask.count_ones() as usize;
259                if size > best_size {
260                    best_size = size;
261                    best_mask = mask;
262                }
263            }
264        }
265        (best_size, best_mask)
266    }
267}
268/// Represents the cycle index polynomial Z(G; x₁, …, xₙ) of a permutation group.
269///
270/// The cycle index is stored as a list of monomials, where each monomial
271/// is represented as a vector of exponents (e[i] = exponent of x_{i+1}).
272#[derive(Debug, Clone)]
273pub struct CycleIndexPolynomial {
274    /// Group name (for display).
275    pub group_name: String,
276    /// Each term: (coefficient, exponent_vector).
277    /// The polynomial is (1/|G|) * ∑ coeff * ∏ x_i^{e_i}.
278    pub terms: Vec<(u64, Vec<u32>)>,
279    /// Order of the group |G|.
280    pub group_order: u64,
281}
282impl CycleIndexPolynomial {
283    /// Create a new cycle index for a named group.
284    pub fn new(group_name: impl Into<String>, group_order: u64) -> Self {
285        Self {
286            group_name: group_name.into(),
287            terms: Vec::new(),
288            group_order,
289        }
290    }
291    /// Add a term to the cycle index.
292    pub fn add_term(&mut self, coeff: u64, exponents: Vec<u32>) {
293        self.terms.push((coeff, exponents));
294    }
295    /// Evaluate Z(G; 1, 1, …, 1) = |G| / |G| = 1 (should return 1).
296    pub fn eval_all_ones(&self) -> u64 {
297        let total: u64 = self.terms.iter().map(|(c, _)| c).sum();
298        total / self.group_order.max(1)
299    }
300    /// Apply Burnside's lemma: the number of distinct colorings with k colors
301    /// is Z(G; k, k, …, k).
302    ///
303    /// Each term `(coeff, exps)` contributes `coeff * ∏_i k^{exps[i]}` = `coeff * k^{∑ exps[i]}`.
304    pub fn burnside_count(&self, k: u64) -> u64 {
305        let total: u64 = self
306            .terms
307            .iter()
308            .map(|(coeff, exps)| {
309                let sum_exps: u32 = exps.iter().sum();
310                let product = k.pow(sum_exps);
311                coeff * product
312            })
313            .sum();
314        total / self.group_order.max(1)
315    }
316    /// The cycle index for C_n (cyclic group of order n).
317    pub fn cyclic(n: u32) -> Self {
318        let mut cip = Self::new(format!("C_{}", n), n as u64);
319        for d in 1..=(n as u64) {
320            if n as u64 % d == 0 {
321                let phi_d = euler_phi(d);
322                let exp_pos = (d as usize).saturating_sub(1);
323                let count = n as u64 / d;
324                let mut exps = vec![0u32; n as usize];
325                if exp_pos < exps.len() {
326                    exps[exp_pos] = count as u32;
327                }
328                cip.add_term(phi_d, exps);
329            }
330        }
331        cip
332    }
333}