Pratt certificates
(bumping this proposal to see if there's any interest or comments. If so, speak now; if not, I'll delete)
Your task: write code that generates a Pratt certificate for a prime number, and write code that verifies an existing Pratt certificate.
What's a Pratt certificate?
A Pratt certificate for a prime number p
is a proof, of a particular type, that p
is indeed prime. Historically, it was used in situations where proving the primality of p
required a computation that was slow due to factoring p-1
, but verifying the certificate (once the initial computation generated it) was quite fast.
A Pratt certificate for p
is a recursive structure consisting of three parts: the prime p
itself; a "witness" integer g
(which is actually a primitive root modulo p
; see the next section for its properties); and Pratt certificates for all primes dividing p-1
. The prime p=2
is special: a Pratt certificate for 2
is just 2
itself.
For example, here is a Pratt certificate for p=3911
:
{3911, 13, {2, {5, 2, {2}}, {17, 3, {2}}, {23, 5, {2, {11, 2, {2, {5, 2, {2}}}}}}}}
The witness is 13
, and the prime factors of 3911-1
are 2
, 5
, 17
, and 23
; each of those new primes itself has a Pratt certificate, which are respectively 2
, {5, 2, {2}}
, {17, 3, {2}}
and {23, 5, {2, {11, 2, {2, {5, 2, {2}}}}}
. In this last Pratt certificate, the prime factors of 23
are 2
and 11
, so a Pratt certificate for 11
must be included, and so on.
How do we generate a Pratt certificate?
Given a prime p
, a Pratt certificate can be generated by finding a primitive root g
modulo p
; factoring p-1
into primes (keeping only one copy of each prime factor); and recursively generating Pratt certificates for every prime factor of p-1
.
How do we verify a Pratt certificate?
Given a prime p
, a witness g
, and the prime factors q1
, q2
, ... of p-1
, a Pratt certificate is verified by checking:
- that
p-1
has no prime factors other than q1
, q2
, ...;
- that the power
g^(p-1)
is congruent to 1
modulo p
;
- that none of the smaller powers
g^((p-1)/q1)
, g^((p-1)/q2)
, ... are congruent to 1
modulo p
; and
- that each of the Pratt certificates of
q1
, q2
, ... are themselves valid.
Scoring and technicalities
You must write two programs or functions (or one of each): one that takes a prime number as input and returns its Pratt certificate; and one that takes an input formatted like a Pratt certificate and returns a truthy or falsy value depending on whether it is an actual Pratt certificate.
- You may choose any reasonable format for the Pratt certificate: nested lists (like the examples in this question), indented multiline strings (like the example on the Wikipedia page), or something similar that a human being could be trivially trained into parsing by eye. You may use any reasonable convention for the trivial Pratt certificate for
2
.
- However: whatever format you choose for the Pratt certificate, your certificate-generating code must output the same format that you take as input to your certificate-verifying code. Note that your certificate-verifying code must be capable of verifying any possible Pratt certificate (in your format) for
p
, not just the one your other program generates for p
.
- If you want, you may write a single program or function that accomplishes both tasks; in that case, your code can either determine which task is being asked of it implicitly from the input, or it can allow the user to instruct it which task to perform in some reasonable way.
- Regardless of whether you use one or two programs, no calculation can be shared or saved between different runs of the code. The programs must work correctly, on any individual prime input and on any individual certificate-shaped input, if it is the first time that code is ever being run on that system.
- You don't have to handle bogus input. You may always assume that the input to your first program is an actual prime number, and that your input to the second program syntactically matches your Pratt certificate format.
- Built-ins that generate or verify Pratt certificates are not allowed. Other types of built-ins (for example, those that factor integers, raise integers to powers in modular arithmetic, find primitive roots) are acceptable.
- This is code-golf, so shorter code (in bytes) is better. If two programs are used, the total number of bytes in both programs is the score; if one program is used, its number of bytes is the score.
Example Pratt certificates given prime inputs
(Note that there are many possible witnesses for any given prime, but the rest of the certificate is unique up to reordering.)
31 -> {31, 3, {2, {3, 2, {2}}, {5, 2, {2}}}}
127 -> {127, 3, {2, {3, 2, {2}}, {7, 3, {2, {3, 2, {2}}}}}}
229 -> {229, 6, {2, {3, 2, {2}}, {19, 2, {2, {3, 2, {2}}}}}}
1093 -> {1093, 5, {2, {3, 2, {2}}, {7, 3, {2, {3, 2, {2}}}}, {13, 2, {2, {3, 2, {2}}}}}}
65537 -> {65537, 3, {2}}
(All the above outputs are examples of truthy inputs for the Pratt-certificate checking code.)
Example falsy inputs for Pratt-certificate checking
{31, 2, {2, {3, 2, {2}}, {5, 2, {2}}}}
{31, 3, {2, {3, 2, {2}}}
{31, 3, {2, {3, 2, {2}}, {5, 2, {2, {3, 2, {2}}}}}}
{127, 2, {2, {3, 2, {2}}, {5, 2, {2}}}}
{85, 4, {6, 5, {5, 2, {2}}}, {14, 3, {13, 2, {2, {3, 2, {2}}}}}}