const std = @import("std");
const builtin = @import("builtin");
const expect = std.testing.expect;
const math = std.math;

const epsilon_16 = 0.002;
const epsilon = 0.000001;

fn epsForType(comptime T: type) T {
    return switch (T) {
        f16 => @as(f16, epsilon_16),
        else => @as(T, epsilon),
    };
}

test "add f16" {
    if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;

    try testAdd(f16);
    try comptime testAdd(f16);
}

test "add f32/f64" {
    try testAdd(f32);
    try comptime testAdd(f32);
    try testAdd(f64);
    try comptime testAdd(f64);
}

test "add f80/f128/c_longdouble" {
    if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
    if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;

    try testAdd(f80);
    try comptime testAdd(f80);
    try testAdd(f128);
    try comptime testAdd(f128);
    try testAdd(c_longdouble);
    try comptime testAdd(c_longdouble);
}

fn testAdd(comptime T: type) !void {
    var one_point_two_five: T = 1.25;
    var two_point_seven_five: T = 2.75;
    _ = &one_point_two_five;
    _ = &two_point_seven_five;
    try expect(one_point_two_five + two_point_seven_five == 4);
}

test "sub f16" {
    if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;

    try testSub(f16);
    try comptime testSub(f16);
}

test "sub f32/f64" {
    try testSub(f32);
    try comptime testSub(f32);
    try testSub(f64);
    try comptime testSub(f64);
}

test "sub f80/f128/c_longdouble" {
    if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
    if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;

    try testSub(f80);
    try comptime testSub(f80);
    try testSub(f128);
    try comptime testSub(f128);
    try testSub(c_longdouble);
    try comptime testSub(c_longdouble);
}

fn testSub(comptime T: type) !void {
    var one_point_two_five: T = 1.25;
    var two_point_seven_five: T = 2.75;
    _ = &one_point_two_five;
    _ = &two_point_seven_five;
    try expect(one_point_two_five - two_point_seven_five == -1.5);
}

test "mul f16" {
    if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;

    try testMul(f16);
    try comptime testMul(f16);
}

test "mul f32/f64" {
    try testMul(f32);
    try comptime testMul(f32);
    try testMul(f64);
    try comptime testMul(f64);
}

test "mul f80/f128/c_longdouble" {
    if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
    if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;

    try testMul(f80);
    try comptime testMul(f80);
    try testMul(f128);
    try comptime testMul(f128);
    try testMul(c_longdouble);
    try comptime testMul(c_longdouble);
}

fn testMul(comptime T: type) !void {
    var one_point_two_five: T = 1.25;
    var two_point_seven_five: T = 2.75;
    _ = &one_point_two_five;
    _ = &two_point_seven_five;
    try expect(one_point_two_five * two_point_seven_five == 3.4375);
}

test "cmp f16" {
    if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
    if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
    if (builtin.cpu.arch.isArm() and builtin.target.abi.float() == .soft) return error.SkipZigTest; // https://github.com/ziglang/zig/issues/21234

    try testCmp(f16);
    try comptime testCmp(f16);
}

test "cmp f32" {
    if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
    if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
    if (builtin.cpu.arch.isArm() and builtin.target.abi.float() == .soft) return error.SkipZigTest; // https://github.com/ziglang/zig/issues/21234

    try testCmp(f32);
    try comptime testCmp(f32);
}

test "cmp f64" {
    if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
    if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
    if (builtin.cpu.arch.isArm() and builtin.target.abi.float() == .soft) return error.SkipZigTest; // https://github.com/ziglang/zig/issues/21234

    try testCmp(f64);
    try comptime testCmp(f64);
}

test "cmp f128" {
    if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
    if (builtin.zig_backend == .stage2_c and builtin.cpu.arch.isArm()) return error.SkipZigTest;
    if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
    if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;

    try testCmp(f128);
    try comptime testCmp(f128);
}

test "cmp f80/c_longdouble" {
    if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
    if (builtin.zig_backend == .stage2_c and builtin.cpu.arch.isArm()) return error.SkipZigTest;
    if (builtin.zig_backend == .stage2_llvm) return error.SkipZigTest;
    if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
    if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
    if (builtin.zig_backend == .stage2_x86_64 and builtin.target.ofmt == .coff) return error.SkipZigTest;

    try testCmp(f80);
    try comptime testCmp(f80);
    try testCmp(c_longdouble);
    try comptime testCmp(c_longdouble);
}

fn testCmp(comptime T: type) !void {
    {
        // No decimal part
        var x: T = 1.0;
        _ = &x;
        try expect(x == 1.0);
        try expect(x != 0.0);
        try expect(x > 0.0);
        try expect(x < 2.0);
        try expect(x >= 1.0);
        try expect(x <= 1.0);
    }
    {
        // Non-zero decimal part
        var x: T = 1.5;
        _ = &x;
        try expect(x != 1.0);
        try expect(x != 2.0);
        try expect(x > 1.0);
        try expect(x < 2.0);
        try expect(x >= 1.0);
        try expect(x <= 2.0);
    }

    @setEvalBranchQuota(2_000);
    var edges = [_]T{
        -math.inf(T),
        -math.floatMax(T),
        -math.floatMin(T),
        -math.floatTrueMin(T),
        -0.0,
        math.nan(T),
        0.0,
        math.floatTrueMin(T),
        math.floatMin(T),
        math.floatMax(T),
        math.inf(T),
    };
    _ = &edges;
    for (edges, 0..) |rhs, rhs_i| {
        for (edges, 0..) |lhs, lhs_i| {
            const no_nan = lhs_i != 5 and rhs_i != 5;
            const lhs_order = if (lhs_i < 5) lhs_i else lhs_i - 2;
            const rhs_order = if (rhs_i < 5) rhs_i else rhs_i - 2;
            try expect((lhs == rhs) == (no_nan and lhs_order == rhs_order));
            try expect((lhs != rhs) == !(no_nan and lhs_order == rhs_order));
            try expect((lhs < rhs) == (no_nan and lhs_order < rhs_order));
            try expect((lhs > rhs) == (no_nan and lhs_order > rhs_order));
            try expect((lhs <= rhs) == (no_nan and lhs_order <= rhs_order));
            try expect((lhs >= rhs) == (no_nan and lhs_order >= rhs_order));
        }
    }
}

test "vector cmp f16" {
    if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
    if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
    if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
    if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest;
    if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
    if (builtin.cpu.arch.isArm()) return error.SkipZigTest;
    if (builtin.cpu.arch.isPowerPC64()) return error.SkipZigTest;
    if (builtin.zig_backend == .stage2_llvm and builtin.cpu.arch == .hexagon) return error.SkipZigTest;

    try testCmpVector(f16);
    try comptime testCmpVector(f16);
}

