Implement a CreditTracker class that tracks time-bounded credit grants. A credit grant is active only while the current time t satisfies start_time <= t < end_time; the interval is half-open, so the credit is no longer active at end_time. Grants with zero remaining credit do not contribute even if the current time falls inside their interval.
The class must expose these methods:
class CreditTracker:
def add_credit(self, start_time: int, end_time: int, credit: int) -> None:
"""Register a credit that is valid for [start_time, end_time)."""
def subtract_credit(self, time: int, credit: int) -> None:
"""Spend credit at time, starting with the earliest-expiring active credit."""
def check_credit(self, time: int) -> int:
"""Return the total remaining credit active at time."""
subtract_credit must always consume from the active credit whose expiration time is earliest. If that credit is exhausted before the requested total is reached, continue with the next earliest-expiring active credit. If the aggregate active credit at the given time is less than the requested amount, raise ValueError.
Example 1:
tracker = CreditTracker()
tracker.add_credit(0, 10, 50) # 50 credits valid from t=0 to t=10
tracker.add_credit(3, 7, 30) # 30 credits valid from t=3 to t=7
tracker.check_credit(5) # → 80 (both credits valid at t=5)
tracker.subtract_credit(5, 40) # Subtract 40 at t=5
# Credit (3,7,30) expires first → subtract 30 from it (now 0)
# Credit (0,10,50) expires next → subtract remaining 10 from it (now 40)
tracker.check_credit(5) # → 40 (only credit (0,10,40) has remaining)
tracker.check_credit(8) # → 40 (credit (3,7) expired, only (0,10,40) valid)
Explanation: At t = 5, both credits are active, and the grant ending at 7 is consumed before the grant ending at 10.
Constraints:
start_time <= time < end_time.end_time among active credits.ValueError.