Skip to content

Commit ddbee82

Browse files
committed
Parse AnfPolynomial from string
1 parent 6c499fa commit ddbee82

File tree

2 files changed

+127
-1
lines changed

2 files changed

+127
-1
lines changed

src/anf_polynom.rs

Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ use itertools::Itertools;
66
use num_bigint::BigUint;
77
use num_traits::{One, Zero};
88
use std::fmt::Display;
9+
use crate::BooleanFunctionError;
910

1011
#[derive(Debug, Clone, Eq, PartialEq)]
1112
enum PolynomialFormat {
@@ -122,6 +123,70 @@ impl AnfPolynomial {
122123
.join(" + ")
123124
}
124125

126+
/// Computes the ANF polynomial from string representation
127+
///
128+
/// Representation must be in the form "`x0*x2*x3 + x2*x3 + x1 + 1`".
129+
///
130+
/// X's index start at 0, meaning the maximum index is variable count - 1.
131+
///
132+
/// # Parameters:
133+
/// - `anf_polynomial`: The string representation of the ANF form
134+
/// - `num_variables`: Variable count of the polynomial
135+
///
136+
/// # Returns:
137+
/// The ANF polynomial, or an error if the input string doesn't respect the format and `unsafe_disable_safety_checks` feature is not activated.
138+
pub fn from_str(anf_polynomial: &str, num_variables: usize) -> Result<Self, BooleanFunctionError> {
139+
let anf_polynomial_filtered = anf_polynomial.chars().filter(|c| !c.is_whitespace()).collect::<String>();
140+
141+
let mut anf_polynomial_obj = Self {
142+
polynomial: if num_variables <= 6 {
143+
PolynomialFormat::Small(0u64)
144+
} else {
145+
PolynomialFormat::Big(BigUint::zero())
146+
},
147+
num_variables,
148+
};
149+
150+
for monomial_string in anf_polynomial_filtered.split('+') {
151+
if monomial_string == "1" {
152+
anf_polynomial_obj.flip_bit_pos(0)?;
153+
continue;
154+
}
155+
let mut bit_position_to_flip = 0u64;
156+
for factor in monomial_string.split('*') {
157+
#[cfg(not(feature = "unsafe_disable_safety_checks"))]
158+
if factor.chars().nth(0).ok_or(BooleanFunctionError::ErrorParsingAnfString)? != 'x' {
159+
return Err(BooleanFunctionError::ErrorParsingAnfString);
160+
}
161+
let factor_index = factor[1..].parse::<u64>().map_err(|_| BooleanFunctionError::ErrorParsingAnfString)?;
162+
#[cfg(not(feature = "unsafe_disable_safety_checks"))]
163+
if factor_index as usize >= num_variables {
164+
return Err(BooleanFunctionError::AnfFormNVariableTooBigFactor(num_variables, factor_index as usize));
165+
}
166+
bit_position_to_flip |= 1 << factor_index;
167+
}
168+
anf_polynomial_obj.flip_bit_pos(bit_position_to_flip)?;
169+
}
170+
171+
Ok(anf_polynomial_obj)
172+
}
173+
174+
fn flip_bit_pos(&mut self, bit_position: u64) -> Result<(), BooleanFunctionError> {
175+
#[cfg(not(feature = "unsafe_disable_safety_checks"))]
176+
if bit_position >= 1 << self.num_variables {
177+
return Err(BooleanFunctionError::UnexpectedError);
178+
}
179+
match &mut self.polynomial {
180+
PolynomialFormat::Small(anf) => {
181+
*anf ^= 1u64 << bit_position
182+
},
183+
PolynomialFormat::Big(anf) => {
184+
anf.set_bit(bit_position, !anf.bit(bit_position))
185+
}
186+
}
187+
Ok(())
188+
}
189+
125190
/// Monomial and number of variables in the monomial
126191
fn bit_monomial_to_string(&self, bit_position: u64) -> (String, usize) {
127192
if bit_position == 0 {
@@ -133,6 +198,17 @@ impl AnfPolynomial {
133198
.collect();
134199
(monomial_coefs_list.join("*"), monomial_coefs_list.len())
135200
}
201+
202+
/// Returns Boolean function internal storage type:
203+
///
204+
/// - [Small](crate::BooleanFunctionType::Small) for `u64` internal storage (less or equal than 6 variables Boolean function)
205+
/// - [Big](crate::BooleanFunctionType::Big) for `BigUInt` internal storage (more than 6 variables Boolean function)
206+
pub fn get_boolean_function_type(&self) -> crate::BooleanFunctionType {
207+
match self.polynomial {
208+
PolynomialFormat::Small(_) => crate::BooleanFunctionType::Small,
209+
PolynomialFormat::Big(_) => crate::BooleanFunctionType::Big
210+
}
211+
}
136212
}
137213

138214
/// Display implementation for `AnfPolynomial`.
@@ -149,6 +225,7 @@ mod tests {
149225
use crate::anf_polynom::AnfPolynomial;
150226
use num_bigint::BigUint;
151227
use num_traits::{Num, One, Zero};
228+
use crate::BooleanFunctionError;
152229

153230
#[test]
154231
fn test_get_polynomial_small() {
@@ -237,4 +314,47 @@ mod tests {
237314
let anf_polynomial = AnfPolynomial::from_anf_big(&BigUint::one(), 3);
238315
assert_eq!(anf_polynomial.to_string(), "1");
239316
}
317+
318+
#[test]
319+
fn test_from_str() {
320+
let anf_str = "x0*x1 + x0 + x0 + x0 + 1";
321+
let anf_polynomial = AnfPolynomial::from_str(anf_str, 3).unwrap();
322+
assert_eq!(anf_polynomial.to_string(), "x0*x1 + x0 + 1");
323+
assert_eq!(anf_polynomial.get_boolean_function_type(), crate::BooleanFunctionType::Small);
324+
325+
let anf_str = "x2 + x0*x1 + x0";
326+
let anf_polynomial = AnfPolynomial::from_str(anf_str, 3).unwrap();
327+
assert_eq!(anf_polynomial.to_string(), "x0*x1 + x0 + x2");
328+
assert_eq!(anf_polynomial.get_boolean_function_type(), crate::BooleanFunctionType::Small);
329+
330+
let anf_str = "x0*x1 + x2 + x0 + 1";
331+
let anf_polynomial = AnfPolynomial::from_str(anf_str, 2);
332+
assert!(anf_polynomial.is_err());
333+
assert_eq!(anf_polynomial.unwrap_err(), BooleanFunctionError::AnfFormNVariableTooBigFactor(2, 2));
334+
335+
let anf_str = "x0*y1 + x2 + x0 + 1";
336+
let anf_polynomial = AnfPolynomial::from_str(anf_str, 3);
337+
assert!(anf_polynomial.is_err());
338+
assert_eq!(anf_polynomial.unwrap_err(), BooleanFunctionError::ErrorParsingAnfString);
339+
340+
let anf_str = "x0*xy1 + x2 + x0 + 1";
341+
let anf_polynomial = AnfPolynomial::from_str(anf_str, 3);
342+
assert!(anf_polynomial.is_err());
343+
assert_eq!(anf_polynomial.unwrap_err(), BooleanFunctionError::ErrorParsingAnfString);
344+
345+
let anf_str = "x0*x1*x2*x3*x4*x5*x6 + x7 + x6 + x6 + 1";
346+
let anf_polynomial = AnfPolynomial::from_str(anf_str, 8).unwrap();
347+
assert_eq!(anf_polynomial.to_string(), "x0*x1*x2*x3*x4*x5*x6 + x7 + 1");
348+
assert_eq!(anf_polynomial.get_boolean_function_type(), crate::BooleanFunctionType::Big);
349+
350+
let anf_str = "x0*x1*x2*x3*x4*x5*x6 + x7";
351+
let anf_polynomial = AnfPolynomial::from_str(anf_str, 8).unwrap();
352+
assert_eq!(anf_polynomial.to_string(), "x0*x1*x2*x3*x4*x5*x6 + x7");
353+
assert_eq!(anf_polynomial.get_boolean_function_type(), crate::BooleanFunctionType::Big);
354+
355+
let anf_str = "x0x1*x2*x3*x4*x5*x6 + x7";
356+
let anf_polynomial = AnfPolynomial::from_str(anf_str, 8);
357+
assert!(anf_polynomial.is_err());
358+
assert_eq!(anf_polynomial.unwrap_err(), BooleanFunctionError::ErrorParsingAnfString);
359+
}
240360
}

src/boolean_function_error.rs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
use thiserror::Error;
44

55
/// Possible errors for the boolean function library.
6-
#[derive(Error, Debug, PartialEq)]
6+
#[derive(Error, Debug, PartialEq, Clone)]
77
pub enum BooleanFunctionError {
88
/// Error parsing hex string: the string length must be a power of 2 to represent a truth table.
99
#[error("Hex truth table length must be a power of 2")]
@@ -30,6 +30,12 @@ pub enum BooleanFunctionError {
3030
/// Cannot generate close balanced Boolean function iterator, as the given function is already balanced
3131
#[error("This Boolean function is already balanced")]
3232
AlreadyBalanced,
33+
/// Error parsing ANF string, should be in the form "x0*x2*x3 + x2*x3 + x1 + 1"
34+
#[error("Error parsing ANF string, should be in the form \"x0*x2*x3 + x2*x3 + x1 + 1\"")]
35+
ErrorParsingAnfString,
36+
/// The given factor is too big for the variable count. For example, x4 cannot exist if variable count = 4
37+
#[error("There are {0} variables in this ANF form, x{1} factor shouldn't appear")]
38+
AnfFormNVariableTooBigFactor(usize, usize),
3339
}
3440

3541
pub(crate) const XOR_DIFFERENT_VAR_COUNT_PANIC_MSG: &'static str =

0 commit comments

Comments
 (0)