204 lines
5.2 KiB
Text
204 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
|
||
|
||
//
|