1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
use clap::Parser;
use rustutils_runnable::Runnable;
use std::error::Error;

/// Print the prime factors of each specified integer number.
#[derive(Parser, Clone, Debug)]
#[clap(author, version, about, long_about = None)]
pub struct Factor {
    /// Print unique factors.
    #[clap(short, long)]
    unique: bool,
    /// Print output as JSON.
    #[clap(short, long)]
    json: bool,
    /// Number to print the prime factors of.
    ///
    /// When none are specified on the command line, read them from standard input.
    number: Vec<u64>,
}

#[derive(thiserror::Error, Debug)]
pub enum StdinError {
    #[error("Error reading a line from standard input: {0:}")]
    ReadingLine(#[from] std::io::Error),
    #[error("Error parsing {0:?} as a number: {1:}")]
    ParsingNumber(String, std::num::ParseIntError),
}

impl Factor {
    pub fn run(&self) -> Result<(), Box<dyn Error>> {
        if self.json {
            // when outputting to JSON, we collect the output into this map, and then
            // JSON-encode it at the end.
            let mut output = std::collections::BTreeMap::new();
            self.handle(|num, factors| {
                output.insert(num.to_string(), factors.to_vec());
            })?;
            println!("{}", serde_json::to_string(&output)?);
        } else {
            self.handle(|num, factors| {
                let factors: String = factors.iter().map(|factor| format!(" {factor}")).collect();
                println!("{num}:{}", factors);
            })?;
        }
        Ok(())
    }

    /// Handle either the supplied numbers, or read from standard input if no numbers were
    /// supplied.
    pub fn handle(&self, callback: impl FnMut(u64, &[u64])) -> Result<(), Box<dyn Error>> {
        if self.number.is_empty() {
            self.handle_stdin(callback)?;
        } else {
            self.handle_numbers(callback);
        }
        Ok(())
    }

    /// Perform factorisation on the numbers supplied as arguments.
    pub fn handle_numbers(&self, mut callback: impl FnMut(u64, &[u64])) {
        for number in &self.number {
            let factors = self.factor(*number);
            callback(*number, &factors);
        }
    }

    /// Read lines from standard input, parse them as numbers, and perform factorisation on
    /// them.
    pub fn handle_stdin(&self, mut callback: impl FnMut(u64, &[u64])) -> Result<(), StdinError> {
        let stdin = std::io::stdin();
        for line in stdin.lines() {
            let line = line.map_err(|e| StdinError::ReadingLine(e))?;
            let number: u64 = line
                .parse()
                .map_err(|e| StdinError::ParsingNumber(line, e))?;
            let factors = self.factor(number);
            callback(number, &factors);
        }
        Ok(())
    }

    /// Given a number, determine the prime factors. If self.unique is true, it
    /// will only return unique factors, instead of all.
    pub fn factor(&self, number: u64) -> Vec<u64> {
        if self.unique {
            primes::factors_uniq(number)
        } else {
            primes::factors(number)
        }
    }
}

impl Runnable for Factor {
    fn run(&self) -> Result<(), Box<dyn Error>> {
        self.run()?;
        Ok(())
    }
}