class Oracle:
    def __init__(self, fn): self.fn, self.queries = fn, 0
    def __call__(self, x):
        self.queries += 1
        return self.fn(x)
def linear_search(size, target):
    if type(size) is not int or size < 1 or target not in range(size):
        raise ValueError("target must index a nonempty domain")
    oracle = Oracle(lambda x: x == target)
    answer = next(x for x in range(size) if oracle(x))
    return {"answer": answer, "queries": oracle.queries, "domain": size}

middle = linear_search(16, 7)
boundary = linear_search(16, 0)
rejected = False
try:
    linear_search(16, 16)
except ValueError:
    rejected = True
assert middle == {"answer": 7, "queries": 8, "domain": 16}
assert boundary["answer"] == 0 and boundary["queries"] == 1
assert rejected and middle["queries"] > boundary["queries"]
print(f"PASS: 27 oracle ledger middle_queries={middle['queries']} boundary_queries={boundary['queries']} invalid_target={rejected}")
