problemreductions/models/misc/
minimum_internal_macro_data_compression.rs1use crate::registry::{FieldInfo, ProblemSchemaEntry};
16use crate::traits::Problem;
17use crate::types::Min;
18use serde::{Deserialize, Serialize};
19
20inventory::submit! {
21 ProblemSchemaEntry {
22 name: "MinimumInternalMacroDataCompression",
23 display_name: "Minimum Internal Macro Data Compression",
24 aliases: &[],
25 dimensions: &[],
26 category: crate::registry::ProblemCategory::Misc,
27 module_path: module_path!(),
28 description: "Find minimum-cost self-referencing compression of a string with embedded pointers",
29 fields: &[
30 FieldInfo { name: "alphabet_size", type_name: "usize", description: "Size of the alphabet (symbols indexed 0..alphabet_size)" },
31 FieldInfo { name: "string", type_name: "Vec<usize>", description: "Source string as symbol indices" },
32 FieldInfo { name: "pointer_cost", type_name: "i64", description: "Pointer cost h (each pointer adds h−1 extra to the cost)" },
33 ],
34 }
35}
36
37#[derive(Debug, Clone, Serialize, Deserialize)]
69#[serde(try_from = "MinimumInternalMacroDataCompressionSerde")]
70pub struct MinimumInternalMacroDataCompression {
71 alphabet_size: usize,
72 string: Vec<usize>,
73 pointer_cost: i64,
74}
75
76#[derive(Deserialize)]
77struct MinimumInternalMacroDataCompressionSerde {
78 alphabet_size: usize,
79 string: Vec<usize>,
80 pointer_cost: i64,
81}
82
83impl TryFrom<MinimumInternalMacroDataCompressionSerde> for MinimumInternalMacroDataCompression {
84 type Error = crate::registry::ConstructionError;
85
86 fn try_from(value: MinimumInternalMacroDataCompressionSerde) -> Result<Self, Self::Error> {
87 if value.alphabet_size == 0 && !value.string.is_empty() {
88 return Err("alphabet_size must be > 0 when the string is non-empty"
89 .to_string()
90 .into());
91 }
92 if value
93 .string
94 .iter()
95 .any(|&symbol| symbol >= value.alphabet_size)
96 {
97 return Err("all symbols must be less than alphabet_size"
98 .to_string()
99 .into());
100 }
101 if value.pointer_cost <= 0 {
102 return Err("pointer_cost must be positive".to_string().into());
103 }
104 Ok(Self {
105 alphabet_size: value.alphabet_size,
106 string: value.string,
107 pointer_cost: value.pointer_cost,
108 })
109 }
110}
111
112impl MinimumInternalMacroDataCompression {
113 pub fn new(alphabet_size: usize, string: Vec<usize>, pointer_cost: i64) -> Self {
120 assert!(
121 alphabet_size > 0 || string.is_empty(),
122 "alphabet_size must be > 0 when the string is non-empty"
123 );
124 assert!(
125 string
126 .iter()
127 .all(|&s| s < alphabet_size || alphabet_size == 0),
128 "all symbols must be less than alphabet_size"
129 );
130 assert!(pointer_cost > 0, "pointer_cost must be positive");
131 Self {
132 alphabet_size,
133 string,
134 pointer_cost,
135 }
136 }
137
138 pub fn string_len(&self) -> usize {
140 self.string.len()
141 }
142
143 pub fn alphabet_size(&self) -> usize {
145 self.alphabet_size
146 }
147
148 pub fn pointer_cost(&self) -> i64 {
150 self.pointer_cost
151 }
152
153 pub fn string(&self) -> &[usize] {
155 &self.string
156 }
157
158 fn decode(&self, config: &[usize]) -> Option<(Vec<usize>, usize, usize)> {
162 let n = self.string.len();
163 let k = self.alphabet_size;
164 let eos = k; let active_len = config.iter().position(|&v| v == eos).unwrap_or(n);
168
169 for &v in &config[active_len..] {
171 if v != eos {
172 return None;
173 }
174 }
175
176 let mut decoded = Vec::new();
180 let mut pointer_count: usize = 0;
181
182 for &v in &config[..active_len] {
183 if v < k {
184 decoded.push(v);
186 } else if v > k {
187 let ref_pos = v - k - 1;
189 if ref_pos >= decoded.len() {
190 return None; }
192 let copy_start = decoded.len();
195 let mut matched = 0;
196 while copy_start + matched < n {
197 let src_idx = ref_pos + matched;
198 if src_idx >= copy_start {
199 break; }
201 if decoded[src_idx] != self.string[copy_start + matched] {
202 break;
203 }
204 decoded.push(decoded[src_idx]);
205 matched += 1;
206 }
207 if matched == 0 {
208 return None; }
210 pointer_count += 1;
211 } else {
212 return None;
214 }
215 }
216
217 Some((decoded, active_len, pointer_count))
218 }
219}
220
221impl Problem for MinimumInternalMacroDataCompression {
222 const NAME: &'static str = "MinimumInternalMacroDataCompression";
223 type Solution = Vec<usize>;
224 type Value = Min<i64>;
225
226 crate::problem_parameters![("alphabet_size", alphabet_size), ("string_len", string_len),];
227
228 fn variant() -> Vec<(&'static str, &'static str)> {
229 crate::variant_params![]
230 }
231
232 fn evaluate(
233 &self,
234 config: &Self::Solution,
235 ) -> Result<Min<i64>, crate::traits::EvaluationError> {
236 Ok({
237 let n = self.string.len();
238 if config.len() != n {
239 return Err(crate::traits::EvaluationError::InvalidConfiguration(
240 "macro encoding length does not match the string".into(),
241 ));
242 }
243
244 if n == 0 {
246 return Ok(Min(Some(0)));
247 }
248
249 match self.decode(config) {
250 Some((decoded, active_len, pointer_count)) => {
251 if decoded != self.string {
252 Min(None)
253 } else {
254 let active_len = i64::try_from(active_len).map_err(|_| {
255 crate::traits::EvaluationError::IntegerOverflow(
256 "converting the active encoding length to i64".to_string(),
257 )
258 })?;
259 let pointer_count = i64::try_from(pointer_count).map_err(|_| {
260 crate::traits::EvaluationError::IntegerOverflow(
261 "converting the pointer count to i64".to_string(),
262 )
263 })?;
264 let pointer_cost = self
265 .pointer_cost
266 .checked_sub(1)
267 .and_then(|cost| cost.checked_mul(pointer_count))
268 .ok_or_else(|| {
269 crate::traits::EvaluationError::IntegerOverflow(
270 "computing the internal macro pointer cost".to_string(),
271 )
272 })?;
273 let cost = active_len.checked_add(pointer_cost).ok_or_else(|| {
274 crate::traits::EvaluationError::IntegerOverflow(
275 "computing the internal macro compression cost".to_string(),
276 )
277 })?;
278 Min(Some(cost))
279 }
280 }
281 None => Min(None),
282 }
283 })
284 }
285}
286
287impl crate::solvers::BruteForceProblem for MinimumInternalMacroDataCompression {
288 fn dimensions(&self) -> Vec<usize> {
289 let n = self.string.len();
290 let domain = self.alphabet_size + n + 1; vec![domain; n]
292 }
293}
294
295crate::declare_variants! {
296 default MinimumInternalMacroDataCompression => "(alphabet_size + string_len + 1) ^ string_len",
297}
298
299crate::register_brute_force! {
300 MinimumInternalMacroDataCompression,
301}
302
303#[cfg(feature = "example-db")]
304pub(crate) fn canonical_model_example_specs() -> Vec<crate::example_db::specs::ModelExampleSpec> {
305 let s: Vec<usize> = vec![0, 1, 2, 0, 1, 2, 0, 1, 2];
316 let optimal_config = vec![
317 0, 1, 2, 4, 4, 3, 3, 3, 3, ];
322 vec![crate::example_db::specs::ModelExampleSpec {
323 id: "minimum_internal_macro_data_compression",
324 instance: Box::new(MinimumInternalMacroDataCompression::new(3, s, 2)),
325 optimal_config: serde_json::to_value(optimal_config)
326 .expect("solution serialization must succeed"),
327 optimal_value: serde_json::json!(7),
328 }]
329}
330
331#[cfg(test)]
332#[path = "../../unit_tests/models/misc/minimum_internal_macro_data_compression.rs"]
333mod tests;