# Generated from the published Markdown; run scripts/build_language_examples.py to refresh.
# Independent examples may reuse top-level names. Python remains the explanation language.


# topics/maps-sets-hashing/maps.md
class HashMap:
    def __init__(self):
        self._entries = {}

    def __len__(self):
        return len(self._entries)

    def size(self):
        return len(self)

    def is_empty(self):
        return len(self) == 0

    def put(self, key, value):
        self._entries[key] = value

    def get(self, key):
        return self._entries[key]

    def remove(self, key):
        return self._entries.pop(key)

    def contains_key(self, key):
        return key in self._entries

    def keys(self):
        return tuple(self._entries)

    def items(self):
        return tuple(self._entries.items())

# topics/maps-sets-hashing/maps.md
directory = {"S01": "Central", "S02": "Museum"}
directory["S03"] = "Harbour"     # insert a new key
directory["S01"] = "Downtown"    # replace an existing value
name = directory["S02"]          # retrieve: "Museum"
del directory["S03"]             # remove one association
present = "S04" in directory     # False: membership checks keys

# topics/maps-sets-hashing/maps.md
missing = object()
result = directory.get("S04", missing)
if result is missing:
    print("No such station")

# topics/maps-sets-hashing/sets.md
class HashSet:
    def __init__(self, elements=()):
        self._elements = set(elements)

    def __len__(self):
        return len(self._elements)

    def size(self):
        return len(self)

    def is_empty(self):
        return len(self) == 0

    def add(self, item):
        self._elements.add(item)

    def contains(self, item):
        return item in self._elements

    def remove(self, item):
        self._elements.remove(item)

    def discard(self, item):
        self._elements.discard(item)

    def elements(self):
        return tuple(self._elements)

    def union(self, other):
        return HashSet(self._elements | other._elements)

    def intersection(self, other):
        return HashSet(self._elements & other._elements)

    def difference(self, other):
        return HashSet(self._elements - other._elements)

# topics/maps-sets-hashing/sets.md
visited = set()             # {} would create an empty dictionary
visited.add("S01")
visited.add("S02")
visited.add("S01")          # already present
present = "S02" in visited  # True
count = len(visited)        # 2
visited.discard("S03")      # absent: no change

# topics/maps-sets-hashing/sets.md
observations = ["S01", "S02", "S01"]
counts = {}
for station in observations:
    counts[station] = counts.get(station, 0) + 1

# topics/maps-sets-hashing/hashing.md
locations = {(2, 3): "Stop A"}   # tuple of integers: valid key
records = {"S01": [72, 81]}     # list as a value: valid
records["S01"].append(85)       # values need not be hashable

# topics/maps-sets-hashing/chaining.md
def get_chained(table, key):
    i = hash(key) % len(table)
    for stored_key, value in table[i]:
        if stored_key == key:
            return value
    raise KeyError(key)

# topics/maps-sets-hashing/chaining.md
def put_chained(table, key, value):
    bucket = table[hash(key) % len(table)]
    for j, (stored_key, _) in enumerate(bucket):
        if stored_key == key:
            bucket[j] = (key, value)
            return False
    bucket.append((key, value))
    return True

# topics/maps-sets-hashing/chaining.md
def remove_chained(table, key):
    bucket = table[hash(key) % len(table)]
    for j, (stored_key, value) in enumerate(bucket):
        if stored_key == key:
            del bucket[j]
            return value
    raise KeyError(key)

# topics/maps-sets-hashing/chaining.md
table = [[] for _ in range(7)]

# topics/maps-sets-hashing/open-addressing.md
EMPTY = object()
DELETED = object()

def get_probed(table, key):
    m = len(table)
    start = hash(key) % m
    for step in range(m):
        slot = table[(start + step) % m]
        if slot is EMPTY:
            raise KeyError(key)
        if slot is DELETED:
            continue
        stored_key, value = slot
        if stored_key == key:
            return value
    raise KeyError(key)

# topics/maps-sets-hashing/open-addressing.md
def put_probed(table, key, value):
    m = len(table)
    start = hash(key) % m
    first_deleted = None
    for step in range(m):
        i = (start + step) % m
        slot = table[i]
        if slot is EMPTY:
            target = i if first_deleted is None else first_deleted
            table[target] = (key, value)
            return True
        if slot is DELETED:
            if first_deleted is None:
                first_deleted = i
        elif slot[0] == key:
            table[i] = (key, value)
            return False
    if first_deleted is not None:
        table[first_deleted] = (key, value)
        return True
    raise OverflowError("hash table is full")

def remove_probed(table, key):
    m = len(table)
    start = hash(key) % m
    for step in range(m):
        i = (start + step) % m
        slot = table[i]
        if slot is EMPTY:
            raise KeyError(key)
        if slot is not DELETED and slot[0] == key:
            table[i] = DELETED
            return slot[1]
    raise KeyError(key)

# topics/maps-sets-hashing/performance.md
def rebuild_chained(table, capacity):
    if type(capacity) is not int or capacity <= 0:
        raise ValueError("capacity must be a positive integer")
    fresh = [[] for _ in range(capacity)]
    for bucket in table:
        for key, value in bucket:
            put_chained(fresh, key, value)
    return fresh

def rebuild_probed(table, capacity):
    if type(capacity) is not int or capacity <= 0:
        raise ValueError("capacity must be a positive integer")
    fresh = [EMPTY] * capacity
    for slot in table:
        if slot is not EMPTY and slot is not DELETED:
            put_probed(fresh, slot[0], slot[1])
    return fresh
