diff --git a/Benchmark.ts b/Benchmark.ts new file mode 100644 index 0000000..e55b628 --- /dev/null +++ b/Benchmark.ts @@ -0,0 +1,186 @@ +//ProjectEulerTS/Benchmark.ts +//Matthew Ellison +// Created: 10-18-20 +//Modified: 10-18-20 +//This runs the benchmark functions for the Java version of the ProjectEuler project +/* + Copyright (C) 2020 Matthew Ellison + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program. If not, see . +*/ + + +const readlineSync = require("readline-sync"); +import { Problem } from "./Problems/Problem"; +import { Stopwatch } from "../../Typescript/typescriptClasses/Stopwatch"; +import { ProblemSelection } from "./ProblemSelection"; + + +enum BenchmarkOptions{runSpecific = 1, runAllShort, runAll, exit, size}; + + +export class Benchmark{ + private static tooLong = []; + //The driver function for the benchmark selection + public static benchmarkMenu(): void{ + let selection: BenchmarkOptions = BenchmarkOptions.size; + + Benchmark.printMenu(); + selection = Benchmark.getMenuSelection(); + + switch(selection){ + case BenchmarkOptions.runSpecific: Benchmark.runSpecific(); break; + case BenchmarkOptions.runAllShort: Benchmark.runAllShort(); break; + case BenchmarkOptions.runAll: Benchmark.runAll(); break; + case BenchmarkOptions.exit: break; + case BenchmarkOptions.size: break; + } + } + //Print the benchmark menu + private static printMenu(): void{ + console.log("1. Run a specific problem"); + console.log("2. Run all problems that have a reasonably short run time"); + console.log("3. Run all problems"); + console.log("4. Exit the menu"); + console.log(); + } + //Returns a valid menu option + private static getMenuSelection(): BenchmarkOptions{ + let selection: number; + let sel: string = readlineSync.question(""); + while(!Benchmark.isValidMenu(sel)){ + console.log("That is an invalid option!\n"); + Benchmark.printMenu(); + selection = Benchmark.getMenuSelection(); + } + selection = parseInt(sel); + return selection; + } + //Determines if a value is a valid menu option. Helper for getBenchmarkMenuSelection + private static isValidMenu(sel: string): boolean{ + let selection: number; + try{ + selection = parseInt(sel); + } + catch(error){ + selection = -1; + } + if((selection > 0) && (selection < BenchmarkOptions.size)){ + return true; + } + else{ + return false; + } + } + //Determines which problem user wants to run and runs it + private static runSpecific(): void{ + //Ask which problem the user wants to run + let problemNumber: number = ProblemSelection.getProblemNumber(); + //Ask how many times to run the problem + let timesToRun: number = Benchmark.getNumberOfTimesToRun(); + + //Get the problem and print its description + let problem: Problem = ProblemSelection.getProblem(problemNumber); + console.log("\n" + problemNumber + ". " + problem.getDescription()); + + //Run the problem the specific number of times + let totalTime: number = Benchmark.runProblem(problem, timesToRun); + + //Print the results + console.log(Benchmark.getBenchmarkResults(problem, totalTime, timesToRun)); + } + //Runs all problems except a few that are specific because of run length + private static runAllShort(): void{ + //Ask how many times to run the problems + let timesToRun: number = Benchmark.getNumberOfTimesToRun(); + + //Run through all valid problem numbers, skipping a few that are in the tooLong list + for(let cnt: number = 1;cnt < ProblemSelection.PROBLEM_NUMBERS.length;++cnt){ + let problemNumber: number = ProblemSelection.PROBLEM_NUMBERS[cnt]; + + //If the problem number is contained in the list of problems that take too long skip it + if(Benchmark.tooLong.includes(problemNumber)){ + continue; + } + + //Get the problem and print its description + let problem: Problem = ProblemSelection.getProblem(problemNumber); + console.log(problemNumber + ". " + problem.getDescription()); + + //Run each problem the specified number of times + let totalTime: number = Benchmark.runProblem(problem, timesToRun); + + //Print the results + console.log(Benchmark.getBenchmarkResults(problem, totalTime, timesToRun)); + } + } + //Runs all problems + private static runAll(): void{ + //Ask how many times to run the problem + let timesToRun: number = Benchmark.getNumberOfTimesToRun(); + + //Run through all valid problem number, skipping a few that are in the tooLong list + for(let cnt: number = 1;cnt < ProblemSelection.PROBLEM_NUMBERS.length;++cnt){ + let problemNumber: number = ProblemSelection.PROBLEM_NUMBERS[cnt]; + + //Get the problem + let problem: Problem = ProblemSelection.getProblem(problemNumber); + + //Run each problem the specified number of times + console.log(problemNumber + ". " + problem.getDescription()); + let totalTime: number = Benchmark.runProblem(problem, timesToRun); + + //Print the results + console.log(Benchmark.getBenchmarkResults(problem, totalTime, timesToRun)); + } + } + //Asks how many times a problem is ksupposed to run and returns the value + private static getNumberOfTimesToRun(): number{ + let numberOfTimesToRun: number = 1; + let num: string = readlineSync.question("How many times do you want to run this problem? "); + try{ + numberOfTimesToRun = parseInt(num); + } + catch(error){ + console.log("That is an invalid number!"); + numberOfTimesToRun = Benchmark.getNumberOfTimesToRun(); + } + return numberOfTimesToRun; + } + //Runs the problem the given number of times + private static runProblem(problem: Problem, timesToRun: number): number{ + let totalTime: number = 0; + process.stdout.write("Solving"); + for(let cnt: number = 0;cnt < timesToRun;++cnt){ + process.stdout.write('.'); + //Reset the data so you are actually counting the run time an additional time + problem.reset(); + //Solve the problem + problem.solve(); + //Get the time dat + totalTime += problem.getTimer().getNano(); + } + return totalTime; + } + //Prints the benchmark results of a problem + private static getBenchmarkResults(problem: Problem, totalTime: number, timesRun: number): string{ + //Calculate the average run time of the problem + totalTime /= timesRun; + let timeResults: string = Stopwatch.getStr(totalTime); + + //Tally the results + let results: string = "\n\n" + problem.getResult() + "\nIt took an average of " + timeResults + " to run this problem through " + timesRun + " iterations\n\n"; + return results; + } +} diff --git a/ProblemSelection.ts b/ProblemSelection.ts new file mode 100644 index 0000000..5b80ca6 --- /dev/null +++ b/ProblemSelection.ts @@ -0,0 +1,85 @@ +//ProjectEulerTS/ProblemSelection.ts +//Matthew Ellison +// Created: 10-18-20 +//Modified: 10-18-20 +//This class holds all of the functions needed to handle a problem +//Unless otherwise listed all non-standard includes are my own creation and available from https://bibucket.org/Mattrixwv/typescriptClasses +/* + Copyright (C) 2020 Matthew Ellison + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program. If not, see . +*/ + + +const readlineSync = require("readline-sync"); +import { Problem } from "./Problems/Problem"; +import { Problem1 } from "./Problems/Problem1"; + + +export class ProblemSelection{ + //Holds the valid problem numbers + public static PROBLEM_NUMBERS = [1]; + + //Returns the problem corresponding to the given problem number + public static getProblem(problemNumber: number): Problem{ + let problem: Problem = null; + switch(problemNumber){ + case 1 : problem = new Problem1(); break; + } + return problem; + } + //Print the description of a problem + public static printDescription(problemNumber: number): void{ + //Get the problem + let problem: Problem = ProblemSelection.getProblem(problemNumber); + //Print the problem's description + console.log(problem.getDescription() + "\n\n"); + } + //Solve a problem + public static solveProblem(problemNumber: number): void{ + //Get the problem + let problem: Problem = ProblemSelection.getProblem(problemNumber); + //Print the problem description + console.log("\n\n" + problem.getDescription()); + //Solve the problem + problem.solve(); + //Print the results + console.log(problem.getResult() + "\nIt took " + problem.getTime() + " to solve this problem.\n\n"); + } + //Get a valid problem number from a user + public static getProblemNumber(): number{ + let problemNumber: number = 0; + let prob = readlineSync.question("Enter a problem number: "); + try{ + problemNumber = parseInt(prob); + } + catch(error){ + problemNumber = -1; + } + while(!ProblemSelection.PROBLEM_NUMBERS.includes(problemNumber)){ + console.log("That is an invalid problem number!"); + problemNumber = ProblemSelection.getProblemNumber(); + } + problemNumber = parseInt(prob); + return problemNumber; + } + //List all valid problem numbers + public static listProblems(): void{ + console.log(ProblemSelection.PROBLEM_NUMBERS[1]); + for(let problemNumber: number = 2;problemNumber < ProblemSelection.PROBLEM_NUMBERS.length;++problemNumber){ + console.log(", " + ProblemSelection.PROBLEM_NUMBERS[problemNumber]); + } + console.log(); + } +} diff --git a/Problems/Problem.ts b/Problems/Problem.ts index 48ae6ad..25c0902 100644 --- a/Problems/Problem.ts +++ b/Problems/Problem.ts @@ -37,7 +37,7 @@ export abstract class Problem{ this.solved = false; } //Gets - getDescription(){ + getDescription(): string{ return this.description; } //Returns the result of solving the problem diff --git a/ProjectEuler.ts b/ProjectEuler.ts new file mode 100644 index 0000000..2c1343d --- /dev/null +++ b/ProjectEuler.ts @@ -0,0 +1,139 @@ +//ProjectEulerTS/ProjectEuler.ts +//Matthew Ellison +// Created: 10-18-20 +//Modified: 10-18-20 +//This is the driver function for the Java version of the ProjectEuler project +//Unless otherwise listed all non-standard includes are my own creation and available from https://bibucket.org/Mattrixwv/typescriptClasses +/* + Copyright (C) 2020 Matthew Ellison + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program. If not, see . +*/ + + +import { ProblemSelection } from "./ProblemSelection"; +import { Benchmark } from "./Benchmark"; +const readlineSync = require("readline-sync"); + + +//An enum to hold the possible menu selections +enum SELECTIONS{SOLVE = 1, DESCRIPTION, LIST, BENCHMARK, EXIT, SIZE}; + +class Driver{ + //Drives the program + public static main(): void{ + console.log("Welcome to ProjectEulerTS\nPress Enter to continue\n\n\n"); + readlineSync.question("", {hideEchoBack: true}); + //Holds the menu selection of the user + let selection: number; + do{ + Driver.printMenu(); + selection = Driver.getMenuSelection(); + + switch(selection){ + case SELECTIONS.SOLVE: Driver.solveMenu(); break; + case SELECTIONS.DESCRIPTION: Driver.descriptionMenu(); break; + case SELECTIONS.LIST: ProblemSelection.listProblems(); break; + case SELECTIONS.BENCHMARK: Benchmark.benchmarkMenu(); break; + case SELECTIONS.EXIT: break; + case SELECTIONS.SIZE: + default: Driver.printErrorMessage(); + } + }while(selection != SELECTIONS.EXIT); + } + //Print the menu + public static printMenu(): void{ + console.log("1. Solve a problem"); + console.log("2. Print a problem description"); + console.log("3. List valid problem numbers"); + console.log("4. Benchmark"); + console.log("5. Exit"); + console.log(); + } + //Get a menu selection from the user + private static getMenuSelection(): SELECTIONS{ + let selection: number; + let sel: string = readlineSync.question(' '); + while(!Driver.isValidMenu(sel)){ + console.log("That is an invalid option!\n"); + selection = Driver.getMenuSelection(); + } + selection = parseInt(sel); + return selection; + } + //Make sure the value passed in is a valid menu option + private static isValidMenu(sel: string): boolean{ + let selection: number; + try{ + selection = parseInt(sel); + } + catch(error){ + selection = -1; + } + if((selection > 0) && (selection < SELECTIONS.SIZE)){ + return true; + } + else{ + return false; + } + } + //Print an error message + private static printErrorMessage(): void{ + console.log("That is an invalid selection!"); + } + //Handle what happens when a user wants to solve a problem + private static async solveMenu(){ + let problemNumber: number = ProblemSelection.getProblemNumber(); + //This selection solves all problems in order + if(problemNumber == 0){ + //Solve to every valid problem number, skipping over 0 + for(let problemLocation: number = 1;problemLocation < ProblemSelection.PROBLEM_NUMBERS.length;++problemLocation){ + //Solve the problems + console.log(ProblemSelection.PROBLEM_NUMBERS[problemLocation] + ". "); + ProblemSelection.solveProblem(ProblemSelection.PROBLEM_NUMBERS[problemLocation]); + } + } + //This is if a single problem number was chosen + else{ + //Solve the problem + ProblemSelection.solveProblem(problemNumber); + } + } + //Handle what happens when a user wants to see the description of a problem + private static descriptionMenu(): void{ + //Give some extra space to print the description + console.log("\n"); + + //Get the problem number + let problemNumber = ProblemSelection.getProblemNumber(); + + //If the problem number is 0 print out all the descriptions + if(problemNumber == 0){ + //Print the description for every valid problem number, skipping over 0 + for(let problemLocation: number = 1;problemLocation < ProblemSelection.PROBLEM_NUMBERS.length;++problemLocation){ + //Print the problem's description + console.log(ProblemSelection.PROBLEM_NUMBERS[problemLocation] + ". "); + ProblemSelection.printDescription(ProblemSelection.PROBLEM_NUMBERS[problemLocation]); + console.log(); + } + } + //Otherwise print out a single problem's description + else{ + ProblemSelection.printDescription(problemNumber); + } + } +} + +//Run the program +Driver.main();