use std::fmt;
pub fn partition_count(n: usize) -> Option<u64> {
let mut p = vec![0u64; n + 1];
p[0] = 1;
for i in 1..=n {
let mut sum: i128 = 0;
let mut k: i64 = 1;
loop {
let g_pos = (k * (3 * k - 1) / 2) as usize;
if g_pos > i {
break;
}
let sign: i128 = if k % 2 == 0 { -1 } else { 1 };
sum += sign * (p[i - g_pos] as i128);
let g_neg = (k * (3 * k + 1) / 2) as usize;
if g_neg <= i {
let sign_neg: i128 = if k % 2 == 0 { -1 } else { 1 };
sum += sign_neg * (p[i - g_neg] as i128);
}
k += 1;
}
p[i] = u64::try_from(sum).ok()?;
}
Some(p[n])
}
pub fn enumerate_partitions(n: usize) -> Vec<Vec<usize>> {
let mut result = Vec::new();
if n == 0 {
result.push(vec![]);
return result;
}
let mut parts: Vec<usize> = Vec::with_capacity(n);
let mut remaining = n;
parts.push(n);
remaining = 0;
struct Frame {
remaining: usize,
max_part: usize,
}
let mut stack: Vec<Frame> = vec![Frame {
remaining: n,
max_part: n,
}];
let _ = parts.pop();
let mut prefix: Vec<usize> = Vec::new();
#[derive(Debug)]
struct State {
remaining: usize,
max_part: usize,
}
let _ = stack.pop();
let mut state_stack: Vec<State> = vec![State {
remaining: n,
max_part: n,
}];
let mut prefix_lens: Vec<usize> = vec![0];
while let Some(state) = state_stack.pop() {
let plen = prefix_lens
.pop()
.expect("prefix_lens tracks state_stack");
prefix.truncate(plen);
if state.remaining == 0 {
result.push(prefix.clone());
continue;
}
let lo = 1usize;
let hi = state.max_part.min(state.remaining);
for part in lo..=hi {
prefix_lens.push(prefix.len());
state_stack.push(State {
remaining: state.remaining - part,
max_part: part,
});
}
let _ = state_stack
.drain(state_stack.len() - (hi - lo + 1)..)
.collect::<Vec<_>>();
let _ = prefix_lens.drain(prefix_lens.len() - (hi - lo + 1)..).collect::<Vec<_>>();
enumerate_parts_into(state.remaining, state.max_part, &mut prefix, &mut result);
}
result
}
fn enumerate_parts_into(
remaining: usize,
max_part: usize,
current: &mut Vec<usize>,
out: &mut Vec<Vec<usize>>,
) {
if remaining == 0 {
out.push(current.clone());
return;
}
let hi = max_part.min(remaining);
for part in (1..=hi).rev() {
current.push(part);
enumerate_parts_into(remaining - part, part, current, out);
current.pop();
}
}
pub fn restricted_partitions(n: usize, max_part: usize) -> Vec<Vec<usize>> {
let mut result = Vec::new();
let mut current = Vec::new();
enumerate_parts_into(n, max_part, &mut current, &mut result);
result
}
pub fn conjugate_partition(partition: &[usize]) -> Vec<usize> {
if partition.is_empty() {
return Vec::new();
}
let max_part = *partition.iter().max().expect("non-empty partition");
let mut conj = Vec::with_capacity(max_part);
for j in 1..=max_part {
let count = partition.iter().filter(|&&p| p >= j).count();
conj.push(count);
}
conj
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct YoungDiagram {
pub partition: Vec<usize>,
}
impl YoungDiagram {
pub fn new(mut partition: Vec<usize>) -> Self {
partition.retain(|&p| p > 0);
partition.sort_unstable_by(|a, b| b.cmp(a));
Self { partition }
}
pub fn rows(&self) -> usize {
self.partition.len()
}
pub fn columns(&self) -> usize {
self.partition.first().copied().unwrap_or(0)
}
pub fn size(&self) -> usize {
self.partition.iter().sum()
}
pub fn conjugate(&self) -> Self {
YoungDiagram {
partition: conjugate_partition(&self.partition),
}
}
pub fn ascii(&self) -> String {
let mut s = String::new();
for &row_len in &self.partition {
for _ in 0..row_len {
s.push_str("[]");
}
s.push('\n');
}
s
}
}
impl fmt::Display for YoungDiagram {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.ascii())
}
}
pub fn young_diagram(partition: &[usize]) -> String {
YoungDiagram::new(partition.to_vec()).ascii()
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_partition_count() {
let expected = [1u64, 1, 2, 3, 5, 7, 11, 15, 22, 30, 42];
for (n, &exp) in expected.iter().enumerate() {
assert_eq!(partition_count(n), Some(exp), "p({n}) should be {exp}");
}
}
#[test]
fn test_partition_count_large() {
assert_eq!(partition_count(100), Some(190569292));
}
#[test]
fn test_enumerate_partitions_0() {
let p = enumerate_partitions(0);
assert_eq!(p, vec![vec![]]);
}
#[test]
fn test_enumerate_partitions_4() {
let p = enumerate_partitions(4);
assert_eq!(p.len(), 5);
for part in &p {
for w in part.windows(2) {
assert!(w[0] >= w[1], "partition not non-increasing: {part:?}");
}
}
for part in &p {
assert_eq!(part.iter().sum::<usize>(), 4);
}
}
#[test]
fn test_enumerate_partitions_count_matches() {
for n in 0..=12 {
let cnt = partition_count(n).expect("count") as usize;
let enum_cnt = enumerate_partitions(n).len();
assert_eq!(cnt, enum_cnt, "mismatch at n={n}");
}
}
#[test]
fn test_restricted_partitions() {
let p = restricted_partitions(6, 3);
assert_eq!(p.len(), 7);
for part in &p {
assert!(
part.iter().all(|&x| x <= 3),
"part exceeds max: {part:?}"
);
assert_eq!(part.iter().sum::<usize>(), 6);
}
}
#[test]
fn test_conjugate_partition() {
assert_eq!(conjugate_partition(&[4, 2, 1]), vec![3, 2, 1, 1]);
assert_eq!(conjugate_partition(&[3, 2, 1]), vec![3, 2, 1]);
assert_eq!(conjugate_partition(&[3, 3, 2]), vec![3, 3, 2]);
let p = vec![5usize, 3, 1];
assert_eq!(conjugate_partition(&conjugate_partition(&p)), p);
}
#[test]
fn test_young_diagram() {
let s = young_diagram(&[3, 2, 1]);
let lines: Vec<&str> = s.trim_end().lines().collect();
assert_eq!(lines.len(), 3);
assert_eq!(lines[0], "[][][]");
assert_eq!(lines[1], "[][]");
assert_eq!(lines[2], "[]");
}
#[test]
fn test_young_diagram_struct() {
let d = YoungDiagram::new(vec![3, 2, 1]);
assert_eq!(d.rows(), 3);
assert_eq!(d.columns(), 3);
assert_eq!(d.size(), 6);
let conj = d.conjugate();
assert_eq!(conj.partition, vec![3, 2, 1]);
}
}