Given a goal and all the glorious data we get back from the API, which includes slope information for all the pieces of the graph, current amount, UTC deadline, and so on - what’s some pseudocode logic to calculate the amount required at an arbitrary time t?

Has someone already worked on a solution to this?
Is there an implementation of this algorithm I can steal from someone’s repo?

The reason I ask is because I frankly do not understand exactly how the road works. If no one has already done, tested and verified an implementation of this, I’ll have to study exactly how the road works, and that sounds like a heck of a headache, but if I need to do that, I’ll do it…

Do you actually mean time t, or do you mean day? All the datapoints for a day are combined into a single value using the “aggday”, which is something like max, min, last, first… things like that.

I do actually mean time t. That is because this algorithm must take into account the UTC deadline for the goal.

Let’s say I have a eep day, and I have to read 15 pages today.
If the UTC deadline is today at 15:00, and I ask the algorithm how many pages I need to read by 14:00 UTC, it should say 0. If I ask how many pages I need to read by 16:00 UTC, it should say 15. It is obviously possible to figure this out from the data returned by the API.

… as far as I understand, with my knowledge of how the road works.

Find in fullroad the relevant road segment. That is: the last segment whose timestamp is before time t. (A road segment in fullroad is an array of three values, the first one being the timestamp.)

Given that road segment, the first item of the triple, the timestamp, is the base time we’ll calculate from. So we’ll call it t_0. The second item is the goal amount at time t_0, so let’s call it b. The third item of the triple is the rate. (Over the course of a segment, the rate is always linear.) Let’s call this variable r.

Then the height of the road at time t is simply the linear extrapolation: b + (t - t_0) \cdot r.

Note, however, that Beeminder always rounds to the day: the road is treated as rising the whole amount of the rate at the time of day you’ve configured in the settings, as opposed to being continuous the way this formula seems to imply.

The only part I’m not clear on, let me know if it sounds roughly correct to you:

the thing I should change in your algorithm, as I understand it, to have it consider this, is to have a function quantize_t that takes a t and rounds it to an utc timestamp of the previous day’s deadline?

Going back to my previous example:

Again, same conditions, eep day. if t is 14:00, it gets quantized to 23 hours before, at UTC 15:00, and will show a negative number since I didn’t derail yesterday (which I can show as 0). Indeed I don’t have to do any words by 14:00, since the deadline is an hour later. If t is 16:00 UTC, it gets quantized to just an hour before the actual t. It will show 15, which is how many I have to do by that time.

eugeniobruno, Beeminder is currently day-based, even your deadline is a different time than your local midnight–it shifts the “day boundary” to the deadline.

You can use the goal’s deadline field (the number of seconds away from midnight that cutoff is set for) to determine which side of the deadline time t is on, and so to which day to round the calculation.

So the actual formula is more like b + (\lfloor t \rfloor - \lfloor t_0 \rfloor) \cdot r, where \lfloor x \rfloor is defined for our purposes as greatest number less than or equal to x that is of the form m + d such that m is a time that is exactly a midnight, and d is the value of the goal’s deadline field.

Yes, exactly. (And the deadline is stored as an offset in seconds from midnight in the deadline field.)

Almost, but the formula I was discussing was for the height of the road at time t, not for the amount that you have to do by time t. But you can convert between the two by subtracting the value in the curval field, which is the total amount you’ve done on the goal so far.

Wow, that was incredibly easy thanks to the extremely clear and concise explanation. I thought I would never get it done correctly, but now I think I should be able to implement it in like 10 minutes. Thanks!