"""Boyer-Moore majority vote: the element occurring more than n/2 times. Three outcomes, three channels -- the caller can always tell them apart: - empty input -> raise EmptySequence (a ValueError) - no element occurs > n/2 -> raise NoMajority (a ValueError) - otherwise -> return the element itself, which may be None, float('nan'), or any other object. The old contract returned None for all three, so a None majority, an empty list and a no-majority list were one value. The confirmation pass counts with `_same`, not `list.count`: identity first, then NaN == NaN, then `==`. That is what lets a non-reflexive element be confirmed when it fills the list -- repeated NaN objects count as equal even though `nan != nan`, and a repeated object whose `__eq__` always answers False is still recognised by identity. Note that distinct objects of such a class are *not* conflated: only identity, NaN-ness and `==` count. Python's own equality still rules elsewhere, so `True` and `1` remain one value. Boyer-Moore's first pass is unchanged: O(n) time, O(1) extra space, one materialisation of the iterable (so a generator is accepted). """ from __future__ import annotations from typing import Any, Iterable class EmptySequence(ValueError): """majority() was given nothing to vote on.""" class NoMajority(ValueError): """No element occurs more than len(seq) // 2 times.""" def _same(a: Any, b: Any) -> bool: """The counting relation: identity, then NaN equality, then `==`.""" if a is b: return True if isinstance(a, float) and isinstance(b, float) and a != a and b != b: return True return a == b def majority(nums: Iterable[Any]) -> Any: arr = list(nums) if not arr: raise EmptySequence("majority() of an empty sequence") cand = arr[0] count = 0 for x in arr: if count == 0: cand = x count = 1 elif _same(x, cand): count += 1 else: count -= 1 if sum(1 for x in arr if _same(x, cand)) * 2 > len(arr): return cand raise NoMajority( "no element occurs more than %d times in a %d-element sequence" % (len(arr) // 2, len(arr)) )