from __future__ import annotations import ast import random class OpaquePredicateInjector(ast.NodeTransformer): def __init__(self, rng: random.Random, seed_name: str, prob: float=0.5): self.rng = rng self.seed_name = seed_name self.prob = prob self.injected = False def visit_If(self, node: ast.If) -> ast.AST: self.generic_visit(node) if self.rng.random() < self.prob: node.test = self._wrap(node.test) self.injected = True return node def visit_While(self, node: ast.While) -> ast.AST: self.generic_visit(node) if self.rng.random() < self.prob * 0.4: node.test = self._wrap(node.test) self.injected = True return node def _wrap(self, test: ast.expr) -> ast.expr: return ast.BoolOp(op=ast.And(), values=[test, self._tautology()]) def _tautology(self) -> ast.expr: s = self.seed_name kind = self.rng.choice(('parity', 'flt3', 'flt5')) if kind == 'parity': src = f'({s} * ({s} + 1)) % 2 == 0' elif kind == 'flt3': src = f'({s} * {s} * {s} - {s}) % 3 == 0' else: src = f'({s} * {s} * {s} * {s} * {s} - {s}) % 5 == 0' return ast.parse(src, mode='eval').body def build_seed_stmt(seed_name: str) -> ast.stmt: return ast.parse(f'{seed_name} = id(int) & 0xFFFFFFFF').body[0]