From 3d4340c02d016c2a016b6a37fcd459b95ec0a46d Mon Sep 17 00:00:00 2001 From: amatgil Date: Thu, 18 Sep 2025 17:24:53 +0200 Subject: [PATCH 1/8] rand -> rand_xoshiro for better reproducibility across archs --- Cargo.lock | 23 +++++++++++++++++++---- Cargo.toml | 4 ++-- site/Cargo.toml | 2 +- src/algorithm/dyadic/mod.rs | 20 ++++++++++++++++---- src/algorithm/monadic/sort.rs | 3 ++- src/constant.rs | 5 +++-- src/run_prim.rs | 15 ++++++++++----- 7 files changed, 53 insertions(+), 19 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 936032514..eba356f57 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5036,7 +5036,7 @@ checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" dependencies = [ "libc", "rand_chacha", - "rand_core", + "rand_core 0.6.4", ] [[package]] @@ -5046,7 +5046,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" dependencies = [ "ppv-lite86", - "rand_core", + "rand_core 0.6.4", ] [[package]] @@ -5058,6 +5058,21 @@ dependencies = [ "getrandom 0.2.15", ] +[[package]] +name = "rand_core" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38" + +[[package]] +name = "rand_xoshiro" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f703f4665700daf5512dcca5f43afa6af89f09db47fb56be587f80636bda2d41" +dependencies = [ + "rand_core 0.9.3", +] + [[package]] name = "rangemap" version = "1.5.1" @@ -5781,7 +5796,7 @@ dependencies = [ "leptos", "leptos_meta", "leptos_router", - "rand", + "rand_xoshiro", "serde", "serde_json", "uiua", @@ -6541,7 +6556,7 @@ dependencies = [ "paste", "pathdiff", "png", - "rand", + "rand_xoshiro", "rawrrr", "rayon", "regex", diff --git a/Cargo.toml b/Cargo.toml index 7e6548c1d..3a63f5d59 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -33,11 +33,11 @@ js-sys = "0.3.69" leptos = "0.6.11" leptos_meta = {version = "0.6.11", features = ["csr"]} leptos_router = {version = "0.6.11", features = ["csr"]} -rand = {version = "0.8.5", features = ["small_rng"]} serde = {version = "1", features = ["derive"]} serde_json = "1.0.115" unicode-segmentation = "1.10" urlencoding = "2" +rand_xoshiro = "0.7.0" # NOTE: if you change the wasm-bindgen version, remember to change it in nix/site.nix as well wasm-bindgen = "=0.2.93" @@ -103,7 +103,6 @@ open = {version = "5", optional = true} parking_lot = "0.12.1" paste = "1.0.14" pathdiff = "0.2.1" -rand.workspace = true rawrrr = {version = "0.2.1", optional = true} rayon = "1.9.0" regex = "1.10.3" @@ -180,6 +179,7 @@ web-sys = {version = "0.3.60", optional = true} eframe = {version = "0.29.1", optional = true, features = ["persistence"]} native-dialog = {version = "0.7.0", optional = true} rmp-serde = {version = "1.3.0", optional = true} +rand_xoshiro = "0.7.0" [features] apng = ["dep:png"] diff --git a/site/Cargo.toml b/site/Cargo.toml index cb67c4e3e..77b9ab8f4 100644 --- a/site/Cargo.toml +++ b/site/Cargo.toml @@ -20,7 +20,7 @@ js-sys.workspace = true leptos.workspace = true leptos_meta.workspace = true leptos_router.workspace = true -rand.workspace = true +rand_xoshiro.workspace = true serde.workspace = true serde_json.workspace = true urlencoding.workspace = true diff --git a/src/algorithm/dyadic/mod.rs b/src/algorithm/dyadic/mod.rs index a93783b00..5eaf47380 100644 --- a/src/algorithm/dyadic/mod.rs +++ b/src/algorithm/dyadic/mod.rs @@ -15,7 +15,11 @@ use std::{ use bytemuck::allocation::cast_vec; use ecow::{eco_vec, EcoVec}; -use rand::prelude::*; +use rand_xoshiro::{ + rand_core::{RngCore, SeedableRng}, + Xoshiro256Plus, +}; + #[cfg(not(target_arch = "wasm32"))] use rayon::prelude::*; use smallvec::SmallVec; @@ -2110,7 +2114,7 @@ impl Value { let mut hasher = DefaultHasher::new(); seed.hash(&mut hasher); let seed = hasher.finish(); - let mut rng = SmallRng::seed_from_u64(seed); + let mut rng = Xoshiro256Plus::seed_from_u64(seed); const SHAPE_REQ: &str = "Shape must be an array of natural \ numbers with at most rank 2"; @@ -2120,7 +2124,7 @@ impl Value { let elem_count = validate_size::(shape.iter().copied(), env)?; let mut data = eco_vec![0.0; elem_count]; for x in data.make_mut() { - *x = rng.gen(); + *x = f64::from_bits(rng.next_u64()); } Ok(Array::new(shape, data)) }; @@ -2166,7 +2170,15 @@ impl Value { 0 => Err(env.error("Cannot pick random row of an empty array").fill()), 1 => Ok(self.row(0)), len => { - let i = RNG.with_borrow_mut(|rng| rng.gen_range(0..len)); + let i = RNG.with_borrow_mut(|rng| { + let upper = len.next_power_of_two(); + loop { + let r = rng.next_u64() as usize; + if r % upper < len { + break len; + } + } + }); Ok(self.row(i)) } } diff --git a/src/algorithm/monadic/sort.rs b/src/algorithm/monadic/sort.rs index 22e93b540..607089e9a 100644 --- a/src/algorithm/monadic/sort.rs +++ b/src/algorithm/monadic/sort.rs @@ -1,7 +1,8 @@ use std::{cmp::Ordering, ptr}; use ecow::EcoVec; -use rand::Rng; +use rand_xoshiro::rand_core::SeedableRng; +use rand_xoshiro::Xoshiro256Plus; use rayon::prelude::*; use crate::{algorithm::ArrayCmpSlice, random_with, val_as_arr, Array, ArrayValue, Value}; diff --git a/src/constant.rs b/src/constant.rs index 68adfdb60..35afbd855 100644 --- a/src/constant.rs +++ b/src/constant.rs @@ -5,7 +5,8 @@ use std::{ }; use ecow::EcoVec; -use rand::prelude::*; +use rand_xoshiro::rand_core::SeedableRng; +use rand_xoshiro::Xoshiro256Plus; use crate::{ parse_doc_line_fragments, Array, Boxed, PrimDocFragment, SysBackend, Value, WILDCARD_NAN, @@ -463,7 +464,7 @@ fn music_constant(backend: &dyn SysBackend) -> Value { hat_mask.push((hat_bits & 1) as f64); hat_bits >>= 1; } - let mut rng = SmallRng::seed_from_u64(0); + let mut rng = Xoshiro256Plus::seed_from_u64(0); let sr = backend.audio_sample_rate(); (0..(BEAT * 2.0 * 16.0 * sr as f64) as usize) .map(|s| { diff --git a/src/run_prim.rs b/src/run_prim.rs index 402493c16..98d89ddfa 100644 --- a/src/run_prim.rs +++ b/src/run_prim.rs @@ -14,13 +14,18 @@ use std::{ collections::HashMap, f64::consts::{PI, TAU}, iter::repeat_n, + mem::transmute, sync::{ atomic::{self, AtomicUsize}, OnceLock, }, + time::{SystemTime, UNIX_EPOCH}, }; -use rand::prelude::*; +use rand_xoshiro::{ + rand_core::{RngCore, SeedableRng}, + Xoshiro256Plus, +}; use crate::{ algorithm::{self, ga::GaOp, loops, reduce, table, zip, *}, @@ -1769,22 +1774,22 @@ fn undo_regex(env: &mut Uiua) -> UiuaResult { } thread_local! { - pub(crate) static RNG: RefCell = RefCell::new(SmallRng::from_entropy()); + pub(crate) static RNG: RefCell = RefCell::new(Xoshiro256Plus::from_seed(transmute(SystemTime::now().saturating_duration_since(UNIX_EPOCH).as_micros()))); } /// Generate a random number, equivalent to [`Primitive::Rand`] pub fn random() -> f64 { - random_with(|rng| rng.gen()) + random_with(|rng| f64::from_bits(rng.next_u64())) } /// Access the interpreter's random number generator for the thread -pub fn random_with(f: impl FnOnce(&mut SmallRng) -> T) -> T { +pub fn random_with(f: impl FnOnce(&mut Xoshiro256Plus) -> T) -> T { RNG.with(|rng| f(&mut rng.borrow_mut())) } /// Seed the random number generator pub fn seed_random(seed: u64) { - random_with(|rng| *rng = SmallRng::seed_from_u64(seed)); + random_with(|rng| *rng = Xoshiro256Plus::seed_from_u64(seed)); } fn stack_n(env: &mut Uiua, n: usize, inverse: bool) -> UiuaResult { From cc389187f2ca50cd757080007f12dd1ca41abe98 Mon Sep 17 00:00:00 2001 From: amatgil Date: Thu, 18 Sep 2025 17:34:34 +0200 Subject: [PATCH 2/8] Remember to return the generated value, not the upper bound :P --- src/algorithm/dyadic/mod.rs | 6 +++--- src/algorithm/monadic/sort.rs | 10 +++++++++- src/run_prim.rs | 3 ++- 3 files changed, 14 insertions(+), 5 deletions(-) diff --git a/src/algorithm/dyadic/mod.rs b/src/algorithm/dyadic/mod.rs index 5eaf47380..df98ecb3e 100644 --- a/src/algorithm/dyadic/mod.rs +++ b/src/algorithm/dyadic/mod.rs @@ -2173,9 +2173,9 @@ impl Value { let i = RNG.with_borrow_mut(|rng| { let upper = len.next_power_of_two(); loop { - let r = rng.next_u64() as usize; - if r % upper < len { - break len; + let r = rng.next_u64() as usize % upper; + if r < len { + break r; } } }); diff --git a/src/algorithm/monadic/sort.rs b/src/algorithm/monadic/sort.rs index 607089e9a..356257fa6 100644 --- a/src/algorithm/monadic/sort.rs +++ b/src/algorithm/monadic/sort.rs @@ -233,7 +233,15 @@ impl Array { let row_len = self.row_len(); let slice = self.data.as_mut_slice(); for i in (1..row_count).rev() { - let j = rng.gen_range(0..=i); + let j = { + let upper = i.next_power_of_two(); + loop { + let r = rng.next_u64() as usize % upper; + if r <= i { + break r; + } + } + }; if i == j { continue; } diff --git a/src/run_prim.rs b/src/run_prim.rs index 98d89ddfa..716f93f00 100644 --- a/src/run_prim.rs +++ b/src/run_prim.rs @@ -1774,7 +1774,8 @@ fn undo_regex(env: &mut Uiua) -> UiuaResult { } thread_local! { - pub(crate) static RNG: RefCell = RefCell::new(Xoshiro256Plus::from_seed(transmute(SystemTime::now().saturating_duration_since(UNIX_EPOCH).as_micros()))); + pub(crate) static RNG: RefCell = RefCell::new(Xoshiro256Plus::seed_from_u64( + SystemTime::now().duration_since(UNIX_EPOCH).map(|t|t.as_micros()).unwrap_or(42) as u64)); } /// Generate a random number, equivalent to [`Primitive::Rand`] From 64e56a5f82cd8a36e800b6d04a8ef27ca2c952f7 Mon Sep 17 00:00:00 2001 From: amatgil Date: Thu, 18 Sep 2025 21:23:25 +0200 Subject: [PATCH 3/8] Implement gen_range(-1..=1), seems to be more uniform than the previous way --- src/algorithm/monadic/sort.rs | 7 +++++-- src/constant.rs | 12 ++++++++---- 2 files changed, 13 insertions(+), 6 deletions(-) diff --git a/src/algorithm/monadic/sort.rs b/src/algorithm/monadic/sort.rs index 356257fa6..ad2b32117 100644 --- a/src/algorithm/monadic/sort.rs +++ b/src/algorithm/monadic/sort.rs @@ -1,8 +1,10 @@ use std::{cmp::Ordering, ptr}; use ecow::EcoVec; -use rand_xoshiro::rand_core::SeedableRng; -use rand_xoshiro::Xoshiro256Plus; +use rand_xoshiro::{ + rand_core::{RngCore, SeedableRng}, + Xoshiro256Plus, +}; use rayon::prelude::*; use crate::{algorithm::ArrayCmpSlice, random_with, val_as_arr, Array, ArrayValue, Value}; @@ -242,6 +244,7 @@ impl Array { } } }; + if i == j { continue; } diff --git a/src/constant.rs b/src/constant.rs index 35afbd855..f7a839b2f 100644 --- a/src/constant.rs +++ b/src/constant.rs @@ -5,8 +5,10 @@ use std::{ }; use ecow::EcoVec; -use rand_xoshiro::rand_core::SeedableRng; -use rand_xoshiro::Xoshiro256Plus; +use rand_xoshiro::{ + rand_core::{RngCore, SeedableRng}, + Xoshiro256Plus, +}; use crate::{ parse_doc_line_fragments, Array, Boxed, PrimDocFragment, SysBackend, Value, WILDCARD_NAN, @@ -465,6 +467,8 @@ fn music_constant(backend: &dyn SysBackend) -> Value { hat_bits >>= 1; } let mut rng = Xoshiro256Plus::seed_from_u64(0); + let mut rand_wrench = + || 2.0 * f64::from_bits(rng.next_u64() >> 12 | 0x3FF0_0000_0000_0000) - 3.0; // gen_range(-1..=1) let sr = backend.audio_sample_rate(); (0..(BEAT * 2.0 * 16.0 * sr as f64) as usize) .map(|s| { @@ -478,11 +482,11 @@ fn music_constant(backend: &dyn SysBackend) -> Value { let h = if (h * secs % 1.0) < 0.5 { 1.0 } else { -1.0 } / 3.0; // Square wave let kick = ((secs % BEAT).powf(0.4) * 40.0 * TAU).sin(); let hat = 0.3 - * rng.gen_range(-1.0..=1.0) + * rand_wrench() * hat_mask[(4.0 * beat) as usize % 32] * (0.0..=0.1).contains(&(secs % (BEAT / 4.0) / (BEAT / 4.0))) as u8 as f64; let snare = 0.5 - * rng.gen_range(-1.0..=1.0) + * rand_wrench() * ((0.5..=0.6).contains(&(secs % (2.0 * BEAT) / (2.0 * BEAT))) as u8 as f64); 0.5 * (m + h + kick + hat + snare) From d230b6cafe4e10a93b2f6eb3ff8dc37627c9d64f Mon Sep 17 00:00:00 2001 From: amatgil Date: Thu, 18 Sep 2025 22:07:40 +0200 Subject: [PATCH 4/8] Make sure the random numbers are in the correct range --- src/algorithm/dyadic/mod.rs | 2 +- src/algorithm/monadic/sort.rs | 5 +---- src/run_prim.rs | 3 +-- 3 files changed, 3 insertions(+), 7 deletions(-) diff --git a/src/algorithm/dyadic/mod.rs b/src/algorithm/dyadic/mod.rs index df98ecb3e..ab8ae9bad 100644 --- a/src/algorithm/dyadic/mod.rs +++ b/src/algorithm/dyadic/mod.rs @@ -2124,7 +2124,7 @@ impl Value { let elem_count = validate_size::(shape.iter().copied(), env)?; let mut data = eco_vec![0.0; elem_count]; for x in data.make_mut() { - *x = f64::from_bits(rng.next_u64()); + *x = f64::from_bits(rng.next_u64() >> 12 | 0x3FF0_0000_0000_0000) - 1.0; } Ok(Array::new(shape, data)) }; diff --git a/src/algorithm/monadic/sort.rs b/src/algorithm/monadic/sort.rs index ad2b32117..c3b49f8f2 100644 --- a/src/algorithm/monadic/sort.rs +++ b/src/algorithm/monadic/sort.rs @@ -1,10 +1,7 @@ use std::{cmp::Ordering, ptr}; use ecow::EcoVec; -use rand_xoshiro::{ - rand_core::{RngCore, SeedableRng}, - Xoshiro256Plus, -}; +use rand_xoshiro::rand_core::RngCore; use rayon::prelude::*; use crate::{algorithm::ArrayCmpSlice, random_with, val_as_arr, Array, ArrayValue, Value}; diff --git a/src/run_prim.rs b/src/run_prim.rs index 716f93f00..944462422 100644 --- a/src/run_prim.rs +++ b/src/run_prim.rs @@ -14,7 +14,6 @@ use std::{ collections::HashMap, f64::consts::{PI, TAU}, iter::repeat_n, - mem::transmute, sync::{ atomic::{self, AtomicUsize}, OnceLock, @@ -1780,7 +1779,7 @@ thread_local! { /// Generate a random number, equivalent to [`Primitive::Rand`] pub fn random() -> f64 { - random_with(|rng| f64::from_bits(rng.next_u64())) + random_with(|rng| f64::from_bits(rng.next_u64() >> 12 | 0x3FF0_0000_0000_0000) - 1.0) } /// Access the interpreter's random number generator for the thread From 37ca97d8ca3fb26d5ea7f1543dd209d50516aca8 Mon Sep 17 00:00:00 2001 From: amatgil Date: Thu, 18 Sep 2025 22:07:59 +0200 Subject: [PATCH 5/8] Make site's `shuffle` call functional --- site/src/main.rs | 23 ++++++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) diff --git a/site/src/main.rs b/site/src/main.rs index 7d9c7b4e8..71aeb9763 100644 --- a/site/src/main.rs +++ b/site/src/main.rs @@ -18,7 +18,10 @@ use js_sys::Date; use leptos::*; use leptos_meta::*; use leptos_router::*; -use rand::prelude::*; +use rand_xoshiro::{ + rand_core::{RngCore, SeedableRng}, + Xoshiro256Plus, +}; use uiua::{now, ConstantDef, Primitive, SysOp}; use uiua_editor::{ binding_name_class, lang, @@ -253,9 +256,23 @@ pub fn MainPage() -> impl IntoView { let indices = if visits < 4 { vec![0, rich_prims.len() - 3, rich_prims.len() - 1] } else { - let mut rng = SmallRng::seed_from_u64(visits as u64); let mut indices: Vec = (0..rich_prims.len()).collect(); - indices.shuffle(&mut rng); + { + // Shuffle indices + let l = indices.len(); + let upper = l.next_power_of_two(); + let mut rng = Xoshiro256Plus::seed_from_u64(visits as u64); + + for i in 0..indices.len() { + let index = loop { + let r = rng.next_u64() as usize % upper; + if r < l { + break r; + } + }; + indices.swap(i, index); + } + } indices.truncate(3); indices.sort_unstable(); indices From 418a93db31142515cad94f86e62259f1a67fbade Mon Sep 17 00:00:00 2001 From: amatgil Date: Thu, 18 Sep 2025 22:26:16 +0200 Subject: [PATCH 6/8] Seed from time properly --- src/run_prim.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/run_prim.rs b/src/run_prim.rs index 944462422..a3783a274 100644 --- a/src/run_prim.rs +++ b/src/run_prim.rs @@ -1773,8 +1773,7 @@ fn undo_regex(env: &mut Uiua) -> UiuaResult { } thread_local! { - pub(crate) static RNG: RefCell = RefCell::new(Xoshiro256Plus::seed_from_u64( - SystemTime::now().duration_since(UNIX_EPOCH).map(|t|t.as_micros()).unwrap_or(42) as u64)); + pub(crate) static RNG: RefCell = RefCell::new(Xoshiro256Plus::seed_from_u64(f64::to_bits(crate::now()))) } /// Generate a random number, equivalent to [`Primitive::Rand`] From dca18e42c2a303001d1513d0f8da95b2b8132e93 Mon Sep 17 00:00:00 2001 From: amatgil Date: Thu, 18 Sep 2025 22:43:09 +0200 Subject: [PATCH 7/8] Add two simple tests --- tests/monadic.ua | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tests/monadic.ua b/tests/monadic.ua index a14de155b..123660231 100644 --- a/tests/monadic.ua +++ b/tests/monadic.ua @@ -257,3 +257,7 @@ repr[{[[1 2][2 3]][3 4]}{[[1 2][2 3]]□{[[1 2][2 3]]□□[3 4]}}] ⍤⤙≍ ⟜⍜binary∘ ⇡257 ⍤⤙≍ ⟜⍜binary∘ ÷⟜⇡256 ⍤⤙≍ ⟜⍜binary∘ ×π ⇡256 + +# Rand/Gen +/×♭×⊃≥₀≤₁ gen1000_1000 0 +/×♭×⊃≥₀≤₁⍥₁₀₀₀₀⚂ \ No newline at end of file From 703a8249dcce4634b3a54c6db659ac11fea86fbd Mon Sep 17 00:00:00 2001 From: amatgil Date: Thu, 18 Sep 2025 22:53:45 +0200 Subject: [PATCH 8/8] Tweak tests, use ahasher --- Cargo.lock | 1 + Cargo.toml | 1 + src/algorithm/dyadic/mod.rs | 5 +++-- src/run_prim.rs | 1 - tests/monadic.ua | 5 +++-- 5 files changed, 8 insertions(+), 5 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index eba356f57..393e8b927 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -6520,6 +6520,7 @@ dependencies = [ name = "uiua" version = "0.17.0-rc.2" dependencies = [ + "ahash", "arboard", "bitflags 2.9.1", "bytemuck", diff --git a/Cargo.toml b/Cargo.toml index 3a63f5d59..16e4eef1f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -180,6 +180,7 @@ eframe = {version = "0.29.1", optional = true, features = ["persistence"]} native-dialog = {version = "0.7.0", optional = true} rmp-serde = {version = "1.3.0", optional = true} rand_xoshiro = "0.7.0" +ahash = "0.8.12" [features] apng = ["dep:png"] diff --git a/src/algorithm/dyadic/mod.rs b/src/algorithm/dyadic/mod.rs index ab8ae9bad..520c6f585 100644 --- a/src/algorithm/dyadic/mod.rs +++ b/src/algorithm/dyadic/mod.rs @@ -8,11 +8,12 @@ use core::f64; use std::{ borrow::Cow, cmp::Ordering, - hash::{DefaultHasher, Hash, Hasher}, + hash::{Hash, Hasher}, iter::{once, repeat_n}, mem::{replace, swap, take}, }; +use ahash::AHasher; use bytemuck::allocation::cast_vec; use ecow::{eco_vec, EcoVec}; use rand_xoshiro::{ @@ -2111,7 +2112,7 @@ impl Value { } /// Generate randomly seeded arrays pub fn gen(&self, seed: &Self, env: &Uiua) -> UiuaResult { - let mut hasher = DefaultHasher::new(); + let mut hasher = AHasher::default(); seed.hash(&mut hasher); let seed = hasher.finish(); let mut rng = Xoshiro256Plus::seed_from_u64(seed); diff --git a/src/run_prim.rs b/src/run_prim.rs index a3783a274..57409e065 100644 --- a/src/run_prim.rs +++ b/src/run_prim.rs @@ -18,7 +18,6 @@ use std::{ atomic::{self, AtomicUsize}, OnceLock, }, - time::{SystemTime, UNIX_EPOCH}, }; use rand_xoshiro::{ diff --git a/tests/monadic.ua b/tests/monadic.ua index 123660231..69dbbacfd 100644 --- a/tests/monadic.ua +++ b/tests/monadic.ua @@ -259,5 +259,6 @@ repr[{[[1 2][2 3]][3 4]}{[[1 2][2 3]]□{[[1 2][2 3]]□□[3 4]}}] ⍤⤙≍ ⟜⍜binary∘ ×π ⇡256 # Rand/Gen -/×♭×⊃≥₀≤₁ gen1000_1000 0 -/×♭×⊃≥₀≤₁⍥₁₀₀₀₀⚂ \ No newline at end of file +⍤⤙≍1 /×♭×⊃≥₀≤₁ gen1000_1000 0 +⍤⤙≍1 /×♭×⊃≥₀≤₁⍥₁₀₀₀₀⚂ +⍤⤙≍ 1000_1000 △gen1000_1000 0 \ No newline at end of file