From bc3119765346de8321fe6a90f66d1418c6330c9a Mon Sep 17 00:00:00 2001 From: Juergen Stuber Date: Fri, 15 Nov 2024 13:49:20 +0100 Subject: [PATCH] Rework order of primes and saved state to get deterministic output --- Cargo.toml | 2 + src/bin/primes/main.rs | 162 ++++++++++++++++++++++++++++++----------- 2 files changed, 121 insertions(+), 43 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index f502fc8..cf19e37 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,3 +15,5 @@ rand = "0.8.4" chrono = "0.4.6" lowdim = { version = "0.6.0", features = ["random"] } primal = "0.3.3" +serde = { version = "1.0.215", features = ["derive"] } +serde_json = "1.0.132" diff --git a/src/bin/primes/main.rs b/src/bin/primes/main.rs index ab8e225..8de8397 100644 --- a/src/bin/primes/main.rs +++ b/src/bin/primes/main.rs @@ -1,3 +1,5 @@ +use std::cmp; +use std::collections::BinaryHeap; use std::env::args; use std::fs; use std::io::stdout; @@ -5,6 +7,9 @@ use std::io::Write; use std::thread::sleep; use std::time::Duration; +use serde::Deserialize; +use serde::Serialize; + use lowdim::bb2d; use lowdim::p2d; use lowdim::v2d; @@ -151,13 +156,52 @@ const DEFAULT_ARG: u64 = 2; const STATE_FILENAME: &str = ".primes.state"; -fn write_state(p: usize) { - let _ = fs::write(STATE_FILENAME, p.to_string()); +#[derive(Clone, Debug, Serialize, Deserialize)] +struct State { + prime: usize, + xs: Vec, +} +impl State { + fn init(len: usize, x: i64) -> State { + State { + prime: 2, + xs: vec![x; len], + } + } + fn load(lines: usize) -> Result { + let s = fs::read_to_string(STATE_FILENAME)?; + let state: State = serde_json::from_str(&s)?; + if state.xs.len() != lines { + return Err("number of lines don't match".into()); + } + Ok(state) + } + fn save(&self) -> Result<()> { + let serialized = serde_json::to_string(self)?; + Ok(fs::write(STATE_FILENAME, serialized)?) + } } -fn read_state() -> Result { - let s = fs::read_to_string(STATE_FILENAME)?; - Ok(s.parse::()?) +#[derive(Clone, Debug, PartialEq, Eq)] +struct LinePos { + /// Index of the line. + index: usize, + /// Rendering position in this line. + pos: Point2d, +} +impl PartialOrd for LinePos { + fn partial_cmp(&self, other: &LinePos) -> Option { + Some(self.cmp(other)) + } +} +impl Ord for LinePos { + fn cmp(&self, other: &Self) -> cmp::Ordering { + other + .pos + .x() + .cmp(&self.pos.x()) + .then(self.index.cmp(&other.index)) + } } fn main() -> Result<()> { @@ -178,10 +222,6 @@ fn main() -> Result<()> { let x_size = i64::try_from(x_size)?; let y_size = i64::try_from(y_size)?; - let last_prime = read_state().unwrap_or(1); - eprintln!("starting after {}", last_prime); - let mut primes_iter = primal::Primes::all().skip_while(|&p| p < last_prime); - let bbox = bb2d(0..x_size as i64, 0..y_size as i64); let mut frame = Frame::new(bbox); @@ -198,60 +238,96 @@ fn main() -> Result<()> { MIN_LINE_SEP }; let line_sep = line_sep.max(MIN_LINE_SEP); + let x0 = bbox.x_start() + MIN_MARGIN; let y0 = MIN_MARGIN + (y_size - 2 * MIN_MARGIN - lines * HEIGHT - (lines - 1) * line_sep) / 2; - let mut start_positions = (0..lines) - .map(|i| p2d(bbox.x_end(), y0 + i * (line_sep + HEIGHT))) + let line_count = usize::try_from(lines)?; + + let start_state = State::load(line_count).unwrap_or_else(|_| State::init(line_count, x0)); + eprintln!("starting at {}", start_state.prime); + + let mut primes_iter = primal::Primes::all().skip_while(|&p| p < start_state.prime); + + let mut start_positions = start_state + .xs + .iter() + .enumerate() + .map(|(i, &x)| p2d(x, y0 + (i as i64) * (line_sep + HEIGHT))) .collect::>(); - let lines = usize::try_from(lines)?; - - let mut visible_primes = (0..lines).map(|_| Vec::new()).collect::>(); + let mut visible_primes = Vec::new(); let space = v2d(2, 0); loop { let mut new_frame = Frame::new(bbox); - let mut new_visible_primes = (0..lines).map(|_| Vec::new()).collect::>(); + let mut new_visible_primes = Vec::new(); - for i in (0..lines).rev() { - let mut pos = start_positions[i]; + // Fill empty space from the left to the right over all lines. + let mut positions = start_positions + .iter() + .enumerate() + .map(|(i, &pos)| LinePos { index: i, pos }) + .collect::>(); + // Render the primes that are already visible. + for p in visible_primes { + let line_pos = positions.pop().unwrap(); + let i = line_pos.index; + let mut pos = line_pos.pos; + + render(&mut new_frame, &mut pos, p); + if new_visible_primes.is_empty() && pos.x() + STROKE_MID < frame.bbox().x_start() { + // The prime is not visible any more, + // not even some overhang from a five bar. + // Omit it from the visible primes and move the start position + // to the start of the next number. + // Only do this if no previous prime was kept. + start_positions[i] = pos + space; + } else { + // Keep the prime for displaying it the next time. + new_visible_primes.push(p); + } + + // Render a space between numbers. + pos += space; + + // Save the new position for later handling. + positions.push(LinePos { index: i, pos }); + } + while let Some(line_pos) = positions.pop() { + let i = line_pos.index; + let mut pos = line_pos.pos; + + // Fill up the visible primes when necessary. + if pos.x() < frame.bbox().x_end() + STROKE_MID { + let p = primes_iter.next().unwrap(); + eprintln!("{} {}", i, p); + + // Keep the prime for displaying it the next time. + new_visible_primes.push(p); - // Render the primes that are already visible. - for &p in &visible_primes[i] { render(&mut new_frame, &mut pos, p); - if pos.x() + STROKE_MID < frame.bbox().x_start() { - // The prime is not visible any more, - // not even some overhang from a five bar. - // Omit it from the visible primes and move the start position - // to the start of the next number. - start_positions[i] = pos + space; - } else { - // Keep the prime for displaying it the next time. - new_visible_primes[i].push(p); - } // Render a space between numbers. pos += space; + + positions.push(LinePos { index: i, pos }); } + } - // Fill up the visible primes when necessary. - while pos.x() < frame.bbox().x_end() + STROKE_MID { - let p = primes_iter.next().unwrap(); - write_state(p); - eprintln!("{}", p); - - // Keep the prime for displaying it the next time. - new_visible_primes[i].push(p); - - render(&mut new_frame, &mut pos, p); - } - - // Scroll one pixel to the left. - start_positions[i] -= v2d(1, 0); + // Scroll one pixel to the left. + for pos in &mut start_positions { + *pos -= v2d(1, 0); } frame = new_frame; visible_primes = new_visible_primes; + // Save the state + let state = State { + prime: visible_primes[0], + xs: start_positions.iter().map(|p| p.x()).collect::>(), + }; + let _ = state.save(); + let mut buf = Vec::with_capacity(frame_size); send(&mut buf, &frame)?; stdout().write_all(&buf)?;