Skip to content

Instantly share code, notes, and snippets.

@mybuddymichael
Created May 13, 2024 18:24
Show Gist options
  • Save mybuddymichael/d974530faead4c9ceb30dd0e6ce2788b to your computer and use it in GitHub Desktop.
Save mybuddymichael/d974530faead4c9ceb30dd0e6ce2788b to your computer and use it in GitHub Desktop.
TagTime reference implementation
const GAP = 45 * 60; // Average gap between pings, in seconds
// const URPING = 1184097393; // Ur-ping ie the birth of Timepie/TagTime! (unixtime)
// const SEED = 11193462; // Initial state of the random number generator
const URPING = 1715455233; // Ur-ping ie the birth of Timepie/TagTime! (unixtime)
const SEED = 584340993; // Initial state of the random number generator
const IA = 16807; // =7^5: Multiplier for LCG random number generator
const IM = 2147483647; // =2^31-1: Modulus used for the RNG
let totalPings = 0;
// Above URPING is in 2007 and it's fine to jump to any later URPING/SEED pair
// like this one in 2018 -- URPING = 1532992625, SEED = 75570 -- without
// deviating from the universal ping schedule.
let pung = URPING; // Global var with unixtime (in seconds) of last computed ping
let state = SEED; // Global variable that's the state of the RNG
// Linear Congruential Generator, returns random integer in {1, ..., IM-1}.
// This is ran0 from Numerical Recipes and has a period of ~2 billion.
function lcg() {
return (state = (IA * state) % IM);
} // lcg()/IM is a U(0,1) R.V.
// Return a random number drawn from an exponential distribution with mean m
function exprand(m) {
return -m * Math.log(lcg() / IM);
}
// Every TagTime gap must be an integer number of seconds not less than 1
function gap() {
return Math.max(1, Math.round(exprand(GAP)));
}
// Return unixtime of the next ping. First call init(t) and then call this in
// succession to get all the pings starting with the first one after time t.
function nextping() {
totalPings += 1;
return (pung += gap());
}
// Start at the beginning of time and walk forward till we hit the first ping
// strictly after time t. Then scooch the state back a step and return the first
// ping *before* (or equal to) t. Then we're ready to call nextping().
function init(t: number) {
let p, s; // keep track of the previous values of the global variables
[pung, state] = [URPING, SEED]; // reset the global state
// for (; totalPings < 10_000_000; nextping()) {
for (; pung <= t; nextping()) {
[p, s] = [pung, state];
} // walk forward
[pung, state] = [p, s]; // rewind a step
return [pung, state]; // return most recent ping time <= t
}
const start = Bun.nanoseconds();
const [mostRecentPing, mostRecentState] = init(Math.floor(Date.now() / 1000)); //init(1702059578);
const nxtPings = Array.from({ length: 32 }, () => nextping());
const end = Bun.nanoseconds();
const time = end - start;
console.log('Total ping count: ', totalPings);
console.log(
'Most recent ping: ',
mostRecentPing,
new Date(mostRecentPing * 1000).toString()
);
console.log('Most recent state:', mostRecentState);
console.log('Next pings:');
for (const p of nxtPings) {
console.log(' ', p, new Date(p * 1000).toString());
}
console.log('Run time (ns): ', `${time}ns`);
console.log('Run time (ms): ', `${time / 1000000}ms`);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment