| ... | @@ -57,6 +57,23 @@ pub const Random = struct { | ... | @@ -57,6 +57,23 @@ pub const Random = struct { |
| 57 | return @bitCast(T, unsigned_result); | 57 | return @bitCast(T, unsigned_result); |
| 58 | } | 58 | } |
| 59 | | 59 | |
| | 60 | /// Constant-time implementation off ::uintLessThan. |
| | 61 | /// The results of this function may be biased. |
| | 62 | pub fn uintLessThanBiased(r: *Random, comptime T: type, less_than: T) T { |
| | 63 | assert(T.is_signed == false); |
| | 64 | assert(0 < less_than); |
| | 65 | // Small is typically u32 |
| | 66 | const Small = @IntType(false, @divTrunc(T.bit_count + 31, 32) * 32); |
| | 67 | // Large is typically u64 |
| | 68 | const Large = @IntType(false, Small.bit_count * 2); |
| | 69 | |
| | 70 | // adapted from: |
| | 71 | // http://www.pcg-random.org/posts/bounded-rands.html |
| | 72 | // "Integer Multiplication (Biased)" |
| | 73 | var x: Small = r.int(Small); |
| | 74 | var m: Large = Large(x) * Large(less_than); |
| | 75 | return @intCast(T, m >> Small.bit_count); |
| | 76 | } |
| 60 | /// Returns an evenly distributed random unsigned integer `0 <= i < less_than`. | 77 | /// Returns an evenly distributed random unsigned integer `0 <= i < less_than`. |
| 61 | /// This function assumes that the underlying ::fillFn produces evenly distributed values. | 78 | /// This function assumes that the underlying ::fillFn produces evenly distributed values. |
| 62 | /// Within this assumption, the runtime of this function is exponentially distributed. | 79 | /// Within this assumption, the runtime of this function is exponentially distributed. |
| ... | @@ -64,8 +81,7 @@ pub const Random = struct { | ... | @@ -64,8 +81,7 @@ pub const Random = struct { |
| 64 | /// the runtime of this function would technically be unbounded. | 81 | /// the runtime of this function would technically be unbounded. |
| 65 | /// However, if ::fillFn is backed by any evenly distributed pseudo random number generator, | 82 | /// However, if ::fillFn is backed by any evenly distributed pseudo random number generator, |
| 66 | /// this function is guaranteed to return. | 83 | /// this function is guaranteed to return. |
| 67 | /// If you need deterministic runtime bounds, consider instead using `r.int(T) % less_than`, | 84 | /// If you need deterministic runtime bounds, use `::uintLessThanBiased`. |
| 68 | /// which will usually be biased toward smaller values. | | |
| 69 | pub fn uintLessThan(r: *Random, comptime T: type, less_than: T) T { | 85 | pub fn uintLessThan(r: *Random, comptime T: type, less_than: T) T { |
| 70 | assert(T.is_signed == false); | 86 | assert(T.is_signed == false); |
| 71 | assert(0 < less_than); | 87 | assert(0 < less_than); |
| ... | @@ -101,6 +117,16 @@ pub const Random = struct { | ... | @@ -101,6 +117,16 @@ pub const Random = struct { |
| 101 | return @intCast(T, m >> Small.bit_count); | 117 | return @intCast(T, m >> Small.bit_count); |
| 102 | } | 118 | } |
| 103 | | 119 | |
| | 120 | /// Constant-time implementation off ::uintAtMost. |
| | 121 | /// The results of this function may be biased. |
| | 122 | pub fn uintAtMostBiased(r: *Random, comptime T: type, at_most: T) T { |
| | 123 | assert(T.is_signed == false); |
| | 124 | if (at_most == maxInt(T)) { |
| | 125 | // have the full range |
| | 126 | return r.int(T); |
| | 127 | } |
| | 128 | return r.uintLessThanBiased(T, at_most + 1); |
| | 129 | } |
| 104 | /// Returns an evenly distributed random unsigned integer `0 <= i <= at_most`. | 130 | /// Returns an evenly distributed random unsigned integer `0 <= i <= at_most`. |
| 105 | /// See ::uintLessThan, which this function uses in most cases, | 131 | /// See ::uintLessThan, which this function uses in most cases, |
| 106 | /// for commentary on the runtime of this function. | 132 | /// for commentary on the runtime of this function. |
| ... | @@ -113,6 +139,22 @@ pub const Random = struct { | ... | @@ -113,6 +139,22 @@ pub const Random = struct { |
| 113 | return r.uintLessThan(T, at_most + 1); | 139 | return r.uintLessThan(T, at_most + 1); |
| 114 | } | 140 | } |
| 115 | | 141 | |
| | 142 | /// Constant-time implementation off ::intRangeLessThan. |
| | 143 | /// The results of this function may be biased. |
| | 144 | pub fn intRangeLessThanBiased(r: *Random, comptime T: type, at_least: T, less_than: T) T { |
| | 145 | assert(at_least < less_than); |
| | 146 | if (T.is_signed) { |
| | 147 | // Two's complement makes this math pretty easy. |
| | 148 | const UnsignedT = @IntType(false, T.bit_count); |
| | 149 | const lo = @bitCast(UnsignedT, at_least); |
| | 150 | const hi = @bitCast(UnsignedT, less_than); |
| | 151 | const result = lo +% r.uintLessThanBiased(UnsignedT, hi -% lo); |
| | 152 | return @bitCast(T, result); |
| | 153 | } else { |
| | 154 | // The signed implementation would work fine, but we can use stricter arithmetic operators here. |
| | 155 | return at_least + r.uintLessThanBiased(T, less_than - at_least); |
| | 156 | } |
| | 157 | } |
| 116 | /// Returns an evenly distributed random integer `at_least <= i < less_than`. | 158 | /// Returns an evenly distributed random integer `at_least <= i < less_than`. |
| 117 | /// See ::uintLessThan, which this function uses in most cases, | 159 | /// See ::uintLessThan, which this function uses in most cases, |
| 118 | /// for commentary on the runtime of this function. | 160 | /// for commentary on the runtime of this function. |
| ... | @@ -131,6 +173,22 @@ pub const Random = struct { | ... | @@ -131,6 +173,22 @@ pub const Random = struct { |
| 131 | } | 173 | } |
| 132 | } | 174 | } |
| 133 | | 175 | |
| | 176 | /// Constant-time implementation off ::intRangeAtMostBiased. |
| | 177 | /// The results of this function may be biased. |
| | 178 | pub fn intRangeAtMostBiased(r: *Random, comptime T: type, at_least: T, at_most: T) T { |
| | 179 | assert(at_least <= at_most); |
| | 180 | if (T.is_signed) { |
| | 181 | // Two's complement makes this math pretty easy. |
| | 182 | const UnsignedT = @IntType(false, T.bit_count); |
| | 183 | const lo = @bitCast(UnsignedT, at_least); |
| | 184 | const hi = @bitCast(UnsignedT, at_most); |
| | 185 | const result = lo +% r.uintAtMostBiased(UnsignedT, hi -% lo); |
| | 186 | return @bitCast(T, result); |
| | 187 | } else { |
| | 188 | // The signed implementation would work fine, but we can use stricter arithmetic operators here. |
| | 189 | return at_least + r.uintAtMostBiased(T, at_most - at_least); |
| | 190 | } |
| | 191 | } |
| 134 | /// Returns an evenly distributed random integer `at_least <= i <= at_most`. | 192 | /// Returns an evenly distributed random integer `at_least <= i <= at_most`. |
| 135 | /// See ::uintLessThan, which this function uses in most cases, | 193 | /// See ::uintLessThan, which this function uses in most cases, |
| 136 | /// for commentary on the runtime of this function. | 194 | /// for commentary on the runtime of this function. |
| ... | @@ -149,15 +207,11 @@ pub const Random = struct { | ... | @@ -149,15 +207,11 @@ pub const Random = struct { |
| 149 | } | 207 | } |
| 150 | } | 208 | } |
| 151 | | 209 | |
| 152 | /// Return a random integer/boolean type. | | |
| 153 | /// TODO: deprecated. use ::boolean or ::int instead. | 210 | /// TODO: deprecated. use ::boolean or ::int instead. |
| 154 | pub fn scalar(r: *Random, comptime T: type) T { | 211 | pub fn scalar(r: *Random, comptime T: type) T { |
| 155 | if (T == bool) return r.boolean(); | 212 | return if (T == bool) r.boolean() else r.int(T); |
| 156 | return r.int(T); | | |
| 157 | } | 213 | } |
| 158 | | 214 | |
| 159 | /// Return a random integer with even distribution between `start` | | |
| 160 | /// inclusive and `end` exclusive. `start` must be less than `end`. | | |
| 161 | /// TODO: deprecated. renamed to ::intRangeLessThan | 215 | /// TODO: deprecated. renamed to ::intRangeLessThan |
| 162 | pub fn range(r: *Random, comptime T: type, start: T, end: T) T { | 216 | pub fn range(r: *Random, comptime T: type, start: T, end: T) T { |
| 163 | return r.intRangeLessThan(T, start, end); | 217 | return r.intRangeLessThan(T, start, end); |
| ... | @@ -373,6 +427,8 @@ fn testRandomIntAtMost() void { | ... | @@ -373,6 +427,8 @@ fn testRandomIntAtMost() void { |
| 373 | assert(r.random.intRangeAtMost(i3, -4, -1) == -1); | 427 | assert(r.random.intRangeAtMost(i3, -4, -1) == -1); |
| 374 | r.next_value = 0xff; | 428 | r.next_value = 0xff; |
| 375 | assert(r.random.intRangeAtMost(i3, -2, 1) == 1); | 429 | assert(r.random.intRangeAtMost(i3, -2, 1) == 1); |
| | 430 | |
| | 431 | assert(r.random.uintAtMost(u0, 0) == 0); |
| 376 | } | 432 | } |
| 377 | | 433 | |
| 378 | // Generator to extend 64-bit seed values into longer sequences. | 434 | // Generator to extend 64-bit seed values into longer sequences. |
| ... | @@ -884,12 +940,16 @@ test "Random range" { | ... | @@ -884,12 +940,16 @@ test "Random range" { |
| 884 | } | 940 | } |
| 885 | | 941 | |
| 886 | fn testRange(r: *Random, start: i8, end: i8) void { | 942 | fn testRange(r: *Random, start: i8, end: i8) void { |
| | 943 | testRangeBias(r, start, end, true); |
| | 944 | testRangeBias(r, start, end, false); |
| | 945 | } |
| | 946 | fn testRangeBias(r: *Random, start: i8, end: i8, biased: bool) void { |
| 887 | const count = @intCast(usize, i32(end) - i32(start)); | 947 | const count = @intCast(usize, i32(end) - i32(start)); |
| 888 | var values_buffer = []bool{false} ** 0x100; | 948 | var values_buffer = []bool{false} ** 0x100; |
| 889 | const values = values_buffer[0..count]; | 949 | const values = values_buffer[0..count]; |
| 890 | var i: usize = 0; | 950 | var i: usize = 0; |
| 891 | while (i < count) { | 951 | while (i < count) { |
| 892 | const value: i32 = r.intRangeLessThan(i8, start, end); | 952 | const value: i32 = if (biased) r.intRangeLessThanBiased(i8, start, end) else r.intRangeLessThan(i8, start, end); |
| 893 | const index = @intCast(usize, value - start); | 953 | const index = @intCast(usize, value - start); |
| 894 | if (!values[index]) { | 954 | if (!values[index]) { |
| 895 | i += 1; | 955 | i += 1; |