I want to execute the code below at compile time to initialize the global variables shown - modpg, rescnt, residues, ep.
proc genPGparameters(prime: int): tuple =
echo("generating parameters for P", prime)
let primes = [2, 3, 5, 7, 11, 13, 17, 19, 23]
var modpg = 1
var excluded_primes = 0
for prm in primes:
excluded_primes += 1; modpg *= prm; if prm == prime: break
var pc = 3; var residues = @[1]
while pc < modpg:
if gcd(modpg, pc) == 1: residues.add(pc)
pc += 2
residues.add(modpg + 1)
let rescnt = residues.len-1
result = (modpg, rescnt, residues, excluded_primes)
const (modpg, rescnt, residues, ep) = genPGparameters(11)
I get this following error message pointer about the last line.
ssozp11x1c3parnew.nim(58, 7) Error: identifier expected, but found '(
I can't find the docs for this case, but I guess it's something simple. How do I get the code to work?
Hi jzakiya, for me I will get: Error: undeclared identifier: 'gcd' (also when you type the run-button below your code snippet).
From the tutorial: https://nim-lang.org/docs/tut1.html#constants
https://nim-lang.org/docs/tut1.html#advanced-types-tuples
I think the tuple-type is missing. This compiles for me:
proc genPGparameters(prime: int): tuple[v1: int,v2:int,v3:seq[int],v4:int] =
echo("generating parameters for P", prime)
let primes = [2, 3, 5, 7, 11, 13, 17, 19, 23]
var modpg = 1
var excluded_primes = 0
for prm in primes:
excluded_primes += 1; modpg *= prm; if prm == prime: break
var pc = 3; var residues = @[1]
while pc < modpg:
# if gcd(modpg, pc) == 1: residues.add(pc)
pc += 2
residues.add(modpg + 1)
let rescnt = residues.len-1
result = (modpg, rescnt, residues, excluded_primes)
const test = genPGparameters(11)
I do import math to get gcd in my program, that's why you got that error.
I still can't get the output from getPGparameters assigned to constants of the same names, to be used later in the program. I thought you could do assignments like:
const (a, b, c) = tuple(x, y, z)
If tuples aren't the way to do this I don't care, I just want to generate the parameters and assign them at compile time, to make it easier to use.
This should work
import math
proc genPGparameters(prime: int): (int, int, seq[int], int) =
echo("generating parameters for P", prime)
let primes = [2, 3, 5, 7, 11, 13, 17, 19, 23]
var modpg = 1
var excluded_primes = 0
for prm in primes:
excluded_primes += 1; modpg *= prm; if prm == prime: break
var pc = 3; var residues = @[1]
while pc < modpg:
if gcd(modpg, pc) == 1: residues.add(pc)
pc += 2
residues.add(modpg + 1)
let rescnt = residues.len-1
result = (modpg, rescnt, residues, excluded_primes)
const res = genPGparameters(11)
echo res
Hi jzakiya,
you simply can not use brackets within your constant-name. Here is the BNF which literals are allowed:
https://nim-lang.org/docs/manual.html#lexical-analysis-numerical-constants
As long as your tuple have no field names you can access the tuples fields with the index operator []
const tconst = genPGparameters(11)
echo tconst[0]
# access the tuples fields with the index operator
echo tconst[1]
for item in tconst[2].pairs:
echo $item
echo tconst[3]
If you want to unpack the tuple you have to declare the resulting variables first, like this:
proc foo(): (int, int, int) = (1,2,3)
static:
var a,b,c : int
(a,b,c) = foo()
echo a,b,c
what is quite strange is that this does not pass gcc compilation, it might be a bug:
proc foo(): (int, int, int) = (1,2,3)
static:
var a,b,c : int
(a,b,c) = foo()
echo a,b,c
Hi woggioni,
the static code block is executed at compiletime inside the vm. you cannot access the var parameters straight outside the block. This is why you get undecleared identifier. You need to declare some consts to get around this. But indeed there could be a more meaningful error message instead of the c-backend complain.
this works:
proc foo(): (int, int, int) = (1,2,3)
static:
var a,b,c : int
(a,b,c) = foo()
const
a1 = a
b1 = b
c1 = c
echo a1,b1,c1
EDIT: this could be related(but the example there makes no sense)
https://github.com/nim-lang/Nim/issues/6681
It´s only allowed to assign the parameters to a const-type outside your static block
Hey thanks for the help!
Here's what I had to do to get it to compile/work fully (using Nim 0.17.2).
import math
proc genPGparameters(prime: int): (int, int, int, seq[int]) =
echo("generating parameters for P", prime)
let primes = [2, 3, 5, 7, 11, 13, 17, 19, 23]
var modpg = 1; var excluded_primescnt = 0
for prm in primes:
excluded_primescnt += 1; modpg *= prm; if prm == prime: break
var pc = 3; var residues = @[1]
while pc < modpg:
if gcd(modpg, pc) == 1: residues.add(pc)
pc += 2
residues.add(modpg + 1)
let rescnt = residues.len-1
result = (modpg, rescnt, excluded_primescnt, residues)
const parameters = genPGparameters(11)
const
modpg = parameters[0]
rescnt = parameters[1]
ep = parameters[2]
residues = parameters[3]
echo("modpg = ", modpg, ", rescnt = ", rescnt, ", excluded_primescnt = ", ep)
echo("residues = ", residues)
The only quirks I had to deal with after the parameters were generated/assigned was I had to recast in the code some places where I used modpg and rescnt to do math. The compiler told me where the casting incompatibilities were, and I just had to use modpg.unit and rescnt.uint in a few places.
The full compiled code is about 5KB larger than the one with explicitly coded values, buts runs with the same speed. This now will allow me to use any Strictly Prime (SP) Prime Generator (PG) without having to create a specific version for them separately. Now I can just change the prime value for genPGparameters and, voila, its done.
The only other question I have is why does the compiler treat differently in the code when modpg/rescnt are assigned specific numerical values versus from the compiletime generated ones?
I would highly recommend putting this kind of example in the Docs/Nim Cookbook. Coming from Ruby/Elixir, this kind of thing is done all the time.
Coming from Ruby/Elixir,
Maybe some C exercises would help :-)
Note that modpg.unit and rescnt.uint are not plain casts (reinterpretation of bit pattern) but type conversions. Type conversions may ensure that the values are not negative before conversion and raise exceptions otherwise. I don't know if -d:release will turn of that check. And when you use a type conversion, then the compiler can not work with real constant values any longer, which may degrade performance. If you are sure that conversion is valid, you may use a plain cast like castuint.
But why are you not using uint from the beginning for your constants if you desire uints?
I would assume that this compiles.
const
modpg = parameters[0].uint
Well, that will not work for the seq.
Maybe try to fix the proc signature to this:
proc genPGparameters(prime: int): (uint, uint, uint, seq[uint]) =
What I was trying to get at is how to make the generated constant values behave like coding specific numerical numbers.
Apparently for numerical constants, when you do this: const modpg = 2310 the compiler will convert it to whatever is required to make math operations work (at least it did so in my code).
But doing: const modpg = parameter[0] causes it to retain the int casting from the proc, which then requires explicit casting when doing math with differently cast values.
It would be nice (so I wouldn't need to explicitly recast these values in some places) if I could make the later case behave like the former case (as explicit uncast numbers).
I can obviously live with it, but it would be nice to have some sort of null casting to make assigned constant values behave the same as explicit numbers.
In the former case I had gone thru casting hell to get the math to work with the least amount of explicit casting (to reduce the coding noise), and it would be nice if the generated constants behaved the same.
Apparent for numerical constants, when you do this: const modpg = 2310 the compiler will convert it to whatever is required to make math operations work (at least it did so in my code).
But doing: const modpg = parameter[0] causes it to retain the int casting from the proc, which then requires explicit casting when doing math with differently cast values.
That is an interesting observation.
So in the later case the const works more like a let statement.
let num = 10
const x = 9
echo(num + x)
let num = 10.int
const x = 9
echo(num + x)
let num = 10.uint
const x = 9
echo(num + x)
let num = 10.89
const x = 9
echo(num + x)
let num = 10.89
const x = 9.5
echo(num + x)
It would be really, really, really nice to also have compile-time constant assignment not create cast mismatches without having to do explicit casting, and retain the implicit characteristics of explicit numerical constant assignment.