diff --git a/.github/workflows/pages.yml b/.github/workflows/pages.yml index 3d758ca6..3b2bab3b 100644 --- a/.github/workflows/pages.yml +++ b/.github/workflows/pages.yml @@ -43,6 +43,15 @@ jobs: with: node-version: 22 + - uses: actions-rs/toolchain@v1 + with: + toolchain: stable + + - run: | + rustup default stable + rustup target add wasm32-unknown-unknown + cargo install wasm-bindgen + - name: Install git, linguist uses: awalsh128/cache-apt-pkgs-action@latest with: diff --git a/docs/Makefile b/docs/Makefile index b85cde3a..380f6f45 100644 --- a/docs/Makefile +++ b/docs/Makefile @@ -40,12 +40,16 @@ _static/dist/bundle.js: @mkdir -p _static/dist @cp ../javascript/dist/bundle-js.js _static/dist/bundle-js.js +_static/dist/rust.js: + @cd ../rust && cargo build --target wasm32-unknown-unknown --features "wasm" && \ + wasm-bindgen target/wasm32-unknown-unknown/release/rust.wasm --out-dir ../docs/dist/ + _static/dist/python.tar.gz: @$(MAKE) -C ../python webpack $(MFLAGS) .PHONY: html html: - @$(MAKE) _static/dist/bundle.js _static/dist/python.tar.gz $(MFLAGS) -j + @$(MAKE) _static/dist/bundle.js _static/dist/python.tar.gz _static/dist/rust.js $(MFLAGS) -j @$(SPHINXBUILD) -M html "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) # Catch-all target: route all unknown targets to Sphinx using the new diff --git a/docs/_static/test-rs.html b/docs/_static/test-rs.html new file mode 100644 index 00000000..8481e39a --- /dev/null +++ b/docs/_static/test-rs.html @@ -0,0 +1,73 @@ + + + + + + JavaScript Tests (Packaged with WebPack + Babel) + + + + + + + +
+ + + + + + + diff --git a/rust/Cargo.toml b/rust/Cargo.toml index 55664d99..1c957cae 100644 --- a/rust/Cargo.toml +++ b/rust/Cargo.toml @@ -9,6 +9,18 @@ itertools = "0.13.0" num-traits = "0.2.19" rstest = "0.21.0" seq-macro = "0.3.5" +js-sys = { version = "0.3", optional = true } + +[dependencies.wasm-bindgen] +version = "0.2" +optional = true + +[lib] +crate-type = ["cdylib"] # Necessary for WebAssembly builds + +[features] +default = [] +wasm = ["wasm-bindgen", "js-sys"] [profile.dev.package."*"] # Set the default for dependencies in Development mode. diff --git a/rust/src/include/mod.rs b/rust/src/include/mod.rs index ca12a900..5f05ec37 100644 --- a/rust/src/include/mod.rs +++ b/rust/src/include/mod.rs @@ -1,4 +1,5 @@ pub mod math; pub mod primes; +pub mod problems; pub mod triangles; pub mod utils; diff --git a/rust/src/include/problems.rs b/rust/src/include/problems.rs new file mode 100644 index 00000000..d523e467 --- /dev/null +++ b/rust/src/include/problems.rs @@ -0,0 +1,67 @@ +use seq_macro::seq; + +use crate::include::utils::Answer; +seq!(N in 0001..=0020 { +use crate::p~N::p~N; +}); +use crate::p0022::p0022; +use crate::p0024::p0024; +use crate::p0027::p0027; +use crate::p0034::p0034; +use crate::p0069::p0069; +use crate::p0076::p0076; +use crate::p0077::p0077; +use crate::p0087::p0087; +use crate::p0357::p0357; +use crate::p0836::p0836; + +type ProblemType = fn() -> Answer; +type ProblemRef<'a> = (&'a usize, ProblemType, bool); + +pub fn get_problem<'b>(n: usize) -> Option> { + return match n { + 1 => Some(( &1, p0001, false)), + 2 => Some(( &2, p0002, false)), + 3 => Some(( &3, p0003, false)), + 4 => Some(( &4, p0004, false)), + 5 => Some(( &5, p0005, false)), + 6 => Some(( &6, p0006, false)), + 7 => Some(( &7, p0007, false)), + 8 => Some(( &8, p0008, false)), + 9 => Some(( &9, p0009, false)), + 10 => Some(( &10, p0010, false)), + 11 => Some(( &11, p0011, false)), + 12 => Some(( &12, p0012, false)), + 13 => Some(( &13, p0013, false)), + 14 => Some(( &14, p0014, false)), + 15 => Some(( &15, p0015, false)), + 16 => Some(( &16, p0016, false)), + 17 => Some(( &17, p0017, false)), + 18 => Some(( &18, p0018, false)), + 19 => Some(( &19, p0019, false)), + 20 => Some(( &20, p0020, false)), + 22 => Some(( &22, p0022, false)), + 24 => Some(( &24, p0024, false)), + 27 => Some(( &27, p0027, false)), + 34 => Some(( &34, p0034, false)), + 69 => Some(( &69, p0069, false)), + 76 => Some(( &76, p0076, false)), + 77 => Some(( &77, p0077, false)), + 87 => Some(( &87, p0087, false)), + 357 => Some((&357, p0357, true)), + 836 => Some((&836, p0836, false)), + _ => None, + }; +} + + +pub fn generate_supported_problems() -> Vec { + let mut supported_problems = Vec::new(); + for n in 1..10000 { + if get_problem(n).is_some() { + supported_problems.push(n); + } + } + + return supported_problems; +} \ No newline at end of file diff --git a/rust/src/include/utils.rs b/rust/src/include/utils.rs index 67eccacd..f6c5103d 100644 --- a/rust/src/include/utils.rs +++ b/rust/src/include/utils.rs @@ -1,4 +1,6 @@ +#[cfg(not(feature = "wasm"))] use std::fs::read_to_string; +#[cfg(not(feature = "wasm"))] use std::path::Path; #[derive(Debug, PartialEq, Eq)] @@ -7,6 +9,27 @@ pub enum Answer { Int(i128), } +#[cfg(feature = "wasm")] +const ANSWERS_TSV: &str = include_str!("../../../_data/answers.tsv"); +#[cfg(feature = "wasm")] +const P0022_NAMES_TXT: &str = include_str!("../../../_data/p0022_names.txt"); +#[cfg(feature = "wasm")] +const P0042_WORDS_TXT: &str = include_str!("../../../_data/p0042_words.txt"); +#[cfg(feature = "wasm")] +const P0067_TRIANGLE_TXT: &str = include_str!("../../../_data/p0067_triangle.txt"); + +#[cfg(feature = "wasm")] +pub fn get_data_file(name: &str) -> String { + return match name { + "answers.tsv" => ANSWERS_TSV.to_string(), + "p0022_names.txt" => P0022_NAMES_TXT.to_string(), + "p0042_words.txt" => P0042_WORDS_TXT.to_string(), + "p0067_triangle.txt" => P0067_TRIANGLE_TXT.to_string(), + _ => panic!("Unknown file name: {}", name), + } +} + +#[cfg(not(feature = "wasm"))] pub fn get_data_file(name: &str) -> String { let data_file = Path::new(env!("CARGO_MANIFEST_DIR")).parent().unwrap().join("_data").join(name); return read_to_string(&data_file).unwrap(); diff --git a/rust/src/lib.rs b/rust/src/lib.rs new file mode 100644 index 00000000..81d13c86 --- /dev/null +++ b/rust/src/lib.rs @@ -0,0 +1,59 @@ +#![allow(unused_imports)] +#[cfg(feature = "wasm")] +use wasm_bindgen::prelude::*; + +#[cfg(feature = "wasm")] +use js_sys::Array; + +use seq_macro::seq; +seq!(N in 0001..=0020 { +pub mod p~N; +}); +pub mod p0022; +pub mod p0024; +pub mod p0027; +pub mod p0034; +pub mod p0069; +pub mod p0076; +pub mod p0077; +pub mod p0087; +pub mod p0357; +pub mod p0836; + +pub mod include; +pub use crate::include::problems::{generate_supported_problems,get_problem}; +pub use crate::include::utils::{Answer,get_answer}; + +#[cfg(feature = "wasm")] +#[wasm_bindgen] +pub fn run_problem(n: usize) -> JsValue { + let Some((_, problem_function, _)) = get_problem(n) else { panic!() }; + let answer = problem_function(); + return match answer { + Answer::String(e) => JsValue::from_str(&e), + Answer::Int(e) => JsValue::from(e), + } +} + +#[cfg(feature = "wasm")] +#[wasm_bindgen] +pub fn get_problems() -> JsValue { + let problems = generate_supported_problems(); + let js_array = Array::new_with_length(problems.len() as u32); + + for (i, &item) in problems.iter().enumerate() { + js_array.set(i as u32, item.into()); + } + + return JsValue::from(js_array); +} + +#[cfg(feature = "wasm")] +#[wasm_bindgen] +pub fn get_js_answer(n: usize) -> JsValue { + let answer = get_answer(n); + return match answer { + Answer::String(e) => JsValue::from_str(&e), + Answer::Int(e) => JsValue::from(e), + } +} diff --git a/rust/src/main.rs b/rust/src/main.rs index d3f0a36a..fbe774bb 100644 --- a/rust/src/main.rs +++ b/rust/src/main.rs @@ -13,61 +13,27 @@ use rstest::rstest; use itertools::Itertools; pub mod include; -use include::math; use include::primes; -use include::utils::{Answer,get_answer}; +#[cfg(not(test))] +use include::problems::generate_supported_problems; +use include::problems::get_problem; +#[cfg(not(test))] +use include::utils::Answer; +use include::utils::get_answer; seq!(N in 0001..=0020 { -mod p~N; +pub mod p~N; }); -mod p0022; -mod p0024; -mod p0027; -mod p0034; -mod p0069; -mod p0076; -mod p0077; -mod p0087; -mod p0357; -mod p0836; +pub mod p0022; +pub mod p0024; +pub mod p0027; +pub mod p0034; +pub mod p0069; +pub mod p0076; +pub mod p0077; +pub mod p0087; +pub mod p0357; +pub mod p0836; -type ProblemType = fn() -> Answer; -type ProblemRef<'a> = (&'a usize, ProblemType, bool); - -fn get_problem<'b>(n: usize) -> ProblemRef<'b> { - return match n { - 1 => ( &1, p0001::p0001, false), - 2 => ( &2, p0002::p0002, false), - 3 => ( &3, p0003::p0003, false), - 4 => ( &4, p0004::p0004, false), - 5 => ( &5, p0005::p0005, false), - 6 => ( &6, p0006::p0006, false), - 7 => ( &7, p0007::p0007, false), - 8 => ( &8, p0008::p0008, false), - 9 => ( &9, p0009::p0009, false), - 10 => ( &10, p0010::p0010, false), - 11 => ( &11, p0011::p0011, false), - 12 => ( &12, p0012::p0012, false), - 13 => ( &13, p0013::p0013, false), - 14 => ( &14, p0014::p0014, false), - 15 => ( &15, p0015::p0015, false), - 16 => ( &16, p0016::p0016, false), - 17 => ( &17, p0017::p0017, false), - 18 => ( &18, p0018::p0018, false), - 19 => ( &19, p0019::p0019, false), - 20 => ( &20, p0020::p0020, false), - 22 => ( &22, p0022::p0022, false), - 24 => ( &24, p0024::p0024, false), - 27 => ( &27, p0027::p0027, false), - 34 => ( &34, p0034::p0034, false), - 69 => ( &69, p0069::p0069, false), - 76 => ( &76, p0076::p0076, false), - 77 => ( &77, p0077::p0077, false), - 87 => ( &87, p0087::p0087, false), - 357 => (&357, p0357::p0357, true), - 836 => (&836, p0836::p0836, false), - _ => panic!(), - }; -} #[cfg(not(test))] fn main() { @@ -75,25 +41,20 @@ fn main() { for i in sieve { println!("{}", i); } - let mut answers: Vec = (1..=20).collect(); - answers.push(22); - answers.push(24); - answers.push(27); - answers.push(34); - answers.push(69); - answers.push(76); - answers.push(77); - answers.push(87); - answers.push(357); + let supported = generate_supported_problems(); - for id in answers { - let (_, func, _) = get_problem(id); - let answer = get_answer(id); - let result = func(); - match (answer, result) { - (Answer::String(e), Answer::String(r)) => println!("Problem {} should return {}. Returned {}!", id, e, r), - (Answer::Int(e), Answer::Int(r)) => println!("Problem {} should return {}. Returned {}!", id, e, r), - _ => panic!("Type mismatch in answer return"), + for id in supported { + match get_problem(id) { + Some((_, func, _)) => { + let answer = get_answer(id); + let result = func(); + match (answer, result) { + (Answer::String(e), Answer::String(r)) => println!("Problem {} should return {}. Returned {}!", id, e, r), + (Answer::Int(e), Answer::Int(r)) => println!("Problem {} should return {}. Returned {}!", id, e, r), + _ => panic!("Type mismatch in answer return"), + } + } + None => panic!("Problem not found"), } } } @@ -115,7 +76,7 @@ seq!(N in 01..=20 { // #[case::problem_357(357)] #[case::problem_836(836)] fn test_problem(#[case] id: usize) -> Result<(), String> { - let (_, func, _slow) = get_problem(id); + let Some((_, func, _slow)) = get_problem(id) else { panic!() }; let answer = get_answer(id); let start = Instant::now(); let result = func(); diff --git a/rust/src/p0003.rs b/rust/src/p0003.rs index e524c376..188b9eb8 100644 --- a/rust/src/p0003.rs +++ b/rust/src/p0003.rs @@ -9,7 +9,7 @@ The prime factors of 13195 are 5, 7, 13 and 29. What is the largest prime factor of the number 600851475143 ? */ -use crate::primes::prime_factors; +use crate::include::primes::prime_factors; use crate::include::utils::Answer; pub fn p0003() -> Answer { diff --git a/rust/src/p0007.rs b/rust/src/p0007.rs index 489e756d..f52e1fe2 100644 --- a/rust/src/p0007.rs +++ b/rust/src/p0007.rs @@ -9,7 +9,7 @@ By listing the first six prime numbers: 2, 3, 5, 7, > What is the 10 001st prime number? */ -use crate::primes::primes; +use crate::include::primes::primes; use crate::include::utils::Answer; pub fn p0007() -> Answer { diff --git a/rust/src/p0010.rs b/rust/src/p0010.rs index 3a927524..88447c1e 100644 --- a/rust/src/p0010.rs +++ b/rust/src/p0010.rs @@ -10,7 +10,7 @@ The sum of the primes below 10 is 2 + 3 + 5 + 7 = 17. Find the sum of all the primes below two million. */ -use crate::primes::primes; +use crate::include::primes::primes; use crate::include::utils::Answer; pub fn p0010() -> Answer { diff --git a/rust/src/p0012.rs b/rust/src/p0012.rs index 29cf7fa1..2f2304b1 100644 --- a/rust/src/p0012.rs +++ b/rust/src/p0012.rs @@ -27,7 +27,7 @@ We can see that 28 is the first triangle number to have over five divisors. What is the value of the first triangle number to have over five hundred divisors? */ -use crate::primes; +use crate::include::primes; use crate::include::utils::Answer; pub fn p0012() -> Answer { diff --git a/rust/src/p0015.rs b/rust/src/p0015.rs index 60c53aec..a286f565 100644 --- a/rust/src/p0015.rs +++ b/rust/src/p0015.rs @@ -10,7 +10,7 @@ The sum of the primes below 10 is 2 + 3 + 5 + 7 = 17. Find the sum of all the primes below two million. */ -use crate::math; +use crate::include::math; use crate::include::utils::Answer; pub fn p0015() -> Answer { diff --git a/rust/src/p0027.rs b/rust/src/p0027.rs index 7662242e..ae42eee3 100644 --- a/rust/src/p0027.rs +++ b/rust/src/p0027.rs @@ -27,7 +27,7 @@ Find the product of the coefficients, a and b, for the quadratic expression that produces the maximum number of primes for consecutive values of n, starting with n=0. */ -use crate::primes::{is_prime,primes_until}; +use crate::include::primes::{is_prime,primes_until}; use crate::include::utils::Answer; pub fn p0027() -> Answer { diff --git a/rust/src/p0034.rs b/rust/src/p0034.rs index 679b88b4..e0859247 100644 --- a/rust/src/p0034.rs +++ b/rust/src/p0034.rs @@ -14,7 +14,7 @@ their digits. Note: as 1! = 1 and 2! = 2 are not sums they are not included. */ -use crate::math::factorial; +use crate::include::math::factorial; use crate::include::utils::Answer; pub fn p0034() -> Answer { diff --git a/rust/src/p0069.rs b/rust/src/p0069.rs index b7299f14..1c951041 100644 --- a/rust/src/p0069.rs +++ b/rust/src/p0069.rs @@ -25,7 +25,7 @@ It can be seen that n=6 produces a maximum n/φ(n) for n ≤ 10. Find the value of n ≤ 1,000,000 for which n/φ(n) is a maximum. */ -use crate::primes::primes; +use crate::include::primes::primes; use crate::include::utils::Answer; pub fn p0069() -> Answer { diff --git a/rust/src/p0077.rs b/rust/src/p0077.rs index c9ec53ba..4c9f38ad 100644 --- a/rust/src/p0077.rs +++ b/rust/src/p0077.rs @@ -17,7 +17,7 @@ What is the first value which can be written as the sum of primes in over five t */ use core::iter::zip; -use crate::primes::primes_until; +use crate::include::primes::primes_until; use crate::include::utils::Answer; diff --git a/rust/src/p0087.rs b/rust/src/p0087.rs index d9a629f6..6d75f49e 100644 --- a/rust/src/p0087.rs +++ b/rust/src/p0087.rs @@ -26,7 +26,7 @@ square, prime cube, and prime fourth power? */ use std::collections::HashSet; -use crate::primes::primes_until; +use crate::include::primes::primes_until; use crate::include::utils::Answer; diff --git a/rust/src/p0357.rs b/rust/src/p0357.rs index 501f2abb..63193c7f 100644 --- a/rust/src/p0357.rs +++ b/rust/src/p0357.rs @@ -13,7 +13,7 @@ such that for every divisor d of n, d+n/d is prime. */ use std::collections::HashSet; -use crate::primes::{is_prime,primes_until,proper_divisors}; +use crate::include::primes::{is_prime,primes_until,proper_divisors}; use crate::include::utils::Answer; pub fn p0357() -> Answer {