random.fz
# This file is part of the Fuzion language implementation.
#
# The Fuzion language implementation is free software: you can redistribute it
# and/or modify it under the terms of the GNU General Public License as published
# by the Free Software Foundation, version 3 of the License.
#
# The Fuzion language implementation is distributed in the hope that it will be
# useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
# License for more details.
#
# You should have received a copy of the GNU General Public License along with The
# Fuzion language implementation. If not, see <https://www.gnu.org/licenses/>.
# -----------------------------------------------------------------------
#
# Tokiwa Software GmbH, Germany
#
# Source code of Fuzion standard library feature random
#
# Author: Fridtjof Siebert (siebert@tokiwa.software)
#
# -----------------------------------------------------------------------
# random -- effect that provides random numbers
#
public random (
# the handler this effect uses to create random numbers
p Random_Handler,
redef r effect_mode.val
) : effect r
is
# for a random instance installed in the current environment, update the
# environment to the next random value. The the contents of the current
# instance are left unchanged, 'random.env' will provide the new instance.
#
next => random p.next mode
# return next random number of type i32 in the range 0..max-1
#
public next_i32(max i32)
pre max ≥ 0
=> (next.get max.as_u64).as_i32
# return next random number of type i64 in the range 0..max-1
#
public next_i64(max i64)
pre max ≥ i64 0
=> (next.get max.as_u64).as_i64
# return next random number of type u32 in the range 0..max-1
#
public next_u32(max u32) => (next.get max.as_u64).as_u32
# return next random number of type u64 in the range 0..max-1
#
public next_u64(max u64) => next.get max
# return next random number of type f32 in the range [0..1),
# result will never be equal to f32 1
#
public next_f32 =>
max := u64 1 << f32.significand_bits.as_u64
(next_u64 max).as_f64.as_f32 / max.as_f64.as_f32
# return next random number of type f64 in the range [0..1),
# result will never be equal to f64 1
#
public next_f64 =>
max := u64 1 << f64.significand_bits.as_u64
(next_u64 max).as_f64 / max.as_f64
# return next random number as a bool
#
public next_bool =>
(next.get 2) %% 2
# get the current random number in the range 0..max-1
#
public get (max u64)
=>
p.get % max # NYI: This implementation is bad if max is big
# install default instance of random
#
type.install_default is
if !effect.is_installed random
_ := random simple_random_handler.default effect_mode.default
# random with no argument returns random.env, i.e., the currently installed
# source of randomness.
#
public random =>
random.install_default
random.env
public simple_random(seed u64, rr ()->unit) =>
_ := random (simple_random_handler seed seed) (effect_mode.inst rr)
public time_seeded_random(rr ()->unit) =>
_ := random simple_random_handler.time_seeded (effect_mode.inst rr)
# Random_Handler -- abstract source of random numbers
#
# This provides the random number input to be used by the 'random' effect.
# Implementation may be dumb sequences of pseudo-random numbers or high-qualiity
# cryptographic random number generators
#
# A Random_Handler contains an immutable state, repeated calls to 'get' result
# in the same value. To produce a sequence of different random numbers, 'next'
# must be used to create a new instance of 'Random_Handler' before 'get' can be
# used to obtain the new random number.
#
private:public Random_Handler ref is
# create a new instance of Random_Handler containing a new state. Depending
# on the qualifity of this random number generator, this might be a simple
# function of the original Random_Handler or it might use an external source
# of entropy to create a new instance.
#
next Random_Handler => abstract
# Return the random number stored in this instance of Random_Handler, in the
# range 0..u64.max
#
# NOTE: this feature is pure, i.e., repeated calls on the same target result
# in equal results. Use 'next' to get different results.
#
get u64 => abstract
# simple random number handler for pseudo random numbers that are not safe
# for security and that do not meet typical requirements for a good
# random number generator.
#
simple_random_handler(seed1 u64, seed2 u64) : Random_Handler is
# get next instance by shuffling bits in seeds around
#
next Random_Handler =>
# NYI: This does not meet any requirements on a decent random number generator
s1 := (seed1 *° 1717171717 +° 13113131313) ^ seed2
s2 := (seed2 *° 9191919191 +° 37637373737) ^ seed1
rot1 := s2 & 0x3f
rot2 := s1 & 0x3f
(simple_random_handler ((s2 << rot2) | (s2 >> (u64 64 - rot2)))
((s1 << rot1) | (s1 >> (u64 64 - rot1))))
# get current random number
#
get u64 => seed1
# name of env var to provide default random seed.
#
# If set, this u64-value will be used too seed the default random number generator.
# If not set, the default random number generator will be time seeded.
#
# If set to unparsable number, panic on installation of the default random
# number generator.
#
type.random_seed_env_var => "FUZION_RANDOM_SEED"
# create the default random handler depending on the environment settings
#
type.default =>
match envir.vars.get random_seed_env_var
s String =>
match s.parse_u64
seed u64 => simple_random_handler seed 98765432109876543
e error => panic "Failed to parse value in env var '$random_seed_env_var': $e"
nil => time_seeded
# create a time-seeded simple handler for pseudo random numbers that are not safe
# for security and that do not meet typical requirements for a good
# random number generator.
#
type.time_seeded Random_Handler =>
# initialize with large numbers XORed with nano time
#
simple_random_handler (u64 77777777777777 ^ time.nano.read) 98765432109876543
# create a time-seeded handler for pseudo random numbers that are not safe
# for security and that do not meet typical requirements for a good
# random number generator.
#
type.time_seeded(rr ()->unit) =>
_ := random time_seeded (effect_mode.inst rr)
last changed: 2024-03-07