# Casting out nines. (2.00)
# Correctness and performance tests generated by Copilot (GPT-5).
import sys
import unittest
import random
import timeit
# --- Your casting out nines functions ---
def residue(n: int) -> int:
return n % 9
def casting_out_nines_add(x: int, y: int) -> bool:
return residue(x + y) == residue(residue(x) + residue(y))
def casting_out_nines_sub(x: int, y: int) -> bool:
return residue(x - y) == residue(residue(x) - residue(y))
def casting_out_nines_mul(x: int, y: int) -> bool:
return residue(x * y) == residue(residue(x) * residue(y))
def casting_out_nines_pow(x: int, y: int) -> bool:
return residue(pow(x, y)) == residue(pow(residue(x), y))
def casting_out_nines_div(x: int, y: int) -> bool:
q, r = divmod(x, y)
return residue(x) == residue(residue(y) * residue(q) + residue(r))
# --- Unit + Randomized Tests ---
class TestCastingOutNines(unittest.TestCase):
def test_residue_basic(self):
self.assertEqual(residue(10), 1)
self.assertEqual(residue(18), 0)
self.assertEqual(residue(0), 0)
def test_addition(self):
self.assertTrue(casting_out_nines_add(123, 456))
self.assertTrue(casting_out_nines_add(9, 18))
self.assertTrue(casting_out_nines_add(0, 0))
def test_subtraction(self):
self.assertTrue(casting_out_nines_sub(456, 123))
self.assertTrue(casting_out_nines_sub(18, 9))
self.assertTrue(casting_out_nines_sub(0, 0))
def test_multiplication(self):
self.assertTrue(casting_out_nines_mul(123, 456))
self.assertTrue(casting_out_nines_mul(9, 18))
self.assertTrue(casting_out_nines_mul(0, 999))
def test_power(self):
self.assertTrue(casting_out_nines_pow(2, 10)) # 1024
self.assertTrue(casting_out_nines_pow(5, 3)) # 125
self.assertTrue(casting_out_nines_pow(9, 5)) # multiples of 9
def test_division(self):
self.assertTrue(casting_out_nines_div(100, 7))
self.assertTrue(casting_out_nines_div(81, 9))
self.assertTrue(casting_out_nines_div(19, 2))
def test_division_by_zero(self):
with self.assertRaises(ZeroDivisionError):
casting_out_nines_div(10, 0)
def test_randomized_inputs(self):
for _ in range(5000):
x = random.randint(-10**6, 10**6)
y = random.randint(-10**6, 10**6)
self.assertTrue(casting_out_nines_add(x, y))
self.assertTrue(casting_out_nines_sub(x, y))
self.assertTrue(casting_out_nines_mul(x, y))
self.assertTrue(casting_out_nines_pow(x % 1000, y % 10)) # keep exponents small
self.assertTrue(casting_out_nines_div(x, y if y else 1)) # avoid division by zero
# --- Benchmark Harness ---
def benchmark_casting_out_nines(trials=10000):
xs = [random.randint(0, 10**6) for _ in range(trials)]
ys = [random.randint(1, 10**6) for _ in range(trials)]
def bench_add():
for x, y in zip(xs, ys):
casting_out_nines_add(x, y)
def bench_sub():
for x, y in zip(xs, ys):
casting_out_nines_sub(x, y)
def bench_mul():
for x, y in zip(xs, ys):
casting_out_nines_mul(x, y)
def bench_pow():
for x, y in zip(xs, ys):
casting_out_nines_pow(abs(x % 50), y % 10)
def bench_div():
for x, y in zip(xs, ys):
casting_out_nines_div(x, y)
results = {
"Addition": timeit.timeit(bench_add, number=1),
"Subtraction": timeit.timeit(bench_sub, number=1),
"Multiplication": timeit.timeit(bench_mul, number=1),
"Power": timeit.timeit(bench_pow, number=1),
"Division": timeit.timeit(bench_div, number=1),
}
return results
if __name__ == "__main__":
print("Running unit and randomized tests...", file=sys.stderr)
unittest.main(exit=False)
print("Benchmarking performance...")
results = benchmark_casting_out_nines(trials=10000)
width = 1 + max(map(len, results.keys()))
for op, t in results.items():
print(f"{op:{width}s}: {t:.4f} seconds")
IyBDYXN0aW5nIG91dCBuaW5lcy4gKDIuMDApCiMgQ29ycmVjdG5lc3MgYW5kIHBlcmZvcm1hbmNlIHRlc3RzIGdlbmVyYXRlZCBieSBDb3BpbG90IChHUFQtNSkuCgppbXBvcnQgc3lzCmltcG9ydCB1bml0dGVzdAppbXBvcnQgcmFuZG9tCmltcG9ydCB0aW1laXQKCiMgLS0tIFlvdXIgY2FzdGluZyBvdXQgbmluZXMgZnVuY3Rpb25zIC0tLQpkZWYgcmVzaWR1ZShuOiBpbnQpIC0+IGludDoKICAgIHJldHVybiBuICUgOQoKZGVmIGNhc3Rpbmdfb3V0X25pbmVzX2FkZCh4OiBpbnQsIHk6IGludCkgLT4gYm9vbDoKICAgIHJldHVybiByZXNpZHVlKHggKyB5KSA9PSByZXNpZHVlKHJlc2lkdWUoeCkgKyByZXNpZHVlKHkpKQoKZGVmIGNhc3Rpbmdfb3V0X25pbmVzX3N1Yih4OiBpbnQsIHk6IGludCkgLT4gYm9vbDoKICAgIHJldHVybiByZXNpZHVlKHggLSB5KSA9PSByZXNpZHVlKHJlc2lkdWUoeCkgLSByZXNpZHVlKHkpKQoKZGVmIGNhc3Rpbmdfb3V0X25pbmVzX211bCh4OiBpbnQsIHk6IGludCkgLT4gYm9vbDoKICAgIHJldHVybiByZXNpZHVlKHggKiB5KSA9PSByZXNpZHVlKHJlc2lkdWUoeCkgKiByZXNpZHVlKHkpKQoKZGVmIGNhc3Rpbmdfb3V0X25pbmVzX3Bvdyh4OiBpbnQsIHk6IGludCkgLT4gYm9vbDoKICAgIHJldHVybiByZXNpZHVlKHBvdyh4LCB5KSkgPT0gcmVzaWR1ZShwb3cocmVzaWR1ZSh4KSwgeSkpCgpkZWYgY2FzdGluZ19vdXRfbmluZXNfZGl2KHg6IGludCwgeTogaW50KSAtPiBib29sOgogICAgcSwgciA9IGRpdm1vZCh4LCB5KQogICAgcmV0dXJuIHJlc2lkdWUoeCkgPT0gcmVzaWR1ZShyZXNpZHVlKHkpICogcmVzaWR1ZShxKSArIHJlc2lkdWUocikpCgojIC0tLSBVbml0ICsgUmFuZG9taXplZCBUZXN0cyAtLS0KY2xhc3MgVGVzdENhc3RpbmdPdXROaW5lcyh1bml0dGVzdC5UZXN0Q2FzZSk6CiAgICBkZWYgdGVzdF9yZXNpZHVlX2Jhc2ljKHNlbGYpOgogICAgICAgIHNlbGYuYXNzZXJ0RXF1YWwocmVzaWR1ZSgxMCksIDEpCiAgICAgICAgc2VsZi5hc3NlcnRFcXVhbChyZXNpZHVlKDE4KSwgMCkKICAgICAgICBzZWxmLmFzc2VydEVxdWFsKHJlc2lkdWUoMCksIDApCgogICAgZGVmIHRlc3RfYWRkaXRpb24oc2VsZik6CiAgICAgICAgc2VsZi5hc3NlcnRUcnVlKGNhc3Rpbmdfb3V0X25pbmVzX2FkZCgxMjMsIDQ1NikpCiAgICAgICAgc2VsZi5hc3NlcnRUcnVlKGNhc3Rpbmdfb3V0X25pbmVzX2FkZCg5LCAxOCkpCiAgICAgICAgc2VsZi5hc3NlcnRUcnVlKGNhc3Rpbmdfb3V0X25pbmVzX2FkZCgwLCAwKSkKCiAgICBkZWYgdGVzdF9zdWJ0cmFjdGlvbihzZWxmKToKICAgICAgICBzZWxmLmFzc2VydFRydWUoY2FzdGluZ19vdXRfbmluZXNfc3ViKDQ1NiwgMTIzKSkKICAgICAgICBzZWxmLmFzc2VydFRydWUoY2FzdGluZ19vdXRfbmluZXNfc3ViKDE4LCA5KSkKICAgICAgICBzZWxmLmFzc2VydFRydWUoY2FzdGluZ19vdXRfbmluZXNfc3ViKDAsIDApKQoKICAgIGRlZiB0ZXN0X211bHRpcGxpY2F0aW9uKHNlbGYpOgogICAgICAgIHNlbGYuYXNzZXJ0VHJ1ZShjYXN0aW5nX291dF9uaW5lc19tdWwoMTIzLCA0NTYpKQogICAgICAgIHNlbGYuYXNzZXJ0VHJ1ZShjYXN0aW5nX291dF9uaW5lc19tdWwoOSwgMTgpKQogICAgICAgIHNlbGYuYXNzZXJ0VHJ1ZShjYXN0aW5nX291dF9uaW5lc19tdWwoMCwgOTk5KSkKCiAgICBkZWYgdGVzdF9wb3dlcihzZWxmKToKICAgICAgICBzZWxmLmFzc2VydFRydWUoY2FzdGluZ19vdXRfbmluZXNfcG93KDIsIDEwKSkgICAjIDEwMjQKICAgICAgICBzZWxmLmFzc2VydFRydWUoY2FzdGluZ19vdXRfbmluZXNfcG93KDUsIDMpKSAgICAjIDEyNQogICAgICAgIHNlbGYuYXNzZXJ0VHJ1ZShjYXN0aW5nX291dF9uaW5lc19wb3coOSwgNSkpICAgICMgbXVsdGlwbGVzIG9mIDkKCiAgICBkZWYgdGVzdF9kaXZpc2lvbihzZWxmKToKICAgICAgICBzZWxmLmFzc2VydFRydWUoY2FzdGluZ19vdXRfbmluZXNfZGl2KDEwMCwgNykpCiAgICAgICAgc2VsZi5hc3NlcnRUcnVlKGNhc3Rpbmdfb3V0X25pbmVzX2Rpdig4MSwgOSkpCiAgICAgICAgc2VsZi5hc3NlcnRUcnVlKGNhc3Rpbmdfb3V0X25pbmVzX2RpdigxOSwgMikpCgogICAgZGVmIHRlc3RfZGl2aXNpb25fYnlfemVybyhzZWxmKToKICAgICAgICB3aXRoIHNlbGYuYXNzZXJ0UmFpc2VzKFplcm9EaXZpc2lvbkVycm9yKToKICAgICAgICAgICAgY2FzdGluZ19vdXRfbmluZXNfZGl2KDEwLCAwKQoKICAgIGRlZiB0ZXN0X3JhbmRvbWl6ZWRfaW5wdXRzKHNlbGYpOgogICAgICAgIGZvciBfIGluIHJhbmdlKDUwMDApOgogICAgICAgICAgICB4ID0gcmFuZG9tLnJhbmRpbnQoLTEwKio2LCAxMCoqNikKICAgICAgICAgICAgeSA9IHJhbmRvbS5yYW5kaW50KC0xMCoqNiwgMTAqKjYpCiAgICAgICAgICAgIHNlbGYuYXNzZXJ0VHJ1ZShjYXN0aW5nX291dF9uaW5lc19hZGQoeCwgeSkpCiAgICAgICAgICAgIHNlbGYuYXNzZXJ0VHJ1ZShjYXN0aW5nX291dF9uaW5lc19zdWIoeCwgeSkpCiAgICAgICAgICAgIHNlbGYuYXNzZXJ0VHJ1ZShjYXN0aW5nX291dF9uaW5lc19tdWwoeCwgeSkpCiAgICAgICAgICAgIHNlbGYuYXNzZXJ0VHJ1ZShjYXN0aW5nX291dF9uaW5lc19wb3coeCAlIDEwMDAsIHkgJSAxMCkpICMga2VlcCBleHBvbmVudHMgc21hbGwKICAgICAgICAgICAgc2VsZi5hc3NlcnRUcnVlKGNhc3Rpbmdfb3V0X25pbmVzX2Rpdih4LCB5IGlmIHkgZWxzZSAxKSkgIyBhdm9pZCBkaXZpc2lvbiBieSB6ZXJvCgojIC0tLSBCZW5jaG1hcmsgSGFybmVzcyAtLS0KZGVmIGJlbmNobWFya19jYXN0aW5nX291dF9uaW5lcyh0cmlhbHM9MTAwMDApOgogICAgeHMgPSBbcmFuZG9tLnJhbmRpbnQoMCwgMTAqKjYpIGZvciBfIGluIHJhbmdlKHRyaWFscyldCiAgICB5cyA9IFtyYW5kb20ucmFuZGludCgxLCAxMCoqNikgZm9yIF8gaW4gcmFuZ2UodHJpYWxzKV0KCiAgICBkZWYgYmVuY2hfYWRkKCk6CiAgICAgICAgZm9yIHgsIHkgaW4gemlwKHhzLCB5cyk6CiAgICAgICAgICAgIGNhc3Rpbmdfb3V0X25pbmVzX2FkZCh4LCB5KQoKICAgIGRlZiBiZW5jaF9zdWIoKToKICAgICAgICBmb3IgeCwgeSBpbiB6aXAoeHMsIHlzKToKICAgICAgICAgICAgY2FzdGluZ19vdXRfbmluZXNfc3ViKHgsIHkpCgogICAgZGVmIGJlbmNoX211bCgpOgogICAgICAgIGZvciB4LCB5IGluIHppcCh4cywgeXMpOgogICAgICAgICAgICBjYXN0aW5nX291dF9uaW5lc19tdWwoeCwgeSkKCiAgICBkZWYgYmVuY2hfcG93KCk6CiAgICAgICAgZm9yIHgsIHkgaW4gemlwKHhzLCB5cyk6CiAgICAgICAgICAgIGNhc3Rpbmdfb3V0X25pbmVzX3BvdyhhYnMoeCAlIDUwKSwgeSAlIDEwKQoKICAgIGRlZiBiZW5jaF9kaXYoKToKICAgICAgICBmb3IgeCwgeSBpbiB6aXAoeHMsIHlzKToKICAgICAgICAgICAgY2FzdGluZ19vdXRfbmluZXNfZGl2KHgsIHkpCgogICAgcmVzdWx0cyA9IHsKICAgICAgICAiQWRkaXRpb24iOiB0aW1laXQudGltZWl0KGJlbmNoX2FkZCwgbnVtYmVyPTEpLAogICAgICAgICJTdWJ0cmFjdGlvbiI6IHRpbWVpdC50aW1laXQoYmVuY2hfc3ViLCBudW1iZXI9MSksCiAgICAgICAgIk11bHRpcGxpY2F0aW9uIjogdGltZWl0LnRpbWVpdChiZW5jaF9tdWwsIG51bWJlcj0xKSwKICAgICAgICAiUG93ZXIiOiB0aW1laXQudGltZWl0KGJlbmNoX3BvdywgbnVtYmVyPTEpLAogICAgICAgICJEaXZpc2lvbiI6IHRpbWVpdC50aW1laXQoYmVuY2hfZGl2LCBudW1iZXI9MSksCiAgICB9CiAgICByZXR1cm4gcmVzdWx0cwoKaWYgX19uYW1lX18gPT0gIl9fbWFpbl9fIjoKICAgIHByaW50KCJSdW5uaW5nIHVuaXQgYW5kIHJhbmRvbWl6ZWQgdGVzdHMuLi4iLCBmaWxlPXN5cy5zdGRlcnIpCiAgICB1bml0dGVzdC5tYWluKGV4aXQ9RmFsc2UpCgogICAgcHJpbnQoIkJlbmNobWFya2luZyBwZXJmb3JtYW5jZS4uLiIpCiAgICByZXN1bHRzID0gYmVuY2htYXJrX2Nhc3Rpbmdfb3V0X25pbmVzKHRyaWFscz0xMDAwMCkKICAgIHdpZHRoID0gMSArIG1heChtYXAobGVuLCByZXN1bHRzLmtleXMoKSkpCiAgICBmb3Igb3AsIHQgaW4gcmVzdWx0cy5pdGVtcygpOgogICAgICAgIHByaW50KGYie29wOnt3aWR0aH1zfToge3Q6LjRmfSBzZWNvbmRzIik=