feat: rewrite tooling
This commit is contained in:
parent
19742c35b0
commit
7945bdd0ce
16 changed files with 754 additions and 915 deletions
2
.gitignore
vendored
2
.gitignore
vendored
|
@ -1,3 +1 @@
|
|||
/target
|
||||
/binaries
|
||||
/old
|
||||
|
|
6
.vscode/extensions.json
vendored
Normal file
6
.vscode/extensions.json
vendored
Normal file
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"recommendations": [
|
||||
"vadimcn.vscode-lldb",
|
||||
"rust-lang.rust-analyzer"
|
||||
]
|
||||
}
|
26
.vscode/launch.json
vendored
Normal file
26
.vscode/launch.json
vendored
Normal file
|
@ -0,0 +1,26 @@
|
|||
{
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
{
|
||||
"type": "lldb",
|
||||
"request": "launch",
|
||||
"name": "Debug executable 'euler'",
|
||||
"cargo": {
|
||||
"args": [
|
||||
"build",
|
||||
"--bin=euler",
|
||||
"--package=euler"
|
||||
],
|
||||
"filter": {
|
||||
"name": "euler",
|
||||
"kind": "bin"
|
||||
}
|
||||
},
|
||||
"args": [
|
||||
"new",
|
||||
"30"
|
||||
],
|
||||
"cwd": "${workspaceFolder}"
|
||||
}
|
||||
]
|
||||
}
|
850
Cargo.lock
generated
850
Cargo.lock
generated
File diff suppressed because it is too large
Load diff
20
Cargo.toml
20
Cargo.toml
|
@ -5,17 +5,19 @@ edition = "2021"
|
|||
default-run = "euler"
|
||||
|
||||
[dependencies]
|
||||
clap = { version = "4.0.4", features = ["derive"] }
|
||||
scraper = "0.14.0"
|
||||
requestty = "0.4.1"
|
||||
reqwest = "0.11"
|
||||
tokio = { version = "1.23", features = ["full"] }
|
||||
regex = "1"
|
||||
clap = { version = "4.4.10", features = ["derive"] }
|
||||
scraper = "0.18.1"
|
||||
requestty = "0.5.0"
|
||||
reqwest = "0.11.22"
|
||||
tokio = { version = "1.34.0", features = ["full"] }
|
||||
regex = "1.10.2"
|
||||
html-escape = "0.2.13"
|
||||
phf = { version = "0.11.1", features = ["macros"] }
|
||||
rayon = "1.7.0"
|
||||
phf = { version = "0.11.2", features = ["macros"] }
|
||||
rayon = "1.8.0"
|
||||
owo-colors = "3.5.0"
|
||||
num-bigint = "0.4.3"
|
||||
num-bigint = "0.4.4"
|
||||
once_cell = "1.18.0"
|
||||
num-to-words = "0.1.1"
|
||||
|
||||
[profile.release]
|
||||
opt-level = "z"
|
||||
|
|
14
build
14
build
|
@ -1,14 +0,0 @@
|
|||
#!/bin/bash
|
||||
|
||||
cargo build --release --bin "[0-9]*" -q
|
||||
cargo build --release --bin "[0-9]*" -q --target x86_64-pc-windows-gnu
|
||||
|
||||
find ./target/release -maxdepth 1 -type f -perm 755 | while read file; do
|
||||
arr=(${file//\// })
|
||||
mv $file binaries/linux/${arr[3]} > /dev/null
|
||||
done
|
||||
|
||||
find ./target/x86_64-pc-windows-gnu/release -maxdepth 1 -type f -perm 755 | while read file; do
|
||||
arr=(${file//\// })
|
||||
mv $file binaries/win64/${arr[4]} > /dev/null
|
||||
done
|
|
@ -16,7 +16,7 @@ All of the solutions here are written in rust. [main.rs](src/main.rs) is the hom
|
|||
|
||||
## Challenge Completion
|
||||
|
||||
### <!-- completed -->22<!-- completed --> out of 100 public challenges completed.
|
||||
### 22 out of 100 public challenges completed.
|
||||
|
||||
- [x] 1 - [Multiples of 3 or 5](src/bin/1.rs)
|
||||
- [x] 2 - [Even Fibonacci numbers](src/bin/2.rs)
|
||||
|
@ -119,4 +119,4 @@ All of the solutions here are written in rust. [main.rs](src/main.rs) is the hom
|
|||
- [ ] 99 - Largest exponential
|
||||
- [ ] 100 - Arranged probability
|
||||
|
||||
<sub>Check out Project Euler <a href="https://projecteuler.net">here</a>.</sub>
|
||||
<sub>Check out Project Euler <a href="https://projecteuler.net">here</a>.</sub>
|
||||
|
|
|
@ -4,6 +4,7 @@ Problem 16 - Power digit sum
|
|||
2 15 = 32768 and the sum of its digits is 3 + 2 + 7 + 6 + 8 = 26.
|
||||
What is the sum of the digits of the number 2 1000 ?
|
||||
*/
|
||||
|
||||
use num_bigint::BigUint;
|
||||
|
||||
fn get_digits(number: usize) -> Vec<u32> {
|
||||
|
|
|
@ -5,11 +5,13 @@ Using names.txt (right click and 'Save Link/Target As...'), a 46K text file co
|
|||
For example, when the list is sorted into alphabetical order, COLIN, which is worth 3 + 15 + 12 + 9 + 14 = 53, is the 938th name in the list. So, COLIN would obtain a score of 938 × 53 = 49714.
|
||||
What is the total of all the name scores in the file?
|
||||
*/
|
||||
|
||||
use euler::RESOURCES;
|
||||
use regex::Regex;
|
||||
use std::fs;
|
||||
|
||||
fn read_names() -> Vec<String> {
|
||||
let mut names = fs::read_to_string(euler::resources_path().join("p022_names.txt"))
|
||||
let mut names = fs::read_to_string(RESOURCES.join("p022_names.txt"))
|
||||
.unwrap()
|
||||
.split(",")
|
||||
.map(|name| Regex::new("\"").unwrap().replace_all(name, "").to_string())
|
||||
|
|
|
@ -7,11 +7,12 @@ By converting each letter in a word to a number corresponding to its alphabetica
|
|||
Using words.txt (right click and 'Save Link/Target As...'), a 16K text file containing nearly two-thousand common English words, how many are triangle words?
|
||||
*/
|
||||
|
||||
use euler::RESOURCES;
|
||||
use regex::Regex;
|
||||
use std::fs;
|
||||
|
||||
fn read_words() -> Vec<String> {
|
||||
return fs::read_to_string(euler::resources_path().join("p042_words.txt"))
|
||||
return fs::read_to_string(RESOURCES.join("p042_words.txt"))
|
||||
.unwrap()
|
||||
.split(",")
|
||||
.map(|name| {
|
||||
|
|
|
@ -9,10 +9,12 @@ That is, 3 + 7 + 4 + 9 = 23.
|
|||
Find the maximum total from top to bottom in triangle.txt (right click and 'Save Link/Target As...'), a 15K text file containing a triangle with one-hundred rows.
|
||||
NOTE: This is a much more difficult version of Problem 18 . It is not possible to try every route to solve this problem, as there are 2 99 altogether! If you could check one trillion (10 12 ) routes every second it would take over twenty billion years to check them all. There is an efficient algorithm to solve it. ;o)
|
||||
*/
|
||||
|
||||
use euler::RESOURCES;
|
||||
use std::fs;
|
||||
|
||||
fn read_triangle() -> Vec<Vec<usize>> {
|
||||
return fs::read_to_string(euler::resources_path().join("p067_triangle.txt"))
|
||||
return fs::read_to_string(RESOURCES.join("p067_triangle.txt"))
|
||||
.unwrap()
|
||||
.split("\n")
|
||||
.map(|name| {
|
||||
|
|
|
@ -1,176 +1,107 @@
|
|||
use euler::{Problem, Result, BASE_DIR, README, SOLUTIONS};
|
||||
use num_to_words::integer_to_en_us as num_to_words;
|
||||
use owo_colors::OwoColorize;
|
||||
use regex::Regex;
|
||||
use scraper::{Html, Selector};
|
||||
use std::fs;
|
||||
use std::io::BufReader;
|
||||
use std::{
|
||||
fs::{File, OpenOptions},
|
||||
io::{BufRead, Write},
|
||||
};
|
||||
|
||||
#[tokio::main]
|
||||
pub async fn execute(problem: Option<u8>) -> Result<(), Box<dyn std::error::Error>> {
|
||||
let code_path = euler::code_path();
|
||||
let problem_number = euler::problem_number(
|
||||
problem,
|
||||
&code_path,
|
||||
"Which problem would you like to solve?",
|
||||
);
|
||||
|
||||
// Fetch the problem information
|
||||
let body = reqwest::get(format!("https://projecteuler.net/problem={problem_number}"))
|
||||
.await?
|
||||
.text()
|
||||
.await?;
|
||||
|
||||
let document = Html::parse_document(body.as_str());
|
||||
let title_selector = Selector::parse("h2")?;
|
||||
let content_selector = Selector::parse(".problem_content")?;
|
||||
let html_tag_regex = Regex::new(r"<[^>]*>")?;
|
||||
|
||||
let mut title = document
|
||||
.select(&title_selector)
|
||||
.next()
|
||||
.unwrap()
|
||||
.text()
|
||||
.collect::<Vec<&str>>()
|
||||
.join("");
|
||||
|
||||
let mut problem = html_tag_regex
|
||||
.replace_all(
|
||||
document
|
||||
.select(&content_selector)
|
||||
.next()
|
||||
.unwrap()
|
||||
.inner_html()
|
||||
.as_str(),
|
||||
" ",
|
||||
)
|
||||
.to_string()
|
||||
.replace("$$", " ");
|
||||
|
||||
let file_body = format!(
|
||||
"/*
|
||||
Problem {} - {}
|
||||
|
||||
{}
|
||||
*/
|
||||
|
||||
pub fn main() {{
|
||||
println!(\"Hello World!\");
|
||||
}}",
|
||||
problem_number,
|
||||
html_escape::decode_html_entities(&mut title).to_string(),
|
||||
html_escape::decode_html_entities(&mut problem)
|
||||
.split("\n")
|
||||
.map(|s| s.trim())
|
||||
.filter(|s| s != &"")
|
||||
.collect::<Vec<&str>>()
|
||||
.join("\n")
|
||||
);
|
||||
|
||||
// Create the file
|
||||
let mut file = File::create(code_path.join(format!("{problem_number}.rs")))?;
|
||||
file.write(file_body.as_bytes())?;
|
||||
pub async fn execute(number: Option<u8>) -> Result<()> {
|
||||
let problem = Problem::new(number).await?;
|
||||
|
||||
// create a file for the problem
|
||||
let mut file = File::create(SOLUTIONS.join(format!("{}.rs", problem.number)))?;
|
||||
file.write(problem.file_body().as_bytes())?;
|
||||
drop(file);
|
||||
|
||||
// Read the contents of the readme for editing
|
||||
let readme_path = euler::readme_path();
|
||||
let mut readme_file = OpenOptions::new().read(true).open(&readme_path)?;
|
||||
// open readme
|
||||
let mut readme_file = OpenOptions::new().read(true).open(README.clone())?;
|
||||
|
||||
let mut readme_content = BufReader::new(&readme_file)
|
||||
.lines()
|
||||
.map(|s| s.unwrap())
|
||||
.collect::<Vec<String>>();
|
||||
let readme_content = {
|
||||
// read
|
||||
let mut content = BufReader::new(&readme_file)
|
||||
.lines()
|
||||
.map(|s| s.unwrap())
|
||||
.collect::<Vec<String>>()
|
||||
.join("\n");
|
||||
|
||||
drop(readme_file);
|
||||
|
||||
// mark problem as done on readme
|
||||
content = content.replace(
|
||||
&format!("\n- [ ] {}", problem.number),
|
||||
&format!("\n- [x] {}", problem.number),
|
||||
);
|
||||
|
||||
// update completed count
|
||||
let completion_regex = Regex::new(r"(\d+) out of")?;
|
||||
let problem_count = fs::read_dir(SOLUTIONS.clone())?.count();
|
||||
|
||||
content = completion_regex
|
||||
.replace(&content, format!("{} out of", problem_count))
|
||||
.to_string();
|
||||
|
||||
content
|
||||
};
|
||||
|
||||
// write the new content to the readme
|
||||
readme_file = OpenOptions::new().write(true).open(README.clone())?;
|
||||
readme_file.write(readme_content.as_bytes())?;
|
||||
drop(readme_file);
|
||||
|
||||
// Mark the problem as done on the readme
|
||||
let readme_regex = Regex::new(format!(" {problem_number} - (.*)").as_str())?;
|
||||
// add the problem to the run command
|
||||
let mut run_file = OpenOptions::new()
|
||||
.read(true)
|
||||
.open(BASE_DIR.join("src").join("commands").join("run.rs"))?;
|
||||
|
||||
for i in 0..readme_content.len() {
|
||||
let line = readme_content[i].as_str();
|
||||
let run_content = {
|
||||
let mut content: String = BufReader::new(&run_file)
|
||||
.lines()
|
||||
.map(|s| s.unwrap())
|
||||
.collect::<Vec<String>>()
|
||||
.join("\n");
|
||||
drop(run_file);
|
||||
|
||||
if readme_regex.is_match(line) {
|
||||
let matched = readme_regex.captures(line).unwrap();
|
||||
readme_content[i] = format!(
|
||||
"- [x] {problem_number} - [{}](src/bin/{problem_number}.rs)",
|
||||
&matched[1].trim()
|
||||
);
|
||||
}
|
||||
}
|
||||
// add new mod
|
||||
let mod_regex = Regex::new(r#"#\[path = ".+"\]\nmod [A-Za-z_]+;"#)?;
|
||||
let final_mod = mod_regex.find_iter(&content).last().unwrap();
|
||||
let word_number = num_to_words(problem.number as i64)?.replace(" ", "_");
|
||||
|
||||
// Update the summary statistics on the readme
|
||||
let mut readme_string = readme_content.join("\n");
|
||||
content.insert_str(
|
||||
final_mod.end(),
|
||||
&format!(
|
||||
r#"
|
||||
#[path = "../bin/{}.rs"]
|
||||
mod {};"#,
|
||||
problem.number, word_number
|
||||
),
|
||||
);
|
||||
|
||||
let completed_regex = Regex::new("<!-- completed -->([0-9]+)<!-- completed -->")?;
|
||||
// add to match
|
||||
let match_regex = Regex::new(r"\d+ => .+\(\),")?;
|
||||
let final_branch = match_regex.find_iter(&content).last().unwrap();
|
||||
|
||||
let new_completed =
|
||||
completed_regex.captures(readme_string.as_str()).unwrap()[1].parse::<u8>()? + 1;
|
||||
content.insert_str(
|
||||
final_branch.end(),
|
||||
&format!(
|
||||
r#"
|
||||
{} => {}::main(),"#,
|
||||
problem.number, word_number
|
||||
),
|
||||
);
|
||||
|
||||
readme_string = completed_regex
|
||||
.replace(
|
||||
readme_string.as_str(),
|
||||
format!("<!-- completed -->{new_completed}<!-- completed -->"),
|
||||
)
|
||||
.to_string();
|
||||
|
||||
// Write the new content to the readme
|
||||
readme_file = OpenOptions::new().write(true).open(&readme_path)?;
|
||||
|
||||
readme_file.write(readme_string.as_bytes())?;
|
||||
|
||||
drop(readme_file);
|
||||
|
||||
// Modify the run command
|
||||
let run_path = euler::run_path();
|
||||
let mut run_file = OpenOptions::new().read(true).open(&run_path)?;
|
||||
|
||||
let mut run_content = BufReader::new(&run_file)
|
||||
.lines()
|
||||
.map(|s| s.unwrap())
|
||||
.collect::<Vec<String>>();
|
||||
|
||||
drop(run_file);
|
||||
|
||||
// Import the problem's file
|
||||
let import_regex = Regex::new(r#"// #\[path = "\.\./bin/{x}\.rs"]"#.replace("{x}", problem_number.to_string().as_str()).as_str())?;
|
||||
|
||||
for i in 0..run_content.len() {
|
||||
let line = run_content[i].as_str();
|
||||
|
||||
if import_regex.is_match(line) {
|
||||
run_content[i] = line.replace("// ", "");
|
||||
run_content[i + 1] = run_content[i + 1].replace("// ", "");
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Add the problem to the match statement
|
||||
let match_regex = Regex::new(
|
||||
r"// {x} =>"
|
||||
.replace("{x}", problem_number.to_string().as_str())
|
||||
.as_str(),
|
||||
)?;
|
||||
|
||||
for i in 0..run_content.len() {
|
||||
let line = run_content[i].as_str();
|
||||
|
||||
if match_regex.is_match(line) {
|
||||
run_content[i] = line.replace("// ", "");
|
||||
break;
|
||||
}
|
||||
}
|
||||
content
|
||||
};
|
||||
|
||||
// Write the new content to the run file
|
||||
run_file = OpenOptions::new()
|
||||
.write(true)
|
||||
.truncate(true)
|
||||
.open(&run_path)?;
|
||||
|
||||
run_file.write(run_content.join("\n").as_bytes())?;
|
||||
|
||||
.open(BASE_DIR.join("src").join("commands").join("run.rs"))?;
|
||||
run_file.write(run_content.as_bytes())?;
|
||||
drop(run_file);
|
||||
|
||||
// Announce completion!
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
use euler::Problem;
|
||||
use owo_colors::OwoColorize;
|
||||
use std::time::Instant;
|
||||
|
||||
#[path = "../bin/8.rs"]
|
||||
mod eight;
|
||||
|
@ -10,6 +12,8 @@ mod eleven;
|
|||
mod fifteen;
|
||||
#[path = "../bin/5.rs"]
|
||||
mod five;
|
||||
#[path = "../bin/42.rs"]
|
||||
mod forty_two;
|
||||
#[path = "../bin/4.rs"]
|
||||
mod four;
|
||||
#[path = "../bin/14.rs"]
|
||||
|
@ -26,6 +30,8 @@ mod seventeen;
|
|||
mod six;
|
||||
#[path = "../bin/16.rs"]
|
||||
mod sixteen;
|
||||
#[path = "../bin/67.rs"]
|
||||
mod sixty_seven;
|
||||
#[path = "../bin/10.rs"]
|
||||
mod ten;
|
||||
#[path = "../bin/13.rs"]
|
||||
|
@ -34,186 +40,29 @@ mod thirteen;
|
|||
mod three;
|
||||
#[path = "../bin/12.rs"]
|
||||
mod twelve;
|
||||
#[path = "../bin/2.rs"]
|
||||
mod two;
|
||||
// #[path = "../bin/19.rs"]
|
||||
// mod nineteen;
|
||||
// #[path = "../bin/20.rs"]
|
||||
// mod twenty;
|
||||
// #[path = "../bin/21.rs"]
|
||||
// mod twenty_one;
|
||||
#[path = "../bin/22.rs"]
|
||||
mod twenty_two;
|
||||
// #[path = "../bin/23.rs"]
|
||||
// mod twenty_three;
|
||||
// #[path = "../bin/24.rs"]
|
||||
// mod twenty_four;
|
||||
// #[path = "../bin/25.rs"]
|
||||
// mod twenty_five;
|
||||
// #[path = "../bin/26.rs"]
|
||||
// mod twenty_six;
|
||||
#[path = "../bin/27.rs"]
|
||||
mod twenty_seven;
|
||||
// #[path = "../bin/28.rs"]
|
||||
// mod twenty_eight;
|
||||
// #[path = "../bin/29.rs"]
|
||||
// mod twenty_nine;
|
||||
// #[path = "../bin/30.rs"]
|
||||
// mod thirty;
|
||||
// #[path = "../bin/31.rs"]
|
||||
// mod thirty_one;
|
||||
// #[path = "../bin/32.rs"]
|
||||
// mod thirty_two;
|
||||
// #[path = "../bin/33.rs"]
|
||||
// mod thirty_three;
|
||||
// #[path = "../bin/34.rs"]
|
||||
// mod thirty_four;
|
||||
// #[path = "../bin/35.rs"]
|
||||
// mod thirty_five;
|
||||
// #[path = "../bin/36.rs"]
|
||||
// mod thirty_six;
|
||||
// #[path = "../bin/37.rs"]
|
||||
// mod thirty_seven;
|
||||
// #[path = "../bin/38.rs"]
|
||||
// mod thirty_eight;
|
||||
// #[path = "../bin/39.rs"]
|
||||
// mod thirty_nine;
|
||||
// #[path = "../bin/40.rs"]
|
||||
// mod forty;
|
||||
// #[path = "../bin/41.rs"]
|
||||
// mod forty_one;
|
||||
#[path = "../bin/42.rs"]
|
||||
mod forty_two;
|
||||
// #[path = "../bin/43.rs"]
|
||||
// mod forty_three;
|
||||
// #[path = "../bin/44.rs"]
|
||||
// mod forty_four;
|
||||
// #[path = "../bin/45.rs"]
|
||||
// mod forty_five;
|
||||
// #[path = "../bin/46.rs"]
|
||||
// mod forty_six;
|
||||
// #[path = "../bin/47.rs"]
|
||||
// mod forty_seven;
|
||||
// #[path = "../bin/48.rs"]
|
||||
// mod forty_eight;
|
||||
// #[path = "../bin/49.rs"]
|
||||
// mod forty_nine;
|
||||
// #[path = "../bin/50.rs"]
|
||||
// mod fifty;
|
||||
// #[path = "../bin/51.rs"]
|
||||
// mod fifty_one;
|
||||
// #[path = "../bin/52.rs"]
|
||||
// mod fifty_two;
|
||||
// #[path = "../bin/53.rs"]
|
||||
// mod fifty_three;
|
||||
// #[path = "../bin/54.rs"]
|
||||
// mod fifty_four;
|
||||
// #[path = "../bin/55.rs"]
|
||||
// mod fifty_five;
|
||||
// #[path = "../bin/56.rs"]
|
||||
// mod fifty_six;
|
||||
// #[path = "../bin/57.rs"]
|
||||
// mod fifty_seven;
|
||||
// #[path = "../bin/58.rs"]
|
||||
// mod fifty_eight;
|
||||
// #[path = "../bin/59.rs"]
|
||||
// mod fifty_nine;
|
||||
// #[path = "../bin/60.rs"]
|
||||
// mod sixty;
|
||||
// #[path = "../bin/61.rs"]
|
||||
// mod sixty_one;
|
||||
// #[path = "../bin/62.rs"]
|
||||
// mod sixty_two;
|
||||
// #[path = "../bin/63.rs"]
|
||||
// mod sixty_three;
|
||||
// #[path = "../bin/64.rs"]
|
||||
// mod sixty_four;
|
||||
// #[path = "../bin/65.rs"]
|
||||
// mod sixty_five;
|
||||
// #[path = "../bin/66.rs"]
|
||||
// mod sixty_six;
|
||||
#[path = "../bin/67.rs"]
|
||||
mod sixty_seven;
|
||||
// #[path = "../bin/68.rs"]
|
||||
// mod sixty_eight;
|
||||
// #[path = "../bin/69.rs"]
|
||||
// mod sixty_nine;
|
||||
// #[path = "../bin/70.rs"]
|
||||
// mod seventy;
|
||||
// #[path = "../bin/71.rs"]
|
||||
// mod seventy_one;
|
||||
// #[path = "../bin/72.rs"]
|
||||
// mod seventy_two;
|
||||
// #[path = "../bin/73.rs"]
|
||||
// mod seventy_three;
|
||||
// #[path = "../bin/74.rs"]
|
||||
// mod seventy_four;
|
||||
// #[path = "../bin/75.rs"]
|
||||
// mod seventy_five;
|
||||
// #[path = "../bin/76.rs"]
|
||||
// mod seventy_six;
|
||||
// #[path = "../bin/77.rs"]
|
||||
// mod seventy_seven;
|
||||
// #[path = "../bin/78.rs"]
|
||||
// mod seventy_eight;
|
||||
// #[path = "../bin/79.rs"]
|
||||
// mod seventy_nine;
|
||||
// #[path = "../bin/80.rs"]
|
||||
// mod eighty;
|
||||
// #[path = "../bin/81.rs"]
|
||||
// mod eighty_one;
|
||||
// #[path = "../bin/82.rs"]
|
||||
// mod eighty_two;
|
||||
// #[path = "../bin/83.rs"]
|
||||
// mod eighty_three;
|
||||
// #[path = "../bin/84.rs"]
|
||||
// mod eighty_four;
|
||||
// #[path = "../bin/85.rs"]
|
||||
// mod eighty_five;
|
||||
// #[path = "../bin/86.rs"]
|
||||
// mod eighty_six;
|
||||
// #[path = "../bin/87.rs"]
|
||||
// mod eighty_seven;
|
||||
// #[path = "../bin/88.rs"]
|
||||
// mod eighty_eight;
|
||||
// #[path = "../bin/89.rs"]
|
||||
// mod eighty_nine;
|
||||
// #[path = "../bin/90.rs"]
|
||||
// mod ninety;
|
||||
// #[path = "../bin/91.rs"]
|
||||
// mod ninety_one;
|
||||
// #[path = "../bin/92.rs"]
|
||||
// mod ninety_two;
|
||||
// #[path = "../bin/93.rs"]
|
||||
// mod ninety_three;
|
||||
// #[path = "../bin/94.rs"]
|
||||
// mod ninety_four;
|
||||
// #[path = "../bin/95.rs"]
|
||||
// mod ninety_five;
|
||||
// #[path = "../bin/96.rs"]
|
||||
// mod ninety_six;
|
||||
// #[path = "../bin/97.rs"]
|
||||
// mod ninety_seven;
|
||||
// #[path = "../bin/98.rs"]
|
||||
// mod ninety_eight;
|
||||
// #[path = "../bin/99.rs"]
|
||||
// mod ninety_nine;
|
||||
// #[path = "../bin/100.rs"]
|
||||
// mod one_hundred;
|
||||
#[path = "../bin/22.rs"]
|
||||
mod twenty_two;
|
||||
#[path = "../bin/2.rs"]
|
||||
mod two;
|
||||
|
||||
#[tokio::main]
|
||||
pub async fn execute(
|
||||
problem: Option<u8>,
|
||||
benchmark: bool,
|
||||
) -> Result<(), Box<dyn std::error::Error>> {
|
||||
let code_path = euler::code_path();
|
||||
let problem_number =
|
||||
euler::problem_number(problem, &code_path, "Which problem would you like to run?");
|
||||
let problem = problem
|
||||
.unwrap_or_else(|| Problem::prompt_number("Please select a problem:", true).unwrap());
|
||||
let mut exists = true; // assume it exists
|
||||
|
||||
let start = std::time::Instant::now();
|
||||
let mut exists = true;
|
||||
let start = if benchmark {
|
||||
Some(Instant::now())
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
match problem_number {
|
||||
// execute the solution
|
||||
match problem {
|
||||
1 => one::main(),
|
||||
2 => two::main(),
|
||||
3 => three::main(),
|
||||
|
@ -232,105 +81,28 @@ pub async fn execute(
|
|||
16 => sixteen::main(),
|
||||
17 => seventeen::main(),
|
||||
18 => eighteen::main(),
|
||||
// 19 => nineteen::main(),
|
||||
// 20 => twenty::main(),
|
||||
// 21 => twenty_one::main(),
|
||||
22 => twenty_two::main(),
|
||||
// 23 => twenty_three::main(),
|
||||
// 24 => twenty_four::main(),
|
||||
// 25 => twenty_five::main(),
|
||||
// 26 => twenty_six::main(),
|
||||
27 => twenty_seven::main(),
|
||||
// 28 => twenty_eight::main(),
|
||||
// 29 => twenty_nine::main(),
|
||||
// 30 => thirty::main(),
|
||||
// 31 => thirty_one::main(),
|
||||
// 32 => thirty_two::main(),
|
||||
// 33 => thirty_three::main(),
|
||||
// 34 => thirty_four::main(),
|
||||
// 35 => thirty_five::main(),
|
||||
// 36 => thirty_six::main(),
|
||||
// 37 => thirty_seven::main(),
|
||||
// 38 => thirty_eight::main(),
|
||||
// 39 => thirty_nine::main(),
|
||||
// 40 => forty::main(),
|
||||
// 41 => forty_one::main(),
|
||||
// 42 => forty_two::main(),
|
||||
// 43 => forty_three::main(),
|
||||
// 44 => forty_four::main(),
|
||||
// 45 => forty_five::main(),
|
||||
// 46 => forty_six::main(),
|
||||
// 47 => forty_seven::main(),
|
||||
// 48 => forty_eight::main(),
|
||||
// 49 => forty_nine::main(),
|
||||
// 50 => fifty::main(),
|
||||
// 51 => fifty_one::main(),
|
||||
// 52 => fifty_two::main(),
|
||||
// 53 => fifty_three::main(),
|
||||
// 54 => fifty_four::main(),
|
||||
// 55 => fifty_five::main(),
|
||||
// 56 => fifty_six::main(),
|
||||
// 57 => fifty_seven::main(),
|
||||
// 58 => fifty_eight::main(),
|
||||
// 59 => fifty_nine::main(),
|
||||
// 60 => sixty::main(),
|
||||
// 61 => sixty_one::main(),
|
||||
// 62 => sixty_two::main(),
|
||||
// 63 => sixty_three::main(),
|
||||
// 64 => sixty_four::main(),
|
||||
// 65 => sixty_five::main(),
|
||||
// 66 => sixty_six::main(),
|
||||
67 => sixty_seven::main(),
|
||||
// 68 => sixty_eight::main(),
|
||||
// 69 => sixty_nine::main(),
|
||||
// 70 => seventy::main(),
|
||||
// 71 => seventy_one::main(),
|
||||
// 72 => seventy_two::main(),
|
||||
// 73 => seventy_three::main(),
|
||||
// 74 => seventy_four::main(),
|
||||
// 75 => seventy_five::main(),
|
||||
// 76 => seventy_six::main(),
|
||||
// 77 => seventy_seven::main(),
|
||||
// 78 => seventy_eight::main(),
|
||||
// 79 => seventy_nine::main(),
|
||||
// 80 => eighty::main(),
|
||||
// 81 => eighty_one::main(),
|
||||
// 82 => eighty_two::main(),
|
||||
// 83 => eighty_three::main(),
|
||||
// 84 => eighty_four::main(),
|
||||
// 85 => eighty_five::main(),
|
||||
// 86 => eighty_six::main(),
|
||||
// 87 => eighty_seven::main(),
|
||||
// 88 => eighty_eight::main(),
|
||||
// 89 => eighty_nine::main(),
|
||||
// 90 => ninety::main(),
|
||||
// 91 => ninety_one::main(),
|
||||
// 92 => ninety_two::main(),
|
||||
// 93 => ninety_three::main(),
|
||||
// 94 => ninety_four::main(),
|
||||
// 95 => ninety_five::main(),
|
||||
// 96 => ninety_six::main(),
|
||||
// 97 => ninety_seven::main(),
|
||||
// 98 => ninety_eight::main(),
|
||||
// 99 => ninety_nine::main(),
|
||||
// 100 => one_hundred::main(),
|
||||
_ => {
|
||||
exists = false;
|
||||
println!(
|
||||
"{} {} {}",
|
||||
"Problem".red().bold(),
|
||||
problem_number.red().bold(),
|
||||
"is not in this repository!".red().bold()
|
||||
"{}",
|
||||
format!("Problem #{problem} is yet to be solved!")
|
||||
.red()
|
||||
.bold()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
let duration = start.elapsed();
|
||||
|
||||
// if the script exists, print the time elapsed
|
||||
if benchmark && exists {
|
||||
let duration = start.unwrap().elapsed();
|
||||
|
||||
println!(
|
||||
"Time elapsed when executing problem {} is: {:?}",
|
||||
problem_number, duration
|
||||
"
|
||||
Time elapsed when executing problem {} is: {:?}",
|
||||
problem, duration
|
||||
);
|
||||
}
|
||||
|
||||
|
|
73
src/lib.rs
73
src/lib.rs
|
@ -1,65 +1,16 @@
|
|||
use std::path::{Path, PathBuf};
|
||||
mod problem;
|
||||
|
||||
fn fetch_dir() -> &'static Path {
|
||||
return Path::new(env!("CARGO_MANIFEST_DIR"));
|
||||
}
|
||||
use once_cell::sync::Lazy;
|
||||
pub use problem::Problem;
|
||||
use std::path::PathBuf;
|
||||
|
||||
pub fn code_path() -> PathBuf {
|
||||
return fetch_dir().join("src").join("bin");
|
||||
}
|
||||
pub type Result<T> = std::result::Result<T, Box<dyn std::error::Error>>;
|
||||
|
||||
pub fn resources_path() -> PathBuf {
|
||||
return fetch_dir().join("resources");
|
||||
}
|
||||
// paths
|
||||
pub const BASE_DIR: Lazy<PathBuf> = Lazy::new(|| PathBuf::from(env!("CARGO_MANIFEST_DIR")));
|
||||
pub const SOLUTIONS: Lazy<PathBuf> = Lazy::new(|| BASE_DIR.join("src").join("bin"));
|
||||
pub const RESOURCES: Lazy<PathBuf> = Lazy::new(|| BASE_DIR.join("resources"));
|
||||
pub const README: Lazy<PathBuf> = Lazy::new(|| BASE_DIR.join("readme.md"));
|
||||
|
||||
pub fn readme_path() -> PathBuf {
|
||||
return fetch_dir().join("readme.md");
|
||||
}
|
||||
|
||||
pub fn run_path() -> PathBuf {
|
||||
return fetch_dir().join("src").join("commands").join("run.rs");
|
||||
}
|
||||
|
||||
pub fn problem_number(problem: Option<u8>, code_path: &PathBuf, prompt: &str) -> u8 {
|
||||
if let Some(n) = problem {
|
||||
return n;
|
||||
} else {
|
||||
return requestty::prompt_one(
|
||||
requestty::Question::int("problemNumber")
|
||||
.message(prompt)
|
||||
.validate(|n, _| {
|
||||
// All numbers must be positive
|
||||
let mut pass = false;
|
||||
|
||||
// Ensure that the problem has not already got a file associated with it
|
||||
let files = std::fs::read_dir(code_path).unwrap();
|
||||
|
||||
for file in files {
|
||||
let file_number = file
|
||||
.unwrap()
|
||||
.file_name()
|
||||
.to_str()
|
||||
.unwrap()
|
||||
.split('.')
|
||||
.collect::<Vec<&str>>()[0]
|
||||
.parse::<i64>()
|
||||
.unwrap();
|
||||
|
||||
if n == file_number {
|
||||
pass = true;
|
||||
}
|
||||
}
|
||||
|
||||
if pass {
|
||||
Ok(())
|
||||
} else {
|
||||
Err("Please ensure that your input is valid!".to_owned())
|
||||
}
|
||||
})
|
||||
.build(),
|
||||
)
|
||||
.unwrap()
|
||||
.as_int()
|
||||
.unwrap() as u8;
|
||||
}
|
||||
}
|
||||
// panic channel
|
||||
pub static mut SILENT_PANIC: bool = false;
|
||||
|
|
23
src/main.rs
23
src/main.rs
|
@ -1,8 +1,12 @@
|
|||
#![allow(dead_code)]
|
||||
|
||||
use clap::{Args, Parser, Subcommand};
|
||||
mod commands;
|
||||
|
||||
use std::panic;
|
||||
|
||||
use clap::{Args, Parser, Subcommand};
|
||||
use euler::{Result, SILENT_PANIC};
|
||||
|
||||
#[derive(Parser)]
|
||||
#[clap(about, author, version)]
|
||||
struct Value {
|
||||
|
@ -32,11 +36,22 @@ enum Commands {
|
|||
Run(RunArgs),
|
||||
}
|
||||
|
||||
pub fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
#[tokio::main]
|
||||
pub async fn main() -> Result<()> {
|
||||
panic::set_hook(Box::new(move |panic_info| {
|
||||
if unsafe { SILENT_PANIC } {
|
||||
std::process::exit(0);
|
||||
} else {
|
||||
println!("{}", panic_info.to_string());
|
||||
}
|
||||
}));
|
||||
|
||||
let value = Value::parse();
|
||||
|
||||
match value.command {
|
||||
Commands::New(NewArgs { problem }) => commands::new::execute(problem),
|
||||
Commands::Run(RunArgs { problem, benchmark }) => commands::run::execute(problem, benchmark),
|
||||
Commands::New(NewArgs { problem }) => commands::new::execute(problem).await,
|
||||
Commands::Run(RunArgs { problem, benchmark }) => {
|
||||
commands::run::execute(problem, benchmark).await
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
126
src/problem.rs
Normal file
126
src/problem.rs
Normal file
|
@ -0,0 +1,126 @@
|
|||
use crate::{Result, SILENT_PANIC, SOLUTIONS};
|
||||
use regex::Regex;
|
||||
use scraper::{Html, Selector};
|
||||
|
||||
pub struct Problem {
|
||||
pub number: u8,
|
||||
title: String,
|
||||
content: String,
|
||||
}
|
||||
|
||||
impl Problem {
|
||||
pub fn prompt_number(prompt: &str, want_solved: bool) -> Result<u8> {
|
||||
unsafe {
|
||||
SILENT_PANIC = true;
|
||||
}
|
||||
|
||||
let number = requestty::prompt_one(
|
||||
requestty::Question::int("problemNumber")
|
||||
.message(prompt)
|
||||
.validate(|n, _| {
|
||||
// ensure the number is in bound
|
||||
if n <= 0 {
|
||||
Err("Please enter a number greater than 0".to_string())
|
||||
} else if n > 100 {
|
||||
Err("Please enter a number less than or equal to 100".to_string())
|
||||
}
|
||||
// ensure that the problem has not already got a file associated with it
|
||||
else {
|
||||
let has_file = std::fs::read_dir(SOLUTIONS.clone()).unwrap().any(|x| {
|
||||
x.unwrap()
|
||||
.file_name()
|
||||
.to_str()
|
||||
.unwrap()
|
||||
.split(".")
|
||||
.next()
|
||||
.unwrap()
|
||||
.parse::<i64>()
|
||||
.unwrap()
|
||||
== n
|
||||
});
|
||||
|
||||
if !want_solved && has_file {
|
||||
Err("This problem has already been solved".to_string())
|
||||
} else if want_solved && !has_file {
|
||||
Err("This problem has not been solved yet".to_string())
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
})
|
||||
.build(),
|
||||
)?
|
||||
.as_int()
|
||||
.unwrap() as u8;
|
||||
|
||||
unsafe {
|
||||
SILENT_PANIC = false;
|
||||
}
|
||||
|
||||
Ok(number)
|
||||
}
|
||||
|
||||
pub async fn new(number: Option<u8>) -> Result<Self> {
|
||||
let number = number
|
||||
.unwrap_or_else(|| Self::prompt_number("Please select a problem:", false).unwrap());
|
||||
|
||||
let body = reqwest::get(format!("https://projecteuler.net/problem={number}"))
|
||||
.await?
|
||||
.text()
|
||||
.await?;
|
||||
|
||||
let document = Html::parse_document(body.as_str());
|
||||
let title_selector = Selector::parse("h2")?;
|
||||
let content_selector = Selector::parse(".problem_content")?;
|
||||
let html_tag_regex = Regex::new(r"<[^>]*>")?;
|
||||
|
||||
let title = document
|
||||
.select(&title_selector)
|
||||
.next()
|
||||
.unwrap()
|
||||
.text()
|
||||
.collect::<Vec<&str>>()
|
||||
.join("");
|
||||
|
||||
let problem = html_tag_regex
|
||||
.replace_all(
|
||||
document
|
||||
.select(&content_selector)
|
||||
.next()
|
||||
.unwrap()
|
||||
.inner_html()
|
||||
.as_str(),
|
||||
" ",
|
||||
)
|
||||
.to_string()
|
||||
.replace("$$", " ");
|
||||
|
||||
Ok(Self {
|
||||
number,
|
||||
title: title.trim().to_string(),
|
||||
content: problem.trim().to_string(),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn file_body(&self) -> String {
|
||||
format!(
|
||||
"/*
|
||||
Problem {} - {}
|
||||
|
||||
{}
|
||||
*/
|
||||
|
||||
pub fn main() {{
|
||||
println!(\"Hello World!\");
|
||||
}}",
|
||||
self.number,
|
||||
html_escape::decode_html_entities(&self.title).to_string(),
|
||||
html_escape::decode_html_entities(&self.content)
|
||||
.split("\n")
|
||||
.map(|s| s.trim())
|
||||
.filter(|s| s != &"")
|
||||
.collect::<Vec<&str>>()
|
||||
.join("\n")
|
||||
)
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue