Skip to content

Commit 1d818f4

Browse files
authored
fix interval KW in nextprime/prevprime (#72)
1 parent 4deaf52 commit 1d818f4

File tree

2 files changed

+63
-21
lines changed

2 files changed

+63
-21
lines changed

src/Primes.jl

Lines changed: 48 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -554,7 +554,7 @@ internally). See also [`prevprime`](@ref).
554554
555555
If `interval` is provided, primes are sought in increments of `interval`.
556556
This can be useful to ensure the presence of certain divisors in `p-1`.
557-
The selected interval should be even.
557+
The range of possible values for `interval` is currently `1:typemax(Int)`.
558558
559559
```jldoctest
560560
julia> nextprime(4)
@@ -569,30 +569,47 @@ julia> nextprime(4, 2)
569569
julia> nextprime(5, 2)
570570
7
571571
572-
julia> nextprime(2^16-1024+1; interval=1024)
573-
133121
572+
julia> nextprime(2^10+1; interval=1024)
573+
12289
574574
575-
julia> gcd(133121 - 1, 1024) # 1024 | p - 1
575+
julia> gcd(12289 - 1, 1024) # 1024 | p - 1
576576
1024
577577
```
578578
"""
579579
function nextprime(n::Integer, i::Integer=1; interval::Integer=1)
580-
i < 0 && return prevprime(n, -i; interval=interval)
581580
i == 0 && throw(DomainError(i))
582-
n < 2 && (n = oftype(n, 2))
583-
interval == 1 && (interval = 2)
581+
i < 0 && return prevprime(n, -i; interval=interval)
582+
interval < 1 && throw(DomainError(interval, "interval must be >= 1"))
583+
# TODO: lift the following condition
584+
interval > typemax(Int) && throw(DomainError(interval, "interval must be <= $(typemax(Int))"))
585+
interval = oftype(n, interval)
586+
if n < 2
587+
n = interval == 1 ?
588+
oftype(n, 2) :
589+
# smallest value >= 2 whose difference from n is a multiple of interval
590+
oftype(n, n + interval * (1 + (n-1)÷(-interval)))
591+
end
584592
if n == 2
585593
if i <= 1
586594
return n
587595
else
588-
n += one(n)
596+
n += interval
589597
i -= 1
590598
end
591599
else
592-
n += iseven(n) ? oftype(n, interval - 1) : zero(n)
600+
n += iseven(n) ? interval : zero(n)
593601
end
594602
# n can now be safely mutated
595-
# @assert isodd(n) && n >= 3
603+
# @assert n >= 3
604+
if iseven(n)
605+
@assert iseven(interval)
606+
throw(DomainError((n, interval),
607+
"`n` and `interval` should not be both even (there is then no correct answer)."))
608+
end
609+
# @assert isodd(n)
610+
interval = Int(interval)
611+
isodd(interval) && (interval = Base.checked_mul(interval, 2))
612+
596613
while true
597614
while !isprime(n)
598615
n = add_!(n, interval)
@@ -614,6 +631,10 @@ The `i`-th largest prime not greater than `n` (in particular
614631
only a pseudo-prime (the function [`isprime`](@ref) is used internally). See
615632
also [`nextprime`](@ref).
616633
634+
If `interval` is provided, primes are sought in increments of `interval`.
635+
This can be useful to ensure the presence of certain divisors in `p-1`.
636+
The range of possible values for `interval` is currently `1:typemax(Int)`.
637+
617638
```jldoctest
618639
julia> prevprime(4)
619640
3
@@ -627,22 +648,28 @@ julia> prevprime(5, 2)
627648
"""
628649
function prevprime(n::Integer, i::Integer=1; interval::Integer=1)
629650
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
651+
interval < 1 && throw(DomainError(interval, "interval must be >= 1"))
652+
interval > typemax(Int) && throw(DomainError(interval, "interval must be <= $(typemax(Int))"))
653+
interval = Int(interval)
654+
655+
n += zero(n) # deep copy of n, which is mutated below
656+
657+
# A bit ugly, but this lets us skip half of the isprime tests when isodd(interval)
658+
@inline function decrement(n)
659+
n = add_!(n, -interval)
660+
iseven(n) && n != 2 ? # n obviously not prime
661+
add_!(n, -interval) :
662+
n
663+
end
664+
637665
while true
638-
n < 2 && throw(ArgumentError("There is no prime less than or equal to $n"))
639-
was_one && n <= 4 && (interval = 1)
640666
while !isprime(n)
641-
n = add_!(n, -interval)
667+
n < 2 && throw(ArgumentError("There is no prime less than or equal to $n"))
668+
n = decrement(n)
642669
end
643670
i -= 1
644671
i <= 0 && break
645-
n = add_!(n, -interval)
672+
n = decrement(n)
646673
end
647674
n
648675
end

test/runtests.jl

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -331,7 +331,17 @@ end
331331
@test nextprime(n, i) == prevprime(n, -i)
332332
end
333333

334+
# interval
335+
@test nextprime(0, interval=2) == 2
336+
@test nextprime(0, interval=3) == 3
337+
@test_throws DomainError nextprime(0, 2, interval=2)
338+
@test_throws DomainError nextprime(0, interval=4)
339+
@test nextprime(-20, interval=2) == 2
340+
@test nextprime(4, interval=5) == 19
341+
@test nextprime(4, 2, interval=5) == 29
342+
@test nextprime(4, 3, interval=5) == 59
334343
@test gcd(nextprime(2^17-1024+1; interval=1024) - 1, 1024) == 1024
344+
@test gcd(nextprime(1024*rand(1:2^6)+1; interval=1024) - 1, 1024) == 1024
335345
end
336346

337347
@testset "prevprime(::$T)" for T in (Int64, Int32, BigInt)
@@ -364,7 +374,12 @@ end
364374
@test prevprime(n, i) == nextprime(n, -i)
365375
end
366376

377+
# interval
367378
@test gcd(prevprime(2^17-1024+1; interval=1024) - 1, 1024) == 1024
379+
@test gcd(prevprime(1024*rand(12:2^6)+1; interval=1024) - 1, 1024) == 1024
380+
@test prevprime(10, interval=4) == 2
381+
@test [prevprime(11, i, interval=2) for i=1:4] == [11, 7, 5, 3]
382+
@test [prevprime(11, i, interval=3) for i=1:3] == [11, 5, 2]
368383
end
369384

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

0 commit comments

Comments
 (0)