diff --git a/Cargo.lock b/Cargo.lock index b056181..bdc1fc5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -519,6 +519,12 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7655c9839580ee829dfacba1d1278c2b7883e50a277ff7541299489d6bdfdc45" +[[package]] +name = "itoa" +version = "1.0.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1a46d1a171d865aa5f83f92695765caa047a9b4cbae2cbf37dbd613a793fd4c" + [[package]] name = "libc" version = "0.2.153" @@ -581,6 +587,28 @@ dependencies = [ "syn", ] +[[package]] +name = "mini-internal" +version = "0.1.38" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f09fa787e06d071d09c2964b065eb7cb1b842a7af5382fc4c9142089f8383f08" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "miniserde" +version = "0.1.38" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e19a2e17a11c24d44c84d7e1b61477e20cd503d024e4b0eb3e97eb9a4d24fa87" +dependencies = [ + "itoa", + "mini-internal", + "ryu", +] + [[package]] name = "miniz_oxide" version = "0.7.2" @@ -685,6 +713,7 @@ version = "0.1.0" dependencies = [ "futures", "miette", + "miniserde", "smol", ] @@ -730,6 +759,12 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "ryu" +version = "1.0.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e86697c916019a8588c99b5fac3cead74ec0b4b819707a682fd4d23fa0ce1ba1" + [[package]] name = "signal-hook-registry" version = "1.4.1" diff --git a/Cargo.toml b/Cargo.toml index 7edcf37..f4850f8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,4 +6,5 @@ edition = "2021" [dependencies] futures = "0.3.30" miette = { version = "5", features = ["fancy"] } +miniserde = "0.1.38" smol = "1.3.0" diff --git a/src/main.rs b/src/main.rs index 5ab77da..2ec8c79 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,9 +1,161 @@ -use futures::{AsyncReadExt, StreamExt}; +use futures::{ + future::ready, io::BufReader, AsyncBufReadExt, AsyncReadExt, AsyncWriteExt, StreamExt, + TryStreamExt, +}; use miette::IntoDiagnostic; +use miniserde::{json::Number, Deserialize, Serialize}; + +fn pow(a: u64, d: u64, n: u64) -> u64 { + let n: u128 = n.into(); + let mut d: u128 = d.into(); + let mut x: u128 = 1; + let mut q: u128 = a.into(); + while d != 0 { + if d & 1 == 1 { + x *= q; + x %= n; + } + q *= q; + q %= n; + d >>= 1; + } + x as _ +} + +fn square(x: u64, n: u64) -> u64 { + let n: u128 = n.into(); + let mut x: u128 = x.into(); + x *= x; + x %= n; + x as _ +} + +fn is_prime(n: u64) -> bool { + const LOG_2_TO_E: [u64; 65] = [ + 0, 1, 2, 3, 3, 4, 5, 5, 6, 7, 7, 8, 9, 10, 10, 11, 12, 12, 13, 14, 14, 15, 16, 16, 17, 18, + 19, 19, 20, 21, 21, 22, 23, 23, 24, 25, 25, 26, 27, 28, 28, 29, 30, 30, 31, 32, 32, 33, 34, + 34, 35, 36, 37, 37, 38, 39, 39, 40, 41, 41, 42, 43, 43, 44, 45, + ]; + + match n { + 0 => false, + 1 => false, + 2 => true, + 3.. => { + let n1 = n - 1; + let s = n1.trailing_zeros(); + let d = n1 >> s; + let log_e_approx = LOG_2_TO_E[(n.ilog2() + 1) as usize]; + for a in 2..=(n - 2).min(2 * log_e_approx * log_e_approx) { + let mut x = pow(a, d, n); + for _ in 0..s { + let y = square(x, n); + if y == 1 && x != 1 && x != n1 { + return false; + } + x = y; + } + if x != 1 { + return false; + } + } + true + } + } +} + +#[test] +fn primes() { + assert!(!is_prime(0)); + assert!(!is_prime(1)); + assert!(is_prime(2)); + assert!(is_prime(3)); + assert!(!is_prime(4)); + assert!(is_prime(5)); + assert!(!is_prime(6)); + assert!(is_prime(7)); + assert!(!is_prime(8)); + assert!(!is_prime(9)); + assert!(!is_prime(10)); + assert!(is_prime(11)); + assert!(!is_prime(12)); + assert!(is_prime(13)); + assert!(!is_prime(14)); + assert!(!is_prime(15)); + assert!(!is_prime(16)); + assert!(is_prime(17)); + assert!(!is_prime(18)); + assert!(is_prime(19)); + assert!(!is_prime(20)); + assert!(!is_prime(21)); + assert!(!is_prime(22)); + assert!(is_prime(23)); + assert!(!is_prime(57)); + assert!(is_prime(97)); + assert!(is_prime(101)); + assert!(is_prime(179)); + assert!(!is_prime(216)); + assert!(!is_prime(426)); + assert!(is_prime(997)); + assert!(is_prime(65537)); +} + +#[derive(Debug, Serialize, Deserialize)] +enum Method { + #[serde(rename = "isPrime")] + IsPrime, +} + +#[derive(Debug, Deserialize)] +struct Request { + method: Method, + number: Number, +} + +#[derive(Debug, Serialize)] +struct Response { + method: Method, + prime: bool, +} + +fn json_prime(number: Number) -> bool { + match number { + Number::U64(n) => is_prime(n), + _ => false, + } +} + +fn try_process(input: String) -> miniserde::Result { + let Request { + method: Method::IsPrime, + number, + } = miniserde::json::from_str(&input)?; + let response = Response { + method: Method::IsPrime, + prime: json_prime(number), + }; + Ok(miniserde::json::to_string(&response)) +} + +fn process(input: String) -> String { + try_process(input).unwrap_or_default() +} async fn handle(stream: smol::io::Result) -> smol::io::Result<()> { - let (reader, mut writer) = stream?.split(); - futures::io::copy(reader, &mut writer).await.map(|_| {}) + let (reader, writer) = stream?.split(); + let mut alive = true; + BufReader::new(reader) + .lines() + .map_ok(process) + .take_while(|item| { + ready(match item { + Ok(s) if s.is_empty() => std::mem::take(&mut alive), + _ => alive, + }) + }) + .map_ok(|s| s + "\n") + .forward(writer.into_sink::()) + .await } fn main() -> miette::Result<()> {