problemreductions/models/misc/
factoring.rs1use crate::registry::{ConstructionError, CreateSpec, ProblemSchemaEntry};
7use crate::traits::Problem;
8use crate::types::Or;
9use num_bigint::{BigUint, ToBigUint};
10use num_traits::{One, Zero};
11use serde::{Deserialize, Serialize};
12
13inventory::submit! {
14 ProblemSchemaEntry {
15 name: "Factoring",
16 display_name: "Factoring",
17 aliases: &[],
18 dimensions: &[],
19 category: crate::registry::ProblemCategory::Misc,
20 module_path: module_path!(),
21 description: "Factor a composite integer into two factors",
22 fields: FactoringCreateSpec::FIELDS,
23 }
24}
25
26#[derive(Debug, Clone, Serialize)]
50pub struct Factoring {
51 m: usize,
53 n: usize,
55 #[serde(with = "super::biguint_serde::decimal_biguint")]
57 target: BigUint,
58}
59
60#[derive(Debug, Deserialize, crate::CreateSpec)]
61struct FactoringCreateSpec {
62 #[serde(with = "super::biguint_serde::decimal_biguint")]
64 target: BigUint,
65 m: Option<usize>,
67 n: Option<usize>,
69}
70
71impl TryFrom<FactoringCreateSpec> for Factoring {
72 type Error = ConstructionError;
73
74 fn try_from(spec: FactoringCreateSpec) -> Result<Self, Self::Error> {
75 match (spec.m, spec.n) {
76 (None, None) => Ok(Self::from_target(spec.target)),
77 (Some(m), Some(n)) if m <= n => Ok(Self {
78 m,
79 n,
80 target: spec.target,
81 }),
82 (Some(m), Some(n)) => Err(format!(
83 "first factor width m={m} must not exceed second factor width n={n}"
84 )
85 .into()),
86 _ => Err("factor widths m and n must be provided together".into()),
87 }
88 }
89}
90
91impl<'de> Deserialize<'de> for Factoring {
92 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
93 where
94 D: serde::Deserializer<'de>,
95 {
96 let spec = FactoringCreateSpec::deserialize(deserializer)?;
97 Self::try_from(spec).map_err(serde::de::Error::custom)
98 }
99}
100
101impl Factoring {
102 pub fn new<T: ToBigUint>(target: T) -> Self {
107 let target = target
108 .to_biguint()
109 .expect("Factoring target must be nonnegative");
110 Self::from_target(target)
111 }
112
113 fn from_target(target: BigUint) -> Self {
114 let target_bits =
115 usize::try_from(target.bits().max(1)).expect("BigUint bit length fits usize");
116 let smaller_width = target_bits.div_ceil(2);
117 let larger_width = target_bits.saturating_sub(1);
118 let (m, n) = (
119 smaller_width.min(larger_width),
120 smaller_width.max(larger_width),
121 );
122 Self { m, n, target }
123 }
124
125 pub fn with_factor_bits<T: ToBigUint>(target: T, m: usize, n: usize) -> Self {
131 assert!(
132 m <= n,
133 "first factor width m must not exceed second factor width n"
134 );
135 let target = target
136 .to_biguint()
137 .expect("Factoring target must be nonnegative");
138 Self { m, n, target }
139 }
140
141 pub fn m(&self) -> usize {
143 self.m
144 }
145
146 pub fn n(&self) -> usize {
148 self.n
149 }
150
151 pub fn num_bits_first(&self) -> usize {
153 self.m()
154 }
155
156 pub fn num_bits_second(&self) -> usize {
158 self.n()
159 }
160
161 pub fn target(&self) -> &BigUint {
163 &self.target
164 }
165
166 pub fn target_bits(&self) -> usize {
168 usize::try_from(self.target.bits().max(1)).expect("BigUint bit length fits usize")
169 }
170
171 fn decode_factors(&self, config: &[usize]) -> (BigUint, BigUint) {
176 let a = bits_to_biguint(&config[..self.m]);
177 let b = bits_to_biguint(&config[self.m..self.m + self.n]);
178 (a, b)
179 }
180
181 pub fn is_valid_solution(&self, solution: &(BigUint, BigUint)) -> bool {
183 self.is_valid_factorization(solution)
184 }
185
186 pub fn is_valid_factorization(&self, solution: &(BigUint, BigUint)) -> bool {
188 let (left, right) = solution;
189 left.bits() <= u64::try_from(self.m).expect("factor width fits u64")
190 && right.bits() <= u64::try_from(self.n).expect("factor width fits u64")
191 && left <= right
192 && left * right == self.target
193 }
194}
195
196fn bits_to_biguint(bits: &[usize]) -> BigUint {
198 bits.iter()
199 .enumerate()
200 .filter(|(_, bit)| **bit == 1)
201 .fold(BigUint::zero(), |value, (index, _)| {
202 value + (BigUint::one() << index)
203 })
204}
205
206#[allow(dead_code)]
208fn int_to_bits(n: &BigUint, num_bits: usize) -> Vec<usize> {
209 (0..num_bits)
210 .map(|index| usize::from(n.bit(u64::try_from(index).expect("bit index fits u64"))))
211 .collect()
212}
213
214#[cfg(test)]
216pub(crate) fn is_factoring(target: &BigUint, a: &BigUint, b: &BigUint) -> bool {
217 a * b == *target
218}
219
220impl Problem for Factoring {
221 const NAME: &'static str = "Factoring";
222 type Solution = (BigUint, BigUint);
223 type Value = Or;
224
225 crate::problem_parameters![
226 ("num_bits_first", num_bits_first),
227 ("num_bits_second", num_bits_second),
228 ("target_bits", target_bits),
229 ];
230
231 fn evaluate(&self, solution: &Self::Solution) -> Result<Or, crate::traits::EvaluationError> {
232 Ok(Or(self.is_valid_factorization(solution)))
233 }
234
235 fn variant() -> Vec<(&'static str, &'static str)> {
236 crate::variant_params![]
237 }
238}
239
240impl crate::solvers::BruteForceProblem for Factoring {
241 fn dimensions(&self) -> Vec<usize> {
242 vec![2; self.m + self.n]
243 }
244}
245
246crate::declare_variants! {
247 default Factoring => "exp((num_bits_first + num_bits_second)^(1/3) * log(num_bits_first + num_bits_second)^(2/3))" create FactoringCreateSpec,
248}
249
250crate::register_brute_force! {
251 Factoring decode |problem: &Factoring, indices: Vec<usize>| problem.decode_factors(&indices),
252}
253
254#[cfg(feature = "example-db")]
255pub(crate) fn canonical_model_example_specs() -> Vec<crate::example_db::specs::ModelExampleSpec> {
256 vec![crate::example_db::specs::ModelExampleSpec {
257 id: "factoring",
258 instance: Box::new(Factoring::new(15)),
259 optimal_config: serde_json::to_value((BigUint::from(3u32), BigUint::from(5u32)))
260 .expect("solution serialization must succeed"),
261 optimal_value: serde_json::json!(true),
262 }]
263}
264
265#[cfg(test)]
266#[path = "../../unit_tests/models/misc/factoring.rs"]
267mod tests;