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
#![feature(isqrt)]
#![feature(try_blocks)]

use std::sync::mpsc::{channel, Receiver};
use std::thread::spawn;
use std::time::SystemTime;

use clap::Parser;
use num_bigint::BigInt;
use rayon::prelude::*;

#[derive(clap::Parser, Debug)]
struct Args {
    #[arg(default_value = "0", short, long)]
    start: u128,
    #[arg(default_value = "340282366920938463463374607431768211455", short, long)]
    end: u128,
    /// Enable benchmark stopwatch timer
    #[arg(short, long)]
    benchmark: bool,
    /// Disable stdout printing
    #[arg(short, long)]
    no_print: bool,
}

fn main() {
    let args = Args::parse();

    let start_time = SystemTime::now();

    let results = run(args.start, args.end);
    for (n, p1, p2) in results {
        let verify = verify(n, p1, p2);
        debug_assert!(verify, "This is expected to be true");
        if !verify {
            continue;
        }
        if !args.no_print {
            println!("{n}: {p1}|{p2}");
        }
    }

    if args.benchmark {
        let duration = SystemTime::now().duration_since(start_time).unwrap();
        println!("Runtime: {:?}", duration);
    }
}

fn run(start: u128, end: u128) -> Receiver<(u128, u128, u128)> {
    let (tx, rx) = channel();

    spawn(move || {
        let tx = tx;
        let base_max = end.isqrt();
        let base_min = start.isqrt();
        let base_range = base_min..=base_max;
        base_range
            .into_par_iter()
            .map(|x| x * x)
            .for_each_with(tx, |s, x| {
                if let Some(r) = check_split(x) {
                    s.send((x, r.0, r.1)).unwrap();
                }
            });
    });
    rx
}

#[inline(always)]
fn check_split(n: u128) -> Option<(u128, u128)> {
    let mut i = 1_u32;
    loop {
        let m = 10_u128.pow(i);
        let part1 = n / m;
        let part2 = n % m;
        if (part1 + part2).checked_mul(part1 + part2) == Some(n) {
            return Some((part1, part2));
        }
        if part1 < 10 {
            break None;
        }
        i += 1;
    }
}

fn verify(n: u128, p1: u128, p2: u128) -> bool {
    (BigInt::from(p1) + BigInt::from(p2)).pow(2) == BigInt::from(n)
}