1use pumpkin_solver::constraints;
17use pumpkin_solver::results::ProblemSolution;
18use pumpkin_solver::results::SatisfactionResult;
19use pumpkin_solver::termination::Indefinite;
20use pumpkin_solver::variables::DomainId;
21use pumpkin_solver::Solver;
22
23struct Bibd {
24 rows: u32,
26 columns: u32,
28 row_sum: u32,
30 column_sum: u32,
32 max_dot_product: u32,
34}
35
36impl Bibd {
37 fn from_args() -> Option<Bibd> {
38 let args = std::env::args()
39 .skip(1)
40 .map(|arg| arg.parse::<u32>())
41 .collect::<Result<Vec<u32>, _>>()
42 .ok()?;
43
44 if args.len() != 3 {
45 return None;
46 }
47
48 let v = args[0];
49 let k = args[1];
50 let l = args[2];
51
52 let r = l * (v - 1) / (k - 1);
53 let b = v * r / k;
54
55 Some(Self {
56 rows: v,
57 columns: b,
58 row_sum: r,
59 column_sum: k,
60 max_dot_product: l,
61 })
62 }
63}
64
65fn create_matrix(solver: &mut Solver, bibd: &Bibd) -> Vec<Vec<DomainId>> {
66 (0..bibd.rows)
67 .map(|_| {
68 (0..bibd.columns)
69 .map(|_| solver.new_bounded_integer(0, 1))
70 .collect::<Vec<_>>()
71 })
72 .collect::<Vec<_>>()
73}
74
75fn main() {
76 env_logger::init();
77
78 let Some(bibd) = Bibd::from_args() else {
79 eprintln!("Usage: {} <v> <k> <l>", std::env::args().next().unwrap());
80 return;
81 };
82
83 println!(
84 "bibd: (v = {}, b = {}, r = {}, k = {}, l = {})",
85 bibd.rows, bibd.columns, bibd.row_sum, bibd.column_sum, bibd.max_dot_product
86 );
87
88 let mut solver = Solver::default();
89
90 let constraint_tag = solver.new_constraint_tag();
93
94 let matrix = create_matrix(&mut solver, &bibd);
96
97 for row in matrix.iter() {
99 let _ = solver
100 .add_constraint(constraints::equals(
101 row.clone(),
102 bibd.row_sum as i32,
103 constraint_tag,
104 ))
105 .post();
106 }
107
108 for row in transpose(&matrix) {
110 let _ = solver
111 .add_constraint(constraints::equals(
112 row,
113 bibd.column_sum as i32,
114 constraint_tag,
115 ))
116 .post();
117 }
118
119 let pairwise_product = (0..bibd.rows)
122 .map(|_| create_matrix(&mut solver, &bibd))
123 .collect::<Vec<_>>();
124
125 for r1 in 0..bibd.rows as usize {
126 for r2 in r1 + 1..bibd.rows as usize {
127 for col in 0..bibd.columns as usize {
128 let _ = solver
129 .add_constraint(constraints::times(
130 matrix[r1][col],
131 matrix[r2][col],
132 pairwise_product[r1][r2][col],
133 constraint_tag,
134 ))
135 .post();
136 }
137
138 let _ = solver
139 .add_constraint(constraints::less_than_or_equals(
140 pairwise_product[r1][r2].clone(),
141 bibd.max_dot_product as i32,
142 constraint_tag,
143 ))
144 .post();
145 }
146 }
147
148 let mut brancher = solver.default_brancher();
149 match solver.satisfy(&mut brancher, &mut Indefinite) {
150 SatisfactionResult::Satisfiable(satisfiable) => {
151 let solution = satisfiable.solution();
152
153 let row_separator = format!("{}+", "+---".repeat(bibd.columns as usize));
154
155 for row in matrix.iter() {
156 let line = row
157 .iter()
158 .map(|var| {
159 if solution.get_integer_value(*var) == 1 {
160 String::from("| * ")
161 } else {
162 String::from("| ")
163 }
164 })
165 .collect::<String>();
166
167 println!("{row_separator}\n{line}|");
168 }
169
170 println!("{row_separator}");
171 }
172 SatisfactionResult::Unsatisfiable(_, _) => {
173 println!("UNSATISFIABLE")
174 }
175 SatisfactionResult::Unknown(_, _) => {
176 println!("UNKNOWN")
177 }
178 };
179}
180
181fn transpose<T: Clone, Inner: AsRef<[T]>>(matrix: &[Inner]) -> Vec<Vec<T>> {
182 let rows = matrix.len();
183 let cols = matrix[0].as_ref().len();
184
185 (0..cols)
186 .map(|col| {
187 (0..rows)
188 .map(|row| matrix[row].as_ref()[col].clone())
189 .collect()
190 })
191 .collect()
192}