Skip to content

libgis: Add a seed option parsing function - #8033

Merged
wenzeslaus merged 4 commits into
OSGeo:mainfrom
wenzeslaus:seed-option
Oct 17, 2026
Merged

wenzeslaus merged 4 commits into
OSGeo:mainfrom
wenzeslaus:seed-option

Conversation

@wenzeslaus

@wenzeslaus wenzeslaus commented Oct 7, 2026 •

Copy link
Copy Markdown
Member

Tools which take a seed option each parse and check the seed on their own, with different rules and messages, since the parser checks an integer option only loosely. This adds one libgis function which reads and checks the seed option with the same rules everywhere, and makes r.sim.water and r.sim.sediment use it. The parsing itself is a new function, G_random_parse_seed(), which the environment variables use too, so a value of GRASS_RANDOM_SEED outside the range is now an error instead of being reduced to its low 32 bits with a warning. What a tool does without the option, such as generating a seed, stays with the tool.

Follows the r.sim migration (#7980) and the rename of its seed option (#8032), both merged.

Details (AI generated)

The function

G_random_seed_from_option(const struct Option *seed) in the new lib/gis/random_options.c, called after G_parser() with an option which has an answer. The answer is read with strtoll(), and a value which is not an integer, has trailing characters, or is outside -2147483648 to 4294967295 is a fatal error naming the option, for example Invalid random seed <12abc> for seed=: not an integer. The range is checked on the long long before it is narrowed to the int64_t the layout functions take. The function neither prints nor records the seed; the tool does that.

An earlier version also took the flag to generate a seed and required exactly one of the two. No tool wants that rule: r.mapcalc seeds automatically when the expression calls rand() and is phasing out -s, r.sim generates a seed without the option, and tools such as r.surf.random and v.random have no flag, so the function reads the option only.

The parser

const char *G_random_parse_seed(const char *text, int64_t *seed) in lrand48.c reads a decimal integer with nothing after it and checks the range; it returns NULL and sets the seed, or a phrase saying what a seed must be, which the caller puts after a colon in a message naming the text and its source. The option function and the environment path are the two callers, each with its own message. The dependency runs from random_options.c to the generator code only; the generator knows nothing of options. A value from GRASS_RANDOM_SEED or SOURCE_DATE_EPOCH outside -2147483648 to 4294967295 was reduced to its low 32 bits with a warning, which was the original behavior; it is now an error like any other bad seed, since a seed cut to its low bits is what the checks exist to prevent. The variables page says so.

r.sim

r.sim.water and r.sim.sediment previously parsed seed with their own strtoll() call and built a throwaway generator state to have the range checked before the inputs are read; they now call the function. The generated seed and the deprecated -s are unchanged. Tests check the range limits are accepted and 1e9 is refused.

Use in other tools

r.mapcalc parses and checks the seed itself today (its migration to the layouts is a separate PR). With the function, the seed handling there becomes:

if (seed->answer && random->answer)
    G_fatal_error(_("%s= and -%c are mutually exclusive"), seed->key,
                  random->key);
/* Refuse an invalid seed before the expression is parsed. */
if (seed->answer)
    seed_value = G_random_seed_from_option(seed);
...
if (seed->answer)
    seeded = 1;
else if (rand_calls > 0) {
    seed_value = G_random_generate_seed();
    seeded = 1;
}

The code and this description were written with Claude Code (Claude Fable 5.1) and reviewed by the author of the PR.

The random seed is the option seed in the eleven other tools which take
one, all through G_OPT_M_SEED, and only r.sim.water and r.sim.sediment
call it random_seed. The key in the history and the run summary is seed
already. The old name keeps working through the renamed options list,
with the parser's warning, as the earlier renames of these tools do.

Written with the help of Claude Code.
Tools which take a seed option each parse and check the seed on their
own, with different rules and messages, since the parser itself checks
an integer option only loosely: 12abc and 1.5 pass as integers.
G_random_seed_from_option() reads the answer with strtoll(), refuses
what is not wholly an integer or is outside the range from -2^31 to
2^32 - 1 with a message naming the option, and returns the seed as
int64_t after checking the range on the long long, so the narrowing is
exact. It does nothing beyond parsing and checking; recording the seed
or generating one stays with the tool. r.sim.water and r.sim.sediment
use it in place of their own parsing, which also drops the throwaway
state they built to check the range before reading the inputs. Their
tests match the range message by words, since it is wrapped.

Written with the help of Claude Code.
@github-actions github-actions Bot added raster Related to raster data processing Python Related code is in Python C Related code is in C HTML Related code is in HTML libraries module docs markdown Related to markdown, markdown files tests Related to Test Suite notebook labels Oct 7, 2026
# Conflicts:
#	raster/r.sim/r.sim.sediment/tests/r_sim_sediment_random_test.py
#	raster/r.sim/r.sim.water/tests/r_sim_water_random_test.py
@wenzeslaus
wenzeslaus marked this pull request as ready for review October 8, 2026 02:24
@wenzeslaus wenzeslaus changed the title libgis: Add a function which reads the seed option of a tool libgis: Add a seed option parsing function Oct 8, 2026
Comment thread lib/gis/random_options.c Outdated
…range

G_random_parse_seed() reads a decimal integer from a text and says, when
it is not a seed, what a seed must be; the seed option function and
the environment variables both call it, with their own messages. A value
from GRASS_RANDOM_SEED or SOURCE_DATE_EPOCH outside -2^31 to 2^32 - 1
is now an error, as from the option, instead of being reduced to its
low 32 bits with a warning: a seed cut to its low bits is what the
checks exist to prevent, and a value beyond long long no longer gets a
message of its own.

Written with the help of Claude Code.
@wenzeslaus
wenzeslaus merged commit a92cbed into OSGeo:main Oct 17, 2026
27 checks passed
@github-actions github-actions Bot added this to the 8.6.0 milestone Oct 17, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

C Related code is in C docs HTML Related code is in HTML libraries markdown Related to markdown, markdown files module notebook Python Related code is in Python raster Related to raster data processing tests Related to Test Suite

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants