1use crate::rules::registry::ReductionOverhead;
4use crate::rules::ReductionGraph;
5use crate::traits::Problem;
6use serde::{Deserialize, Serialize};
7use std::collections::BTreeMap;
8use std::fs;
9use std::path::Path;
10
11#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
13pub struct ProblemSide {
14 pub problem: String,
16 pub variant: BTreeMap<String, String>,
18 pub instance: serde_json::Value,
20}
21
22impl ProblemSide {
23 pub fn from_problem<P>(problem: &P) -> Self
25 where
26 P: Problem + Serialize,
27 {
28 Self {
29 problem: P::NAME.to_string(),
30 variant: variant_to_map(P::variant()),
31 instance: serde_json::to_value(problem).expect("Failed to serialize problem instance"),
32 }
33 }
34
35 pub fn problem_ref(&self) -> ProblemRef {
37 ProblemRef {
38 name: self.problem.clone(),
39 variant: self.variant.clone(),
40 }
41 }
42}
43
44#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
46pub struct ProblemRef {
47 pub name: String,
48 pub variant: BTreeMap<String, String>,
49}
50
51#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
53pub struct SolutionPair {
54 pub source_config: Vec<usize>,
55 pub target_config: Vec<usize>,
56}
57
58#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
60pub struct RuleExample {
61 pub source: ProblemSide,
62 pub target: ProblemSide,
63 pub solutions: Vec<SolutionPair>,
64}
65
66#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
68pub struct ModelExample {
69 pub problem: String,
70 pub variant: BTreeMap<String, String>,
71 pub instance: serde_json::Value,
72 pub optimal_config: Vec<usize>,
73 pub optimal_value: serde_json::Value,
74}
75
76impl ModelExample {
77 pub fn new(
78 problem: &str,
79 variant: BTreeMap<String, String>,
80 instance: serde_json::Value,
81 optimal_config: Vec<usize>,
82 optimal_value: serde_json::Value,
83 ) -> Self {
84 Self {
85 problem: problem.to_string(),
86 variant,
87 instance,
88 optimal_config,
89 optimal_value,
90 }
91 }
92
93 pub fn problem_ref(&self) -> ProblemRef {
94 ProblemRef {
95 name: self.problem.clone(),
96 variant: self.variant.clone(),
97 }
98 }
99}
100
101#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
103pub struct RuleDb {
104 pub rules: Vec<RuleExample>,
105}
106
107#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
109pub struct ModelDb {
110 pub models: Vec<ModelExample>,
111}
112
113#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
115pub struct ExampleDb {
116 pub models: Vec<ModelExample>,
117 pub rules: Vec<RuleExample>,
118}
119
120pub fn lookup_overhead(
122 source_name: &str,
123 source_variant: &BTreeMap<String, String>,
124 target_name: &str,
125 target_variant: &BTreeMap<String, String>,
126) -> Option<ReductionOverhead> {
127 let graph = ReductionGraph::new();
128 let matched =
129 graph.find_best_entry(source_name, source_variant, target_name, target_variant)?;
130 Some(matched.overhead)
131}
132
133pub fn variant_to_map(variant: Vec<(&str, &str)>) -> BTreeMap<String, String> {
138 variant
139 .into_iter()
140 .map(|(k, v)| {
141 let value = if k == "graph" && v.is_empty() {
142 "SimpleGraph".to_string()
143 } else {
144 v.to_string()
145 };
146 (k.to_string(), value)
147 })
148 .collect()
149}
150
151fn write_json_file<T: Serialize>(dir: &Path, name: &str, payload: &T) {
152 fs::create_dir_all(dir).expect("Failed to create examples directory");
153 let path = dir.join(format!("{name}.json"));
154 let json = serde_json::to_string_pretty(payload).expect("Failed to serialize example");
155 fs::write(&path, json).expect("Failed to write example JSON");
156 println!("Exported: {}", path.display());
157}
158
159fn render_compact_array<T: Serialize>(items: &[T]) -> String {
160 if items.is_empty() {
161 "[]".to_string()
162 } else {
163 let rows = items
164 .iter()
165 .map(|item| {
166 format!(
167 " {}",
168 serde_json::to_string(item).expect("Failed to serialize example entry")
169 )
170 })
171 .collect::<Vec<_>>()
172 .join(",\n");
173 format!("[\n{rows}\n ]")
174 }
175}
176
177fn write_example_db_file(dir: &Path, db: &ExampleDb) {
178 fs::create_dir_all(dir).expect("Failed to create examples directory");
179 let path = dir.join("examples.json");
180 let json = format!(
181 "{{\n \"models\": {},\n \"rules\": {}\n}}\n",
182 render_compact_array(&db.models),
183 render_compact_array(&db.rules)
184 );
185 fs::write(&path, json).expect("Failed to write example JSON");
186 println!("Exported: {}", path.display());
187}
188
189pub fn write_rule_example_to(dir: &Path, name: &str, example: &RuleExample) {
191 write_json_file(dir, name, example);
192}
193
194pub fn write_model_example_to(dir: &Path, name: &str, example: &ModelExample) {
196 write_json_file(dir, name, example);
197}
198
199pub fn write_rule_db_to(dir: &Path, db: &RuleDb) {
201 write_json_file(dir, "rules", db);
202}
203
204pub fn write_model_db_to(dir: &Path, db: &ModelDb) {
206 write_json_file(dir, "models", db);
207}
208
209pub fn write_example_db_to(dir: &Path, db: &ExampleDb) {
211 write_example_db_file(dir, db);
212}
213
214#[cfg(test)]
215#[path = "unit_tests/export.rs"]
216mod tests;