rustutils_factor/
lib.rs

1use clap::Parser;
2use rustutils_runnable::Runnable;
3use std::error::Error;
4
5/// Print the prime factors of each specified integer number.
6#[derive(Parser, Clone, Debug)]
7#[clap(author, version, about, long_about = None)]
8pub struct Factor {
9    /// Print unique factors.
10    #[clap(short, long)]
11    unique: bool,
12    /// Print output as JSON.
13    #[clap(short, long)]
14    json: bool,
15    /// Number to print the prime factors of.
16    ///
17    /// When none are specified on the command line, read them from standard input.
18    number: Vec<u64>,
19}
20
21#[derive(thiserror::Error, Debug)]
22pub enum StdinError {
23    #[error("Error reading a line from standard input: {0:}")]
24    ReadingLine(#[from] std::io::Error),
25    #[error("Error parsing {0:?} as a number: {1:}")]
26    ParsingNumber(String, std::num::ParseIntError),
27}
28
29impl Factor {
30    pub fn run(&self) -> Result<(), Box<dyn Error>> {
31        if self.json {
32            // when outputting to JSON, we collect the output into this map, and then
33            // JSON-encode it at the end.
34            let mut output = std::collections::BTreeMap::new();
35            self.handle(|num, factors| {
36                output.insert(num.to_string(), factors.to_vec());
37            })?;
38            println!("{}", serde_json::to_string(&output)?);
39        } else {
40            self.handle(|num, factors| {
41                let factors: String = factors.iter().map(|factor| format!(" {factor}")).collect();
42                println!("{num}:{}", factors);
43            })?;
44        }
45        Ok(())
46    }
47
48    /// Handle either the supplied numbers, or read from standard input if no numbers were
49    /// supplied.
50    pub fn handle(&self, callback: impl FnMut(u64, &[u64])) -> Result<(), Box<dyn Error>> {
51        if self.number.is_empty() {
52            self.handle_stdin(callback)?;
53        } else {
54            self.handle_numbers(callback);
55        }
56        Ok(())
57    }
58
59    /// Perform factorisation on the numbers supplied as arguments.
60    pub fn handle_numbers(&self, mut callback: impl FnMut(u64, &[u64])) {
61        for number in &self.number {
62            let factors = self.factor(*number);
63            callback(*number, &factors);
64        }
65    }
66
67    /// Read lines from standard input, parse them as numbers, and perform factorisation on
68    /// them.
69    pub fn handle_stdin(&self, mut callback: impl FnMut(u64, &[u64])) -> Result<(), StdinError> {
70        let stdin = std::io::stdin();
71        for line in stdin.lines() {
72            let line = line.map_err(|e| StdinError::ReadingLine(e))?;
73            let number: u64 = line
74                .parse()
75                .map_err(|e| StdinError::ParsingNumber(line, e))?;
76            let factors = self.factor(number);
77            callback(number, &factors);
78        }
79        Ok(())
80    }
81
82    /// Given a number, determine the prime factors. If self.unique is true, it
83    /// will only return unique factors, instead of all.
84    pub fn factor(&self, number: u64) -> Vec<u64> {
85        if self.unique {
86            primes::factors_uniq(number)
87        } else {
88            primes::factors(number)
89        }
90    }
91}
92
93impl Runnable for Factor {
94    fn run(&self) -> Result<(), Box<dyn Error>> {
95        self.run()?;
96        Ok(())
97    }
98}