I raised this issue regarding 0.17.2 but it still persists in 0.18.0.
I want to generate some system constants at compile time.
I need to generate some constant arrays of numbers for a (selectable) prime generator (PG).
When the array size gets past some (?) value the compiler quits with this message:
generating parameters for P13
stack trace: (most recent call last)
twinprimes_ssozp13par5d.nim(81)
lib/system.nim(3806) genPGparameters
lib/system.nim(3806, 14) Error: interpretation requires too many iterations
This SERIOUSLY impedes my development process in Nim, which doesn't exist in C++.
I provide code for the case that will compile, then the version that the compiler squawks at.
The array residues for P13 (prime generator for prime of 13) contains 5760 values. The array restwins contains 2970 values.
When I attempt to compile array inverses, containing another 5760 values, the compiler squawks.
I ended up using Ruby to generate the inverses array values, then displayed to terminal, copied, pasted into an editor, line formatted, copied, then pasted the formatted array values into my Nim source code so I could compile and run the program.
I need to do the next higher prime generator P17, whose array residues|resinvrs each contain 92,160 values, but the compiler ends when trying to create just the residues array.
So somewhere between (5760 + 2970) = 8730 - 92,160 iterations, the compiler stops working.
Besides this being an arbitrary decision to have the compiler act like this, it's undocumented, and there is apparently no (known) mechanism to extend this arbitrary limit to a user. Is there some compiler flag to eliminate|extend it?
To say the least This makes me Very Unhappy!! :-(.
My development has stalled because of this. And it is totally not feasible for me to generate two 92,160 element arrays in Ruby (which it has no problem doing) and go through the print|format|copy|paste dance of two 1000+ lines of numbers, again. And that's just for P17. P19 has 1,658,880 values for each array,
It would make me very, very Happy :-) to eliminate this barrier to my continued project development.
# This code will compile
import math # for sqrt function
import strutils, typetraits # for number input
import times, os # for timing code execution
import osproc # for getting threads count
import threadpool # for parallel processing
{.experimental.} # required to use 'parallel' (0.17.x)
proc genPGparameters(prime: int): (int, int, int, seq[int], seq[int]) =
echo("generating parameters for P", prime)
let primes = [2, 3, 5, 7, 11, 13, 17, 19, 23]
var modpg = 1
for prm in primes: (modpg *= prm; if prm == prime: break)
var pc = 1
var residues: seq[int] = @[]
while pc < modpg: (pc += 2; if gcd(modpg, pc) == 1: residues.add(pc))
let rescnt = residues.len
var restwins: seq[int] = @[]
var j = 0
while j < rescnt-1:
if residues[j]+2 == residues[j+1]:
restwins.add residues[j]; restwins.add residues[j+1]; j.inc
j.inc
let rescntp = restwins.len
result = (modpg, rescnt, rescntp, residues, restwins)
# Generate at compile time the parameters for P13
const parameters = genPGparameters(13)
# This won't compile with the addition of creating the 'inverses' array
import math # for sqrt function
import strutils, typetraits # for number input
import times, os # for timing code execution
import osproc # for getting threads count
import threadpool # for parallel processing
{.experimental.} # required to use 'parallel' (0.17.x)
proc genPGparameters(prime: int): (int, int, int, seq[int], seq[int], seq[int]) =
echo("generating parameters for P", prime)
let primes = [2, 3, 5, 7, 11, 13, 17, 19, 23]
var modpg = 1
for prm in primes: (modpg *= prm; if prm == prime: break)
var pc = 1
var residues: seq[int] = @[]
while pc < modpg: (pc += 2; if gcd(modpg, pc) == 1: residues.add(pc))
let rescnt = residues.len
var restwins: seq[int] = @[]
var j = 0
while j < rescnt-1:
if residues[j]+2 == residues[j+1]:
restwins.add residues[j]; restwins.add residues[j+1]; j.inc
j.inc
let rescntp = restwins.len
var inverses: seq[int] = @[]
j = 0
while j < rescnt:
let res = residues[j]
for inv in residues:
if res*inv mod modpg == 1: (inverses.add(inv); break)
j.inc
result = (modpg, rescnt, rescntp, residues, restwins, inverses)
# Generate at compile time the parameters for P13
const parameters = genPGparameters(13)
I could use Ruby to create all the parameters and dump them into a file too, which would be easier.
I Don't want to jump through extra HOOPs because the Nim compiler is deficient!!
I don't have to do this in C++.
The compiler should work for the human, not the other way around.
PLEASE, PLEASE, PLEASE, FIX THE COMPILER!!
I could use Ruby to create all the parameters and dump them into a file too, which would be easier.
You could, couldn't you? It does sound better than doing this:
displayed to terminal, copied, pasted into an editor, line formatted, copied, then pasted the formatted array values into my Nim source code
So maybe you should give it some consideration. Or you could wait for someone to add your desired compiler flag, I suppose.
I don't have to do this in C++.
The compiler should work for the human, not the other way around.
I don't know who the C++ compiler works for, but it's certainly not human kind.
It's a mystery to me in what sense C++ supports that out of the box
C++ has supported the feature of throwing the compiler into an infinite loop at least since the introduction of an accidentally Turing-complete template system.
I'm also reached this limit before, but avoided it by simplifying compile-time code.
Alright, I take it back. :-)
BTW let instead of const for computed lookup tables is also an option.
I started changing the MaxLoopIterations* = 1500_000 # max iterations of all loops line in compiler/vmdef.nim in increments of 1 million, until it was 20M, recompiling after each change, and still got error. Then I went up in 100M increments until 1 trillion, and still got error.
Do I need to recompile|rebuild Nim to make the change? Is merely changing the file sufficient? I doesn't seem so.
OK, I finally got it to compile with the original iteration count, but man, did I have to jump through hoops to do it. :-(
To reduce the number of loops, instead of doing brute force trial-and-error to test each residue against the others to find its inverse, I found the code here
https://rosettacode.org/wiki/Modular_inverse#Nim
on Rosetta Code, that had a Nim version already done. [Notice: the code there doesn't compile without changing proc mulInv(a0, b0): int = to proc mulInv(a0, b0: int): int =. In fact, quite a few of those old Rosetta code Nim examples don't compile now, but I digress.]
proc mulInv(a0, b0: int): int =
var (a, b, x0) = (a0, b0, 0)
result = 1
if b == 1: return
while a > 1:
let q = a div b
a = a mod b
swap a, b
result = result - q * x0
swap x0, result
if result < 0: result += b0
Then I was able to eliminate the loop-in-a-loop construct, and just do this:
var inverses: seq[int] = @[]
for res in residues: inverses.add(mulInv(res,modpg))
I was trying to avoid using another function just to do this, since I don't use it at runtime, but you do what you gotta do. :-(
Does Nim already have this function in its library? If not, can you please add it (math lib, etc).
Below is final code that compiles with original compiler iteration value, with some added diagnostic checks.
[Note: I changed function name to more standard|descriptive usage.]
# Compute modular inverse of a to base b, e.g. a*a_inverse mod b = 1
proc modinv(a0, b0: int): int =
var (a, b, x0) = (a0, b0, 0)
result = 1
if b == 1: return
while a > 1:
let q = a div b
a = a mod b
swap a, b
result = result - q * x0
swap x0, result
if result < 0: result += b0
# Create constant parameters for chosen PG at compile time
proc genPGparameters(prime: int): (int, int, int, seq[int], seq[int], seq[int]) =
echo("generating parameters for P", prime)
let primes = [2, 3, 5, 7, 11, 13, 17, 19, 23]
var modpg = 1
for prm in primes: (modpg *= prm; if prm == prime: break)
var pc = 1
var residues: seq[int] = @[]
while pc < modpg: (pc += 2; if gcd(modpg, pc) == 1: residues.add(pc))
let rescnt = residues.len
var restwins: seq[int] = @[]
var j = 0
while j < rescnt-1:
if residues[j]+2 == residues[j+1]:
restwins.add residues[j]; restwins.add residues[j+1]; j += 1
j += 1
let rescntp = restwins.len
inverses: seq[int] = @[]
for res in residues: inverses.add(modinv(res,modpg))
echo("residues size = ", residues.len)
echo("inverses size = ", inverses.len)
for j in 0..residues.len-1: (if residues[j]*inverses[j] mod modpg != 1: echo("error at index ",j))
result = (modpg, rescnt, rescntp, residues, restwins, inverses)
# Generate at compile time the parameters for P13
const parameters = genPGparameters(13)
This must be is under the Magical Limit using P13. :-)
So what will it do with P17? And the answer is.......Nope! It bombs (again) when trying to compute the 92,160 values for the residues array, as it never gets to the inverse array computation. I ultimately tried changing the iteration variable upto 1 trillion again before giving up. I think I will need to rebuild Nim with a new value in that file to make it take. That's for tomorrow (maybe), unless someone can instruct me how to do it without rebuilding Nim.
So, at least partial success, and I have learned something new and useful.
But Please, FIX THIS!, and DOCUMENT IT!
Why not add a flag to set/disable the iteration limit like he wants? I'm sure that as the user base grows, more people are going to hit the limit and get annoyed.
Because I am curious whether it can be supported out of the box by a "reasonable" limit first.
OK, I set MaxLoopIterations* = 1_000_000_000 (1 Billion) in ~/nim-0.18.0/compiler/vmdef.nim and rebuilt doing ./koch boot -d:release and was able to compile P17.
I think this value is way more reasonable, because as stated previously, on modern machines (or really any system with gigahertz clock speeds) we're only talking seconds for even a billion iterations.
I really hope you will up the value. Until so, I'll patch my own systems. Thanks for instructing how to do this.
Again, I strongly urge you to PROMINENTLY DOCUMENT this capability too.
@jzakiya: The limit is at one billion now.
+1!! :-)