Current section
Files
Jump to
Current section
Files
lib/tagtime.ex
defmodule Tagtime do
@moduledoc """
Implementation of the TagTime universal ping schedule.
https://forum.beeminder.com/t/official-reference-implementation-of-the-tagtime-universal-ping-schedule/4282
"""
alias Tagtime.RNG
# First ping of TagTime
@urping 1_184_097_393
# Initial state for the RNG
@seed 11_193_462
@doc """
Returns the seed (RNG state) and most recent ping <= t.
"""
@spec init(number) :: {integer, integer}
def init(t) do
# Starting with the urping, fast forward to the most recent ping <= t
init(t, @seed, @urping, @seed, @urping)
end
# Walk forward until the first ping is hit
defp init(t, seed, last_ping, _s, _p) when last_ping <= t do
s = seed
p = last_ping
{seed, last_ping} = next_ping(seed, last_ping)
init(t, seed, last_ping, s, p)
end
# Rewinds a step to return the most recent ping <= t
defp init(t, _seed, last_ping, s, p) when last_ping > t do
{s, p}
end
@doc """
Returns new seed and the unix time of the next ping.
`gap` is the average time between pings in seconds, 45 minutes by default.
"""
@spec next_ping(integer, integer, integer) :: {integer, integer}
def next_ping(seed, last_ping, gap \\ 45 * 60) do
{new_seed, generated_number} = RNG.exprand(seed, gap)
# Gap must be at least 1 second out
calculated_gap = max(1, round(generated_number))
{new_seed, last_ping + calculated_gap}
end
end