205 lines
5.2 KiB
Text
205 lines
5.2 KiB
Text
|
|
// ------------------------------------------------------------
|
|||
|
|
// Lucas–Lehmer Mersenne prime tester
|
|||
|
|
// Using FutureBasic 7.0.37
|
|||
|
|
//
|
|||
|
|
// November 2025, R.W.
|
|||
|
|
// ------------------------------------------------------------
|
|||
|
|
|
|||
|
|
include "gmp.incl"
|
|||
|
|
|
|||
|
|
// ------------------------------------------------------------
|
|||
|
|
// Lucas–Lehmer Mersenne prime tester
|
|||
|
|
// ------------------------------------------------------------
|
|||
|
|
|
|||
|
|
CFTimeInterval tim
|
|||
|
|
|
|||
|
|
// Global mpz_t bigints reused everywhere
|
|||
|
|
mpz_t ts // Lucas–Lehmer state ts
|
|||
|
|
mpz_t mp, mp_minus1
|
|||
|
|
mpz_t tmp // scratch for reduction
|
|||
|
|
unsigned long gN // last exponent tested (for [Next])
|
|||
|
|
long gCount
|
|||
|
|
|
|||
|
|
// ------------------------------------------------------------
|
|||
|
|
// Init / clear big integers
|
|||
|
|
// ------------------------------------------------------------
|
|||
|
|
local fn InitBigInts
|
|||
|
|
long maxBits
|
|||
|
|
maxBits = 120000 // enough for p up to ~100k
|
|||
|
|
mpz_init2( ts, maxBits )
|
|||
|
|
mpz_init2( mp, maxBits )
|
|||
|
|
mpz_init2( mp_minus1, maxBits )
|
|||
|
|
mpz_init2( tmp, maxBits )
|
|||
|
|
end fn
|
|||
|
|
|
|||
|
|
local fn ClearBigInts
|
|||
|
|
mpz_clear( ts )
|
|||
|
|
mpz_clear( mp )
|
|||
|
|
mpz_clear( mp_minus1 )
|
|||
|
|
mpz_clear( tmp )
|
|||
|
|
end fn
|
|||
|
|
|
|||
|
|
// ------------------------------------------------------------
|
|||
|
|
// Small 32-bit primality test for exponents p
|
|||
|
|
// ------------------------------------------------------------
|
|||
|
|
local fn IsPrime32( n as unsigned long ) as boolean
|
|||
|
|
unsigned long d
|
|||
|
|
// quick filter/rejects
|
|||
|
|
if n < 2 then return _False
|
|||
|
|
if n == 2 then return _True
|
|||
|
|
// even = composite
|
|||
|
|
if ( n AND 1 ) == 0 then return _False
|
|||
|
|
d = 3
|
|||
|
|
while d * d <= n
|
|||
|
|
if ( n MOD d ) == 0 then return _False
|
|||
|
|
d = d + 2
|
|||
|
|
wend
|
|||
|
|
end fn = _true
|
|||
|
|
|
|||
|
|
// ------------------------------------------------------------
|
|||
|
|
// IsMersennePrime_LL
|
|||
|
|
// Lucas–Lehmer test for M_p = 2^p − 1
|
|||
|
|
// Returns _true if M_p is prime
|
|||
|
|
// ------------------------------------------------------------
|
|||
|
|
|
|||
|
|
local fn IsMersennePrime_LL( p as unsigned long ) as boolean
|
|||
|
|
unsigned long k
|
|||
|
|
// quick filter/rejects
|
|||
|
|
if p == 2 then return _True
|
|||
|
|
if p < 2 then return _False
|
|||
|
|
// no even exponents (except the handled p = 2)
|
|||
|
|
if ( p AND 1 ) == 0 then return _False
|
|||
|
|
// exponent itself must be prime
|
|||
|
|
if fn IsPrime32( p ) == _false then return _False
|
|||
|
|
// build M_p = 2^p − 1
|
|||
|
|
mpz_set_ui( mp_minus1, 1 )
|
|||
|
|
mpz_mul_2exp( mp_minus1, mp_minus1, p ) // mp_minus1 = 1 << p
|
|||
|
|
mpz_sub_ui( mp_minus1, mp_minus1, 1 ) // Mp = 2^p - 1
|
|||
|
|
// Lucas–Lehmer sequence:
|
|||
|
|
mpz_set_ui( ts, 4 )
|
|||
|
|
k = 0
|
|||
|
|
// s = 4; repeat p−2 times: s = s^2 − 2 (mod M_p)
|
|||
|
|
while k < p - 2
|
|||
|
|
mpz_mul( ts, ts, ts ) // s = s^2
|
|||
|
|
mpz_sub_ui( ts, ts, 2 ) // s -= 2
|
|||
|
|
mpz_mod( ts, ts, mp_minus1 ) // s = s mod M_p
|
|||
|
|
k++
|
|||
|
|
wend
|
|||
|
|
if fn mpz_cmp_ui( ts, 0 ) == 0 then return _True
|
|||
|
|
end fn = _False
|
|||
|
|
|
|||
|
|
// ------------------------------------------------------------
|
|||
|
|
// LL_More
|
|||
|
|
// Scan upward from gN until the next M_p is found
|
|||
|
|
// ------------------------------------------------------------
|
|||
|
|
|
|||
|
|
void local fn LL_More
|
|||
|
|
CFStringRef pline
|
|||
|
|
boolean iterating
|
|||
|
|
unsigned long i
|
|||
|
|
|
|||
|
|
tim = fn CACurrentMediaTime
|
|||
|
|
|
|||
|
|
iterating = _true
|
|||
|
|
// start from the next exponent after gN
|
|||
|
|
i = gN + 1
|
|||
|
|
// ensure we start from a valid candidate:
|
|||
|
|
if i <= 2
|
|||
|
|
i = 2
|
|||
|
|
else
|
|||
|
|
if ( i AND 1 ) == 0
|
|||
|
|
i++
|
|||
|
|
end if
|
|||
|
|
end if
|
|||
|
|
|
|||
|
|
while iterating
|
|||
|
|
if fn IsMersennePrime_LL( i )
|
|||
|
|
gCount++
|
|||
|
|
//pline = fn StringWithFormat( @"M%lu", i )
|
|||
|
|
pline = fn StringWithFormat(@"%4lu M%lu", gCount, i)
|
|||
|
|
print pline;
|
|||
|
|
printf @" (%.3f secs)", ( fn CACurrentMediaTime - tim )
|
|||
|
|
iterating = _false
|
|||
|
|
end if
|
|||
|
|
// advance to next candidate exponent
|
|||
|
|
if i == 2
|
|||
|
|
i = 3
|
|||
|
|
else
|
|||
|
|
i = i + 2
|
|||
|
|
end if
|
|||
|
|
wend
|
|||
|
|
// remember last exponent tested so [Next] continues from here
|
|||
|
|
if i >= 3
|
|||
|
|
gN = i - 2
|
|||
|
|
else
|
|||
|
|
gN = i
|
|||
|
|
end if
|
|||
|
|
end fn
|
|||
|
|
|
|||
|
|
// ------------------------------------------------------------
|
|||
|
|
// Compute Mersenne primes up to an initial exponent gN
|
|||
|
|
// ------------------------------------------------------------
|
|||
|
|
local fn Lucas_LehmerMP
|
|||
|
|
unsigned long i
|
|||
|
|
|
|||
|
|
fn InitBigInts
|
|||
|
|
|
|||
|
|
print @"\nMersenne primes found with Lucas–Lehmer:\n"
|
|||
|
|
|
|||
|
|
// initial exponent limit for the first scan
|
|||
|
|
gN = 5000
|
|||
|
|
gCount = 0
|
|||
|
|
|
|||
|
|
tim = fn CACurrentMediaTime
|
|||
|
|
// handle p = 2 separately
|
|||
|
|
if gN >= 2
|
|||
|
|
gCount++
|
|||
|
|
i = 2
|
|||
|
|
printf @"%4lu M%lu", gCount, i
|
|||
|
|
end if
|
|||
|
|
|
|||
|
|
// scan odd exponents 3..gN
|
|||
|
|
i = 3
|
|||
|
|
while i <= gN
|
|||
|
|
if fn IsMersennePrime_LL( i )
|
|||
|
|
gCount++
|
|||
|
|
printf @"%4lu M%lu", gCount, i
|
|||
|
|
end if
|
|||
|
|
i = i + 2
|
|||
|
|
wend
|
|||
|
|
|
|||
|
|
printf @"\nElapsed time: %.3f secs", ( fn CACurrentMediaTime - tim )
|
|||
|
|
print @"\nClick [Next] for the next exponents.\n"
|
|||
|
|
end fn
|
|||
|
|
|
|||
|
|
// ------------------------------------------------------------
|
|||
|
|
// Dialog handler
|
|||
|
|
// ------------------------------------------------------------
|
|||
|
|
void local fn DoDialog( ev as long, tag as long, wnd as long, obj as CFTypeRef )
|
|||
|
|
select ev
|
|||
|
|
case _btnClick
|
|||
|
|
select tag
|
|||
|
|
case 2
|
|||
|
|
fn LL_More
|
|||
|
|
end select
|
|||
|
|
|
|||
|
|
case _windowShouldClose
|
|||
|
|
fn ClearBigInts
|
|||
|
|
end
|
|||
|
|
end select
|
|||
|
|
end fn
|
|||
|
|
|
|||
|
|
on dialog fn DoDialog
|
|||
|
|
|
|||
|
|
// ------------------------------------------------------------
|
|||
|
|
// Main
|
|||
|
|
// ------------------------------------------------------------
|
|||
|
|
window 1, @"Lucas–Lehmer Test", ( 0, 0, 620, 500 )
|
|||
|
|
button 2, YES,, @"Next", ( 500, 20, 100, 20 ),,, 1
|
|||
|
|
|
|||
|
|
fn Lucas_LehmerMP
|
|||
|
|
|
|||
|
|
HandleEvents
|
|||
|
|
|
|||
|
|
//
|