123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163 |
- #!/usr/bin/env python3
- import argparse
- import functools
- import math
- import os
- import re
- import subprocess
- import sys
- import itertools
- def gen_names():
- for i in itertools.count():
- name = "p{:d}".format(i)
- if name not in nameset:
- yield name
- nameset=set()
- names = gen_names()
- class YafuError(Exception):
- pass
- verbose = False
- def diag(*args):
- if verbose:
- print(*args, file=sys.stderr)
- factorcache = set()
- factorcachefile = None
- def cache_factor(f):
- if f not in factorcache:
- factorcache.add(f)
- if factorcachefile is not None:
- factorcachefile.write("{:d}\n".format(f))
- factorcachefile.flush()
- yafu = None
- yafu_pattern = re.compile(rb"^P\d+ = (\d+)$")
- def call_yafu(n):
- n_orig = n
- diag("starting yafu", n_orig)
- p = subprocess.Popen([yafu, "-v", "-v"], stdin=subprocess.PIPE,
- stdout=subprocess.PIPE)
- p.stdin.write("{:d}\n".format(n).encode("ASCII"))
- p.stdin.close()
- factors = []
- for line in iter(p.stdout.readline, b''):
- line = line.rstrip(b"\r\n")
- diag("yafu output:", line.decode())
- m = yafu_pattern.match(line)
- if m is not None:
- f = int(m.group(1))
- if n % f != 0:
- raise YafuError("bad yafu factor {:d}".format(f))
- factors.append(f)
- if f >> 64:
- cache_factor(f)
- n //= f
- p.wait()
- diag("done yafu", n_orig)
- return factors, n
- def factorise(n):
- allfactors = []
- for f in factorcache:
- if n % f == 0:
- n //= f
- allfactors.append(f)
- while n > 1:
- factors, n = call_yafu(n)
- allfactors.extend(factors)
- return sorted(allfactors)
- def product(ns):
- return functools.reduce(lambda a,b: a*b, ns, 1)
- smallprimes = set()
- commands = {}
- def proveprime(p, name=None):
- if p >> 32 == 0:
- smallprimes.add(p)
- return "{:d}".format(p)
- if name is None:
- name = next(names)
- print("{} = {:d}".format(name, p))
- fs = factorise(p-1)
- fs.reverse()
- prod = product(fs)
- qs = []
- for q in fs:
- newprod = prod // q
- if newprod * newprod * newprod > p:
- prod = newprod
- else:
- qs.append(q)
- assert prod == product(qs)
- assert prod * prod * prod > p
- qset = set(qs)
- qnamedict = {q: proveprime(q) for q in qset}
- qnames = [qnamedict[q] for q in qs]
- for w in itertools.count(2):
- assert pow(w, p-1, p) == 1, "{}={:d} is not prime!".format(name, p)
- diag("trying witness", w, "for", p)
- for q in qset:
- wpower = pow(w, (p-1) // q, p) - 1
- if math.gcd(wpower, p) != 1:
- break
- else:
- diag("found witness", w, "for", p)
- break
- commands[p]= (name, w, qnames)
- return name
- def main():
- parser = argparse.ArgumentParser(description='')
- parser.add_argument("prime", nargs="+",
- help="Number to prove prime. Can be prefixed by a "
- "variable name and '=', e.g. 'x=9999999967'.")
- parser.add_argument("--cryptsuite", action="store_true",
- help="Generate abbreviated Pockle calls suitable "
- "for the tests in cryptsuite.py.")
- parser.add_argument("--yafu", default="yafu",
- help="yafu binary to help with factoring.")
- parser.add_argument("-v", "--verbose", action="store_true",
- help="Write diagnostics to standard error.")
- parser.add_argument("--cache", help="Cache of useful factors of things.")
- args = parser.parse_args()
- global verbose, yafu
- verbose = args.verbose
- yafu = args.yafu
- if args.cache is not None:
- with open(args.cache, "r") as fh:
- for line in iter(fh.readline, ""):
- factorcache.add(int(line.rstrip("\r\n")))
- global factorcachefile
- factorcachefile = open(args.cache, "a")
- for ps in args.prime:
- name, value = (ps.split("=", 1) if "=" in ps
- else (None, ps))
- proveprime(int(value, 0), name)
- print("po = pockle_new()")
- if len(smallprimes) > 0:
- if args.cryptsuite:
- print("add_small(po, {})".format(
- ", ".join("{:d}".format(q) for q in sorted(smallprimes))))
- else:
- for q in sorted(smallprimes):
- print("pockle_add_small_prime(po, {:d})".format(q))
- for p, (name, w, qnames) in sorted(commands.items()):
- print("{cmd}(po, {name}, [{qs}], {w:d})".format(
- cmd = "add" if args.cryptsuite else "pockle_add_prime",
- name=name, w=w, qs=", ".join(qnames)))
- if __name__ == '__main__':
- main()
|