test "vector cmp f32" {
    if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
    if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
    if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
    if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
    if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
    if (builtin.cpu.arch.isArm()) return error.SkipZigTest;
    if (builtin.cpu.arch.isPowerPC64()) return error.SkipZigTest;
    if (builtin.zig_backend == .stage2_llvm and builtin.cpu.arch == .hexagon) return error.SkipZigTest;

    try testCmpVector(f32);
    try comptime testCmpVector(f32);
}

test "vector cmp f64" {
    if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
    if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
    if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
    if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
    if (builtin.cpu.arch.isArm()) return error.SkipZigTest;
    if (builtin.cpu.arch.isPowerPC64()) return error.SkipZigTest;
    if (builtin.zig_backend == .stage2_llvm and builtin.cpu.arch == .hexagon) return error.SkipZigTest;

    try testCmpVector(f64);
    try comptime testCmpVector(f64);
}

test "vector cmp f128" {
    if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
    if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
    if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
    if (builtin.zig_backend == .stage2_c and builtin.cpu.arch.isArm()) return error.SkipZigTest;
    if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
    if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
    if (builtin.cpu.arch.isArm()) return error.SkipZigTest;
    if (builtin.cpu.arch.isPowerPC64()) return error.SkipZigTest;
    if (builtin.zig_backend == .stage2_llvm and builtin.cpu.arch == .hexagon) return error.SkipZigTest;

    try testCmpVector(f128);
    try comptime testCmpVector(f128);
}

test "vector cmp f80/c_longdouble" {
    if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
    if (builtin.zig_backend == .stage2_llvm and builtin.cpu.arch == .hexagon) return error.SkipZigTest;
    if (builtin.zig_backend == .stage2_llvm and builtin.cpu.arch == .powerpc64le) return error.SkipZigTest;
    if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
    if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
    if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest;
    if (builtin.zig_backend == .stage2_x86_64 and builtin.target.ofmt == .coff) return error.SkipZigTest;

    try testCmpVector(f80);
    try comptime testCmpVector(f80);
    try testCmpVector(c_longdouble);
    try comptime testCmpVector(c_longdouble);
}

fn testCmpVector(comptime T: type) !void {
    @setEvalBranchQuota(2_000);
    var edges = [_]T{
        -math.inf(T),
        -math.floatMax(T),
        -math.floatMin(T),
        -math.floatTrueMin(T),
        -0.0,
        math.nan(T),
        0.0,
        math.floatTrueMin(T),
        math.floatMin(T),
        math.floatMax(T),
        math.inf(T),
    };
    _ = &edges;
    for (edges, 0..) |rhs, rhs_i| {
        const rhs_v: @Vector(4, T) = .{ rhs, rhs, rhs, rhs };
        for (edges, 0..) |lhs, lhs_i| {
            const no_nan = lhs_i != 5 and rhs_i != 5;
            const lhs_order = if (lhs_i < 5) lhs_i else lhs_i - 2;
            const rhs_order = if (rhs_i < 5) rhs_i else rhs_i - 2;
            const lhs_v: @Vector(4, T) = .{ lhs, lhs, lhs, lhs };
            try expect(@reduce(.And, (lhs_v == rhs_v)) == (no_nan and lhs_order == rhs_order));
            try expect(@reduce(.And, (lhs_v != rhs_v)) == !(no_nan and lhs_order == rhs_order));
            try expect(@reduce(.And, (lhs_v < rhs_v)) == (no_nan and lhs_order < rhs_order));
            try expect(@reduce(.And, (lhs_v > rhs_v)) == (no_nan and lhs_order > rhs_order));
            try expect(@reduce(.And, (lhs_v <= rhs_v)) == (no_nan and lhs_order <= rhs_order));
            try expect(@reduce(.And, (lhs_v >= rhs_v)) == (no_nan and lhs_order >= rhs_order));
        }
    }
}

test "different sized float comparisons" {
    if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
    if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
    if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;

    try testDifferentSizedFloatComparisons();
    try comptime testDifferentSizedFloatComparisons();
}

fn testDifferentSizedFloatComparisons() !void {
    var a: f16 = 1;
    var b: f64 = 2;
    _ = .{ &a, &b };
    try expect(a < b);
}

// TODO This is waiting on library support for the Windows build (not sure why the other's don't need it)
//test "@nearbyint" {
//    comptime testNearbyInt();
//    testNearbyInt();
//}

//fn testNearbyInt() void {
//    // TODO test f16, f128, and c_longdouble
//    // https://github.com/ziglang/zig/issues/4026
//    {
//        var a: f32 = 2.1;
//    try expect(@nearbyint(a) == 2);
//    }
//    {
//        var a: f64 = -3.75;
//    try expect(@nearbyint(a) == -4);
//    }
//}

test "negative f128 intFromFloat at compile-time" {
    if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
    if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
    if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;

    const a: f128 = -2;
    var b: i64 = @intFromFloat(a);
    _ = &b;
    try expect(@as(i64, -2) == b);
}

test "@sqrt f16" {
    if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
    if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
    if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
    if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;

    try testSqrt(f16);
    try comptime testSqrt(f16);
}

test "@sqrt f32/f64" {
    if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
    if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
    if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;

    try testSqrt(f32);
    try comptime testSqrt(f32);
    try testSqrt(f64);
    try comptime testSqrt(f64);
}

test "@sqrt f80/f128/c_longdouble" {
    if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
    if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
    if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
    if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
    if (builtin.zig_backend == .stage2_x86_64 and builtin.target.ofmt == .coff) return error.SkipZigTest;

    if (builtin.os.tag == .freebsd) {
        // TODO https://github.com/ziglang/zig/issues/10875
        return error.SkipZigTest;
    }

    try testSqrt(f80);
    try comptime testSqrt(f80);
    try testSqrt(f128);
    try comptime testSqrt(f128);
    try testSqrt(c_longdouble);
    try comptime testSqrt(c_longdouble);
}

