Function wrap [src]
Odd sawtooth function
|
/ | / /
/ |/ /
--/----/----/--
/ /| /
/ / | /
|
Limit x to the half-open interval [-r, r).
Prototype
pub fn wrap(x: anytype, r: anytype) @TypeOf(x)
Example
test wrap {
// Within range
try testing.expect(wrap(@as(i32, -75), @as(i32, 180)) == -75);
try testing.expect(wrap(@as(i32, -75), @as(i32, -180)) == -75);
// Below
try testing.expect(wrap(@as(i32, -225), @as(i32, 180)) == 135);
try testing.expect(wrap(@as(i32, -225), @as(i32, -180)) == 135);
// Above
try testing.expect(wrap(@as(i32, 361), @as(i32, 180)) == 1);
try testing.expect(wrap(@as(i32, 361), @as(i32, -180)) == 1);
// One period, right limit, positive r
try testing.expect(wrap(@as(i32, 180), @as(i32, 180)) == -180);
// One period, left limit, positive r
try testing.expect(wrap(@as(i32, -180), @as(i32, 180)) == -180);
// One period, right limit, negative r
try testing.expect(wrap(@as(i32, 180), @as(i32, -180)) == 180);
// One period, left limit, negative r
try testing.expect(wrap(@as(i32, -180), @as(i32, -180)) == 180);
// Two periods, right limit, positive r
try testing.expect(wrap(@as(i32, 540), @as(i32, 180)) == -180);
// Two periods, left limit, positive r
try testing.expect(wrap(@as(i32, -540), @as(i32, 180)) == -180);
// Two periods, right limit, negative r
try testing.expect(wrap(@as(i32, 540), @as(i32, -180)) == 180);
// Two periods, left limit, negative r
try testing.expect(wrap(@as(i32, -540), @as(i32, -180)) == 180);
// Floating point
try testing.expect(wrap(@as(f32, 1.125), @as(f32, 1.0)) == -0.875);
try testing.expect(wrap(@as(f32, -127.5), @as(f32, 180)) == -127.5);
// Mix of comptime and non-comptime
var i: i32 = 1;
_ = &i;
try testing.expect(wrap(i, 10) == 1);
const limit: i32 = 180;
// Within range
try testing.expect(wrap(@as(i32, -75), limit) == -75);
// Below
try testing.expect(wrap(@as(i32, -225), limit) == 135);
// Above
try testing.expect(wrap(@as(i32, 361), limit) == 1);
}
Source
pub fn wrap(x: anytype, r: anytype) @TypeOf(x) {
const info_x = @typeInfo(@TypeOf(x));
const info_r = @typeInfo(@TypeOf(r));
if (info_x == .int and info_x.int.signedness != .signed) {
@compileError("x must be floating point, comptime integer, or signed integer.");
}
switch (info_r) {
.int => {
// in the rare usecase of r not being comptime_int or float,
// take the penalty of having an intermediary type conversion,
// otherwise the alternative is to unwind iteratively to avoid overflow
const R = comptime do: {
var info = info_r;
info.int.bits += 1;
info.int.signedness = .signed;
break :do @Type(info);
};
const radius: if (info_r.int.signedness == .signed) @TypeOf(r) else R = r;
return @intCast(@mod(x - radius, 2 * @as(R, r)) - r); // provably impossible to overflow
},
else => {
return @mod(x - r, 2 * r) - r;
},
}
}