Files
2026-04-29 00:17:18 -05:00

43 lines
1.4 KiB
Python

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]