Example #1
0
    def test_insert(self):
        h = IndexedHeap()

        inserted1 = h.insert({'key': 'z', 'value': 300})
        self.assertEqual(inserted1, {'key': 'z', 'value': 300, 'index': 0},
            'correctly inserted and returned new item')

        inserted2 = h.insert({'key': 'y', 'value': 200})
        self.assertEqual(inserted2, {'key': 'y', 'value': 200, 'index': 0},
            'correctly inserted and returned new item')
        self.assertEqual(inserted1, {'key': 'z', 'value': 300, 'index': 1},
            'correctly updated existing items')

        inserted3 = h.insert({'key': 'x', 'value': 100})
        self.assertEqual(inserted3, {'key': 'x', 'value': 100, 'index': 0},
            'correctly inserted and returned new item')
        self.assertEqual(inserted2, {'key': 'y', 'value': 200, 'index': 2},
            'correctly inserted and returned item')
        self.assertEqual(inserted1, {'key': 'z', 'value': 300, 'index': 1},
            'correctly updated existing items')
Example #2
0
    def test_insert(self):
        h = IndexedHeap()

        inserted1 = h.insert({'key': 'z', 'value': 300})
        self.assertEqual(inserted1, {
            'key': 'z',
            'value': 300,
            'index': 0
        }, 'correctly inserted and returned new item')

        inserted2 = h.insert({'key': 'y', 'value': 200})
        self.assertEqual(inserted2, {
            'key': 'y',
            'value': 200,
            'index': 0
        }, 'correctly inserted and returned new item')
        self.assertEqual(inserted1, {
            'key': 'z',
            'value': 300,
            'index': 1
        }, 'correctly updated existing items')

        inserted3 = h.insert({'key': 'x', 'value': 100})
        self.assertEqual(inserted3, {
            'key': 'x',
            'value': 100,
            'index': 0
        }, 'correctly inserted and returned new item')
        self.assertEqual(inserted2, {
            'key': 'y',
            'value': 200,
            'index': 2
        }, 'correctly inserted and returned item')
        self.assertEqual(inserted1, {
            'key': 'z',
            'value': 300,
            'index': 1
        }, 'correctly updated existing items')
Example #3
0
class LFUCache(Cache):
    """ Least Frequently Used cache implementation.

    To implement fast least frequently used key retrieval this class uses a heap.

    Args:
        data: dict, data structure containing the items.
        heap: object, instance of LFUHeap
    """
    def __init__(self, max_size):
        Cache.__init__(self, max_size)
        self.data = {}
        self.heap = IndexedHeap()

    def __len__(self):
        """ Returns the number of items in the cache. """
        return len(self.data)

    def write(self, key, value):
        """ Writes the data to the cache.

        When writing, the frequency of the key is also incremented.

        Complexity:

        Args:
            key, the key to write the data under.
            value, the value to store.
        """
        # Evict item if necessare before inserting.
        evicted = None
        if len(self.data) == self.max_size and key not in self.data:
            evicted = self.evict()

        # Write the cache item.
        if key not in self.data:
            item = {'key': 0, 'value': value, '_key': key}
            self.heap.insert(item)
            self.data[key] = item
        else:
            item = self.data[key]
            item['value'] = value

        self.increment_frequency(item)
        return evicted

    def read(self, key):
        """ Reads the value for the given key from the cache.

        The key gets its frequency incremented whenever it is read.

        Raises:
            Exception, when a cache miss occurs.
        """
        if key not in self.data:
            raise Exception('Cache miss for key={key}'.format(key=key))

        item = self.data[key]
        self.increment_frequency(item)

        return item['value']

    def increment_frequency(self, item):
        """ Increments the frequency of the given key.

        Method also normalizes frequencies whenever the values reach the top
        limit for integers in python.

        See: src.heap.IndexedHeap for the way objects are inserted in the heap.

        Complexity: O(n) because of the index lookup.

        Args:
            item: format: {key, _key, value, index} where key is the frequency
                by which items are sorted in the heap.
        """
        index = item['index']
        item = self.heap.remove(index)
        item['key'] += 1
        self.heap.insert(item)

    def evict(self):
        """ Evicts the element with the least usage frequency.

        Complexity: O(log n) because of the heap.

        Returns:
            dict, in case a value had to be evicted. Format {key, value}.
                key: str
                value: anything
            None, in case no eviction takes place
        """
        node = self.heap.extract_min()
        key = node['_key']
        del self.data[key]
        return {'key': node['_key'], 'value': node['value']}
Example #4
0
 def test_remove_from_single_item_heap(self):
     h = IndexedHeap()
     h.insert({'key': 'x', 'value': 1000})
     h.remove(0)
     self.assertEqual(h.data, [], 'empty data in the heap')
Example #5
0
class LFUCache(Cache):
    """ Least Frequently Used cache implementation.

    To implement fast least frequently used key retrieval this class uses a heap.

    Args:
        data: dict, data structure containing the items.
        heap: object, instance of LFUHeap
    """
    def __init__(self, max_size):
        Cache.__init__(self, max_size)
        self.data = {}
        self.heap = IndexedHeap()

    def __len__(self):
        """ Returns the number of items in the cache. """
        return len(self.data)

    def write(self, key, value):
        """ Writes the data to the cache.

        When writing, the frequency of the key is also incremented.

        Complexity:

        Args:
            key, the key to write the data under.
            value, the value to store.
        """
        # Evict item if necessare before inserting.
        evicted = None
        if len(self.data) == self.max_size and key not in self.data:
            evicted = self.evict()

        # Write the cache item.
        if key not in self.data:
            item = {'key': 0, 'value': value, '_key': key}
            self.heap.insert(item)
            self.data[key] = item
        else:
            item = self.data[key]
            item['value'] = value

        self.increment_frequency(item)
        return evicted

    def read(self, key):
        """ Reads the value for the given key from the cache.

        The key gets its frequency incremented whenever it is read.

        Raises:
            Exception, when a cache miss occurs.
        """
        if key not in self.data:
            raise Exception('Cache miss for key={key}'.format(key=key))

        item = self.data[key]
        self.increment_frequency(item)

        return item['value']

    def increment_frequency(self, item):
        """ Increments the frequency of the given key.

        Method also normalizes frequencies whenever the values reach the top
        limit for integers in python.

        See: src.heap.IndexedHeap for the way objects are inserted in the heap.

        Complexity: O(n) because of the index lookup.

        Args:
            item: format: {key, _key, value, index} where key is the frequency
                by which items are sorted in the heap.
        """
        index = item['index']
        item = self.heap.remove(index)
        item['key'] += 1
        self.heap.insert(item)

    def evict(self):
        """ Evicts the element with the least usage frequency.

        Complexity: O(log n) because of the heap.

        Returns:
            dict, in case a value had to be evicted. Format {key, value}.
                key: str
                value: anything
            None, in case no eviction takes place
        """
        node = self.heap.extract_min()
        key = node['_key']
        del self.data[key]
        return {'key': node['_key'], 'value': node['value']}
Example #6
0
 def test_remove_from_single_item_heap(self):
     h = IndexedHeap()
     h.insert({'key': 'x', 'value': 1000})
     h.remove(0)
     self.assertEqual(h.data, [], 'empty data in the heap')