diff --git a/Cargo.lock b/Cargo.lock index 43bcfb2..bac37b2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,3 +1,8 @@ +[[package]] +name = "adler32" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "aho-corasick" version = "0.7.1" @@ -29,6 +34,35 @@ name = "bitflags" version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "byteorder" +version = "1.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "cairo-rs" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", + "cairo-sys-rs 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", + "glib 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", + "glib-sys 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", + "gobject-sys 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.50 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "cairo-sys-rs" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "glib-sys 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.50 (registry+https://github.com/rust-lang/crates.io-index)", + "pkg-config 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "cfg-if" version = "0.1.7" @@ -48,6 +82,15 @@ dependencies = [ "vec_map 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "deflate" +version = "0.7.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "adler32 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", + "byteorder 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "env_logger" version = "0.6.1" @@ -60,6 +103,37 @@ dependencies = [ "termcolor 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "glib" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", + "glib-sys 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", + "gobject-sys 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.50 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "glib-sys" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "libc 0.2.50 (registry+https://github.com/rust-lang/crates.io-index)", + "pkg-config 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "gobject-sys" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "glib-sys 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.50 (registry+https://github.com/rust-lang/crates.io-index)", + "pkg-config 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "humantime" version = "1.2.0" @@ -68,6 +142,14 @@ dependencies = [ "quick-error 1.2.2 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "inflate" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "adler32 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "lazy_static" version = "1.3.0" @@ -95,6 +177,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" name = "multiplicative-persistence" version = "0.1.0" dependencies = [ + "cairo-rs 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", "clap 2.32.0 (registry+https://github.com/rust-lang/crates.io-index)", "env_logger 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -102,6 +185,7 @@ dependencies = [ "num-bigint 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", "num-traits 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", "num_cpus 1.10.0 (registry+https://github.com/rust-lang/crates.io-index)", + "png 0.14.0 (registry+https://github.com/rust-lang/crates.io-index)", "threadpool 1.7.1 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -122,6 +206,15 @@ dependencies = [ "num-traits 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "num-iter" +version = "0.1.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "num-integer 0.1.39 (registry+https://github.com/rust-lang/crates.io-index)", + "num-traits 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "num-traits" version = "0.2.6" @@ -135,6 +228,22 @@ dependencies = [ "libc 0.2.50 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "pkg-config" +version = "0.3.14" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "png" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", + "deflate 0.7.19 (registry+https://github.com/rust-lang/crates.io-index)", + "inflate 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)", + "num-iter 0.1.37 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "quick-error" version = "1.2.2" @@ -277,22 +386,34 @@ dependencies = [ ] [metadata] +"checksum adler32 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "7e522997b529f05601e05166c07ed17789691f562762c7f3b987263d2dedee5c" "checksum aho-corasick 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)" = "765f00fadb78fa7c796b21e3d6928affd322fc9d4a44febec6b452f6a9ef8deb" "checksum ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b" "checksum atty 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "9a7d5b8723950951411ee34d271d99dddcc2035a16ab25310ea2c8cfd4369652" "checksum bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "228047a76f468627ca71776ecdebd732a3423081fcf5125585bcd7c49886ce12" +"checksum byteorder 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a019b10a2a7cdeb292db131fc8113e57ea2a908f6e7894b0c3c671893b65dbeb" +"checksum cairo-rs 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "9e09d8a818b2ccc8983f04d95a9350c3cf8d24cc456cedca3b88fa3a81fdc0e2" +"checksum cairo-sys-rs 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b3fa13914fdc013387afa771f554f2f71d6ae931f4e5be9246c337d60c3dc484" "checksum cfg-if 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "11d43355396e872eefb45ce6342e4374ed7bc2b3a502d1b28e36d6e23c05d1f4" "checksum clap 2.32.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b957d88f4b6a63b9d70d5f454ac8011819c6efa7727858f458ab71c756ce2d3e" +"checksum deflate 0.7.19 (registry+https://github.com/rust-lang/crates.io-index)" = "8a6abb26e16e8d419b5c78662aa9f82857c2386a073da266840e474d5055ec86" "checksum env_logger 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b61fa891024a945da30a9581546e8cfaf5602c7b3f4c137a2805cf388f92075a" +"checksum glib 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)" = "0c4e8fdc159c196a5dfa53a92929ac4c10c8a6637ffb43951f3fff89c2cd2365" +"checksum glib-sys 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5bda542f3caee39a027638e9644ff89204101ad916fd7370b585ad2c5fc97e61" +"checksum gobject-sys 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "23e05a14290d3dc255223cba51db4b0f3da438d5250657996fa99b2a30faf43e" "checksum humantime 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3ca7e5f2e110db35f93b837c81797f3714500b81d517bf20c431b16d3ca4f114" +"checksum inflate 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)" = "1cdb29978cc5797bd8dcc8e5bf7de604891df2a8dc576973d71a281e916db2ff" "checksum lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bc5729f27f159ddd61f4df6228e827e86643d4d3e7c32183cb30a1c08f604a14" "checksum libc 0.2.50 (registry+https://github.com/rust-lang/crates.io-index)" = "aab692d7759f5cd8c859e169db98ae5b52c924add2af5fbbca11d12fefb567c1" "checksum log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)" = "c84ec4b527950aa83a329754b01dbe3f58361d1c5efacd1f6d68c494d08a17c6" "checksum memchr 2.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "2efc7bc57c883d4a4d6e3246905283d8dae951bb3bd32f49d6ef297f546e1c39" "checksum num-bigint 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "57450397855d951f1a41305e54851b1a7b8f5d2e349543a02a2effe25459f718" "checksum num-integer 0.1.39 (registry+https://github.com/rust-lang/crates.io-index)" = "e83d528d2677f0518c570baf2b7abdcf0cd2d248860b68507bdcb3e91d4c0cea" +"checksum num-iter 0.1.37 (registry+https://github.com/rust-lang/crates.io-index)" = "af3fdbbc3291a5464dc57b03860ec37ca6bf915ed6ee385e7c6c052c422b2124" "checksum num-traits 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "0b3a5d7cc97d6d30d8b9bc8fa19bf45349ffe46241e8816f50f62f6d6aaabee1" "checksum num_cpus 1.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1a23f0ed30a54abaa0c7e83b1d2d87ada7c3c23078d1d87815af3e3b6385fbba" +"checksum pkg-config 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)" = "676e8eb2b1b4c9043511a9b7bea0915320d7e502b0a079fb03f9635a5252b18c" +"checksum png 0.14.0 (registry+https://github.com/rust-lang/crates.io-index)" = "9adebf7fb91ccf5eac9da1a8e00e83cb8ae882c3e8d8e4ad59da73cb8c82a2c9" "checksum quick-error 1.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "9274b940887ce9addde99c4eee6b5c44cc494b182b97e73dc8ffdcb3397fd3f0" "checksum redox_syscall 0.1.51 (registry+https://github.com/rust-lang/crates.io-index)" = "423e376fffca3dfa06c9e9790a9ccd282fafb3cc6e6397d01dbf64f9bacc6b85" "checksum redox_termios 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7e891cfe48e9100a70a3b6eb652fef28920c117d366339687bd5576160db0f76" diff --git a/Cargo.toml b/Cargo.toml index 6aa0a6e..883e67a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,6 +5,7 @@ authors = ["Tom Milligan "] edition = "2018" [dependencies] +cairo-rs = { version = "0.6.0", features = ["png"] } clap = "^2.32.0" env_logger = "^0.6.1" lazy_static = "^1.3.0" @@ -12,4 +13,6 @@ log = "^0.4.6" num-bigint = "^0.2.2" num-traits = "^0.2.6" num_cpus = "^1.10.0" +png = "0.14.0" threadpool = "^1.7.1" + diff --git a/README.md b/README.md index 1f5d556..55cad11 100644 --- a/README.md +++ b/README.md @@ -10,20 +10,23 @@ This project uses the standard `rustup` and `cargo` toolchain. Originally tested ## Usage -A single binary `mpersist` will be built. Run with `cargo run`. Subcommands are: +Build binaries with `cargo build --release --bins`, they will be output in `target/release`: -- `search`: look for the smallest integer with the largest multiplicative persistence value -- `for`: get the multiplicative persistence of any positive integer +- `mpersist` + - `for`: get the multiplicative persistence of any positive integer + - `list`: get the multiplicative persistence of values in a range + - `search`: look for the smallest integer with the largest multiplicative persistence value +- `mp-visualize` -## Examples +### Data generation ```bash -cargo run -- for 12 # 1 -cargo run -- for 77 # 4 +./mpersist for 12 # 1 +./mpersist -- for 77 # 4 ``` ```bash -cargo run -- search +./mpersist search 3 39 4 77 # etc. @@ -32,6 +35,17 @@ cargo run -- search # - starting with integers 233 digits in length # - search for 10 rounds (i.e. search up to 243 digits in length) # - use 4 threads -cargo run -- search -f 233 -n 10 -t 4 +./mpersist search -f 233 -n 10 -t 4 # this might take some time! ``` + +### Visualization + +For visualization examples, see the [example](example/) directory. +The `mp-visualize` binary reads the output of `mpersist list` from stdin, and lays it out in a [Sacks spiral](https://en.wikipedia.org/wiki/Ulam_spiral#Variants). + +```bash +./mpersist list 0 3500 | ./mp-visualize default.png +./mpersist list 0 100000 | ./mp-visualize -d 0.5 -a 0.25 -r 4 -f 2.0 -w 900 shockwave.png +./mpersist list 0 25000 | ./mp-visualize -d 0.25 -a 0.25 -r 3.5 stars.png +``` diff --git a/example/default.png b/example/default.png new file mode 100644 index 0000000..22057a0 Binary files /dev/null and b/example/default.png differ diff --git a/example/shockwave.png b/example/shockwave.png new file mode 100644 index 0000000..710663d Binary files /dev/null and b/example/shockwave.png differ diff --git a/example/stars.png b/example/stars.png new file mode 100644 index 0000000..2a8fbad Binary files /dev/null and b/example/stars.png differ diff --git a/integrate/check b/integrate/check index 220fc67..2f54930 100755 --- a/integrate/check +++ b/integrate/check @@ -19,3 +19,8 @@ check() { check 'for 77' for.stdout check 'list 38 42' list.stdout check 'search' search.stdout + + +cargo run --bin mpersist -- list 0 100 | + cargo run --bin mp-visualize -- /tmp/visualize.png && + sha512sum -c ./integrate/visualize.png.sha512 diff --git a/integrate/visualize.png.sha512 b/integrate/visualize.png.sha512 new file mode 100644 index 0000000..e4e8dc8 --- /dev/null +++ b/integrate/visualize.png.sha512 @@ -0,0 +1 @@ +2229f7ca0465b940c3408ca1ae806c406b0ca39457a89ef37f4e28c6fa47f0430bea9167f6a69745ff63bb90df8c15e45053acb9dfd2c4512722022f82f1f751 /tmp/visualize.png diff --git a/src/bin/mp-visualize.rs b/src/bin/mp-visualize.rs new file mode 100644 index 0000000..eb5fe3c --- /dev/null +++ b/src/bin/mp-visualize.rs @@ -0,0 +1,155 @@ +extern crate cairo; +extern crate clap; + +use std::f64::consts::PI; +use std::fs::File; +use std::io::{self, BufRead}; + +use cairo::{Context, Format, ImageSurface}; +use clap::{App, Arg}; + +#[derive(Debug, Copy, Clone)] +struct Options { + canvas_height: i32, + canvas_width: i32, + dot_radius: f64, + mp_filter_low: f64, + spacing_angular: f64, + spacing_radial: f64, +} + +fn visualize(output_path: &str, options: &Options) { + let Options { + canvas_height, + canvas_width, + mp_filter_low, + dot_radius, + spacing_angular, + spacing_radial, + } = options; + let surface = ImageSurface::create(Format::ARgb32, *canvas_width, *canvas_height) + .expect("Couldn't create surface"); + let context = Context::new(&surface); + + // paint canvas white + context.set_source_rgb(1.0, 1.0, 1.0); + context.paint(); + context.set_source_rgb(0.0, 0.0, 0.0); + + // draw line chart + let stdin = io::stdin(); + for line in stdin.lock().lines() { + let data = line.unwrap(); + let split: Vec<&str> = data.split(" ").collect(); + let candidate: f64 = split[0].parse::().unwrap(); + let mut mp: f64 = split[1].parse::().unwrap(); + + if mp <= *mp_filter_low { + mp = 0.0; + } + + let n = candidate * spacing_angular; + let r = n.sqrt(); + let t = 2.0 * PI * r; + let x = (*canvas_width as f64 * 0.5) + (r * t.cos()) * spacing_radial; + let y = (*canvas_height as f64 * 0.5) - (r * t.sin()) * spacing_radial; + let s = mp * dot_radius; + context.move_to(x, y); + context.arc(x, y, s, 0.0, 2.0 * PI); + context.fill(); + } + context.stroke(); + + let mut file = File::create(output_path).expect("Couldn't create file"); + surface + .write_to_png(&mut file) + .expect("Couldn't write to png"); +} + +pub fn main() { + let app = App::new("mp-visualize") + .about("Visualise mp values in prime spiral format") + .arg( + Arg::with_name("output_path") + .help("Output file path") + .index(1) + .required(true), + ) + .arg( + Arg::with_name("width") + .help("canvas width") + .takes_value(true) + .short("w") + .long("width"), + ) + .arg( + Arg::with_name("height") + .help("canvas height") + .takes_value(true) + .short("i") + .long("height"), + ) + .arg( + Arg::with_name("dot_radius") + .help("multiplier for dot radius") + .takes_value(true) + .short("d") + .long("dot-radius"), + ) + .arg( + Arg::with_name("mp_filter_low") + .help("filter out mp values below this number") + .takes_value(true) + .short("f") + .long("filter"), + ) + .arg( + Arg::with_name("spacing_angular") + .help("alter spacing of lines radiating from the center") + .takes_value(true) + .short("a") + .long("angular-spacing"), + ) + .arg( + Arg::with_name("spacing_radial") + .help("alter spacing between rings of dots") + .takes_value(true) + .short("r") + .long("radial-spacing"), + ); + let matches = app.get_matches(); + let output_path = matches.value_of("output_path").unwrap(); + let options = Options { + canvas_height: matches + .value_of("height") + .unwrap_or("600") + .parse() + .expect("Invalid integer for height"), + canvas_width: matches + .value_of("width") + .unwrap_or("600") + .parse() + .expect("Invalid integer for width"), + dot_radius: matches + .value_of("dot_radius") + .unwrap_or("1.0") + .parse() + .expect("Invalid float for dot radius"), + mp_filter_low: matches + .value_of("mp_filter_low") + .unwrap_or("0.0") + .parse() + .expect("Invalid float for mp filter"), + spacing_angular: matches + .value_of("spacing_angular") + .unwrap_or("1.0") + .parse() + .expect("Invalid float for angular spacing"), + spacing_radial: matches + .value_of("spacing_radial") + .unwrap_or("5.0") + .parse() + .expect("Invalid float for radial spacing"), + }; + visualize(output_path, &options); +}