//! # The Cosmic Timeline //! //! The Observatory's horizontal bar from Creation to Annihilation: the whole //! of the calendar's representable domain, some 10^154 Planck times of it, //! laid across a single row of glyph cells and offered up for clicking. //! Every click selects one segment of the bar and makes that segment the //! whole bar; the view narrows by a factor of the segment count at each //! descent, the descents compose, and they may be repeated until the span //! beneath the pointer is narrower than a hundred Planck times, at which //! point the Observatory declines to descend further, there being nothing //! finer to show and no wish to pretend otherwise. //! //! The events of cosmic history are not painted on the bar all at once. //! Each is withheld until the view has narrowed enough to give it room, so //! that the widest views list the first stars and the death of the Sun, a //! window a billion years wide lists the Snowball and the Cambrian, and a //! window seventeen years wide lists the Whisper and the Unix epoch — each //! event in the company of its own era, and never more of them than the //! panel beneath the bar has lines. The curious observer who drills down is //! rewarded. The incurious one sees a bar with a word at either end, which //! is also a true statement about time, if a coarse one, and the calendar //! has a mask for coarse. //! //! Every quantity here that is a time is an `I512` or a `U512`, and every //! comparison, subtraction and quotient is performed on those integers in //! full. The single exemption, tolerated because a GPU cannot be addressed //! in any other tongue, is the last line of `TimelineView::position_of`, //! which turns an exact ratio into a fraction of a bar's width and confesses //! to the sin at length where it is committed. use loic::{ As, I512, I512_ZERO, LOIC_TO_UNIX_OFFSET_SECONDS, U512, U512_ZERO, annihilation_planck, creation_planck, }; use std::str::FromStr; use crate::events::EventId; /// The first nineteen powers of ten, transcribed in full and looked up by /// index rather than raised at runtime, so that no exponentiation is ever /// entrusted to a floating-point unit, whose powers of ten are exact only /// until they are not and which does not say when. Index `n` furnishes /// 10^n, exactly, as a `u64`, from 10^0 to 10^18, the largest power of ten /// that type can hold; that reaches every count of attoseconds, /// femtoseconds, picoseconds, nanoseconds and microseconds that the span /// label of `TimelineView::format_span` prints from the table, and it is /// the first thing the event table's scientific-notation helpers reach for /// before they resign themselves to multiplying by ten one power at a time. const POWERS_OF_10: [u64; 19] = [ 1, // 10^0 10, // 10^1 100, // 10^2 1_000, // 10^3 10_000, // 10^4 100_000, // 10^5 1_000_000, // 10^6 10_000_000, // 10^7 100_000_000, // 10^8 1_000_000_000, // 10^9 10_000_000_000, // 10^10 100_000_000_000, // 10^11 1_000_000_000_000, // 10^12 10_000_000_000_000, // 10^13 100_000_000_000_000, // 10^14 1_000_000_000_000_000, // 10^15 10_000_000_000_000_000, // 10^16 100_000_000_000_000_000, // 10^17 1_000_000_000_000_000_000, // 10^18 ]; /// Planck times per second, transcribed in decimal: the integer part of /// `PLANCK_DENOMINATOR / PLANCK_NUMERATOR`, which is 10^50 divided by /// 5,391,247 — forty-four digits of it, approximately 1.855 × 10^43. The /// reliquary is parsed once, when the event table is first built, into the /// `I512` by which every second in that table is multiplied. It is kept as /// a string because a string can be read against its derivation, which is /// `derive_constants.sh` at the workspace root, and because a forty-four /// digit literal is a thing the eye can check and the compiler cannot be /// asked to have opinions about. The fraction of a Planck time that the /// integer part leaves off — less than one — is the whole of what any /// marker placed by this number is early by, per second of the interval it /// was multiplied across. const PLANCK_PER_SECOND_DECIMAL: &str = "18548584399861479171701834473545730700151560"; /// Planck times per Julian year, transcribed in decimal: the figure above /// multiplied by `SECONDS_PER_JULIAN_YEAR`, 31,557,600, which is 365.25 /// days of 86,400 SI seconds and the only year this calendar acknowledges, /// because it is a definition and not a measurement of anything that /// wobbles. Fifty-one digits, approximately 5.853 × 10^50; derivation, /// `derive_constants.sh` at the workspace root. Every event in the table /// dated in years — which is most of them, the universe having been /// unhurried for the greater part of its life — is placed by multiplying /// its count of years by this integer, exactly, with no intermediate that a /// floating-point unit was invited to round. const PLANCK_PER_YEAR_DECIMAL: &str = "585348807057068615108897811582366751143102869856000"; /// The seconds by which the kernel's atomic clock leads civil time: the ten /// that Coordinated Universal Time began with on the first of January 1972, /// and the twenty-seven leap seconds inserted since, the last of them at /// the close of 2016 — thirty-seven in all, every one of them a negotiation /// with a rotating planet. The workspace README, section 1.2, holds all /// thirty-seven in the contempt they have earned and discloses, in the same /// breath, that the calendar's own epoch is pinned by exactly this many of /// them: `CLOCK_TAI` on Linux is `CLOCK_REALTIME` plus this offset, and the /// library's Unix offset is stated on that scale. This module needs the /// number for the return journey, from the atomic scale the library counts /// in to the civil scale the history books use, and takes it once, here, /// to state the Whisper as a Unix timestamp. const TAI_MINUS_UTC_SECONDS: i64 = 37; /// The Whisper on the Unix scale: 1969-10-30 06:30:41 UTC, which is /// 5,419,759 seconds before the Unix epoch and therefore negative, Unix /// time not having begun yet. The library states the same instant on the /// atomic scale, thirty-seven seconds ahead, as `LOIC_TO_UNIX_OFFSET_SECONDS`, /// because `CLOCK_TAI` is what it reads; this module wants the civil number, /// because the dates in the event table are civil ones, recorded by people /// who did not know what a leap second was and would not have cared. The /// relation between the two is one addition, performed here, once, by the /// compiler. /// /// A civil date is placed on the timeline exactly where `now()` would place /// it on a kernel whose clock read that date with today's offset: its /// distance from this constant in seconds, times the Planck times in a /// second. That is the only arithmetic the calendar has for turning a /// clock's reading into an instant, and the timeline uses it rather than /// inventing a second arithmetic that could one day disagree with the /// first. const WHISPER_UNIX_SECONDS: i64 = -(LOIC_TO_UNIX_OFFSET_SECONDS + TAI_MINUS_UTC_SECONDS); /// A compile-time proof that the civil timestamp of the Whisper, written /// down here as every history of the ARPANET would give it, agrees with the /// library's atomic offset plus the leap seconds it stands on. The two /// arrive by independent roads — one transcribed from the record of the /// first transmission, one derived from `CLOCK_TAI`'s arithmetic — and they /// are meant to meet at −5,419,759. Were they not to, either the workspace /// README's disclosure in section 1.2 or the library's constant would be /// wrong, and the crate declines to build until someone says which; a /// binary whose epoch is in dispute by thirty-seven seconds is not a /// calendar, it is a rumour with a compiler. const _WHISPER_UTC_AGREES_WITH_THE_LIBRARY: () = assert!( WHISPER_UNIX_SECONDS == -5_419_759, "the Whisper's civil second and the library's atomic offset disagree beyond the leap seconds" ); /// Consult the table for 10^`exp`, and answer 1 — the zeroth power, the /// multiplicative identity, the number that changes nothing it touches — /// for any exponent the table does not hold, whether negative or past /// eighteen, rather than a fraction the `u64` could not carry or an error /// the caller could not use. The span label that consults it has already /// shifted its exponent into the range of a unit prefix before asking, and /// the shift, not this function, is where the label's honesty is decided. fn pow10(exp: i32) -> u64 { usize::try_from(exp) .ok() .and_then(|index| POWERS_OF_10.get(index)) .copied() .unwrap_or(1) } /// The cell under a pixel offset: the offset divided by the cell's width, /// clamped at zero from below and then truncated toward it, which for a /// non-negative quotient is the floor, which is the index of the cell the /// pixel lies in, which is the whole of the question. Two floating-point /// operations are committed here, a division and a `max`, and both are /// forgiven: the operands are pixel measurements, a few hundred wide at /// most, which an `f32` carries to a precision no pixel can resolve, and /// the result is delivered into a `usize` the moment it is known. Nothing /// temporal passes through this function. It is a ruler, and a ruler is /// permitted to be made of wood. #[allow( clippy::cast_possible_truncation, clippy::cast_sign_loss, reason = "the floor of a non-negative quotient is the cell under the pixel; the cut selects it" )] fn cell_under(offset: f32, cell_width: f32) -> usize { (offset / cell_width).max(0.0) as usize } /// A window onto the cosmic timeline: the contiguous stretch of /// representable time, from `start` to `end` inclusive, that the bar is /// currently showing. The full view is the whole domain, Creation to /// Annihilation; every narrower view is one segment of some wider one, /// reached by clicking, and the Observatory keeps the wider ones on a stack /// so that the descent can be retraced one step at a time. /// /// A view is a pair of instants and nothing else. Its width, its order of /// magnitude, the position of any instant within it, and the label it /// wears are all derived on demand from the two ends — in 512-bit /// integers, with the one exception that `position_of` confesses to in /// full. Nothing is cached, because nothing here is expensive enough to /// deserve the risk of being stale. #[derive(Clone)] pub struct TimelineView { /// The first instant the view shows, in Planck times since the Whisper: /// negative for everything before 06:30:41 UTC on 30 October 1969, /// which is most of the universe. pub start: I512, /// The last instant the view shows, in Planck times since the Whisper. /// Never less than `start`; the difference between them is the view's /// span, which for the full view does not fit in this type at all and /// is computed unsigned for that reason. pub end: I512, } impl TimelineView { /// Manifest the widest view there is: `start` at Creation, the Big Bang /// as the library locates it some 8.08 × 10^60 Planck times before the /// Whisper, and `end` at Annihilation, the last coordinate 512 signed /// bits can name, a little past 10^103 years hence. Between them lies /// every instant the calendar can represent, which is the same as /// saying every instant, since the calendar does not concede that there /// are others. This is the view the Observatory opens on and the view /// Escape returns it to. pub fn full() -> Self { Self { start: *creation_planck(), end: *annihilation_planck(), } } /// The width of this view in Planck times, furnished as a `U512` because /// the widest view is wider than a signed integer can say. Creation is /// negative, Annihilation is `I512::MAX`, and their difference — the /// library's `total_span`, some 6.7 × 10^153 — exceeds `I512::MAX` by /// exactly the age of the universe, which is not a margin one waves /// away. So the subtraction is performed on the unsigned bit patterns /// and permitted to wrap, and for `end >= start` the wrapped difference /// is the exact width at every zoom, including the one at which the /// signed subtraction would have overflowed and, in a release build, /// said nothing about it. Two's complement is a circle, and a circle has /// no edge to fall off. The test `full_span_agrees_with_the_library` /// holds this function to the library's own span, bit for bit. pub fn span(&self) -> U512 { self.end .cast_unsigned() .wrapping_sub(self.start.cast_unsigned()) } /// The number of decimal digits in the span: `ilog10` plus one, an /// integer stand-in for the view's order of magnitude that costs one /// 512-bit logarithm and no floating-point at all. It is the coin in /// which this module prices everything — which events are legible at /// this zoom, which unit the span label speaks in, whether the /// Observatory will consent to zoom further — because a threshold on a /// digit count is a threshold on a power of ten, and a power of ten is /// the only kind of "about" the calendar is prepared to be. /// /// The full view has 154 digits. A span of zero, which no view has, /// would be reported as one digit rather than as a panic; the guard /// exists because `ilog10` is honest about the logarithm of nothing, /// and this function prefers to be honest about something. pub fn span_digits(&self) -> usize { let span = self.span(); if span == U512_ZERO { 1 } else { usize::try_from(span.ilog10()).map_or(1, |magnitude| magnitude + 1) } } /// Whether this view is the full one: `start` at Creation and `end` at /// Annihilation, both compared exactly, in all 512 bits, because a view /// that is merely enormous is not the same thing as a view that is /// everything. The Observatory asks this to decide whether the words /// beneath the bar's ends read Creation and Annihilation or merely /// Start and End, whether to offer the way back, and whether the /// breadcrumb of fractions has anything yet to say. pub fn is_full(&self) -> bool { self.start == *creation_planck() && self.end == *annihilation_planck() } /// Descend into one segment of this view: divide the span into /// `segment_count` equal parts and make the part numbered /// `segment_index`, counting from zero at `start`, the whole of a new /// view. Each descent narrows the window by the segment count — some /// sixty at the Observatory's width — and the descents compose: forty /// of them at fifty-eight segments apiece carry the full span's 154 /// digits down to about eighty-four, which is the walk the test /// `position_is_exact_at_ends_and_midpoint_at_every_zoom` takes. /// /// The division is integer division on the `U512` span, so the segments /// are equal to the Planck time and the remainder falls past the last /// of them, unshown; a view a few Planck times narrower than the bar /// suggested is a smaller lie than a fractional Planck time would be, /// and the calendar does not deal in those. The new view runs from /// `start + index × segment` to `start + (index + 1) × segment`, /// computed in `I512`, which the remark in the body explains is safe. /// /// # Panics /// /// Panics if `segment_index` does not fit an `i64` — a bar more than /// nine quintillion cells wide, drawn on a monitor that does not exist — /// or if `segment_count` is zero, which is a division by nothing and /// the caller's confession that it drew no bar at all. pub fn zoom_into(&self, segment_index: usize, segment_count: usize) -> Self { let span = self.span(); let segment_span = span / (segment_count as u64).as_::(); // A segment is narrower than the span it divides, the widest span // divided is the full one, and the full span divided by anything // greater than one is below 2^511: so the segment's bit pattern, // reinterpreted as signed, is its value, and the cast neither costs // nor loses anything. Divided by one it would not be, and one // segment is not a division but the view again; no bar asks for it. let segment_span_i = segment_span.cast_signed(); let index = i64::try_from(segment_index).expect("no bar has more cells than an i64 holds"); let new_start = self.start + segment_span_i * index.as_::(); let new_end = new_start + segment_span_i; Self { start: new_start, end: new_end, } } /// Locate an instant within this view as a fraction of the way from /// `start` to `end`: 0.0 at the first instant, 1.0 at the last, and /// `None` for any instant the view does not contain, which is the /// Observatory's cue to draw no caret rather than a caret in the /// margin. The bar multiplies the fraction by its width in pixels and /// places the caret there; the hit test reverses the same arithmetic to /// find out which caret was struck. /// /// # On the Reluctant Use of Floating-Point /// /// This function commits the venial sin of `f64` arithmetic, but only in /// its last line, and only for the conversion of one exact ratio into a /// pixel ratio. The offset of the instant from `start` and the span of /// the view are both computed in 512-bit integers, exactly; both are /// then brought down to sixty-four bits by the same right shift, chosen /// so that the span keeps its leading sixty-four bits and the offset /// loses the same low bits; and only then is one divided by the other. /// The ratio of the shifted quantities is exact to one part in 2^63, /// which is finer than an `f64` can express, so the two conversions and /// the one division are the only floating-point operations performed, /// each faithful to one part in 2^53, and a pixel is one part in a few /// thousand. The underlying temporal values remain in their integer /// sanctity until the very moment we must speak to the GPU in its /// native tongue, and the orders of magnitude discarded at that moment — /// nearly all of them, in the full view — were never going to fit on a /// monitor anyway; we shed no tears. The test /// `position_is_exact_at_ends_and_midpoint_at_every_zoom` checks the /// two ends and the midpoint of forty successive views against 0, 1 and /// one half. pub fn position_of(&self, planck_time: &I512) -> Option { if planck_time < &self.start || planck_time > &self.end { return None; } // `start <= planck_time` has just been established, so the wrapped // unsigned difference is the exact offset — even in the full view, // where the signed difference between an instant near Annihilation // and Creation does not fit an I512 and a checked subtraction would // have nothing honest to say. The same circle as `span`, walked for // the same reason. let offset = planck_time .cast_unsigned() .wrapping_sub(self.start.cast_unsigned()); let span = self.span(); if span == U512_ZERO { return Some(0.0); } // Both quantities are brought down to 64 bits by the same shift: the // span's bit length less sixty-four, saturating at zero for spans // that already fit, so the span keeps its leading 64 bits and the // offset — never the larger — loses the same low bits. Their ratio // is then exact to one part in 2^63, which is more than an f64 can // hold, and the division below is the only floating-point operation // performed on the way to the pixel. The clamp is a courtesy to the // rounding, not a correction of the arithmetic: the shifted offset // cannot exceed the shifted span, so the quotient cannot exceed one. let shift = (U512::BITS - span.leading_zeros()).saturating_sub(64); let offset_top = (offset >> shift).as_::(); let span_top = (span >> shift).as_::(); Some((offset_top as f64 / span_top as f64).clamp(0.0, 1.0)) } /// Pronounce the span of this view in a unit a human might recognise — /// "~minutes", "~billions to trillions of years", "~10^-33 seconds" — /// prefixed with a tilde, because the label is chosen by the span's /// digit count and a digit count is honest only to within a factor of /// ten. The Observatory prints it beneath the zoomed bar, centred, in /// cyan. It is the one place the calendar utters the word "minutes", /// and it does so under protest, with the tilde as its disclaimer. /// /// The digit-count thresholds are derived from the Planck time constant /// and from nothing else: /// - `planck_per_second` = 10^50 / 5391247 = 1.855…×10^43 (44 digits) /// - `planck_per_minute` = `planck_per_second` × 60 (46 digits) /// - `planck_per_hour` = `planck_per_second` × 3600 (47 digits; the arm /// below takes 47 and 48 together, hours to days) /// - `planck_per_day` = `planck_per_second` × 86400 (49 digits) /// - `planck_per_year` = `planck_per_second` × 31557600 (51 digits) /// /// Therefore `span_digits` ≈ 44 + log10(seconds) for any span of a /// second or more, and for a sub-second span the exponent of the seconds /// is `span_digits` − 44, from which a prefix — micro, nano, pico, /// femto, atto — is chosen by range and a power of ten from the table /// is printed before it. Below 10^-30 seconds the function gives up on /// prefixes and prints the exponent; below 10^10 Planck times it gives /// up on seconds altogether and prints Planck times, which is the unit /// the calendar wanted to use from the start. pub fn format_span(&self) -> String { let digits = self.span_digits(); // The thresholds, from `derive_constants.sh` at the workspace root. // `planck_per_second` has 44 digits, so a span of: // 44 digits is about a second; // 46 digits about a minute, sixty of those; // 47 to 48 digits an hour to a day; // 49 to 50 digits days to months, a range the calendar labels // "days to years" so as not to say the other word; // 51 digits about a year, the Julian one; // 60 digits about a billion years. // The arms below are these numbers and nothing cleverer. match digits { 0..=10 => format!("~10^{digits} Planck times"), 11..=43 => { // The sub-second range: 11 to 43 digits is 10^-33 to 10^-1 // seconds, and the exponent of the seconds is the digit // count less the forty-four digits of one second. let seconds_exp = i32::try_from(digits).expect("a digit count beyond an i32") - 44; if seconds_exp < -30 { format!("~10^{seconds_exp} seconds") } else if seconds_exp < -15 { // Attoseconds, 10^-18 s: eighteen is added to the // exponent to name the count of them. format!("~{} attoseconds", pow10(seconds_exp + 18)) } else if seconds_exp < -9 { // Femtoseconds, 10^-15 s: fifteen added. format!("~{} femtoseconds", pow10(seconds_exp + 15)) } else if seconds_exp < -6 { // Picoseconds, 10^-12 s: twelve added. format!("~{} picoseconds", pow10(seconds_exp + 12)) } else if seconds_exp < -3 { // Nanoseconds, 10^-9 s: nine added — the kernel's own // unit, and the last one it can vouch for. format!("~{} nanoseconds", pow10(seconds_exp + 9)) } else { // Microseconds, 10^-6 s: six added. format!("~{} microseconds", pow10(seconds_exp + 6)) } } 44..=45 => "~seconds".to_string(), 46 => "~minutes".to_string(), 47..=48 => "~hours to days".to_string(), 49..=50 => "~days to years".to_string(), 51..=53 => "~years to millennia".to_string(), 54..=59 => "~millions to billions of years".to_string(), 60..=70 => "~billions to trillions of years".to_string(), _ => format!("~10^{} years", digits - 51), } } } /// One event of cosmic history as the timeline knows it: a name for the /// list beneath the bar, a coordinate for the caret upon it, an identifier /// for the passage that opens when the caret is clicked, and a threshold /// that says how far in the observer must have zoomed to be shown it at /// all. The table of them is `cosmic_events`. The events themselves are not /// invented here, only placed, and the placing is done in the calendar's /// own integers from the numbers the passages in `events` give. pub struct CosmicEvent { /// The identifier that ties this marker to its passage — the same /// `EventId` that `events::get_passage` answers to, so that a caret and /// the text behind it cannot come apart. pub id: EventId, /// The short name printed in the list beneath the bar, in amber, behind /// a dash, at most eight of them at once. pub name: &'static str, /// The event's coordinate, in Planck times since the Whisper: negative /// for everything that happened before 06:30:41 UTC on 30 October 1969, /// which is nearly all of it, and positive for the handful of things /// that have happened since and the many that have not happened yet. pub planck_time: I512, /// The widest view, measured in digits of its span, that shows this /// event: it appears when `view.span_digits() <= max_span_digits` and /// is withheld from anything wider. A small number is an event that /// demands a close look; a large one is visible from far off. The /// numbers are tiered by hand so that no view, at any zoom, lists more /// events than the panel beneath the bar has lines, and a test holds /// them to it. pub max_span_digits: usize, } /// The most events any view lists, and the number of lines the panel /// beneath the bar has for listing them: eight. The thresholds in /// `cosmic_events` are tiered so that no view, at any zoom, has more to say /// than it has lines to say it in, and the test /// `no_view_lists_more_events_than_the_panel_holds` slides a window of /// every width from 10^1 to 10^154 Planck times along the chronology to /// make sure. The Observatory takes the first `EVENT_LINES` of whatever is /// visible, which the thresholds guarantee is all of it; the take is a /// statement, not a truncation. pub const EVENT_LINES: usize = 8; /// The chronology: every event the timeline can show, from the end of the /// Planck epoch to the evaporation of the last black hole, in the order it /// happened or is expected to, each placed in Planck times since the /// Whisper by integer arithmetic on the numbers its passage gives. Built /// on first request and kept for the life of the process by /// `visible_events`, the universe's past not being expected to change /// between frames. /// /// # Integer Arithmetic for Temporal Constants /// /// Every conversion herein is performed in `I512`, and the seductive /// imprecision of floating-point is not merely avoided but never offered. /// A time given in scientific notation — 10^-36 seconds, say — is split /// into a mantissa and an exponent; the mantissa is multiplied by the /// Planck times in a second, and the product is scaled by a power of ten /// from the table, multiplying for a positive exponent and dividing for a /// negative one. Years are multiplied by the Planck times in a Julian year. /// Dates given as "so many years ago" are restated from the Whisper, which /// is the present as far as the calendar is concerned; the helper /// `years_before_present_to_planck` says why that matters. Civil dates — /// the Julian calendar, ENIAC, the genesis block — are placed by their Unix /// timestamps through the same offset the library applies to the kernel's /// clock, so that a marker that names a day sits on that day, and the test /// `civil_dates_land_on_their_days` holds it there. /// /// # On the Thresholds /// /// An event's `max_span_digits` says how far in you must have zoomed to be /// shown it. The numbers are tiered by hand so that the whole past fits its /// eight lines at the widest views (the first stars, the Solar System, the /// Sun's death) and the rest surface as the view narrows, each in the /// company of its own era: a window a billion years wide lists the Snowball /// and the Cambrian, a window seventeen years wide lists the Whisper and /// the Unix epoch. The test `no_view_lists_more_events_than_the_panel_holds` /// is the proof that the tiers hold, and the reason the numbers are what /// they are and not rounder. /// /// # Panics /// /// Panics if either decimal reliquary at the top of this module fails to /// parse as an `I512`, which is to say if someone has edited forty-four or /// fifty-one digits and introduced a letter; the table is built once, so /// the panic would arrive with the first frame and not later. No entry in /// the table can panic on its own: every exponent handed to the helpers is /// a small literal, far inside the ranges their conversions guard. #[allow( clippy::too_many_lines, reason = "a table of sixty events and their derivations is one table; cut into pieces it would stop reading as the chronology it is" )] pub fn cosmic_events() -> Vec { let planck_per_second = I512::from_str(PLANCK_PER_SECOND_DECIMAL).unwrap(); let planck_per_year = I512::from_str(PLANCK_PER_YEAR_DECIMAL).unwrap(); // The present, as the table understands it: the age of the universe in // Julian years at the Whisper, 13,806,491,023. Read the digits — all // eleven — and you have read the Boltzmann constant, 1.380649 × 10⁻²³ // J/K, mantissa then exponent, straight through with the sign dropped: // the exchange rate between information and heat, buried in the age of // everything (workspace README, section 2.1). The library carries it as // `AGE_UNIVERSE_JULIAN_YEARS`; the derivation is `derive_constants.sh` // at the workspace root. Every "years ago" in this table is subtracted // from this number, not from the year you are reading it in. let present_years: i64 = 13_806_491_023; // Seconds after the Big Bang, given in scientific notation as // mantissa × 10^exponent, to Planck times since the Whisper: the first // instants of the universe are too brief for any other notation and too // sacred for any other arithmetic. Integer throughout. let seconds_sci_to_planck = |mantissa: i64, exponent: i32| -> I512 { // The product is reassociated so that the scaling by the power of // ten comes last, on the largest operand: // planck = seconds × planck_per_second // = mantissa × 10^exp × planck_per_second // = mantissa × planck_per_second × 10^exp // For the sub-second times this helper exists for, exp is negative // and the last step is a division by 10^(−exp) of a number already // forty-odd digits wide, which is why the multiplication precedes // it: dividing first would leave nothing to multiply. let base = planck_per_second * (mantissa).as_::(); let ten = 10i64.as_::(); // A positive exponent multiplies, a negative one divides; the table // reaches 10^18, and anything beyond it — the Grand Unification // epoch at 10^-36 seconds, the end of inflation at 10^-32 — is // applied a power at a time by a fold, thirty-odd divisions by ten // standing in for the one division by a number the table declines // to hold. let planck_offset = if let Ok(exp) = usize::try_from(exponent) { POWERS_OF_10.get(exp).map_or_else( || (0..exp).fold(base, |acc, _| acc * ten), |&power| base * power.as_::(), ) } else { let exp = usize::try_from(-exponent).expect("a negated negative exponent is positive"); POWERS_OF_10.get(exp).map_or_else( || (0..exp).fold(base, |acc, _| acc / ten), |&power| base / power.as_::(), ) }; creation_planck() + planck_offset }; // Whole seconds after the Big Bang to Planck times since the Whisper: // one multiplication, exact, which is the reason the second was // transcribed to forty-four digits rather than approximated to two. let seconds_to_planck = |seconds: i64| -> I512 { creation_planck() + planck_per_second * (seconds).as_::() }; // Whole Julian years after the Big Bang, likewise: the year's Planck // count, all fifty-one digits of it, times the count of years. let years_to_planck = |years: i64| -> I512 { creation_planck() + planck_per_year * (years).as_::() }; // Years after the Big Bang with a magnitude attached: mantissa × 10^exp, // the ten applied one power at a time in `I512`, so that // `years_exp_to_planck(1, 14)` is 10^14 years after the Big Bang — the // death of the last stars — and `years_exp_to_planck(1, 100)` is the // googol at which the last black holes go, a count of years no `i64` // could have carried to this closure. let years_exp_to_planck = |mantissa: i64, exponent: u32| -> I512 { let mut years = (mantissa).as_::(); for _ in 0..exponent { years *= (10i64).as_::(); } *creation_planck() + planck_per_year * years }; // Years before the Whisper to Planck times. "Present" is the epoch, // 1969, not the year you are reading this in: the calendar has exactly // one present and it has already happened. A date given as "N years // ago" from a later vantage must be restated from the Whisper's; for // anything older than a million years the restatement is invisible, // and for anything younger it is not, which is why the civil dates // below do not pass through this helper at all. let years_before_present_to_planck = |years_ago: i64| -> I512 { years_to_planck(present_years - years_ago) }; // Years after the Whisper to Planck times: the same arithmetic with the // sign reversed, for the future the calendar has already scheduled. let years_after_present_to_planck = |years_future: i64| -> I512 { years_to_planck(present_years + years_future) }; // Years before the present in scientific notation, mantissa × 10^exp // years ago, for the ages of the Earth and its early life, which are // known to two or three figures and given here as exactly that many. let years_ago_exp_to_planck = |mantissa: i64, exponent: u32| -> I512 { let mut years_ago = (mantissa).as_::(); for _ in 0..exponent { years_ago *= (10i64).as_::(); } *creation_planck() + planck_per_year * ((present_years).as_::() - years_ago) }; // Years after the present in scientific notation, mantissa × 10^exp // years hence: the oceans, Andromeda, the red giant, given to the // figures the models share and not one more. let years_future_exp_to_planck = |mantissa: i64, exponent: u32| -> I512 { let mut years_future = (mantissa).as_::(); for _ in 0..exponent { years_future *= (10i64).as_::(); } *creation_planck() + planck_per_year * ((present_years).as_::() + years_future) }; // A civil instant, given as its Unix timestamp in UTC, placed from the // Whisper by the calendar's own arithmetic — the timestamp's distance // from `WHISPER_UNIX_SECONDS`, in seconds, times the Planck times in a // second — which is exactly what `now()` does to the kernel's clock, // and so exactly where `now()` would have placed the moment had it been // running then. Exact to the second, as the dates in the passages are; // a marker that names a day sits on that day, and one that names a // second sits on the second. let civil_to_planck = |unix_seconds: i64| -> I512 { planck_per_second * (unix_seconds - WHISPER_UNIX_SECONDS).as_::() }; vec![ // ════════════════════════════════════════════════════════════════════ // THE PLANCK ERA: the first 10^-43 seconds, one Planck time, the unit the calendar counts in // ════════════════════════════════════════════════════════════════════ CosmicEvent { id: EventId::PlanckEpochEnds, name: "Planck Epoch ends", planck_time: *creation_planck() + (1i64).as_::(), max_span_digits: 5, }, // ════════════════════════════════════════════════════════════════════ // THE GRAND UNIFICATION ERA: 10^-43 to 10^-36 seconds, three forces still on speaking terms // ════════════════════════════════════════════════════════════════════ CosmicEvent { id: EventId::GrandUnificationEpoch, name: "Grand Unification Epoch", planck_time: seconds_sci_to_planck(1, -36), // 1e-36 seconds max_span_digits: 15, }, // ════════════════════════════════════════════════════════════════════ // THE ELECTROWEAK ERA: 10^-36 to 10^-12 seconds, inflation, and the Higgs field turning on // ════════════════════════════════════════════════════════════════════ CosmicEvent { id: EventId::InflationEnds, name: "Inflation ends", planck_time: seconds_sci_to_planck(1, -32), // 1e-32 seconds max_span_digits: 20, }, CosmicEvent { id: EventId::ElectroweakEpoch, name: "Electroweak Epoch", planck_time: seconds_sci_to_planck(1, -12), // 1e-12 seconds max_span_digits: 35, }, // ════════════════════════════════════════════════════════════════════ // THE QUARK ERA: 10^-12 to 10^-5 seconds, a plasma in which nothing has yet been confined // ════════════════════════════════════════════════════════════════════ CosmicEvent { id: EventId::QuarkEpoch, name: "Quark Epoch", planck_time: seconds_sci_to_planck(1, -6), // 1e-6 seconds max_span_digits: 40, }, // ════════════════════════════════════════════════════════════════════ // THE HADRON ERA: 10^-5 seconds to one second, the quarks confined and the antimatter gone // ════════════════════════════════════════════════════════════════════ CosmicEvent { id: EventId::HadronEpoch, name: "Hadron Epoch", planck_time: seconds_sci_to_planck(1, -4), // 1e-4 seconds max_span_digits: 42, }, CosmicEvent { id: EventId::NeutrinoDecoupling, name: "Neutrino decoupling", planck_time: seconds_to_planck(1), // 1 second max_span_digits: 46, }, // ════════════════════════════════════════════════════════════════════ // NUCLEOSYNTHESIS: one second to twenty minutes, the whole universe run as one reactor // ════════════════════════════════════════════════════════════════════ CosmicEvent { id: EventId::NucleosynthesisBegins, name: "Nucleosynthesis begins", planck_time: seconds_to_planck(10), // 10 seconds max_span_digits: 47, }, CosmicEvent { id: EventId::NucleosynthesisEnds, name: "Nucleosynthesis ends", planck_time: seconds_to_planck(1200), // 20 minutes max_span_digits: 49, }, // ════════════════════════════════════════════════════════════════════ // THE PHOTON ERA: twenty minutes to 380,000 years, opaque, cooling, and nothing scheduled // ════════════════════════════════════════════════════════════════════ CosmicEvent { id: EventId::CoolerThanTheSunsCore, name: "Cooler than the Sun's core", // In the radiation era the temperature falls as the inverse // square root of the age, T ≈ 1.3 × 10^10 K / sqrt(t / 1 s); the // Sun's core, 15.7 million K, is crossed at ~7 × 10^5 s, and the // marker sits there, a week and a day in. planck_time: seconds_to_planck(700_000), // ~8 days max_span_digits: 51, }, CosmicEvent { id: EventId::MatterRadiationEquality, name: "Matter-radiation equality", planck_time: years_to_planck(47_000), // 47,000 years max_span_digits: 56, }, CosmicEvent { id: EventId::Recombination, name: "Recombination (CMB)", planck_time: years_to_planck(380_000), // 380,000 years max_span_digits: 58, }, // ════════════════════════════════════════════════════════════════════ // THE DARK AGES: 380,000 to some 200 million years, and not one photon that was new // ════════════════════════════════════════════════════════════════════ CosmicEvent { id: EventId::DarkAgesBegin, name: "Dark Ages begin", planck_time: years_to_planck(380_000), // 380,000 years max_span_digits: 60, }, // ════════════════════════════════════════════════════════════════════ // COSMIC DAWN: some 200 to 500 million years, the first stars and the first galaxies // ════════════════════════════════════════════════════════════════════ CosmicEvent { id: EventId::FirstStarsIgnite, name: "First stars ignite", planck_time: years_to_planck(200_000_000), // 200 million years max_span_digits: 63, }, CosmicEvent { id: EventId::FirstGalaxiesForm, name: "First galaxies form", planck_time: years_to_planck(300_000_000), // 300 million years max_span_digits: 60, }, // ════════════════════════════════════════════════════════════════════ // THE STELLIFEROUS ERA: the age of stars, from reionization to the noon and the afternoon // ════════════════════════════════════════════════════════════════════ CosmicEvent { id: EventId::ReionizationComplete, name: "Reionization complete", planck_time: years_to_planck(1_000_000_000), // 1 billion years max_span_digits: 61, }, CosmicEvent { id: EventId::CosmicNoon, name: "Cosmic noon", planck_time: years_to_planck(3_500_000_000), // z ≈ 2, 3.5 billion years max_span_digits: 62, }, CosmicEvent { id: EventId::ExpansionAccelerates, name: "Expansion begins to accelerate", planck_time: years_to_planck(7_500_000_000), // z ≈ 0.7, ~6 billion years ago max_span_digits: 62, }, // ════════════════════════════════════════════════════════════════════ // THE SOLAR SYSTEM: some 9.2 billion years after the Big Bang, an arm, a cloud, a star // ════════════════════════════════════════════════════════════════════ CosmicEvent { id: EventId::SolarSystemForms, name: "Solar System forms", planck_time: years_ago_exp_to_planck(46, 8), // 4.6 billion years ago max_span_digits: 63, }, CosmicEvent { id: EventId::EarthForms, name: "Earth forms", planck_time: years_ago_exp_to_planck(454, 7), // 4.54 billion years ago max_span_digits: 61, }, CosmicEvent { id: EventId::MoonForms, name: "Moon-forming impact", planck_time: years_ago_exp_to_planck(45, 8), // 4.5 billion years ago max_span_digits: 60, }, // ════════════════════════════════════════════════════════════════════ // LIFE ON EARTH: from a chemistry nobody understands to the extinctions that made room // ════════════════════════════════════════════════════════════════════ CosmicEvent { id: EventId::FirstLife, name: "First life emerges", planck_time: years_ago_exp_to_planck(38, 8), // 3.8 billion years ago max_span_digits: 62, }, CosmicEvent { id: EventId::GreatOxidation, name: "Great Oxidation Event", planck_time: years_ago_exp_to_planck(243, 7), // 2.43 billion years ago max_span_digits: 60, }, CosmicEvent { id: EventId::FirstEukaryotes, name: "First eukaryotes", planck_time: years_ago_exp_to_planck(165, 7), // 1.65 billion years ago max_span_digits: 60, }, CosmicEvent { id: EventId::SnowballEarth, name: "Snowball Earth", planck_time: years_before_present_to_planck(717_000_000), // Sturtian onset max_span_digits: 60, }, CosmicEvent { id: EventId::CambrianExplosion, name: "Cambrian Explosion", planck_time: years_before_present_to_planck(539_000_000), // 538.8 million years ago max_span_digits: 60, }, CosmicEvent { id: EventId::PermianExtinction, name: "The Great Dying (Permian)", planck_time: years_before_present_to_planck(252_000_000), // 251.9 million years ago max_span_digits: 60, }, CosmicEvent { id: EventId::DinosaurExtinction, name: "Dinosaur extinction", planck_time: years_before_present_to_planck(66_000_000), // 66 million years ago max_span_digits: 61, }, // ════════════════════════════════════════════════════════════════════ // HUMAN HISTORY: three hundred thousand years, the last two percent of them written down // ════════════════════════════════════════════════════════════════════ CosmicEvent { id: EventId::HomoSapiens, name: "Homo sapiens", planck_time: years_before_present_to_planck(300_000), // Jebel Irhoud max_span_digits: 59, }, CosmicEvent { id: EventId::FirstFarmers, name: "First farmers", planck_time: years_before_present_to_planck(11_500), // Fertile Crescent max_span_digits: 57, }, CosmicEvent { id: EventId::FirstWriting, name: "First writing", planck_time: years_before_present_to_planck(5400), // 5400 years ago max_span_digits: 56, }, CosmicEvent { id: EventId::JulianCalendar, name: "Julian calendar", planck_time: civil_to_planck(-63_555_926_400), // 1 January 45 BC (Julian) max_span_digits: 55, }, CosmicEvent { id: EventId::LeibnizBinary, name: "Leibniz explains binary", planck_time: civil_to_planck(-8_417_952_000), // April 1703 max_span_digits: 54, }, CosmicEvent { id: EventId::PlanckUnits, name: "Planck units proposed", planck_time: civil_to_planck(-2_228_688_000), // 18 May 1899 max_span_digits: 53, }, CosmicEvent { id: EventId::FirstComputer, name: "First electronic computer", planck_time: civil_to_planck(-759_283_200), // 10 December 1945, ENIAC's first run max_span_digits: 52, }, CosmicEvent { id: EventId::FirstTransistor, name: "First transistor", planck_time: civil_to_planck(-695_692_800), // 16 December 1947 max_span_digits: 52, }, // ════════════════════════════════════════════════════════════════════ // THE WHISPER AND BEYOND: around epoch zero, where the count changes sign // ════════════════════════════════════════════════════════════════════ CosmicEvent { id: EventId::ArpanetWhisper, name: "The Whisper (ARPANET)", planck_time: I512_ZERO, // the epoch itself: zero, by definition and by decree max_span_digits: 54, }, CosmicEvent { id: EventId::UnixEpoch, name: "Unix epoch", planck_time: civil_to_planck(0), // 1 January 1970 00:00:00 UTC max_span_digits: 52, }, CosmicEvent { id: EventId::FirstEmail, name: "First email", planck_time: years_after_present_to_planck(2), // late 1971; the month is not recorded max_span_digits: 52, }, CosmicEvent { id: EventId::FirstLeapSecond, name: "First leap second", planck_time: civil_to_planck(78_796_800), // 1972-06-30 23:59:60 ends here max_span_digits: 52, }, CosmicEvent { id: EventId::FirstWebsite, name: "First website", planck_time: civil_to_planck(681_436_800), // 6 August 1991 max_span_digits: 52, }, CosmicEvent { id: EventId::FirstSmartphone, name: "iPhone released", planck_time: civil_to_planck(1_183_075_200), // 29 June 2007 max_span_digits: 52, }, CosmicEvent { id: EventId::FirstBitcoinBlock, name: "Bitcoin genesis block", planck_time: civil_to_planck(1_231_006_505), // 3 January 2009 18:15:05 UTC max_span_digits: 52, }, CosmicEvent { id: EventId::UnixTimeOverflows, name: "32-bit time overflows", planck_time: civil_to_planck(2_147_483_648), // 19 January 2038 03:14:08 UTC max_span_digits: 52, }, // ════════════════════════════════════════════════════════════════════ // THE FAR FUTURE: Voyager, the continents, the oceans, Andromeda, the Sun, the last stars // ════════════════════════════════════════════════════════════════════ CosmicEvent { id: EventId::VoyagerPassesGliese445, name: "Voyager 1 passes Gliese 445", planck_time: years_after_present_to_planck(40_000), // ~40,000 years from now max_span_digits: 57, }, CosmicEvent { id: EventId::PangaeaProxima, name: "Pangaea Proxima", planck_time: years_future_exp_to_planck(25, 7), // 250 million years from now max_span_digits: 60, }, CosmicEvent { id: EventId::OceansEvaporate, name: "Oceans evaporate", planck_time: years_future_exp_to_planck(1, 9), // ~1 billion years from now max_span_digits: 60, }, CosmicEvent { id: EventId::AndromedaArrives, name: "Andromeda arrives (even odds)", planck_time: years_future_exp_to_planck(45, 8), // 4.5 billion years from now max_span_digits: 62, }, CosmicEvent { id: EventId::SunRedGiant, name: "Sun becomes red giant", planck_time: years_future_exp_to_planck(5, 9), // 5 billion years from now max_span_digits: 65, }, CosmicEvent { id: EventId::LocalGroupAlone, name: "Local Group alone", planck_time: years_exp_to_planck(1, 11), // 10^11 years after Big Bang max_span_digits: 64, }, CosmicEvent { id: EventId::LastStarsDie, name: "Last stars die", planck_time: years_exp_to_planck(1, 14), // 10^14 years after Big Bang max_span_digits: 70, }, // ════════════════════════════════════════════════════════════════════ // THE DEGENERATE ERA: the remnants cooling, the planets stripped, the galaxies unbound // ════════════════════════════════════════════════════════════════════ CosmicEvent { id: EventId::DegenerateEra, name: "Degenerate Era", planck_time: years_exp_to_planck(1, 15), // 10^15 years max_span_digits: 75, }, CosmicEvent { id: EventId::GalaxiesDisperse, name: "Galaxies disperse", planck_time: years_exp_to_planck(1, 19), // 10^19 years max_span_digits: 80, }, // ════════════════════════════════════════════════════════════════════ // THE BLACK HOLE ERA: if the protons decay, only holes are left; if not, the rocks go round // ════════════════════════════════════════════════════════════════════ CosmicEvent { id: EventId::ProtonDecayComplete, name: "Proton decay (if any) complete", planck_time: years_exp_to_planck(1, 40), // 10^40 years max_span_digits: 100, }, CosmicEvent { id: EventId::BlackHoleEraBegins, name: "Black Hole Era begins", planck_time: years_exp_to_planck(1, 41), // 10^41 years (after proton decay) max_span_digits: 102, }, CosmicEvent { id: EventId::RocksBecomeSpheres, name: "Rocks flow into spheres", planck_time: years_exp_to_planck(1, 65), // 10^65 years, if protons persist max_span_digits: 118, }, // ════════════════════════════════════════════════════════════════════ // THE DARK ERA: the holes evaporate, the dwarfs leak away, the bits run out // ════════════════════════════════════════════════════════════════════ CosmicEvent { id: EventId::StellarBlackHolesEvaporate, name: "Stellar black holes evaporate", planck_time: years_exp_to_planck(1, 67), // 10^67 years max_span_digits: 120, }, CosmicEvent { id: EventId::WhiteDwarfsErode, name: "White dwarfs erode away", planck_time: years_exp_to_planck(1, 78), // 10^78 years max_span_digits: 130, }, CosmicEvent { id: EventId::SagittariusAEvaporates, name: "Sagittarius A* evaporates", planck_time: years_exp_to_planck(1, 87), // ~10^87 years at its present mass max_span_digits: 140, }, CosmicEvent { id: EventId::SupermassiveBlackHolesEvaporate, name: "Supermassive black holes evaporate", planck_time: years_exp_to_planck(1, 100), // 10^100 years max_span_digits: 150, }, ] } /// The events this view is entitled to show: those whose threshold admits /// the view's digit count and whose coordinate lies within its two ends, /// in the table's order, which is chronological, so that the list beneath /// the bar reads in the order things happened. Furnished as references /// into a table built once and kept for the life of the process — the /// Observatory asks this once per frame, the frames are not throttled, and /// the past does not change often enough to justify placing sixty events /// in `I512` again each time. What changes is the view, and the view is /// the only argument. pub fn visible_events(view: &TimelineView) -> Vec<&'static CosmicEvent> { // The table, built on first request and never again: sixty events, // sixty 512-bit placements, one `LazyLock`, and a `'static` lifetime // that is the truth, since the process cannot outlive its own past. use std::sync::LazyLock; static EVENTS: LazyLock> = LazyLock::new(cosmic_events); let span_digits = view.span_digits(); EVENTS .iter() .filter(|event| { // An event is shown when the view has narrowed enough to admit // it — its threshold at or above the span's digit count — and // when it lies between the view's two ends, inclusive. The first // test is about the observer; the second is about the event. span_digits <= event.max_span_digits && event.planck_time >= view.start && event.planck_time <= view.end }) .collect() } /// The outcome of a click on the bar, reduced to the two numbers the zoom /// needs: which segment was struck and how many there were to strike. The /// pair is what `TimelineView::zoom_into` consumes, and what the breadcrumb /// above the bar prints, one-indexed, as the fraction the observer chose. #[derive(Clone, Copy)] pub struct TimelineHit { /// The segment struck, counting from zero at the bar's left end and /// never reaching `segment_count`: the bar's right edge is folded into /// the last segment rather than into a segment that does not exist. pub segment_index: usize, /// How many segments the bar was divided into when it was struck — one /// per glyph cell between the brackets, at the Observatory's width. pub segment_count: usize, } impl TimelineHit { /// Resolve a click into the segment it landed in. `bar_x` is the pixel /// at which the bar's first segment begins, just inside the opening /// bracket; `bar_width` is the width in pixels of all the segments /// together; `click_x` is where the pointer was released; and /// `segment_count` is how many equal segments the caller drew, which /// the Observatory takes to be the number of glyph cells between the /// brackets. A click outside the bar's horizontal extent yields `None` /// — the vertical test belongs to the caller, which knows where the /// bar is and this function does not. Inside it, the offset from the /// bar's start is divided by the width of one segment and floored, and /// the result is clamped to the last segment, so that the bar's right /// edge, which the division would assign to a segment past the end, /// belongs to the segment it borders. /// /// # Panics /// /// In a debug build, panics if `segment_count` is zero, since /// `segment_count - 1` then underflows a `usize`; a release build wraps /// instead and answers segment zero of no segments. A bar with no /// segments has nothing to be clicked, and the caller that drew one has /// already asked a question this function cannot frame. pub fn from_click( bar_x: f32, bar_width: f32, click_x: f32, segment_count: usize, ) -> Option { if click_x < bar_x || click_x > bar_x + bar_width { return None; } let relative_x = click_x - bar_x; let segment_width = bar_width / segment_count as f32; let segment_index = cell_under(relative_x, segment_width); Some(Self { segment_index: segment_index.min(segment_count - 1), segment_count, }) } } #[cfg(test)] mod tests { use super::*; /// The full view knows itself: constructed from Creation and /// Annihilation, it answers `is_full`, which is the least a view can do. #[test] fn test_full_view() { let view = TimelineView::full(); assert!(view.is_full()); } /// One descent into the first of ten segments is no longer the full /// view and is narrower than it — narrower by the factor of ten the /// segment count promised, though the test asks only for narrower. #[test] fn test_zoom_into() { let view = TimelineView::full(); let zoomed = view.zoom_into(0, 10); assert!(!zoomed.is_full()); assert!(zoomed.span() < view.span()); } /// The full view lists nothing: every event on the table demands a /// closer look than 154 digits of span affords. #[test] fn test_event_visibility() { let full = TimelineView::full(); let visible = visible_events(&full); // In the full view, 154 digits wide, nothing is legible: the most // patient event on the table, the last black hole, waits for 150. // The assertion is looser than the fact, and the fact is empty. assert!(visible.is_empty() || visible.len() < 5); } /// The full view's width is the library's own total span, bit for bit, /// even though no I512 can hold the difference of its two ends: the /// wrapped unsigned subtraction here and the unsigned addition in the /// library arrive at the same 154 digits by different roads. #[test] fn full_span_agrees_with_the_library() { use loic::total_span; assert_eq!(TimelineView::full().span(), *total_span()); } /// The position of an instant is exact at both ends and at the midpoint /// of every view, from the full span down forty zoom levels: the ratio /// is taken on the leading bits of both quantities, never on their /// decimal strings, so no zoom level is quietly off by a power of ten, /// and the midpoint — the span halved by a shift and added to the start /// — lands within a millionth of a millionth of one half at every depth. #[test] fn position_is_exact_at_ends_and_midpoint_at_every_zoom() { let mut view = TimelineView::full(); for _ in 0..40 { let half = (view.span() >> 1u32).cast_signed(); let midpoint = view.start + half; assert_eq!(view.position_of(&view.start), Some(0.0)); assert_eq!(view.position_of(&view.end), Some(1.0)); let position = view .position_of(&midpoint) .expect("a midpoint that is not inside its view is not a midpoint"); assert!( (position - 0.5).abs() < 1e-12, "midpoint at {position}, not one half" ); view = view.zoom_into(17, 58); } } /// The table is a chronology: every event is at or after the one before /// it, so the panel's list, which takes the table's order, reads in the /// order things happened. A table that placed the Cambrian after the /// dinosaurs would still render; it would merely be wrong, in the way /// this test exists to refuse. #[test] fn events_are_chronological() { let events = cosmic_events(); for pair in events.windows(2) { assert!( pair[0].planck_time <= pair[1].planck_time, "{} is listed before {} in the table and happens after it in the universe", pair[0].name, pair[1].name ); } } /// The panel lists `EVENT_LINES` events and no more. A view with `d` /// span digits is narrower than 10^d Planck times and lists the events /// whose threshold admits `d`; so for every `d` from 1 to the full /// span's 154, no window of width 10^d may contain more than /// `EVENT_LINES` such events. This is the sliding window over the /// chronology, in the calendar's own integers — offsets from Creation /// as `U512`, widths as exact powers of ten — and it is the reason the /// thresholds are what they are and not rounder. #[test] fn no_view_lists_more_events_than_the_panel_holds() { let events = cosmic_events(); let creation = creation_planck().cast_unsigned(); let ten = 10u8.as_::(); for digits in 1..=154u32 { let width = ten.pow(digits); let level = usize::try_from(digits).expect("one hundred and fifty-four fits a usize"); let offsets: Vec = events .iter() .filter(|event| event.max_span_digits >= level) .map(|event| event.planck_time.cast_unsigned().wrapping_sub(creation)) .collect(); let mut first = 0; for (last, &offset) in offsets.iter().enumerate() { while offset - offsets[first] > width { first += 1; } assert!( last - first < EVENT_LINES, "a {digits}-digit view lists {} events; the panel has {EVENT_LINES} lines", last - first + 1 ); } } } /// Civil dates sit exactly where the calendar's own arithmetic puts /// them: the Unix epoch is 5,419,759 seconds after the Whisper, the /// genesis block is its timestamp plus that, and the transistor, which /// is dated before the epoch, lands twenty-two years before it — /// twenty-one and most of another, which integer division calls /// twenty-one — and not, as "years before present" would have it from /// a later present, somewhere in the nineteenth century. #[test] fn civil_dates_land_on_their_days() { let events = cosmic_events(); let planck_per_second = I512::from_str(PLANCK_PER_SECOND_DECIMAL).unwrap(); let seconds_from_whisper = |id: EventId| -> I512 { let event = events .iter() .find(|event| event.id == id) .expect("an event with a passage and no place in the table"); event.planck_time / planck_per_second }; assert_eq!( seconds_from_whisper(EventId::UnixEpoch), 5_419_759i64.as_::() ); assert_eq!( seconds_from_whisper(EventId::FirstBitcoinBlock), (1_231_006_505i64 + 5_419_759).as_::() ); assert_eq!( seconds_from_whisper(EventId::FirstTransistor), (-695_692_800i64 + 5_419_759).as_::() ); let seconds_per_julian_year = 31_557_600i64.as_::(); assert_eq!( seconds_from_whisper(EventId::FirstTransistor) / seconds_per_julian_year, (-21i64).as_::() ); } }