153 lines
3.9 KiB
Python
153 lines
3.9 KiB
Python
import pathlib
|
|
import sys
|
|
|
|
# Q98 / 2026-10-27: resolve lfu_cache from THIS file's directory and nowhere
|
|
# else. This line used to read
|
|
# sys.path.insert(0, "/root/.hermes/wizard-outputs/beam")
|
|
# which put a HOST directory first on sys.path for the whole process: all eight
|
|
# cells below graded /root/.hermes/wizard-outputs/beam/lfu_cache.py, so a
|
|
# poisoned copy of the committed module still read `8 passed`, and the lane's
|
|
# own `bash wizard-work/beam/CHECK` read rc 1 with
|
|
# ImportError: 'test_lru_cache' module incorrectly imported from
|
|
# '/root/.hermes/wizard-outputs/beam'
|
|
# The assertion below is the part that makes the binding a fact rather than an
|
|
# intention: it is the rule test_toposort.py (Q61) already states.
|
|
sys.path.insert(0, str(pathlib.Path(__file__).resolve().parent))
|
|
|
|
import lfu_cache as lfu_cache_module
|
|
from lfu_cache import LFUCache
|
|
|
|
assert pathlib.Path(lfu_cache_module.__file__).resolve() == \
|
|
pathlib.Path(__file__).resolve().parent / "lfu_cache.py", \
|
|
lfu_cache_module.__file__
|
|
|
|
|
|
def test_basic_get_put():
|
|
c = LFUCache(2)
|
|
c.put(1, 10)
|
|
c.put(2, 20)
|
|
assert c.get(1) == 10
|
|
assert c.get(2) == 20
|
|
assert c.get(3) == -1
|
|
|
|
|
|
def test_evict_least_freq():
|
|
c = LFUCache(2)
|
|
c.put(1, 10)
|
|
c.put(2, 20)
|
|
c.get(1) # freq 1->2
|
|
c.put(3, 30) # evict 2 (freq1)
|
|
assert c.get(2) == -1
|
|
assert c.get(1) == 10
|
|
assert c.get(3) == 30
|
|
|
|
|
|
def test_lru_within_same_freq():
|
|
c = LFUCache(2)
|
|
c.put(1, 10)
|
|
c.put(2, 20)
|
|
# both freq1; touch nothing extra; insert 3 evicts LRU among freq1 -> key1
|
|
c.put(3, 30)
|
|
assert c.get(1) == -1
|
|
assert c.get(2) == 20
|
|
assert c.get(3) == 30
|
|
|
|
|
|
def test_update_existing():
|
|
c = LFUCache(2)
|
|
c.put(1, 10)
|
|
c.put(1, 11)
|
|
assert c.get(1) == 11
|
|
# updating counts as access; freq now 2
|
|
|
|
|
|
def test_capacity_zero():
|
|
c = LFUCache(0)
|
|
c.put(1, 10)
|
|
assert c.get(1) == -1
|
|
|
|
|
|
def test_evict_after_freq_tiebreak():
|
|
c = LFUCache(3)
|
|
c.put(1, 1)
|
|
c.put(2, 2)
|
|
c.put(3, 3)
|
|
c.get(1)
|
|
c.get(2)
|
|
# freq: 1->2, 2->2, 3->1 ; min freq bucket {3}
|
|
c.put(4, 4) # evict 3
|
|
assert c.get(3) == -1
|
|
assert c.get(1) == 1 and c.get(2) == 2 and c.get(4) == 4
|
|
|
|
|
|
def test_negative_capacity_raises():
|
|
try:
|
|
LFUCache(-1)
|
|
assert False
|
|
except ValueError:
|
|
pass
|
|
|
|
|
|
def test_many_ops_stress():
|
|
# brute-force compare against a naive dict-based LFU
|
|
import random
|
|
random.seed(42)
|
|
cap = 5
|
|
c = LFUCache(cap)
|
|
# naive
|
|
store = {}
|
|
freq = {}
|
|
order = {} # key -> insertion/access counter for LRU tiebreak
|
|
counter = [0]
|
|
|
|
def naive_get(k):
|
|
if k not in store:
|
|
return -1
|
|
freq[k] += 1
|
|
counter[0] += 1
|
|
order[k] = counter[0]
|
|
return store[k]
|
|
|
|
def naive_put(k, v):
|
|
nonlocal store
|
|
if cap == 0:
|
|
return
|
|
if k in store:
|
|
store[k] = v
|
|
freq[k] += 1
|
|
counter[0] += 1
|
|
order[k] = counter[0]
|
|
return
|
|
if len(store) >= cap:
|
|
# evict min freq, then min order
|
|
minf = min(freq[x] for x in store)
|
|
candidates = [x for x in store if freq[x] == minf]
|
|
victim = min(candidates, key=lambda x: order[x])
|
|
del store[victim]; del freq[victim]; del order[victim]
|
|
store[k] = v
|
|
freq[k] = 1
|
|
counter[0] += 1
|
|
order[k] = counter[0]
|
|
|
|
ops = 2000
|
|
keys = list(range(1, 12))
|
|
for _ in range(ops):
|
|
k = random.choice(keys)
|
|
if random.random() < 0.5:
|
|
a = c.get(k)
|
|
b = naive_get(k)
|
|
assert a == b, (a, b, k)
|
|
else:
|
|
v = random.randint(0, 99)
|
|
c.put(k, v)
|
|
naive_put(k, v)
|
|
# final consistency: all keys
|
|
for k in keys:
|
|
assert c.get(k) == naive_get(k), k
|
|
|
|
|
|
if __name__ == "__main__":
|
|
for name in [n for n in globals() if n.startswith("test_")]:
|
|
globals()[name]()
|
|
print("ALL TESTS PASSED")
|