fn testSqrt(comptime T: type) !void {
    const eps = epsForType(T);
    var four: T = 4.0;
    try expect(@sqrt(four) == 2.0);
    var nine: T = 9.0;
    try expect(@sqrt(nine) == 3.0);
    var twenty_five: T = 25.0;
    try expect(@sqrt(twenty_five) == 5.0);
    var sixty_four: T = 64.0;
    try expect(@sqrt(sixty_four) == 8.0);
    var one_point_one: T = 1.1;

    try expect(math.approxEqAbs(T, @sqrt(one_point_one), 1.0488088481701516, eps));
    var two: T = 2.0;
    try expect(math.approxEqAbs(T, @sqrt(two), 1.4142135623730950, eps));
    var three_point_six: T = 3.6;
    try expect(math.approxEqAbs(T, @sqrt(three_point_six), 1.8973665961010276, eps));
    var sixty_four_point_one: T = 64.1;
    try expect(math.approxEqAbs(T, @sqrt(sixty_four_point_one), 8.00624756049923802, eps));
    var twelve: T = 12.0;
    try expect(math.approxEqAbs(T, @sqrt(twelve), 3.46410161513775459, eps));
    var thirteen: T = 13.0;
    try expect(math.approxEqAbs(T, @sqrt(thirteen), 3.60555127546398929, eps));
    var fourteen: T = 14.0;
    try expect(math.approxEqAbs(T, @sqrt(fourteen), 3.74165738677394139, eps));
    var a: T = 7.539840;
    try expect(math.approxEqAbs(T, @sqrt(a), 2.74587690911300684, eps));
    var b: T = 19.230934;
    try expect(math.approxEqAbs(T, @sqrt(b), 4.38530888307767894, eps));
    var c: T = 8942.230469;
    try expect(math.approxEqAbs(T, @sqrt(c), 94.5633674791671111, eps));

    // special cases
    var inf: T = math.inf(T);
    try expect(math.isPositiveInf(@sqrt(inf)));
    var zero: T = 0.0;
    try expect(@sqrt(zero) == 0.0);
    var neg_zero: T = -0.0;
    try expect(@sqrt(neg_zero) == 0.0);
    var neg_one: T = -1.0;
    try expect(math.isNan(@sqrt(neg_one)));
    var nan: T = math.nan(T);
    try expect(math.isNan(@sqrt(nan)));

    _ = .{
        &four,
        &nine,
        &twenty_five,
        &sixty_four,
        &one_point_one,
        &two,
        &three_point_six,
        &sixty_four_point_one,
        &twelve,
        &thirteen,
        &fourteen,
        &a,
        &b,
        &c,
        &inf,
        &zero,
        &neg_zero,
        &neg_one,
        &nan,
    };
}

test "@sqrt with vectors" {
    if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
    if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
    if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
    if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
    if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;

    try testSqrtWithVectors();
    try comptime testSqrtWithVectors();
}

fn testSqrtWithVectors() !void {
    var v: @Vector(4, f32) = [_]f32{ 1.1, 2.2, 3.3, 4.4 };
    _ = &v;
    const result = @sqrt(v);
    try expect(math.approxEqAbs(f32, @sqrt(@as(f32, 1.1)), result[0], epsilon));
    try expect(math.approxEqAbs(f32, @sqrt(@as(f32, 2.2)), result[1], epsilon));
    try expect(math.approxEqAbs(f32, @sqrt(@as(f32, 3.3)), result[2], epsilon));
    try expect(math.approxEqAbs(f32, @sqrt(@as(f32, 4.4)), result[3], epsilon));
}

test "@sin f16" {
    if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
    if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
    if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
    if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;

    try testSin(f16);
    try comptime testSin(f16);
}

test "@sin f32/f64" {
    if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
    if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
    if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
    if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;

    try testSin(f32);
    comptime try testSin(f32);
    try testSin(f64);
    comptime try testSin(f64);
}

test "@sin f80/f128/c_longdouble" {
    if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
    if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
    if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
    if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
    if (builtin.zig_backend == .stage2_x86_64 and builtin.target.ofmt == .coff) return error.SkipZigTest;

    try testSin(f80);
    comptime try testSin(f80);
    try testSin(f128);
    comptime try testSin(f128);
    try testSin(c_longdouble);
    comptime try testSin(c_longdouble);
}

fn testSin(comptime T: type) !void {
    const eps = epsForType(T);
    var zero: T = 0;
    _ = &zero;
    try expect(@sin(zero) == 0);
    var pi: T = math.pi;
    _ = &pi;
    try expect(math.approxEqAbs(T, @sin(pi), 0, eps));
    try expect(math.approxEqAbs(T, @sin(pi / 2.0), 1, eps));
    try expect(math.approxEqAbs(T, @sin(pi / 4.0), 0.7071067811865475, eps));
}

test "@sin with vectors" {
    if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
    if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
    if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
    if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
    if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;

    try testSinWithVectors();
    try comptime testSinWithVectors();
}

fn testSinWithVectors() !void {
    var v: @Vector(4, f32) = [_]f32{ 1.1, 2.2, 3.3, 4.4 };
    _ = &v;
    const result = @sin(v);
    try expect(math.approxEqAbs(f32, @sin(@as(f32, 1.1)), result[0], epsilon));
    try expect(math.approxEqAbs(f32, @sin(@as(f32, 2.2)), result[1], epsilon));
    try expect(math.approxEqAbs(f32, @sin(@as(f32, 3.3)), result[2], epsilon));
    try expect(math.approxEqAbs(f32, @sin(@as(f32, 4.4)), result[3], epsilon));
}

test "@cos f16" {
    if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
    if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
    if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
    if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;

    try testCos(f16);
    try comptime testCos(f16);
}

test "@cos f32/f64" {
    if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
    if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
    if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
    if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;

    try testCos(f32);
    try comptime testCos(f32);
    try testCos(f64);
    try comptime testCos(f64);
}

test "@cos f80/f128/c_longdouble" {
    if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
    if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
    if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
    if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
    if (builtin.zig_backend == .stage2_x86_64 and builtin.target.ofmt == .coff) return error.SkipZigTest;

    try testCos(f80);
    try comptime testCos(f80);
    try testCos(f128);
    try comptime testCos(f128);
    try testCos(c_longdouble);
    try comptime testCos(c_longdouble);
}

fn testCos(comptime T: type) !void {
    const eps = epsForType(T);
    var zero: T = 0;
    _ = &zero;
    try expect(@cos(zero) == 1);
    var pi: T = math.pi;
    _ = &pi;
    try expect(math.approxEqAbs(T, @cos(pi), -1, eps));
    try expect(math.approxEqAbs(T, @cos(pi / 2.0), 0, eps));
    try expect(math.approxEqAbs(T, @cos(pi / 4.0), 0.7071067811865475, eps));
}

test "@cos with vectors" {
    if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
    if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
    if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
    if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
    if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;

    try testCosWithVectors();
    try comptime testCosWithVectors();
}

fn testCosWithVectors() !void {
    var v: @Vector(4, f32) = [_]f32{ 1.1, 2.2, 3.3, 4.4 };
    _ = &v;
    const result = @cos(v);
    try expect(math.approxEqAbs(f32, @cos(@as(f32, 1.1)), result[0], epsilon));
    try expect(math.approxEqAbs(f32, @cos(@as(f32, 2.2)), result[1], epsilon));
    try expect(math.approxEqAbs(f32, @cos(@as(f32, 3.3)), result[2], epsilon));
    try expect(math.approxEqAbs(f32, @cos(@as(f32, 4.4)), result[3], epsilon));
}

test "@tan f16" {
    if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
    if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
    if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
    if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;

    try testTan(f16);
    try comptime testTan(f16);
}

test "@tan f32/f64" {
    if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
    if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
    if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
    if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;

    try testTan(f32);
    try comptime testTan(f32);
    try testTan(f64);
    try comptime testTan(f64);
}

