         if time? and not misc.is_date(time)
             throw Error("time must be a date")
 
-        # If cache_time is nonzero, we will cache the results of this value computation,
-        # so we can use it in the future.
-        cache_time = 0
-        if not time? and @_cache?
-            # There is a cache,
-            # No time was specified, but the cache exists, so we simply move forward
-            # applying patches, starting with the last cached version.
-            value = @_cache.value   # the version that was cached most recently
-            for x in @_patches.slice(@_cache.start, @_patches.length)   # all patches starting with the one for cache
+        # Determine oldest cached value
+        oldest_cached_time = @_cache.oldest_time()  # undefined if nothing cached
+        # If the oldest cached value exists and is at least as old as the requested
+        # point in time, use it as a base.
+        if oldest_cached_time? and (not time? or time - 0 >= oldest_cached_time - 0)
+            # There is something in the cache, and it is at least as far back in time
+            # as the value we want to compute now.
+            cache = @_cache.newest_value_at_most(time)
+            value = cache.value
+            start = cache.start
+            cache_time = cache.time
+            for x in @_patches.slice(cache.start, @_patches.length)   # all patches starting with the cached one
+                if time? and x.time > time
+                    # Done -- no more patches need to be applied
+                    break
                 value = apply_patch(x.patch, value)[0]   # apply patch x to update value to be closer to what we want
-                cache_time = x.patch.time                # also record the time of the last patch we applied.
-            # Now value is the result of applying all patches, and cache_time is the most
-            # recent timestamp of any patch.
+                cache_time = x.time                      # also record the time of the last patch we applied.
+                start += 1
+            if not time? or start - cache.start >= 10
+                # Newest -- or at least 10 patches needed to be applied -- so cache result
+                @_cache.include(cache_time, value, start)
+                @_cache.prune(Math.max(3, Math.min(Math.ceil(30000000/value.length), MAX_PATCHLIST_CACHE_SIZE)))
         else
-            # A time was specified or there is no cache.
-
-            # Find the newest snapshot at a time that is <=time.
+            # Cache is empty or doesn't have anything sufficiently old to be useful.
+            # Find the newest snapshot at a time that is <= time.
             value = '' # default in case no snapshots
             start = 0
             if @_patches.length > 0  # otherwise the [..] notation below has surprising behavior

            # nothing to do
            return
        v = []
        for x in patches
            if x? and (not @_times[x.time - 0] or (x.snapshot? and not @_snapshot_times[x.time - 0]))
                v.push(x)
                @_times[x.time - 0] = true
                if x.snapshot?
                    @_snapshot_times[x.time - 0] = true
                    # WARNING: again, assuming patch times are unique here.
                    @_patches = (y for y in @_patches when y.time - 0 != x.time - 0)
        if @_cache?
            # if any patch introduced is as old as cached result (but
            # newer than latest snapshot),
            # then clear cache, since can't build on it
            for x in v
                if x.time - 0 <= @_cache.time - 0
                    delete @_cache
                    break
        # this is O(n*log(n)) where n is the length of @_patches and patches;
        # better would be an insertion sort which would be O(m*log(n)) where m=patches.length...
        if v.length > 0


    ###
    value: Return the value of the string at the given (optional)
    point in time.  If the optional time is given, only include patches up
    to (and including) the given time; otherwise, return current value.
    ###
    value: (time) =>
        #start_time = new Date()
