diff --git a/Cargo.lock b/Cargo.lock index 0fc692cdd..623d5cf78 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -631,6 +631,7 @@ dependencies = [ "dashu-int", "num-modular", "num-order", + "rand", "rustversion", "static_assertions", ] @@ -645,6 +646,7 @@ dependencies = [ "dashu-base", "num-modular", "num-order", + "rand", "rustversion", "static_assertions", ] @@ -676,6 +678,7 @@ dependencies = [ "dashu-int", "num-modular", "num-order", + "rand", "rustversion", ] diff --git a/Cargo.toml b/Cargo.toml index ddb6f239e..a299e6900 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -46,7 +46,7 @@ bytes = "1" chrono = "0.4.38" cpu-time = "1.0.0" crrl = "0.9.0" -dashu = "0.4.2" +dashu = { version = "0.4.2", features = ["rand"] } derive_more = "0.99.18" dirs-next = "2.0.0" divrem = "1.0.0" diff --git a/build/instructions_template.rs b/build/instructions_template.rs index 5e64f3d79..f6f9351e7 100644 --- a/build/instructions_template.rs +++ b/build/instructions_template.rs @@ -419,6 +419,8 @@ enum SystemClauseType { GetUnknown, #[strum_discriminants(strum(props(Arity = "1", Name = "$install_new_block")))] InstallNewBlock, + #[strum_discriminants(strum(props(Arity = "3", Name = "$random_integer")))] + RandomInteger, #[strum_discriminants(strum(props(Arity = "0", Name = "$maybe")))] Maybe, #[strum_discriminants(strum(props(Arity = "1", Name = "$current_time")))] @@ -1805,6 +1807,7 @@ fn generate_instruction_preface() -> TokenStream { &Instruction::CallGetDoubleQuotes | &Instruction::CallGetUnknown | &Instruction::CallInstallNewBlock | + &Instruction::CallRandomInteger | &Instruction::CallMaybe | &Instruction::CallCpuNow | &Instruction::CallDeterministicLengthRundown | @@ -2042,6 +2045,7 @@ fn generate_instruction_preface() -> TokenStream { &Instruction::ExecuteGetDoubleQuotes | &Instruction::ExecuteGetUnknown | &Instruction::ExecuteInstallNewBlock | + &Instruction::ExecuteRandomInteger | &Instruction::ExecuteMaybe | &Instruction::ExecuteCpuNow | &Instruction::ExecuteDeterministicLengthRundown | diff --git a/src/lib/random.pl b/src/lib/random.pl index a32967976..649815744 100644 --- a/src/lib/random.pl +++ b/src/lib/random.pl @@ -15,16 +15,14 @@ % Succeeds with probability 0.5. maybe :- '$maybe'. -% The higher the precision, the slower it gets. -random_number_precision(64). - %% random(-R). % % Generates a random floating number between 0 (inclusive) and 1 (exclusive). random(R) :- var(R), - random_number_precision(N), - rnd(N, R). + N is 2^50, + '$random_integer'(0, N, K), + R is K/N. %% random_integer(+Lower, +Upper, -R). % @@ -41,25 +39,10 @@ type_error(integer, Lower, random_integer/3) ; \+ integer(Upper) -> type_error(integer, Upper, random_integer/3) - ; Upper > Lower, - random(R0), - R is floor((Upper - Lower) * R0 + Lower) + ; Lower < Upper, + '$random_integer'(Lower, Upper, R) ). -rnd(N, R) :- - rnd_(N, 0, R). - -rnd_(0, R, R) :- !. -rnd_(N, R0, R) :- - maybe, - !, - N1 is N - 1, - rnd_(N1, R0, R). -rnd_(N, R0, R) :- - N1 is N - 1, - R1 is R0 + 1.0 / 2.0 ^ N, - rnd_(N1, R1, R). - %% set_random(+Seed). % % Sets a seed that will be used for subsequent random generations in this library. diff --git a/src/machine/dispatch.rs b/src/machine/dispatch.rs index a3ace102f..f77f7f0f1 100644 --- a/src/machine/dispatch.rs +++ b/src/machine/dispatch.rs @@ -4061,6 +4061,14 @@ impl Machine { .install_new_block(self.machine_st.registers[1]); step_or_fail!(self, self.machine_st.p = self.machine_st.cp); } + &Instruction::CallRandomInteger => { + self.random_integer(); + step_or_fail!(self, self.machine_st.p += 1); + } + &Instruction::ExecuteRandomInteger => { + self.random_integer(); + step_or_fail!(self, self.machine_st.p = self.machine_st.cp); + } &Instruction::CallMaybe => { self.maybe(); step_or_fail!(self, self.machine_st.p += 1); diff --git a/src/machine/system_calls.rs b/src/machine/system_calls.rs index 371b97e14..3ab0831b2 100644 --- a/src/machine/system_calls.rs +++ b/src/machine/system_calls.rs @@ -4157,6 +4157,63 @@ impl Machine { } } + #[inline(always)] + pub(crate) fn random_integer(&mut self) { + let a1 = self.deref_register(1); + let a2 = self.deref_register(2); + let value = match (Number::try_from(a1), Number::try_from(a2)) { + (Ok(Number::Fixnum(lower)), Ok(Number::Fixnum(upper))) => { + let (lower, upper) = (lower.get_num(), upper.get_num()); + if lower >= upper { + self.machine_st.fail = true; + return; + } + let value = self.rng.gen_range(lower..upper); + Number::Fixnum(Fixnum::build_with(value)) + } + (Ok(Number::Fixnum(lower)), Ok(Number::Integer(upper))) => { + let lower = Integer::from(lower); + if &lower >= &*upper { + self.machine_st.fail = true; + return; + } + let value = self.rng.gen_range(lower..(&*upper).clone()); + Number::arena_from(value, &mut self.machine_st.arena) + } + (Ok(Number::Integer(lower)), Ok(Number::Fixnum(upper))) => { + let upper = Integer::from(upper); + if &*lower >= &upper { + self.machine_st.fail = true; + return; + } + let value = self.rng.gen_range((&*lower).clone()..upper); + Number::arena_from(value, &mut self.machine_st.arena) + } + (Ok(Number::Integer(lower)), Ok(Number::Integer(upper))) => { + if &*lower >= &*upper { + self.machine_st.fail = true; + return; + } + let value = self.rng.gen_range((&*lower).clone()..(&*upper).clone()); + Number::arena_from(value, &mut self.machine_st.arena) + } + _ => { + self.machine_st.fail = true; + return; + } + }; + let a3 = self.deref_register(3); + match value { + Number::Fixnum(n) => { + self.machine_st.unify_fixnum(n, a3); + } + Number::Integer(n) => { + self.machine_st.unify_big_int(n, a3); + } + _ => unreachable!(), + } + } + #[inline(always)] pub(crate) fn maybe(&mut self) { self.machine_st.fail = self.rng.gen();