Skip to main content

problemreductions/registry/
info.rs

1//! Problem metadata and information types.
2//!
3//! This module provides types for describing problem characteristics:
4//!
5//! - [`ComplexityClass`] - Computational complexity (P, NP-complete, etc.)
6//! - [`ProblemInfo`] - Rich metadata about a problem type
7//! - [`ProblemMetadata`] - Trait for problems to provide their metadata
8//!
9//! # Example
10//!
11//! ```rust
12//! use problemreductions::registry::{ProblemInfo, ComplexityClass};
13//!
14//! let info = ProblemInfo::new("3-SAT", "Satisfiability with 3-literal clauses")
15//!     .with_aliases(&["3-CNF-SAT", "3SAT"])
16//!     .with_complexity(ComplexityClass::NpComplete)
17//!     .with_reduction_from("SAT");
18//!
19//! assert!(info.is_np_complete());
20//! assert_eq!(info.all_names().len(), 3);
21//! ```
22
23use std::fmt;
24
25/// Computational complexity class of a problem.
26///
27/// Used to classify problems by their computational difficulty.
28/// Most problems in this library are [`NpComplete`](ComplexityClass::NpComplete).
29///
30/// # Example
31///
32/// ```rust
33/// use problemreductions::registry::ComplexityClass;
34///
35/// let class = ComplexityClass::NpComplete;
36/// assert!(class.is_hard());
37/// assert_eq!(class.name(), "NP-complete");
38/// ```
39#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
40pub enum ComplexityClass {
41    /// In P (polynomial time)
42    P,
43    /// NP-complete
44    NpComplete,
45    /// NP-hard (at least as hard as NP-complete)
46    NpHard,
47    /// PSPACE-complete
48    PspaceComplete,
49    /// Unknown or unclassified
50    Unknown,
51}
52
53impl ComplexityClass {
54    /// Get the complexity class name.
55    pub fn name(&self) -> &'static str {
56        match self {
57            ComplexityClass::P => "P",
58            ComplexityClass::NpComplete => "NP-complete",
59            ComplexityClass::NpHard => "NP-hard",
60            ComplexityClass::PspaceComplete => "PSPACE-complete",
61            ComplexityClass::Unknown => "Unknown",
62        }
63    }
64
65    /// Check if this problem is at least NP-hard.
66    pub fn is_hard(&self) -> bool {
67        matches!(
68            self,
69            ComplexityClass::NpComplete | ComplexityClass::NpHard | ComplexityClass::PspaceComplete
70        )
71    }
72}
73
74impl fmt::Display for ComplexityClass {
75    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
76        write!(f, "{}", self.name())
77    }
78}
79
80/// Metadata about a problem type.
81///
82/// Contains static information about a problem definition, including its name,
83/// description, complexity class, and relationships to other problems.
84/// Use the builder methods to construct instances.
85///
86/// # Example
87///
88/// ```rust
89/// use problemreductions::registry::{ProblemInfo, ComplexityClass};
90///
91/// let info = ProblemInfo::new("Vertex Cover", "Find minimum vertices covering all edges")
92///     .with_aliases(&["VC", "Minimum Vertex Cover"])
93///     .with_complexity(ComplexityClass::NpComplete)
94///     .with_reduction_from("Independent Set")
95///     .with_reference("https://en.wikipedia.org/wiki/Vertex_cover");
96///
97/// println!("{}", info);  // "Vertex Cover (NP-complete)"
98/// ```
99///
100/// # Builder Pattern
101///
102/// All builder methods are `const fn` and can be used in const contexts:
103///
104/// ```rust
105/// use problemreductions::registry::{ProblemInfo, ComplexityClass};
106///
107/// const MY_PROBLEM_INFO: ProblemInfo = ProblemInfo::new("My Problem", "Description")
108///     .with_complexity(ComplexityClass::NpComplete);
109/// ```
110#[derive(Debug, Clone, PartialEq, Eq)]
111pub struct ProblemInfo {
112    /// The canonical name of the problem.
113    pub name: &'static str,
114    /// Alternative names for the problem.
115    pub aliases: &'static [&'static str],
116    /// A brief description of the problem.
117    pub description: &'static str,
118    /// The computational complexity class.
119    pub complexity_class: ComplexityClass,
120    /// Whether this has a decision version (yes/no answer).
121    pub decision_version: bool,
122    /// Whether this has an optimization version.
123    pub optimization_version: bool,
124    /// The canonical problem this reduces from (for NP-completeness proof).
125    pub canonical_reduction_from: Option<&'static str>,
126    /// Wikipedia or reference URL.
127    pub reference_url: Option<&'static str>,
128    /// Struct field descriptions for schema export.
129    pub fields: &'static [FieldInfo],
130}
131
132impl ProblemInfo {
133    /// Create a new ProblemInfo with minimal required fields.
134    pub const fn new(name: &'static str, description: &'static str) -> Self {
135        Self {
136            name,
137            aliases: &[],
138            description,
139            complexity_class: ComplexityClass::NpComplete,
140            decision_version: true,
141            optimization_version: true,
142            canonical_reduction_from: None,
143            reference_url: None,
144            fields: &[],
145        }
146    }
147
148    /// Builder method to add aliases.
149    pub const fn with_aliases(mut self, aliases: &'static [&'static str]) -> Self {
150        self.aliases = aliases;
151        self
152    }
153
154    /// Builder method to set complexity class.
155    pub const fn with_complexity(mut self, class: ComplexityClass) -> Self {
156        self.complexity_class = class;
157        self
158    }
159
160    /// Builder method to set decision version availability.
161    pub const fn with_decision(mut self, has_decision: bool) -> Self {
162        self.decision_version = has_decision;
163        self
164    }
165
166    /// Builder method to set optimization version availability.
167    pub const fn with_optimization(mut self, has_optimization: bool) -> Self {
168        self.optimization_version = has_optimization;
169        self
170    }
171
172    /// Builder method to set the canonical reduction source.
173    pub const fn with_reduction_from(mut self, source: &'static str) -> Self {
174        self.canonical_reduction_from = Some(source);
175        self
176    }
177
178    /// Builder method to set reference URL.
179    pub const fn with_reference(mut self, url: &'static str) -> Self {
180        self.reference_url = Some(url);
181        self
182    }
183
184    /// Builder method to set struct field descriptions.
185    pub const fn with_fields(mut self, fields: &'static [FieldInfo]) -> Self {
186        self.fields = fields;
187        self
188    }
189
190    /// Check if this problem is NP-complete.
191    pub fn is_np_complete(&self) -> bool {
192        self.complexity_class == ComplexityClass::NpComplete
193    }
194
195    /// Get all names (canonical + aliases).
196    pub fn all_names(&self) -> Vec<&'static str> {
197        let mut names = vec![self.name];
198        names.extend_from_slice(self.aliases);
199        names
200    }
201}
202
203impl fmt::Display for ProblemInfo {
204    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
205        write!(f, "{} ({})", self.name, self.complexity_class)
206    }
207}
208
209/// Description of a struct field for JSON schema export.
210#[derive(Debug, Clone, PartialEq, Eq)]
211pub struct FieldInfo {
212    /// Field name as it appears in the Rust struct.
213    pub name: &'static str,
214    /// Type name (e.g., `Vec<W>`, `UnGraph<(), ()>`).
215    pub type_name: &'static str,
216    /// Human-readable description of what this field represents.
217    pub description: &'static str,
218}
219
220/// Trait for problems that provide static metadata.
221///
222/// Implement this trait to enable introspection and discovery for problem types.
223///
224/// # Example
225///
226/// ```rust
227/// use problemreductions::registry::{
228///     ProblemMetadata, ProblemInfo, ComplexityClass
229/// };
230///
231/// struct MyProblem;
232///
233/// impl ProblemMetadata for MyProblem {
234///     fn problem_info() -> ProblemInfo {
235///         ProblemInfo::new("My Problem", "Description")
236///             .with_complexity(ComplexityClass::NpComplete)
237///     }
238/// }
239///
240/// // Get problem metadata
241/// let info = MyProblem::problem_info();
242/// assert_eq!(info.name, "My Problem");
243/// ```
244pub trait ProblemMetadata {
245    /// Returns the problem info for this problem type.
246    ///
247    /// This includes the problem name, description, aliases, complexity class,
248    /// and known reductions.
249    fn problem_info() -> ProblemInfo;
250}
251
252#[cfg(test)]
253#[path = "../unit_tests/registry/info.rs"]
254mod tests;