# Path: tapes/reservoir.py
# class ExponentiallyDecayingReservoir(object):
#     def __init__(self, size=_DEFAULT_SIZE, alpha=_DEFAULT_ALPHA):
#         self.size = size
#         self.alpha = alpha
#         self.start_time = time()
#         self.next_scale_time = self.start_time + _RESCALE_THRESHOLD
#         self.values = SortedDict()
# 
#     def _rescale_if_needed(self):
#         now = time()
#         if now > self.next_scale_time:
#             self.next_scale_time = now + _RESCALE_THRESHOLD
#             old_start_time = self.start_time
#             self.start_time = now
#             scaling_factor = exp(-self.alpha * (self.start_time - old_start_time))
# 
#             self.values = SortedDict(
#                 (priority * scaling_factor, _WeightedSample(sample.value, sample.weight * scaling_factor))
#                 for priority, sample in self.values.items()
#             )
# 
#     def update(self, value):
#         self._rescale_if_needed()
#         timestamp = time()
#         item_weight = exp(self.alpha * (timestamp - self.start_time))
#         sample = _WeightedSample(value, item_weight)
#         priority = item_weight / random()
# 
#         if len(self.values) < self.size:
#             self.values[priority] = sample
#         else:
#             first_key = next(self.values.iterkeys())
#             if first_key < priority:
#                 self.values.setdefault(priority, sample)
#                 del self.values[first_key]
# 
#     def get_snapshot(self):
#         return _WeightedSnapshot(self.values.values())


import unittest
import fudge
from tapes.reservoir import ExponentiallyDecayingReservoir
         .expects_call().returns(0.1)
         .next_call().returns(0.2)
         .next_call().returns(0.3)
         .next_call().returns(0.4)
         .next_call().returns(0.01)
         .next_call().returns(0.01))

        reservoir = ExponentiallyDecayingReservoir(size=3)

        reservoir.update(1)
        reservoir.update(2)
        reservoir.update(3)
        reservoir.update(4)
        reservoir.update(5)
        reservoir.update(6)

        snapshot = reservoir.get_snapshot()
        assert float(snapshot.get_min()) > 0
        assert float(snapshot.get_max()) > 0
        assert float(snapshot.get_mean()) > 0
        assert float(snapshot.get_sd()) > 0

    @fudge.patch('tapes.reservoir.time')
    def test_reservoir_gets_rescaled(self, time):
        (time.expects_call().returns(1.0)
         .next_call().returns(2.0)
         .next_call().returns(2.0)
         .next_call().returns(24 * 60 * 60.0)
         .next_call().returns(24 * 60 * 60.0))


# Path: tapes/reservoir.py
# class ExponentiallyDecayingReservoir(object):
#     def __init__(self, size=_DEFAULT_SIZE, alpha=_DEFAULT_ALPHA):
#         self.size = size
#         self.alpha = alpha
#         self.start_time = time()
#         self.next_scale_time = self.start_time + _RESCALE_THRESHOLD
#         self.values = SortedDict()
# 
#     def _rescale_if_needed(self):
#         now = time()
#         if now > self.next_scale_time:
#             self.next_scale_time = now + _RESCALE_THRESHOLD
#             old_start_time = self.start_time
#             self.start_time = now
#             scaling_factor = exp(-self.alpha * (self.start_time - old_start_time))
# 
#             self.values = SortedDict(
#                 (priority * scaling_factor, _WeightedSample(sample.value, sample.weight * scaling_factor))
#                 for priority, sample in self.values.items()
#             )
# 
#     def update(self, value):
#         self._rescale_if_needed()
#         timestamp = time()
#         item_weight = exp(self.alpha * (timestamp - self.start_time))
#         sample = _WeightedSample(value, item_weight)
#         priority = item_weight / random()
# 
#         if len(self.values) < self.size:
#             self.values[priority] = sample
#         else:
#             first_key = next(self.values.iterkeys())
#             if first_key < priority:
#                 self.values.setdefault(priority, sample)
#                 del self.values[first_key]
# 
#     def get_snapshot(self):
#         return _WeightedSnapshot(self.values.values())


# Path: tests/test_exponentially_decaying_reservoir.py
import unittest
import fudge
from tapes.reservoir import ExponentiallyDecayingReservoir

         .expects_call().returns(0.1)
         .next_call().returns(0.2)
         .next_call().returns(0.3)
         .next_call().returns(0.4)
         .next_call().returns(0.01)
         .next_call().returns(0.01))

        reservoir = ExponentiallyDecayingReservoir(size=3)

        reservoir.update(1)
        reservoir.update(2)
        reservoir.update(3)
        reservoir.update(4)
        reservoir.update(5)
        reservoir.update(6)

        snapshot = reservoir.get_snapshot()
        assert float(snapshot.get_min()) > 0
        assert float(snapshot.get_max()) > 0
        assert float(snapshot.get_mean()) > 0
        assert float(snapshot.get_sd()) > 0

    @fudge.patch('tapes.reservoir.time')
    def test_reservoir_gets_rescaled(self, time):
        (time.expects_call().returns(1.0)
         .next_call().returns(2.0)
         .next_call().returns(2.0)
         .next_call().returns(24 * 60 * 60.0)
         .next_call().returns(24 * 60 * 60.0))


        r = ExponentiallyDecayingReservoir()