├── .dir-locals.el ├── .envrc ├── .gitignore ├── .ocamlformat ├── CHANGES.md ├── LICENSE.md ├── Makefile ├── README.md ├── TODO.org ├── bench ├── dune ├── main.ml ├── runner.sh └── src │ ├── bench_divisors.ml │ ├── bench_divisors.mli │ ├── bench_is_prime.ml │ ├── bench_is_prime.mli │ ├── bench_pow.ml │ ├── bench_pow.mli │ ├── bench_prime_sieve.ml │ ├── bench_prime_sieve.mli │ ├── bench_run_length_encode.ml │ ├── bench_run_length_encode.mli │ ├── bench_sqrt.ml │ ├── bench_sqrt.mli │ ├── dune │ └── import.ml ├── bin ├── dune ├── import.ml ├── main.ml ├── main.mli ├── solutions.ml └── solutions.mli ├── data ├── 008.txt ├── 011.txt ├── 013.txt ├── 018.txt ├── 022.txt ├── 042.txt ├── 054.txt ├── 059.txt ├── 067.txt ├── 105.txt ├── 345.txt ├── dune ├── dune.inc ├── gen-data.sh ├── gen-dune.sh ├── gen-problem.sh ├── import.ml ├── parse.ml └── parse.mli ├── default.nix ├── diagrams ├── 587 │ ├── sketch.gsp │ └── sketch.pdf └── 727 │ ├── 727.ggb │ ├── 727.png │ └── scan.pdf ├── doc ├── benchmark-divisors.txt ├── benchmark-pow.txt ├── benchmark-primes.txt ├── benchmark-rle.txt └── benchmark-sqrt.txt ├── dune ├── dune-project ├── euler.opam ├── flake.lock ├── flake.nix ├── pkg └── pkg.ml ├── release.nix ├── run-solutions.sh ├── shell.nix ├── sol ├── dune ├── euler_solutions.mli ├── import.ml ├── lib │ ├── debug_printing.ml │ ├── debug_printing.mli │ ├── dune │ ├── euler_solution_helpers.ml │ ├── import.ml │ ├── solution.ml │ ├── solution.mli │ └── solution_intf.ml ├── module-list.pl ├── sol_001.ml ├── sol_001.mli ├── sol_002.ml ├── sol_002.mli ├── sol_003.ml ├── sol_003.mli ├── sol_004.ml ├── sol_004.mli ├── sol_005.ml ├── sol_005.mli ├── sol_006.ml ├── sol_006.mli ├── sol_007.ml ├── sol_007.mli ├── sol_008.ml ├── sol_008.mli ├── sol_009.ml ├── sol_009.mli ├── sol_010.ml ├── sol_010.mli ├── sol_010_seq.ml ├── sol_010_seq.mli ├── sol_011.ml ├── sol_011.mli ├── sol_012.ml ├── sol_012.mli ├── sol_013.ml ├── sol_013.mli ├── sol_014.ml ├── sol_014.mli ├── sol_014_memo_slower.ml ├── sol_014_memo_slower.mli ├── sol_014_naive.ml ├── sol_014_naive.mli ├── sol_015.ml ├── sol_015.mli ├── sol_016.ml ├── sol_016.mli ├── sol_017.ml ├── sol_017.mli ├── sol_018.ml ├── sol_018.mli ├── sol_019.ml ├── sol_019.mli ├── sol_020.ml ├── sol_020.mli ├── sol_021.ml ├── sol_021.mli ├── sol_022.ml ├── sol_022.mli ├── sol_023.ml ├── sol_023.mli ├── sol_024.ml ├── sol_024.mli ├── sol_025.ml ├── sol_025.mli ├── sol_026.ml ├── sol_026.mli ├── sol_027.ml ├── sol_027.mli ├── sol_028.ml ├── sol_028.mli ├── sol_029.ml ├── sol_029.mli ├── sol_030.ml ├── sol_030.mli ├── sol_031.ml ├── sol_031.mli ├── sol_032.ml ├── sol_032.mli ├── sol_033.ml ├── sol_033.mli ├── sol_034.ml ├── sol_034.mli ├── sol_035.ml ├── sol_035.mli ├── sol_036.ml ├── sol_036.mli ├── sol_037.ml ├── sol_037.mli ├── sol_038.ml ├── sol_038.mli ├── sol_039.ml ├── sol_039.mli ├── sol_040.ml ├── sol_040.mli ├── sol_041.ml ├── sol_041.mli ├── sol_042.ml ├── sol_042.mli ├── sol_043.ml ├── sol_043.mli ├── sol_044.ml ├── sol_044.mli ├── sol_045.ml ├── sol_045.mli ├── sol_046.ml ├── sol_046.mli ├── sol_047.ml ├── sol_047.mli ├── sol_048.ml ├── sol_048.mli ├── sol_049.ml ├── sol_049.mli ├── sol_050.ml ├── sol_050.mli ├── sol_051.ml ├── sol_051.mli ├── sol_052.ml ├── sol_052.mli ├── sol_053.ml ├── sol_053.mli ├── sol_054.ml ├── sol_054.mli ├── sol_055.ml ├── sol_055.mli ├── sol_056.ml ├── sol_056.mli ├── sol_057.ml ├── sol_057.mli ├── sol_058.ml ├── sol_058.mli ├── sol_059.ml ├── sol_059.mli ├── sol_060.ml ├── sol_060.mli ├── sol_061.ml ├── sol_061.mli ├── sol_062.ml ├── sol_062.mli ├── sol_063.ml ├── sol_063.mli ├── sol_067.ml ├── sol_067.mli ├── sol_080.ml ├── sol_080.mli ├── sol_084.ml ├── sol_084.mli ├── sol_103.ml ├── sol_103.mli ├── sol_105.ml ├── sol_105.mli ├── sol_106.ml ├── sol_106.mli ├── sol_108.ml ├── sol_108.mli ├── sol_109.ml ├── sol_109.mli ├── sol_111.ml ├── sol_111.mli ├── sol_116.ml ├── sol_116.mli ├── sol_117.ml ├── sol_117.mli ├── sol_118.ml ├── sol_118.mli ├── sol_127.ml ├── sol_127.mli ├── sol_131.ml ├── sol_131.mli ├── sol_142.ml ├── sol_142.mli ├── sol_148.ml ├── sol_148.mli ├── sol_162.ml ├── sol_162.mli ├── sol_166.ml ├── sol_166.mli ├── sol_169.ml ├── sol_169.mli ├── sol_169_naive.ml ├── sol_169_naive.mli ├── sol_174.ml ├── sol_174.mli ├── sol_205.ml ├── sol_205.mli ├── sol_206.ml ├── sol_206.mli ├── sol_207.ml ├── sol_207.mli ├── sol_214.ml ├── sol_214.mli ├── sol_293.ml ├── sol_293.mli ├── sol_303.ml ├── sol_315.ml ├── sol_315.mli ├── sol_323.ml ├── sol_323.mli ├── sol_323_sim.ml ├── sol_323_sim.mli ├── sol_329.ml ├── sol_329.mli ├── sol_345.ml ├── sol_345.mli ├── sol_348.ml ├── sol_348.mli ├── sol_359.ml ├── sol_359.mli ├── sol_401.ml ├── sol_401.mli ├── sol_407.ml ├── sol_407.mli ├── sol_425.ml ├── sol_425.mli ├── sol_429.ml ├── sol_429.mli ├── sol_500.ml ├── sol_500.mli ├── sol_504.ml ├── sol_504.mli ├── sol_531.ml ├── sol_531.mli ├── sol_549.ml ├── sol_549.mli ├── sol_587.ml ├── sol_587.mli ├── sol_587_int.ml ├── sol_587_int.mli ├── sol_601.ml ├── sol_601.mli ├── sol_609.ml ├── sol_609.mli ├── sol_613.ml ├── sol_613.mli ├── sol_622.ml ├── sol_622.mli ├── sol_650.ml ├── sol_650.mli ├── sol_662.ml ├── sol_662.mli ├── sol_666.ml ├── sol_666.mli ├── sol_692.ml ├── sol_692.mli ├── sol_700.ml ├── sol_700.mli ├── sol_719.ml ├── sol_719.mli ├── sol_725.ml ├── sol_725.mli ├── sol_727.ml ├── sol_727.mli ├── sol_800.ml ├── sol_800.mli ├── sol_816.ml ├── sol_816.mli ├── sol_blank.ml ├── sol_blank.mli ├── solutions_without_answer_expect_tests.txt ├── template.ml └── template.mli ├── src ├── algebra.ml ├── algebra.mli ├── bitset.ml ├── bitset.mli ├── composition_infix.ml ├── composition_infix.mli ├── distribution.ml ├── distribution.mli ├── distribution_intf.ml ├── dune ├── euler.ml ├── geometry.ml ├── geometry.mli ├── import.ml ├── interfaces.ml ├── memo.ml ├── memo.mli ├── number_theory.ml ├── number_theory.mli ├── number_theory_intf.ml ├── numerics.ml ├── numerics.mli ├── numerics_intf.ml ├── poker.ml ├── poker.mli ├── sequences.ml └── sequences.mli └── test ├── dune ├── import.ml ├── test_algebra.ml ├── test_algebra.mli ├── test_bezout.ml ├── test_bezout.mli ├── test_bitset.ml ├── test_bitset.mli ├── test_chinese_remainder_theorem.ml ├── test_chinese_remainder_theorem.mli ├── test_digits.ml ├── test_digits.mli ├── test_distribution.ml ├── test_distribution.mli ├── test_factorial_prime_factor.ml ├── test_factorial_prime_factor.mli ├── test_integrate.ml ├── test_integrate.mli ├── test_number_theory.ml ├── test_number_theory.mli ├── test_sequences.ml └── test_sequences.mli /.dir-locals.el: -------------------------------------------------------------------------------- 1 | ((tuareg-mode . ((fill-column . 90) 2 | ))) 3 | -------------------------------------------------------------------------------- /.envrc: -------------------------------------------------------------------------------- 1 | use flake 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.annot 2 | *.cmo 3 | *.cma 4 | *.cmi 5 | *.a 6 | *.o 7 | *.cmx 8 | *.cmxs 9 | *.cmxa 10 | 11 | # ocamlbuild working directory 12 | _build/ 13 | 14 | # ocamlbuild targets 15 | *.byte 16 | *.native 17 | 18 | # oasis generated files 19 | setup.data 20 | setup.log 21 | 22 | # Merlin configuring file for Vim and Emacs 23 | .merlin 24 | 25 | # Dune generated files 26 | *.install 27 | 28 | # Local OPAM switch 29 | _opam/ 30 | -------------------------------------------------------------------------------- /.ocamlformat: -------------------------------------------------------------------------------- 1 | profile=janestreet 2 | -------------------------------------------------------------------------------- /CHANGES.md: -------------------------------------------------------------------------------- 1 | # 0.5.0 2019-05-20 New York, NY, USA 2 | 3 | - Add solutions to problems: 4 | - 111 5 | - 148 6 | - 359 7 | - 662 8 | - 666 9 | - Performance improvements to some solutions 10 | - OCamlformat all the things! 11 | - Switch from Core.Command to Cmdliner for CLI interface 12 | - Solutions are now run by the `run` command, not top-level commands. 13 | - Solutions can be listed by the `list` command. 14 | - Upgrade to Jane Street libs v0.12.0 15 | - Use Base-style map and set interfaces, preparing to reduce the dependencies to 16 | just Base where possible. 17 | 18 | # 0.4.0 2018-11-17 New York, NY, USA 19 | 20 | - Initial release using topkg. 21 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018-2020 Aaron L. Zeng 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: all check clean fmt test 2 | 3 | all: check test fmt 4 | 5 | check: 6 | dune build @check 7 | 8 | clean: 9 | dune clean 10 | 11 | fmt: 12 | dune build @fmt --auto-promote 13 | 14 | test: 15 | dune runtest --auto-promote 16 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # projecteuler-ocaml 2 | My solutions to ProjectEuler problems in OCaml. Don't spoil yourself, please! 3 | 4 | # How to run 5 | 6 | ``` 7 | dune build -p euler 8 | dune exec -p euler -- euler PROBLEM_NUMBER [-time] 9 | ``` 10 | 11 | If passed the `-time` flag, the program will report the time taken to execute 12 | the solution code. 13 | 14 | The `EULER_DEBUG` environment variable, when set to a non-empty string, will 15 | enable debug/progress printing in some solutions. 16 | -------------------------------------------------------------------------------- /TODO.org: -------------------------------------------------------------------------------- 1 | * TODO one separate library for answer tests 2 | So it's easier to run separately, and so we can test by shelling out 3 | to euler, which means we can run in parallel. 4 | 5 | Alternatively, figure out how to [[https://github.com/ocaml/dune/issues/1516][make dune run tests in parallel]]. 6 | * TODO ~Solution_intf.Arg.main~ should return a string 7 | Might be easier to automate things if it returns a string instead of 8 | printing. 9 | * TODO Add ~unimplemented~ function 10 | #+BEGIN_SRC ocaml 11 | let () = unimplemented [%here] 12 | #+END_SRC 13 | * TODO Add support for inline benchmarking 14 | E.g., using ~ppx_bench~. This might require build system support. 15 | * TODO Benchmark solutions under flambda 16 | * TODO Bind libdivide as a library and use it for constant divisions 17 | -------------------------------------------------------------------------------- /bench/dune: -------------------------------------------------------------------------------- 1 | (executables 2 | (names main) 3 | (modules main) 4 | (libraries core_bench.inline_benchmarks euler_bench)) 5 | -------------------------------------------------------------------------------- /bench/main.ml: -------------------------------------------------------------------------------- 1 | let () = Inline_benchmarks_public.Runner.main ~libname:"euler_bench" 2 | -------------------------------------------------------------------------------- /bench/runner.sh: -------------------------------------------------------------------------------- 1 | export BENCHMARKS_RUNNER=TRUE 2 | export BENCH_LIB=euler_bench 3 | exec dune exec -- ./main.exe -fork -run-without-cross-library-inlining "$@" 4 | -------------------------------------------------------------------------------- /bench/src/bench_divisors.ml: -------------------------------------------------------------------------------- 1 | open! Core 2 | open! Import 3 | 4 | let%bench ("Euler.Int.divisors" [@indexed n = [ 17; 60; 100003; 120000; 600851475143 ]]) = 5 | Number_theory.Int.divisors n 6 | ;; 7 | -------------------------------------------------------------------------------- /bench/src/bench_divisors.mli: -------------------------------------------------------------------------------- 1 | (*_ This signature is deliberately empty. *) 2 | -------------------------------------------------------------------------------- /bench/src/bench_is_prime.ml: -------------------------------------------------------------------------------- 1 | open! Core 2 | open! Import 3 | 4 | let%bench_fun ("is_prime cached" [@indexed n = [ 10; 100; 1_000; 10_000 ]]) = 5 | ignore (Number_theory.Int.is_prime n : bool); 6 | fun () -> Number_theory.Int.is_prime n 7 | ;; 8 | -------------------------------------------------------------------------------- /bench/src/bench_is_prime.mli: -------------------------------------------------------------------------------- 1 | (*_ This signature is deliberately empty. *) 2 | -------------------------------------------------------------------------------- /bench/src/bench_pow.ml: -------------------------------------------------------------------------------- 1 | open! Core 2 | open! Import 3 | 4 | let%bench_module "integer exponentiation" = 5 | (module struct 6 | let exponents = [ 0; 1; 2; 4; 5; 10; 15; 20; 25; 30 ] 7 | let%bench ("Int.pow" [@indexed exp = exponents]) = Int.pow 3 exp 8 | 9 | let%bench ("pow_fast without functor" [@indexed exp = exponents]) = 10 | Number_theory.Int.addition_chain_pow 3 exp 11 | ;; 12 | end) 13 | ;; 14 | 15 | let%bench_module "integer exponentiation with compile-time constant exponent" = 16 | (module struct 17 | let%bench "Int.pow" = Int.pow 3 5 18 | let%bench "pow_fast without functor" = Number_theory.Int.addition_chain_pow 3 5 19 | end) 20 | ;; 21 | -------------------------------------------------------------------------------- /bench/src/bench_pow.mli: -------------------------------------------------------------------------------- 1 | (*_ This signature is deliberately empty. *) 2 | -------------------------------------------------------------------------------- /bench/src/bench_prime_sieve.ml: -------------------------------------------------------------------------------- 1 | open! Core 2 | open! Import 3 | 4 | let%bench_fun "prime_sieve(10^6)" = 5 | Gc.disable_compaction ~allocation_policy:`Don't_change (); 6 | fun () -> Number_theory.prime_sieve 1_000_000 7 | ;; 8 | -------------------------------------------------------------------------------- /bench/src/bench_prime_sieve.mli: -------------------------------------------------------------------------------- 1 | (*_ This signature is deliberately empty. *) 2 | -------------------------------------------------------------------------------- /bench/src/bench_run_length_encode.ml: -------------------------------------------------------------------------------- 1 | open! Core 2 | open! Import 3 | 4 | let%bench_fun "Euler.run_length_encode" = 5 | let length = 200 in 6 | let list = 7 | List.gen_with_length length [%quickcheck.generator: bool] |> Quickcheck.random_value 8 | in 9 | fun () -> Sequences.run_length_encode list ~equal:Bool.equal 10 | ;; 11 | -------------------------------------------------------------------------------- /bench/src/bench_run_length_encode.mli: -------------------------------------------------------------------------------- 1 | (*_ This signature is deliberately empty. *) 2 | -------------------------------------------------------------------------------- /bench/src/bench_sqrt.ml: -------------------------------------------------------------------------------- 1 | open! Core 2 | open! Import 3 | 4 | let%bench_module "Newton's method" = 5 | (module struct 6 | let sqrt_newton x = 7 | Numerics.Float.newton's_method 8 | ~f:(fun y -> Float.((y * y) - x)) 9 | ~f':(Float.( * ) 2.0) 10 | ~epsilon:1e-12 11 | ~init:1.0 12 | ;; 13 | 14 | let%bench "1.0" = sqrt_newton 1.0 15 | let%bench "1.5" = sqrt_newton 1.5 16 | let%bench "2.0" = sqrt_newton 2.0 17 | let%bench "3.0" = sqrt_newton 3.0 18 | let%bench "100.0" = sqrt_newton 100.0 19 | let%bench "20_000.0" = sqrt_newton 20_000.0 20 | end) 21 | ;; 22 | 23 | let%bench_module "bisection" = 24 | (module struct 25 | let sqrt_bisect x = 26 | Numerics.Float.bisect ~f:(fun y -> Float.((y * y) - x)) ~epsilon:1e-12 ~lo:1.0 ~hi:x 27 | ;; 28 | 29 | let%bench "1.0" = sqrt_bisect 1.0 30 | let%bench "1.5" = sqrt_bisect 1.5 31 | let%bench "2.0" = sqrt_bisect 2.0 32 | let%bench "3.0" = sqrt_bisect 3.0 33 | let%bench "100.0" = sqrt_bisect 100.0 34 | let%bench "20_000.0" = sqrt_bisect 20_000.0 35 | end) 36 | ;; 37 | 38 | let%bench_module "built-in [sqrt] function" = 39 | (module struct 40 | let%bench "1.0" = sqrt 1.0 41 | let%bench "1.5" = sqrt 1.5 42 | let%bench "2.0" = sqrt 2.0 43 | let%bench "3.0" = sqrt 3.0 44 | let%bench "100.0" = sqrt 100.0 45 | let%bench "20_000.0" = sqrt 20_000.0 46 | end) 47 | ;; 48 | -------------------------------------------------------------------------------- /bench/src/bench_sqrt.mli: -------------------------------------------------------------------------------- 1 | (*_ This signature is deliberately empty. *) 2 | -------------------------------------------------------------------------------- /bench/src/dune: -------------------------------------------------------------------------------- 1 | (library 2 | (name euler_bench) 3 | (libraries euler) 4 | (library_flags -linkall) 5 | (preprocess 6 | (pps ppx_jane))) 7 | -------------------------------------------------------------------------------- /bench/src/import.ml: -------------------------------------------------------------------------------- 1 | open! Core 2 | include Euler 3 | -------------------------------------------------------------------------------- /bin/dune: -------------------------------------------------------------------------------- 1 | (executables 2 | (names main) 3 | (public_names euler) 4 | (libraries cmdliner core core_bench euler euler_solution_helpers 5 | euler_solutions) 6 | (preprocess 7 | (pps ppx_jane))) 8 | -------------------------------------------------------------------------------- /bin/import.ml: -------------------------------------------------------------------------------- 1 | open! Core 2 | include Cmdliner 3 | include Euler_solution_helpers 4 | 5 | let error_s sexp = 6 | Or_error.error_s sexp |> Result.map_error ~f:(fun e -> `Msg (Error.to_string_hum e)) 7 | ;; 8 | -------------------------------------------------------------------------------- /bin/main.ml: -------------------------------------------------------------------------------- 1 | open! Core 2 | open! Import 3 | 4 | let main = 5 | Cmd.group 6 | (Cmd.info "euler" ~doc:"Run ProjectEuler solutions" ~version:"%%VERSION%%") 7 | ~default:Term.(ret (const (`Help (`Auto, None)))) 8 | [ Solutions.list_command; Solutions.run_command ] 9 | ;; 10 | 11 | let () = Cmd.eval main |> exit 12 | -------------------------------------------------------------------------------- /bin/main.mli: -------------------------------------------------------------------------------- 1 | (*_ This signature is deliberately empty. *) 2 | -------------------------------------------------------------------------------- /bin/solutions.ml: -------------------------------------------------------------------------------- 1 | open! Core 2 | open! Import 3 | 4 | (* TODO: Allow positional arg to pick just one, maybe using a regexp? *) 5 | let list_solutions verbose = 6 | List.iter Euler_solutions.all ~f:(fun (name, (module Sol : Solution.S)) -> 7 | print_endline name; 8 | if verbose then print_endline (" " ^ Sol.description)) 9 | ;; 10 | 11 | let verbose_arg = 12 | Arg.(value & flag & info [ "v"; "verbose" ] ~doc:"Show solution descriptions") 13 | ;; 14 | 15 | let list_command = 16 | Cmd.v 17 | (Cmd.info "list" ~doc:"List solution commands") 18 | Term.(const list_solutions $ verbose_arg) 19 | ;; 20 | 21 | let debug_arg = 22 | Arg.( 23 | value 24 | & flag 25 | & info 26 | ~env:(Cmd.Env.info "EULER_DEBUG") 27 | [ "d"; "debug" ] 28 | ~doc:"Enable debug/progress printing") 29 | ;; 30 | 31 | let time_arg = Arg.(value & flag & info [ "t"; "time" ] ~doc:"Measure and print runtime") 32 | let name_arg = Arg.(required & pos 0 (some string) None & info [] ~docv:"NAME") 33 | 34 | let run_solution name debug time = 35 | match List.Assoc.find Euler_solutions.all name ~equal:String.equal with 36 | | None -> error_s [%message "No such solution found" (name : string)] 37 | | Some (module Sol) -> 38 | (match Sol.run ~print_debug:debug ~print_elapsed_time:time () with 39 | | () -> Ok () 40 | | exception e -> error_s [%message "Solution raised" (name : string) ~_:(e : exn)]) 41 | ;; 42 | 43 | let run_command = 44 | Cmd.v 45 | (Cmd.info "run" ~doc:"Run a solution (by name)") 46 | Term.(term_result (const run_solution $ name_arg $ debug_arg $ time_arg)) 47 | ;; 48 | -------------------------------------------------------------------------------- /bin/solutions.mli: -------------------------------------------------------------------------------- 1 | open! Core 2 | open! Import 3 | 4 | val list_command : unit Cmd.t 5 | val run_command : unit Cmd.t 6 | -------------------------------------------------------------------------------- /data/008.txt: -------------------------------------------------------------------------------- 1 | 7316717653133062491922511967442657474235534919493496983520312774506326239578318016984801869478851843858615607891129494954595017379583319528532088055111254069874715852386305071569329096329522744304355766896648950445244523161731856403098711121722383113622298934233803081353362766142828064444866452387493035890729629049156044077239071381051585930796086670172427121883998797908792274921901699720888093776657273330010533678812202354218097512545405947522435258490771167055601360483958644670632441572215539753697817977846174064955149290862569321978468622482839722413756570560574902614079729686524145351004748216637048440319989000889524345065854122758866688116427171479924442928230863465674813919123162824586178664583591245665294765456828489128831426076900422421902267105562632111110937054421750694165896040807198403850962455444362981230987879927244284909188845801561660979191338754992005240636899125607176060588611646710940507754100225698315520005593572972571636269561882670428252483600823257530420752963450 2 | -------------------------------------------------------------------------------- /data/011.txt: -------------------------------------------------------------------------------- 1 | 08 02 22 97 38 15 00 40 00 75 04 05 07 78 52 12 50 77 91 08 2 | 49 49 99 40 17 81 18 57 60 87 17 40 98 43 69 48 04 56 62 00 3 | 81 49 31 73 55 79 14 29 93 71 40 67 53 88 30 03 49 13 36 65 4 | 52 70 95 23 04 60 11 42 69 24 68 56 01 32 56 71 37 02 36 91 5 | 22 31 16 71 51 67 63 89 41 92 36 54 22 40 40 28 66 33 13 80 6 | 24 47 32 60 99 03 45 02 44 75 33 53 78 36 84 20 35 17 12 50 7 | 32 98 81 28 64 23 67 10 26 38 40 67 59 54 70 66 18 38 64 70 8 | 67 26 20 68 02 62 12 20 95 63 94 39 63 08 40 91 66 49 94 21 9 | 24 55 58 05 66 73 99 26 97 17 78 78 96 83 14 88 34 89 63 72 10 | 21 36 23 09 75 00 76 44 20 45 35 14 00 61 33 97 34 31 33 95 11 | 78 17 53 28 22 75 31 67 15 94 03 80 04 62 16 14 09 53 56 92 12 | 16 39 05 42 96 35 31 47 55 58 88 24 00 17 54 24 36 29 85 57 13 | 86 56 00 48 35 71 89 07 05 44 44 37 44 60 21 58 51 54 17 58 14 | 19 80 81 68 05 94 47 69 28 73 92 13 86 52 17 77 04 89 55 40 15 | 04 52 08 83 97 35 99 16 07 97 57 32 16 26 26 79 33 27 98 66 16 | 88 36 68 87 57 62 20 72 03 46 33 67 46 55 12 32 63 93 53 69 17 | 04 42 16 73 38 25 39 11 24 94 72 18 08 46 29 32 40 62 76 36 18 | 20 69 36 41 72 30 23 88 34 62 99 69 82 67 59 85 74 04 36 16 19 | 20 73 35 29 78 31 90 01 74 31 49 71 48 86 81 16 23 57 05 54 20 | 01 70 54 71 83 51 54 69 16 92 33 48 61 43 52 01 89 19 67 48 21 | -------------------------------------------------------------------------------- /data/018.txt: -------------------------------------------------------------------------------- 1 | 75 2 | 95 64 3 | 17 47 82 4 | 18 35 87 10 5 | 20 04 82 47 65 6 | 19 01 23 75 03 34 7 | 88 02 77 73 07 63 67 8 | 99 65 04 28 06 16 70 92 9 | 41 41 26 56 83 40 80 70 33 10 | 41 48 72 33 47 32 37 16 94 29 11 | 53 71 44 65 25 43 91 52 97 51 14 12 | 70 11 33 28 77 73 17 78 39 68 17 57 13 | 91 71 52 38 17 14 91 43 58 50 27 29 48 14 | 63 66 04 68 89 53 67 30 73 16 69 87 40 31 15 | 04 62 98 27 23 09 70 98 73 93 38 53 60 04 23 16 | -------------------------------------------------------------------------------- /data/345.txt: -------------------------------------------------------------------------------- 1 | 7 53 183 439 863 497 383 563 79 973 287 63 343 169 583 2 | 627 343 773 959 943 767 473 103 699 303 957 703 583 639 913 3 | 447 283 463 29 23 487 463 993 119 883 327 493 423 159 743 4 | 217 623 3 399 853 407 103 983 89 463 290 516 212 462 350 5 | 960 376 682 962 300 780 486 502 912 800 250 346 172 812 350 6 | 870 456 192 162 593 473 915 45 989 873 823 965 425 329 803 7 | 973 965 905 919 133 673 665 235 509 613 673 815 165 992 326 8 | 322 148 972 962 286 255 941 541 265 323 925 281 601 95 973 9 | 445 721 11 525 473 65 511 164 138 672 18 428 154 448 848 10 | 414 456 310 312 798 104 566 520 302 248 694 976 430 392 198 11 | 184 829 373 181 631 101 969 613 840 740 778 458 284 760 390 12 | 821 461 843 513 17 901 711 993 293 157 274 94 192 156 574 13 | 34 124 4 878 450 476 712 914 838 669 875 299 823 329 699 14 | 815 559 813 459 522 788 168 586 966 232 308 833 251 631 107 15 | 813 883 451 509 615 77 281 613 459 205 380 274 302 35 805 16 | -------------------------------------------------------------------------------- /data/dune: -------------------------------------------------------------------------------- 1 | ;; generate dune rules 2 | ;; 3 | ;; see https://jbuilder.readthedocs.io/en/latest/dune-files.html#include for 4 | ;; example 5 | 6 | (include dune.inc) 7 | 8 | (rule 9 | (targets dune.inc.gen) 10 | (deps 11 | (glob_files *.txt)) 12 | (action 13 | (with-stdout-to 14 | %{targets} 15 | (run ./gen-dune.sh)))) 16 | 17 | (rule 18 | (alias runtest) 19 | (action 20 | (diff dune.inc dune.inc.gen))) 21 | 22 | (rule 23 | (alias runtest) 24 | (deps 25 | (glob_files *.sh)) 26 | (action 27 | (run shellcheck %{deps}))) 28 | 29 | (library 30 | (name euler_data) 31 | (libraries core delimited_parsing re) 32 | (preprocess 33 | (pps ppx_jane))) 34 | 35 | (rule 36 | (targets euler_data.ml) 37 | (deps 38 | (glob_files *.txt)) 39 | (action 40 | (with-stdout-to 41 | %{targets} 42 | (run ./gen-data.sh)))) 43 | -------------------------------------------------------------------------------- /data/dune.inc: -------------------------------------------------------------------------------- 1 | (rule 2 | (targets problem_008.ml) 3 | (deps "008.txt") 4 | (action (with-stdout-to %{targets} (run ./gen-problem.sh 008)))) 5 | 6 | (rule 7 | (targets problem_011.ml) 8 | (deps "011.txt") 9 | (action (with-stdout-to %{targets} (run ./gen-problem.sh 011)))) 10 | 11 | (rule 12 | (targets problem_013.ml) 13 | (deps "013.txt") 14 | (action (with-stdout-to %{targets} (run ./gen-problem.sh 013)))) 15 | 16 | (rule 17 | (targets problem_018.ml) 18 | (deps "018.txt") 19 | (action (with-stdout-to %{targets} (run ./gen-problem.sh 018)))) 20 | 21 | (rule 22 | (targets problem_022.ml) 23 | (deps "022.txt") 24 | (action (with-stdout-to %{targets} (run ./gen-problem.sh 022)))) 25 | 26 | (rule 27 | (targets problem_042.ml) 28 | (deps "042.txt") 29 | (action (with-stdout-to %{targets} (run ./gen-problem.sh 042)))) 30 | 31 | (rule 32 | (targets problem_054.ml) 33 | (deps "054.txt") 34 | (action (with-stdout-to %{targets} (run ./gen-problem.sh 054)))) 35 | 36 | (rule 37 | (targets problem_059.ml) 38 | (deps "059.txt") 39 | (action (with-stdout-to %{targets} (run ./gen-problem.sh 059)))) 40 | 41 | (rule 42 | (targets problem_067.ml) 43 | (deps "067.txt") 44 | (action (with-stdout-to %{targets} (run ./gen-problem.sh 067)))) 45 | 46 | (rule 47 | (targets problem_105.ml) 48 | (deps "105.txt") 49 | (action (with-stdout-to %{targets} (run ./gen-problem.sh 105)))) 50 | 51 | (rule 52 | (targets problem_345.ml) 53 | (deps "345.txt") 54 | (action (with-stdout-to %{targets} (run ./gen-problem.sh 345)))) 55 | 56 | -------------------------------------------------------------------------------- /data/gen-data.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | echo "module Parse = Parse" 4 | for file in *.txt; do 5 | num="$(basename "$file" .txt)" 6 | echo "module Problem_$num = Problem_$num" 7 | done 8 | -------------------------------------------------------------------------------- /data/gen-dune.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | set -eu 4 | 5 | for file in *.txt; do 6 | num="$(basename "$file" .txt)" 7 | cat </dev/null 2>&1; then 8 | >&2 echo "$num.txt contains delimited quote, aborting" 9 | exit 1 10 | fi 11 | 12 | cat < String.split_lines 15 | |> Array.of_list_map ~f:(fun line -> 16 | line |> split_line_on_spaces |> Array.of_list_map ~f:conv) 17 | ;; 18 | 19 | let csv_line line ~f = 20 | let reader = 21 | let open Csv.Let_syntax in 22 | Csv.Row.builder >>| Csv.Row.to_list >>| List.map ~f 23 | in 24 | match line |> Csv.list_of_string reader with 25 | | [] -> failwith "no rows" 26 | | [ row ] -> row 27 | | rows -> raise_s [%message "csv_line: too many rows" ~_:(List.length rows : int)] 28 | ;; 29 | -------------------------------------------------------------------------------- /data/parse.mli: -------------------------------------------------------------------------------- 1 | open! Core 2 | open! Import 3 | 4 | val space_separated_grid : string -> conv:(string -> 'a) -> 'a array array 5 | val csv_line : string -> f:(string -> 'a) -> 'a list 6 | -------------------------------------------------------------------------------- /default.nix: -------------------------------------------------------------------------------- 1 | { lib, nix-gitignore, buildDunePackage, bignum, cmdliner, core, core_bench 2 | , delimited_parsing, expect_test_helpers_core, perl, re, shellcheck }: 3 | 4 | buildDunePackage rec { 5 | pname = "euler"; 6 | version = "0.5.0"; 7 | useDune2 = true; 8 | src = nix-gitignore.gitignoreFilterSource lib.cleanSourceFilter [ ] ./.; 9 | postPatch = '' 10 | patchShebangs sol/module-list.pl 11 | ''; 12 | buildInputs = [ 13 | bignum 14 | cmdliner 15 | core 16 | core_bench 17 | delimited_parsing 18 | expect_test_helpers_core 19 | perl 20 | re 21 | ]; 22 | checkInputs = [ shellcheck ]; 23 | passthru.checkInputs = checkInputs; 24 | meta = { homepage = "https://github.com/bcc32/projecteuler-ocaml"; }; 25 | } 26 | -------------------------------------------------------------------------------- /diagrams/587/sketch.gsp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bcc32/projecteuler-ocaml/f4f9b5a063cf951cc5ea1f291ab086cdc2c979cb/diagrams/587/sketch.gsp -------------------------------------------------------------------------------- /diagrams/587/sketch.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bcc32/projecteuler-ocaml/f4f9b5a063cf951cc5ea1f291ab086cdc2c979cb/diagrams/587/sketch.pdf -------------------------------------------------------------------------------- /diagrams/727/727.ggb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bcc32/projecteuler-ocaml/f4f9b5a063cf951cc5ea1f291ab086cdc2c979cb/diagrams/727/727.ggb -------------------------------------------------------------------------------- /diagrams/727/727.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bcc32/projecteuler-ocaml/f4f9b5a063cf951cc5ea1f291ab086cdc2c979cb/diagrams/727/727.png -------------------------------------------------------------------------------- /diagrams/727/scan.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bcc32/projecteuler-ocaml/f4f9b5a063cf951cc5ea1f291ab086cdc2c979cb/diagrams/727/scan.pdf -------------------------------------------------------------------------------- /doc/benchmark-divisors.txt: -------------------------------------------------------------------------------- 1 | Estimated testing time 50s (5 benchmarks x 10s). Change using -quota SECS. 2 | ┌─────────────────────────────────┬────────────┬───────────┬──────────┬──────────┬────────────┐ 3 | │ Name │ Time/Run │ mWd/Run │ mjWd/Run │ Prom/Run │ Percentage │ 4 | ├─────────────────────────────────┼────────────┼───────────┼──────────┼──────────┼────────────┤ 5 | │ Euler.Int.divisors:17 │ 167.42ns │ 134.01w │ │ │ 2.29% │ 6 | │ Euler.Int.divisors:60 │ 532.91ns │ 435.02w │ │ │ 7.29% │ 7 | │ Euler.Int.divisors:100003 │ 1_972.03ns │ 575.03w │ │ │ 26.99% │ 8 | │ Euler.Int.divisors:120000 │ 1_417.12ns │ 987.07w │ 0.47w │ 0.47w │ 19.39% │ 9 | │ Euler.Int.divisors:600851475143 │ 7_307.57ns │ 2_162.11w │ 0.27w │ 0.27w │ 100.00% │ 10 | └─────────────────────────────────┴────────────┴───────────┴──────────┴──────────┴────────────┘ 11 | -------------------------------------------------------------------------------- /doc/benchmark-pow.txt: -------------------------------------------------------------------------------- 1 | Estimated testing time 3.66667m (22 benchmarks x 10s). Change using -quota SECS. 2 | ┌──────────────────────────────────────────────────────────┬──────────┬────────────┐ 3 | │ Name │ Time/Run │ Percentage │ 4 | ├──────────────────────────────────────────────────────────┼──────────┼────────────┤ 5 | │ integer exponentiation/Int.pow:0 │ 8.14ns │ 74.26% │ 6 | │ integer exponentiation/Int.pow:1 │ 8.67ns │ 79.05% │ 7 | │ integer exponentiation/Int.pow:2 │ 8.67ns │ 79.05% │ 8 | │ integer exponentiation/Int.pow:4 │ 10.16ns │ 92.60% │ 9 | │ integer exponentiation/Int.pow:5 │ 10.15ns │ 92.59% │ 10 | │ integer exponentiation/Int.pow:10 │ 10.15ns │ 92.60% │ 11 | │ integer exponentiation/Int.pow:15 │ 10.16ns │ 92.62% │ 12 | │ integer exponentiation/Int.pow:20 │ 10.97ns │ 99.99% │ 13 | │ integer exponentiation/Int.pow:25 │ 10.95ns │ 99.87% │ 14 | │ integer exponentiation/Int.pow:30 │ 10.97ns │ 100.00% │ 15 | │ integer exponentiation/pow_fast without functor:0 │ 2.67ns │ 24.39% │ 16 | │ integer exponentiation/pow_fast without functor:1 │ 2.68ns │ 24.45% │ 17 | │ integer exponentiation/pow_fast without functor:2 │ 2.67ns │ 24.38% │ 18 | │ integer exponentiation/pow_fast without functor:4 │ 2.67ns │ 24.38% │ 19 | │ integer exponentiation/pow_fast without functor:5 │ 2.68ns │ 24.41% │ 20 | │ integer exponentiation/pow_fast without functor:10 │ 2.68ns │ 24.43% │ 21 | │ integer exponentiation/pow_fast without functor:15 │ 2.68ns │ 24.43% │ 22 | │ integer exponentiation/pow_fast without functor:20 │ 2.68ns │ 24.42% │ 23 | │ integer exponentiation/pow_fast without functor:25 │ 2.68ns │ 24.44% │ 24 | │ integer exponentiation/pow_fast without functor:30 │ 2.67ns │ 24.36% │ 25 | │ integer exponentiation/Int.pow const(5) │ 7.13ns │ 64.99% │ 26 | │ integer exponentiation/pow_fast without functor const(5) │ 2.08ns │ 18.95% │ 27 | └──────────────────────────────────────────────────────────┴──────────┴────────────┘ 28 | -------------------------------------------------------------------------------- /doc/benchmark-primes.txt: -------------------------------------------------------------------------------- 1 | Estimated testing time 1.33333m (8 benchmarks x 10s). Change using -quota SECS. 2 | ┌────────────────────────────────────┬──────────────┬────────────────┬───────────────┬──────────┬────────────┐ 3 | │ Name │ Time/Run │ mWd/Run │ mjWd/Run │ Prom/Run │ Percentage │ 4 | ├────────────────────────────────────┼──────────────┼────────────────┼───────────────┼──────────┼────────────┤ 5 | │ primes/Sequence.t/1000 │ 105.19us │ 12_346.62w │ 0.71w │ 0.71w │ 0.03% │ 6 | │ primes/Sequence.t/10000 │ 1_432.16us │ 116_991.77w │ 6.69w │ 6.69w │ 0.38% │ 7 | │ primes/Sequence.t/100000 │ 22_080.75us │ 1_139_115.67w │ 33.65w │ 33.65w │ 5.85% │ 8 | │ primes/Sequence.t/1000000 │ 377_517.75us │ 11_197_297.15w │ 266.96w │ 266.96w │ 100.00% │ 9 | │ primes/Eratosthenes' sieve/1000 │ 6.74us │ 12.01w │ 1_002.00w │ │ │ 10 | │ primes/Eratosthenes' sieve/10000 │ 69.13us │ 12.07w │ 10_002.00w │ │ 0.02% │ 11 | │ primes/Eratosthenes' sieve/100000 │ 831.57us │ 12.67w │ 100_002.01w │ │ 0.22% │ 12 | │ primes/Eratosthenes' sieve/1000000 │ 11_907.56us │ 14.40w │ 1_000_002.01w │ │ 3.15% │ 13 | └────────────────────────────────────┴──────────────┴────────────────┴───────────────┴──────────┴────────────┘ 14 | Benchmarks that take 1ns to 100ms can be estimated precisely. For more reliable 15 | estimates, redesign your benchmark to have a shorter execution time. 16 | -------------------------------------------------------------------------------- /doc/benchmark-rle.txt: -------------------------------------------------------------------------------- 1 | Estimated testing time 10s (1 benchmarks x 10s). Change using -quota SECS. 2 | ┌─────────────────────────┬──────────┬─────────┬──────────┬──────────┬────────────┐ 3 | │ Name │ Time/Run │ mWd/Run │ mjWd/Run │ Prom/Run │ Percentage │ 4 | ├─────────────────────────┼──────────┼─────────┼──────────┼──────────┼────────────┤ 5 | │ Euler.run_length_encode │ 1.91us │ 1.80kw │ 5.45w │ 5.45w │ 100.00% │ 6 | └─────────────────────────┴──────────┴─────────┴──────────┴──────────┴────────────┘ 7 | -------------------------------------------------------------------------------- /doc/benchmark-sqrt.txt: -------------------------------------------------------------------------------- 1 | Estimated testing time 3m (18 benchmarks x 10s). Change using -quota SECS. 2 | ┌──────────────────────────────────────┬──────────┬─────────┬────────────┐ 3 | │ Name │ Time/Run │ mWd/Run │ Percentage │ 4 | ├──────────────────────────────────────┼──────────┼─────────┼────────────┤ 5 | │ sqrt/Newton's method/1. │ 7.83ns │ 8.00w │ 1.58% │ 6 | │ sqrt/Newton's method/1.5 │ 46.39ns │ 32.00w │ 9.34% │ 7 | │ sqrt/Newton's method/2. │ 59.44ns │ 38.00w │ 11.97% │ 8 | │ sqrt/Newton's method/3. │ 59.88ns │ 38.00w │ 12.06% │ 9 | │ sqrt/Newton's method/100. │ 127.37ns │ 56.00w │ 25.65% │ 10 | │ sqrt/Newton's method/20000. │ 182.20ns │ 80.00w │ 36.69% │ 11 | │ sqrt/bisection/1. │ 12.18ns │ 16.00w │ 2.45% │ 12 | │ sqrt/bisection/1.5 │ 331.66ns │ 250.00w │ 66.80% │ 13 | │ sqrt/bisection/2. │ 363.36ns │ 256.00w │ 73.18% │ 14 | │ sqrt/bisection/3. │ 349.09ns │ 262.00w │ 70.31% │ 15 | │ sqrt/bisection/100. │ 408.88ns │ 298.01w │ 82.35% │ 16 | │ sqrt/bisection/20000. │ 496.53ns │ 346.01w │ 100.00% │ 17 | │ sqrt/built-in [sqrt] function/1. │ 3.02ns │ 2.00w │ 0.61% │ 18 | │ sqrt/built-in [sqrt] function/1.5 │ 5.97ns │ 2.00w │ 1.20% │ 19 | │ sqrt/built-in [sqrt] function/2. │ 5.97ns │ 2.00w │ 1.20% │ 20 | │ sqrt/built-in [sqrt] function/3. │ 5.97ns │ 2.00w │ 1.20% │ 21 | │ sqrt/built-in [sqrt] function/100. │ 5.98ns │ 2.00w │ 1.20% │ 22 | │ sqrt/built-in [sqrt] function/20000. │ 5.96ns │ 2.00w │ 1.20% │ 23 | └──────────────────────────────────────┴──────────┴─────────┴────────────┘ 24 | -------------------------------------------------------------------------------- /dune: -------------------------------------------------------------------------------- 1 | (data_only_dirs pkg) 2 | 3 | (rule 4 | (alias runtest) 5 | (deps run-solutions.sh) 6 | (action 7 | (run shellcheck %{deps}))) 8 | -------------------------------------------------------------------------------- /dune-project: -------------------------------------------------------------------------------- 1 | (lang dune 2.0) 2 | (name euler) 3 | -------------------------------------------------------------------------------- /euler.opam: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bcc32/projecteuler-ocaml/f4f9b5a063cf951cc5ea1f291ab086cdc2c979cb/euler.opam -------------------------------------------------------------------------------- /flake.lock: -------------------------------------------------------------------------------- 1 | { 2 | "nodes": { 3 | "flake-utils": { 4 | "inputs": { 5 | "systems": "systems" 6 | }, 7 | "locked": { 8 | "lastModified": 1731533236, 9 | "narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=", 10 | "owner": "numtide", 11 | "repo": "flake-utils", 12 | "rev": "11707dc2f618dd54ca8739b309ec4fc024de578b", 13 | "type": "github" 14 | }, 15 | "original": { 16 | "owner": "numtide", 17 | "repo": "flake-utils", 18 | "type": "github" 19 | } 20 | }, 21 | "nixpkgs": { 22 | "locked": { 23 | "lastModified": 1736241350, 24 | "narHash": "sha256-CHd7yhaDigUuJyDeX0SADbTM9FXfiWaeNyY34FL1wQU=", 25 | "owner": "NixOS", 26 | "repo": "nixpkgs", 27 | "rev": "8c9fd3e564728e90829ee7dbac6edc972971cd0f", 28 | "type": "github" 29 | }, 30 | "original": { 31 | "owner": "NixOS", 32 | "ref": "nixpkgs-unstable", 33 | "repo": "nixpkgs", 34 | "type": "github" 35 | } 36 | }, 37 | "ocaml-overlays": { 38 | "inputs": { 39 | "nixpkgs": [ 40 | "nixpkgs" 41 | ] 42 | }, 43 | "locked": { 44 | "lastModified": 1736236448, 45 | "narHash": "sha256-cA4np0psOb0BMe1858PwOi1njt+JjHuZ7hmCkmIxeWY=", 46 | "owner": "nix-ocaml", 47 | "repo": "nix-overlays", 48 | "rev": "f740eb85a31066b68ccc121f8d05f0cc2ec451ed", 49 | "type": "github" 50 | }, 51 | "original": { 52 | "owner": "nix-ocaml", 53 | "repo": "nix-overlays", 54 | "type": "github" 55 | } 56 | }, 57 | "root": { 58 | "inputs": { 59 | "flake-utils": "flake-utils", 60 | "nixpkgs": "nixpkgs", 61 | "ocaml-overlays": "ocaml-overlays" 62 | } 63 | }, 64 | "systems": { 65 | "locked": { 66 | "lastModified": 1681028828, 67 | "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", 68 | "owner": "nix-systems", 69 | "repo": "default", 70 | "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", 71 | "type": "github" 72 | }, 73 | "original": { 74 | "owner": "nix-systems", 75 | "repo": "default", 76 | "type": "github" 77 | } 78 | } 79 | }, 80 | "root": "root", 81 | "version": 7 82 | } 83 | -------------------------------------------------------------------------------- /flake.nix: -------------------------------------------------------------------------------- 1 | { 2 | description = "Solutions to ProjectEuler problems in OCaml"; 3 | 4 | inputs = { 5 | nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable"; 6 | flake-utils.url = "github:numtide/flake-utils"; 7 | ocaml-overlays.url = "github:nix-ocaml/nix-overlays"; 8 | ocaml-overlays.inputs.nixpkgs.follows = "nixpkgs"; 9 | }; 10 | 11 | outputs = 12 | { 13 | self, 14 | flake-utils, 15 | nixpkgs, 16 | ocaml-overlays, 17 | }: 18 | flake-utils.lib.eachDefaultSystem ( 19 | system: 20 | let 21 | pkgs = import nixpkgs { 22 | inherit system; 23 | overlays = [ ocaml-overlays.overlays.default ]; 24 | }; 25 | in 26 | with pkgs; 27 | rec { 28 | devShells.default = mkShell { 29 | inputsFrom = [ packages.default ]; 30 | buildInputs = lib.optional stdenv.isLinux inotify-tools ++ [ 31 | ocamlPackages.merlin 32 | ocamlformat 33 | ocamlPackages.ocaml-lsp 34 | ocamlPackages.ocp-indent 35 | ocamlPackages.utop 36 | ]; 37 | }; 38 | 39 | packages.default = ocamlPackages.buildDunePackage rec { 40 | pname = "euler"; 41 | version = "0.5.0"; 42 | useDune2 = true; 43 | src = nix-gitignore.gitignoreFilterSource lib.cleanSourceFilter [ ] ./.; 44 | postPatch = '' 45 | patchShebangs sol/module-list.pl 46 | ''; 47 | buildInputs = with ocamlPackages; [ 48 | bignum 49 | cmdliner 50 | core 51 | core_bench 52 | delimited_parsing 53 | expect_test_helpers_core 54 | perl 55 | re 56 | ]; 57 | checkInputs = [ shellcheck ]; 58 | passthru.checkInputs = checkInputs; 59 | meta = { 60 | homepage = "https://github.com/bcc32/projecteuler-ocaml"; 61 | }; 62 | }; 63 | } 64 | ); 65 | } 66 | -------------------------------------------------------------------------------- /pkg/pkg.ml: -------------------------------------------------------------------------------- 1 | #use "topfind" 2 | #require "topkg-jbuilder.auto" 3 | -------------------------------------------------------------------------------- /release.nix: -------------------------------------------------------------------------------- 1 | { pkgs ? import { } }: 2 | 3 | pkgs.ocamlPackages.callPackage ./. { } 4 | -------------------------------------------------------------------------------- /run-solutions.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -euxo pipefail 4 | 5 | list_solutions() { 6 | dune exec -p euler -- euler list | grep -v naive 7 | } 8 | 9 | run_solution() { 10 | sol=$1 11 | echo "$sol" 12 | dune exec -p euler --no-build -- euler run "$sol" --time 13 | } 14 | export -f run_solution 15 | 16 | echo "building..." 17 | dune build -p euler 18 | echo "done building" 19 | 20 | if command -v parallel >/dev/null 2>&1; then 21 | list_solutions \ 22 | | parallel --keep-order run_solution 23 | else 24 | for i in $(list_solutions); do 25 | run_solution "$i" 26 | done 27 | fi 28 | -------------------------------------------------------------------------------- /shell.nix: -------------------------------------------------------------------------------- 1 | { pkgs ? import { } }: 2 | 3 | with pkgs; 4 | let pkg = ocamlPackages.callPackage ./. { }; 5 | in mkShell { 6 | inputsFrom = [ pkg ]; 7 | buildInputs = pkg.checkInputs ++ [ 8 | inotify-tools 9 | ocamlPackages.merlin 10 | ocamlformat 11 | ocamlPackages.ocp-indent 12 | ocamlPackages.utop 13 | ]; 14 | } 15 | -------------------------------------------------------------------------------- /sol/dune: -------------------------------------------------------------------------------- 1 | (library 2 | (name euler_solutions) 3 | (synopsis "A collection of OCaml solutions to ProjectEuler problems") 4 | (libraries bignum core core_kernel.hash_heap core_kernel.total_map euler 5 | euler_data euler_solution_helpers expect_test_helpers_core re) 6 | (preprocess 7 | (pps ppx_jane)) 8 | (inline_tests) 9 | (modules 10 | (:standard \ template))) 11 | 12 | (rule 13 | (target euler_solutions.ml) 14 | (deps 15 | (:script ./module-list.pl) 16 | (glob_files sol*.ml)) 17 | (action 18 | (with-stdout-to 19 | %{target} 20 | (run %{script})))) 21 | 22 | (rule 23 | (target solutions_without_answer_expect_tests.txt.gen) 24 | (deps 25 | (glob_files sol*.ml)) 26 | (action 27 | (with-stdout-to 28 | %{target} 29 | (run grep -L "\\.*\\" --exclude=*.pp.ml %{deps})))) 30 | 31 | (rule 32 | (alias runtest) 33 | (action 34 | (diff solutions_without_answer_expect_tests.txt 35 | solutions_without_answer_expect_tests.txt.gen))) 36 | -------------------------------------------------------------------------------- /sol/euler_solutions.mli: -------------------------------------------------------------------------------- 1 | open! Core 2 | open! Import 3 | 4 | val all : (string, (module Solution.S)) List.Assoc.t 5 | -------------------------------------------------------------------------------- /sol/import.ml: -------------------------------------------------------------------------------- 1 | open! Core 2 | include Euler 3 | include Euler_data 4 | include Euler_solution_helpers 5 | include Expect_test_helpers_core 6 | -------------------------------------------------------------------------------- /sol/lib/debug_printing.ml: -------------------------------------------------------------------------------- 1 | open! Core 2 | open! Import 3 | 4 | (* TODO: Shadow Debug module so that functions automatically check 5 | [debug] flag. *) 6 | 7 | module Export = struct 8 | let debug = 9 | match Sys.getenv "EULER_DEBUG" with 10 | | None | Some "" -> false 11 | | Some _ -> true 12 | ;; 13 | 14 | let debug_timing here name f = 15 | if not debug 16 | then f () 17 | else ( 18 | let start = Time_ns.now () in 19 | Debug.eprint_s [%message "starting" (name : string) (here : Source_code_position.t)]; 20 | protect ~f ~finally:(fun () -> 21 | let end_ = Time_ns.now () in 22 | let elapsed = Time_ns.diff end_ start in 23 | Debug.eprint_s 24 | [%message 25 | "done" 26 | (name : string) 27 | (here : Source_code_position.t) 28 | (elapsed : Time_ns.Span.t)])) 29 | ;; 30 | end 31 | -------------------------------------------------------------------------------- /sol/lib/debug_printing.mli: -------------------------------------------------------------------------------- 1 | open! Core 2 | open! Import 3 | 4 | module Export : sig 5 | (** When true, enable debugging and progress printing in various places in 6 | solutions. 7 | 8 | Enabled by setting the [EULER_DEBUG] environment variable to a non-empty 9 | string. *) 10 | val debug : bool 11 | 12 | (** When [debug] is true, [debug_timing here f x] prints the elapsed time taken to 13 | evaluate (f x) and returns the result. *) 14 | val debug_timing : Source_code_position.t -> string -> (unit -> 'a) -> 'a 15 | end 16 | -------------------------------------------------------------------------------- /sol/lib/dune: -------------------------------------------------------------------------------- 1 | (library 2 | (name euler_solution_helpers) 3 | (libraries core core_unix core_unix.sys_unix) 4 | (preprocess 5 | (pps ppx_jane)) 6 | (inline_tests)) 7 | -------------------------------------------------------------------------------- /sol/lib/euler_solution_helpers.ml: -------------------------------------------------------------------------------- 1 | open! Core 2 | open! Import 3 | module Solution = Solution 4 | include Debug_printing.Export 5 | -------------------------------------------------------------------------------- /sol/lib/import.ml: -------------------------------------------------------------------------------- 1 | open! Core 2 | -------------------------------------------------------------------------------- /sol/lib/solution.ml: -------------------------------------------------------------------------------- 1 | open! Core 2 | open! Import 3 | include Solution_intf 4 | 5 | let time_unit f () = 6 | let start = Time_ns.now () in 7 | protect ~f ~finally:(fun () -> 8 | let finish = Time_ns.now () in 9 | print_s [%sexp (Time_ns.diff finish start : Time_ns.Span.t)]) 10 | ;; 11 | 12 | type t = ?print_debug:bool -> ?print_elapsed_time:bool -> unit -> unit 13 | 14 | let make ~(problem : Solution_id.t) ~main = 15 | (module struct 16 | let name = 17 | match problem with 18 | | Number n -> Int.to_string n 19 | | Tagged { number; tag; description = _ } -> sprintf "%d-%s" number tag 20 | | Custom { name; description = _ } -> name 21 | ;; 22 | 23 | let description = 24 | match problem with 25 | | Number n -> sprintf "Problem %d" n 26 | | Tagged { number; tag = _; description } -> 27 | sprintf "Problem %d (%s)" number description 28 | | Custom { name = _; description } -> description 29 | ;; 30 | 31 | let run ?(print_debug = false) ?(print_elapsed_time = false) () = 32 | (* FIXME: Don't set [debug] through an environment variable, make it a ref or a 33 | [Set_once]. *) 34 | (* re-exec self with [EULER_DEBUG] set *) 35 | if print_debug && not Debug_printing.Export.debug 36 | then 37 | never_returns 38 | (Core_unix.exec 39 | () 40 | ~prog:Sys_unix.executable_name 41 | ~argv:(Array.to_list (Sys.get_argv ())) 42 | ~env:(`Extend [ "EULER_DEBUG", "1" ])); 43 | if print_elapsed_time then time_unit main () else main () 44 | ;; 45 | end : S) 46 | ;; 47 | -------------------------------------------------------------------------------- /sol/lib/solution.mli: -------------------------------------------------------------------------------- 1 | include Solution_intf.Solution (** @inline *) 2 | -------------------------------------------------------------------------------- /sol/lib/solution_intf.ml: -------------------------------------------------------------------------------- 1 | (** Solutions to Project Euler problems, with conveniences for timing and 2 | command-line parsing. *) 3 | 4 | open! Core 5 | open! Import 6 | 7 | module Solution_id = struct 8 | type t = 9 | | Number of int 10 | | Tagged of 11 | { number : int 12 | ; tag : string 13 | ; description : string 14 | } 15 | | Custom of 16 | { name : string 17 | ; description : string 18 | } 19 | end 20 | 21 | module type Arg = sig 22 | val problem : Solution_id.t 23 | val main : unit -> unit 24 | end 25 | 26 | module type S = sig 27 | (** Name to identify this solution. *) 28 | val name : string 29 | 30 | (** One-line human-readable description of this solution. *) 31 | val description : string 32 | 33 | (** [run ?print_debug ?print_elapsed_time ()] this solution, maybe printing 34 | progress/debug output and at the end, the time elapsed. 35 | 36 | @param print_debug default = false 37 | @param print_elapsed_time default = false *) 38 | val run : ?print_debug:bool -> ?print_elapsed_time:bool -> unit -> unit 39 | end 40 | 41 | module type Solution = sig 42 | module type Arg = Arg 43 | module type S = S 44 | 45 | type t = ?print_debug:bool -> ?print_elapsed_time:bool -> unit -> unit 46 | 47 | val make : problem:Solution_id.t -> main:(unit -> unit) -> (module S) 48 | end 49 | -------------------------------------------------------------------------------- /sol/module-list.pl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env perl 2 | use 5.016; 3 | use warnings; 4 | 5 | my @modules; 6 | 7 | for (glob 'sol*.ml') { 8 | if (/\A (? sol [^.]*) [.]ml \z/xms) { 9 | push @modules, ucfirst $+{module_name}; 10 | } 11 | } 12 | 13 | print < List.filter ~f:(fun x -> x mod 3 = 0 || x mod 5 = 0) 7 | |> List.sum (module Int) ~f:Fn.id 8 | |> printf "%d\n" 9 | ;; 10 | 11 | let%expect_test "answer" = 12 | main (); 13 | [%expect {| 233168 |}] 14 | ;; 15 | 16 | include (val Solution.make ~problem:(Number 1) ~main) 17 | -------------------------------------------------------------------------------- /sol/sol_001.mli: -------------------------------------------------------------------------------- 1 | open! Core 2 | open! Import 3 | include Solution.S 4 | -------------------------------------------------------------------------------- /sol/sol_002.ml: -------------------------------------------------------------------------------- 1 | open! Core 2 | open! Import 3 | 4 | let main () = 5 | Number_theory.Int.fibonacci 6 | |> Sequence.take_while ~f:(fun x -> x <= 4000000) 7 | |> Sequence.filter ~f:(fun x -> x mod 2 = 0) 8 | |> Sequence.sum (module Int) ~f:Fn.id 9 | |> printf "%d\n" 10 | ;; 11 | 12 | (* 6us *) 13 | let%expect_test "answer" = 14 | main (); 15 | [%expect {| 4613732 |}] 16 | ;; 17 | 18 | include (val Solution.make ~problem:(Number 2) ~main) 19 | -------------------------------------------------------------------------------- /sol/sol_002.mli: -------------------------------------------------------------------------------- 1 | open! Core 2 | open! Import 3 | include Solution.S 4 | -------------------------------------------------------------------------------- /sol/sol_003.ml: -------------------------------------------------------------------------------- 1 | open! Core 2 | open! Import 3 | 4 | let main () = Number_theory.Int.factor 600851475143 |> List.last_exn |> printf "%d\n" 5 | 6 | (* 97.713us *) 7 | let%expect_test "answer" = 8 | main (); 9 | [%expect {| 6857 |}] 10 | ;; 11 | 12 | include (val Solution.make ~problem:(Number 3) ~main) 13 | -------------------------------------------------------------------------------- /sol/sol_003.mli: -------------------------------------------------------------------------------- 1 | open! Core 2 | open! Import 3 | include Solution.S 4 | -------------------------------------------------------------------------------- /sol/sol_004.ml: -------------------------------------------------------------------------------- 1 | open! Core 2 | open! Import 3 | 4 | let is_palindrome s = String.( = ) s (String.rev s) 5 | 6 | let main () = 7 | let ans = ref 0 in 8 | for i = 100 to 999 do 9 | for j = 100 to 999 do 10 | let digits = i * j |> Int.to_string in 11 | if is_palindrome digits && i * j > !ans then ans := i * j 12 | done 13 | done; 14 | printf "%d\n" !ans 15 | ;; 16 | 17 | let%expect_test "answer" = 18 | main (); 19 | [%expect {| 906609 |}] 20 | ;; 21 | 22 | include (val Solution.make ~problem:(Number 4) ~main) 23 | -------------------------------------------------------------------------------- /sol/sol_004.mli: -------------------------------------------------------------------------------- 1 | open! Core 2 | open! Import 3 | include Solution.S 4 | -------------------------------------------------------------------------------- /sol/sol_005.ml: -------------------------------------------------------------------------------- 1 | open! Core 2 | open! Import 3 | 4 | let main () = 5 | List.range ~stop:`inclusive 1 20 6 | |> List.fold ~init:1 ~f:Number_theory.Int.lcm 7 | |> printf "%d\n" 8 | ;; 9 | 10 | let%expect_test "answer" = 11 | main (); 12 | [%expect {| 232792560 |}] 13 | ;; 14 | 15 | include (val Solution.make ~problem:(Number 5) ~main) 16 | -------------------------------------------------------------------------------- /sol/sol_005.mli: -------------------------------------------------------------------------------- 1 | open! Core 2 | open! Import 3 | include Solution.S 4 | -------------------------------------------------------------------------------- /sol/sol_006.ml: -------------------------------------------------------------------------------- 1 | open! Core 2 | open! Import 3 | 4 | let hund = List.range ~stop:`inclusive 1 100 5 | let sum = List.sum (module Int) ~f:Fn.id 6 | let sqr x = x * x 7 | let main () = sqr (sum hund) - sum (List.map ~f:sqr hund) |> printf "%d\n" 8 | 9 | let%expect_test "answer" = 10 | main (); 11 | [%expect {| 25164150 |}] 12 | ;; 13 | 14 | include (val Solution.make ~problem:(Number 6) ~main) 15 | -------------------------------------------------------------------------------- /sol/sol_006.mli: -------------------------------------------------------------------------------- 1 | open! Core 2 | open! Import 3 | include Solution.S 4 | -------------------------------------------------------------------------------- /sol/sol_007.ml: -------------------------------------------------------------------------------- 1 | open! Core 2 | open! Import 3 | 4 | let main () = 5 | Sequence.nth_exn Number_theory.Int.primes 10000 (* 0-index *) |> printf "%d\n" 6 | ;; 7 | 8 | let%expect_test "answer" = 9 | main (); 10 | [%expect {| 104743 |}] 11 | ;; 12 | 13 | include (val Solution.make ~problem:(Number 7) ~main) 14 | -------------------------------------------------------------------------------- /sol/sol_007.mli: -------------------------------------------------------------------------------- 1 | open! Core 2 | open! Import 3 | include Solution.S 4 | -------------------------------------------------------------------------------- /sol/sol_008.ml: -------------------------------------------------------------------------------- 1 | open! Core 2 | open! Import 3 | 4 | let doit str = 5 | Sequence.range 0 (String.length str - 12) 6 | |> Sequence.map ~f:(fun i -> 7 | String.sub str ~pos:i ~len:13 8 | |> Sequences.digits_of_string 9 | |> List.fold ~init:1 ~f:( * )) 10 | |> Sequence.max_elt ~compare:Int.compare 11 | |> Option.value_exn 12 | ;; 13 | 14 | let main () = doit Problem_008.data |> printf "%d\n" 15 | 16 | (* 3.3ms *) 17 | let%expect_test "answer" = 18 | main (); 19 | [%expect {| 23514624000 |}] 20 | ;; 21 | 22 | include (val Solution.make ~problem:(Number 8) ~main) 23 | -------------------------------------------------------------------------------- /sol/sol_008.mli: -------------------------------------------------------------------------------- 1 | open! Core 2 | open! Import 3 | include Solution.S 4 | -------------------------------------------------------------------------------- /sol/sol_009.ml: -------------------------------------------------------------------------------- 1 | open! Core 2 | open! Import 3 | 4 | let main () = 5 | with_return_option (fun r -> 6 | for c = 5 to 500 do 7 | for b = 4 to c - 1 do 8 | let a = 1000 - b - c in 9 | if Geometry.is_pythagorean_triple a b c then r.return (a * b * c) 10 | done 11 | done) 12 | |> Option.value_exn 13 | |> printf "%d\n" 14 | ;; 15 | 16 | let%expect_test "answer" = 17 | main (); 18 | [%expect {| 31875000 |}] 19 | ;; 20 | 21 | include (val Solution.make ~problem:(Number 9) ~main) 22 | -------------------------------------------------------------------------------- /sol/sol_009.mli: -------------------------------------------------------------------------------- 1 | open! Core 2 | open! Import 3 | include Solution.S 4 | -------------------------------------------------------------------------------- /sol/sol_010.ml: -------------------------------------------------------------------------------- 1 | open! Core 2 | open! Import 3 | 4 | let main () = 5 | let limit = 2_000_000 in 6 | let is_prime = Number_theory.prime_sieve limit in 7 | let sum = ref 0 in 8 | for i = 2 to limit do 9 | if is_prime.(i) then sum := !sum + i 10 | done; 11 | printf "%d\n" !sum 12 | ;; 13 | 14 | let%expect_test "answer" = 15 | main (); 16 | [%expect {| 142913828922 |}] 17 | ;; 18 | 19 | include (val Solution.make ~problem:(Number 10) ~main) 20 | -------------------------------------------------------------------------------- /sol/sol_010.mli: -------------------------------------------------------------------------------- 1 | open! Core 2 | open! Import 3 | include Solution.S 4 | -------------------------------------------------------------------------------- /sol/sol_010_seq.ml: -------------------------------------------------------------------------------- 1 | open! Core 2 | open! Import 3 | 4 | let main () = 5 | Number_theory.Int.primes 6 | |> Sequence.take_while ~f:(fun x -> x < 2_000_000) 7 | |> Sequence.sum (module Int) ~f:Fn.id 8 | |> printf "%d\n" 9 | ;; 10 | 11 | (* 142913828922 12 | 912.866719ms *) 13 | 14 | include 15 | (val Solution.make 16 | ~problem: 17 | (Tagged { number = 10; tag = "seq"; description = "using primes Sequence.t" }) 18 | ~main) 19 | -------------------------------------------------------------------------------- /sol/sol_010_seq.mli: -------------------------------------------------------------------------------- 1 | open! Core 2 | open! Import 3 | include Solution.S 4 | -------------------------------------------------------------------------------- /sol/sol_011.ml: -------------------------------------------------------------------------------- 1 | open! Core 2 | open! Import 3 | 4 | let size = 20 5 | let product_size = 4 6 | 7 | let horizontal_products grid = 8 | let open Sequence.Let_syntax in 9 | let%bind row = Array.to_sequence grid in 10 | let%map i = Sequence.range ~stop:`inclusive 0 (size - product_size) in 11 | Sequence.fold 12 | ~init:1 13 | ~f:( * ) 14 | (let%map j = Sequence.range i (i + product_size) in 15 | row.(j)) 16 | ;; 17 | 18 | let vertical_products grid = horizontal_products (Array.transpose_exn grid) 19 | 20 | let lr_diagonal_products grid = 21 | let open Sequence.Let_syntax in 22 | let indices = Sequence.range ~stop:`inclusive 0 (size - product_size) in 23 | let%bind i = indices in 24 | let%map j = indices in 25 | Sequence.fold 26 | ~init:1 27 | ~f:( * ) 28 | (let%map x = Sequence.range 0 product_size in 29 | grid.(i + x).(j + x)) 30 | ;; 31 | 32 | let rl_diagonal_products grid = 33 | let grid = Array.copy grid in 34 | Array.rev_inplace grid; 35 | lr_diagonal_products grid 36 | ;; 37 | 38 | let main () = 39 | let grid = Problem_011.data |> Parse.space_separated_grid ~conv:Int.of_string in 40 | [ horizontal_products grid 41 | ; vertical_products grid 42 | ; lr_diagonal_products grid 43 | ; rl_diagonal_products grid 44 | ] 45 | |> Sequence.of_list 46 | |> Sequence.concat 47 | |> Sequence.max_elt ~compare:Int.compare 48 | |> Option.value_exn 49 | |> printf "%d\n" 50 | ;; 51 | 52 | (* 148.681us *) 53 | let%expect_test "answer" = 54 | main (); 55 | [%expect {| 70600674 |}] 56 | ;; 57 | 58 | include (val Solution.make ~problem:(Number 11) ~main) 59 | -------------------------------------------------------------------------------- /sol/sol_011.mli: -------------------------------------------------------------------------------- 1 | open! Core 2 | open! Import 3 | include Solution.S 4 | -------------------------------------------------------------------------------- /sol/sol_012.ml: -------------------------------------------------------------------------------- 1 | open! Core 2 | open! Import 3 | 4 | let triangle n = n * (n + 1) / 2 5 | 6 | let main () = 7 | let i = ref 1 in 8 | while Number_theory.Int.num_divisors (triangle !i) <= 500 do 9 | Int.incr i 10 | done; 11 | printf "%d\n" (triangle !i) 12 | ;; 13 | 14 | let%expect_test "answer" = 15 | main (); 16 | [%expect {| 76576500 |}] 17 | ;; 18 | 19 | include (val Solution.make ~problem:(Number 12) ~main) 20 | -------------------------------------------------------------------------------- /sol/sol_012.mli: -------------------------------------------------------------------------------- 1 | open! Core 2 | open! Import 3 | include Solution.S 4 | -------------------------------------------------------------------------------- /sol/sol_013.ml: -------------------------------------------------------------------------------- 1 | open! Core 2 | open! Import 3 | 4 | let prefix_length = 10 5 | 6 | let main () = 7 | Problem_013.data 8 | |> String.split_lines 9 | |> List.sum (module Bigint) ~f:Bigint.of_string 10 | |> Bigint.to_string 11 | |> String.subo ~len:prefix_length 12 | |> printf "%s\n" 13 | ;; 14 | 15 | (* 0.11ms *) 16 | let%expect_test "answer" = 17 | main (); 18 | [%expect {| 5537376230 |}] 19 | ;; 20 | 21 | include (val Solution.make ~problem:(Number 13) ~main) 22 | -------------------------------------------------------------------------------- /sol/sol_013.mli: -------------------------------------------------------------------------------- 1 | open! Core 2 | open! Import 3 | include Solution.S 4 | -------------------------------------------------------------------------------- /sol/sol_014.ml: -------------------------------------------------------------------------------- 1 | open! Core 2 | open! Import 3 | 4 | let limit = 1_000_000 5 | let collatz n = if n mod 2 = 0 then n / 2 else (3 * n) + 1 6 | 7 | let rec collatz_length = 8 | let cache = 9 | lazy 10 | (let cache = Option_array.create ~len:(limit + 1) in 11 | Option_array.set_some cache 1 1; 12 | cache) 13 | in 14 | fun n -> 15 | let cache = force cache in 16 | if n <= limit && Option_array.is_some cache n 17 | then Option_array.unsafe_get_some_exn cache n 18 | else ( 19 | let length = collatz_length (collatz n) + 1 in 20 | if n <= limit then Option_array.unsafe_set_some cache n length; 21 | length) 22 | ;; 23 | 24 | let main () = 25 | Sequence.range ~stop:`inclusive 1 limit 26 | |> Sequence.max_elt ~compare:(fun a b -> 27 | Int.compare (collatz_length a) (collatz_length b)) 28 | |> Option.value_exn 29 | |> printf "%d\n" 30 | ;; 31 | 32 | (* 94ms *) 33 | let%expect_test "answer" = 34 | main (); 35 | [%expect {| 837799 |}] 36 | ;; 37 | 38 | include (val Solution.make ~problem:(Number 14) ~main) 39 | -------------------------------------------------------------------------------- /sol/sol_014.mli: -------------------------------------------------------------------------------- 1 | open! Core 2 | open! Import 3 | include Solution.S 4 | -------------------------------------------------------------------------------- /sol/sol_014_memo_slower.ml: -------------------------------------------------------------------------------- 1 | open! Core 2 | open! Import 3 | 4 | let collatz n = if n mod 2 = 0 then n / 2 else (3 * n) + 1 5 | 6 | let collatz_length = 7 | Memo.recursive 8 | (module Int) 9 | (fun collatz_length n -> 10 | match n with 11 | | 1 -> 1 12 | | n -> 1 + collatz_length (collatz n)) 13 | ;; 14 | 15 | let main () = 16 | Sequence.range ~stop:`inclusive 1 1000000 17 | |> Sequence.max_elt ~compare:(fun a b -> 18 | Int.compare (collatz_length a) (collatz_length b)) 19 | |> Option.value_exn 20 | |> printf "%d\n" 21 | ;; 22 | 23 | (* 837799 24 | 3.37354s *) 25 | include 26 | (val Solution.make 27 | ~problem: 28 | (Tagged 29 | { number = 14 30 | ; tag = "memo" 31 | ; description = "slower method using hashtbl-memoized collatz_length" 32 | }) 33 | ~main) 34 | -------------------------------------------------------------------------------- /sol/sol_014_memo_slower.mli: -------------------------------------------------------------------------------- 1 | open! Core 2 | open! Import 3 | include Solution.S 4 | -------------------------------------------------------------------------------- /sol/sol_014_naive.ml: -------------------------------------------------------------------------------- 1 | open! Core 2 | open! Import 3 | 4 | let collatz n = if n mod 2 = 0 then n / 2 else (3 * n) + 1 5 | 6 | let collatz_length n = 7 | let rec iter n acc = 8 | match n with 9 | | 1 -> acc 10 | | n -> iter (collatz n) (acc + 1) 11 | in 12 | iter n 1 13 | ;; 14 | 15 | let main () = 16 | let argmax = ref 0 in 17 | let max = ref 0 in 18 | for i = 1 to 1000000 do 19 | let len = collatz_length i in 20 | if len > !max 21 | then ( 22 | argmax := i; 23 | max := len) 24 | done; 25 | printf "%d\n" !argmax 26 | ;; 27 | 28 | (* 837799 29 | 484.910406ms *) 30 | 31 | include 32 | (val Solution.make 33 | ~problem: 34 | (Tagged { number = 14; tag = "naive"; description = "brute-force method" }) 35 | ~main) 36 | -------------------------------------------------------------------------------- /sol/sol_014_naive.mli: -------------------------------------------------------------------------------- 1 | open! Core 2 | open! Import 3 | include Solution.S 4 | -------------------------------------------------------------------------------- /sol/sol_015.ml: -------------------------------------------------------------------------------- 1 | open! Core 2 | open! Import 3 | 4 | let main () = 5 | let n = Bigint.of_int 40 in 6 | let r = Bigint.of_int 20 in 7 | Number_theory.Bigint.binomial n r |> printf !"%{Bigint}\n" 8 | ;; 9 | 10 | let%expect_test "answer" = 11 | main (); 12 | [%expect {| 137846528820 |}] 13 | ;; 14 | 15 | include (val Solution.make ~problem:(Number 15) ~main) 16 | -------------------------------------------------------------------------------- /sol/sol_015.mli: -------------------------------------------------------------------------------- 1 | open! Core 2 | open! Import 3 | include Solution.S 4 | -------------------------------------------------------------------------------- /sol/sol_016.ml: -------------------------------------------------------------------------------- 1 | open! Core 2 | open! Import 3 | 4 | let sum_digits n base = 5 | let open Bigint in 6 | let rec iter n acc = if n = zero then acc else iter (n / base) ((n % base) + acc) in 7 | iter n zero 8 | ;; 9 | 10 | let main () = 11 | let open Bigint in 12 | let base = of_int 2 in 13 | let expt = of_int 1000 in 14 | let num = pow base expt in 15 | sum_digits num (of_int 10) |> to_int_exn |> printf "%d\n" 16 | ;; 17 | 18 | let%expect_test "answer" = 19 | main (); 20 | [%expect {| 1366 |}] 21 | ;; 22 | 23 | include (val Solution.make ~problem:(Number 16) ~main) 24 | -------------------------------------------------------------------------------- /sol/sol_016.mli: -------------------------------------------------------------------------------- 1 | open! Core 2 | open! Import 3 | include Solution.S 4 | -------------------------------------------------------------------------------- /sol/sol_017.ml: -------------------------------------------------------------------------------- 1 | open! Core 2 | open! Import 3 | 4 | let how_many_letters n = 5 | if n > 1000 then ksprintf invalid_arg "n is too big: %d" n; 6 | if n < 1 then ksprintf invalid_arg "n is too small: %d" n; 7 | let less_than_twenty = function 8 | | 0 -> 0 9 | | 1 -> String.length "one" 10 | | 2 -> String.length "two" 11 | | 3 -> String.length "three" 12 | | 4 -> String.length "four" 13 | | 5 -> String.length "five" 14 | | 6 -> String.length "six" 15 | | 7 -> String.length "seven" 16 | | 8 -> String.length "eight" 17 | | 9 -> String.length "nine" 18 | | 10 -> String.length "ten" 19 | | 11 -> String.length "eleven" 20 | | 12 -> String.length "twelve" 21 | | 13 -> String.length "thirteen" 22 | | 14 -> String.length "fourteen" 23 | | 15 -> String.length "fifteen" 24 | | 16 -> String.length "sixteen" 25 | | 17 -> String.length "seventeen" 26 | | 18 -> String.length "eighteen" 27 | | 19 -> String.length "nineteen" 28 | | _ -> assert false 29 | in 30 | let last_two_digits n = 31 | match n / 10 with 32 | | 0 | 1 -> less_than_twenty n 33 | | 2 -> less_than_twenty (n % 10) + String.length "twenty" 34 | | 3 -> less_than_twenty (n % 10) + String.length "thirty" 35 | | 4 -> less_than_twenty (n % 10) + String.length "forty" 36 | | 5 -> less_than_twenty (n % 10) + String.length "fifty" 37 | | 6 -> less_than_twenty (n % 10) + String.length "sixty" 38 | | 7 -> less_than_twenty (n % 10) + String.length "seventy" 39 | | 8 -> less_than_twenty (n % 10) + String.length "eighty" 40 | | 9 -> less_than_twenty (n % 10) + String.length "ninety" 41 | | _ -> assert false 42 | in 43 | let hundred n = 44 | match n / 100 with 45 | | 0 -> last_two_digits n 46 | | (1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9) as h -> 47 | last_two_digits (n % 100) 48 | + less_than_twenty h 49 | + String.length "hundred" 50 | + if n % 100 = 0 then 0 else String.length "and" 51 | | _ -> assert false 52 | in 53 | let thousand n = 54 | hundred (n % 1000) 55 | + if n = 1000 then String.length "one" + String.length "thousand" else 0 56 | in 57 | thousand n 58 | ;; 59 | 60 | let main () = 61 | Sequence.range 1 1000 ~stop:`inclusive 62 | |> Sequence.sum (module Int) ~f:how_many_letters 63 | |> printf "%d\n" 64 | ;; 65 | 66 | let%expect_test "answer" = 67 | main (); 68 | [%expect {| 21124 |}] 69 | ;; 70 | 71 | include (val Solution.make ~problem:(Number 17) ~main) 72 | -------------------------------------------------------------------------------- /sol/sol_017.mli: -------------------------------------------------------------------------------- 1 | open! Core 2 | open! Import 3 | include Solution.S 4 | -------------------------------------------------------------------------------- /sol/sol_018.ml: -------------------------------------------------------------------------------- 1 | open! Core 2 | open! Import 3 | 4 | let propagate bot top = 5 | let rec iter bot top acc = 6 | match bot, top with 7 | | b1 :: b2 :: bs, t :: ts -> 8 | let max = t + Int.max b1 b2 in 9 | iter (b2 :: bs) ts (max :: acc) 10 | | _, [] -> acc 11 | | _ -> raise_s [%message "length mismatched" (bot : int list) (top : int list)] 12 | in 13 | iter (Array.to_list bot) (Array.to_list top) [] |> Array.of_list_rev 14 | ;; 15 | 16 | let max_sum_exn triangle = 17 | let triangle = Array.copy triangle in 18 | Array.rev_inplace triangle; 19 | triangle 20 | |> Array.reduce_exn ~f:propagate 21 | |> Array.max_elt ~compare:Int.compare 22 | |> Option.value_exn 23 | ;; 24 | 25 | let main () = 26 | let triangle = Parse.space_separated_grid Problem_018.data ~conv:Int.of_string in 27 | max_sum_exn triangle |> printf "%d\n" 28 | ;; 29 | 30 | (* 0.061ms *) 31 | let%expect_test "answer" = 32 | main (); 33 | [%expect {| 1074 |}] 34 | ;; 35 | 36 | include (val Solution.make ~problem:(Number 18) ~main) 37 | -------------------------------------------------------------------------------- /sol/sol_018.mli: -------------------------------------------------------------------------------- 1 | open! Core 2 | open! Import 3 | include Solution.S 4 | 5 | val max_sum_exn : int array array -> int 6 | -------------------------------------------------------------------------------- /sol/sol_019.ml: -------------------------------------------------------------------------------- 1 | open! Core 2 | open! Import 3 | 4 | let main () = 5 | let start = Date.of_string "1901-01-01" in 6 | let stop = Date.of_string "2000-12-31" in 7 | Sequence.unfold_step ~init:start ~f:(fun d -> 8 | if Date.O.(d > stop) then Done else Yield { value = d; state = Date.add_months d 1 }) 9 | |> Sequence.count ~f:(fun d -> [%equal: Day_of_week.t] Sun (Date.day_of_week d)) 10 | |> printf "%d\n" 11 | ;; 12 | 13 | (* 167.972us *) 14 | let%expect_test "answer" = 15 | main (); 16 | [%expect {| 171 |}] 17 | ;; 18 | 19 | include (val Solution.make ~problem:(Number 19) ~main) 20 | -------------------------------------------------------------------------------- /sol/sol_019.mli: -------------------------------------------------------------------------------- 1 | open! Core 2 | open! Import 3 | include Solution.S 4 | -------------------------------------------------------------------------------- /sol/sol_020.ml: -------------------------------------------------------------------------------- 1 | open! Core 2 | open! Import 3 | 4 | let main () = 5 | Bigint.of_int 100 6 | |> Number_theory.Bigint.factorial 7 | |> Number_theory.Bigint.As_base10.sum (module Bigint) ~f:Fn.id 8 | |> printf !"%{Bigint}\n" 9 | ;; 10 | 11 | let%expect_test "answer" = 12 | main (); 13 | [%expect {| 648 |}] 14 | ;; 15 | 16 | include (val Solution.make ~problem:(Number 20) ~main) 17 | -------------------------------------------------------------------------------- /sol/sol_020.mli: -------------------------------------------------------------------------------- 1 | open! Core 2 | open! Import 3 | include Solution.S 4 | -------------------------------------------------------------------------------- /sol/sol_021.ml: -------------------------------------------------------------------------------- 1 | open! Core 2 | open! Import 3 | 4 | let sum_proper_divisors = 5 | Memo.simple 6 | (module Int) 7 | (fun n -> 8 | let sd = List.sum (module Int) (Number_theory.Int.divisors n) ~f:Fn.id in 9 | sd - n) 10 | ;; 11 | 12 | let amicable n = 13 | let sd = sum_proper_divisors n in 14 | sd <> n && sum_proper_divisors sd = n 15 | ;; 16 | 17 | let main () = 18 | Sequence.range 2 10_000 19 | |> Sequence.filter ~f:amicable 20 | |> Sequence.sum (module Int) ~f:Fn.id 21 | |> printf "%d\n" 22 | ;; 23 | 24 | (* 20.397ms *) 25 | let%expect_test "answer" = 26 | main (); 27 | [%expect {| 31626 |}] 28 | ;; 29 | 30 | include (val Solution.make ~problem:(Number 21) ~main) 31 | -------------------------------------------------------------------------------- /sol/sol_021.mli: -------------------------------------------------------------------------------- 1 | open! Core 2 | open! Import 3 | include Solution.S 4 | -------------------------------------------------------------------------------- /sol/sol_022.ml: -------------------------------------------------------------------------------- 1 | open! Core 2 | open! Import 3 | 4 | let letter_score ch = Char.to_int ch - Char.to_int 'A' + 1 5 | let name_score name = String.to_list name |> List.sum (module Int) ~f:letter_score 6 | 7 | let main () = 8 | let names = 9 | Problem_022.data |> Parse.csv_line ~f:Fn.id |> List.sort ~compare:String.compare 10 | in 11 | List.mapi names ~f:(fun i name -> name_score name * (i + 1)) 12 | |> List.sum (module Int) ~f:Fn.id 13 | |> printf "%d\n" 14 | ;; 15 | 16 | (* 5.4ms *) 17 | let%expect_test "answer" = 18 | main (); 19 | [%expect {| 871198282 |}] 20 | ;; 21 | 22 | include (val Solution.make ~problem:(Number 22) ~main) 23 | -------------------------------------------------------------------------------- /sol/sol_022.mli: -------------------------------------------------------------------------------- 1 | open! Core 2 | open! Import 3 | include Solution.S 4 | -------------------------------------------------------------------------------- /sol/sol_023.ml: -------------------------------------------------------------------------------- 1 | open! Core 2 | open! Import 3 | 4 | let limit = 28123 5 | 6 | let is_abundant n = 7 | let divisor_sum = Number_theory.Int.divisors n |> List.sum (module Int) ~f:Fn.id in 8 | divisor_sum > 2 * n 9 | ;; 10 | 11 | let abundant_numbers = 12 | Sequence.range 12 limit ~stop:`inclusive |> Sequence.filter ~f:is_abundant 13 | ;; 14 | 15 | let main () = 16 | let can_sum = Array.create false ~len:(limit + 1) in 17 | let rec iter outer inner = 18 | match outer, inner with 19 | | [], [] -> () 20 | | [], _ -> raise_s [%message "unexpected state" (outer : int list) (inner : int list)] 21 | | _ :: tl, [] -> iter tl tl 22 | | x :: xs, y :: ys -> 23 | if x + y > limit 24 | then iter xs xs 25 | else ( 26 | can_sum.(x + y) <- true; 27 | iter outer ys) 28 | in 29 | let abundant_numbers = Sequence.to_list abundant_numbers in 30 | iter abundant_numbers abundant_numbers; 31 | let sum = ref 0 in 32 | for i = 0 to limit do 33 | if not can_sum.(i) then sum := !sum + i 34 | done; 35 | printf "%d\n" !sum 36 | ;; 37 | 38 | (* 92ms *) 39 | let%expect_test "answer" = 40 | main (); 41 | [%expect {| 4179871 |}] 42 | ;; 43 | 44 | include (val Solution.make ~problem:(Number 23) ~main) 45 | -------------------------------------------------------------------------------- /sol/sol_023.mli: -------------------------------------------------------------------------------- 1 | open! Core 2 | open! Import 3 | include Solution.S 4 | -------------------------------------------------------------------------------- /sol/sol_024.ml: -------------------------------------------------------------------------------- 1 | open! Core 2 | open! Import 3 | 4 | let elements = 10 5 | let index = 999_999 6 | 7 | let main () = 8 | let rec loop i index candidates = 9 | if Set.is_empty candidates 10 | then [] 11 | else ( 12 | let elements_after_ith = elements - i - 1 in 13 | let permutations = Number_theory.Int.factorial elements_after_ith in 14 | let nth_at_i = index / permutations in 15 | let x = Set.nth candidates nth_at_i |> Option.value_exn ~here:[%here] in 16 | x :: loop (i + 1) (index % permutations) (Set.remove candidates x)) 17 | in 18 | loop 0 index (Set.of_increasing_iterator_unchecked (module Int) ~len:elements ~f:Fn.id) 19 | |> List.map ~f:Int.to_string 20 | |> String.concat 21 | |> printf "%s\n" 22 | ;; 23 | 24 | (* 135ms *) 25 | let%expect_test "answer" = 26 | main (); 27 | [%expect {| 2783915460 |}] 28 | ;; 29 | 30 | include (val Solution.make ~problem:(Number 24) ~main) 31 | -------------------------------------------------------------------------------- /sol/sol_024.mli: -------------------------------------------------------------------------------- 1 | open! Core 2 | open! Import 3 | include Solution.S 4 | -------------------------------------------------------------------------------- /sol/sol_025.ml: -------------------------------------------------------------------------------- 1 | open! Core 2 | open! Import 3 | 4 | let threshold = Bigint.(pow (of_int 10) (of_int 999)) 5 | 6 | let main () = 7 | Sequence.findi Number_theory.Bigint.fibonacci ~f:(fun _ f -> Bigint.(f >= threshold)) 8 | |> Option.value_exn 9 | |> fst 10 | |> printf "%d\n" 11 | ;; 12 | 13 | (* 1.692ms *) 14 | let%expect_test "answer" = 15 | main (); 16 | [%expect {| 4782 |}] 17 | ;; 18 | 19 | include (val Solution.make ~problem:(Number 25) ~main) 20 | -------------------------------------------------------------------------------- /sol/sol_025.mli: -------------------------------------------------------------------------------- 1 | open! Core 2 | open! Import 3 | include Solution.S 4 | -------------------------------------------------------------------------------- /sol/sol_026.ml: -------------------------------------------------------------------------------- 1 | open! Core 2 | open! Import 3 | 4 | let rec divide_through k n = Bigint.(if n % k = zero then divide_through k (n / k) else n) 5 | 6 | let cycle_length n = 7 | let n = 8 | n 9 | |> Bigint.of_int 10 | |> divide_through (Bigint.of_int 2) 11 | |> divide_through (Bigint.of_int 5) 12 | in 13 | let rec loop d c = 14 | Bigint.(if d % n = zero then c else loop ((of_int 10 * d) + of_int 9) Int.(c + 1)) 15 | in 16 | loop (Bigint.of_int 9) 1 17 | ;; 18 | 19 | let main () = 20 | Sequence.range 1 1000 21 | |> Sequence.map ~f:(fun n -> n, cycle_length n) 22 | |> Sequence.max_elt ~compare:[%compare: _ * int] 23 | |> Option.value_exn 24 | |> fst 25 | |> printf "%d\n" 26 | ;; 27 | 28 | (* 23ms *) 29 | let%expect_test "answer" = 30 | main (); 31 | [%expect {| 983 |}] 32 | ;; 33 | 34 | include (val Solution.make ~problem:(Number 26) ~main) 35 | -------------------------------------------------------------------------------- /sol/sol_026.mli: -------------------------------------------------------------------------------- 1 | open! Core 2 | open! Import 3 | include Solution.S 4 | -------------------------------------------------------------------------------- /sol/sol_027.ml: -------------------------------------------------------------------------------- 1 | open! Core 2 | open! Import 3 | 4 | type answer = 5 | { mutable a : int 6 | ; mutable b : int 7 | ; mutable number_of_primes : int 8 | } 9 | [@@deriving sexp] 10 | 11 | let is_prime = lazy (Number_theory.prime_sieve 20_000) 12 | let largest_q = ref 0 13 | 14 | let count_primes a b = 15 | let rec loop n count = 16 | let q = (n * n) + (a * n) + b in 17 | if q > !largest_q then largest_q := q; 18 | if q >= 0 && (force is_prime).(q) then loop (n + 1) (count + 1) else count 19 | in 20 | loop 0 0 21 | ;; 22 | 23 | let main () = 24 | let answer = { a = 0; b = 0; number_of_primes = 0 } in 25 | for a = -999 to 999 do 26 | for b = -1000 to 1000 do 27 | let primes = count_primes a b in 28 | if primes > answer.number_of_primes 29 | then ( 30 | answer.a <- a; 31 | answer.b <- b; 32 | answer.number_of_primes <- primes) 33 | done 34 | done; 35 | if debug then Debug.eprint_s [%message "" (answer : answer) (!largest_q : int)]; 36 | printf "%d\n" (answer.a * answer.b) 37 | ;; 38 | 39 | (* 50ms *) 40 | let%expect_test "answer" = 41 | main (); 42 | [%expect {| -59231 |}] 43 | ;; 44 | 45 | include (val Solution.make ~problem:(Number 27) ~main) 46 | -------------------------------------------------------------------------------- /sol/sol_027.mli: -------------------------------------------------------------------------------- 1 | open! Core 2 | open! Import 3 | include Solution.S 4 | -------------------------------------------------------------------------------- /sol/sol_028.ml: -------------------------------------------------------------------------------- 1 | open! Core 2 | open! Import 3 | 4 | let main () = 5 | let i = ref 1 in 6 | let di = ref 2 in 7 | let sum = ref 1 in 8 | for _ = 1 to 500 do 9 | for _ = 1 to 4 do 10 | i := !i + !di; 11 | sum := !sum + !i 12 | done; 13 | di := !di + 2 14 | done; 15 | printf "%d\n" !sum 16 | ;; 17 | 18 | (* 8.126us *) 19 | let%expect_test "answer" = 20 | main (); 21 | [%expect {| 669171001 |}] 22 | ;; 23 | 24 | include (val Solution.make ~problem:(Number 28) ~main) 25 | -------------------------------------------------------------------------------- /sol/sol_028.mli: -------------------------------------------------------------------------------- 1 | open! Core 2 | open! Import 3 | include Solution.S 4 | -------------------------------------------------------------------------------- /sol/sol_029.ml: -------------------------------------------------------------------------------- 1 | open! Core 2 | open! Import 3 | 4 | let main () = 5 | let powers = Hash_set.create (module Bigint) in 6 | for a = 2 to 100 do 7 | for b = 2 to 100 do 8 | let power = Bigint.pow (Bigint.of_int a) (Bigint.of_int b) in 9 | Hash_set.add powers power 10 | done 11 | done; 12 | printf "%d\n" (Hash_set.length powers) 13 | ;; 14 | 15 | (* 5.239ms *) 16 | let%expect_test "answer" = 17 | main (); 18 | [%expect {| 9183 |}] 19 | ;; 20 | 21 | include (val Solution.make ~problem:(Number 29) ~main) 22 | -------------------------------------------------------------------------------- /sol/sol_029.mli: -------------------------------------------------------------------------------- 1 | open! Core 2 | open! Import 3 | include Solution.S 4 | -------------------------------------------------------------------------------- /sol/sol_030.ml: -------------------------------------------------------------------------------- 1 | open! Core 2 | open! Import 3 | 4 | (* Derived by observing that 9^5 * n < 10^(n - 1) for all n > 6. *) 5 | let max_digits = 6 6 | let limit = Int.pow 10 max_digits 7 | 8 | (* optimized for constant exponent 9 | 10 | See https://en.wikipedia.org/wiki/Addition-chain_exponentiation *) 11 | let[@inline always] fifth_power n = 12 | let nn = n * n in 13 | nn * nn * n 14 | ;; 15 | 16 | let sum_fifth_of_digits n = 17 | let rec loop n ac = if n = 0 then ac else loop (n / 10) (ac + fifth_power (n mod 10)) in 18 | loop n 0 19 | ;; 20 | 21 | let main () = 22 | let sum = ref 0 in 23 | for n = 2 to limit - 1 do 24 | if n = sum_fifth_of_digits n then sum := !sum + n 25 | done; 26 | printf "%d\n" !sum 27 | ;; 28 | 29 | (* 34.65ms *) 30 | let%expect_test "answer" = 31 | main (); 32 | [%expect {| 443839 |}] 33 | ;; 34 | 35 | include (val Solution.make ~problem:(Number 30) ~main) 36 | -------------------------------------------------------------------------------- /sol/sol_030.mli: -------------------------------------------------------------------------------- 1 | open! Core 2 | open! Import 3 | include Solution.S 4 | -------------------------------------------------------------------------------- /sol/sol_031.ml: -------------------------------------------------------------------------------- 1 | open! Core 2 | open! Import 3 | 4 | let rec make_change total coins = 5 | match total, coins with 6 | | 0, _ -> 1 7 | | t, _ when t < 0 -> 0 8 | | _, [] -> 0 9 | | t, hd :: tl -> (if hd <= t then make_change (t - hd) coins else 0) + make_change t tl 10 | ;; 11 | 12 | let main () = make_change 200 [ 1; 2; 5; 10; 20; 50; 100; 200 ] |> printf "%d\n" 13 | 14 | (* 97.216ms *) 15 | let%expect_test "answer" = 16 | main (); 17 | [%expect {| 73682 |}] 18 | ;; 19 | 20 | include (val Solution.make ~problem:(Number 31) ~main) 21 | -------------------------------------------------------------------------------- /sol/sol_031.mli: -------------------------------------------------------------------------------- 1 | open! Core 2 | open! Import 3 | include Solution.S 4 | -------------------------------------------------------------------------------- /sol/sol_032.ml: -------------------------------------------------------------------------------- 1 | open! Core 2 | open! Import 3 | 4 | (* WLOG a < b, so a has at most 2 digits *) 5 | let pandigital () = 6 | let expected_digits = List.range 1 10 in 7 | let merge n ~into:sorted_digits = 8 | List.merge 9 | ~compare:Int.compare 10 | (List.sort ~compare:Int.compare (Number_theory.Int.As_base10.to_list n)) 11 | sorted_digits 12 | in 13 | let has_consecutive_duplicate = 14 | Option.is_some << List.find_consecutive_duplicate ~equal:Int.equal 15 | in 16 | let open Sequence.Let_syntax in 17 | let%bind a = Sequence.range 1 100 in 18 | let digits = merge a ~into:[] in 19 | if has_consecutive_duplicate digits 20 | then Sequence.empty 21 | else ( 22 | let%bind b = Sequence.range (a + 1) (Number_theory.Int.isqrt (1_000_000_000 / a)) in 23 | let digits = merge b ~into:digits in 24 | if has_consecutive_duplicate digits 25 | then Sequence.empty 26 | else ( 27 | let c = a * b in 28 | let digits = merge c ~into:digits in 29 | if [%equal: int list] expected_digits digits then return c else Sequence.empty)) 30 | ;; 31 | 32 | let main () = 33 | pandigital () 34 | |> Sequence.to_list 35 | |> Set.of_list (module Int) 36 | |> Set.sum (module Int) ~f:Fn.id 37 | |> printf "%d\n" 38 | ;; 39 | 40 | (* 200ms *) 41 | let%expect_test "answer" = 42 | main (); 43 | [%expect {| 45228 |}] 44 | ;; 45 | 46 | include (val Solution.make ~problem:(Number 32) ~main) 47 | -------------------------------------------------------------------------------- /sol/sol_032.mli: -------------------------------------------------------------------------------- 1 | open! Core 2 | open! Import 3 | include Solution.S 4 | -------------------------------------------------------------------------------- /sol/sol_033.ml: -------------------------------------------------------------------------------- 1 | open! Core 2 | open! Import 3 | 4 | let main () = 5 | let fractions = 6 | let open Sequence.Let_syntax in 7 | let%bind n1 = Sequence.range 1 10 in 8 | let%bind n2 = Sequence.range 0 10 in 9 | let%bind d1 = Sequence.range 1 10 in 10 | let%bind d2 = Sequence.range 0 10 in 11 | let n = (10 * n1) + n2 in 12 | let d = (10 * d1) + d2 in 13 | if n >= d || (n2 = 0 && d2 = 0) 14 | then Sequence.empty 15 | else if n1 = d2 && Bignum.(n2 // d1 = n // d) 16 | then ( 17 | (* this case isn't used *) 18 | if debug then Debug.eprintf "case 1: %d/%d = %d/%d" n d n2 d1; 19 | return Bignum.(n // d)) 20 | else if n2 = d1 && Bignum.(n1 // d2 = n // d) 21 | then ( 22 | if debug then Debug.eprintf "case 2: %d/%d = %d/%d" n d n1 d2; 23 | return Bignum.(n // d)) 24 | else Sequence.empty 25 | in 26 | fractions 27 | |> Sequence.fold ~init:Bignum.one ~f:Bignum.( * ) 28 | |> Bignum.den_as_bigint 29 | |> printf !"%{Bigint}\n" 30 | ;; 31 | 32 | (* 0.926ms *) 33 | let%expect_test "answer" = 34 | main (); 35 | [%expect {| 100 |}] 36 | ;; 37 | 38 | include (val Solution.make ~problem:(Number 33) ~main) 39 | -------------------------------------------------------------------------------- /sol/sol_033.mli: -------------------------------------------------------------------------------- 1 | open! Core 2 | open! Import 3 | include Solution.S 4 | -------------------------------------------------------------------------------- /sol/sol_034.ml: -------------------------------------------------------------------------------- 1 | open! Core 2 | open! Import 3 | 4 | (* Derived by observing that 9! * n < 10^(n - 1) for all n > 7. *) 5 | let max_digits = 7 6 | 7 | let incr digits = 8 | let rec loop i = 9 | if i >= 0 10 | then 11 | if digits.(i) = 9 12 | then ( 13 | digits.(i) <- 0; 14 | loop (i - 1)) 15 | else digits.(i) <- digits.(i) + 1 16 | else failwith "no digits to increment" 17 | in 18 | loop (Array.length digits - 1) 19 | ;; 20 | 21 | let main () = 22 | let factorial = Array.init 10 ~f:Number_theory.Int.factorial in 23 | let digits = Array.create ~len:max_digits 0 in 24 | (* start at 10 *) 25 | digits.(Array.length digits - 1) <- 9; 26 | let sum = ref 0 in 27 | for num_digits = 2 to max_digits do 28 | for n = Int.pow 10 (num_digits - 1) to Int.pow 10 num_digits - 1 do 29 | incr digits; 30 | if debug 31 | then ( 32 | let n' = Array.fold digits ~init:0 ~f:(fun acc x -> (acc * 10) + x) in 33 | [%test_eq: int] n n'); 34 | let n' = 35 | (* actually substantially faster than Array.sum *) 36 | let sum = ref 0 in 37 | for i = Array.length digits - num_digits to Array.length digits - 1 do 38 | sum := !sum + factorial.(digits.(i)) 39 | done; 40 | !sum 41 | in 42 | if n = n' 43 | then ( 44 | if debug then Debug.eprintf "%d" n; 45 | sum := !sum + n) 46 | done 47 | done; 48 | printf "%d\n" !sum 49 | ;; 50 | 51 | (* 141.852ms *) 52 | let%expect_test "answer" = 53 | main (); 54 | [%expect {| 40730 |}] 55 | ;; 56 | 57 | include (val Solution.make ~problem:(Number 34) ~main) 58 | -------------------------------------------------------------------------------- /sol/sol_034.mli: -------------------------------------------------------------------------------- 1 | open! Core 2 | open! Import 3 | include Solution.S 4 | -------------------------------------------------------------------------------- /sol/sol_035.ml: -------------------------------------------------------------------------------- 1 | open! Core 2 | open! Import 3 | 4 | let limit = 999_999 5 | let is_prime = lazy (Number_theory.prime_sieve limit) 6 | 7 | let prime_circle n = 8 | let digits = Number_theory.Int.As_base10.to_array n in 9 | let len = Array.length digits in 10 | let double_digits = Array.append digits digits in 11 | let results = ref [] in 12 | let rec loop i = 13 | if i < len 14 | then ( 15 | let n = 16 | let rec loop pos end_pos acc = 17 | if pos < end_pos 18 | then loop (pos + 1) end_pos ((10 * acc) + double_digits.(pos)) 19 | else acc 20 | in 21 | loop i (i + len) 0 22 | in 23 | if (force is_prime).(n) 24 | then ( 25 | results := n :: !results; 26 | loop (i + 1)) 27 | else None) 28 | else Some !results 29 | in 30 | loop 0 31 | ;; 32 | 33 | let main () = 34 | let circular_primes = Hash_set.create (module Int) in 35 | for n = 2 to limit do 36 | if (force is_prime).(n) && not (Hash_set.mem circular_primes n) 37 | then Option.iter (prime_circle n) ~f:(List.iter ~f:(Hash_set.add circular_primes)) 38 | done; 39 | printf "%d\n" (Hash_set.length circular_primes) 40 | ;; 41 | 42 | (* 59.769ms *) 43 | let%expect_test "answer" = 44 | main (); 45 | [%expect {| 55 |}] 46 | ;; 47 | 48 | include (val Solution.make ~problem:(Number 35) ~main) 49 | -------------------------------------------------------------------------------- /sol/sol_035.mli: -------------------------------------------------------------------------------- 1 | open! Core 2 | open! Import 3 | include Solution.S 4 | -------------------------------------------------------------------------------- /sol/sol_036.mli: -------------------------------------------------------------------------------- 1 | open! Core 2 | open! Import 3 | include Solution.S 4 | -------------------------------------------------------------------------------- /sol/sol_037.ml: -------------------------------------------------------------------------------- 1 | open! Core 2 | open! Import 3 | 4 | let extend_right prime = 5 | [ 1; 3; 5; 7; 9 ] 6 | |> List.map ~f:(fun d -> (prime * 10) + d) 7 | |> List.filter ~f:Number_theory.Int.is_prime 8 | ;; 9 | 10 | let search start ~step = 11 | let rec loop result queue ~step = 12 | match Fqueue.dequeue queue with 13 | | None -> result 14 | | Some (hd, tl) -> 15 | step hd |> List.fold ~init:tl ~f:Fqueue.enqueue |> loop (Set.add result hd) ~step 16 | in 17 | loop (Set.empty (module Int)) (Fqueue.of_list start) ~step 18 | ;; 19 | 20 | let is_left_truncatable n = 21 | let pop_left_digit n = 22 | let rec loop power_of_ten = 23 | if power_of_ten * 10 > n then n mod power_of_ten else loop (power_of_ten * 10) 24 | in 25 | loop 1 26 | in 27 | let rec loop n = 28 | if n < 10 && (n = 2 || n = 3 || n = 5 || n = 7) 29 | then true 30 | else Number_theory.Int.is_prime n && loop (pop_left_digit n) 31 | in 32 | loop n 33 | ;; 34 | 35 | let main () = 36 | search [ 2; 3; 5; 7 ] ~step:extend_right 37 | |> Fn.flip Set.diff (Set.of_list (module Int) [ 2; 3; 5; 7 ]) 38 | |> Set.filter ~f:is_left_truncatable 39 | |> Set.sum (module Int) ~f:Fn.id 40 | |> printf "%d\n" 41 | ;; 42 | 43 | (* 9.74066ms *) 44 | let%expect_test "answer" = 45 | main (); 46 | [%expect {| 748317 |}] 47 | ;; 48 | 49 | include (val Solution.make ~problem:(Number 37) ~main) 50 | -------------------------------------------------------------------------------- /sol/sol_037.mli: -------------------------------------------------------------------------------- 1 | open! Core 2 | open! Import 3 | include Solution.S 4 | -------------------------------------------------------------------------------- /sol/sol_038.ml: -------------------------------------------------------------------------------- 1 | open! Core 2 | open! Import 3 | 4 | let reify k n = 5 | if debug then Debug.eprintf "k = %d, n = %d" k n; 6 | List.range 1 n 7 | |> List.map ~f:(fun n -> k * n |> Int.to_string) 8 | |> String.concat 9 | |> Int.of_string 10 | ;; 11 | 12 | let pandigital_multiples () = 13 | let max = ref 0 in 14 | let digits_used = Array.create false ~len:10 in 15 | for k = 1 to 9999 do 16 | if debug then Debug.eprintf "k = %d" k; 17 | Array.fill digits_used false ~pos:0 ~len:10; 18 | let digits_filled = ref 0 in 19 | let rec loop n = 20 | if !digits_filled = 9 21 | then max := Int.max !max (reify k n) 22 | else ( 23 | try 24 | Number_theory.Int.As_base10.iter (k * n) ~f:(fun d -> 25 | if d = 0 || digits_used.(d) 26 | then Exn.raise_without_backtrace Exit 27 | else ( 28 | digits_used.(d) <- true; 29 | incr digits_filled)); 30 | loop (n + 1) 31 | with 32 | | Exit -> ()) 33 | in 34 | loop 1 35 | done; 36 | !max 37 | ;; 38 | 39 | let main () = pandigital_multiples () |> printf "%d\n" 40 | 41 | (* 1.815ms *) 42 | let%expect_test "answer" = 43 | main (); 44 | [%expect {| 932718654 |}] 45 | ;; 46 | 47 | include (val Solution.make ~problem:(Number 38) ~main) 48 | -------------------------------------------------------------------------------- /sol/sol_038.mli: -------------------------------------------------------------------------------- 1 | open! Core 2 | open! Import 3 | include Solution.S 4 | -------------------------------------------------------------------------------- /sol/sol_039.ml: -------------------------------------------------------------------------------- 1 | open! Core 2 | open! Import 3 | 4 | let main () = 5 | Sequence.range ~stop:`inclusive 12 1000 6 | |> Sequence.map ~f:(fun p -> 7 | let solutions = ref 0 in 8 | for c = 5 to (p / 2) - 1 do 9 | for b = (p - c) / 2 to c - 1 do 10 | let a = p - c - b in 11 | if Geometry.is_pythagorean_triple a b c then incr solutions 12 | done 13 | done; 14 | p, !solutions) 15 | |> Sequence.max_elt ~compare:(fun (_, a) (_, b) -> Int.compare a b) 16 | |> Option.value_exn 17 | |> fst 18 | |> printf "%d\n" 19 | ;; 20 | 21 | (* 40.293ms *) 22 | let%expect_test "answer" = 23 | main (); 24 | [%expect {| 840 |}] 25 | ;; 26 | 27 | include (val Solution.make ~problem:(Number 39) ~main) 28 | -------------------------------------------------------------------------------- /sol/sol_039.mli: -------------------------------------------------------------------------------- 1 | open! Core 2 | open! Import 3 | include Solution.S 4 | -------------------------------------------------------------------------------- /sol/sol_040.ml: -------------------------------------------------------------------------------- 1 | open! Core 2 | open! Import 3 | 4 | let champernowne's_constant = 5 | let open Sequence.Let_syntax in 6 | let%bind nat = Number_theory.Int.natural_numbers () ~init:1 in 7 | Number_theory.Int.As_base10.to_sequence nat 8 | ;; 9 | 10 | let limit = 1_000_000 11 | let indices = [ 1; 10; 100; 1_000; 10_000; 100_000; 1_000_000 ] 12 | 13 | let main () = 14 | champernowne's_constant 15 | |> Fn.flip Sequence.take limit 16 | |> Sequence.foldi ~init:1 ~f:(fun i ac digit -> 17 | if List.mem indices (i + 1) ~equal:Int.equal then ac * digit else ac) 18 | |> printf "%d\n" 19 | ;; 20 | 21 | (* 97.183ms *) 22 | let%expect_test "answer" = 23 | main (); 24 | [%expect {| 210 |}] 25 | ;; 26 | 27 | include (val Solution.make ~problem:(Number 40) ~main) 28 | -------------------------------------------------------------------------------- /sol/sol_040.mli: -------------------------------------------------------------------------------- 1 | open! Core 2 | open! Import 3 | include Solution.S 4 | -------------------------------------------------------------------------------- /sol/sol_041.ml: -------------------------------------------------------------------------------- 1 | open! Core 2 | open! Import 3 | 4 | let main () = 5 | with_return (fun { return } -> 6 | for n = 9 downto 0 do 7 | let digits = List.init n ~f:(fun i -> n - i) in 8 | Sequences.iter_permutations digits ~compare:Int.descending ~f:(fun digits -> 9 | let n = 10 | Array.Permissioned.to_sequence_mutable digits 11 | |> Number_theory.Int.As_base10.of_sequence 12 | in 13 | if Number_theory.Int.is_prime n then return n) 14 | done; 15 | assert false) 16 | |> printf "%d\n" 17 | ;; 18 | 19 | (* 349.457ms *) 20 | let%expect_test "answer" = 21 | main (); 22 | [%expect {| 7652413 |}] 23 | ;; 24 | 25 | include (val Solution.make ~problem:(Number 41) ~main) 26 | -------------------------------------------------------------------------------- /sol/sol_041.mli: -------------------------------------------------------------------------------- 1 | open! Core 2 | open! Import 3 | include Solution.S 4 | -------------------------------------------------------------------------------- /sol/sol_042.ml: -------------------------------------------------------------------------------- 1 | open! Core 2 | open! Import 3 | 4 | let word_value word = 5 | let letter_value letter = Char.to_int letter - Char.to_int 'A' + 1 in 6 | String.to_list word |> List.sum (module Int) ~f:letter_value 7 | ;; 8 | 9 | let is_triangle_number = 10 | Memo.simple 11 | (module Int) 12 | (fun t -> 13 | let n = Float.(sqrt (of_int t * 2.0) |> to_int) in 14 | t = n * (n + 1) / 2) 15 | ;; 16 | 17 | let main () = 18 | Problem_042.data 19 | |> Parse.csv_line ~f:Fn.id 20 | |> List.map ~f:word_value 21 | |> List.count ~f:is_triangle_number 22 | |> printf "%d\n" 23 | ;; 24 | 25 | (* 0.708ms *) 26 | let%expect_test "answer" = 27 | main (); 28 | [%expect {| 162 |}] 29 | ;; 30 | 31 | include (val Solution.make ~problem:(Number 42) ~main) 32 | -------------------------------------------------------------------------------- /sol/sol_042.mli: -------------------------------------------------------------------------------- 1 | open! Core 2 | open! Import 3 | include Solution.S 4 | -------------------------------------------------------------------------------- /sol/sol_043.ml: -------------------------------------------------------------------------------- 1 | open! Core 2 | open! Import 3 | 4 | let is_div d1 d2 d3 p = ((100 * d1) + (10 * d2) + d3) mod p = 0 5 | 6 | let follows ~requirements digit digits = 7 | match requirements.(digit) with 8 | | None -> true 9 | | Some modulus when is_div digits.(digit - 2) digits.(digit - 1) digits.(digit) modulus 10 | -> true 11 | | _ -> false 12 | ;; 13 | 14 | let rec iter digit digits ~is_used ~requirements ~f = 15 | if digit >= Array.length requirements 16 | then f (digits |> Array.to_sequence_mutable |> Number_theory.Int.As_base10.of_sequence) 17 | else 18 | for d = 0 to 9 do 19 | digits.(digit) <- d; 20 | if (not is_used.(d)) && follows ~requirements digit digits 21 | then ( 22 | is_used.(d) <- true; 23 | iter (digit + 1) digits ~is_used ~requirements ~f; 24 | is_used.(d) <- false) 25 | done 26 | ;; 27 | 28 | let sum_pandigital () = 29 | let sum = ref 0 in 30 | let digits = Array.create ~len:10 0 in 31 | let is_used = Array.create ~len:10 false in 32 | let requirements = 33 | [| None; None; None; Some 2; Some 3; Some 5; Some 7; Some 11; Some 13; Some 17 |] 34 | in 35 | iter 0 digits ~is_used ~requirements ~f:(fun n -> 36 | if debug then Debug.eprintf "%d" n; 37 | sum := !sum + n); 38 | !sum 39 | ;; 40 | 41 | let main () = sum_pandigital () |> printf "%d\n" 42 | 43 | (* 2.789ms *) 44 | let%expect_test "answer" = 45 | main (); 46 | [%expect {| 16695334890 |}] 47 | ;; 48 | 49 | include (val Solution.make ~problem:(Number 43) ~main) 50 | -------------------------------------------------------------------------------- /sol/sol_043.mli: -------------------------------------------------------------------------------- 1 | open! Core 2 | open! Import 3 | include Solution.S 4 | -------------------------------------------------------------------------------- /sol/sol_044.ml: -------------------------------------------------------------------------------- 1 | open! Core 2 | open! Import 3 | 4 | (* https://en.wikipedia.org/wiki/Pentagonal_number#Tests_for_pentagonal_numbers *) 5 | let is_pentagonal n = 6 | let s = Number_theory.Int.isqrt (1 + (24 * n)) in 7 | s * s = 1 + (24 * n) && s mod 6 = 5 8 | ;; 9 | 10 | let main () = 11 | with_return (fun { return } -> 12 | for k = 1 to 1_000_000 do 13 | for j = k - 1 downto 1 do 14 | let pk = k * ((3 * k) - 1) / 2 in 15 | let pj = j * ((3 * j) - 1) / 2 in 16 | if is_pentagonal (pk + pj) && is_pentagonal (pk - pj) then return (pk - pj) 17 | done 18 | done; 19 | assert false) 20 | |> printf "%d\n" 21 | ;; 22 | 23 | (* 25.063ms *) 24 | let%expect_test "answer" = 25 | main (); 26 | [%expect {| 5482660 |}] 27 | ;; 28 | 29 | include (val Solution.make ~problem:(Number 44) ~main) 30 | -------------------------------------------------------------------------------- /sol/sol_044.mli: -------------------------------------------------------------------------------- 1 | open! Core 2 | open! Import 3 | include Solution.S 4 | -------------------------------------------------------------------------------- /sol/sol_045.ml: -------------------------------------------------------------------------------- 1 | open! Core 2 | open! Import 3 | 4 | let prev = 40755 5 | 6 | let merge_keeping_only_dups xs ys = 7 | Sequence.merge_with_duplicates xs ys ~compare:Int.compare 8 | |> Sequence.filter_map ~f:(function 9 | | Left _ | Right _ -> None 10 | | Both (x, _) -> Some x) 11 | ;; 12 | 13 | let main () = 14 | let map_nats ~f = Number_theory.Int.natural_numbers () ~init:1 |> Sequence.map ~f in 15 | let triangle = map_nats ~f:(fun n -> n * (n + 1) / 2) in 16 | let pentagonal = map_nats ~f:(fun n -> n * ((3 * n) - 1) / 2) in 17 | let hexagonal = map_nats ~f:(fun n -> n * ((2 * n) - 1)) in 18 | [ triangle; pentagonal; hexagonal ] 19 | |> List.reduce_balanced_exn ~f:merge_keeping_only_dups 20 | |> Sequence.drop_while ~f:(fun x -> x <= prev) 21 | |> Sequence.hd_exn 22 | |> printf "%d\n" 23 | ;; 24 | 25 | (* 8.8ms *) 26 | let%expect_test "answer" = 27 | main (); 28 | [%expect {| 1533776805 |}] 29 | ;; 30 | 31 | include (val Solution.make ~problem:(Number 45) ~main) 32 | -------------------------------------------------------------------------------- /sol/sol_045.mli: -------------------------------------------------------------------------------- 1 | open! Core 2 | open! Import 3 | include Solution.S 4 | -------------------------------------------------------------------------------- /sol/sol_046.ml: -------------------------------------------------------------------------------- 1 | open! Core 2 | open! Import 3 | 4 | let cannot_be_written n = 5 | let upper_bound = Number_theory.Int.isqrt (n / 2) in 6 | not 7 | (Sequence.range ~stop:`inclusive 1 upper_bound 8 | |> Sequence.exists ~f:(fun s -> n - (2 * s * s) |> Number_theory.Int.is_prime)) 9 | ;; 10 | 11 | let main () = 12 | Sequence.unfold_step ~init:3 ~f:(fun s -> Yield { value = s; state = s + 2 }) 13 | |> Sequence.filter ~f:(Fn.non Number_theory.Int.is_prime) 14 | |> Sequence.find_exn ~f:cannot_be_written 15 | |> printf "%d\n" 16 | ;; 17 | 18 | (* 5.236ms *) 19 | let%expect_test "answer" = 20 | main (); 21 | [%expect {| 5777 |}] 22 | ;; 23 | 24 | include (val Solution.make ~problem:(Number 46) ~main) 25 | -------------------------------------------------------------------------------- /sol/sol_046.mli: -------------------------------------------------------------------------------- 1 | open! Core 2 | open! Import 3 | include Solution.S 4 | -------------------------------------------------------------------------------- /sol/sol_047.ml: -------------------------------------------------------------------------------- 1 | open! Core 2 | open! Import 3 | 4 | let main () = 5 | let distinct_prime_factors = 6 | Number_theory.Int.natural_numbers ~init:2 () 7 | |> Sequence.map ~f:(fun n -> 8 | if debug then Debug.eprintf "factoring %d" n; 9 | n, Number_theory.Int.prime_factor n |> List.length) 10 | in 11 | let each_consecutive_4 = 12 | let dpf = distinct_prime_factors in 13 | let a, dpf = Option.value_exn (Sequence.next dpf) in 14 | let b, dpf = Option.value_exn (Sequence.next dpf) in 15 | let c, dpf = Option.value_exn (Sequence.next dpf) in 16 | Sequence.folding_map dpf ~init:(a, b, c) ~f:(fun (a, b, c) d -> 17 | (b, c, d), (a, b, c, d)) 18 | in 19 | let rec loop seq = 20 | let ((a, apf), (_, bpf), (_, cpf), (_, dpf)), seq = 21 | Option.value_exn (Sequence.next seq) 22 | in 23 | (* Check [dpf] first à la Boyer-Moore: 24 | https://en.wikipedia.org/wiki/Boyer%E2%80%93Moore_string-search_algorithm. 25 | 26 | This doesn't actually buy us much performance-wise, but it's cool, so 27 | whatever. *) 28 | let step n = loop (Sequence.drop_eagerly seq n) in 29 | if dpf <> 4 30 | then step 3 31 | else if cpf <> 4 32 | then step 2 33 | else if bpf <> 4 34 | then step 1 35 | else if apf <> 4 36 | then step 0 37 | else a 38 | in 39 | loop each_consecutive_4 |> printf "%d\n" 40 | ;; 41 | 42 | (* 98.823ms *) 43 | let%expect_test "answer" = 44 | main (); 45 | [%expect {| 134043 |}] 46 | ;; 47 | 48 | include (val Solution.make ~problem:(Number 47) ~main) 49 | -------------------------------------------------------------------------------- /sol/sol_047.mli: -------------------------------------------------------------------------------- 1 | open! Core 2 | open! Import 3 | include Solution.S 4 | -------------------------------------------------------------------------------- /sol/sol_048.ml: -------------------------------------------------------------------------------- 1 | open! Core 2 | open! Import 3 | 4 | module Bigint = struct 5 | include Bigint 6 | 7 | let modulus = 8 | let ten = Bigint.of_int 10 in 9 | Bigint.pow ten ten 10 | ;; 11 | 12 | let ( + ) a b = (a + b) % modulus 13 | end 14 | 15 | let main () = 16 | let sum = 17 | Sequence.range ~stop:`inclusive 1 1000 18 | |> Sequence.sum 19 | (module Bigint) 20 | ~f:(fun n -> 21 | let n = Bigint.of_int n in 22 | Bigint.pow n n) 23 | in 24 | sum |> printf !"%{Bigint}\n" 25 | ;; 26 | 27 | (* 4.026ms *) 28 | let%expect_test "answer" = 29 | main (); 30 | [%expect {| 9110846700 |}] 31 | ;; 32 | 33 | include (val Solution.make ~problem:(Number 48) ~main) 34 | -------------------------------------------------------------------------------- /sol/sol_048.mli: -------------------------------------------------------------------------------- 1 | open! Core 2 | open! Import 3 | include Solution.S 4 | -------------------------------------------------------------------------------- /sol/sol_049.ml: -------------------------------------------------------------------------------- 1 | open! Core 2 | open! Import 3 | 4 | let example_x = 1487 5 | 6 | let mem_binary haystack needle = 7 | Array.binary_search haystack ~compare:Int.compare `First_equal_to needle 8 | |> Option.is_some 9 | ;; 10 | 11 | let main () = 12 | let is_prime = Number_theory.prime_sieve 10_000 in 13 | let visited = Array.create false ~len:10_000 in 14 | try 15 | for a = 1000 to 9999 do 16 | if (not visited.(a)) && is_prime.(a) 17 | then ( 18 | let prime_permutations = 19 | a 20 | |> Number_theory.Int.As_base10.to_list 21 | |> Sequences.permutations ~compare:Int.compare 22 | |> Sequence.map ~f:Number_theory.Int.As_base10.of_list 23 | |> Sequence.filter ~f:(Array.get is_prime) 24 | |> Sequence.to_array 25 | in 26 | Array.sort prime_permutations ~compare:Int.compare; 27 | for i = 0 to Array.length prime_permutations - 1 do 28 | (* skip prime_permutations with leading zeros *) 29 | if prime_permutations.(i) >= 1000 30 | then ( 31 | visited.(prime_permutations.(i)) <- true; 32 | for j = i + 1 to Array.length prime_permutations - 1 do 33 | let x = prime_permutations.(i) in 34 | let y = prime_permutations.(j) in 35 | let z = y + (y - x) in 36 | if mem_binary prime_permutations z && x <> example_x 37 | then ( 38 | printf "%d%d%d\n" x y z; 39 | Exn.raise_without_backtrace Exit) 40 | done) 41 | done) 42 | done 43 | with 44 | | Exit -> () 45 | ;; 46 | 47 | (* 1.749ms *) 48 | let%expect_test "answer" = 49 | main (); 50 | [%expect {| 296962999629 |}] 51 | ;; 52 | 53 | include (val Solution.make ~problem:(Number 49) ~main) 54 | -------------------------------------------------------------------------------- /sol/sol_049.mli: -------------------------------------------------------------------------------- 1 | open! Core 2 | open! Import 3 | include Solution.S 4 | -------------------------------------------------------------------------------- /sol/sol_050.ml: -------------------------------------------------------------------------------- 1 | open! Core 2 | open! Import 3 | 4 | let limit = 1_000_000 5 | 6 | let main () = 7 | let is_prime = Number_theory.prime_sieve limit in 8 | let primes = 9 | Array.filter_mapi is_prime ~f:(fun i is_prime -> Option.some_if is_prime i) 10 | in 11 | let max = ref 0 in 12 | let arg_max = ref 0 in 13 | for pos = 0 to Array.length primes do 14 | if is_prime.(pos) 15 | then ( 16 | let sum = ref 0 in 17 | try 18 | for len = 1 to Array.length primes - pos do 19 | sum := !sum + primes.(pos + len - 1); 20 | if !sum > limit then Exn.raise_without_backtrace Exit; 21 | if len > !max && is_prime.(!sum) 22 | then ( 23 | max := len; 24 | arg_max := !sum) 25 | done 26 | with 27 | | Exit -> ()) 28 | done; 29 | printf "%d\n" !arg_max 30 | ;; 31 | 32 | (* 56.427ms *) 33 | let%expect_test "answer" = 34 | main (); 35 | [%expect {| 997651 |}] 36 | ;; 37 | 38 | include (val Solution.make ~problem:(Number 50) ~main) 39 | -------------------------------------------------------------------------------- /sol/sol_050.mli: -------------------------------------------------------------------------------- 1 | open! Core 2 | open! Import 3 | include Solution.S 4 | -------------------------------------------------------------------------------- /sol/sol_051.mli: -------------------------------------------------------------------------------- 1 | open! Core 2 | open! Import 3 | include Solution.S 4 | -------------------------------------------------------------------------------- /sol/sol_052.ml: -------------------------------------------------------------------------------- 1 | open! Core 2 | open! Import 3 | 4 | let count_digits = Number_theory.Int.As_base10.fold ~init:0 ~f:(fun ac _ -> ac + 1) 5 | 6 | let same_digits n = 7 | let sort_digits n = 8 | Number_theory.Int.As_base10.to_list n |> List.sort ~compare:Int.compare 9 | in 10 | let n_digits = sort_digits n in 11 | count_digits (n * 6) = List.length n_digits 12 | && List.range 2 6 ~stop:`inclusive 13 | |> List.map ~f:(( * ) n) 14 | |> List.map ~f:sort_digits 15 | |> List.for_all ~f:([%equal: int list] n_digits) 16 | ;; 17 | 18 | let main () = 19 | Number_theory.Int.natural_numbers ~init:1 () 20 | |> Sequence.find_exn ~f:same_digits 21 | |> printf "%d\n" 22 | ;; 23 | 24 | (* 138.157ms *) 25 | let%expect_test "answer" = 26 | main (); 27 | [%expect {| 142857 |}] 28 | ;; 29 | 30 | include (val Solution.make ~problem:(Number 52) ~main) 31 | -------------------------------------------------------------------------------- /sol/sol_052.mli: -------------------------------------------------------------------------------- 1 | open! Core 2 | open! Import 3 | include Solution.S 4 | -------------------------------------------------------------------------------- /sol/sol_053.ml: -------------------------------------------------------------------------------- 1 | open! Core 2 | open! Import 3 | 4 | let main () = 5 | let limit = Bigint.of_int 1_000_000 in 6 | Sequence.( 7 | range 1 100 ~stop:`inclusive 8 | |> concat_map ~f:(fun n -> range 0 n ~stop:`inclusive |> map ~f:(fun r -> n, r)) 9 | |> map ~f:(fun (n, r) -> 10 | let n = Bigint.of_int n in 11 | let r = Bigint.of_int r in 12 | Number_theory.Bigint.binomial n r) 13 | |> count ~f:(Bigint.( < ) limit)) 14 | |> printf "%d\n" 15 | ;; 16 | 17 | (* 15.168ms *) 18 | let%expect_test "answer" = 19 | main (); 20 | [%expect {| 4075 |}] 21 | ;; 22 | 23 | include (val Solution.make ~problem:(Number 53) ~main) 24 | -------------------------------------------------------------------------------- /sol/sol_053.mli: -------------------------------------------------------------------------------- 1 | open! Core 2 | open! Import 3 | include Solution.S 4 | -------------------------------------------------------------------------------- /sol/sol_054.ml: -------------------------------------------------------------------------------- 1 | open! Core 2 | open! Import 3 | 4 | let main () = 5 | Problem_054.data 6 | |> Parse.space_separated_grid ~conv:Poker.Card.of_string 7 | |> Array.map ~f:(fun line -> 8 | let make_hand cards = cards |> Array.to_list |> Poker.Hand.of_card_list_exn in 9 | let p1 = Array.subo line ~len:5 |> make_hand in 10 | let p2 = Array.subo line ~pos:5 |> make_hand in 11 | p1, p2) 12 | |> Array.count ~f:(fun (p1, p2) -> 13 | Poker.Hand_classification.( > ) (Poker.Hand.classify p1) (Poker.Hand.classify p2)) 14 | |> printf "%d\n" 15 | ;; 16 | 17 | (* 6.821ms *) 18 | let%expect_test "answer" = 19 | main (); 20 | [%expect {| 376 |}] 21 | ;; 22 | 23 | include (val Solution.make ~problem:(Number 54) ~main) 24 | -------------------------------------------------------------------------------- /sol/sol_054.mli: -------------------------------------------------------------------------------- 1 | open! Core 2 | open! Import 3 | include Solution.S 4 | -------------------------------------------------------------------------------- /sol/sol_055.ml: -------------------------------------------------------------------------------- 1 | open! Core 2 | open! Import 3 | 4 | let is_palindrome n = 5 | let s = Bigint.to_string n in 6 | String.equal s (String.rev s) 7 | ;; 8 | 9 | let is_lychrel n = 10 | Sequence.unfold_step ~init:n ~f:(fun n -> 11 | let next = Bigint.(n + (n |> to_string |> String.rev |> of_string)) in 12 | Yield { value = next; state = next }) 13 | |> Fn.flip Sequence.take 50 14 | |> Sequence.exists ~f:is_palindrome 15 | |> not 16 | ;; 17 | 18 | let main () = 19 | Sequence.range 1 10_000 20 | |> Sequence.map ~f:Bigint.of_int 21 | |> Sequence.count ~f:is_lychrel 22 | |> printf "%d\n" 23 | ;; 24 | 25 | (* 38.775ms *) 26 | let%expect_test "answer" = 27 | main (); 28 | [%expect {| 249 |}] 29 | ;; 30 | 31 | include (val Solution.make ~problem:(Number 55) ~main) 32 | -------------------------------------------------------------------------------- /sol/sol_055.mli: -------------------------------------------------------------------------------- 1 | open! Core 2 | open! Import 3 | include Solution.S 4 | -------------------------------------------------------------------------------- /sol/sol_056.ml: -------------------------------------------------------------------------------- 1 | open! Core 2 | open! Import 3 | 4 | let main () = 5 | let range = Number_theory.Bigint.range Bigint.one (Bigint.of_int 100) in 6 | Sequence.cartesian_product range range 7 | |> Sequence.map ~f:(fun (a, b) -> 8 | Bigint.pow a b |> Number_theory.Bigint.As_base10.sum (module Bigint) ~f:Fn.id) 9 | |> Sequence.max_elt ~compare:Bigint.compare 10 | |> Option.value_exn 11 | |> printf !"%{Bigint}\n" 12 | ;; 13 | 14 | (* 134.391ms *) 15 | let%expect_test "answer" = 16 | main (); 17 | [%expect {| 972 |}] 18 | ;; 19 | 20 | include (val Solution.make ~problem:(Number 56) ~main) 21 | -------------------------------------------------------------------------------- /sol/sol_056.mli: -------------------------------------------------------------------------------- 1 | open! Core 2 | open! Import 3 | include Solution.S 4 | -------------------------------------------------------------------------------- /sol/sol_057.ml: -------------------------------------------------------------------------------- 1 | open! Core 2 | open! Import 3 | 4 | let is_more_num_digits r = 5 | let num = r |> Bignum.num_as_bigint |> Bigint.to_string |> String.length in 6 | let den = r |> Bignum.den_as_bigint |> Bigint.to_string |> String.length in 7 | num > den 8 | ;; 9 | 10 | let main () = 11 | let expansions = 12 | let open Bignum.O in 13 | let init = of_int 1 + (1 // 2) in 14 | Sequence.unfold_step ~init ~f:(fun s -> 15 | let next = of_int 1 + (of_int 1 / (of_int 1 + s)) in 16 | Yield { value = s; state = next }) 17 | in 18 | expansions 19 | |> Fn.flip Sequence.take 1000 20 | |> Sequence.count ~f:is_more_num_digits 21 | |> printf "%d\n" 22 | ;; 23 | 24 | (* 19.716ms *) 25 | let%expect_test "answer" = 26 | main (); 27 | [%expect {| 153 |}] 28 | ;; 29 | 30 | include (val Solution.make ~problem:(Number 57) ~main) 31 | -------------------------------------------------------------------------------- /sol/sol_057.mli: -------------------------------------------------------------------------------- 1 | open! Core 2 | open! Import 3 | include Solution.S 4 | -------------------------------------------------------------------------------- /sol/sol_058.ml: -------------------------------------------------------------------------------- 1 | open! Core 2 | open! Import 3 | 4 | (* Interpolate the quadratic polynomial [ax^2 + bx + c] passing through points 5 | [(1, y1), (2, y2), (3, y3)] using the Lagrange interpolation formula. *) 6 | let interpolate y1 y2 y3 = 7 | stage (fun x -> 8 | (y1 * (x - 2) * (x - 3) / ((1 - 2) * (1 - 3))) 9 | + (y2 * (x - 1) * (x - 3) / ((2 - 1) * (2 - 3))) 10 | + (y3 * (x - 1) * (x - 2) / ((3 - 1) * (3 - 2)))) 11 | ;; 12 | 13 | let main () = 14 | let top_right = unstage (interpolate 3 13 31) in 15 | let top_left = unstage (interpolate 5 17 37) in 16 | let bottom_left = unstage (interpolate 7 21 43) in 17 | Number_theory.Int.natural_numbers () ~init:1 18 | |> Sequence.folding_map ~init:(0, 1) ~f:(fun (primes, total) r -> 19 | let side_length = (2 * r) + 1 in 20 | let primes = 21 | primes 22 | + ([ top_right r; top_left r; bottom_left r ] 23 | |> List.count ~f:Number_theory.Int.is_prime) 24 | in 25 | let total = total + 4 in 26 | (primes, total), (side_length, (primes, total))) 27 | |> Sequence.find_exn ~f:(fun (_, (primes, total)) -> primes * 10 < total) 28 | |> fst 29 | |> printf "%d\n" 30 | ;; 31 | 32 | (* 26241 33 | 337.116ms *) 34 | include (val Solution.make ~problem:(Number 58) ~main) 35 | -------------------------------------------------------------------------------- /sol/sol_058.mli: -------------------------------------------------------------------------------- 1 | open! Core 2 | open! Import 3 | include Solution.S 4 | -------------------------------------------------------------------------------- /sol/sol_059.ml: -------------------------------------------------------------------------------- 1 | open! Core 2 | open! Import 3 | 4 | (* TODO Replace [Bytes.unsafe_{set,get}] with infix notation. *) 5 | 6 | let decrypt ciphertext ~key = 7 | let plaintext = Bytes.copy ciphertext in 8 | let key_length = String.length key in 9 | for i = 0 to Bytes.length plaintext - 1 do 10 | Bytes.unsafe_set 11 | plaintext 12 | i 13 | (Char.unsafe_of_int 14 | (Char.to_int (Bytes.unsafe_get plaintext i) 15 | lxor Char.to_int key.[i mod key_length])) 16 | done; 17 | Bytes.unsafe_to_string ~no_mutation_while_string_reachable:plaintext 18 | ;; 19 | 20 | let key_length = 3 21 | let ( lxor ) a b = Char.unsafe_of_int (Char.to_int a lxor Char.to_int b) 22 | let is_english_char char = Char.is_alpha char || Char.is_whitespace char 23 | 24 | let best_key_char ~key_index ~ciphertext = 25 | let ciphertext = 26 | Bytes.unsafe_to_string ~no_mutation_while_string_reachable:ciphertext 27 | in 28 | List.map Char.all ~f:(fun c -> 29 | ( c 30 | , String.foldi ciphertext ~init:0 ~f:(fun i ac x -> 31 | if i mod key_length = key_index && is_english_char (c lxor x) then ac + 1 else ac) 32 | )) 33 | |> List.max_elt ~compare:[%compare: _ * int] 34 | |> Option.value_exn 35 | |> fst 36 | ;; 37 | 38 | let main () = 39 | let ciphertext = 40 | Problem_059.data 41 | |> Parse.csv_line ~f:Int.of_string 42 | |> List.map ~f:Char.of_int_exn 43 | |> Bytes.of_char_list 44 | in 45 | let a = best_key_char ~ciphertext ~key_index:0 in 46 | let b = best_key_char ~ciphertext ~key_index:1 in 47 | let c = best_key_char ~ciphertext ~key_index:2 in 48 | let plaintext = decrypt ciphertext ~key:(String.of_char_list [ a; b; c ]) in 49 | if debug then Debug.eprint_s [%sexp (plaintext : string)]; 50 | plaintext |> String.sum (module Int) ~f:Char.to_int |> printf "%d\n" 51 | ;; 52 | 53 | (* 7.376ms *) 54 | let%expect_test "answer" = 55 | main (); 56 | [%expect {| 107359 |}] 57 | ;; 58 | 59 | include (val Solution.make ~problem:(Number 59) ~main) 60 | -------------------------------------------------------------------------------- /sol/sol_059.mli: -------------------------------------------------------------------------------- 1 | open! Core 2 | open! Import 3 | include Solution.S 4 | -------------------------------------------------------------------------------- /sol/sol_060.mli: -------------------------------------------------------------------------------- 1 | open! Core 2 | open! Import 3 | include Solution.S 4 | -------------------------------------------------------------------------------- /sol/sol_061.mli: -------------------------------------------------------------------------------- 1 | open! Core 2 | open! Import 3 | include Solution.S 4 | -------------------------------------------------------------------------------- /sol/sol_062.ml: -------------------------------------------------------------------------------- 1 | open! Core 2 | open! Import 3 | 4 | module Digit_set : sig 5 | type t [@@deriving compare, hash, sexp_of] 6 | 7 | val of_int : int -> t 8 | end = struct 9 | type t = int list [@@deriving compare, hash, sexp_of] 10 | 11 | let of_int int = 12 | Number_theory.Int.As_base10.to_list int |> List.sort ~compare:Int.compare 13 | ;; 14 | end 15 | 16 | let find min_permutations = 17 | let cubes_by_digit_set = Hashtbl.create (module Digit_set) in 18 | let[@inline] cube n = Number_theory.Int.addition_chain_pow n 3 in 19 | let rec loop n = 20 | let c = cube n in 21 | let ds = Digit_set.of_int c in 22 | Hashtbl.add_multi cubes_by_digit_set ~key:ds ~data:c; 23 | let cubes = Hashtbl.find_multi cubes_by_digit_set ds in 24 | if List.length cubes >= min_permutations 25 | then Option.value_exn (List.min_elt cubes ~compare:Int.compare) 26 | else loop (n + 1) 27 | in 28 | loop 1 29 | ;; 30 | 31 | let min_permutations = 5 32 | let main () = find min_permutations |> printf "%d\n" 33 | 34 | (* 29.618ms *) 35 | let%expect_test "answer" = 36 | main (); 37 | [%expect {| 127035954683 |}] 38 | ;; 39 | 40 | include (val Solution.make ~problem:(Number 62) ~main) 41 | -------------------------------------------------------------------------------- /sol/sol_062.mli: -------------------------------------------------------------------------------- 1 | open! Core 2 | open! Import 3 | include Solution.S 4 | -------------------------------------------------------------------------------- /sol/sol_063.ml: -------------------------------------------------------------------------------- 1 | open! Core 2 | open! Import 3 | 4 | let number_of_digits_in_pow a b = 5 | 1 + (float b *. Float.log10 (float a) |> Float.iround_down_exn) 6 | ;; 7 | 8 | let%test_unit _ = 9 | let pos = Quickcheck.Generator.small_positive_int in 10 | Quickcheck.test 11 | (Quickcheck.Generator.tuple2 pos pos) 12 | ~sexp_of:[%sexp_of: int * int] 13 | ~f:(fun (a, b) -> 14 | let expect = 15 | Bigint.pow (Bigint.of_int a) (Bigint.of_int b) 16 | |> Bigint.to_string 17 | |> String.length 18 | in 19 | [%test_result: int] (number_of_digits_in_pow a b) ~expect) 20 | ;; 21 | 22 | (* n-digit numbers are those in the half-open interval [10^(n-1), 10^n). 23 | Clearly, k^n is outside of this range for k >= 10, so we only concern 24 | ourselves with k < 10. 25 | 26 | We can limit ourselves to n where k^n >= 10^(n - 1) for some k < 10. Clearly 27 | this is true the longest for k=9, so we solve 9^n >= 10^(n - 1) to get: 28 | 29 | {v 30 | 9^n >= 10^(n - 1) 31 | n log(9) >= (n - 1) log(10) 32 | (log(10) - log(9)) n <= log(10) 33 | n <= 21.8543453284 34 | v} 35 | *) 36 | 37 | (* n-digit numbers that are also nth powers *) 38 | let count_digit_powers n = 39 | List.range 1 10 |> List.count ~f:(fun x -> number_of_digits_in_pow x n = n) 40 | ;; 41 | 42 | let main () = 43 | List.range 1 22 |> List.sum (module Int) ~f:count_digit_powers |> printf "%d\n" 44 | ;; 45 | 46 | (* 0.046ms *) 47 | let%expect_test "answer" = 48 | main (); 49 | [%expect {| 49 |}] 50 | ;; 51 | 52 | include (val Solution.make ~problem:(Number 63) ~main) 53 | -------------------------------------------------------------------------------- /sol/sol_063.mli: -------------------------------------------------------------------------------- 1 | open! Core 2 | open! Import 3 | include Solution.S 4 | -------------------------------------------------------------------------------- /sol/sol_067.ml: -------------------------------------------------------------------------------- 1 | open! Core 2 | open! Import 3 | 4 | let main () = 5 | Problem_067.data 6 | |> Parse.space_separated_grid ~conv:Int.of_string 7 | |> Sol_018.max_sum_exn 8 | |> printf "%d\n" 9 | ;; 10 | 11 | (* 0.903ms *) 12 | let%expect_test "answer" = 13 | main (); 14 | [%expect {| 7273 |}] 15 | ;; 16 | 17 | include (val Solution.make ~problem:(Number 67) ~main) 18 | -------------------------------------------------------------------------------- /sol/sol_067.mli: -------------------------------------------------------------------------------- 1 | open! Core 2 | open! Import 3 | include Solution.S 4 | -------------------------------------------------------------------------------- /sol/sol_080.ml: -------------------------------------------------------------------------------- 1 | open! Core 2 | open! Import 3 | 4 | let sqrt n = 5 | let open Bignum in 6 | Numerics.Bignum.newton's_method 7 | ~f:(fun x -> (x * x) - n) 8 | ~f':(fun x -> of_int 2 * x) 9 | ~init:one 10 | ~epsilon:Bignum.(tenth ** 101) 11 | ;; 12 | 13 | let hundred_decimal_digits n = 14 | Bignum.(n * (ten ** 101) |> round_as_bigint |> Option.value_exn) 15 | ;; 16 | 17 | let digital_sum n = 18 | n 19 | |> Bignum.of_int 20 | |> sqrt 21 | |> hundred_decimal_digits 22 | |> Bigint.to_string 23 | |> String.subo ~len:100 24 | |> Sequences.digits_of_string 25 | |> List.sum (module Int) ~f:Fn.id 26 | ;; 27 | 28 | let main () = 29 | let range = List.range 1 100 ~stop:`inclusive |> Set.of_list (module Int) in 30 | let squares = 31 | List.range 1 10 ~stop:`inclusive 32 | |> List.map ~f:(Fn.flip Int.pow 2) 33 | |> Set.of_list (module Int) 34 | in 35 | let irrational_sqrts = Set.diff range squares in 36 | irrational_sqrts 37 | |> Set.to_sequence 38 | |> Sequence.sum (module Int) ~f:digital_sum 39 | |> printf "%d\n" 40 | ;; 41 | 42 | (* 95.364ms *) 43 | let%expect_test "answer" = 44 | main (); 45 | [%expect {| 40886 |}] 46 | ;; 47 | 48 | include (val Solution.make ~problem:(Number 80) ~main) 49 | -------------------------------------------------------------------------------- /sol/sol_080.mli: -------------------------------------------------------------------------------- 1 | open! Core 2 | open! Import 3 | include Solution.S 4 | -------------------------------------------------------------------------------- /sol/sol_084.mli: -------------------------------------------------------------------------------- 1 | open! Core 2 | open! Import 3 | include Solution.S 4 | -------------------------------------------------------------------------------- /sol/sol_103.ml: -------------------------------------------------------------------------------- 1 | open! Core 2 | open! Import 3 | 4 | let rec power_set list = 5 | match list with 6 | | [] -> [ [] ] 7 | | hd :: tl -> 8 | power_set tl |> List.concat_map ~f:(fun subset -> [ subset; hd :: subset ]) 9 | ;; 10 | 11 | let%expect_test _ = 12 | power_set [ 1; 2; 3 ] |> [%sexp_of: int list list] |> print_s; 13 | [%expect 14 | {| 15 | (() 16 | (1) 17 | (2) 18 | (1 2) 19 | (3) 20 | (1 3) 21 | (2 3) 22 | (1 2 3)) |}] 23 | ;; 24 | 25 | let is_special list = 26 | let subsets = power_set list |> List.tl_exn in 27 | let subsets = Array.of_list subsets in 28 | let sum subset = List.sum (module Int) ~f:Fn.id subset in 29 | with_return (fun { return } -> 30 | for i = 0 to Array.length subsets - 1 do 31 | for j = i + 1 to Array.length subsets - 1 do 32 | let b, c = subsets.(i), subsets.(j) in 33 | let sb, sc = sum b, sum c in 34 | let lb, lc = List.length b, List.length c in 35 | if not (sb <> sc && lb > lc ==> (sb > sc) && lc > lb ==> (sc > sb)) 36 | then return false 37 | done 38 | done; 39 | true) 40 | ;; 41 | 42 | let%test _ = is_special [ 2; 3; 4 ] 43 | let%test _ = not (is_special [ 2; 3; 5 ]) 44 | 45 | let rec sets n ~ubound = 46 | if n = 0 47 | then Sequence.singleton [] 48 | else 49 | Sequence.range 1 ubound ~stop:`inclusive 50 | |> Sequence.concat_map ~f:(fun x -> 51 | sets (n - 1) ~ubound:(x - 1) |> Sequence.map ~f:(fun set -> x :: set)) 52 | ;; 53 | 54 | let optimum_special_set n ~ubound = 55 | let compare_by_sum = Comparable.lift Int.compare ~f:(List.sum (module Int) ~f:Fn.id) in 56 | sets n ~ubound 57 | |> Sequence.filter ~f:is_special 58 | |> Sequence.min_elt ~compare:compare_by_sum 59 | |> Option.value_exn 60 | ;; 61 | 62 | let%expect_test _ = 63 | let limit = 4 in 64 | (* change to 6 to see the example from the problem *) 65 | for n = 1 to limit do 66 | print_s [%message "" (n : int) (optimum_special_set n ~ubound:30 : int list)] 67 | done; 68 | [%expect 69 | {| 70 | ((n 1) ("optimum_special_set n ~ubound:30" (1))) 71 | ((n 2) ("optimum_special_set n ~ubound:30" (2 1))) 72 | ((n 3) ("optimum_special_set n ~ubound:30" (4 3 2))) 73 | ((n 4) ("optimum_special_set n ~ubound:30" (7 6 5 3))) |}] 74 | ;; 75 | 76 | let main () = 77 | optimum_special_set 7 ~ubound:45 78 | |> List.rev_map ~f:Int.to_string 79 | |> String.concat 80 | |> print_endline 81 | ;; 82 | 83 | (* 20313839404245 84 | 4.60712m *) 85 | 86 | include (val Solution.make ~problem:(Number 103) ~main) 87 | -------------------------------------------------------------------------------- /sol/sol_103.mli: -------------------------------------------------------------------------------- 1 | open! Core 2 | open! Import 3 | include Solution.S 4 | 5 | val is_special : int list -> bool 6 | -------------------------------------------------------------------------------- /sol/sol_105.ml: -------------------------------------------------------------------------------- 1 | open! Core 2 | open! Import 3 | 4 | let main () = 5 | Problem_105.data 6 | |> String.split_lines 7 | |> List.map ~f:(String.split ~on:',' >> List.map ~f:Int.of_string) 8 | |> List.filter ~f:Sol_103.is_special 9 | |> List.sum (module Int) ~f:(List.sum (module Int) ~f:Fn.id) 10 | |> printf "%d\n" 11 | ;; 12 | 13 | (* 73702 14 | 5.02189s *) 15 | include (val Solution.make ~problem:(Number 105) ~main) 16 | -------------------------------------------------------------------------------- /sol/sol_105.mli: -------------------------------------------------------------------------------- 1 | open! Core 2 | open! Import 3 | include Solution.S 4 | -------------------------------------------------------------------------------- /sol/sol_106.mli: -------------------------------------------------------------------------------- 1 | open! Core 2 | open! Import 3 | include Solution.S 4 | -------------------------------------------------------------------------------- /sol/sol_108.ml: -------------------------------------------------------------------------------- 1 | open! Core 2 | open! Import 3 | 4 | (* TODO: After solving 110, see if the below can be made faster. *) 5 | 6 | let num_solutions n = 7 | if debug && n % 1000 = 0 then Debug.eprintf "%d" n; 8 | let count = ref 2 in 9 | for i = n + 2 to (2 * n) - 1 do 10 | if n * i mod (i - n) = 0 then incr count 11 | done; 12 | !count 13 | ;; 14 | 15 | let main () = 16 | Number_theory.Int.natural_numbers () ~init:1 17 | |> Sequence.find_exn ~f:(fun n -> num_solutions n > 1000) 18 | |> printf "%d\n" 19 | ;; 20 | 21 | (* 180180 22 | 3.5m *) 23 | include (val Solution.make ~problem:(Number 108) ~main) 24 | -------------------------------------------------------------------------------- /sol/sol_108.mli: -------------------------------------------------------------------------------- 1 | open! Core 2 | open! Import 3 | include Solution.S 4 | -------------------------------------------------------------------------------- /sol/sol_109.ml: -------------------------------------------------------------------------------- 1 | open! Core 2 | open! Import 3 | 4 | module Hit : sig 5 | type t [@@deriving compare, hash, enumerate, sexp] 6 | 7 | val score : t -> int 8 | val is_double : t -> bool 9 | end = struct 10 | module T = struct 11 | module For_enumerate = struct 12 | let all_of_int = 13 | [ 20; 1; 18; 4; 13; 6; 10; 15; 2; 17; 3; 19; 7; 16; 8; 11; 14; 9; 12; 5 ] 14 | ;; 15 | end 16 | 17 | open For_enumerate 18 | 19 | type t = 20 | | Single of int 21 | | Double of int 22 | | Triple of int 23 | | Bulls_eye 24 | | Double_bulls_eye 25 | [@@deriving compare, hash, enumerate, sexp] 26 | end 27 | 28 | include T 29 | 30 | let score t = 31 | match t with 32 | | Single n -> n 33 | | Double n -> 2 * n 34 | | Triple n -> 3 * n 35 | | Bulls_eye -> 25 36 | | Double_bulls_eye -> 50 37 | ;; 38 | 39 | let is_double t = 40 | match t with 41 | | Double _ | Double_bulls_eye -> true 42 | | _ -> false 43 | ;; 44 | end 45 | 46 | module Hit_or_miss : sig 47 | type t [@@deriving compare, enumerate, sexp_of] 48 | 49 | val score : t -> int 50 | val is_double : t -> bool 51 | end = struct 52 | type t = Hit.t option [@@deriving compare, enumerate] 53 | 54 | let sexp_of_t = function 55 | | None -> Sexp.Atom "Miss" 56 | | Some hit -> Sexp.List [ Sexp.Atom "Hit"; [%sexp (hit : Hit.t)] ] 57 | ;; 58 | 59 | let score = Option.value_map ~default:0 ~f:Hit.score 60 | let is_double = Option.value_map ~default:false ~f:Hit.is_double 61 | end 62 | 63 | let count_checkouts ~f = 64 | let count = ref 0 in 65 | List.iter Hit_or_miss.all ~f:(fun last -> 66 | if Hit_or_miss.is_double last 67 | then 68 | List.iter Hit_or_miss.all ~f:(fun second -> 69 | List.iter Hit_or_miss.all ~f:(fun first -> 70 | if Hit_or_miss.compare first second <= 0 71 | then 72 | if f 73 | (Hit_or_miss.score first 74 | + Hit_or_miss.score second 75 | + Hit_or_miss.score last) 76 | then incr count))); 77 | !count 78 | ;; 79 | 80 | let%expect_test _ = 81 | print_s [%sexp (count_checkouts ~f:(Int.( = ) 6) : int)]; 82 | [%expect {| 11 |}] 83 | ;; 84 | 85 | let main () = count_checkouts ~f:(fun x -> x < 100) |> printf "%d\n" 86 | 87 | (* 1.895ms *) 88 | let%expect_test "answer" = 89 | main (); 90 | [%expect {| 38182 |}] 91 | ;; 92 | 93 | include (val Solution.make ~problem:(Number 109) ~main) 94 | -------------------------------------------------------------------------------- /sol/sol_109.mli: -------------------------------------------------------------------------------- 1 | open! Core 2 | open! Import 3 | include Solution.S 4 | -------------------------------------------------------------------------------- /sol/sol_111.ml: -------------------------------------------------------------------------------- 1 | open! Core 2 | open! Import 3 | 4 | let change_some_digits digits ~num_digits_to_change ~f = 5 | let rec loop ~pos ~num_digits_to_change = 6 | if num_digits_to_change = 0 7 | then f digits 8 | else if pos < Array.length digits 9 | then ( 10 | loop ~pos:(pos + 1) ~num_digits_to_change; 11 | let old_digit = digits.(pos) in 12 | for new_digit = 0 to 9 do 13 | if old_digit <> new_digit 14 | then ( 15 | digits.(pos) <- new_digit; 16 | loop ~pos:(pos + 1) ~num_digits_to_change:(num_digits_to_change - 1); 17 | digits.(pos) <- old_digit) 18 | done) 19 | in 20 | loop ~pos:0 ~num_digits_to_change 21 | ;; 22 | 23 | let primes_with_runs ~main_digit ~num_digits = 24 | let digits = Array.create ~len:num_digits main_digit in 25 | with_return (fun { return } -> 26 | for num_digits_to_change = 0 to num_digits - 1 do 27 | let sum = ref 0 in 28 | change_some_digits digits ~num_digits_to_change ~f:(fun digits -> 29 | if digits.(0) <> 0 30 | then ( 31 | let n = 32 | Array.to_sequence_mutable digits |> Number_theory.Int.As_base10.of_sequence 33 | in 34 | if Number_theory.Int.is_prime n then sum := !sum + n)); 35 | if !sum <> 0 then return !sum 36 | done; 37 | assert false) 38 | ;; 39 | 40 | let num_digits = 10 41 | 42 | let main () = 43 | let sum = ref 0 in 44 | for main_digit = 0 to 9 do 45 | sum := !sum + (primes_with_runs ~main_digit ~num_digits : int) 46 | done; 47 | printf "%d\n" !sum 48 | ;; 49 | 50 | (* 66.153ms *) 51 | let%expect_test "answer" = 52 | main (); 53 | [%expect {| 612407567715 |}] 54 | ;; 55 | 56 | include (val Solution.make ~problem:(Number 111) ~main) 57 | -------------------------------------------------------------------------------- /sol/sol_111.mli: -------------------------------------------------------------------------------- 1 | open! Core 2 | open! Import 3 | include Solution.S 4 | -------------------------------------------------------------------------------- /sol/sol_116.ml: -------------------------------------------------------------------------------- 1 | open! Core 2 | open! Import 3 | 4 | module Cache_key = struct 5 | type t = 6 | { total_size : int 7 | ; block_size : int 8 | } 9 | [@@deriving compare, hash, sexp_of] 10 | end 11 | 12 | let ways_to_replace = 13 | Memo.recursive 14 | (module Cache_key) 15 | (fun ways_to_replace { total_size; block_size } -> 16 | if block_size > total_size 17 | then 1 18 | else ( 19 | let gray_tile = ways_to_replace { total_size = total_size - 1; block_size } in 20 | let colored_tile = 21 | ways_to_replace { total_size = total_size - block_size; block_size } 22 | in 23 | gray_tile + colored_tile)) 24 | ;; 25 | 26 | let main () = 27 | (* Need to subtract 1 from each because we want to exclude empty 28 | "replacements". *) 29 | let red = ways_to_replace { total_size = 50; block_size = 2 } - 1 in 30 | let green = ways_to_replace { total_size = 50; block_size = 3 } - 1 in 31 | let blue = ways_to_replace { total_size = 50; block_size = 4 } - 1 in 32 | printf "%d\n" (red + green + blue) 33 | ;; 34 | 35 | (* 46us *) 36 | let%expect_test "answer" = 37 | main (); 38 | [%expect {| 20492570929 |}] 39 | ;; 40 | 41 | include (val Solution.make ~problem:(Number 116) ~main) 42 | -------------------------------------------------------------------------------- /sol/sol_116.mli: -------------------------------------------------------------------------------- 1 | open! Core 2 | open! Import 3 | include Solution.S 4 | -------------------------------------------------------------------------------- /sol/sol_117.ml: -------------------------------------------------------------------------------- 1 | open! Core 2 | open! Import 3 | 4 | let ways_to_replace = 5 | Memo.recursive 6 | (module Int) 7 | (fun ways_to_replace total_size -> 8 | if total_size < 0 9 | then 0 10 | else if total_size = 0 11 | then 1 12 | else ( 13 | let gray = ways_to_replace (total_size - 1) in 14 | let red = ways_to_replace (total_size - 2) in 15 | let green = ways_to_replace (total_size - 3) in 16 | let blue = ways_to_replace (total_size - 4) in 17 | gray + red + green + blue)) 18 | ;; 19 | 20 | let main () = printf "%d\n" (ways_to_replace 50) 21 | 22 | (* 15us *) 23 | let%expect_test "answer" = 24 | main (); 25 | [%expect {| 100808458960497 |}] 26 | ;; 27 | 28 | include (val Solution.make ~problem:(Number 117) ~main) 29 | -------------------------------------------------------------------------------- /sol/sol_117.mli: -------------------------------------------------------------------------------- 1 | open! Core 2 | open! Import 3 | include Solution.S 4 | -------------------------------------------------------------------------------- /sol/sol_118.ml: -------------------------------------------------------------------------------- 1 | open! Core 2 | open! Import 3 | 4 | let set_diff a b ~equal = 5 | match a, b with 6 | | [], _ | _, [] -> a 7 | | _ -> List.filter a ~f:(Fn.non (List.mem b ~equal)) 8 | ;; 9 | 10 | let iter_subsets set ~f = 11 | let rec loop set subset = 12 | match set with 13 | | [] -> f subset 14 | | hd :: tl -> 15 | loop tl subset; 16 | loop tl (hd :: subset) 17 | in 18 | loop set [] 19 | ;; 20 | 21 | let iter_allprime_subsets set ~f = 22 | let rec loop set min subsets = 23 | match set with 24 | | [] -> f subsets 25 | | set -> 26 | iter_subsets set ~f:(fun subset -> 27 | Sequences.iter_permutations subset ~compare:Int.compare ~f:(fun n -> 28 | let n = 29 | n 30 | |> Array.Permissioned.to_sequence_mutable 31 | |> Number_theory.Int.As_base10.of_sequence 32 | in 33 | if n >= min && Number_theory.Int.is_prime n 34 | then loop (set_diff set subset ~equal:Int.equal) (n + 1) (subset :: subsets))) 35 | in 36 | loop set 0 [] 37 | ;; 38 | 39 | let main () = 40 | let count = ref 0 in 41 | iter_allprime_subsets [ 1; 2; 3; 4; 5; 6; 7; 8; 9 ] ~f:(fun subsets -> 42 | if debug then Debug.eprint_s [%sexp (subsets : int list list)]; 43 | incr count); 44 | printf "%d\n" !count 45 | ;; 46 | 47 | (* 44680 48 | 4.0339s *) 49 | 50 | include (val Solution.make ~problem:(Number 118) ~main) 51 | -------------------------------------------------------------------------------- /sol/sol_118.mli: -------------------------------------------------------------------------------- 1 | open! Core 2 | open! Import 3 | include Solution.S 4 | -------------------------------------------------------------------------------- /sol/sol_127.ml: -------------------------------------------------------------------------------- 1 | open! Core 2 | open! Import 3 | 4 | let prime_factors = 5 | Memo.simple 6 | (module Int) 7 | (fun n -> Number_theory.Int.prime_factor n |> List.map ~f:(fun (p, _) -> p)) 8 | ;; 9 | 10 | let prime_factors_product = 11 | Memo.simple (module Int) (fun n -> prime_factors n |> List.fold ~init:1 ~f:( * )) 12 | ;; 13 | 14 | (* precondition: a, b, c are pairwise coprime *) 15 | let radical a b c = 16 | prime_factors_product a * prime_factors_product b * prime_factors_product c 17 | ;; 18 | 19 | let%expect_test "radical" = 20 | print_s [%sexp (radical 8 9 7 : int)]; 21 | [%expect {| 42 |}] 22 | ;; 23 | 24 | let sieve ubound pfs = 25 | let is_coprime = Array.create true ~len:(ubound + 1) in 26 | List.iter pfs ~f:(fun pf -> 27 | for i = 0 to ubound / pf do 28 | is_coprime.(i * pf) <- false 29 | done); 30 | Array.filter_mapi is_coprime ~f:(fun i x -> if x then Some i else None) 31 | ;; 32 | 33 | let abc_hits ubound = 34 | Sequence.range 1 ubound 35 | |> Sequence.sum 36 | (module Int) 37 | ~f:(fun c -> 38 | if debug && c mod 100 = 0 then Debug.eprintf "c=%d" c; 39 | let pfs = prime_factors c in 40 | c 41 | * (sieve ((c / 2) - 1) pfs 42 | |> Array.count ~f:(fun a -> 43 | let b = c - a in 44 | radical a b c < c))) 45 | ;; 46 | 47 | let%expect_test "example" = 48 | print_s [%sexp (abc_hits 1000 : int)]; 49 | [%expect {| 12523 |}] 50 | ;; 51 | 52 | let main () = abc_hits 120_000 |> printf "%d\n" 53 | 54 | (* 18407904 55 | 11.8236m *) 56 | 57 | include (val Solution.make ~problem:(Number 127) ~main) 58 | -------------------------------------------------------------------------------- /sol/sol_127.mli: -------------------------------------------------------------------------------- 1 | open! Core 2 | open! Import 3 | include Solution.S 4 | -------------------------------------------------------------------------------- /sol/sol_131.mli: -------------------------------------------------------------------------------- 1 | open! Core 2 | open! Import 3 | include Solution.S 4 | -------------------------------------------------------------------------------- /sol/sol_142.ml: -------------------------------------------------------------------------------- 1 | open! Core 2 | open! Import 3 | 4 | let squareq = Deque.create () 5 | let i = ref 1 6 | 7 | let rec work ubound = 8 | let x = !i * !i in 9 | if x <= ubound 10 | then ( 11 | (* Hash_set.add squares x; *) 12 | Deque.enqueue_back squareq x; 13 | incr i; 14 | work ubound) 15 | ;; 16 | 17 | let is_perfect_square = Number_theory.Int.is_perfect_square 18 | 19 | let main () = 20 | let rec loop return x = 21 | work (2 * x); 22 | with_return (fun { return = break } -> 23 | Deque.iter squareq ~f:(fun x_y -> 24 | let y = x - x_y in 25 | if y <= 0 then break (); 26 | if is_perfect_square (x + y) 27 | then 28 | Deque.iter squareq ~f:(fun y_z -> 29 | let z = y - y_z in 30 | if z <= 0 then break (); 31 | if is_perfect_square (y + z) 32 | && is_perfect_square (x + z) 33 | && is_perfect_square (x - z) 34 | then return (x + y + z)))); 35 | loop return (x + 1) 36 | in 37 | with_return (fun { return } -> loop return 1) |> printf "%d\n" 38 | ;; 39 | 40 | (* 1006193 41 | 3.32094s *) 42 | include (val Solution.make ~problem:(Number 142) ~main) 43 | -------------------------------------------------------------------------------- /sol/sol_142.mli: -------------------------------------------------------------------------------- 1 | open! Core 2 | open! Import 3 | include Solution.S 4 | -------------------------------------------------------------------------------- /sol/sol_148.mli: -------------------------------------------------------------------------------- 1 | open! Core 2 | open! Import 3 | include Solution.S 4 | -------------------------------------------------------------------------------- /sol/sol_162.ml: -------------------------------------------------------------------------------- 1 | open! Core 2 | open! Import 3 | 4 | (* iterate over the possible positions of the first occurrences of each 5 | necessary digit. *) 6 | 7 | module Necessary_digit = struct 8 | type t = 9 | | Zero 10 | | One 11 | | Ten 12 | [@@deriving compare, equal] 13 | end 14 | 15 | open Necessary_digit 16 | 17 | module Element = struct 18 | type t = 19 | | Other 20 | | First_occurrence_of of Necessary_digit.t 21 | [@@deriving compare, equal] 22 | end 23 | 24 | open Element 25 | 26 | let patterns digits = 27 | let pattern = 28 | List.init (digits - 3) ~f:(fun _ -> Other) 29 | @ [ First_occurrence_of Zero; First_occurrence_of One; First_occurrence_of Ten ] 30 | in 31 | Sequences.permutations pattern ~compare:Element.compare 32 | |> Sequence.filter ~f:(fun perm -> 33 | not ([%equal: Element.t] (List.hd_exn perm) (First_occurrence_of Zero))) 34 | ;; 35 | 36 | let allowed_numbers digits = 37 | patterns digits 38 | |> Sequence.sum 39 | (module Int) 40 | ~f:(fun pattern -> 41 | List.fold pattern ~init:(1, 13) ~f:(fun (ac, allowed_digits) elt -> 42 | match elt with 43 | | Other -> ac * allowed_digits, allowed_digits 44 | | First_occurrence_of _ -> ac, allowed_digits + 1) 45 | |> fst) 46 | ;; 47 | 48 | let main () = 49 | let limit = 16 in 50 | Sequence.range 3 limit ~stop:`inclusive 51 | |> Sequence.sum (module Int) ~f:allowed_numbers 52 | |> printf "%X\n" 53 | ;; 54 | 55 | (* 6.268ms *) 56 | let%expect_test "answer" = 57 | main (); 58 | [%expect {| 3D58725572C62302 |}] 59 | ;; 60 | 61 | include (val Solution.make ~problem:(Number 162) ~main) 62 | -------------------------------------------------------------------------------- /sol/sol_162.mli: -------------------------------------------------------------------------------- 1 | open! Core 2 | open! Import 3 | include Solution.S 4 | -------------------------------------------------------------------------------- /sol/sol_166.ml: -------------------------------------------------------------------------------- 1 | open! Core 2 | open! Import 3 | 4 | let[@inline always] is_in_range x = x >= 0 && x <= 9 5 | 6 | (* 7 | {v 8 | a b c d 9 | e f g h 10 | i j k l 11 | m n o p 12 | v} 13 | *) 14 | 15 | let main () = 16 | let count = ref 0 in 17 | for a = 0 to 9 do 18 | if debug then Debug.eprint_s [%message (a : int)]; 19 | for b = 0 to 9 do 20 | for c = 0 to 9 do 21 | for d = 0 to 9 do 22 | let x = a + b + c + d in 23 | for e = 0 to 9 do 24 | for f = 0 to 9 do 25 | for g = 0 to 9 do 26 | let h = x - e - f - g in 27 | if is_in_range h 28 | then 29 | for i = 0 to 9 do 30 | for j = 0 to 9 do 31 | for k = 0 to 9 do 32 | let l = x - i - j - k in 33 | if is_in_range l 34 | then ( 35 | let m = x - a - e - i in 36 | if is_in_range m && m + j + g + d = x 37 | then ( 38 | let n = x - b - f - j in 39 | if is_in_range n 40 | then ( 41 | let o = x - c - g - k in 42 | if is_in_range o 43 | then ( 44 | let p = x - m - n - o in 45 | if is_in_range p && a + f + k + p = x then incr count)))) 46 | done 47 | done 48 | done 49 | done 50 | done 51 | done 52 | done 53 | done 54 | done 55 | done; 56 | printf "%d\n" !count 57 | ;; 58 | 59 | (* 7130034 60 | 12s *) 61 | 62 | include (val Solution.make ~problem:(Number 166) ~main) 63 | -------------------------------------------------------------------------------- /sol/sol_166.mli: -------------------------------------------------------------------------------- 1 | open! Core 2 | open! Import 3 | include Solution.S 4 | -------------------------------------------------------------------------------- /sol/sol_169.ml: -------------------------------------------------------------------------------- 1 | open! Core 2 | open! Import 3 | 4 | (* Recurrence described by ed_r at https://projecteuler.net/thread=169#13152. *) 5 | let f = 6 | Memo.recursive 7 | (module Bigint) 8 | (fun f n -> 9 | let open Bigint.O in 10 | if n = zero 11 | then Bigint.of_int 1 12 | else if n % Bigint.of_int 2 = zero 13 | then f (n asr 1) + f (Bigint.pred (n asr 1)) 14 | else f (n asr 1)) 15 | ;; 16 | 17 | let main () = f Bigint.(pow (of_int 10) (of_int 25)) |> printf !"%{Bigint}\n" 18 | 19 | (* TODO Check timing. *) 20 | (* 0.826ms *) 21 | let%expect_test "answer" = 22 | main (); 23 | [%expect {| 178653872807 |}] 24 | ;; 25 | 26 | include (val Solution.make ~problem:(Number 169) ~main) 27 | -------------------------------------------------------------------------------- /sol/sol_169.mli: -------------------------------------------------------------------------------- 1 | open! Core 2 | open! Import 3 | include Solution.S 4 | -------------------------------------------------------------------------------- /sol/sol_169_naive.mli: -------------------------------------------------------------------------------- 1 | open! Core 2 | open! Import 3 | include Solution.S 4 | -------------------------------------------------------------------------------- /sol/sol_174.ml: -------------------------------------------------------------------------------- 1 | open! Core 2 | open! Import 3 | 4 | (* A square lamina with outer side length a and inner side length b consists of 5 | [a^2 - b^2 = (a + b)(a - b)] tiles. Thus, for a given number of tiles (t), 6 | the number of possible square lamina is given by the number of 7 | factorizations, [t = (a + b)(a - b)] where a and b are positive integers. 8 | 9 | For each even factor [x] of [t] where [x * x < t] and [(t/x - x) % 2 = 0], we 10 | can construct such a square lamina with [a - b = x] and [a + b = t / x]. *) 11 | 12 | let count_square_laminae t = 13 | if t % 4 <> 0 14 | then 0 15 | else 16 | Number_theory.Int.divisors (t / 4) 17 | |> List.count ~f:(fun x -> 18 | let x = x * 2 in 19 | x * x < t) 20 | ;; 21 | 22 | let%expect_test "examples" = 23 | print_s [%sexp (count_square_laminae 8 : int)]; 24 | [%expect {| 1 |}]; 25 | print_s [%sexp (count_square_laminae 32 : int)]; 26 | [%expect {| 2 |}] 27 | ;; 28 | 29 | let count_square_laminae_naive t = 30 | Sequence.length 31 | (let open Sequence.Let_syntax in 32 | let%bind outer = 33 | Number_theory.Int.natural_numbers ~init:3 () 34 | |> Sequence.take_while ~f:(fun n -> (n * n) - ((n - 2) * (n - 2)) <= t) 35 | in 36 | let%bind inner = Sequence.range (outer - 2) 0 ~stride:(-2) in 37 | if (outer * outer) - (inner * inner) = t then return () else Sequence.empty) 38 | ;; 39 | 40 | let%test_unit "vs naive" = 41 | Quickcheck.test 42 | Quickcheck.Generator.small_positive_int 43 | ~sexp_of:[%sexp_of: int] 44 | ~f:(fun t -> 45 | [%test_result: int] ~expect:(count_square_laminae_naive t) (count_square_laminae t)) 46 | ;; 47 | 48 | let main () = 49 | let tiles_by_type = Hashtbl.create (module Int) in 50 | debug_timing [%here] "iterating" (fun () -> 51 | for t = 1 to 1_000_000 do 52 | if debug && t % 10_000 = 0 then Debug.eprintf "%d" t; 53 | Hashtbl.add_multi tiles_by_type ~key:(count_square_laminae t) ~data:t 54 | done); 55 | [%test_result: int] 56 | ~expect:832 57 | (Hashtbl.find_multi tiles_by_type 15 |> List.length) 58 | ~message:"Expected N(15) = 832"; 59 | let sum = ref 0 in 60 | for n = 1 to 10 do 61 | sum := !sum + (Hashtbl.find_multi tiles_by_type n |> List.length) 62 | done; 63 | printf "%d\n" !sum 64 | ;; 65 | 66 | (* 209566 67 | 489.617085ms *) 68 | 69 | include (val Solution.make ~problem:(Number 174) ~main) 70 | -------------------------------------------------------------------------------- /sol/sol_174.mli: -------------------------------------------------------------------------------- 1 | open! Core 2 | open! Import 3 | include Solution.S 4 | -------------------------------------------------------------------------------- /sol/sol_205.ml: -------------------------------------------------------------------------------- 1 | open! Core 2 | open! Import 3 | 4 | type dist = Percent.t Map.M(Int).t 5 | 6 | let empty_dist : dist = Map.of_alist_exn (module Int) [ 0, Percent.of_mult 1.0 ] 7 | 8 | let shift_n dist ~n : dist = 9 | let comparator = Map.comparator dist in 10 | Map.to_alist dist 11 | |> List.map ~f:(Tuple2.map_fst ~f:(( + ) n)) 12 | |> Map.Using_comparator.of_alist_exn ~comparator 13 | ;; 14 | 15 | let scale_div_n dist ~n : dist = 16 | Map.map dist ~f:(fun p -> Percent.scale p (1. /. float n)) 17 | ;; 18 | 19 | let merge_dist ~key:_ data = 20 | let prob = 21 | match data with 22 | | `Both (p1, p2) -> Percent.(p1 + p2) 23 | | `Left p1 -> p1 24 | | `Right p2 -> p2 25 | in 26 | Some prob 27 | ;; 28 | 29 | let add_die dist die : dist = 30 | Sequence.range 1 die ~stop:`inclusive 31 | |> Sequence.map ~f:(fun n -> dist |> shift_n ~n |> scale_div_n ~n:die) 32 | |> Sequence.fold ~init:(Map.empty (module Int)) ~f:(Map.merge ~f:merge_dist) 33 | ;; 34 | 35 | let dice_set ~faces ~len : dist = 36 | Sequence.range 0 len 37 | |> Sequence.fold ~init:empty_dist ~f:(fun dist _ -> add_die dist faces) 38 | ;; 39 | 40 | let win_prob winner loser : Percent.t = 41 | Map.mapi winner ~f:(fun ~key:roll_w ~data:prob_w -> 42 | Map.mapi loser ~f:(fun ~key:roll_l ~data:prob_l -> 43 | if roll_w > roll_l then Percent.(prob_w * prob_l) else Percent.zero) 44 | |> Map.data 45 | |> List.sum (module Percent) ~f:Fn.id) 46 | |> Map.data 47 | |> List.sum (module Percent) ~f:Fn.id 48 | ;; 49 | 50 | let main () = 51 | let peter = dice_set ~faces:4 ~len:9 in 52 | let colin = dice_set ~faces:6 ~len:6 in 53 | win_prob peter colin |> Percent.to_mult |> printf "%.07f\n" 54 | ;; 55 | 56 | (* 0.168ms *) 57 | let%expect_test "answer" = 58 | main (); 59 | [%expect {| 0.5731441 |}] 60 | ;; 61 | 62 | include (val Solution.make ~problem:(Number 205) ~main) 63 | -------------------------------------------------------------------------------- /sol/sol_205.mli: -------------------------------------------------------------------------------- 1 | open! Core 2 | open! Import 3 | include Solution.S 4 | -------------------------------------------------------------------------------- /sol/sol_206.ml: -------------------------------------------------------------------------------- 1 | open! Core 2 | open! Import 3 | 4 | let main () = 5 | let pattern = "1.2.3.4.5.6.7.8.9.0" in 6 | let pattern_re = Re.Perl.compile_pat pattern in 7 | let sqrt_replace digit ~dir = 8 | String.tr pattern ~target:'.' ~replacement:digit 9 | |> Int.of_string 10 | |> Float.of_int 11 | |> sqrt 12 | |> Float.iround_exn ~dir 13 | in 14 | let lb = sqrt_replace '0' ~dir:`Down |> Int.round_down ~to_multiple_of:10 in 15 | let ub = sqrt_replace '9' ~dir:`Up |> Int.round_up ~to_multiple_of:10 in 16 | Sequence.range lb ub ~stop:`inclusive ~stride:10 17 | |> Sequence.find_exn ~f:(fun n -> n * n |> Int.to_string |> Re.execp pattern_re) 18 | |> printf "%d\n" 19 | ;; 20 | 21 | (* 1389019170 22 | 16s *) 23 | 24 | include (val Solution.make ~problem:(Number 206) ~main) 25 | -------------------------------------------------------------------------------- /sol/sol_206.mli: -------------------------------------------------------------------------------- 1 | open! Core 2 | open! Import 3 | include Solution.S 4 | -------------------------------------------------------------------------------- /sol/sol_207.ml: -------------------------------------------------------------------------------- 1 | open! Core 2 | open! Import 3 | 4 | (* 5 | 4^t = 2^t + k 6 | 4^t - 2^t = k 7 | n^2 - n = k where n = 2^t 8 | ==> the partition is perfect iff n is a power of 2. 9 | *) 10 | 11 | let main () = 12 | let ns = Number_theory.Int.natural_numbers ~init:2 () in 13 | Sequence.unfold_with ns ~init:(0, 0) ~f:(fun (part, perfect) n -> 14 | let new_state = part + 1, if Int.is_pow2 n then perfect + 1 else perfect in 15 | Yield { value = new_state, (n * n) - n; state = new_state }) 16 | |> Sequence.find_exn ~f:(fun ((part, perfect), _m) -> 17 | (* perfect / part < 1 / 12345 *) 18 | 12345 * perfect < part) 19 | |> Tuple2.get2 20 | |> printf "%d\n" 21 | ;; 22 | 23 | (* 4.557ms *) 24 | let%expect_test "answer" = 25 | main (); 26 | [%expect {| 44043947822 |}] 27 | ;; 28 | 29 | include (val Solution.make ~problem:(Number 207) ~main) 30 | -------------------------------------------------------------------------------- /sol/sol_207.mli: -------------------------------------------------------------------------------- 1 | open! Core 2 | open! Import 3 | include Solution.S 4 | -------------------------------------------------------------------------------- /sol/sol_214.ml: -------------------------------------------------------------------------------- 1 | open! Core 2 | open! Import 3 | 4 | let totient_chain_length limit = 5 | let table = Option_array.create ~len:(limit + 1) in 6 | Option_array.set_some table 1 1; 7 | let rec totient_chain_length n = 8 | if Option_array.is_some table n 9 | then Option_array.get_some_exn table n 10 | else ( 11 | let result = totient_chain_length (Number_theory.Int.totient n) + 1 in 12 | Option_array.set_some table n result; 13 | result) 14 | in 15 | stage totient_chain_length 16 | ;; 17 | 18 | let limit = 40_000_000 19 | 20 | let main () = 21 | let totient_chain_length = unstage (totient_chain_length limit) in 22 | debug_timing [%here] "sieving" (fun () -> Number_theory.prime_sieve limit) 23 | |> Array.foldi ~init:0 ~f:(fun i acc is_prime -> 24 | if is_prime && totient_chain_length i = 25 then acc + i else acc) 25 | |> printf "%d\n" 26 | ;; 27 | 28 | (* 1677366278943 29 | 46.0785s *) 30 | 31 | include (val Solution.make ~problem:(Number 214) ~main) 32 | -------------------------------------------------------------------------------- /sol/sol_214.mli: -------------------------------------------------------------------------------- 1 | open! Core 2 | open! Import 3 | include Solution.S 4 | -------------------------------------------------------------------------------- /sol/sol_293.ml: -------------------------------------------------------------------------------- 1 | open! Core 2 | open! Import 3 | 4 | let is_prime = lazy (Number_theory.prime_sieve 100_000) 5 | 6 | let is_admissible n = 7 | if debug && n % 1_000_000 = 0 then Debug.eprintf !"%{Int#hum}" n; 8 | let rec start n i = 9 | if n = 1 10 | then true 11 | else if (force is_prime).(i) 12 | then if n mod i <> 0 then false else continue n i 13 | else start n (Number_theory.Int.next_probable_prime i) 14 | and continue n i = if n mod i = 0 then continue (n / i) i else start n (i + 1) in 15 | start n 2 16 | ;; 17 | 18 | let pseudo_fortunate n = 19 | let rec loop i = if Number_theory.Int.is_prime (n + i) then i else loop (i + 1) in 20 | loop 2 21 | ;; 22 | 23 | let main () = 24 | Number_theory.Int.natural_numbers ~init:2 () 25 | |> Sequence.take_while ~f:(fun x -> x < 1_000_000_000) 26 | |> Sequence.filter ~f:is_admissible 27 | |> Sequence.map ~f:pseudo_fortunate 28 | |> Sequence.fold ~init:(Set.empty (module Int)) ~f:Set.add 29 | |> Set.sum (module Int) ~f:Fn.id 30 | |> printf "%d\n" 31 | ;; 32 | 33 | (* 2209 34 | 2.3m *) 35 | 36 | include (val Solution.make ~problem:(Number 293) ~main) 37 | -------------------------------------------------------------------------------- /sol/sol_293.mli: -------------------------------------------------------------------------------- 1 | open! Core 2 | open! Import 3 | include Solution.S 4 | -------------------------------------------------------------------------------- /sol/sol_315.ml: -------------------------------------------------------------------------------- 1 | open! Core 2 | open! Import 3 | 4 | let digit_segments = 5 | (* segments are numbered: 6 | top, top left, top right, middle, bottom left, bottom right, bottom *) 7 | [| [ 0; 1; 2; 4; 5; 6 ] 8 | ; [ 2; 5 ] 9 | ; [ 0; 2; 3; 4; 6 ] 10 | ; [ 0; 2; 3; 5; 6 ] 11 | ; [ 1; 2; 3; 5 ] 12 | ; [ 0; 1; 3; 5; 6 ] 13 | ; [ 0; 1; 3; 4; 5; 6 ] 14 | ; [ 0; 1; 2; 5 ] 15 | ; [ 0; 1; 2; 3; 4; 5; 6 ] 16 | ; [ 0; 1; 2; 3; 5; 6 ] 17 | |] 18 | ;; 19 | 20 | let digit_segments = 21 | Array.map digit_segments ~f:(fun segments -> 22 | List.fold segments ~init:0 ~f:(fun ac x -> ac lor (1 lsl x))) 23 | ;; 24 | 25 | let distance s1 s2 = Int.popcount (s1 lxor s2) 26 | 27 | let transition_cost n1 n2 = 28 | let rec loop n1 n2 ac = 29 | match n1, n2 with 30 | | 0, 0 -> ac 31 | | n, 0 -> loop (n1 / 10) n2 (ac + distance 0 digit_segments.(n mod 10)) 32 | | 0, n -> loop n1 (n2 / 10) (ac + distance 0 digit_segments.(n mod 10)) 33 | | n1, n2 -> 34 | loop 35 | (n1 / 10) 36 | (n2 / 10) 37 | (ac + distance digit_segments.(n1 mod 10) digit_segments.(n2 mod 10)) 38 | in 39 | loop n1 n2 0 40 | ;; 41 | 42 | let digital_root n = 43 | let rec loop n ac = 44 | match n with 45 | | 0 -> ac 46 | | n -> loop (n / 10) (ac + (n mod 10)) 47 | in 48 | loop n 0 49 | ;; 50 | 51 | let digital_root_sequence n = 52 | Sequence.Generator.run 53 | (let open Sequence.Generator.Let_syntax in 54 | let rec loop s = 55 | let d = digital_root s in 56 | if d = s 57 | then Sequence.Generator.yield s 58 | else ( 59 | let%bind () = Sequence.Generator.yield s in 60 | loop d) 61 | in 62 | loop n) 63 | ;; 64 | 65 | let sam_cost sequence = 66 | sequence 67 | |> Sequence.map ~f:(transition_cost 0) 68 | |> Sequence.sum (module Int) ~f:(fun x -> 2 * x) 69 | ;; 70 | 71 | let max_cost sequence = 72 | let left = Sequence.append (Sequence.singleton 0) sequence in 73 | let right = Sequence.append sequence (Sequence.singleton 0) in 74 | Sequence.zip left right 75 | |> Sequence.sum (module Int) ~f:(fun (x, y) -> transition_cost x y) 76 | ;; 77 | 78 | let main () = 79 | let diff = ref 0 in 80 | let primes = Number_theory.prime_sieve 20_000_000 in 81 | for i = 10_000_000 to 20_000_000 do 82 | if primes.(i) 83 | then ( 84 | let seq = digital_root_sequence i in 85 | diff := !diff + sam_cost seq - max_cost seq) 86 | done; 87 | printf "%d\n" !diff 88 | ;; 89 | 90 | (* 13625242 91 | 1s *) 92 | 93 | include (val Solution.make ~problem:(Number 315) ~main) 94 | -------------------------------------------------------------------------------- /sol/sol_315.mli: -------------------------------------------------------------------------------- 1 | open! Core 2 | open! Import 3 | include Solution.S 4 | -------------------------------------------------------------------------------- /sol/sol_323.ml: -------------------------------------------------------------------------------- 1 | open! Core 2 | open! Import 3 | 4 | let word_size = 32 5 | 6 | (* probability of transitioning from [a] 1 bits to [b] 1 bits *) 7 | let p_transition a b = 8 | assert (a <= b); 9 | assert (a < 32); 10 | assert (b <= 32); 11 | let zero_bits = word_size - a in 12 | let diff = b - a in 13 | float (Number_theory.Int.binomial zero_bits diff) /. (2. ** float zero_bits) 14 | ;; 15 | 16 | let expectation_to_32 = 17 | Memo.recursive 18 | (module Int) 19 | (fun expectation_to_32 n -> 20 | if n = 32 21 | then 0.0 22 | else ( 23 | let e = ref 1. in 24 | (* - E[n] = 1 + P[n] E[n] + P[n + 1]E[n + 1] + ... + P[32]E[32] 25 | - E[n] = (1 + P[n + 1]E[n + 1] + ... + P[32]E[32]) / (1 - P[n]) *) 26 | for i = n + 1 to 32 do 27 | e := !e +. (p_transition n i *. expectation_to_32 i) 28 | done; 29 | !e /. (1. -. p_transition n n))) 30 | ;; 31 | 32 | let main () = printf "%.10f\n" @@ expectation_to_32 0 33 | 34 | (* 0.166ms *) 35 | let%expect_test "answer" = 36 | main (); 37 | [%expect {| 6.3551758451 |}] 38 | ;; 39 | 40 | include (val Solution.make ~problem:(Number 323) ~main) 41 | -------------------------------------------------------------------------------- /sol/sol_323.mli: -------------------------------------------------------------------------------- 1 | open! Core 2 | open! Import 3 | include Solution.S 4 | -------------------------------------------------------------------------------- /sol/sol_323_sim.ml: -------------------------------------------------------------------------------- 1 | open! Core 2 | open! Import 3 | 4 | let simulate_one bits ~random_state = 5 | let end_ = 1 lsl bits in 6 | let mask = end_ - 1 in 7 | let x = ref 0 in 8 | let count = ref 0 in 9 | while !x <> mask do 10 | incr count; 11 | let y = Random.State.int random_state end_ in 12 | x := !x lor y 13 | done; 14 | !count 15 | ;; 16 | 17 | let simulate bits times ~random_state = 18 | let sample_counts = Array.create 0 ~len:40 in 19 | for _ = 1 to times do 20 | let sample = simulate_one bits ~random_state in 21 | sample_counts.(sample) <- sample_counts.(sample) + 1 22 | done; 23 | sample_counts 24 | ;; 25 | 26 | let expectation sample_counts total = 27 | let e = ref 0. in 28 | Array.iteri sample_counts ~f:(fun i x -> e := !e +. (float i *. float x)); 29 | !e /. float total 30 | ;; 31 | 32 | let main () = 33 | let times = 100_000_000 in 34 | let bits = 32 in 35 | let random_state = Random.State.make_self_init () in 36 | let samples = simulate bits times ~random_state in 37 | if debug then Debug.eprint_s [%sexp (samples : int array)]; 38 | printf "%.10f\n" @@ expectation samples times 39 | ;; 40 | 41 | (* XXX doesn't quite work *) 42 | (* 6.3551679500 43 | 39s *) 44 | 45 | include 46 | (val Solution.make 47 | ~problem: 48 | (Tagged { number = 323; tag = "sim"; description = "Monte Carlo method" }) 49 | ~main) 50 | -------------------------------------------------------------------------------- /sol/sol_323_sim.mli: -------------------------------------------------------------------------------- 1 | open! Core 2 | open! Import 3 | include Solution.S 4 | -------------------------------------------------------------------------------- /sol/sol_329.ml: -------------------------------------------------------------------------------- 1 | open! Core 2 | open! Import 3 | 4 | module Is_prime = struct 5 | type t = 6 | [ `Prime 7 | | `Not_prime 8 | ] 9 | [@@deriving compare, equal, quickcheck, sexp_of] 10 | 11 | let of_char_exn = function 12 | | 'P' -> `Prime 13 | | 'N' -> `Not_prime 14 | | c -> invalid_argf "Is_prime.of_char_exn: '%c'" c () 15 | ;; 16 | 17 | include (val Comparator.make ~compare ~sexp_of_t) 18 | end 19 | 20 | module Dist = Distribution.Bignum 21 | 22 | let is_prime = lazy (Number_theory.prime_sieve 500) 23 | 24 | let step (pos : (int, _) Dist.t) : (int * Is_prime.t, _) Dist.t = 25 | Dist.bind' 26 | (module struct 27 | type t = int * Is_prime.t [@@deriving compare, quickcheck, sexp_of] 28 | 29 | type comparator_witness = 30 | (Int.comparator_witness, Is_prime.comparator_witness) Tuple.T2.comparator_witness 31 | 32 | let comparator = Tuple.T2.comparator Int.comparator Is_prime.comparator 33 | end) 34 | pos 35 | ~f:(fun x -> 36 | let sound = 37 | if (force is_prime).(x) 38 | then Dist.uniform' (module Is_prime) [ `Prime; `Prime; `Not_prime ] 39 | else Dist.uniform' (module Is_prime) [ `Prime; `Not_prime; `Not_prime ] 40 | in 41 | let pos = 42 | match x with 43 | | 1 -> Dist.singleton (module Int) 2 44 | | 500 -> Dist.singleton (module Int) 499 45 | | x -> Dist.uniform' (module Int) [ x - 1; x + 1 ] 46 | in 47 | Dist.cartesian_product pos sound) 48 | ;; 49 | 50 | let main () = 51 | let init = ref (Dist.uniform' (module Int) (List.range 1 500 ~stop:`inclusive)) in 52 | let product = ref Bignum.one in 53 | let expected = 54 | "PPPPNNPPPNPPNPN" |> String.to_list |> List.map ~f:Is_prime.of_char_exn 55 | in 56 | List.iter expected ~f:(fun expected -> 57 | assert (Map.length (Dist.to_map !init) = 500); 58 | let next = 59 | step !init 60 | |> Dist.to_alist 61 | |> List.filter_map ~f:(fun ((pos, is_prime), p) -> 62 | if Is_prime.equal is_prime expected then Some (pos, p) else None) 63 | |> Dist.of_alist_exn (module Int) 64 | in 65 | let total = Dist.total next in 66 | (product := Bignum.(!product * total)); 67 | let next = Dist.normalize next in 68 | init := next); 69 | printf 70 | !"%{Bigint}/%{Bigint}\n" 71 | (Bignum.num_as_bigint !product) 72 | (Bignum.den_as_bigint !product) 73 | ;; 74 | 75 | (* 80ms *) 76 | let%expect_test "answer" = 77 | main (); 78 | [%expect {| 199740353/29386561536000 |}] 79 | ;; 80 | 81 | include (val Solution.make ~problem:(Number 329) ~main) 82 | -------------------------------------------------------------------------------- /sol/sol_329.mli: -------------------------------------------------------------------------------- 1 | open! Core 2 | open! Import 3 | include Solution.S 4 | -------------------------------------------------------------------------------- /sol/sol_345.ml: -------------------------------------------------------------------------------- 1 | open! Core 2 | open! Import 3 | 4 | let grid () = Problem_345.data |> Parse.space_separated_grid ~conv:Int.of_string 5 | 6 | let max_sum grid = 7 | let module Cache_key = struct 8 | type t = 9 | { upto_row : int 10 | ; colset : Bitset.t 11 | } 12 | [@@deriving compare, hash, sexp_of] 13 | end 14 | in 15 | let loop = 16 | Memo.recursive 17 | (module Cache_key) 18 | (fun loop { upto_row; colset } -> 19 | if upto_row < 0 20 | then 0 21 | else 22 | Bitset.fold colset ~init:0 ~f:(fun best col -> 23 | let cand = 24 | loop { upto_row = upto_row - 1; colset = Bitset.remove colset col } 25 | in 26 | Int.max best (cand + grid.(upto_row).(col)))) 27 | in 28 | loop 29 | { upto_row = Array.length grid - 1 30 | ; colset = Bitset.of_list (List.init (Array.length grid) ~f:Fn.id) 31 | } 32 | ;; 33 | 34 | let main () = max_sum (grid ()) |> printf "%d\n" 35 | 36 | (* 60ms *) 37 | let%expect_test "answer" = 38 | main (); 39 | [%expect {| 13938 |}] 40 | ;; 41 | 42 | include (val Solution.make ~problem:(Number 345) ~main) 43 | -------------------------------------------------------------------------------- /sol/sol_345.mli: -------------------------------------------------------------------------------- 1 | open! Core 2 | open! Import 3 | include Solution.S 4 | -------------------------------------------------------------------------------- /sol/sol_348.ml: -------------------------------------------------------------------------------- 1 | open! Core 2 | open! Import 3 | 4 | let palindromes_n_digits n = 5 | if n mod 2 = 0 6 | then ( 7 | let lb = Int.pow 10 ((n / 2) - 1) in 8 | let ub = Int.pow 10 (n / 2) in 9 | Sequence.range lb ub 10 | |> Sequence.map ~f:(fun n -> 11 | let d = Number_theory.Int.As_base10.(to_list n) in 12 | d @ List.rev d |> Number_theory.Int.As_base10.(of_list))) 13 | else ( 14 | let lb = Int.pow 10 (n / 2) in 15 | let ub = Int.pow 10 ((n / 2) + 1) in 16 | Sequence.range lb ub 17 | |> Sequence.map ~f:(fun n -> 18 | let d = Number_theory.Int.As_base10.(to_list n) in 19 | d @ List.tl_exn (List.rev d) |> Number_theory.Int.As_base10.(of_list))) 20 | ;; 21 | 22 | let palindromes = 23 | Number_theory.Int.natural_numbers () ~init:1 |> Sequence.bind ~f:palindromes_n_digits 24 | ;; 25 | 26 | (* greater than 1 *) 27 | let _squares = 28 | Number_theory.Int.natural_numbers () ~init:2 |> Sequence.map ~f:(fun x -> x * x) 29 | ;; 30 | 31 | (* ditto *) 32 | let cubes = 33 | Number_theory.Int.natural_numbers () ~init:2 |> Sequence.map ~f:(fun x -> x * x * x) 34 | ;; 35 | 36 | let main () = 37 | palindromes 38 | |> Sequence.filter ~f:(fun p -> 39 | let count = 40 | cubes 41 | |> Sequence.take_while ~f:(fun x -> x < p) 42 | |> Sequence.count ~f:(fun c -> Number_theory.Int.is_perfect_square (p - c)) 43 | in 44 | count = 4) 45 | |> Fn.flip Sequence.take 5 46 | |> Sequence.sum (module Int) ~f:Fn.id 47 | |> printf "%d\n" 48 | ;; 49 | 50 | (* 1004195061 51 | 1.5s *) 52 | 53 | include (val Solution.make ~problem:(Number 348) ~main) 54 | -------------------------------------------------------------------------------- /sol/sol_348.mli: -------------------------------------------------------------------------------- 1 | open! Core 2 | open! Import 3 | include Solution.S 4 | -------------------------------------------------------------------------------- /sol/sol_359.mli: -------------------------------------------------------------------------------- 1 | open! Core 2 | open! Import 3 | include Solution.S 4 | -------------------------------------------------------------------------------- /sol/sol_401.mli: -------------------------------------------------------------------------------- 1 | open! Core 2 | open! Import 3 | include Solution.S 4 | -------------------------------------------------------------------------------- /sol/sol_407.ml: -------------------------------------------------------------------------------- 1 | open! Core 2 | open! Import 3 | 4 | let limit = 10_000_000 5 | 6 | (* Modified solution inspired by umu: 7 | https://projecteuler.net/thread=407;post=92348. *) 8 | 9 | (* a^2 = a (mod n) means a^2-a = a(a - 1) is divisible by n. Since a and (a - 10 | 1) are coprime, and a < n, n = xy for 2 <= x, y and x|a, y|(a - 1). *) 11 | 12 | let main () = 13 | let ms = Option_array.create ~len:(limit + 1) in 14 | let somes = ref 0 in 15 | let all = ref 0 in 16 | let rec loop m m_divisors m_pred_divisors = 17 | if m < 2 18 | then () 19 | else ( 20 | if debug && m mod 10_000 = 0 21 | then Debug.eprintf !"%d %{Percent}" m (Percent.of_mult (float !somes /. float !all)); 22 | List.iter m_divisors ~f:(fun x -> 23 | List.iter m_pred_divisors ~f:(fun y -> 24 | let n = x * y in 25 | if m < n && n <= limit && not (Option_array.unsafe_is_some ms n) 26 | then ( 27 | Option_array.unsafe_set_some ms n m; 28 | incr somes))); 29 | incr all; 30 | loop (m - 1) m_pred_divisors (Number_theory.Int.divisors (m - 2))) 31 | in 32 | loop limit (Number_theory.Int.divisors limit) (Number_theory.Int.divisors (limit - 1)); 33 | let sum = ref 0 in 34 | (* M(1) = 0 *) 35 | for i = 2 to limit do 36 | if Option_array.unsafe_is_some ms i 37 | then sum := !sum + Option_array.unsafe_get_some_exn ms i 38 | else sum := !sum + 1 (* 1^2 = 1 (mod n) trivially for any n *) 39 | done; 40 | printf "%d\n" !sum 41 | ;; 42 | 43 | (* 39782849136421 44 | 42.3606s *) 45 | 46 | include (val Solution.make ~problem:(Number 407) ~main) 47 | -------------------------------------------------------------------------------- /sol/sol_407.mli: -------------------------------------------------------------------------------- 1 | open! Core 2 | open! Import 3 | include Solution.S 4 | -------------------------------------------------------------------------------- /sol/sol_425.mli: -------------------------------------------------------------------------------- 1 | open! Core 2 | open! Import 3 | include Solution.S 4 | -------------------------------------------------------------------------------- /sol/sol_429.ml: -------------------------------------------------------------------------------- 1 | open! Core 2 | open! Import 3 | 4 | (* The unitary divisors of [n] are [p_1^a_1 * ... * p_k^a_k] where [p_1, ..., 5 | p_k] are any subset of the prime factors of [n]. 6 | 7 | Induct on [k]. Let [S_k(n)] denote the sum of the squares of unitary 8 | divisors corresponding to primes [1..k]. 9 | 10 | For [k = 0], [S_0(n) = 1^2 = 1]. 11 | 12 | For [k + 1], we have: 13 | 14 | {v 15 | S_{k + 1}(n) = (p_1^a_1 * ... * p_k^a_k)^2 + ... 16 | = S_k(n) + S_k(n) * (p_{k+1}^{a_{k+1}})^2 17 | v} 18 | 19 | For each term above, a new copy has p_{k+1}^{a_{k+1}} since we can factor 20 | out the common new prime. 21 | 22 | Thus, once we have the prime factorization of [n], we can use an easy 23 | linear recurrence to calculate [S(n)]. *) 24 | 25 | let modulus = 1_000_000_009 26 | 27 | let main () = 28 | let factors = Number_theory.factorial_prime_factor 100_000_000 in 29 | let s = ref 1 in 30 | List.iter factors ~f:(fun (p, a) -> 31 | s := !s * (1 + Number_theory.Int.powmod p (2 * a) ~modulus) mod modulus); 32 | printf "%d\n" !s 33 | ;; 34 | 35 | (* 98792821 36 | 9.3s *) 37 | 38 | include (val Solution.make ~problem:(Number 429) ~main) 39 | -------------------------------------------------------------------------------- /sol/sol_429.mli: -------------------------------------------------------------------------------- 1 | open! Core 2 | open! Import 3 | include Solution.S 4 | -------------------------------------------------------------------------------- /sol/sol_500.ml: -------------------------------------------------------------------------------- 1 | open! Core 2 | open! Import 3 | 4 | let modulo = 500_500_507 5 | 6 | (* We know [PrimePi[7_400_000] >= 500_500] so this includes all first 7 | 500,000 primes, which is the upper bound on the primes used. *) 8 | let upper_bound = 7_400_000 9 | 10 | let main () = 11 | let queue = 12 | Number_theory.prime_sieve upper_bound 13 | |> Array.filter_mapi ~f:(fun i p -> Option.some_if p i) 14 | |> Pairing_heap.of_array ~cmp:Int.compare 15 | in 16 | let number = ref 1 in 17 | for _ = 1 to 500_500 do 18 | let factor = Pairing_heap.pop_exn queue in 19 | number := !number * factor mod modulo; 20 | let factor = factor * factor in 21 | if factor < upper_bound then Pairing_heap.add queue factor 22 | done; 23 | !number |> printf "%d\n" 24 | ;; 25 | 26 | (* 35407281 27 | 500ms *) 28 | 29 | include (val Solution.make ~problem:(Number 500) ~main) 30 | -------------------------------------------------------------------------------- /sol/sol_500.mli: -------------------------------------------------------------------------------- 1 | open! Core 2 | open! Import 3 | include Solution.S 4 | -------------------------------------------------------------------------------- /sol/sol_504.ml: -------------------------------------------------------------------------------- 1 | open! Core 2 | open! Import 3 | 4 | let limit = 100 5 | 6 | (* TODO: Move this into Geometry. *) 7 | (* https://en.wikipedia.org/wiki/Pick%27s_theorem *) 8 | 9 | let lattice_points x y = 10 | let boundary_points = x + y + Number_theory.Int.gcd x y in 11 | (((x * y) - boundary_points) / 2) + 1 12 | ;; 13 | 14 | let lattice_points = 15 | let cache = 16 | lazy 17 | (Array.init (limit + 1) ~f:(fun x -> 18 | Array.init (limit + 1) ~f:(fun y -> lattice_points x y))) 19 | in 20 | fun x y -> (force cache).(x).(y) 21 | ;; 22 | 23 | let main () = 24 | let count = ref 0 in 25 | for a = 1 to limit do 26 | for b = 1 to limit do 27 | for c = 1 to limit do 28 | for d = 1 to limit do 29 | let points = 30 | (* (n - 1) points strictly inside on each axis, plus origin *) 31 | a 32 | + b 33 | + c 34 | + d 35 | - 3 36 | + lattice_points a b 37 | + lattice_points b c 38 | + lattice_points c d 39 | + lattice_points d a 40 | in 41 | if Number_theory.Int.is_perfect_square points then incr count 42 | done 43 | done 44 | done 45 | done; 46 | printf "%d\n" !count 47 | ;; 48 | 49 | (* 694687 50 | 15s *) 51 | 52 | include (val Solution.make ~problem:(Number 504) ~main) 53 | -------------------------------------------------------------------------------- /sol/sol_504.mli: -------------------------------------------------------------------------------- 1 | open! Core 2 | open! Import 3 | include Solution.S 4 | -------------------------------------------------------------------------------- /sol/sol_531.ml: -------------------------------------------------------------------------------- 1 | open! Core 2 | open! Import 3 | 4 | let g a n b m = 5 | let x = Number_theory.Int.gcd n m in 6 | if a % x <> b % x 7 | then 0 8 | else ( 9 | let y, modulus = 10 | Number_theory.Int.chinese_remainder_theorem [ a / x, n / x; b / x, m / x ] 11 | in 12 | (y % modulus * x) + (a % x)) 13 | ;; 14 | 15 | let%expect_test "g(2, 4, 4, 6)" = 16 | print_s [%sexp (g 2 4 4 6 : int)]; 17 | [%expect {| 10 |}] 18 | ;; 19 | 20 | let%expect_test "g(3, 4, 4, 6)" = 21 | print_s [%sexp (g 3 4 4 6 : int)]; 22 | [%expect {| 0 |}] 23 | ;; 24 | 25 | let%test_unit _ = 26 | let limit = 25 in 27 | let max_result = limit * limit in 28 | let gen = 29 | let open Quickcheck.Let_syntax in 30 | let%bind () = return () 31 | and n = Int.gen_incl 1 limit 32 | and m = Int.gen_incl 1 limit in 33 | let%map () = return () 34 | and a = Int.gen_incl 0 (n - 1) 35 | and b = Int.gen_incl 0 (m - 1) in 36 | a, n, b, m 37 | in 38 | Quickcheck.test gen ~sexp_of:[%sexp_of: int * int * int * int] ~f:(fun (a, n, b, m) -> 39 | let lowest = 40 | Sequence.range 0 max_result 41 | |> Sequence.find ~f:(fun x -> x % n = a && x % m = b) 42 | |> Option.value ~default:0 43 | in 44 | [%test_result: int] (g a n b m) ~expect:lowest) 45 | ;; 46 | 47 | let phi = Memo.simple (module Int) Number_theory.Int.totient 48 | let f n m = g (phi n) n (phi m) m 49 | 50 | let main () = 51 | let sum = ref 0 in 52 | for n = 1_000_000 to 1_004_998 do 53 | for m = n + 1 to 1_004_999 do 54 | sum := !sum + f n m 55 | done 56 | done; 57 | printf "%d\n" !sum 58 | ;; 59 | 60 | (* 4515432351156203105 61 | 10s *) 62 | 63 | include (val Solution.make ~problem:(Number 531) ~main) 64 | -------------------------------------------------------------------------------- /sol/sol_531.mli: -------------------------------------------------------------------------------- 1 | open! Core 2 | open! Import 3 | include Solution.S 4 | -------------------------------------------------------------------------------- /sol/sol_549.ml: -------------------------------------------------------------------------------- 1 | open! Core 2 | open! Import 3 | 4 | let limit = 5 | match Sys.getenv "LIMIT" with 6 | | Some x -> Int.of_string x 7 | | None -> 100_000_000 8 | ;; 9 | 10 | let divide n p = 11 | let rec loop n ac = if n mod p = 0 then loop (n / p) (ac + 1) else ac in 12 | loop n 0 13 | ;; 14 | 15 | (* Sieve inspired by Nore's solution at 16 | https://projecteuler.net/thread=549#235492. *) 17 | let sieve n = 18 | let m = Array.create 0 ~len:(n + 1) in 19 | for p = 2 to n do 20 | if m.(p) = 0 (* prime *) 21 | then ( 22 | (* loop invariant: [smallest] is the smallest multiple of [p] such that 23 | [power | smallest!]. [k] is incremented each loop and serves to 24 | adjust [smallest] when needed. *) 25 | let rec loop_powers power k smallest = 26 | if power <= n 27 | then ( 28 | if debug then Debug.eprintf "power %d" power; 29 | let k, smallest = 30 | let rec ensure_enough_p k smallest = 31 | if k <= 0 32 | then k, smallest 33 | else ( 34 | let smallest = smallest + p in 35 | ensure_enough_p (k - divide smallest p) smallest) 36 | in 37 | ensure_enough_p k smallest 38 | in 39 | let rec loop j = 40 | if j <= n 41 | then ( 42 | if smallest > m.(j) then m.(j) <- smallest; 43 | loop (j + power)) 44 | in 45 | loop power; 46 | loop_powers (power * p) (k + 1) smallest) 47 | in 48 | loop_powers p 1 0) 49 | done; 50 | Array.sum (module Int) m ~f:Fn.id 51 | ;; 52 | 53 | let main () = sieve limit |> printf "%d\n" 54 | 55 | (* 476001479068717 56 | 9.4s *) 57 | 58 | include (val Solution.make ~problem:(Number 549) ~main) 59 | -------------------------------------------------------------------------------- /sol/sol_549.mli: -------------------------------------------------------------------------------- 1 | open! Core 2 | open! Import 3 | include Solution.S 4 | -------------------------------------------------------------------------------- /sol/sol_587.ml: -------------------------------------------------------------------------------- 1 | open! Core 2 | open! Import 3 | 4 | (* fix the circle radius to be 1 *) 5 | let l_section_area = Float.(1. - (pi / 4.)) 6 | let area_threshold = Percent.apply (Percent.of_bp_int 10) l_section_area 7 | 8 | (* see diagram for notation *) 9 | let concave_triangle_area n = 10 | let open Float.O in 11 | let theta = Float.atan2 1. (float n) in 12 | let tan_theta = 1. / float n in 13 | let x_d = 14 | let a = 1. + (tan_theta ** 2.) in 15 | let b = -2. * (1. + tan_theta) in 16 | let c = 1. in 17 | match Algebra.quadratic_formula a b c with 18 | | `None | `One _ -> assert false 19 | | `Two (x_d, _) -> x_d 20 | in 21 | let phi = Float.asin (1. - x_d) in 22 | let a_ABC = 0.5 * Float.sin theta in 23 | let a_ODB = phi / 2. in 24 | let a_ODC = 0.5 * (1. - Float.sin theta) * Float.sin phi in 25 | a_ABC - (a_ODB - a_ODC) 26 | ;; 27 | 28 | let main () = 29 | let open Float.O in 30 | Number_theory.Int.natural_numbers () ~init:1 31 | |> Sequence.find_exn ~f:(fun n -> concave_triangle_area n < area_threshold) 32 | |> printf "%d\n" 33 | ;; 34 | 35 | (* 764us *) 36 | let%expect_test "answer" = 37 | main (); 38 | [%expect {| 2240 |}] 39 | ;; 40 | 41 | include (val Solution.make ~problem:(Number 587) ~main) 42 | -------------------------------------------------------------------------------- /sol/sol_587.mli: -------------------------------------------------------------------------------- 1 | open! Core 2 | open! Import 3 | include Solution.S 4 | -------------------------------------------------------------------------------- /sol/sol_587_int.ml: -------------------------------------------------------------------------------- 1 | open! Core 2 | open! Import 3 | 4 | (* fix the circle radius to be 1 *) 5 | let l_section_area = Float.(1. - (pi / 4.)) 6 | let area_threshold = Percent.apply (Percent.of_bp_int 10) l_section_area 7 | 8 | let height n x = 9 | let open Float.O in 10 | let y_line = x / float n in 11 | (* (x - 1)^2 + (y - 1)^2 = 1 *) 12 | let y_circle = 1. - sqrt (1. - ((x - 1.) ** 2.)) in 13 | Float.min y_line y_circle 14 | ;; 15 | 16 | let concave_triangle_area n = 17 | Numerics.Float.integrate () ~f:(height n) ~lo:0. ~hi:1. ~intervals:1000 18 | ;; 19 | 20 | let main () = 21 | Number_theory.Int.natural_numbers () ~init:1 22 | |> Sequence.find_exn ~f:(fun n -> Float.(concave_triangle_area n < area_threshold)) 23 | |> printf "%d\n" 24 | ;; 25 | 26 | (* 2240 27 | 354ms *) 28 | 29 | include 30 | (val Solution.make 31 | ~problem:(Tagged { number = 587; tag = "int"; description = "integration" }) 32 | ~main) 33 | -------------------------------------------------------------------------------- /sol/sol_587_int.mli: -------------------------------------------------------------------------------- 1 | open! Core 2 | open! Import 3 | include Solution.S 4 | -------------------------------------------------------------------------------- /sol/sol_601.ml: -------------------------------------------------------------------------------- 1 | open! Core 2 | open! Import 3 | module NT = Number_theory.Make (Int64) 4 | 5 | let lcm_all n = 6 | Sequence.range 2 n ~stop:`inclusive 7 | |> Sequence.map ~f:Int64.of_int 8 | |> Sequence.fold ~init:1L ~f:NT.lcm 9 | ;; 10 | 11 | let num_multiples_less_than n k = Int64.O.((n - 2L) / k) 12 | 13 | (* 14 | P(s, N) = COUNT(1 7 | let max_y = 30. - (x * 0.75) in 8 | Numerics.Float.integrate () ~lo:0. ~hi:max_y ~intervals:32_000 ~f:(fun y -> 9 | let to_southeast = Float.atan2 (-y) (40. - x) in 10 | let to_northwest = Float.atan2 (30. - y) (-x) in 11 | to_northwest - to_southeast)) 12 | / (Float.pi * 30. * 40.) 13 | |> printf "%.10f\n" 14 | ;; 15 | 16 | (* 0.3916721504 17 | 6.56162m *) 18 | 19 | include (val Solution.make ~problem:(Number 613) ~main) 20 | -------------------------------------------------------------------------------- /sol/sol_613.mli: -------------------------------------------------------------------------------- 1 | open! Core 2 | open! Import 3 | include Solution.S 4 | -------------------------------------------------------------------------------- /sol/sol_622.ml: -------------------------------------------------------------------------------- 1 | open! Core 2 | open! Import 3 | 4 | let riffle_shuffle cards = 5 | let half = Array.length cards / 2 in 6 | let left = Array.sub cards ~pos:0 ~len:half in 7 | let right = Array.sub cards ~pos:half ~len:half in 8 | for i = 0 to half - 1 do 9 | cards.(2 * i) <- left.(i); 10 | cards.((2 * i) + 1) <- right.(i) 11 | done 12 | ;; 13 | 14 | let num_shuffles_naive n = 15 | let are_in_order = Array.for_alli ~f:Int.equal in 16 | let cards = Array.init n ~f:Fn.id in 17 | let rec loop count = 18 | riffle_shuffle cards; 19 | if are_in_order cards then count else loop (count + 1) 20 | in 21 | loop 1 22 | ;; 23 | 24 | let%expect_test _ = 25 | List.range 2 256 ~stop:`inclusive 26 | |> List.filter ~f:(fun x -> x % 2 = 0 && num_shuffles_naive x = 8) 27 | |> [%sexp_of: int list] 28 | |> print_s; 29 | [%expect {| 30 | (18 52 86 256) |}] 31 | ;; 32 | 33 | (* See https://en.wikipedia.org/wiki/Out_shuffle and 34 | http://mathworld.wolfram.com/MultiplicativeOrder.html *) 35 | 36 | let moduli_for_which_multiplicative_order_of_2_is ~target = 37 | let a = (1 lsl target) - 1 in 38 | let divisors = Number_theory.Int.divisors a in 39 | List.filter divisors ~f:(fun x -> 40 | let rec loop a' = 41 | if a' = 0 then true else if a' % x = 0 then false else loop (a' / 2) 42 | in 43 | loop (a / 2)) 44 | ;; 45 | 46 | let%expect_test _ = 47 | moduli_for_which_multiplicative_order_of_2_is ~target:8 48 | |> [%sexp_of: int list] 49 | |> print_s; 50 | [%expect {| 51 | (17 51 85 255) |}] 52 | ;; 53 | 54 | let target = 60 55 | 56 | let main () = 57 | moduli_for_which_multiplicative_order_of_2_is ~target 58 | |> List.sum (module Int) ~f:succ 59 | |> printf "%d\n" 60 | ;; 61 | 62 | (* 6.762ms *) 63 | let%expect_test "answer" = 64 | main (); 65 | [%expect {| 3010983666182123972 |}] 66 | ;; 67 | 68 | include (val Solution.make ~problem:(Number 622) ~main) 69 | -------------------------------------------------------------------------------- /sol/sol_622.mli: -------------------------------------------------------------------------------- 1 | open! Core 2 | open! Import 3 | include Solution.S 4 | -------------------------------------------------------------------------------- /sol/sol_650.mli: -------------------------------------------------------------------------------- 1 | open! Core 2 | open! Import 3 | include Solution.S 4 | -------------------------------------------------------------------------------- /sol/sol_662.mli: -------------------------------------------------------------------------------- 1 | open! Core 2 | open! Import 3 | include Solution.S 4 | -------------------------------------------------------------------------------- /sol/sol_666.mli: -------------------------------------------------------------------------------- 1 | open! Core 2 | open! Import 3 | include Solution.S 4 | -------------------------------------------------------------------------------- /sol/sol_692.mli: -------------------------------------------------------------------------------- 1 | open! Core 2 | open! Import 3 | include Solution.S 4 | -------------------------------------------------------------------------------- /sol/sol_700.ml: -------------------------------------------------------------------------------- 1 | open! Core 2 | open! Import 3 | 4 | let birthday = 1504170715041707 5 | let modulus = 4503599627370517 6 | 7 | let main () = 8 | let seq = Queue.of_list [ birthday ] in 9 | let smallest_so_far = ref birthday in 10 | let should_guess = ref true in 11 | while !should_guess do 12 | let expected_guesses = 0.5 *. float modulus /. float !smallest_so_far in 13 | if Float.( < ) (float !smallest_so_far) expected_guesses 14 | then (* switch strategy *) 15 | should_guess := false 16 | else ( 17 | (* advance [smallest_so_far] *) 18 | smallest_so_far := (!smallest_so_far + birthday) % modulus; 19 | if !smallest_so_far < Queue.last_exn seq 20 | then ( 21 | if debug then Debug.eprintf "%d" !smallest_so_far; 22 | Queue.enqueue seq !smallest_so_far)) 23 | done; 24 | let modinv = Number_theory.Int.invmod birthday ~modulus in 25 | let index_of = 26 | Array.init !smallest_so_far ~f:(fun k -> 27 | if k > 0 then modinv * k % modulus else modulus) 28 | in 29 | let remaining_candidates = Array.init !smallest_so_far ~f:Fn.id in 30 | Array.sort 31 | remaining_candidates 32 | ~compare:(Comparable.lift Int.compare ~f:(fun k -> Array.unsafe_get index_of k)); 33 | Array.iter remaining_candidates ~f:(fun smallest_so_far -> 34 | if smallest_so_far < Queue.last_exn seq 35 | then ( 36 | if debug then Debug.eprintf "%d" smallest_so_far; 37 | Queue.enqueue seq smallest_so_far)); 38 | if debug then Debug.eprint_s [%sexp (seq : int Queue.t)]; 39 | printf "%d\n" (Queue.sum (module Int) seq ~f:Fn.id) 40 | ;; 41 | 42 | (* 24.629867s 43 | 1517926517777556 *) 44 | 45 | include (val Solution.make ~problem:(Number 700) ~main) 46 | -------------------------------------------------------------------------------- /sol/sol_700.mli: -------------------------------------------------------------------------------- 1 | open! Core 2 | open! Import 3 | include Solution.S 4 | -------------------------------------------------------------------------------- /sol/sol_719.ml: -------------------------------------------------------------------------------- 1 | open! Core 2 | open! Import 3 | 4 | (* Inspired by NabiNaga's solution at 5 | https://projecteuler.net/thread=719;page=3#365650 *) 6 | let rec can_sum ~digits_of:n ~target = 7 | if n = target 8 | then true 9 | else if n < target 10 | then false 11 | else ( 12 | let rec loop pow10 = 13 | if pow10 >= n 14 | then false 15 | else if can_sum ~digits_of:(n / pow10) ~target:(target - (n % pow10)) 16 | then true 17 | else loop (pow10 * 10) 18 | in 19 | loop 10) 20 | ;; 21 | 22 | (* As noted by ecnerwala, https://projecteuler.net/thread=719#359366, 23 | 24 | Splitting numbers preserves the digit sum and thus the value mod 9. 25 | Therefore, we only need to check the numbers whose squares are congruent to 26 | them mod 9, i.e., sqrt in {0, 1} mod 9. *) 27 | 28 | let is_s_number ~sqrt ~n = sqrt % 9 <= 1 && can_sum ~digits_of:n ~target:sqrt 29 | 30 | let t ~max_sqrt = 31 | let squares = 32 | Sequence.range 2 max_sqrt ~stop:`inclusive |> Sequence.map ~f:(fun x -> x, x * x) 33 | in 34 | Sequence.filter squares ~f:(fun (sqrt, n) -> is_s_number ~sqrt ~n) 35 | ;; 36 | 37 | let%expect_test "T(10^4)" = 38 | let s_numbers = t ~max_sqrt:100 in 39 | Sequence.sum (module Int) s_numbers ~f:(fun (_, n) -> n) |> [%sexp_of: int] |> print_s; 40 | [%expect {| 41333 |}] 41 | ;; 42 | 43 | (* 128088830547982 44 | 981.608194ms *) 45 | let main () = 46 | let max_sqrt = 1_000_000 in 47 | let s_numbers = t ~max_sqrt in 48 | s_numbers 49 | |> Sequence.sum (module Int) ~f:(fun (_, n) -> n) 50 | |> [%sexp_of: int] 51 | |> print_s 52 | ;; 53 | 54 | include (val Solution.make ~problem:(Number 719) ~main) 55 | -------------------------------------------------------------------------------- /sol/sol_719.mli: -------------------------------------------------------------------------------- 1 | open! Core 2 | open! Import 3 | include Solution.S 4 | -------------------------------------------------------------------------------- /sol/sol_725.mli: -------------------------------------------------------------------------------- 1 | open! Core 2 | open! Import 3 | include Solution.S 4 | -------------------------------------------------------------------------------- /sol/sol_727.mli: -------------------------------------------------------------------------------- 1 | open! Core 2 | open! Import 3 | include Solution.S 4 | -------------------------------------------------------------------------------- /sol/sol_800.ml: -------------------------------------------------------------------------------- 1 | open! Core 2 | open! Import 3 | 4 | (* Compare [p^q q^p] and [limit^limit] *) 5 | let compare_powers ~p ~q ~limit = 6 | Float.compare ((p *. Float.log q) +. (q *. Float.log p)) (limit *. Float.log limit) 7 | ;; 8 | 9 | (* Return the number of distinct integers [n = p^q q^p] where [p != q] are prime, and 10 | where [n <= limit^limit]. 11 | 12 | Assume WLOG that [p < q]. 13 | 14 | The largest [q] we need to search will be the one for which [p = 2] causes [n] to 15 | exceed [limit^limit]. 16 | 17 | 2^q q^2 <= limit^limit 18 | 19 | q log(2) + 2 log(q) <= limit log(limit) 20 | 21 | q log(2) <= limit log(limit) 22 | 23 | q <= limit log(limit) / log(2) *) 24 | let count_hybrids limit = 25 | let limit = float limit in 26 | let max_q = Float.iround_down_exn (limit *. Float.log limit /. Float.log 2.) in 27 | if debug then Debug.eprint_s [%message (max_q : int)]; 28 | let primes = 29 | debug_timing [%here] "prime sieve" (fun () -> Number_theory.prime_sieve max_q) 30 | |> Array.filter_mapi ~f:(fun i b -> Option.some_if b (float i)) 31 | in 32 | (* Keep track of the maximum value of [p] for each value of [q]. The max [p] for [q'] 33 | is probably close to the max [p] for [q]. *) 34 | let p_index = ref (-1) in 35 | Array.fold primes ~init:0 ~f:(fun count q -> 36 | (* If [p] is bounded by [q] and not [limit], increase it. *) 37 | if Float.equal primes.(!p_index + 2) q then incr p_index; 38 | (* Bound [p] by [limit] *) 39 | while !p_index >= 0 && compare_powers ~p:primes.(!p_index) ~q ~limit > 0 do 40 | decr p_index 41 | done; 42 | count + (!p_index + 1)) 43 | ;; 44 | 45 | let main () = 46 | let ans = count_hybrids 800_800 in 47 | print_s [%sexp (ans : int)] 48 | ;; 49 | 50 | (* 348.114382ms *) 51 | let%expect_test "answer" = 52 | main (); 53 | [%expect {| 1412403576 |}] 54 | ;; 55 | 56 | include (val Solution.make ~problem:(Number 800) ~main) 57 | -------------------------------------------------------------------------------- /sol/sol_800.mli: -------------------------------------------------------------------------------- 1 | open! Core 2 | open! Import 3 | include Solution.S 4 | -------------------------------------------------------------------------------- /sol/sol_816.mli: -------------------------------------------------------------------------------- 1 | open! Core 2 | open! Import 3 | include Solution.S 4 | -------------------------------------------------------------------------------- /sol/sol_blank.ml: -------------------------------------------------------------------------------- 1 | open! Core 2 | open! Import 3 | 4 | let main () = 5 | if debug then Debug.eprint ""; 6 | printf "\n" 7 | ;; 8 | 9 | include 10 | (val Solution.make 11 | ~problem: 12 | (Custom 13 | { name = "blank" 14 | ; description = 15 | "dummy solution for benchmarking solution apparatus overhead" 16 | }) 17 | ~main) 18 | -------------------------------------------------------------------------------- /sol/sol_blank.mli: -------------------------------------------------------------------------------- 1 | open! Core 2 | open! Import 3 | include Solution.S 4 | -------------------------------------------------------------------------------- /sol/solutions_without_answer_expect_tests.txt: -------------------------------------------------------------------------------- 1 | sol_010_seq.ml 2 | sol_014_memo_slower.ml 3 | sol_014_naive.ml 4 | sol_051.ml 5 | sol_058.ml 6 | sol_060.ml 7 | sol_103.ml 8 | sol_105.ml 9 | sol_108.ml 10 | sol_118.ml 11 | sol_127.ml 12 | sol_142.ml 13 | sol_148.ml 14 | sol_166.ml 15 | sol_169_naive.ml 16 | sol_174.ml 17 | sol_206.ml 18 | sol_214.ml 19 | sol_293.ml 20 | sol_303.ml 21 | sol_315.ml 22 | sol_323_sim.ml 23 | sol_348.ml 24 | sol_401.ml 25 | sol_407.ml 26 | sol_425.ml 27 | sol_429.ml 28 | sol_500.ml 29 | sol_504.ml 30 | sol_531.ml 31 | sol_549.ml 32 | sol_587_int.ml 33 | sol_609.ml 34 | sol_613.ml 35 | sol_650.ml 36 | sol_662.ml 37 | sol_700.ml 38 | sol_719.ml 39 | sol_blank.ml 40 | -------------------------------------------------------------------------------- /sol/template.ml: -------------------------------------------------------------------------------- 1 | open! Core 2 | open! Import 3 | 4 | let main () = raise_s [%message "unimplemented" [%here]] 5 | 6 | include (val Solution.make ~problem:(Number ___) ~main) 7 | -------------------------------------------------------------------------------- /sol/template.mli: -------------------------------------------------------------------------------- 1 | open! Core 2 | open! Import 3 | include Solution.S 4 | -------------------------------------------------------------------------------- /src/algebra.ml: -------------------------------------------------------------------------------- 1 | open! Core 2 | open! Import 3 | 4 | let ( >> ) x y = Float.(x / y > 1000.) 5 | let sort_roots x y = if Float.(x < y) then `Two (x, y) else `Two (y, x) 6 | 7 | (* Formula taken from p. 162-3 of https://cr.yp.to/2005-590/goldberg.pdf. 8 | 9 | David Goldberg. 1991. What every computer scientist should know about floating-point 10 | arithmetic. ACM Comput. Surv. 23, 1 (March 1991), 5-48. DOI: 11 | https://doi.org/10.1145/103162.103163. *) 12 | let quadratic_formula a b c = 13 | let open Float.O in 14 | let determinant = (b * b) - (4. * a * c) in 15 | match Float.sign_or_nan determinant with 16 | | Neg | Nan -> `None 17 | | Zero -> `One (-b / (2. * a)) 18 | | Pos -> 19 | let d = sqrt determinant in 20 | if b * b >> abs (a * c) 21 | then 22 | if b > 0. 23 | then sort_roots ((-b - d) / (2. * a)) (2. * c / (-b - d)) 24 | else sort_roots (2. * c / (-b + d)) ((-b + d) / (2. * a)) 25 | else ( 26 | let x0 = (-b - d) / (2. * a) in 27 | let x1 = (-b + d) / (2. * a) in 28 | sort_roots x0 x1) 29 | ;; 30 | -------------------------------------------------------------------------------- /src/algebra.mli: -------------------------------------------------------------------------------- 1 | (** Basic algebra. *) 2 | 3 | open! Core 4 | open! Import 5 | 6 | (** [quadratic_formula a b c] returns real-valued solutions to [a x^2 + b x + c 7 | = 0], for [a <> 0]. If there are two solutions, the first returned result is 8 | smaller than the second. *) 9 | val quadratic_formula 10 | : float 11 | -> float 12 | -> float 13 | -> [ `Two of float * float | `One of float | `None ] 14 | -------------------------------------------------------------------------------- /src/bitset.ml: -------------------------------------------------------------------------------- 1 | open! Core 2 | open! Import 3 | 4 | let max_elt_plus_one = 62 5 | 6 | type t = int [@@deriving compare, equal, hash, quickcheck] 7 | 8 | let empty = 0 9 | let is_allowable_element int = Int.between int ~low:0 ~high:(max_elt_plus_one - 1) 10 | let mem t int = t land (1 lsl int) <> 0 11 | let add t int = t lor (1 lsl int) 12 | let remove t int = t land lnot (1 lsl int) 13 | let union t u = t lor u 14 | let inter t u = t land u 15 | let diff t u = t land lnot u 16 | let of_list ints = ints |> List.fold ~init:empty ~f:add 17 | let is_subset subset ~of_:supserset = diff subset supserset = empty 18 | 19 | let fold t ~init ~f = 20 | Sequence.range 0 max_elt_plus_one 21 | |> Sequence.filter ~f:(mem t) 22 | |> Sequence.fold ~init ~f 23 | ;; 24 | 25 | let iter t ~f = 26 | Sequence.range 0 max_elt_plus_one |> Sequence.filter ~f:(mem t) |> Sequence.iter ~f 27 | ;; 28 | 29 | let length t = Int.popcount t 30 | 31 | module C = Container.Make0 (struct 32 | type nonrec t = t 33 | 34 | module Elt = Int 35 | 36 | let fold = fold 37 | let iter = `Custom iter 38 | let length = `Custom length 39 | end) 40 | 41 | let is_empty = C.is_empty 42 | let fold_result = C.fold_result 43 | let fold_until = C.fold_until 44 | let exists = C.exists 45 | let for_all = C.for_all 46 | let count = C.count 47 | let sum = C.sum 48 | let find = C.find 49 | let find_map = C.find_map 50 | let to_list = C.to_list 51 | let to_array = C.to_array 52 | let min_elt = C.min_elt 53 | let max_elt = C.max_elt 54 | let sexp_of_t t = t |> to_list |> [%sexp_of: int list] 55 | let t_of_sexp sexp = sexp |> [%of_sexp: int list] |> of_list 56 | -------------------------------------------------------------------------------- /src/bitset.mli: -------------------------------------------------------------------------------- 1 | open! Core 2 | open! Import 3 | 4 | (** Represents a subset of \(\[0, 62)\). *) 5 | type t [@@immediate] [@@deriving compare, equal, hash, quickcheck, sexp] 6 | 7 | include Container.S0 with type t := t and type elt := int 8 | 9 | val empty : t 10 | val is_allowable_element : int -> bool 11 | val add : t -> int -> t 12 | val remove : t -> int -> t 13 | val union : t -> t -> t 14 | val inter : t -> t -> t 15 | val diff : t -> t -> t 16 | val of_list : int list -> t 17 | val is_subset : t -> of_:t -> bool 18 | -------------------------------------------------------------------------------- /src/composition_infix.ml: -------------------------------------------------------------------------------- 1 | open! Core 2 | 3 | module Export = struct 4 | let ( << ) f g = Fn.compose f g 5 | let ( >> ) f g = Fn.compose g f 6 | end 7 | 8 | include Export 9 | -------------------------------------------------------------------------------- /src/composition_infix.mli: -------------------------------------------------------------------------------- 1 | (** Infix operators for function composition. *) 2 | 3 | open! Core 4 | 5 | module Export : sig 6 | val ( << ) : ('b -> 'c) -> ('a -> 'b) -> 'a -> 'c 7 | val ( >> ) : ('a -> 'b) -> ('b -> 'c) -> 'a -> 'c 8 | end 9 | 10 | include module type of Export 11 | -------------------------------------------------------------------------------- /src/distribution.mli: -------------------------------------------------------------------------------- 1 | include Distribution_intf.Distribution (** @inline *) 2 | -------------------------------------------------------------------------------- /src/dune: -------------------------------------------------------------------------------- 1 | (library 2 | (name euler) 3 | (public_name euler) 4 | (synopsis "a collection of utilities for solving ProjectEuler math problems") 5 | (libraries bignum core) 6 | (inline_tests) 7 | (preprocess 8 | (pps ppx_jane))) 9 | -------------------------------------------------------------------------------- /src/euler.ml: -------------------------------------------------------------------------------- 1 | (** This module replaces the [Memo] module from [Core_kernel]. *) 2 | module Memo = Memo 3 | 4 | open! Core 5 | open! Import 6 | module Algebra = Algebra 7 | module Bitset = Bitset 8 | module Composition_infix = Composition_infix 9 | module Distribution = Distribution 10 | module Geometry = Geometry 11 | module Numerics = Numerics 12 | module Number_theory = Number_theory 13 | module Poker = Poker 14 | module Sequences = Sequences 15 | include Composition_infix.Export 16 | include Interfaces 17 | -------------------------------------------------------------------------------- /src/geometry.ml: -------------------------------------------------------------------------------- 1 | open! Core 2 | open! Import 3 | 4 | let is_pythagorean_triple a b c = (a * a) + (b * b) = c * c 5 | 6 | let iter_all_pythagorean_triples ~with_hypotenuse_less_than:max_hypot ~f = 7 | for m = 2 to Number_theory.Int.isqrt max_hypot + 1 do 8 | let m'2 = m * m in 9 | for n = 1 to m - 1 do 10 | if Number_theory.Int.gcd m n = 1 && (m mod 2 = 0 || n mod 2 = 0) 11 | then ( 12 | let n'2 = n * n in 13 | let mn = 2 * m * n in 14 | let rec loop k = 15 | let c = k * (m'2 + n'2) in 16 | if c > max_hypot 17 | then () 18 | else ( 19 | let a = k * (m'2 - n'2) in 20 | let b = k * mn in 21 | f a b c; 22 | loop (k + 1)) 23 | in 24 | loop 1) 25 | done 26 | done 27 | ;; 28 | 29 | (* FIXME this is buggy, doesn't generate in the correct order *) 30 | let pythagorean_triples = 31 | let open Sequence.Let_syntax in 32 | let triples_unsorted = 33 | let%map m, n = 34 | let%bind m = Number_theory.Int.natural_numbers () ~init:2 in 35 | let%bind n = Sequence.range 1 m in 36 | if Number_theory.Int.gcd m n = 1 && (m mod 2 = 0 || n mod 2 = 0) 37 | then return (m, n) 38 | else Sequence.empty 39 | in 40 | let a = (m * m) - (n * n) in 41 | let b = 2 * m * n in 42 | let c = (m * m) + (n * n) in 43 | let%bind k = Number_theory.Int.natural_numbers () ~init:1 in 44 | return (k * a, k * b, k * c) 45 | in 46 | Sequence.interleave triples_unsorted 47 | ;; 48 | -------------------------------------------------------------------------------- /src/geometry.mli: -------------------------------------------------------------------------------- 1 | (** Geometric identities and formulas. *) 2 | 3 | open! Core 4 | open! Import 5 | 6 | (*_ TODO Add law of sines, cosines, etc. *) 7 | (*_ TODO Add formula for triangle area based on sides. *) 8 | (*_ TODO Add formula for polygon area based on lattice points. *) 9 | 10 | (** [is_pythagorean_triple a b c] is equivalent to [a^2 + b^2 = c^2]. *) 11 | val is_pythagorean_triple : int -> int -> int -> bool 12 | 13 | val iter_all_pythagorean_triples 14 | : with_hypotenuse_less_than:int 15 | -> f:(int -> int -> int -> unit) 16 | -> unit 17 | 18 | (** [pythagorean_triples] *) 19 | val pythagorean_triples : (int * int * int) Sequence.t 20 | -------------------------------------------------------------------------------- /src/import.ml: -------------------------------------------------------------------------------- 1 | open! Core 2 | include Composition_infix 3 | -------------------------------------------------------------------------------- /src/interfaces.ml: -------------------------------------------------------------------------------- 1 | module type Distribution = Distribution.S 2 | module type Number_theory = Number_theory.S 3 | module type Numerics = Numerics.S 4 | -------------------------------------------------------------------------------- /src/memo.ml: -------------------------------------------------------------------------------- 1 | open! Core 2 | open! Import 3 | 4 | type ('a, 'b) fn = 'a -> 'b 5 | 6 | let[@inline always] simple key f = 7 | let cache = Hashtbl.create key in 8 | fun x -> Hashtbl.findi_or_add cache x ~default:f 9 | ;; 10 | 11 | let[@inline always] recursive key f = 12 | let cache = Hashtbl.create key in 13 | let rec memoized_f x = Hashtbl.findi_or_add cache x ~default:(f memoized_f) in 14 | memoized_f 15 | ;; 16 | -------------------------------------------------------------------------------- /src/memo.mli: -------------------------------------------------------------------------------- 1 | (** I like this interface better than [Core_kernel.Memo], and I have no need for 2 | the LRU stuff. *) 3 | 4 | open! Core 5 | open! Import 6 | 7 | type ('a, 'b) fn = 'a -> 'b 8 | 9 | val simple : (module Base.Hashtbl.Key.S with type t = 'a) -> ('a -> 'b) -> ('a, 'b) fn 10 | 11 | val recursive 12 | : (module Base.Hashtbl.Key.S with type t = 'a) 13 | -> (('a -> 'b) -> 'a -> 'b) 14 | -> ('a, 'b) fn 15 | -------------------------------------------------------------------------------- /src/number_theory.mli: -------------------------------------------------------------------------------- 1 | include Number_theory_intf.Number_theory (** @inline *) 2 | -------------------------------------------------------------------------------- /src/numerics.mli: -------------------------------------------------------------------------------- 1 | include Numerics_intf.Numerics (** @inline *) 2 | -------------------------------------------------------------------------------- /src/numerics_intf.ml: -------------------------------------------------------------------------------- 1 | open! Core 2 | open! Import 3 | 4 | (** Real numbers *) 5 | module type Real = sig 6 | type t [@@deriving sexp_of] 7 | 8 | val ( + ) : t -> t -> t 9 | val ( - ) : t -> t -> t 10 | val ( * ) : t -> t -> t 11 | val ( / ) : t -> t -> t 12 | val of_int : int -> t 13 | val abs : t -> t 14 | val sign_exn : t -> Sign.t 15 | 16 | include Comparable with type t := t 17 | end 18 | 19 | module type S = sig 20 | (** a numeric type capable of representing (possibly approximate) real numbers *) 21 | type real 22 | 23 | (*_ FIXME either rename to delta or make it a relative error *) 24 | 25 | (** [bisect ~f ~epsilon ~lo ~hi] finds a solution [x] to the equation [f x = 26 | 0]. 27 | 28 | @return a value in [[lo, hi]] no farther than [epsilon] from a solution to 29 | [f x = 0]. *) 30 | val bisect : f:(real -> real) -> epsilon:real -> lo:real -> hi:real -> real 31 | 32 | (** Integrate [f] numerically on the interval (lo, hi), approximating with 33 | the given number of [intervals]. 34 | 35 | @param method_ default is [`Simpson's_rule] *) 36 | val integrate 37 | : ?method_:[ `Midpoint | `Trapezoid | `Simpson's_rule ] 38 | -> unit 39 | -> f:(real -> real) 40 | -> lo:real 41 | -> hi:real 42 | -> intervals:int 43 | -> real 44 | 45 | (*_ FIXME consistent use of error in x or y *) 46 | 47 | (** [newton's_method ~f ~f' ~epsilon ~init] finds a solution [x] to the 48 | equation [f x = 0]. 49 | 50 | @param f' the derivative of [f] 51 | 52 | @return a value no farther than [epsilon] from a solution to [f x = 0]. *) 53 | val newton's_method 54 | : f:(real -> real) 55 | -> f':(real -> real) 56 | -> epsilon:real 57 | -> init:real 58 | -> real 59 | end 60 | 61 | module type Numerics = sig 62 | (** Numerical integration, root-finding, etc. *) 63 | 64 | module type Real = Real 65 | module type S = S 66 | 67 | module Make (M : Real) : S with type real = M.t 68 | module Float : S with type real = float 69 | module Bignum : S with type real = Bignum.t 70 | end 71 | -------------------------------------------------------------------------------- /src/poker.mli: -------------------------------------------------------------------------------- 1 | (** Standard 52-card poker cards *) 2 | 3 | open! Core 4 | open! Import 5 | 6 | module Rank : sig 7 | type t = 8 | | Two 9 | | Three 10 | | Four 11 | | Five 12 | | Six 13 | | Seven 14 | | Eight 15 | | Nine 16 | | Ten 17 | | Jack 18 | | Queen 19 | | King 20 | | Ace 21 | [@@deriving compare, hash, sexp] 22 | 23 | val pred : t -> t option 24 | val succ : t -> t option 25 | 26 | include Comparable with type t := t 27 | include Stringable with type t := t 28 | end 29 | 30 | module Suit : sig 31 | type t = 32 | | Clubs 33 | | Diamonds 34 | | Hearts 35 | | Spades 36 | [@@deriving compare, hash, sexp] 37 | 38 | include Comparable with type t := t 39 | include Stringable with type t := t 40 | end 41 | 42 | module Card : sig 43 | type t = private 44 | { rank : Rank.t 45 | ; suit : Suit.t 46 | } 47 | [@@deriving compare, fields, hash, sexp] 48 | 49 | (** compare by rank, then by suit *) 50 | val compare : t -> t -> int 51 | 52 | (** 2C, QH, etc. *) 53 | include Stringable with type t := t 54 | end 55 | 56 | module Hand_classification : sig 57 | (** Each [Rank.t] represents the highest card in each group, starting with the 58 | largest multiplicity (ties broken by rank, descending). *) 59 | type t = 60 | | High_card of Rank.t * Rank.t * Rank.t * Rank.t * Rank.t 61 | | One_pair of Rank.t * Rank.t * Rank.t * Rank.t 62 | | Two_pairs of Rank.t * Rank.t * Rank.t 63 | | Three_of_a_kind of Rank.t * Rank.t * Rank.t 64 | | Straight of Rank.t 65 | | Flush of Rank.t * Rank.t * Rank.t * Rank.t * Rank.t 66 | | Full_house of Rank.t * Rank.t 67 | | Four_of_a_kind of Rank.t * Rank.t 68 | | Straight_flush of Rank.t (** [rank < Ace] *) 69 | | Royal_flush 70 | [@@deriving compare, sexp] 71 | 72 | include Comparable.S with type t := t 73 | include Invariant.S with type t := t 74 | end 75 | 76 | module Hand : sig 77 | type t 78 | 79 | (** raises unless length is 5 *) 80 | val of_card_list_exn : Card.t list -> t 81 | 82 | val classify : t -> Hand_classification.t 83 | end 84 | -------------------------------------------------------------------------------- /test/dune: -------------------------------------------------------------------------------- 1 | (library 2 | (name euler_test) 3 | (libraries bignum core euler expect_test_helpers_core) 4 | (preprocess 5 | (pps ppx_jane)) 6 | (inline_tests)) 7 | -------------------------------------------------------------------------------- /test/import.ml: -------------------------------------------------------------------------------- 1 | open! Core 2 | 3 | module Q = Quickcheck.Configure (struct 4 | include Quickcheck 5 | 6 | let default_trial_count = 2500 7 | end) 8 | 9 | include Euler 10 | include Expect_test_helpers_core 11 | -------------------------------------------------------------------------------- /test/test_algebra.ml: -------------------------------------------------------------------------------- 1 | open! Core 2 | open! Import 3 | 4 | let%test_unit "quadratic_formula" = 5 | let gen = 6 | let open Quickcheck.Let_syntax in 7 | let reasonable_ranges = 8 | let%map_open () = return () 9 | and magnitude = Float.gen_incl (-5.) 5. 10 | and sign = of_list [ -1.; 0.; 1. ] in 11 | Float.copysign (exp magnitude) sign 12 | in 13 | let%map_open () = return () 14 | and a = reasonable_ranges |> filter ~f:(fun x -> Float.(x <> 0.)) 15 | and b = reasonable_ranges 16 | and c = reasonable_ranges in 17 | a, b, c 18 | in 19 | Q.test gen ~sexp_of:[%sexp_of: float * float * float] ~f:(fun (a, b, c) -> 20 | let expect = 21 | match Float.(sign_or_nan ((b * b) - (4. * a * c))) with 22 | | Neg | Nan -> 0 23 | | Zero -> 1 24 | | Pos -> 2 25 | in 26 | let test_root here x = 27 | let open Float.O in 28 | let y = (a * (x ** 2.)) + (b * x) + c in 29 | if abs y >= 1e-7 30 | then 31 | raise_s 32 | [%message 33 | "Expected y=f(x) to be approximately zero" 34 | (x : float) 35 | (y : float) 36 | (here : Source_code_position.t)] 37 | in 38 | match Algebra.quadratic_formula a b c with 39 | | `None -> [%test_result: int] 0 ~expect 40 | | `One x -> 41 | [%test_result: int] 1 ~expect; 42 | test_root [%here] x 43 | | `Two (x0, x1) -> 44 | [%test_result: int] 2 ~expect; 45 | if Float.O.(x0 >= x1) 46 | then raise_s [%message "Expected root x0 < x1" (x0 : float) (x1 : float)]; 47 | test_root [%here] x0; 48 | test_root [%here] x1) 49 | ;; 50 | -------------------------------------------------------------------------------- /test/test_algebra.mli: -------------------------------------------------------------------------------- 1 | (*_ This signature is deliberately empty. *) 2 | -------------------------------------------------------------------------------- /test/test_bezout.ml: -------------------------------------------------------------------------------- 1 | open! Core 2 | open! Import 3 | 4 | let%test_unit "Extended Euclidean Algorithm" = 5 | let gen = 6 | let open Quickcheck.Let_syntax in 7 | let%map_open () = return () 8 | and a = small_non_negative_int 9 | and b = small_non_negative_int in 10 | a, b 11 | in 12 | Q.iter gen ~f:(fun (a, b) -> 13 | let s, t, g = Number_theory.Int.bezout a b in 14 | [%test_result: int] g ~expect:(Number_theory.Int.gcd a b); 15 | [%test_result: int] ((s * a) + (t * b)) ~expect:g) 16 | ;; 17 | -------------------------------------------------------------------------------- /test/test_bezout.mli: -------------------------------------------------------------------------------- 1 | (*_ This signature is deliberately empty. *) 2 | -------------------------------------------------------------------------------- /test/test_bitset.mli: -------------------------------------------------------------------------------- 1 | (*_ This signature is deliberately empty. *) 2 | -------------------------------------------------------------------------------- /test/test_chinese_remainder_theorem.ml: -------------------------------------------------------------------------------- 1 | open! Core 2 | open! Import 3 | 4 | let are_pairwise_coprime xs = 5 | let rec loop xs = 6 | match xs with 7 | | [] -> true 8 | | hd :: tl -> 9 | List.for_all tl ~f:(fun x -> Bigint.(one = Number_theory.Bigint.gcd x hd)) 10 | && loop tl 11 | in 12 | loop xs 13 | ;; 14 | 15 | let%test_unit "Chinese remainder theorem" = 16 | let gen = 17 | let open Quickcheck.Let_syntax in 18 | let%bind_open moduli = 19 | Bigint.gen_positive |> list_non_empty |> filter ~f:are_pairwise_coprime 20 | in 21 | List.map moduli ~f:(fun modulus -> 22 | let%map residue = Bigint.gen_incl Bigint.zero (Bigint.pred modulus) in 23 | residue, modulus) 24 | |> Quickcheck.Generator.all 25 | in 26 | Q.test 27 | gen 28 | (* example from https://en.wikipedia.org/wiki/Chinese_remainder_theorem#Computation *) 29 | ~examples: 30 | [ [ 0, 3; 3, 4; 4, 5 ] 31 | |> List.map ~f:(fun (x, y) -> Bigint.of_int x, Bigint.of_int y) 32 | ] 33 | ~sexp_of:[%sexp_of: (Bigint.t * Bigint.t) list] 34 | ~f:(fun residues -> 35 | let x, m = Number_theory.Bigint.chinese_remainder_theorem residues in 36 | let residue_product = 37 | List.fold residues ~init:Bigint.one ~f:(fun ac (_, m) -> Bigint.(ac * m)) 38 | in 39 | [%test_result: Bigint.t] m ~expect:residue_product; 40 | List.iter residues ~f:(fun (r, m) -> 41 | [%test_result: Bigint.t] Bigint.(x % m) ~expect:r)) 42 | ;; 43 | -------------------------------------------------------------------------------- /test/test_chinese_remainder_theorem.mli: -------------------------------------------------------------------------------- 1 | (*_ This signature is deliberately empty. *) 2 | -------------------------------------------------------------------------------- /test/test_digits.mli: -------------------------------------------------------------------------------- 1 | (*_ This signature is deliberately empty. *) 2 | -------------------------------------------------------------------------------- /test/test_distribution.mli: -------------------------------------------------------------------------------- 1 | (*_ This signature is deliberately empty. *) 2 | -------------------------------------------------------------------------------- /test/test_factorial_prime_factor.ml: -------------------------------------------------------------------------------- 1 | open! Core 2 | open! Import 3 | 4 | let%test_unit "factorial_prime_factor" = 5 | Q.test Quickcheck.Generator.small_positive_int ~sexp_of:[%sexp_of: int] ~f:(fun n -> 6 | let expect = 7 | n 8 | |> Bigint.of_int 9 | |> Number_theory.Bigint.factorial 10 | |> Number_theory.Bigint.prime_factor 11 | |> List.map ~f:(Tuple2.map_fst ~f:Bigint.to_int_exn) 12 | in 13 | [%test_result: (int * int) list] ~expect (Number_theory.factorial_prime_factor n)) 14 | ;; 15 | -------------------------------------------------------------------------------- /test/test_factorial_prime_factor.mli: -------------------------------------------------------------------------------- 1 | (*_ This signature is deliberately empty. *) 2 | -------------------------------------------------------------------------------- /test/test_integrate.ml: -------------------------------------------------------------------------------- 1 | open! Core 2 | open! Import 3 | 4 | let%test_unit "integrate" = 5 | let open Bignum.O in 6 | Quickcheck.test 7 | [%quickcheck.generator: Bignum.t -> Bignum.t] 8 | ~examples:[ (fun x -> x ** 2) ] 9 | ~f:(fun f -> 10 | let expect = 11 | let interval ~f ~x_lo ~x_mi ~x_hi = 12 | let f = f << of_int in 13 | Int.( - ) x_hi x_lo // 6 * (f x_lo + (of_int 4 * f x_mi) + f x_hi) 14 | in 15 | interval ~f ~x_lo:1 ~x_mi:2 ~x_hi:3 + interval ~f ~x_lo:3 ~x_mi:4 ~x_hi:5 16 | in 17 | [%test_result: Bignum.t] 18 | ~expect 19 | (Numerics.Bignum.integrate 20 | () 21 | ~method_:`Simpson's_rule 22 | ~f 23 | ~lo:(of_int 1) 24 | ~hi:(of_int 5) 25 | ~intervals:2)) 26 | ;; 27 | -------------------------------------------------------------------------------- /test/test_integrate.mli: -------------------------------------------------------------------------------- 1 | (*_ This signature is deliberately empty. *) 2 | -------------------------------------------------------------------------------- /test/test_number_theory.mli: -------------------------------------------------------------------------------- 1 | (*_ This signature is deliberately empty. *) 2 | -------------------------------------------------------------------------------- /test/test_sequences.mli: -------------------------------------------------------------------------------- 1 | (*_ This signature is deliberately empty. *) 2 | --------------------------------------------------------------------------------