lib/stabilizer/src/properties/intrinsics.zig
daab053ee43316e1809a84551d573ddd1e5bf3d2
1 const std = @import("std");
2 const hypothesis = @import("hypothesis");
3 const stabilizer = @import("stabilizer");
4
5 const Allocator = std.mem.Allocator;
6
7 const powif = stabilizer.powif;
8 const memset_i8 = stabilizer.memset_i8;
9 const memset_i16 = stabilizer.memset_i16;
10 const memset_i32 = stabilizer.memset_i32;
11 const memset_i64 = stabilizer.memset_i64;
12
13 fn settings() hypothesis.Settings {
14 return hypothesis.Settings.quick()
15 .withSeed(0x57ab_11e5)
16 .withDatabase("zig-out/hypothesis-failures/stabilizer");
17 }
18
19 fn drawUsize(data: *hypothesis.ConjectureData, min: usize, max: usize, shrink_towards: usize) !usize {
20 return @intCast(try data.drawInteger(
21 @intCast(min),
22 @intCast(max),
23 @intCast(shrink_towards),
24 ));
25 }
26
27 fn drawI32(data: *hypothesis.ConjectureData, min: i32, max: i32, shrink_towards: i32) !i32 {
28 return hypothesis.strategies.u64ToInt(i32, try data.drawInteger(
29 hypothesis.strategies.intToU64(i32, min),
30 hypothesis.strategies.intToU64(i32, max),
31 hypothesis.strategies.intToU64(i32, shrink_towards),
32 ));
33 }
34
35 const IntrinsicPowProperty = struct {
36 pub fn property(data: *hypothesis.ConjectureData, allocator: Allocator) !void {
37 _ = allocator;
38 const base_i = try drawI32(data, -32, 32, 2);
39 const exponent = try drawI32(data, -8, 8, 0);
40 const base: f32 = @floatFromInt(base_i);
41 const expected = std.math.pow(f32, base, @floatFromInt(exponent));
42 const actual = powif(base, @intCast(exponent));
43 if (std.math.isNan(expected)) {
44 try std.testing.expect(std.math.isNan(actual));
45 } else {
46 try std.testing.expectEqual(expected, actual);
47 }
48 }
49 };
50
51 test "pbt: powif matches upstream float exponent cast" {
52 try hypothesis.checkNamed(IntrinsicPowProperty, "stabilizer-powif", settings());
53 }
54
55 const IntrinsicMemsetProperty = struct {
56 pub fn property(data: *hypothesis.ConjectureData, allocator: Allocator) !void {
57 const variant = try drawUsize(data, 0, 3, 0);
58 const len = try drawUsize(data, 0, if (variant == 0) 255 else 256, 0);
59 const prefix = try drawUsize(data, 0, 16, 0);
60 const suffix = try drawUsize(data, 0, 16, 0);
61 const value: u8 = @truncate(try drawUsize(data, 0, 255, 0));
62 const total = prefix + len + suffix;
63 const bytes = try allocator.alloc(u8, total);
64 defer allocator.free(bytes);
65 @memset(bytes, 0xa5);
66
67 switch (variant) {
68 0 => memset_i8(bytes[prefix..].ptr, value, @intCast(len), 1, false),
69 1 => memset_i16(bytes[prefix..].ptr, value, @intCast(len), 1, false),
70 2 => memset_i32(bytes[prefix..].ptr, value, @intCast(len), 1, false),
71 else => memset_i64(bytes[prefix..].ptr, value, @intCast(len), 1, true),
72 }
73
74 for (bytes[0..prefix]) |byte| try std.testing.expectEqual(@as(u8, 0xa5), byte);
75 for (bytes[prefix..][0..len]) |byte| try std.testing.expectEqual(value, byte);
76 for (bytes[prefix + len ..]) |byte| try std.testing.expectEqual(@as(u8, 0xa5), byte);
77 }
78 };
79
80 test "pbt: memset intrinsics preserve exact byte ranges" {
81 try hypothesis.checkNamed(IntrinsicMemsetProperty, "stabilizer-memset-intrinsics", settings());
82 }