59 lines
1.6 KiB
Python
59 lines
1.6 KiB
Python
import math
|
|
import random
|
|
from multiprocessing import Pool
|
|
from typing import Optional, Iterable, Tuple
|
|
|
|
def gen_nums(n: int) -> list[int]:
|
|
assert n > 0
|
|
nums = list(range(2 ** n))
|
|
random.shuffle(nums)
|
|
return nums
|
|
|
|
def chunks(n: int, p: int) -> Iterable[Tuple[int, int]]:
|
|
base, rem = divmod(n, p)
|
|
start = 0
|
|
for i in range(p):
|
|
size = base + (1 if i < rem else 0)
|
|
end = start + size
|
|
yield start, end
|
|
start = end
|
|
|
|
def t_find(args: Tuple[int, int, list[int], int]) -> Optional[int]:
|
|
s, e, haystack, needle = args
|
|
for i in range(s, e):
|
|
if haystack[i] == needle:
|
|
return i
|
|
return None
|
|
|
|
def find(needle: int, haystack: list[int]) -> Optional[int]:
|
|
size = len(haystack)
|
|
assert size >= 1
|
|
|
|
procs = int(math.log2(size))
|
|
ranges = list(chunks(size, procs))
|
|
with Pool(processes=procs) as pool:
|
|
res = pool.map(t_find, [(s, e, haystack, needle) for (s, e) in ranges])
|
|
|
|
for idx in res:
|
|
if idx is not None:
|
|
return idx
|
|
return None
|
|
|
|
def fuzz_test(trials: int = 200, max_n: int = 16, seed: Optional[int] = None) -> None:
|
|
if seed is not None:
|
|
random.seed(seed)
|
|
|
|
for t in range(1, trials + 1):
|
|
n = random.randint(1, max_n)
|
|
arr = gen_nums(n)
|
|
|
|
idx_true = random.randrange(len(arr))
|
|
needle = arr[idx_true]
|
|
expected = idx_true
|
|
|
|
got = find(needle, arr)
|
|
assert got == expected, f"[t={t}] Expected {expected}, got {got}; n={n}"
|
|
|
|
print(f"Fuzz OK: {trials} trials, max_n={max_n}")
|
|
|
|
fuzz_test(trials=1000, max_n=16) |