RosettaCodeData/Task/Lucas-Lehmer-test/FutureBasic/lucas-lehmer-test.basic

205 lines
5.2 KiB
Text
Raw Permalink Normal View History

2026-04-30 12:34:36 -04:00
// ------------------------------------------------------------
// LucasLehmer Mersenne prime tester
// Using FutureBasic 7.0.37
//
// November 2025, R.W.
// ------------------------------------------------------------
include "gmp.incl"
// ------------------------------------------------------------
// LucasLehmer Mersenne prime tester
// ------------------------------------------------------------
CFTimeInterval tim
// Global mpz_t bigints reused everywhere
mpz_t ts // LucasLehmer 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
// LucasLehmer 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
// LucasLehmer sequence:
mpz_set_ui( ts, 4 )
k = 0
// s = 4; repeat p2 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 LucasLehmer:\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, @"LucasLehmer Test", ( 0, 0, 620, 500 )
button 2, YES,, @"Next", ( 500, 20, 100, 20 ),,, 1
fn Lucas_LehmerMP
HandleEvents
//