Created
May 13, 2024 18:24
-
-
Save mybuddymichael/d974530faead4c9ceb30dd0e6ce2788b to your computer and use it in GitHub Desktop.
TagTime reference implementation
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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