test "@tan f80/f128/c_longdouble" {
    if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
    if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
    if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
    if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
    if (builtin.zig_backend == .stage2_x86_64 and builtin.target.ofmt == .coff) return error.SkipZigTest;

    try testTan(f80);
    try comptime testTan(f80);
    try testTan(f128);
    try comptime testTan(f128);
    try testTan(c_longdouble);
    try comptime testTan(c_longdouble);
}

fn testTan(comptime T: type) !void {
    const eps = epsForType(T);
    var zero: T = 0;
    _ = &zero;
    try expect(@tan(zero) == 0);
    var pi: T = math.pi;
    _ = &pi;
    try expect(math.approxEqAbs(T, @tan(pi), 0, eps));
    try expect(math.approxEqAbs(T, @tan(pi / 3.0), 1.732050807568878, eps));
    try expect(math.approxEqAbs(T, @tan(pi / 4.0), 1, eps));
}

test "@tan with vectors" {
    if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
    if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
    if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
    if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
    if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;

    try testTanWithVectors();
    try comptime testTanWithVectors();
}

fn testTanWithVectors() !void {
    var v: @Vector(4, f32) = [_]f32{ 1.1, 2.2, 3.3, 4.4 };
    _ = &v;
    const result = @tan(v);
    try expect(math.approxEqAbs(f32, @tan(@as(f32, 1.1)), result[0], epsilon));
    try expect(math.approxEqAbs(f32, @tan(@as(f32, 2.2)), result[1], epsilon));
    try expect(math.approxEqAbs(f32, @tan(@as(f32, 3.3)), result[2], epsilon));
    try expect(math.approxEqAbs(f32, @tan(@as(f32, 4.4)), result[3], epsilon));
}

test "@exp f16" {
    if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
    if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
    if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
    if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;

    try testExp(f16);
    try comptime testExp(f16);
}

test "@exp f32/f64" {
    if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
    if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
    if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
    if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;

    try testExp(f32);
    try comptime testExp(f32);
    try testExp(f64);
    try comptime testExp(f64);
}

test "@exp f80/f128/c_longdouble" {
    if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
    if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
    if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
    if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
    if (builtin.zig_backend == .stage2_x86_64 and builtin.target.ofmt == .coff) return error.SkipZigTest;

    try testExp(f80);
    try comptime testExp(f80);
    try testExp(f128);
    try comptime testExp(f128);
    try testExp(c_longdouble);
    try comptime testExp(c_longdouble);
}

fn testExp(comptime T: type) !void {
    const eps = epsForType(T);

    var zero: T = 0;
    _ = &zero;
    try expect(@exp(zero) == 1);

    var two: T = 2;
    _ = &two;
    try expect(math.approxEqAbs(T, @exp(two), 7.389056098930650, eps));

    var five: T = 5;
    _ = &five;
    try expect(math.approxEqAbs(T, @exp(five), 148.4131591025766, eps));
}

test "@exp with vectors" {
    if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
    if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
    if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
    if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
    if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;

    try testExpWithVectors();
    try comptime testExpWithVectors();
}

fn testExpWithVectors() !void {
    var v: @Vector(4, f32) = [_]f32{ 1.1, 2.2, 0.3, 0.4 };
    _ = &v;
    const result = @exp(v);
    try expect(math.approxEqAbs(f32, @exp(@as(f32, 1.1)), result[0], epsilon));
    try expect(math.approxEqAbs(f32, @exp(@as(f32, 2.2)), result[1], epsilon));
    try expect(math.approxEqAbs(f32, @exp(@as(f32, 0.3)), result[2], epsilon));
    try expect(math.approxEqAbs(f32, @exp(@as(f32, 0.4)), result[3], epsilon));
}

test "@exp2 f16" {
    if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
    if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
    if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
    if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;

    try testExp2(f16);
    try comptime testExp2(f16);
}

test "@exp2 f32/f64" {
    if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
    if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
    if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
    if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;

    try testExp2(f32);
    try comptime testExp2(f32);
    try testExp2(f64);
    try comptime testExp2(f64);
}

test "@exp2 f80/f128/c_longdouble" {
    if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
    if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
    if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
    if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
    if (builtin.zig_backend == .stage2_x86_64 and builtin.target.ofmt == .coff) return error.SkipZigTest;

    try testExp2(f80);
    try comptime testExp2(f80);
    try testExp2(f128);
    try comptime testExp2(f128);
    try testExp2(c_longdouble);
    try comptime testExp2(c_longdouble);
}

fn testExp2(comptime T: type) !void {
    const eps = epsForType(T);
    var two: T = 2;
    try expect(@exp2(two) == 4);
    var one_point_five: T = 1.5;
    try expect(math.approxEqAbs(T, @exp2(one_point_five), 2.8284271247462, eps));
    var four_point_five: T = 4.5;
    try expect(math.approxEqAbs(T, @exp2(four_point_five), 22.627416997969, eps));
    _ = .{ &two, &one_point_five, &four_point_five };
}

test "@exp2 with @vectors" {
    if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
    if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
    if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
    if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
    if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;

    try testExp2WithVectors();
    try comptime testExp2WithVectors();
}

fn testExp2WithVectors() !void {
    var v: @Vector(4, f32) = [_]f32{ 1.1, 2.2, 0.3, 0.4 };
    _ = &v;
    const result = @exp2(v);
    try expect(math.approxEqAbs(f32, @exp2(@as(f32, 1.1)), result[0], epsilon));
    try expect(math.approxEqAbs(f32, @exp2(@as(f32, 2.2)), result[1], epsilon));
    try expect(math.approxEqAbs(f32, @exp2(@as(f32, 0.3)), result[2], epsilon));
    try expect(math.approxEqAbs(f32, @exp2(@as(f32, 0.4)), result[3], epsilon));
}

test "@log f16" {
    if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
    if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
    if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
    if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;

    try testLog(f16);
    try comptime testLog(f16);
}

test "@log f32/f64" {
    if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
    if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
    if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
    if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;

    try testLog(f32);
    try comptime testLog(f32);
    try testLog(f64);
    try comptime testLog(f64);
}

test "@log f80/f128/c_longdouble" {
    if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
    if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
    if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
    if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
    if (builtin.zig_backend == .stage2_x86_64 and builtin.target.ofmt == .coff) return error.SkipZigTest;

    try testLog(f80);
    try comptime testLog(f80);
    try testLog(f128);
    try comptime testLog(f128);
    try testLog(c_longdouble);
    try comptime testLog(c_longdouble);
}

fn testLog(comptime T: type) !void {
    const eps = epsForType(T);
    var e: T = math.e;
    try expect(math.approxEqAbs(T, @log(e), 1, eps));
    var two: T = 2;
    try expect(math.approxEqAbs(T, @log(two), 0.6931471805599, eps));
    var five: T = 5;
    try expect(math.approxEqAbs(T, @log(five), 1.6094379124341, eps));
    _ = .{ &e, &two, &five };
}

