83 82 bytes
This program implements the Tonelli-Shanks algorithm. I wrote it by closely following the Wikipedia page. It takes as input
The absence of a square root is reported by the following error:
TypeError: pow() 3rd argument not allowed unless all arguments are integers
This is very intricately golfed code, written in the imperative style, as opposed to the more common functional style of Pyth.
The one subtle aspect of Pyth I'm using is
=, which, if not immediately followed by a variable, searches forward in the program for the next variable, then assigns the result of the following expression to that variable, then returns that result. I will refer throughout the explanation to the wikipedia page: Tonelli-Shanks algorithm, as that is the algorithm I am implementing.
A takes a 2-tuple as input, and assigns the values to
H respectively, and returns its input.
Q is the initial input.
e returns the last element of a sequence. After this snippet,
M defines a 2 input function
g, where the inputs are
.^ is Pyth's fast modular exponentiation function. This snippet defines
g to mean exponentiation mod
f defines a repeat until false loop, and returns the number of iterations it runs for, given
1 as its input. During each iteration of the loop, we divide
H by 2, set
H to that value, and check whether the result is odd. Once it is, we stop.
K stores the number of iterations this took.
One very tricky thing is the
= searches ahead for the next variable, which is
T is set to 2. However,
T inside an
f loop is the iteration counter, so we use
; to get the value of
T from the global environment. This is done to save a couple of bytes of whitespace that would otherwise be needed to separate the numbers.
After this snippet,
S from the wikipedia article (wiki), and
Q from the wiki, and
Now, we need to find a quadratic nonresidue mod
p. We'll brute force this using the Euler criterion.
/ is floored division, so
ftgT/Q;1 finds the first integer
T ^ ((p-1)/2) != 1, as desired. Recall that
; again pulls
T from the global environment, which is still 2. This result is
z from the wiki.
Next, to create
c from the wiki, we need
z^Q, so we wrap the above in
g ... H and assign the result to
c from the wiki.
Let's separate out this:
~ is like
=, but returns the original value of the variable, not its new value. Thus, it returns
G, which is
n from the wiki.
J the value of
n^((Q+1)/2), which is
R from the wiki.
Now, the following takes effect:
G the value
n^Q, which is
t from the wiki.
Now, we have our loop variables set up.
M, c, t, R from the wiki are
K, T, G, J.
The body of the loop is complicated, so I'm going to present it with the whitespace, the way I wrote it:
First, we check whether
G is 1. If so, we exit the loop.
The next code that runs is:
Here, we search for the first value of
i such that
G^(2^i) mod Q = 1, starting at 1. The result is saved in
Here, we take the old value of
K, subtract the new value of
K, subtract 1, raise 2 to that power, and then raise
T to that power mod
Q, and then assign the result to
T. This makes
T equal to
b from the wiki.
This is also the line which terminates the loop and fails if there is no solution, because in that case the new value of
K will equal the old value of
K, 2 will be raised to the
-1, and the modular exponentiation will raise an error.
Next, we multiply
J by the above result and store it back in
Then we square
T and store the result back in
T back to
c from the wiki.
Then we multiply
G by that result, take it mod
Q and store the result back in
And we terminate the loop.
After the loop's over,
J is a square root of
p. To find the smallest one, we use the following code:
_BJ creates the list of
J and its negation,
% implicitly takes
Q as its second argument, and uses the default behavior of Pyth to apply
% ... Q to each member of the sequence. Then
S sorts the list and
h takes its first member, the minimum.