private <T, R> void addToActions(StateHistory<TimeStamp, T> history, BiFunction<TimeStamp, T, R> toActionValue, Type type, int address, ArrayList<TimedAction> actions) {
    TimeStamp curTimestamp = TimeStamp.MIN_VALUE;
    while (true) {
      T curValue = history.getValueAt(curTimestamp);
      if (curValue != null)
        throw new RuntimeException("unexpected non-null value " + curValue + " at " + curTimestamp + " in " + history);
      TimeStamp nextTimestamp = history.getNextStateStartTime(curTimestamp);
      if (nextTimestamp == null) // reached end
        break;
      T nextValue = history.getValueAt(nextTimestamp);
      if (nextValue == null)
        throw new RuntimeException("unexpected null value at " + nextTimestamp + " in " + history);
      // If this action spans scenes, put it into the LCD off period before the scene.
      if (nextTimestamp.scene > curTimestamp.scene)
        curTimestamp = new TimeStamp(nextTimestamp.scene, 0, 0);
      long fromCycles = curTimestamp.toCycles();
      actions.add(new TimedAction(curTimestamp.scene, fromCycles, nextTimestamp.toCycles(), new Action<R>(type, toActionValue.apply(nextTimestamp, nextValue), address)));
      curTimestamp = history.getNextStateStartTime(nextTimestamp);
      if (curTimestamp == null) // reached end
        throw new RuntimeException("unexpected end of history after " + nextTimestamp + " value " + nextValue);
    }
  }