test "@log with @vectors" {
    if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
    if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
    if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
    if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
    if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
    if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;

    {
        var v: @Vector(4, f32) = [_]f32{ 1.1, 2.2, 0.3, 0.4 };
        _ = &v;
        const result = @log(v);
        try expect(@log(@as(f32, 1.1)) == result[0]);
        try expect(@log(@as(f32, 2.2)) == result[1]);
        try expect(@log(@as(f32, 0.3)) == result[2]);
        try expect(@log(@as(f32, 0.4)) == result[3]);
    }
}

test "@log2 f16" {
    if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
    if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
    if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
    if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;

    try testLog2(f16);
    try comptime testLog2(f16);
}

test "@log2 f32/f64" {
    if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
    if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
    if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
    if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;

    try testLog2(f32);
    try comptime testLog2(f32);
    try testLog2(f64);
    try comptime testLog2(f64);
}

test "@log2 f80/f128/c_longdouble" {
    if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
    if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
    if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
    if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
    if (builtin.zig_backend == .stage2_x86_64 and builtin.target.ofmt == .coff) return error.SkipZigTest;

    try testLog2(f80);
    try comptime testLog2(f80);
    try testLog2(f128);
    try comptime testLog2(f128);
    try testLog2(c_longdouble);
    try comptime testLog2(c_longdouble);
}

fn testLog2(comptime T: type) !void {
    const eps = epsForType(T);
    var four: T = 4;
    try expect(@log2(four) == 2);
    var six: T = 6;
    try expect(math.approxEqAbs(T, @log2(six), 2.5849625007212, eps));
    var ten: T = 10;
    try expect(math.approxEqAbs(T, @log2(ten), 3.3219280948874, eps));
    _ = .{ &four, &six, &ten };
}

test "@log2 with vectors" {
    if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
    if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
    if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
    if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
    if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
    // https://github.com/ziglang/zig/issues/13681
    if (builtin.zig_backend == .stage2_llvm and
        builtin.cpu.arch == .aarch64 and
        builtin.os.tag == .windows) return error.SkipZigTest;

    try testLog2WithVectors();
    try comptime testLog2WithVectors();
}

fn testLog2WithVectors() !void {
    var v: @Vector(4, f32) = [_]f32{ 1.1, 2.2, 0.3, 0.4 };
    _ = &v;
    const result = @log2(v);
    try expect(@log2(@as(f32, 1.1)) == result[0]);
    try expect(@log2(@as(f32, 2.2)) == result[1]);
    try expect(@log2(@as(f32, 0.3)) == result[2]);
    try expect(@log2(@as(f32, 0.4)) == result[3]);
}

test "@log10 f16" {
    if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
    if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
    if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
    if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;

    try testLog10(f16);
    try comptime testLog10(f16);
}

test "@log10 f32/f64" {
    if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
    if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
    if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
    if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;

    try testLog10(f32);
    try comptime testLog10(f32);
    try testLog10(f64);
    try comptime testLog10(f64);
}

test "@log10 f80/f128/c_longdouble" {
    if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
    if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
    if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
    if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
    if (builtin.zig_backend == .stage2_x86_64 and builtin.target.ofmt == .coff) return error.SkipZigTest;

    try testLog10(f80);
    try comptime testLog10(f80);
    try testLog10(f128);
    try comptime testLog10(f128);
    try testLog10(c_longdouble);
    try comptime testLog10(c_longdouble);
}

fn testLog10(comptime T: type) !void {
    const eps = epsForType(T);
    var hundred: T = 100;
    try expect(@log10(hundred) == 2);
    var fifteen: T = 15;
    try expect(math.approxEqAbs(T, @log10(fifteen), 1.176091259056, eps));
    var fifty: T = 50;
    try expect(math.approxEqAbs(T, @log10(fifty), 1.698970004336, eps));
    _ = .{ &hundred, &fifteen, &fifty };
}

test "@log10 with vectors" {
    if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
    if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
    if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
    if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
    if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;

    try testLog10WithVectors();
    try comptime testLog10WithVectors();
}

fn testLog10WithVectors() !void {
    var v: @Vector(4, f32) = [_]f32{ 1.1, 2.2, 0.3, 0.4 };
    _ = &v;
    const result = @log10(v);
    try expect(@log10(@as(f32, 1.1)) == result[0]);
    try expect(@log10(@as(f32, 2.2)) == result[1]);
    try expect(@log10(@as(f32, 0.3)) == result[2]);
    try expect(@log10(@as(f32, 0.4)) == result[3]);
}

test "@abs f16" {
    if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
    if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
    if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;

    try testFabs(f16);
    try comptime testFabs(f16);
}

test "@abs f32/f64" {
    if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
    if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO

    try testFabs(f32);
    try comptime testFabs(f32);
    try testFabs(f64);
    try comptime testFabs(f64);
}

test "@abs f80/f128/c_longdouble" {
    if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
    if (builtin.zig_backend == .stage2_c and builtin.cpu.arch.isArm()) return error.SkipZigTest;
    if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
    if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
    if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
    if (builtin.zig_backend == .stage2_x86_64 and builtin.target.ofmt == .coff) return error.SkipZigTest;

    try testFabs(f80);
    try comptime testFabs(f80);
    try testFabs(f128);
    try comptime testFabs(f128);
    try testFabs(c_longdouble);
    try comptime testFabs(c_longdouble);
}

fn testFabs(comptime T: type) !void {
    var two_point_five: T = 2.5;
    try expect(@abs(two_point_five) == 2.5);
    var neg_two_point_five: T = -2.5;
    try expect(@abs(neg_two_point_five) == 2.5);

    var twelve: T = 12.0;
    try expect(@abs(twelve) == 12.0);
    var neg_fourteen: T = -14.0;
    try expect(@abs(neg_fourteen) == 14.0);

    // normals
    var one: T = 1.0;
    try expect(@abs(one) == 1.0);
    var neg_one: T = -1.0;
    try expect(@abs(neg_one) == 1.0);
    var min: T = math.floatMin(T);
    try expect(@abs(min) == math.floatMin(T));
    var neg_min: T = -math.floatMin(T);
    try expect(@abs(neg_min) == math.floatMin(T));
    var max: T = math.floatMax(T);
    try expect(@abs(max) == math.floatMax(T));
    var neg_max: T = -math.floatMax(T);
    try expect(@abs(neg_max) == math.floatMax(T));

    // subnormals
    var zero: T = 0.0;
    try expect(@abs(zero) == 0.0);
    var neg_zero: T = -0.0;
    try expect(@abs(neg_zero) == 0.0);
    var true_min: T = math.floatTrueMin(T);
    try expect(@abs(true_min) == math.floatTrueMin(T));
    var neg_true_min: T = -math.floatTrueMin(T);
    try expect(@abs(neg_true_min) == math.floatTrueMin(T));

    // non-finite numbers
    var inf: T = math.inf(T);
    try expect(math.isPositiveInf(@abs(inf)));
    var neg_inf: T = -math.inf(T);
    try expect(math.isPositiveInf(@abs(neg_inf)));
    var nan: T = math.nan(T);
    try expect(math.isNan(@abs(nan)));

    _ = .{
        &two_point_five,
        &neg_two_point_five,
        &twelve,
        &neg_fourteen,
        &one,
        &neg_one,
        &min,
        &neg_min,
        &max,
        &neg_max,
        &zero,
        &neg_zero,
        &true_min,
        &neg_true_min,
        &inf,
        &neg_inf,
        &nan,
    };
}

