Skip to content

Commit 4deaf52

Browse files
Kenoararslan
authored andcommitted
Add an option to specify the increment to use when searching for primes (#71)
A common operation in cryptography is to want to find a prime `p` such that some `n` divides `p-1`. This is accomplished by starting at some value `q` which `n` divides and then proceeding in increments of `n` rather than increments of `1` until a prime is found. To enable this procedure, add a keyword argument to `nextprime`/`prevprime` that lets the user specify the desired increment. Also, while we're at it, speed up the default `prevprime` case for larger values by proceeding in increments of `2`.
1 parent 4680129 commit 4deaf52

File tree

2 files changed

+34
-12
lines changed

2 files changed

+34
-12
lines changed

src/Primes.jl

Lines changed: 30 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -544,14 +544,18 @@ end
544544
add_!(n::Integer, x::Int) = Base.checked_add(n, oftype(n, x))
545545

546546
"""
547-
nextprime(n::Integer, i::Integer=1)
547+
nextprime(n::Integer, i::Integer=1; interval::Integer=1)
548548
549549
The `i`-th smallest prime not less than `n` (in particular,
550550
`nextprime(p) == p` if `p` is prime). If `i < 0`, this is equivalent to
551551
prevprime(n, -i). Note that for `n::BigInt`, the returned number is
552552
only a pseudo-prime (the function [`isprime`](@ref) is used
553553
internally). See also [`prevprime`](@ref).
554554
555+
If `interval` is provided, primes are sought in increments of `interval`.
556+
This can be useful to ensure the presence of certain divisors in `p-1`.
557+
The selected interval should be even.
558+
555559
```jldoctest
556560
julia> nextprime(4)
557561
5
@@ -564,12 +568,19 @@ julia> nextprime(4, 2)
564568
565569
julia> nextprime(5, 2)
566570
7
571+
572+
julia> nextprime(2^16-1024+1; interval=1024)
573+
133121
574+
575+
julia> gcd(133121 - 1, 1024) # 1024 | p - 1
576+
1024
567577
```
568578
"""
569-
function nextprime(n::Integer, i::Integer=1)
570-
i < 0 && return prevprime(n, -i)
579+
function nextprime(n::Integer, i::Integer=1; interval::Integer=1)
580+
i < 0 && return prevprime(n, -i; interval=interval)
571581
i == 0 && throw(DomainError(i))
572582
n < 2 && (n = oftype(n, 2))
583+
interval == 1 && (interval = 2)
573584
if n == 2
574585
if i <= 1
575586
return n
@@ -578,24 +589,24 @@ function nextprime(n::Integer, i::Integer=1)
578589
i -= 1
579590
end
580591
else
581-
n += iseven(n)
592+
n += iseven(n) ? oftype(n, interval - 1) : zero(n)
582593
end
583594
# n can now be safely mutated
584595
# @assert isodd(n) && n >= 3
585596
while true
586597
while !isprime(n)
587-
n = add_!(n, 2)
598+
n = add_!(n, interval)
588599
end
589600
i -= 1
590601
i <= 0 && break
591-
n = add_!(n, 2)
602+
n = add_!(n, interval)
592603
end
593604
n
594605
end
595606

596607

597608
"""
598-
prevprime(n::Integer, i::Integer=1)
609+
prevprime(n::Integer, i::Integer=1; interval::Integer=1)
599610
600611
The `i`-th largest prime not greater than `n` (in particular
601612
`prevprime(p) == p` if `p` is prime). If `i < 0`, this is equivalent to
@@ -614,17 +625,24 @@ julia> prevprime(5, 2)
614625
3
615626
```
616627
"""
617-
function prevprime(n::Integer, i::Integer=1)
618-
i <= 0 && return nextprime(n, -i)
619-
n += zero(n) # deep copy of n, which is mutated below
628+
function prevprime(n::Integer, i::Integer=1; interval::Integer=1)
629+
i <= 0 && return nextprime(n, -i; interval=interval)
630+
i == 1 && n == 2 && return n
631+
# A bit ugly, but this lets us speed up prime walking 2x in the (common)
632+
# case that n >> 2, while preventing prevprime(3) from skipping 2 and giving
633+
# the wrong answer.
634+
was_one = interval == 1
635+
was_one && (interval = 2)
636+
n -= iseven(n) ? oftype(n, interval-1) : zero(n) # deep copy of n, which is mutated below
620637
while true
621638
n < 2 && throw(ArgumentError("There is no prime less than or equal to $n"))
639+
was_one && n <= 4 && (interval = 1)
622640
while !isprime(n)
623-
n = add_!(n, -1)
641+
n = add_!(n, -interval)
624642
end
625643
i -= 1
626644
i <= 0 && break
627-
n = add_!(n, -1)
645+
n = add_!(n, -interval)
628646
end
629647
n
630648
end

test/runtests.jl

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -330,6 +330,8 @@ end
330330
n = rand(600:2^20)
331331
@test nextprime(n, i) == prevprime(n, -i)
332332
end
333+
334+
@test gcd(nextprime(2^17-1024+1; interval=1024) - 1, 1024) == 1024
333335
end
334336

335337
@testset "prevprime(::$T)" for T in (Int64, Int32, BigInt)
@@ -361,6 +363,8 @@ end
361363
n = rand(600:2^20)
362364
@test prevprime(n, i) == nextprime(n, -i)
363365
end
366+
367+
@test gcd(prevprime(2^17-1024+1; interval=1024) - 1, 1024) == 1024
364368
end
365369

366370
@testset "prime(::$T)" for T = (Int64, Int32, BigInt)

0 commit comments

Comments
 (0)