Below is the Nim direct translation of the C++ code in my paper. I have verified it on Nim 0.17 on Linux. In comparison on this same machine the Nim code runs a bit faster for some (larger >= 10^9) test values (not comprehensibly benchmarked yet).
I'll post it to a gist later to make it easier to download. I'm now re-architecting it to make it parallel friendlier (I hope). Feedback welcome on style, and performance tips. ssozp5.nim #[ This Nim source file will compile to an executable program to perform the Segmented Sieve of Zakiya (SSoZ) to find primes <= N. It is based on the P5 Strictly Prime (SP) Prime Generator. Prime Genrators have the form: mod*k + ri; ri -> {1,r1..mod-1} The residues ri are integers coprime to mod, i.e. gcd(ri,mod) = 1 For P5, mod = 2*3*5 = 30 and the number of residues are rescnt = (2-1)(3-1)(5-1) = 8, which are {1,7,11,13,17,19,23,29}. Adjust segment byte length parameter B (usually L1|l2 cache size) for optimum operational performance for cpu being used. Verified on Nim 0.17, using clang (smaller exec) or gcc $ nim c --cc:[clang|gcc] --d:release ssozp5.nim Then run executable: $ ./ssozp5 <cr>, and enter value for N. As coded, input values cover the range: 7 -- 2^64-1 Related code, papers, and tutorials, are downloadable here: http://www.4shared.com/folder/TcMrUvTB/_online.html Use of this code is free subject to acknowledgment of copyright. Copyright (c) 2017 Jabari Zakiya -- jzakiya at gmail dot com Version Date: 2017/08/21 This code is provided under the terms of the GNU General Public License Version 3, GPLv3, or greater. License copy/terms are here: http://www.gnu.org/licenses/ ]# import math import strutils, typetraits let pbits = [ 8,7,7,6,7,6,6,5,7,6,6,5,6,5,5,4,7,6,6,5,6,5,5,4,6,5,5,4,5,4,4,3 ,7,6,6,5,6,5,5,4,6,5,5,4,5,4,4,3,6,5,5,4,5,4,4,3,5,4,4,3,4,3,3,2 ,7,6,6,5,6,5,5,4,6,5,5,4,5,4,4,3,6,5,5,4,5,4,4,3,5,4,4,3,4,3,3,2 ,6,5,5,4,5,4,4,3,5,4,4,3,4,3,3,2,5,4,4,3,4,3,3,2,4,3,3,2,3,2,2,1 ,7,6,6,5,6,5,5,4,6,5,5,4,5,4,4,3,6,5,5,4,5,4,4,3,5,4,4,3,4,3,3,2 ,6,5,5,4,5,4,4,3,5,4,4,3,4,3,3,2,5,4,4,3,4,3,3,2,4,3,3,2,3,2,2,1 ,6,5,5,4,5,4,4,3,5,4,4,3,4,3,3,2,5,4,4,3,4,3,3,2,4,3,3,2,3,2,2,1 ,5,4,4,3,4,3,3,2,4,3,3,2,3,2,2,1,4,3,3,2,3,2,2,1,3,2,2,1,2,1,1,0 ] let residues = [1, 7, 11, 13, 17, 19, 23, 29, 31] # Global parameters const modp5 = 30 # prime generator mod value rescnt = 8 # number of residues for prime generator var pcnt = 0 # number of primes from r1..sqrt(N) primecnt = 0'u64 # number of primes <= N next: seq[uint64] # array of primes first multiples primes: seq[int] # array of primes <= sqrt(N) seg: seq[uint8] # seg[B] segment byte array proc sozp7(val: int | int64) = let md = 210 let rscnt = 48 let res = [1, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67 , 71, 73, 79, 83, 89, 97,101,103,107,109,113,121,127,131,137,139 ,143,149,151,157,163,167,169,173,179,181,187,191,193,197,199,209,211] var posn = newSeq[int](210) for i in 0 .. <rscnt: posn[res[i]] = i-1 let num = (val-1) or 1 # if value even number then subtract 1 var k = num div md # kth residue group var modk = md * k # base num value var r = 1 # starting with first residue while num >= modk+res[r]: r += 1 # find last pc position <= num let maxprms = k*rscnt + r - 1 # maximum number of prime candidates var prms = newSeq[bool](maxprms) # array of prime candidates set False let sqrtN = int(sqrt float64(num)) modk=0; r=0; k=0 # sieve to eliminate nonprimes from small primes prms array for prm in prms: r += 1; if r > rscnt: (r = 1; modk += md; k += 1) if prm: continue let res_r = res[r] let prime = modk + res_r if prime > sqrtN: break let prmstep = prime * rscnt for ri in res[1..rscnt]: let prod = res_r * ri # residues cross-product var nonprm = (k*(prime + ri) + prod div md)*rscnt + posn[prod mod md] while nonprm < maxprms: prms[nonprm] = true; nonprm += prmstep # the prms array now has all the positions for primes r1..N # extract prime numbers and count from prms into primes array primes = @[7] # r1 prime for P5 modk = 0; r = 0 for prm in prms: r += 1; if r > rscnt: (r = 1; modk += md) if not prm: primes.add(modk + res[r]) pcnt = len(primes) proc next_init() = var pos = newSeq[int](modp5) for i in 0 .. <rescnt: pos[residues[i]] = i-1 pos[1] = rescnt-1 # for each prime store resgroup on each restrack for prime*(modk+ri) for j, prime in primes: let k = uint((prime-2)) div uint(modp5) # find the resgroup it's in let r = uint((prime-2)) mod uint(modp5) + 2 # its base residue value for ri in residues[1 .. rescnt]: # for each residue value let prod: int = int(r) * ri # create residues cross-product r*ri let row: int = pos[prod mod modp5] * pcnt # create residue track address next[row + j] = k*(uint(prime) + uint(ri)) + uint(prod-2) div uint(modp5) proc segsieve(Kn: int) = # for Kn resgroups in segment for b in 0 .. <Kn: # for every byte in the segment seg[b] = 0 # set every byte bit to prime (0) for r in 0 .. <rescnt: # for each ith (of 8) residues for P5 let biti = uint8(1 shl r) # set the ith residue track bit mask let row = r*pcnt # set address to ith row in next[] for j in 0 .. <pcnt: # for each prime <= sqrt(N) for restrack if next[row+j] < uint(Kn): # if 1st mult resgroup index <= seg size var k: int = int(next[row+j]) # get its resgroup value let prime = primes[j] # get the prime while k < Kn: # for each primenth byte < segment size seg[k] = seg[k] or biti # set ith residue in byte as nonprime k += int(prime) next[row+j] = uint(k - Kn) # 1st resgroup in next eligible segment else: next[row+j] -= uint(Kn) # if 1st mult resgroup index > seg size # count the primes in the segment for byt in seg[0..<Kn]: # for the Kn resgroup bytes primecnt += uint(pbits[byt]) # count the '0' bits as primes proc printprms(Kn: int, Ki: uint64) = # Extract and print the primes in each segment: # recommend piping output to less: ./ssozpxxx | less # can then use Home, End, Pgup, Pgdn keys to see primes for k in 0 .. <Kn: # for Kn residues groups|bytes for r in 0 .. <8: # for each residue|bit in a byte if (int(seg[k]) and (1 shl r)) == 0: # if it's '0' it's a prime write(stdout, " ", uint64(modp5)*(Ki+uint64(k)) + uint64(residues[r+1])) #echo "\n" proc ssozp5() = stdout.write "Enter integer number: " let val = stdin.readline.parseBiggestUInt # find primes <= val (7..2^64-1) let B = 262144 # L2D_CACHE_SIZE 256*1024 bytes let KB = B # number of segment resgroups seg = newSeq[uint8](B) # create segment array of B bytes size writeLine(stdout, "segment has ", B, " bytes and ", KB, " residues groups") let num = uint64((val-1) or 1) # if val even subtract 1 var k: uint64 = num div modp5 # resgroup of num var modk = uint64(modp5) * k # base value of num var r = 1 # starting with first residue while num >= modk+uint64(residues[r]): r += 1 # find last pc position <= num let maxpcs = k * uint64(rescnt) + uint64(r-1) # maximum number of prime candidates let Kmax = uint64(num-2) div uint64(modp5) + 1 # maximum number of resgroups for val writeLine(stdout, "prime candidates = ", maxpcs, "; resgroups = ", Kmax) let sqrtN = int(sqrt float64(num)) sozP7(sqrtN) # get pcnt and primes <= sqrt(nun) writeLine(stdout, "create next[", rescnt, "x", pcnt, "] array") next = newSeq[uint64](rescnt*pcnt) # create the next[] array next_init() # load with first nonprimes resgroups primecnt = 3 # 2,3,5 the P5 excluded primes count var Kn: int = KB # set sieve resgroups size to segment size var Ki = 0'u64 # starting resgroup index for each segment echo "perform segmented SoZ\n"; while Ki < Kmax: # for KB size resgroup slices up to Kmax if Kmax-Ki < uint64(KB): Kn = int(Kmax-Ki) # set sieve resgroups size for last segment segsieve(Kn) # sieve primes for current segment #printprms(Kn,Ki) # print primes for the segment (optional) Ki += uint64(KB) # next segment slice var lprime = 0'u64 # get last prime and primecnt <= val modk = uint64(modp5)*(Kmax-1) # mod for last resgroup in last segment var b = Kn-1 # num bytes to last resgroup in segment r = rescnt-1 # from last restrack in resgroup while true: # repeat until last prime determined if (int(seg[b]) and (1 shl r)) == 0: lprime = modk+uint64(residues[r+1]) # determine the prime value if lprime <= num: break # if <= num exit from loop with lprime primecnt -= 1 # else reduce total primecnt # reduce restrack, setup next resgroup r -= 1; if r < 0: (r = rescnt-1; modk -= modp5; b -= 1) # if necessary writeLine(stdout, "last segment = ", Kn, " resgroups; segments iterations = ", Ki div uint64(KB)) writeLine(stdout, "last prime (of ", primecnt, ") is ", lprime) ssozp5()