from __future__ import annotations from pathlib import Path from mathstream import ( StreamNumber, add, sub, mul, div, mod, pow, is_even, is_odd, clear_logs, collect_garbage, DivideByZeroError, active_streams, tracked_files, ) NUMBERS_DIR = Path(__file__).parent / "tests" def write_number(name: str, digits: str) -> StreamNumber: """Persist digits to disk and return a streamable handle.""" NUMBERS_DIR.mkdir(parents=True, exist_ok=True) target = NUMBERS_DIR / f"{name}.txt" target.write_text(digits, encoding="utf-8") return StreamNumber(target) def read_number(num: StreamNumber) -> str: """Collapse streamed chunks back into a concrete string.""" return "".join(num.stream()) def check(label: str, result: StreamNumber, expected: str) -> None: actual = read_number(result) assert ( actual == expected ), f"{label} expected {expected}, got {actual}" print(f"{label} = {actual}") def check_bool(label: str, value: bool, expected: bool) -> None: assert value is expected, f"{label} expected {expected}, got {value}" print(f"{label} = {value}") def main() -> None: clear_logs() # Build a handful of example operands on disk. big = write_number("huge", "98765432123456789") small = write_number("tiny", "34567") negative = write_number("negative", "-1200") exponent = write_number("power", "5") negative_divisor = write_number("neg_divisor", "-34567") literal_even = StreamNumber(literal="2000") literal_odd = StreamNumber(literal="-3") zero_literal = StreamNumber(literal="0") # Showcase the core operations. total = add(big, small) difference = sub(big, small) product = mul(small, negative) quotient = div(big, small) powered = pow(small, exponent) modulus = mod(big, small) neg_mod_pos = mod(negative, small) pos_mod_neg = mod(small, negative) neg_mod_neg = mod(negative, negative_divisor) literal_combo = add(literal_even, literal_odd) print("Operands stored under:", NUMBERS_DIR) check("huge + tiny", total, "98765432123491356") check("huge - tiny", difference, "98765432123422222") check("tiny * negative", product, "-41480400") check("huge // tiny", quotient, "2857217349595") check("tiny ** power", powered, "49352419431622775997607") check("huge % tiny", modulus, "6424") check("negative % tiny", neg_mod_pos, "33367") check("tiny % negative", pos_mod_neg, "-233") check("negative % neg_divisor", neg_mod_neg, "-1200") check("literal_even + literal_odd", literal_combo, "1997") check_bool("is_even(negative)", is_even(negative), True) check_bool("is_even(tiny)", is_even(small), False) check_bool("is_odd(tiny)", is_odd(small), True) check_bool("is_odd(negative)", is_odd(negative), False) check_bool("is_even(literal_even)", is_even(literal_even), True) check_bool("is_odd(literal_odd)", is_odd(literal_odd), True) # Custom exception coverage try: div(literal_even, zero_literal) except DivideByZeroError: print("div(literal_even, zero_literal) raised DivideByZeroError as expected") else: raise AssertionError("div by zero did not raise DivideByZeroError") try: mod(literal_even, zero_literal) except DivideByZeroError: print("mod(literal_even, zero_literal) raised DivideByZeroError as expected") else: raise AssertionError("mod by zero did not raise DivideByZeroError") # manual frees should immediately drop staged files staged = [ total, difference, product, quotient, powered, modulus, neg_mod_pos, pos_mod_neg, neg_mod_neg, literal_combo, ] for stream in staged: stream.free() literal_even.free() literal_odd.free() zero_literal.free() check_bool("total freed file gone", total.path.exists(), False) check_bool("literal_even freed file gone", literal_even.path.exists(), False) removed = collect_garbage(0) print(f"collect_garbage removed {len(removed)} files after manual free") check_bool("huge operand persists", big.path.exists(), True) print("Active streams:", active_streams()) print("Tracked files:", tracked_files()) if __name__ == "__main__": main()