import runpy from pathlib import Path import typer app = typer.Typer() @app.command("add", help="Add a new problem to the projecteuler repository.") def add_newproblem(num: int, name: str | None = None) -> None: """Add a new problem to the projecteuler repository.""" typer.echo(f"Adding problem {num} to the projecteuler repository.") if name: title = f"{num:04d}.{name}" else: title = f"{num:04d}" Path(f"solutions/{title}").mkdir(parents=True, exist_ok=True) file_name = f"solutions/{title}/euler_{num}.py" Path(file_name).touch() with open(file_name, "w", encoding="utf-8") as f: f.write("""''' ''' import time from functools import wraps from typing import Any, Callable, TypeVar F = TypeVar("F", bound=Callable[..., Any]) def benchmark(repeat: int = 1) -> Callable[[F], F]: if repeat < 1: raise ValueError("repeat must >= 1") def decorator(func: F) -> F: @wraps(func) def wrapper(*args: Any, **kwargs: Any) -> Any: total = 0.0 result = None for _ in range(repeat): start = time.perf_counter() result = func(*args, **kwargs) end = time.perf_counter() total += end - start wrapper.avg_time = total / repeat # type: ignore[attr-defined] wrapper.total_time = total # type: ignore[attr-defined] print( f"[Benchmark] {func.__name__} | repeated {repeat} times | " f"average: {wrapper.avg_time:.6f}s | total: {wrapper.total_time:.6f}s" # type: ignore[attr-defined] ) return result wrapper.avg_time = 0.0 # type: ignore[attr-defined] wrapper.total_time = 0.0 # type: ignore[attr-defined] return wrapper # type: ignore[return-value] return decorator""") @app.command("list", help="List all problems in the projecteuler repository.") def list_problems(every: bool = False): """List all problems in the projecteuler repository.""" if every: typer.echo("Listing all problems in the projecteuler repository.") else: typer.echo("Listing near problems in the projecteuler repository.") for path in Path("solutions").iterdir(): if path.is_dir(): if every: tmp = list(path.iterdir()) tmp = all(ff.is_dir() for ff in tmp) if tmp: for ff in path.iterdir(): if ff.is_dir(): typer.echo(f"{ff.name}") else: typer.echo(f"{path.name}") else: if "_" in path.name: typer.echo(f"{path.name} (completed)") else: typer.echo(f"{path.name}") @app.command("solution", help="Run a solution for a given problem.") def run_solution( num: int, special: None | str = None, list_solutions: bool = False, ) -> None: # Find target folders that match the problem number target_folders = [] for folder in Path("solutions").iterdir(): if not folder.is_dir(): continue # Check for exact match (e.g., "0001") if folder.name.startswith(f"{num:04d}"): target_folders.append(folder) break # Check for range folders (e.g., "0001_0050") if "_" in folder.name: try: start_str, end_str = folder.name.split("_") start_num = int(start_str) end_num = int(end_str) if start_num <= num <= end_num: for subfolder in folder.iterdir(): if subfolder.is_dir() and subfolder.name.startswith( f"{num:04d}" ): target_folders.append(subfolder) break except (ValueError, IndexError): continue if not target_folders: typer.echo(f"No folder found for problem {num}") return target_folder = target_folders[0] if list_solutions: # List all Python files in the target folder for file in target_folder.iterdir(): if file.is_file() and file.suffix == ".py": typer.echo(f"{file.name}") return # Determine the filename to run filename = f"euler_{num}.py" if special: filename = f"euler_{num}_{special}.py" file_path = target_folder / filename if not file_path.exists(): typer.echo(f"Solution file not found: {file_path}") return # Run the solution runpy.run_path(file_path.resolve().as_posix(), run_name="__main__") @app.command("version", help="Display the version of the projecteuler CLI.") def version(): """Display the version of the projecteuler CLI.""" typer.echo("projecteuler solution version 0.1.0") def main(): app() if __name__ == "__main__": main()