problemreductions/models/misc/
kth_largest_m_tuple.rs1use crate::registry::{FieldInfo, ProblemSchemaEntry, ProblemSizeFieldEntry};
8use crate::traits::Problem;
9use crate::types::Sum;
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 module_path: module_path!(),
20 description: "Count m-tuples whose total size meets a bound and compare against a threshold K",
21 fields: &[
22 FieldInfo { name: "sets", type_name: "Vec<Vec<u64>>", description: "m sets, each containing positive integer sizes" },
23 FieldInfo { name: "k", type_name: "u64", description: "Threshold K (answer YES iff count >= K)" },
24 FieldInfo { name: "bound", type_name: "u64", description: "Lower bound B on tuple sum" },
25 ],
26 }
27}
28
29inventory::submit! {
30 ProblemSizeFieldEntry {
31 name: "KthLargestMTuple",
32 fields: &["num_sets", "total_tuples"],
33 }
34}
35
36#[derive(Debug, Clone, Serialize)]
66pub struct KthLargestMTuple {
67 sets: Vec<Vec<u64>>,
68 k: u64,
69 bound: u64,
70}
71
72impl KthLargestMTuple {
73 fn validate(sets: &[Vec<u64>], k: u64, bound: u64) -> Result<(), String> {
74 if sets.is_empty() {
75 return Err("KthLargestMTuple requires at least one set".to_string());
76 }
77 if sets.iter().any(|s| s.is_empty()) {
78 return Err("Every set must be non-empty".to_string());
79 }
80 if sets.iter().any(|s| s.contains(&0)) {
81 return Err("All sizes must be positive (> 0)".to_string());
82 }
83 if k == 0 {
84 return Err("Threshold K must be positive".to_string());
85 }
86 if bound == 0 {
87 return Err("Bound B must be positive".to_string());
88 }
89 Ok(())
90 }
91
92 pub fn try_new(sets: Vec<Vec<u64>>, k: u64, bound: u64) -> Result<Self, String> {
94 Self::validate(&sets, k, bound)?;
95 Ok(Self { sets, k, bound })
96 }
97
98 pub fn new(sets: Vec<Vec<u64>>, k: u64, bound: u64) -> Self {
104 Self::try_new(sets, k, bound).unwrap_or_else(|msg| panic!("{msg}"))
105 }
106
107 pub fn sets(&self) -> &[Vec<u64>] {
109 &self.sets
110 }
111
112 pub fn k(&self) -> u64 {
114 self.k
115 }
116
117 pub fn bound(&self) -> u64 {
119 self.bound
120 }
121
122 pub fn num_sets(&self) -> usize {
124 self.sets.len()
125 }
126
127 pub fn total_tuples(&self) -> usize {
129 self.sets.iter().map(|s| s.len()).product()
130 }
131}
132
133#[derive(Deserialize)]
134struct KthLargestMTupleDef {
135 sets: Vec<Vec<u64>>,
136 k: u64,
137 bound: u64,
138}
139
140impl<'de> Deserialize<'de> for KthLargestMTuple {
141 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
142 where
143 D: Deserializer<'de>,
144 {
145 let data = KthLargestMTupleDef::deserialize(deserializer)?;
146 Self::try_new(data.sets, data.k, data.bound).map_err(D::Error::custom)
147 }
148}
149
150impl Problem for KthLargestMTuple {
151 const NAME: &'static str = "KthLargestMTuple";
152 type Value = Sum<u64>;
153
154 fn variant() -> Vec<(&'static str, &'static str)> {
155 crate::variant_params![]
156 }
157
158 fn dims(&self) -> Vec<usize> {
159 self.sets.iter().map(|s| s.len()).collect()
160 }
161
162 fn evaluate(&self, config: &[usize]) -> Sum<u64> {
163 if config.len() != self.num_sets() {
164 return Sum(0);
165 }
166 for (i, &choice) in config.iter().enumerate() {
167 if choice >= self.sets[i].len() {
168 return Sum(0);
169 }
170 }
171 let total: u64 = config
172 .iter()
173 .enumerate()
174 .map(|(i, &choice)| self.sets[i][choice])
175 .sum();
176 if total >= self.bound {
177 Sum(1)
178 } else {
179 Sum(0)
180 }
181 }
182}
183
184crate::declare_variants! {
187 default KthLargestMTuple => "total_tuples * num_sets",
188}
189
190#[cfg(feature = "example-db")]
191pub(crate) fn canonical_model_example_specs() -> Vec<crate::example_db::specs::ModelExampleSpec> {
192 vec![crate::example_db::specs::ModelExampleSpec {
195 id: "kth_largest_m_tuple",
196 instance: Box::new(KthLargestMTuple::new(
197 vec![vec![2, 5, 8], vec![3, 6], vec![1, 4, 7]],
198 14,
199 12,
200 )),
201 optimal_config: vec![2, 1, 2],
202 optimal_value: serde_json::json!(1),
203 }]
204}
205
206#[cfg(test)]
207#[path = "../../unit_tests/models/misc/kth_largest_m_tuple.rs"]
208mod tests;