diff --git a/Cargo.toml b/Cargo.toml index 2bf8179..ec85626 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,5 +6,6 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] +clap = { version = "4.5.35", features = ["derive"] } lll-rs = "0.2.0" rug = "1.27.0" diff --git a/gen_values.py b/gen_values.py index f5ab69a..f73c9a2 100644 --- a/gen_values.py +++ b/gen_values.py @@ -1,6 +1,28 @@ -from random import randint -p = randint(100,1000) -a = ["Integer::from("+str(p*randint(1,100)+randint(0,20))+"), " for _ in range(200)] -for b in a: - print(b) -print(p) +import sys +import random +import argparse + +def generate_test_file(noise_bits, number): + + p = random.randint(100, 1000) + while p % 2 == 0: + p = random.randint(100, 1000) + + max_noise = (1 << noise_bits) - 1 # 2^noise_bits - 1 + + a = [str(p * random.randint(1, 100) + random.randint(0, max_noise)) for _ in range(number)] + + print(noise_bits) + + for b in a: + print(b) + + print(f"// True p: {p}") + +if __name__ == "__main__": + parser = argparse.ArgumentParser(description='Generate test input for AGCD computation') + parser.add_argument('--noise-bits', type=int, default=5, help='Number of noise bits (default: 5)') + parser.add_argument('--number', type=int, default=20, help='Number of numbers to generate (default: 20)') + + args = parser.parse_args() + generate_test_file(args.noise_bits, args.number) diff --git a/src/agcd.rs b/src/agcd.rs index 8c0dda4..cf712ec 100644 --- a/src/agcd.rs +++ b/src/agcd.rs @@ -1,7 +1,7 @@ -use rug::Integer; use crate::matrix::Matrix; use crate::utils::abs; use lll_rs::l2::bigl2; +use rug::Integer; pub fn agcd(ciphertexts: Vec, noise_bits: usize) -> Integer { // 1. Build lattice matrix basis diff --git a/src/file.rs b/src/file.rs new file mode 100644 index 0000000..6848f8c --- /dev/null +++ b/src/file.rs @@ -0,0 +1,52 @@ +use std::fs; +use std::path::Path; +use rug::Integer; +use std::io; + +pub struct AgcdInput { + pub noise_bits: usize, + pub numbers: Vec, +} + +// Parse the input file, return noise_bits and numbers. +pub fn parse_file(path: &Path) -> io::Result { + let content = fs::read_to_string(path)?; + let mut lines = content.lines(); + + // Parse noise_bits from first non comment line + let noise_bits = loop { + match lines.next() { + Some(first_line) => { + let trimmed = first_line.trim(); + if trimmed.starts_with("//") { + continue; // Skip comment lines + } + break trimmed.parse::() + .map_err(|e| io::Error::new( + io::ErrorKind::InvalidData, + format!("First non-comment line must be noise_bits (usize): {}", e) + ))?; + }, + None => return Err(io::Error::new( + io::ErrorKind::InvalidData, + "Input file is empty or contains only comments" + )), + } + }; + + // Parse numbers from remaining lines + let numbers: Vec = lines + .filter(|line| !line.trim().starts_with("//") && !line.trim().is_empty()) // ignore comment and empty lines + .filter_map(|line| line.trim().parse::().ok()) + .map(Integer::from) + .collect(); + + if numbers.len() < 2 { + return Err(io::Error::new( + io::ErrorKind::InvalidData, + "Need at least 2 numbers to compute AGCD" + )); + } + + Ok(AgcdInput { noise_bits, numbers }) +} diff --git a/src/main.rs b/src/main.rs index 8d64b9c..99a64b4 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,32 +1,42 @@ -use rug::Integer; +mod agcd; mod lll; mod matrix; mod utils; -mod agcd; -use crate::agcd::agcd; +mod file; -fn main() { - // 1. Build lattice matrix basis - let ciphertexts = vec![ -Integer::from(32219), -Integer::from(21254), -Integer::from(16764), -Integer::from(338), -Integer::from(29960), -Integer::from(23516), -Integer::from(7084), -Integer::from(26735), -Integer::from(23195), -Integer::from(11928), -Integer::from(985), -Integer::from(11916), -Integer::from(13217), -Integer::from(29966), -Integer::from(14171), -Integer::from(13211), -Integer::from(23514), -Integer::from(19643) - ]; - let noise_bits = 2; - let _ = agcd(ciphertexts, noise_bits); +use crate::agcd::agcd; +use crate::file::parse_file; +use clap::{Parser, Subcommand}; +use std::path::{Path, PathBuf}; + +#[derive(Parser)] +#[command(author, version, about, long_about = None)] +#[command(propagate_version = true)] +struct Cli { + #[command(subcommand)] + command: Commands, +} + +#[derive(Subcommand)] +enum Commands { + /// approximate GCD + Agcd { + /// (default './input.txt') + path: Option, + }, +} + +fn main() -> std::io::Result<()> { + let cli = Cli::parse(); + + match &cli.command { + Commands::Agcd { path } => { + let path = path.as_deref().unwrap_or(Path::new("./input.txt")); + let input = parse_file(path)?; + + let result = agcd(input.numbers, input.noise_bits); + println!("Approximate GCD with noise_bits={}: {}", input.noise_bits, result); + } + } + Ok(()) }