# Unique values. Fast lookup. Set operations.
# sets in practice — add, remove, check, compare

squad = {"Raven", "Wolf", "Ghost"}

# ─────────────────────────────────────────────
# Adding elements
# ─────────────────────────────────────────────

squad.add("Viper")
squad.add("Raven")      # already there — ignored
print(squad)            # {'Ghost', 'Raven', 'Viper', 'Wolf'} — order varies

# ─────────────────────────────────────────────
# Removing elements
# ─────────────────────────────────────────────

squad.remove("Wolf")        # removes Wolf — KeyError if missing
# squad.remove("Fox")       # KeyError: 'Fox'

squad.discard("Fox")        # no error if missing — silent
squad.discard("Viper")      # removes Viper

removed = squad.pop()       # removes and returns a random element
print(removed)              # unpredictable

squad.clear()               # empties the set
print(squad)                # set()

# ─────────────────────────────────────────────
# Membership checking
# ─────────────────────────────────────────────

squad = {"Raven", "Wolf", "Ghost"}
print("Wolf" in squad)      # True
print("Fox" in squad)       # False
print("Fox" not in squad)   # True

# ─────────────────────────────────────────────
# Set operations
# ─────────────────────────────────────────────

alpha = {"Raven", "Wolf", "Ghost"}
bravo = {"Ghost", "Viper", "Bull"}

# union — all elements from both, no duplicates
print(alpha | bravo)
# {'Bull', 'Ghost', 'Raven', 'Viper', 'Wolf'}

# intersection — only elements in both
print(alpha & bravo)
# {'Ghost'}

# difference — in alpha but not in bravo
print(alpha - bravo)
# {'Raven', 'Wolf'}

# symmetric difference — in either but not both
print(alpha ^ bravo)
# {'Bull', 'Raven', 'Viper', 'Wolf'}

# operations return new sets — originals unchanged
print(alpha)    # {'Ghost', 'Raven', 'Wolf'}
print(bravo)    # {'Bull', 'Ghost', 'Viper'}

# ─────────────────────────────────────────────
# Checking relationships
# ─────────────────────────────────────────────

a = {1, 2, 3}
b = {1, 2, 3, 4, 5}

print(a.issubset(b))        # True — all of a is in b
print(b.issuperset(a))      # True — b contains all of a
print(a.isdisjoint({4, 5})) # True — no elements in common
print(a.isdisjoint({3, 4})) # False — 3 is shared

# ─────────────────────────────────────────────
# Useful patterns
# ─────────────────────────────────────────────

# deduplicate a list
names = ["Raven", "Wolf", "Raven", "Ghost", "Wolf"]
unique = list(set(names))
print(unique)       # ['Ghost', 'Raven', 'Wolf'] — order not guaranteed

# find common elements between two lists
list1 = ["Raven", "Wolf", "Ghost"]
list2 = ["Ghost", "Viper", "Bull"]
common = set(list1) & set(list2)
print(common)       # {'Ghost'}

# find elements in one list but not another
only_in_list1 = set(list1) - set(list2)
print(only_in_list1)    # {'Raven', 'Wolf'}

# ─────────────────────────────────────────────
# Quick reference
# ─────────────────────────────────────────────

# s.add(x)          — add element (ignored if exists)
# s.remove(x)       — remove (KeyError if missing)
# s.discard(x)      — remove (silent if missing)
# s.pop()           — remove and return random element
# s.clear()         — empty the set
# x in s            — membership check (fast)
# s | other         — union
# s & other         — intersection
# s - other         — difference
# s ^ other         — symmetric difference
# s.issubset(other) — True if s ⊆ other
# s.issuperset(other)— True if s ⊇ other
# s.isdisjoint(other)— True if no common elements
#
# set operations return new sets — originals unchanged
# no indexing — sets are unordered
# use discard() over remove() when key might be missing
