Tonelli-Shanks algorithm

Translation of the Wikipedia pseudocode, heavily influenced by Sidef and Python.

#  Legendre operator (𝑛│𝑝)
sub infix:<β”‚> (Int \𝑛, Int \𝑝 where 𝑝.is-prime && (𝑝 != 2)) {
    given 𝑛.expmod( (𝑝-1) div 2, 𝑝 ) {
        when 0  {  0 }
        when 1  {  1 }
        default { -1 }
    }
}

sub tonelli-shanks ( \𝑛, \𝑝 where (𝑛│𝑝) > 0 ) {
    my $𝑄 = 𝑝 - 1;
    my $𝑆 = 0;
    $𝑄 +>= 1 and $𝑆++ while $𝑄 %% 2;
    return 𝑛.expmod((𝑝+1) div 4, 𝑝) if $𝑆 == 1;
    my $𝑐 = ((2..𝑝).first: (*│𝑝) < 0).expmod($𝑄, 𝑝);
    my $𝑅 = 𝑛.expmod( ($𝑄+1) +> 1, 𝑝 );
    my $𝑑 = 𝑛.expmod( $𝑄, 𝑝 );
    while ($𝑑-1)Β % 𝑝 {
        my $b;
        my $𝑑2 = $𝑑² % 𝑝;
        for 1 .. $𝑆 {
            if ($𝑑2-1)Β %% 𝑝 {
                $b = $𝑐.expmod(1 +< ($𝑆-1-$_), 𝑝);
                $𝑆 = $_;
                last;
            }
            $𝑑2 = $𝑑2Β²Β % 𝑝;
        }
        $𝑅 = ($𝑅 * $b)Β % 𝑝;
        $𝑐 = $bΒ²Β % 𝑝;
        $𝑑 = ($𝑑 * $𝑐)Β % 𝑝;
    }
    $𝑅;
}

my @tests = (
    (10, 13),
    (56, 101),
    (1030, 10009),
    (1032, 10009),
    (44402, 100049),
    (665820697, 1000000009),
    (881398088036, 1000000000039),
    (41660815127637347468140745042827704103445750172002,
      100000000000000000000000000000000000000000000000577)
);

 for @tests -> ($n, $p) {
    try my $t = tonelli-shanks($n, $p);
    say "No solution for ({$n}, {$p})." and next ifΒ !$t or ($tΒ² - $n)Β % $p;
    say "Roots of $n are ($t, {$p-$t}) mod $p";
}

Output:

Last updated

Was this helpful?