test "@abs with vectors" {
    if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
    if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
    if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
    if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;

    try testFabsWithVectors();
    try comptime testFabsWithVectors();
}

fn testFabsWithVectors() !void {
    var v: @Vector(4, f32) = [_]f32{ 1.1, -2.2, 0.3, -0.4 };
    _ = &v;
    const result = @abs(v);
    try expect(math.approxEqAbs(f32, @abs(@as(f32, 1.1)), result[0], epsilon));
    try expect(math.approxEqAbs(f32, @abs(@as(f32, -2.2)), result[1], epsilon));
    try expect(math.approxEqAbs(f32, @abs(@as(f32, 0.3)), result[2], epsilon));
    try expect(math.approxEqAbs(f32, @abs(@as(f32, -0.4)), result[3], epsilon));
}

test "@floor f16" {
    if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
    if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
    if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;

    try testFloor(f16);
    try comptime testFloor(f16);
}

test "@floor f32/f64" {
    if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
    if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
    if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;

    try testFloor(f32);
    try comptime testFloor(f32);
    try testFloor(f64);
    try comptime testFloor(f64);
}

test "@floor f80/f128/c_longdouble" {
    if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
    if (builtin.zig_backend == .stage2_c and builtin.cpu.arch.isArm()) return error.SkipZigTest;
    if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
    if (builtin.zig_backend == .stage2_x86_64 and builtin.target.ofmt != .elf and builtin.target.ofmt != .macho) return error.SkipZigTest;
    if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
    if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;

    if (builtin.zig_backend == .stage2_llvm and builtin.os.tag == .windows) {
        // https://github.com/ziglang/zig/issues/12602
        return error.SkipZigTest;
    }

    try testFloor(f80);
    try comptime testFloor(f80);
    try testFloor(f128);
    try comptime testFloor(f128);
    try testFloor(c_longdouble);
    try comptime testFloor(c_longdouble);
}

fn testFloor(comptime T: type) !void {
    var two_point_one: T = 2.1;
    try expect(@floor(two_point_one) == 2.0);
    var neg_two_point_one: T = -2.1;
    try expect(@floor(neg_two_point_one) == -3.0);
    var three_point_five: T = 3.5;
    try expect(@floor(three_point_five) == 3.0);
    var neg_three_point_five: T = -3.5;
    try expect(@floor(neg_three_point_five) == -4.0);
    var twelve: T = 12.0;
    try expect(@floor(twelve) == 12.0);
    var neg_twelve: T = -12.0;
    try expect(@floor(neg_twelve) == -12.0);
    var fourteen_point_seven: T = 14.7;
    try expect(@floor(fourteen_point_seven) == 14.0);
    var neg_fourteen_point_seven: T = -14.7;
    try expect(@floor(neg_fourteen_point_seven) == -15.0);

    _ = .{
        &two_point_one,
        &neg_two_point_one,
        &three_point_five,
        &neg_three_point_five,
        &twelve,
        &neg_twelve,
        &fourteen_point_seven,
        &neg_fourteen_point_seven,
    };
}

test "@floor with vectors" {
    if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
    if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
    if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
    if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;

    try testFloorWithVectors();
    try comptime testFloorWithVectors();
}

fn testFloorWithVectors() !void {
    var v: @Vector(4, f32) = [_]f32{ 1.1, -2.2, 0.3, -0.4 };
    _ = &v;
    const result = @floor(v);
    try expect(math.approxEqAbs(f32, @floor(@as(f32, 1.1)), result[0], epsilon));
    try expect(math.approxEqAbs(f32, @floor(@as(f32, -2.2)), result[1], epsilon));
    try expect(math.approxEqAbs(f32, @floor(@as(f32, 0.3)), result[2], epsilon));
    try expect(math.approxEqAbs(f32, @floor(@as(f32, -0.4)), result[3], epsilon));
}

test "@ceil f16" {
    if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
    if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
    if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;

    try testCeil(f16);
    try comptime testCeil(f16);
}

test "@ceil f32/f64" {
    if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
    if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
    if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;

    try testCeil(f32);
    try comptime testCeil(f32);
    try testCeil(f64);
    try comptime testCeil(f64);
}

test "@ceil f80/f128/c_longdouble" {
    if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
    if (builtin.zig_backend == .stage2_c and builtin.cpu.arch.isArm()) return error.SkipZigTest;
    if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
    if (builtin.zig_backend == .stage2_x86_64 and builtin.target.ofmt != .elf and builtin.target.ofmt != .macho) return error.SkipZigTest;
    if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
    if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;

    if (builtin.zig_backend == .stage2_llvm and builtin.os.tag == .windows) {
        // https://github.com/ziglang/zig/issues/12602
        return error.SkipZigTest;
    }

    try testCeil(f80);
    try comptime testCeil(f80);
    try testCeil(f128);
    try comptime testCeil(f128);
    try testCeil(c_longdouble);
    try comptime testCeil(c_longdouble);
}

fn testCeil(comptime T: type) !void {
    var two_point_one: T = 2.1;
    try expect(@ceil(two_point_one) == 3.0);
    var neg_two_point_one: T = -2.1;
    try expect(@ceil(neg_two_point_one) == -2.0);
    var three_point_five: T = 3.5;
    try expect(@ceil(three_point_five) == 4.0);
    var neg_three_point_five: T = -3.5;
    try expect(@ceil(neg_three_point_five) == -3.0);
    var twelve: T = 12.0;
    try expect(@ceil(twelve) == 12.0);
    var neg_twelve: T = -12.0;
    try expect(@ceil(neg_twelve) == -12.0);
    var fourteen_point_seven: T = 14.7;
    try expect(@ceil(fourteen_point_seven) == 15.0);
    var neg_fourteen_point_seven: T = -14.7;
    try expect(@ceil(neg_fourteen_point_seven) == -14.0);

    _ = .{
        &two_point_one,
        &neg_two_point_one,
        &three_point_five,
        &neg_three_point_five,
        &twelve,
        &neg_twelve,
        &fourteen_point_seven,
        &neg_fourteen_point_seven,
    };
}

