problemreductions/models/misc/
subset_product.rs1use crate::registry::{FieldInfo, ProblemSchemaEntry};
11use crate::traits::Problem;
12use num_bigint::{BigUint, ToBigUint};
13use num_traits::{One, Zero};
14use serde::{Deserialize, Serialize};
15
16inventory::submit! {
17 ProblemSchemaEntry {
18 name: "SubsetProduct",
19 display_name: "Subset Product",
20 aliases: &[],
21 dimensions: &[],
22 module_path: module_path!(),
23 description: "Find a subset of positive integers whose product equals exactly a target value",
24 fields: &[
25 FieldInfo { name: "sizes", type_name: "Vec<BigUint>", description: "Positive integer sizes s(a) for each element" },
26 FieldInfo { name: "target", type_name: "BigUint", description: "Target product B" },
27 ],
28 }
29}
30
31#[derive(Debug, Clone, Serialize, Deserialize)]
53pub struct SubsetProduct {
54 #[serde(with = "super::biguint_serde::decimal_biguint_vec")]
55 sizes: Vec<BigUint>,
56 #[serde(with = "super::biguint_serde::decimal_biguint")]
57 target: BigUint,
58}
59
60impl SubsetProduct {
61 pub fn new<S, T>(sizes: Vec<S>, target: T) -> Self
67 where
68 S: ToBigUint,
69 T: ToBigUint,
70 {
71 let sizes: Vec<BigUint> = sizes
72 .into_iter()
73 .map(|s| s.to_biguint().expect("All sizes must be positive (> 0)"))
74 .collect();
75 assert!(
76 sizes.iter().all(|s| !s.is_zero()),
77 "All sizes must be positive (> 0)"
78 );
79 let target = target
80 .to_biguint()
81 .expect("SubsetProduct target must be nonnegative");
82 assert!(!target.is_zero(), "SubsetProduct target must be positive");
83 Self { sizes, target }
84 }
85
86 #[cfg(test)]
88 pub(crate) fn new_unchecked(sizes: Vec<BigUint>, target: BigUint) -> Self {
89 Self { sizes, target }
90 }
91
92 pub fn sizes(&self) -> &[BigUint] {
94 &self.sizes
95 }
96
97 pub fn target(&self) -> &BigUint {
99 &self.target
100 }
101
102 pub fn num_elements(&self) -> usize {
104 self.sizes.len()
105 }
106}
107
108impl Problem for SubsetProduct {
109 const NAME: &'static str = "SubsetProduct";
110 type Value = crate::types::Or;
111
112 fn variant() -> Vec<(&'static str, &'static str)> {
113 crate::variant_params![]
114 }
115
116 fn dims(&self) -> Vec<usize> {
117 vec![2; self.num_elements()]
118 }
119
120 fn evaluate(&self, config: &[usize]) -> crate::types::Or {
121 crate::types::Or({
122 if config.len() != self.num_elements() {
123 return crate::types::Or(false);
124 }
125 if config.iter().any(|&v| v >= 2) {
126 return crate::types::Or(false);
127 }
128 let mut product = BigUint::one();
129 for (i, &x) in config.iter().enumerate() {
130 if x == 1 {
131 product *= &self.sizes[i];
132 if product > self.target {
133 return crate::types::Or(false);
134 }
135 }
136 }
137 product == self.target
138 })
139 }
140}
141
142crate::declare_variants! {
143 default SubsetProduct => "2^num_elements",
144}
145
146#[cfg(feature = "example-db")]
147pub(crate) fn canonical_model_example_specs() -> Vec<crate::example_db::specs::ModelExampleSpec> {
148 vec![crate::example_db::specs::ModelExampleSpec {
150 id: "subset_product",
151 instance: Box::new(SubsetProduct::new(vec![2u32, 3, 5, 7, 6, 10], 210u32)),
152 optimal_config: vec![1, 1, 1, 1, 0, 0],
153 optimal_value: serde_json::json!(true),
154 }]
155}
156
157#[cfg(test)]
158#[path = "../../unit_tests/models/misc/subset_product.rs"]
159mod tests;