```python """ LRU Cache implementation with O(1) get and put operations. Uses a doubly-linked list (via OrderedDict) and a dictionary to achieve O(1) time complexity for both get and put operations. Invariants: 1. The cache never exceeds its fixed positive capacity. 2. On every get(key) that hits, the accessed item is moved to the most-recently-used position. 3. On every put(key, value): - If key exists, its value is updated and it becomes most-recently-used. - If key doesn't exist and cache is at capacity, the least-recently-used item is evicted. - If key doesn't exist and cache is below capacity, the new item is added as most-recently-used. 4. The order in the underlying OrderedDict reflects recency of use: first item is LRU, last is MRU. """ from collections import OrderedDict class LRUCache: """ LRU Cache with O(1) get and put operations. Args: capacity: A fixed positive integer representing the maximum number of items. Raises: ValueError: If capacity is not a positive integer. """ def __init__(self, capacity: int): if not isinstance(capacity, int) or capacity <= 0: raise ValueError("Capacity must be a positive integer") self.capacity = capacity # OrderedDict maintains insertion order; we use move_to_end to track recency self._cache: OrderedDict = OrderedDict() def get(self, key: int) -> int: """ Get the value for the given key. Args: key: The key to look up. Returns: The value if key exists, otherwise -1. """ if key not in self._cache: return -1 # Move to end (most recently used) self._cache.move_to_end(key) return self._cache[key] def put(self, key: int, value: int) -> None: """ Put a key-value pair into the cache. Args: key: The key to insert or update. value: The value to associate with the key. """ if key in self._cache: # Update existing key and move to most recently used self._cache[key] = value self._cache.move_to_end(key) else: # If at capacity, evict least recently used (first item) if len(self._cache) >= self.capacity: # popitem(last=False) removes the first item (LRU) self._cache.popitem(last=False) # Add new item as most recently used self._cache[key] = value # ============================================================================= # Tests # ============================================================================= def test_basic_get_put(): """Test basic get and put operations.""" cache = LRUCache(2) cache.put(1, 1) cache.put(2, 2) assert cache.get(1) == 1 assert cache.get(2) == 2 assert cache.get(3) == -1 # Key not found def test_update_existing_key(): """Test updating an existing key's value.""" cache = LRUCache(2) cache.put(1, 1) cache.put(2, 2) cache.put(1, 10) # Update key 1 assert cache.get(1) == 10 assert cache.get(2) == 2 def test_eviction_order(): """Test that eviction follows LRU order.""" cache = LRUCache(2) cache.put(1, 1) cache.put(2, 2) # Access key 1 to make it most recently used assert cache.get(1) == 1 # Now key 2 is LRU, so adding key 3 should evict key 2 cache.put(3, 3) assert cache.get(2) == -1 # Key 2 was evicted assert cache.get(1) == 1 # Key 1 still present assert cache.get(3) == 3 # Key 3 was added def test_eviction_after_update(): """Test that updating a key affects eviction order.""" cache = LRUCache(3) cache.put(1, 1) cache.put(2, 2) cache.put(3, 3) # Access key 1 to make it most recently used assert cache.get(1) == 1 # Now order of recency: 2 (LRU), 3, 1 (MRU) # Adding key 4 should evict key 2 (LRU) cache.put(4, 4) assert cache.get(2) == -1 # Key 2 was evicted assert cache.get(1) == 1 assert cache.get(3) == 3 assert cache.get(4) == 4 def test_capacity_one(): """Test cache with capacity 1.""" cache = LRUCache(1) cache.put(1, 1) assert cache.get(1) == 1 cache.put(2, 2) # Evicts key 1 assert cache.get(1) == -1 assert cache.get(2) == 2 def test_multiple_updates(): """Test multiple updates to the same key.""" cache = LRUCache(2) cache.put(1, 1) cache.put(1, 2) cache.put(1, 3) assert cache.get(1) == 3 cache.put(2, 4) cache.put(3, 5) # Should evict key 1 (LRU after last access was key 1) assert cache.get(1) == -1 assert cache.get(2) == 4 assert cache.get(3) == 5 def test_invalid_capacity(): """Test that invalid capacity raises ValueError.""" try: LRUCache(0) assert False, "Should have raised ValueError" except ValueError: pass try: LRUCache(-1) assert False, "Should have raised ValueError" except ValueError: pass try: LRUCache(1.5) assert False, "Should have raised ValueError" except ValueError: pass def test_large_sequence(): """Test a longer sequence of operations.""" cache = LRUCache(3) cache.put(1, 1) cache.put(2, 2) cache.put(3, 3) assert cache.get(1) == 1 # 1 is now MRU, order: 2, 3, 1 cache.put(4, 4) # Evicts 2 (LRU), order: 3, 1, 4 assert cache.get(2) == -1 assert cache.get(3) == 3 assert cache.get(4) == 4 cache.put(5, 5) # Evicts 3 (LRU), order: 1, 4, 5 assert cache.get(3) == -1 assert cache.get(1) == 1 assert cache.get(4) == 4 assert cache.get(5) == 5 if __name__ == "__main__": # Run all tests test_basic_get_put() test_update_existing_key() test_eviction_order() test_eviction_after_update() test_capacity_one() test_multiple_updates() test_invalid_capacity() test_large_sequence() print("All tests passed!") ```