test "@ceil with vectors" {
    if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
    if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
    if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
    if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;

    try testCeilWithVectors();
    try comptime testCeilWithVectors();
}

fn testCeilWithVectors() !void {
    var v: @Vector(4, f32) = [_]f32{ 1.1, -2.2, 0.3, -0.4 };
    _ = &v;
    const result = @ceil(v);
    try expect(math.approxEqAbs(f32, @ceil(@as(f32, 1.1)), result[0], epsilon));
    try expect(math.approxEqAbs(f32, @ceil(@as(f32, -2.2)), result[1], epsilon));
    try expect(math.approxEqAbs(f32, @ceil(@as(f32, 0.3)), result[2], epsilon));
    try expect(math.approxEqAbs(f32, @ceil(@as(f32, -0.4)), result[3], epsilon));
}

test "@trunc f16" {
    if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
    if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
    if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;

    try testTrunc(f16);
    try comptime testTrunc(f16);
}

test "@trunc f32/f64" {
    if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
    if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
    if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;

    try testTrunc(f32);
    try comptime testTrunc(f32);
    try testTrunc(f64);
    try comptime testTrunc(f64);
}

test "@trunc f80/f128/c_longdouble" {
    if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
    if (builtin.zig_backend == .stage2_c and builtin.cpu.arch.isArm()) return error.SkipZigTest;
    if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
    if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
    if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
    if (builtin.zig_backend == .stage2_x86_64 and builtin.target.ofmt == .coff) return error.SkipZigTest;

    if (builtin.zig_backend == .stage2_llvm and builtin.os.tag == .windows) {
        // https://github.com/ziglang/zig/issues/12602
        return error.SkipZigTest;
    }

    try testTrunc(f80);
    try comptime testTrunc(f80);
    try testTrunc(f128);
    try comptime testTrunc(f128);
    try testTrunc(c_longdouble);
    try comptime testTrunc(c_longdouble);
}

fn testTrunc(comptime T: type) !void {
    var two_point_one: T = 2.1;
    try expect(@trunc(two_point_one) == 2.0);
    var neg_two_point_one: T = -2.1;
    try expect(@trunc(neg_two_point_one) == -2.0);
    var three_point_five: T = 3.5;
    try expect(@trunc(three_point_five) == 3.0);
    var neg_three_point_five: T = -3.5;
    try expect(@trunc(neg_three_point_five) == -3.0);
    var twelve: T = 12.0;
    try expect(@trunc(twelve) == 12.0);
    var neg_twelve: T = -12.0;
    try expect(@trunc(neg_twelve) == -12.0);
    var fourteen_point_seven: T = 14.7;
    try expect(@trunc(fourteen_point_seven) == 14.0);
    var neg_fourteen_point_seven: T = -14.7;
    try expect(@trunc(neg_fourteen_point_seven) == -14.0);

    _ = .{
        &two_point_one,
        &neg_two_point_one,
        &three_point_five,
        &neg_three_point_five,
        &twelve,
        &neg_twelve,
        &fourteen_point_seven,
        &neg_fourteen_point_seven,
    };
}

test "@trunc with vectors" {
    if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
    if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
    if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
    if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;

    try testTruncWithVectors();
    try comptime testTruncWithVectors();
}

fn testTruncWithVectors() !void {
    var v: @Vector(4, f32) = [_]f32{ 1.1, -2.2, 0.3, -0.4 };
    _ = &v;
    const result = @trunc(v);
    try expect(math.approxEqAbs(f32, @trunc(@as(f32, 1.1)), result[0], epsilon));
    try expect(math.approxEqAbs(f32, @trunc(@as(f32, -2.2)), result[1], epsilon));
    try expect(math.approxEqAbs(f32, @trunc(@as(f32, 0.3)), result[2], epsilon));
    try expect(math.approxEqAbs(f32, @trunc(@as(f32, -0.4)), result[3], epsilon));
}

test "neg f16" {
    if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
    if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
    if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
    if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest;

    if (builtin.os.tag == .freebsd) {
        // TODO file issue to track this failure
        return error.SkipZigTest;
    }

    try testNeg(f16);
    try comptime testNeg(f16);
}

test "neg f32/f64" {
    if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
    if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
    if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;

    try testNeg(f32);
    try comptime testNeg(f32);
    try testNeg(f64);
    try comptime testNeg(f64);
}

test "neg f80/f128/c_longdouble" {
    if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
    if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
    if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
    if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest;
    if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
    if (builtin.zig_backend == .stage2_x86_64 and builtin.target.ofmt == .coff) return error.SkipZigTest;

    try testNeg(f80);
    try comptime testNeg(f80);
    try testNeg(f128);
    try comptime testNeg(f128);
    try testNeg(c_longdouble);
    try comptime testNeg(c_longdouble);
}

fn testNeg(comptime T: type) !void {
    var two_point_five: T = 2.5;
    try expect(-two_point_five == -2.5);
    var neg_two_point_five: T = -2.5;
    try expect(-neg_two_point_five == 2.5);

    var twelve: T = 12.0;
    try expect(-twelve == -12.0);
    var neg_fourteen: T = -14.0;
    try expect(-neg_fourteen == 14.0);

    // normals
    var one: T = 1.0;
    try expect(-one == -1.0);
    var neg_one: T = -1.0;
    try expect(-neg_one == 1.0);
    var min: T = math.floatMin(T);
    try expect(-min == -math.floatMin(T));
    var neg_min: T = -math.floatMin(T);
    try expect(-neg_min == math.floatMin(T));
    var max: T = math.floatMax(T);
    try expect(-max == -math.floatMax(T));
    var neg_max: T = -math.floatMax(T);
    try expect(-neg_max == math.floatMax(T));

    // subnormals
    var zero: T = 0.0;
    try expect(-zero == -0.0);
    var neg_zero: T = -0.0;
    try expect(-neg_zero == 0.0);
    var true_min: T = math.floatTrueMin(T);
    try expect(-true_min == -math.floatTrueMin(T));
    var neg_true_min: T = -math.floatTrueMin(T);
    try expect(-neg_true_min == math.floatTrueMin(T));

    // non-finite numbers
    var inf: T = math.inf(T);
    try expect(math.isNegativeInf(-inf));
    var neg_inf: T = -math.inf(T);
    try expect(math.isPositiveInf(-neg_inf));
    var nan: T = math.nan(T);
    try expect(math.isNan(-nan));
    try expect(math.signbit(-nan));
    var neg_nan: T = -math.nan(T);
    try expect(math.isNan(-neg_nan));
    try expect(!math.signbit(-neg_nan));

    _ = .{
        &two_point_five,
        &neg_two_point_five,
        &twelve,
        &neg_fourteen,
        &one,
        &neg_one,
        &min,
        &neg_min,
        &max,
        &neg_max,
        &zero,
        &neg_zero,
        &true_min,
        &neg_true_min,
        &inf,
        &neg_inf,
        &nan,
        &neg_nan,
    };
}

