authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2017-05-31 18:23:56-04:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2017-05-31 18:23:56-04:00
log1e301b03a9110b3cd8a29530ad9cbc85b403a902
tree1fda7506b50ae3b8638eb28f8ce6515dfd617cb2
parent1ae2002b415614935746946c511e8093a1de2902

change std.rand.Rand.rangeUnsigned to std.rand.Rand.range

and make it support signed integers

3 files changed, 116 insertions(+), 16 deletions(-)

example/guess_number/main.zig+1-1
...@@ -12,7 +12,7 @@ pub fn main() -> %void {...@@ -12,7 +12,7 @@ pub fn main() -> %void {
12 const seed = std.mem.readInt(seed_bytes, usize, true);12 const seed = std.mem.readInt(seed_bytes, usize, true);
13 var rand = Rand.init(seed);13 var rand = Rand.init(seed);
1414
15 const answer = rand.rangeUnsigned(u8, 0, 100) + 1;15 const answer = rand.range(u8, 0, 100) + 1;
1616
17 while (true) {17 while (true) {
18 %%io.stdout.printf("\nGuess a number between 1 and 100: ");18 %%io.stdout.printf("\nGuess a number between 1 and 100: ");
std/math.zig+52
...@@ -41,6 +41,10 @@ pub fn sub(comptime T: type, a: T, b: T) -> %T {...@@ -41,6 +41,10 @@ pub fn sub(comptime T: type, a: T, b: T) -> %T {
41 if (@subWithOverflow(T, a, b, &answer)) error.Overflow else answer41 if (@subWithOverflow(T, a, b, &answer)) error.Overflow else answer
42}42}
4343
44pub fn negate(x: var) -> %@typeOf(x) {
45 return sub(@typeOf(x), 0, x);
46}
47
44error Overflow;48error Overflow;
45pub fn shl(comptime T: type, a: T, b: T) -> %T {49pub fn shl(comptime T: type, a: T, b: T) -> %T {
46 var answer: T = undefined;50 var answer: T = undefined;
...@@ -341,3 +345,51 @@ test "math.floor" {...@@ -341,3 +345,51 @@ test "math.floor" {
341 assert(floor(f64(999.0)) == 999.0);345 assert(floor(f64(999.0)) == 999.0);
342 assert(floor(f64(-999.0)) == -999.0);346 assert(floor(f64(-999.0)) == -999.0);
343}347}
348
349/// Returns the absolute value of the integer parameter.
350/// Result is an unsigned integer.
351pub fn absCast(x: var) -> @IntType(false, @typeOf(x).bit_count) {
352 const uint = @IntType(false, @typeOf(x).bit_count);
353 if (x >= 0)
354 return uint(x);
355
356 return uint(-(x + 1)) + 1;
357}
358
359test "math.absCast" {
360 assert(absCast(i32(-999)) == 999);
361 assert(@typeOf(absCast(i32(-999))) == u32);
362
363 assert(absCast(i32(999)) == 999);
364 assert(@typeOf(absCast(i32(999))) == u32);
365
366 assert(absCast(i32(@minValue(i32))) == -@minValue(i32));
367 assert(@typeOf(absCast(i32(@minValue(i32)))) == u32);
368}
369
370/// Returns the negation of the integer parameter.
371/// Result is a signed integer.
372error Overflow;
373pub fn negateCast(x: var) -> %@IntType(true, @typeOf(x).bit_count) {
374 if (@typeOf(x).is_signed)
375 return negate(x);
376
377 const int = @IntType(true, @typeOf(x).bit_count);
378 if (x > -@minValue(int))
379 return error.Overflow;
380
381 if (x == -@minValue(int))
382 return @minValue(int);
383
384 return -int(x);
385}
386
387test "math.negateCast" {
388 assert(%%negateCast(u32(999)) == -999);
389 assert(@typeOf(%%negateCast(u32(999))) == i32);
390
391 assert(%%negateCast(u32(-@minValue(i32))) == @minValue(i32));
392 assert(@typeOf(%%negateCast(u32(-@minValue(i32)))) == i32);
393
394 if (negateCast(u32(@maxValue(i32) + 10))) |_| unreachable else |err| assert(err == error.Overflow);
395}
std/rand.zig+63-15
...@@ -1,6 +1,7 @@...@@ -1,6 +1,7 @@
1const assert = @import("debug.zig").assert;1const assert = @import("debug.zig").assert;
2const rand_test = @import("rand_test.zig");2const rand_test = @import("rand_test.zig");
3const mem = @import("mem.zig");3const mem = @import("mem.zig");
4const math = @import("math.zig");
45
5pub const MT19937_32 = MersenneTwister(6pub const MT19937_32 = MersenneTwister(
6 u32, 624, 397, 31,7 u32, 624, 397, 31,
...@@ -63,18 +64,43 @@ pub const Rand = struct {...@@ -63,18 +64,43 @@ pub const Rand = struct {
6364
64 /// Get a random unsigned integer with even distribution between `start`65 /// Get a random unsigned integer with even distribution between `start`
65 /// inclusive and `end` exclusive.66 /// inclusive and `end` exclusive.
66 // TODO support signed integers and then rename to "range"67 pub fn range(r: &Rand, comptime T: type, start: T, end: T) -> T {
67 pub fn rangeUnsigned(r: &Rand, comptime T: type, start: T, end: T) -> T {68 assert(start <= end);
68 const range = end - start;69 if (T.is_signed) {
69 const leftover = @maxValue(T) % range;70 const uint = @IntType(false, T.bit_count);
70 const upper_bound = @maxValue(T) - leftover;71 if (start >= 0 and end >= 0) {
71 var rand_val_array: [@sizeOf(T)]u8 = undefined;72 return T(r.range(uint, uint(start), uint(end)));
7273 } else if (start < 0 and end < 0) {
73 while (true) {74 // Can't overflow because the range is over signed ints
74 r.fillBytes(rand_val_array[0..]);75 return %%math.negateCast(r.range(uint, math.absCast(end), math.absCast(start)) + 1);
75 const rand_val = mem.readInt(rand_val_array, T, false);76 } else if (start < 0 and end >= 0) {
76 if (rand_val < upper_bound) {77 const end_uint = uint(end);
77 return start + (rand_val % range);78 const total_range = math.absCast(start) + end_uint;
79 const value = r.range(uint, 0, total_range);
80 const result = if (value < end_uint) {
81 T(value)
82 } else if (value == end_uint) {
83 start
84 } else {
85 // Can't overflow because the range is over signed ints
86 %%math.negateCast(value - end_uint)
87 };
88 return result;
89 } else {
90 unreachable;
91 }
92 } else {
93 const total_range = end - start;
94 const leftover = @maxValue(T) % total_range;
95 const upper_bound = @maxValue(T) - leftover;
96 var rand_val_array: [@sizeOf(T)]u8 = undefined;
97
98 while (true) {
99 r.fillBytes(rand_val_array[0..]);
100 const rand_val = mem.readInt(rand_val_array, T, false);
101 if (rand_val < upper_bound) {
102 return start + (rand_val % total_range);
103 }
78 }104 }
79 }105 }
80 }106 }
...@@ -94,7 +120,7 @@ pub const Rand = struct {...@@ -94,7 +120,7 @@ pub const Rand = struct {
94 } else {120 } else {
95 @compileError("unknown floating point type")121 @compileError("unknown floating point type")
96 };122 };
97 return T(r.rangeUnsigned(int_type, 0, precision)) / T(precision);123 return T(r.range(int_type, 0, precision)) / T(precision);
98 }124 }
99};125};
100126
...@@ -175,16 +201,38 @@ test "rand float 32" {...@@ -175,16 +201,38 @@ test "rand float 32" {
175 }201 }
176}202}
177203
178test "testMT19937_64" {204test "rand.MT19937_64" {
179 var rng = MT19937_64.init(rand_test.mt64_seed);205 var rng = MT19937_64.init(rand_test.mt64_seed);
180 for (rand_test.mt64_data) |value| {206 for (rand_test.mt64_data) |value| {
181 assert(value == rng.get());207 assert(value == rng.get());
182 }208 }
183}209}
184210
185test "testMT19937_32" {211test "rand.MT19937_32" {
186 var rng = MT19937_32.init(rand_test.mt32_seed);212 var rng = MT19937_32.init(rand_test.mt32_seed);
187 for (rand_test.mt32_data) |value| {213 for (rand_test.mt32_data) |value| {
188 assert(value == rng.get());214 assert(value == rng.get());
189 }215 }
190}216}
217
218test "rand.Rand.range" {
219 var r = Rand.init(42);
220 testRange(&r, -4, 3);
221 testRange(&r, -4, -1);
222 testRange(&r, 10, 14);
223}
224
225fn testRange(r: &Rand, start: i32, end: i32) {
226 const count = usize(end - start);
227 var values_buffer = []bool{false} ** 20;
228 const values = values_buffer[0..count];
229 var i: usize = 0;
230 while (i < count) {
231 const value = r.range(i32, start, end);
232 const index = usize(value - start);
233 if (!values[index]) {
234 i += 1;
235 values[index] = true;
236 }
237 }
238}