refactored and documented random.onyx
authorBrendan Hansen <brendan.f.hansen@gmail.com>
Mon, 13 Feb 2023 18:42:50 +0000 (12:42 -0600)
committerBrendan Hansen <brendan.f.hansen@gmail.com>
Mon, 13 Feb 2023 18:42:50 +0000 (12:42 -0600)
core/random/random.onyx

index 527fb16f616573880816773677256626a24ead74..20c7ccdd7435d7db70fcf3ad74fba14e3c116bdf 100644 (file)
 package core.random
 
-#local seed : i64 = 8675309
+//
+// The state of a random number generator.
+Random :: struct {
+    seed: i64;
+}
 
-#local RANDOM_MULTIPLIER :: 25214903917
-#local RANDOM_INCREMENT  :: cast(i64) 11
-// #local RANDOM_MODULUS    :: 1 << 32
+#inject Random {
+    //
+    // Creates a new random number generator.
+    // An initial seed can be passed in, otherwise the
+    // current UNIX time is used.
+    make :: (seed: i64 = core.os.time()) -> Random {
+        return .{ seed };
+    }
 
-set_seed :: #match {
-    (s: u32) do seed = ~~s; ,
-    (s: u64) do seed =   s; ,
-}
+    //
+    // Sets the seed of the random number generator
+    set_seed :: #match {
+        (self: ^Random, s: u32) { self.seed = ~~s; },
+        (self: ^Random, s: u64) { self.seed =   s; },
+    }
 
-int :: (s := ^seed) -> u32 {
-    *s = *s * RANDOM_MULTIPLIER + RANDOM_INCREMENT;
-    return cast(u32) ((*s >> 16) & ~~0xffffffff);
-}
+    //
+    // Generates a random 32-bit integer.
+    int :: (self: ^Random) -> u32 {
+        s := self.seed * RANDOM_MULTIPLIER + RANDOM_INCREMENT;
+        defer self.seed = s;
+        return cast(u32) ((s >> 16) & ~~0xffffffff);
+    }
 
-between :: (lo: i32, hi: i32) -> i32 do return int () % (hi + 1 - lo) + lo;
+    //
+    // Generates a random 32-bit integer between `lo` and `hi`.
+    between :: (self: ^Random, lo: i32, hi: i32) -> i32 {
+        return self->int() % (hi + 1 - lo) + lo;
+    }
 
-float :: (lo := 0.0f, hi := 1.0f) -> f32 {
-    return (cast(f32) (int() % (1 << 20)) / cast(f32) (1 << 20)) * (hi - lo) + lo;
-}
+    //
+    // Generates a random floating point number between `lo` and `hi`.
+    float :: (self: ^Random, lo := 0.0f, hi := 1.0f) -> f32 {
+        return (cast(f32) (self->int() % (1 << 20)) / cast(f32) (1 << 20)) * (hi - lo) + lo;
+    }
 
-choice :: (a: [] $T) -> T {
-    return a[between(0, a.count - 1)];
-}
+    //
+    // Returns a random element from a slice.
+    choice :: (self: ^Random, a: [] $T) -> T {
+        return a[self->between(0, a.count - 1)];
+    }
 
-string :: (bytes_long: u32, alpha_numeric := false, allocator := context.allocator) -> str {
-    memory :: package core.memory
+    //
+    // Returns a random string of length `bytes_long`. If `alpha_numeric` is
+    // true, then the string will only consist of alpha-numeric characters.
+    string :: (self: ^Random, bytes_long: u32, alpha_numeric := false, allocator := context.allocator) -> str {
+        memory :: package core.memory
 
-    s := memory.make_slice(u8, bytes_long, allocator=allocator);
-    for^ s {
-        if alpha_numeric {
-            #persist alpha_numeral := "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
-            *it = choice(alpha_numeral);
-        } else {
-            *it = ~~between(32, 127);
+        s := memory.make_slice(u8, bytes_long, allocator=allocator);
+        for^ s {
+            if alpha_numeric {
+                #persist alpha_numeral := "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
+                *it = self->choice(alpha_numeral);
+            } else {
+                *it = ~~(self->between(32, 127));
+            }
         }
+        return s;
     }
-    return s;
 }
+
+//
+// Below are procedures that use a global random number
+// generator for quick random number generation.
+//
+
+
+//
+// The global random state.
+global_random := Random.{ 8675309 };
+
+set_seed :: #match {
+    (s: u32) { global_random->set_seed(~~s); },
+    (s: u64) { global_random->set_seed(s); },
+}
+
+int :: () =>
+    global_random->int();
+
+between :: (lo: i32, hi: i32) =>
+    global_random->between(lo, hi);
+
+float :: (lo := 0.0f, hi := 1.0f) =>
+    global_random->float(lo, hi);
+
+choice :: (a: [] $T) =>
+    global_random->choice(a);
+
+string :: (bytes_long: u32, alpha_numeric := false, allocator := context.allocator) =>
+    global_random->string(bytes_long, alpha_numeric, allocator);
+
+
+
+//
+// Internal implementation details
+//
+
+#package {
+    RANDOM_MULTIPLIER :: 25214903917
+    RANDOM_INCREMENT  :: cast(i64) 11
+}
+