test "eval @setFloatMode at compile-time" {
    if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO

    const result = comptime fnWithFloatMode();
    try expect(result == 1234.0);
}

fn fnWithFloatMode() f32 {
    @setFloatMode(std.builtin.FloatMode.strict);
    return 1234.0;
}

test "float literal at compile time not lossy" {
    try expect(16777216.0 + 1.0 == 16777217.0);
    try expect(9007199254740992.0 + 1.0 == 9007199254740993.0);
}

test "f128 at compile time is lossy" {
    try expect(@as(f128, 10384593717069655257060992658440192.0) + 1 == 10384593717069655257060992658440192.0);
}

test "comptime fixed-width float zero divided by zero produces NaN" {
    if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
    if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
    if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
    if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;

    inline for (.{ f16, f32, f64, f80, f128 }) |F| {
        try expect(math.isNan(@as(F, 0) / @as(F, 0)));
    }
}

test "comptime fixed-width float non-zero divided by zero produces signed Inf" {
    if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
    if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
    if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;

    inline for (.{ f16, f32, f64, f80, f128 }) |F| {
        const pos = @as(F, 1) / @as(F, 0);
        const neg = @as(F, -1) / @as(F, 0);
        try expect(math.isInf(pos));
        try expect(math.isInf(neg));
        try expect(pos > 0);
        try expect(neg < 0);
    }
}

test "comptime float compared with runtime int" {
    const f = 10.0;
    var i: usize = 0;
    _ = &i;
    try std.testing.expect(i < f);
}
test "comptime nan < runtime 0" {
    const f = comptime std.math.nan(f64);
    var i: usize = 0;
    _ = &i;
    try std.testing.expect(!(f < i));
}
test "comptime inf > runtime 0" {
    const f = comptime std.math.inf(f64);
    var i: usize = 0;
    _ = &i;
    try std.testing.expect(f > i);
}
test "comptime -inf < runtime 0" {
    const f = comptime -std.math.inf(f64);
    var i: usize = 0;
    _ = &i;
    try std.testing.expect(f < i);
}
test "comptime inf >= runtime 1" {
    const f = comptime std.math.inf(f64);
    var i: usize = 1;
    _ = &i;
    try std.testing.expect(f >= i);
}
test "comptime isNan(nan * 1)" {
    if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO

    const nan_times_one = comptime std.math.nan(f64) * 1;
    try std.testing.expect(std.math.isNan(nan_times_one));
}
test "runtime isNan(nan * 1)" {
    if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO

    const nan_times_one = std.math.nan(f64) * 1;
    try std.testing.expect(std.math.isNan(nan_times_one));
}
test "comptime isNan(nan * 0)" {
    if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO

    const nan_times_zero = comptime std.math.nan(f64) * 0;
    try std.testing.expect(std.math.isNan(nan_times_zero));
    const zero_times_nan = 0 * comptime std.math.nan(f64);
    try std.testing.expect(std.math.isNan(zero_times_nan));
}
test "runtime isNan(nan * 0)" {
    if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO

    const nan_times_zero = std.math.nan(f64) * 0;
    try std.testing.expect(std.math.isNan(nan_times_zero));
    const zero_times_nan = 0 * std.math.nan(f64);
    try std.testing.expect(std.math.isNan(zero_times_nan));
}
test "comptime isNan(inf * 0)" {
    if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO

    const inf_times_zero = comptime std.math.inf(f64) * 0;
    try std.testing.expect(std.math.isNan(inf_times_zero));
    const zero_times_inf = 0 * comptime std.math.inf(f64);
    try std.testing.expect(std.math.isNan(zero_times_inf));
}
test "runtime isNan(inf * 0)" {
    if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO

    const inf_times_zero = std.math.inf(f64) * 0;
    try std.testing.expect(std.math.isNan(inf_times_zero));
    const zero_times_inf = 0 * std.math.inf(f64);
    try std.testing.expect(std.math.isNan(zero_times_inf));
}

test "optimized float mode" {
    if (builtin.zig_backend != .stage2_llvm) return error.SkipZigTest;
    if (builtin.mode == .Debug) return error.SkipZigTest;

    const big = 0x1p40;
    const small = 0.001;
    const tiny = 0x1p-10;

    const S = struct {
        fn strict(x: f64) f64 {
            @setFloatMode(.strict);
            return x + big - big;
        }
        fn optimized(x: f64) f64 {
            @setFloatMode(.optimized);
            return x + big - big;
        }
    };
    try expect(S.optimized(small) == small);
    try expect(S.strict(small) == tiny);
}

fn MakeType(comptime x: anytype) type {
    return struct {
        fn get() @TypeOf(x) {
            return x;
        }
    };
}

const nan_a: f32 = @bitCast(@as(u32, 0xffc00000));
const nan_b: f32 = @bitCast(@as(u32, 0xffe00000));

fn testMemoization() !void {
    try expect(MakeType(nan_a) == MakeType(nan_a));
    try expect(MakeType(nan_b) == MakeType(nan_b));
    try expect(MakeType(nan_a) != MakeType(nan_b));
}

fn testVectorMemoization(comptime T: type) !void {
    const nan_a_v: T = @splat(nan_a);
    const nan_b_v: T = @splat(nan_b);
    try expect(MakeType(nan_a_v) == MakeType(nan_a_v));
    try expect(MakeType(nan_b_v) == MakeType(nan_b_v));
    try expect(MakeType(nan_a_v) != MakeType(nan_b_v));
}

test "comptime calls are only memoized when float arguments are bit-for-bit equal" {
    try comptime testMemoization();
    try comptime testVectorMemoization(@Vector(4, f32));
}

test "result location forwarded through unary float builtins" {
    if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
    if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
    if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
    if (builtin.zig_backend == .stage2_x86_64 and builtin.target.ofmt != .elf and builtin.target.ofmt != .macho) return error.SkipZigTest;
    if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
    if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;

    const S = struct {
        var x: u32 = 10;
    };

    var y: f64 = 0.0;
    y = @sqrt(@floatFromInt(S.x));
    y = @sin(@floatFromInt(S.x));
    y = @cos(@floatFromInt(S.x));
    y = @tan(@floatFromInt(S.x));
    y = @exp(@floatFromInt(S.x));
    y = @exp2(@floatFromInt(S.x));
    y = @log(@floatFromInt(S.x));
    y = @log2(@floatFromInt(S.x));
    y = @log10(@floatFromInt(S.x));
    y = @floor(@floatFromInt(S.x));
    y = @ceil(@floatFromInt(S.x));
    y = @trunc(@floatFromInt(S.x));
    y = @round(@floatFromInt(S.x));
}
