problemreductions/models/misc/
kth_largest_m_tuple.rs1use crate::registry::{CreateSpec, ProblemSchemaEntry};
8use crate::traits::Problem;
9use crate::types::Or;
10use serde::de::Error as _;
11use serde::{Deserialize, Deserializer, Serialize};
12
13inventory::submit! {
14 ProblemSchemaEntry {
15 name: "KthLargestMTuple",
16 display_name: "Kth Largest m-Tuple",
17 aliases: &[],
18 dimensions: &[],
19 category: crate::registry::ProblemCategory::Misc,
20 module_path: module_path!(),
21 description: "Count m-tuples whose total size meets a bound and compare against a threshold K",
22 fields: KthLargestMTupleCreateSpec::FIELDS,
23 }
24}
25
26#[derive(Debug, Clone, Serialize)]
55pub struct KthLargestMTuple {
56 sets: Vec<Vec<i64>>,
57 k: i64,
58 bound: i64,
59}
60
61#[derive(Debug, Deserialize, crate::CreateSpec)]
62struct KthLargestMTupleCreateSpec {
63 subsets: Vec<Vec<i64>>,
65 k: i64,
67 bound: i64,
69}
70
71impl TryFrom<KthLargestMTupleCreateSpec> for KthLargestMTuple {
72 type Error = crate::registry::ConstructionError;
73
74 fn try_from(spec: KthLargestMTupleCreateSpec) -> Result<Self, Self::Error> {
75 Self::try_new(spec.subsets, spec.k, spec.bound)
76 }
77}
78
79impl KthLargestMTuple {
80 fn validate(
81 sets: &[Vec<i64>],
82 k: i64,
83 bound: i64,
84 ) -> Result<(), crate::registry::ConstructionError> {
85 if sets.is_empty() {
86 return Err("KthLargestMTuple requires at least one set"
87 .to_string()
88 .into());
89 }
90 if sets.iter().any(|s| s.is_empty()) {
91 return Err("Every set must be non-empty".to_string().into());
92 }
93 sets.iter()
94 .try_fold(1usize, |total, set| total.checked_mul(set.len()))
95 .ok_or_else(|| {
96 crate::registry::ConstructionError::IntegerOverflow(
97 "representing the total tuple count".into(),
98 )
99 })?;
100 if sets.iter().flatten().any(|&size| size <= 0) {
101 return Err("All sizes must be positive (> 0)".to_string().into());
102 }
103 if k <= 0 {
104 return Err("Threshold K must be positive".to_string().into());
105 }
106 if bound <= 0 {
107 return Err("Bound B must be positive".to_string().into());
108 }
109 Ok(())
110 }
111
112 pub fn try_new(
115 sets: Vec<Vec<i64>>,
116 k: i64,
117 bound: i64,
118 ) -> Result<Self, crate::registry::ConstructionError> {
119 Self::validate(&sets, k, bound)?;
120 Ok(Self { sets, k, bound })
121 }
122
123 pub fn new(sets: Vec<Vec<i64>>, k: i64, bound: i64) -> Self {
129 Self::try_new(sets, k, bound).unwrap_or_else(|msg| panic!("{msg}"))
130 }
131
132 pub fn sets(&self) -> &[Vec<i64>] {
134 &self.sets
135 }
136
137 pub fn k(&self) -> i64 {
139 self.k
140 }
141
142 pub fn bound(&self) -> i64 {
144 self.bound
145 }
146
147 pub fn num_sets(&self) -> usize {
149 self.sets.len()
150 }
151
152 pub fn total_tuples(&self) -> usize {
154 self.sets.iter().map(Vec::len).product()
155 }
156
157 fn has_at_least_k_qualifying_tuples(&self) -> Result<bool, crate::traits::EvaluationError> {
158 let mut choices = vec![0; self.sets.len()];
159 let mut qualifying = 0;
160
161 loop {
162 let mut sum = 0i64;
163 for (set, &choice) in self.sets.iter().zip(&choices) {
164 sum = sum.checked_add(set[choice]).ok_or_else(|| {
165 crate::traits::EvaluationError::IntegerOverflow(
166 "summing a KthLargestMTuple tuple".to_string(),
167 )
168 })?;
169 }
170 if sum >= self.bound {
171 qualifying += 1;
172 if qualifying == self.k {
173 return Ok(true);
174 }
175 }
176
177 let mut advanced = false;
178 for set_index in (0..choices.len()).rev() {
179 choices[set_index] += 1;
180 if choices[set_index] == self.sets[set_index].len() {
181 choices[set_index] = 0;
182 } else {
183 advanced = true;
184 break;
185 }
186 }
187 if !advanced {
188 return Ok(false);
189 }
190 }
191 }
192}
193
194#[derive(Deserialize)]
195struct KthLargestMTupleDef {
196 sets: Vec<Vec<i64>>,
197 k: i64,
198 bound: i64,
199}
200
201impl<'de> Deserialize<'de> for KthLargestMTuple {
202 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
203 where
204 D: Deserializer<'de>,
205 {
206 let data = KthLargestMTupleDef::deserialize(deserializer)?;
207 Self::try_new(data.sets, data.k, data.bound).map_err(D::Error::custom)
208 }
209}
210
211impl Problem for KthLargestMTuple {
212 const NAME: &'static str = "KthLargestMTuple";
213 type Solution = ();
214 type Value = Or;
215
216 crate::problem_parameters![("num_sets", num_sets), ("total_tuples", total_tuples),];
217
218 fn variant() -> Vec<(&'static str, &'static str)> {
219 crate::variant_params![]
220 }
221
222 fn evaluate(&self, _solution: &Self::Solution) -> Result<Or, crate::traits::EvaluationError> {
223 Ok(Or(self.has_at_least_k_qualifying_tuples()?))
224 }
225}
226
227impl crate::solvers::BruteForceProblem for KthLargestMTuple {
228 fn dimensions(&self) -> Vec<usize> {
229 vec![]
230 }
231}
232
233crate::declare_variants! {
236 default KthLargestMTuple => "total_tuples * num_sets" create KthLargestMTupleCreateSpec,
237}
238
239crate::register_brute_force! {
240 KthLargestMTuple decode |_, _| (),
241}
242
243#[cfg(feature = "example-db")]
244pub(crate) fn canonical_model_example_specs() -> Vec<crate::example_db::specs::ModelExampleSpec> {
245 vec![crate::example_db::specs::ModelExampleSpec {
248 id: "kth_largest_m_tuple",
249 instance: Box::new(KthLargestMTuple::new(
250 vec![vec![2, 5, 8], vec![3, 6], vec![1, 4, 7]],
251 14,
252 12,
253 )),
254 optimal_config: serde_json::json!(null),
255 optimal_value: serde_json::json!(true),
256 }]
257}
258
259#[cfg(test)]
260#[path = "../../unit_tests/models/misc/kth_largest_m_tuple.rs"]
261mod tests;