|  | // Copyright 2021 The Tint Authors. | 
|  | // | 
|  | // Licensed under the Apache License, Version 2.0 (the "License"); | 
|  | // you may not use this file except in compliance with the License. | 
|  | // You may obtain a copy of the License at | 
|  | // | 
|  | //     http://www.apache.org/licenses/LICENSE-2.0 | 
|  | // | 
|  | // Unless required by applicable law or agreed to in writing, software | 
|  | // distributed under the License is distributed on an "AS IS" BASIS, | 
|  | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | 
|  | // See the License for the empecific language governing permissions and | 
|  | // limitations under the License. | 
|  |  | 
|  | #include "src/tint/resolver/const_eval_test.h" | 
|  |  | 
|  | #include "src/tint/utils/result.h" | 
|  |  | 
|  | using namespace tint::number_suffixes;  // NOLINT | 
|  | using ::testing::HasSubstr; | 
|  |  | 
|  | namespace tint::resolver { | 
|  | namespace { | 
|  |  | 
|  | struct Case { | 
|  | Case(utils::VectorRef<Value> in_args, utils::VectorRef<Value> expected_values) | 
|  | : args(std::move(in_args)), | 
|  | expected(Success{std::move(expected_values), CheckConstantFlags{}}) {} | 
|  |  | 
|  | Case(utils::VectorRef<Value> in_args, std::string expected_err) | 
|  | : args(std::move(in_args)), expected(Failure{std::move(expected_err)}) {} | 
|  |  | 
|  | /// Expected value may be positive or negative | 
|  | Case& PosOrNeg() { | 
|  | Success s = expected.Get(); | 
|  | s.flags.pos_or_neg = true; | 
|  | expected = s; | 
|  | return *this; | 
|  | } | 
|  |  | 
|  | /// Expected value should be compared using EXPECT_FLOAT_EQ instead of EXPECT_EQ. | 
|  | /// If optional epsilon is passed in, will be compared using EXPECT_NEAR with that epsilon. | 
|  | Case& FloatComp(std::optional<double> epsilon = {}) { | 
|  | Success s = expected.Get(); | 
|  | s.flags.float_compare = true; | 
|  | s.flags.float_compare_epsilon = epsilon; | 
|  | expected = s; | 
|  | return *this; | 
|  | } | 
|  |  | 
|  | struct Success { | 
|  | utils::Vector<Value, 2> values; | 
|  | CheckConstantFlags flags; | 
|  | }; | 
|  | struct Failure { | 
|  | std::string error; | 
|  | }; | 
|  |  | 
|  | utils::Vector<Value, 8> args; | 
|  | utils::Result<Success, Failure> expected; | 
|  | }; | 
|  |  | 
|  | static std::ostream& operator<<(std::ostream& o, const Case& c) { | 
|  | o << "args: "; | 
|  | for (auto& a : c.args) { | 
|  | o << a << ", "; | 
|  | } | 
|  | o << "expected: "; | 
|  | if (c.expected) { | 
|  | auto s = c.expected.Get(); | 
|  | if (s.values.Length() == 1) { | 
|  | o << s.values[0]; | 
|  | } else { | 
|  | o << "["; | 
|  | for (auto& v : s.values) { | 
|  | if (&v != &s.values[0]) { | 
|  | o << ", "; | 
|  | } | 
|  | o << v; | 
|  | } | 
|  | o << "]"; | 
|  | } | 
|  | o << ", pos_or_neg: " << s.flags.pos_or_neg; | 
|  | o << ", float_compare: " << s.flags.float_compare; | 
|  | } else { | 
|  | o << "[ERROR: " << c.expected.Failure().error << "]"; | 
|  | } | 
|  | return o; | 
|  | } | 
|  |  | 
|  | using ScalarTypes = std::variant<AInt, AFloat, u32, i32, f32, f16>; | 
|  |  | 
|  | /// Creates a Case with Values for args and result | 
|  | static Case C(std::initializer_list<Value> args, Value result) { | 
|  | return Case{utils::Vector<Value, 8>{args}, utils::Vector<Value, 2>{std::move(result)}}; | 
|  | } | 
|  |  | 
|  | /// Creates a Case with Values for args and result | 
|  | static Case C(std::initializer_list<Value> args, std::initializer_list<Value> results) { | 
|  | return Case{utils::Vector<Value, 8>{args}, utils::Vector<Value, 2>{results}}; | 
|  | } | 
|  |  | 
|  | /// Convenience overload that creates a Case with just scalars | 
|  | static Case C(std::initializer_list<ScalarTypes> sargs, ScalarTypes sresult) { | 
|  | utils::Vector<Value, 8> args; | 
|  | for (auto& sa : sargs) { | 
|  | std::visit([&](auto&& v) { return args.Push(Val(v)); }, sa); | 
|  | } | 
|  | Value result = Val(0_a); | 
|  | std::visit([&](auto&& v) { result = Val(v); }, sresult); | 
|  | return Case{std::move(args), utils::Vector<Value, 2>{std::move(result)}}; | 
|  | } | 
|  |  | 
|  | /// Creates a Case with Values for args and result | 
|  | static Case C(std::initializer_list<ScalarTypes> sargs, | 
|  | std::initializer_list<ScalarTypes> sresults) { | 
|  | utils::Vector<Value, 8> args; | 
|  | for (auto& sa : sargs) { | 
|  | std::visit([&](auto&& v) { return args.Push(Val(v)); }, sa); | 
|  | } | 
|  | utils::Vector<Value, 2> results; | 
|  | for (auto& sa : sresults) { | 
|  | std::visit([&](auto&& v) { return results.Push(Val(v)); }, sa); | 
|  | } | 
|  | return Case{std::move(args), std::move(results)}; | 
|  | } | 
|  |  | 
|  | /// Creates a Case with Values for args and expected error | 
|  | static Case E(std::initializer_list<Value> args, std::string err) { | 
|  | return Case{utils::Vector<Value, 8>{args}, std::move(err)}; | 
|  | } | 
|  |  | 
|  | /// Convenience overload that creates an expected-error Case with just scalars | 
|  | static Case E(std::initializer_list<ScalarTypes> sargs, std::string err) { | 
|  | utils::Vector<Value, 8> args; | 
|  | for (auto& sa : sargs) { | 
|  | std::visit([&](auto&& v) { return args.Push(Val(v)); }, sa); | 
|  | } | 
|  | return Case{std::move(args), std::move(err)}; | 
|  | } | 
|  |  | 
|  | using ResolverConstEvalBuiltinTest = ResolverTestWithParam<std::tuple<builtin::Function, Case>>; | 
|  |  | 
|  | TEST_P(ResolverConstEvalBuiltinTest, Test) { | 
|  | Enable(builtin::Extension::kF16); | 
|  |  | 
|  | auto builtin = std::get<0>(GetParam()); | 
|  | auto& c = std::get<1>(GetParam()); | 
|  |  | 
|  | utils::Vector<const ast::Expression*, 8> args; | 
|  | for (auto& a : c.args) { | 
|  | args.Push(a.Expr(*this)); | 
|  | } | 
|  |  | 
|  | auto* expr = Call(Source{{12, 34}}, builtin::str(builtin), std::move(args)); | 
|  | GlobalConst("C", expr); | 
|  |  | 
|  | if (c.expected) { | 
|  | auto expected_case = c.expected.Get(); | 
|  |  | 
|  | ASSERT_TRUE(r()->Resolve()) << r()->error(); | 
|  |  | 
|  | auto* sem = Sem().Get(expr); | 
|  | ASSERT_NE(sem, nullptr); | 
|  | const constant::Value* value = sem->ConstantValue(); | 
|  | ASSERT_NE(value, nullptr); | 
|  | EXPECT_TYPE(value->Type(), sem->Type()); | 
|  |  | 
|  | if (value->Type()->Is<type::Struct>()) { | 
|  | // The result type of the constant-evaluated expression is a structure. | 
|  | // Compare each of the fields individually. | 
|  | for (size_t i = 0; i < expected_case.values.Length(); i++) { | 
|  | CheckConstant(value->Index(i), expected_case.values[i], expected_case.flags); | 
|  | } | 
|  | } else { | 
|  | // Return type is not a structure. Just compare the single value | 
|  | ASSERT_EQ(expected_case.values.Length(), 1u) | 
|  | << "const-eval returned non-struct, but Case expected multiple values"; | 
|  | CheckConstant(value, expected_case.values[0], expected_case.flags); | 
|  | } | 
|  | } else { | 
|  | ASSERT_FALSE(r()->Resolve()); | 
|  | EXPECT_EQ(r()->error(), c.expected.Failure().error); | 
|  | } | 
|  | } | 
|  |  | 
|  | INSTANTIATE_TEST_SUITE_P(  // | 
|  | MixedAbstractArgs, | 
|  | ResolverConstEvalBuiltinTest, | 
|  | testing::Combine(testing::Values(builtin::Function::kAtan2), | 
|  | testing::ValuesIn(std::vector{ | 
|  | C({0_a, -0.0_a}, kPi<AFloat>), | 
|  | C({1.0_a, 0_a}, kPiOver2<AFloat>), | 
|  | }))); | 
|  |  | 
|  | template <typename T> | 
|  | std::vector<Case> AbsCases() { | 
|  | std::vector<Case> cases = { | 
|  | C({T(0)}, T(0)), | 
|  | C({T(2.0)}, T(2.0)), | 
|  | C({T::Highest()}, T::Highest()), | 
|  |  | 
|  | // Vector tests | 
|  | C({Vec(T(2.0), T::Highest())}, Vec(T(2.0), T::Highest())), | 
|  | }; | 
|  | ConcatIntoIf<IsSignedIntegral<T>>( | 
|  | cases, | 
|  | std::vector<Case>{ | 
|  | C({Negate(T(0))}, T(0)), | 
|  | C({Negate(T(2.0))}, T(2.0)), | 
|  | // If e is signed and is the largest negative, the result is e | 
|  | C({T::Lowest()}, T::Lowest()), | 
|  |  | 
|  | // 1 more then min i32 | 
|  | C({Negate(T(2147483647))}, T(2147483647)), | 
|  |  | 
|  | C({Vec(T(0), Negate(T(0)))}, Vec(T(0), T(0))), | 
|  | C({Vec(Negate(T(2.0)), T(2.0), T::Highest())}, Vec(T(2.0), T(2.0), T::Highest())), | 
|  | }); | 
|  | return cases; | 
|  | } | 
|  | INSTANTIATE_TEST_SUITE_P(  // | 
|  | Abs, | 
|  | ResolverConstEvalBuiltinTest, | 
|  | testing::Combine(testing::Values(builtin::Function::kAbs), | 
|  | testing::ValuesIn(Concat(AbsCases<AInt>(),  // | 
|  | AbsCases<i32>(), | 
|  | AbsCases<u32>(), | 
|  | AbsCases<AFloat>(), | 
|  | AbsCases<f32>(), | 
|  | AbsCases<f16>())))); | 
|  |  | 
|  | static std::vector<Case> AllCases() { | 
|  | return { | 
|  | C({Val(true)}, Val(true)), | 
|  | C({Val(false)}, Val(false)), | 
|  |  | 
|  | C({Vec(true, true)}, Val(true)), | 
|  | C({Vec(true, false)}, Val(false)), | 
|  | C({Vec(false, true)}, Val(false)), | 
|  | C({Vec(false, false)}, Val(false)), | 
|  |  | 
|  | C({Vec(true, true, true)}, Val(true)), | 
|  | C({Vec(false, true, true)}, Val(false)), | 
|  | C({Vec(true, false, true)}, Val(false)), | 
|  | C({Vec(true, true, false)}, Val(false)), | 
|  | C({Vec(false, false, false)}, Val(false)), | 
|  |  | 
|  | C({Vec(true, true, true, true)}, Val(true)), | 
|  | C({Vec(false, true, true, true)}, Val(false)), | 
|  | C({Vec(true, false, true, true)}, Val(false)), | 
|  | C({Vec(true, true, false, true)}, Val(false)), | 
|  | C({Vec(true, true, true, false)}, Val(false)), | 
|  | C({Vec(false, false, false, false)}, Val(false)), | 
|  | }; | 
|  | } | 
|  | INSTANTIATE_TEST_SUITE_P(  // | 
|  | All, | 
|  | ResolverConstEvalBuiltinTest, | 
|  | testing::Combine(testing::Values(builtin::Function::kAll), testing::ValuesIn(AllCases()))); | 
|  |  | 
|  | static std::vector<Case> AnyCases() { | 
|  | return { | 
|  | C({Val(true)}, Val(true)), | 
|  | C({Val(false)}, Val(false)), | 
|  |  | 
|  | C({Vec(true, true)}, Val(true)), | 
|  | C({Vec(true, false)}, Val(true)), | 
|  | C({Vec(false, true)}, Val(true)), | 
|  | C({Vec(false, false)}, Val(false)), | 
|  |  | 
|  | C({Vec(true, true, true)}, Val(true)), | 
|  | C({Vec(false, true, true)}, Val(true)), | 
|  | C({Vec(true, false, true)}, Val(true)), | 
|  | C({Vec(true, true, false)}, Val(true)), | 
|  | C({Vec(false, false, false)}, Val(false)), | 
|  |  | 
|  | C({Vec(true, true, true, true)}, Val(true)), | 
|  | C({Vec(false, true, true, true)}, Val(true)), | 
|  | C({Vec(true, false, true, true)}, Val(true)), | 
|  | C({Vec(true, true, false, true)}, Val(true)), | 
|  | C({Vec(true, true, true, false)}, Val(true)), | 
|  | C({Vec(false, false, false, false)}, Val(false)), | 
|  | }; | 
|  | } | 
|  | INSTANTIATE_TEST_SUITE_P(  // | 
|  | Any, | 
|  | ResolverConstEvalBuiltinTest, | 
|  | testing::Combine(testing::Values(builtin::Function::kAny), testing::ValuesIn(AnyCases()))); | 
|  |  | 
|  | template <typename T> | 
|  | std::vector<Case> Atan2Cases() { | 
|  | return { | 
|  | // If y is +/-0 and x is negative or -0, +/-PI is returned | 
|  | C({T(0.0), -T(0.0)}, kPi<T>).PosOrNeg().FloatComp(), | 
|  |  | 
|  | // If y is +/-0 and x is positive or +0, +/-0 is returned | 
|  | C({T(0.0), T(0.0)}, T(0.0)).PosOrNeg(), | 
|  |  | 
|  | // If x is +/-0 and y is negative, -PI/2 is returned | 
|  | C({-T(1.0), T(0.0)}, -kPiOver2<T>).FloatComp(),  // | 
|  | C({-T(1.0), -T(0.0)}, -kPiOver2<T>).FloatComp(), | 
|  |  | 
|  | // If x is +/-0 and y is positive, +PI/2 is returned | 
|  | C({T(1.0), T(0.0)}, kPiOver2<T>).FloatComp(),  // | 
|  | C({T(1.0), -T(0.0)}, kPiOver2<T>).FloatComp(), | 
|  |  | 
|  | // Vector tests | 
|  | C({Vec(T(0.0), T(0.0)), Vec(-T(0.0), T(0.0))}, Vec(kPi<T>, T(0.0))).PosOrNeg().FloatComp(), | 
|  | C({Vec(-T(1.0), -T(1.0)), Vec(T(0.0), -T(0.0))}, Vec(-kPiOver2<T>, -kPiOver2<T>)) | 
|  | .FloatComp(), | 
|  | C({Vec(T(1.0), T(1.0)), Vec(T(0.0), -T(0.0))}, Vec(kPiOver2<T>, kPiOver2<T>)).FloatComp(), | 
|  | }; | 
|  | } | 
|  | INSTANTIATE_TEST_SUITE_P(  // | 
|  | Atan2, | 
|  | ResolverConstEvalBuiltinTest, | 
|  | testing::Combine(testing::Values(builtin::Function::kAtan2), | 
|  | testing::ValuesIn(Concat(Atan2Cases<AFloat>(),  // | 
|  | Atan2Cases<f32>(), | 
|  | Atan2Cases<f16>())))); | 
|  |  | 
|  | template <typename T> | 
|  | std::vector<Case> AtanCases() { | 
|  | return { | 
|  | C({T(1.0)}, kPiOver4<T>).FloatComp(), | 
|  | C({-T(1.0)}, -kPiOver4<T>).FloatComp(), | 
|  |  | 
|  | // If i is +/-0, +/-0 is returned | 
|  | C({T(0.0)}, T(0.0)).PosOrNeg(), | 
|  |  | 
|  | // Vector tests | 
|  | C({Vec(T(0.0), T(1.0), -T(1.0))}, Vec(T(0.0), kPiOver4<T>, -kPiOver4<T>)).FloatComp(), | 
|  | }; | 
|  | } | 
|  | INSTANTIATE_TEST_SUITE_P(  // | 
|  | Atan, | 
|  | ResolverConstEvalBuiltinTest, | 
|  | testing::Combine(testing::Values(builtin::Function::kAtan), | 
|  | testing::ValuesIn(Concat(AtanCases<AFloat>(),  // | 
|  | AtanCases<f32>(), | 
|  | AtanCases<f16>())))); | 
|  |  | 
|  | template <typename T> | 
|  | std::vector<Case> AtanhCases() { | 
|  | return { | 
|  | // If i is +/-0, +/-0 is returned | 
|  | C({T(0.0)}, T(0.0)).PosOrNeg(), | 
|  |  | 
|  | C({T(0.9)}, T(1.4722193)).FloatComp(), | 
|  |  | 
|  | // Vector tests | 
|  | C({Vec(T(0.0), T(0.9), -T(0.9))}, Vec(T(0.0), T(1.4722193), -T(1.4722193))).FloatComp(), | 
|  |  | 
|  | E({T(1.1)}, | 
|  | "12:34 error: atanh must be called with a value in the range (-1 .. 1) (exclusive)"), | 
|  | E({-T(1.1)}, | 
|  | "12:34 error: atanh must be called with a value in the range (-1 .. 1) (exclusive)"), | 
|  | }; | 
|  | } | 
|  | INSTANTIATE_TEST_SUITE_P(  // | 
|  | Atanh, | 
|  | ResolverConstEvalBuiltinTest, | 
|  | testing::Combine(testing::Values(builtin::Function::kAtanh), | 
|  | testing::ValuesIn(Concat(AtanhCases<AFloat>(),  // | 
|  | AtanhCases<f32>(), | 
|  | AtanhCases<f16>())))); | 
|  |  | 
|  | template <typename T> | 
|  | std::vector<Case> AcosCases() { | 
|  | return { | 
|  | // If i is +/-0, +/-0 is returned | 
|  | C({T(0.87758256189)}, T(0.5)).FloatComp(), | 
|  |  | 
|  | C({T(1.0)}, T(0.0)), | 
|  | C({-T(1.0)}, kPi<T>).FloatComp(), | 
|  |  | 
|  | // Vector tests | 
|  | C({Vec(T(1.0), -T(1.0))}, Vec(T(0), kPi<T>)).FloatComp(), | 
|  |  | 
|  | E({T(1.1)}, | 
|  | "12:34 error: acos must be called with a value in the range [-1 .. 1] (inclusive)"), | 
|  | E({-T(1.1)}, | 
|  | "12:34 error: acos must be called with a value in the range [-1 .. 1] (inclusive)"), | 
|  | }; | 
|  | } | 
|  | INSTANTIATE_TEST_SUITE_P(  // | 
|  | Acos, | 
|  | ResolverConstEvalBuiltinTest, | 
|  | testing::Combine(testing::Values(builtin::Function::kAcos), | 
|  | testing::ValuesIn(Concat(AcosCases<AFloat>(),  // | 
|  | AcosCases<f32>(), | 
|  | AcosCases<f16>())))); | 
|  |  | 
|  | template <typename T> | 
|  | std::vector<Case> AcoshCases() { | 
|  | return { | 
|  | C({T(1.0)}, T(0.0)), | 
|  | C({T(11.5919532755)}, kPi<T>).FloatComp(), | 
|  |  | 
|  | // Vector tests | 
|  | C({Vec(T(1.0), T(11.5919532755))}, Vec(T(0), kPi<T>)).FloatComp(), | 
|  |  | 
|  | E({T::Smallest()}, "12:34 error: acosh must be called with a value >= 1.0"), | 
|  | E({-T(1.1)}, "12:34 error: acosh must be called with a value >= 1.0"), | 
|  | E({T(0)}, "12:34 error: acosh must be called with a value >= 1.0"), | 
|  | }; | 
|  | } | 
|  | INSTANTIATE_TEST_SUITE_P(  // | 
|  | Acosh, | 
|  | ResolverConstEvalBuiltinTest, | 
|  | testing::Combine(testing::Values(builtin::Function::kAcosh), | 
|  | testing::ValuesIn(Concat(AcoshCases<AFloat>(),  // | 
|  | AcoshCases<f32>(), | 
|  | AcoshCases<f16>())))); | 
|  |  | 
|  | template <typename T> | 
|  | std::vector<Case> AsinCases() { | 
|  | return { | 
|  | // If i is +/-0, +/-0 is returned | 
|  | C({T(0.0)}, T(0.0)), | 
|  | C({-T(0.0)}, -T(0.0)), | 
|  |  | 
|  | C({T(1.0)}, kPiOver2<T>).FloatComp(), | 
|  | C({-T(1.0)}, -kPiOver2<T>).FloatComp(), | 
|  |  | 
|  | // Vector tests | 
|  | C({Vec(T(0.0), T(1.0), -T(1.0))}, Vec(T(0.0), kPiOver2<T>, -kPiOver2<T>)).FloatComp(), | 
|  |  | 
|  | E({T(1.1)}, | 
|  | "12:34 error: asin must be called with a value in the range [-1 .. 1] (inclusive)"), | 
|  | E({-T(1.1)}, | 
|  | "12:34 error: asin must be called with a value in the range [-1 .. 1] (inclusive)"), | 
|  | }; | 
|  | } | 
|  | INSTANTIATE_TEST_SUITE_P(  // | 
|  | Asin, | 
|  | ResolverConstEvalBuiltinTest, | 
|  | testing::Combine(testing::Values(builtin::Function::kAsin), | 
|  | testing::ValuesIn(Concat(AsinCases<AFloat>(),  // | 
|  | AsinCases<f32>(), | 
|  | AsinCases<f16>())))); | 
|  |  | 
|  | template <typename T> | 
|  | std::vector<Case> AsinhCases() { | 
|  | return { | 
|  | // If i is +/-0, +/-0 is returned | 
|  | C({T(0.0)}, T(0.0)), | 
|  | C({-T(0.0)}, -T(0.0)), | 
|  |  | 
|  | C({T(0.9)}, T(0.80886693565278)).FloatComp(), | 
|  | C({-T(2.0)}, -T(1.4436354751788)).FloatComp(), | 
|  |  | 
|  | // Vector tests | 
|  | C({Vec(T(0.0), T(0.9), -T(2.0))},  // | 
|  | Vec(T(0.0), T(0.8088669356278), -T(1.4436354751788))) | 
|  | .FloatComp(), | 
|  | }; | 
|  | } | 
|  | INSTANTIATE_TEST_SUITE_P(  // | 
|  | Asinh, | 
|  | ResolverConstEvalBuiltinTest, | 
|  | testing::Combine(testing::Values(builtin::Function::kAsinh), | 
|  | testing::ValuesIn(Concat(AsinhCases<AFloat>(),  // | 
|  | AsinhCases<f32>(), | 
|  | AsinhCases<f16>())))); | 
|  |  | 
|  | template <typename T> | 
|  | std::vector<Case> CeilCases() { | 
|  | return { | 
|  | C({T(0)}, T(0)), | 
|  | C({-T(0)}, -T(0)), | 
|  | C({-T(1.5)}, -T(1.0)), | 
|  | C({T(1.5)}, T(2.0)), | 
|  | C({T::Lowest()}, T::Lowest()), | 
|  | C({T::Highest()}, T::Highest()), | 
|  |  | 
|  | C({Vec(T(0), T(1.5), -T(1.5))}, Vec(T(0), T(2.0), -T(1.0))), | 
|  | }; | 
|  | } | 
|  | INSTANTIATE_TEST_SUITE_P(  // | 
|  | Ceil, | 
|  | ResolverConstEvalBuiltinTest, | 
|  | testing::Combine( | 
|  | testing::Values(builtin::Function::kCeil), | 
|  | testing::ValuesIn(Concat(CeilCases<AFloat>(), CeilCases<f32>(), CeilCases<f16>())))); | 
|  |  | 
|  | template <typename T> | 
|  | std::vector<Case> ClampCases() { | 
|  | return { | 
|  | C({T(0), T(0), T(0)}, T(0)), | 
|  | C({T(0), T(42), T::Highest()}, T(42)), | 
|  | C({T::Lowest(), T(0), T(42)}, T(0)), | 
|  | C({T(0), T::Lowest(), T::Highest()}, T(0)), | 
|  | C({T(0), T::Highest(), T::Lowest()}, T::Lowest()), | 
|  | C({T::Highest(), T::Highest(), T::Highest()}, T::Highest()), | 
|  | C({T::Lowest(), T::Lowest(), T::Lowest()}, T::Lowest()), | 
|  | C({T::Highest(), T::Lowest(), T::Highest()}, T::Highest()), | 
|  | C({T::Lowest(), T::Lowest(), T::Highest()}, T::Lowest()), | 
|  |  | 
|  | // Vector tests | 
|  | C({Vec(T(0), T(0)),                         // | 
|  | Vec(T(0), T(42)),                        // | 
|  | Vec(T(0), T::Highest())},                // | 
|  | Vec(T(0), T(42))),                        // | 
|  | C({Vec(T::Lowest(), T(0), T(0)),            // | 
|  | Vec(T(0), T::Lowest(), T::Highest()),    // | 
|  | Vec(T(42), T::Highest(), T::Lowest())},  // | 
|  | Vec(T(0), T(0), T::Lowest())), | 
|  | }; | 
|  | } | 
|  | INSTANTIATE_TEST_SUITE_P(  // | 
|  | Clamp, | 
|  | ResolverConstEvalBuiltinTest, | 
|  | testing::Combine(testing::Values(builtin::Function::kClamp), | 
|  | testing::ValuesIn(Concat(ClampCases<AInt>(),  // | 
|  | ClampCases<i32>(), | 
|  | ClampCases<u32>(), | 
|  | ClampCases<AFloat>(), | 
|  | ClampCases<f32>(), | 
|  | ClampCases<f16>())))); | 
|  |  | 
|  | template <typename T> | 
|  | std::vector<Case> CosCases() { | 
|  | return { | 
|  | C({-T(0)}, T(1)), | 
|  | C({T(0)}, T(1)), | 
|  |  | 
|  | C({T(0.75)}, T(0.7316888689)).FloatComp(), | 
|  |  | 
|  | // Vector test | 
|  | C({Vec(T(0), -T(0), T(0.75))}, Vec(T(1), T(1), T(0.7316888689))).FloatComp(), | 
|  | }; | 
|  | } | 
|  | INSTANTIATE_TEST_SUITE_P(  // | 
|  | Cos, | 
|  | ResolverConstEvalBuiltinTest, | 
|  | testing::Combine(testing::Values(builtin::Function::kCos), | 
|  | testing::ValuesIn(Concat(CosCases<AFloat>(),  // | 
|  | CosCases<f32>(), | 
|  | CosCases<f16>())))); | 
|  |  | 
|  | template <typename T> | 
|  | std::vector<Case> CoshCases() { | 
|  | auto error_msg = [](auto a) { | 
|  | return "12:34 error: " + OverflowErrorMessage(a, FriendlyName<decltype(a)>()); | 
|  | }; | 
|  | return { | 
|  | C({T(0)}, T(1)), | 
|  | C({-T(0)}, T(1)), | 
|  | C({T(1)}, T(1.5430806348)).FloatComp(), | 
|  |  | 
|  | C({T(.75)}, T(1.2946832847)).FloatComp(), | 
|  |  | 
|  | // Vector tests | 
|  | C({Vec(T(0), -T(0), T(1))}, Vec(T(1), T(1), T(1.5430806348))).FloatComp(), | 
|  |  | 
|  | E({T(10000)}, error_msg(T::Inf())), | 
|  | }; | 
|  | } | 
|  | INSTANTIATE_TEST_SUITE_P(  // | 
|  | Cosh, | 
|  | ResolverConstEvalBuiltinTest, | 
|  | testing::Combine(testing::Values(builtin::Function::kCosh), | 
|  | testing::ValuesIn(Concat(CoshCases<AFloat>(),  // | 
|  | CoshCases<f32>(), | 
|  | CoshCases<f16>())))); | 
|  |  | 
|  | template <typename T> | 
|  | std::vector<Case> CountLeadingZerosCases() { | 
|  | using B = BitValues<T>; | 
|  | return { | 
|  | C({B::Lsh(1, 31)}, T(0)),  // | 
|  | C({B::Lsh(1, 30)}, T(1)),  // | 
|  | C({B::Lsh(1, 29)}, T(2)),  // | 
|  | C({B::Lsh(1, 28)}, T(3)), | 
|  | //... | 
|  | C({B::Lsh(1, 3)}, T(28)),  // | 
|  | C({B::Lsh(1, 2)}, T(29)),  // | 
|  | C({B::Lsh(1, 1)}, T(30)),  // | 
|  | C({B::Lsh(1, 0)}, T(31)), | 
|  |  | 
|  | C({T(0b1111'0000'1111'0000'1111'0000'1111'0000)}, T(0)), | 
|  | C({T(0b0111'1000'0111'1000'0111'1000'0111'1000)}, T(1)), | 
|  | C({T(0b0011'1100'0011'1100'0011'1100'0011'1100)}, T(2)), | 
|  | C({T(0b0001'1110'0001'1110'0001'1110'0001'1110)}, T(3)), | 
|  | //... | 
|  | C({T(0b0000'0000'0000'0000'0000'0000'0000'0111)}, T(29)), | 
|  | C({T(0b0000'0000'0000'0000'0000'0000'0000'0011)}, T(30)), | 
|  | C({T(0b0000'0000'0000'0000'0000'0000'0000'0001)}, T(31)), | 
|  | C({T(0b0000'0000'0000'0000'0000'0000'0000'0000)}, T(32)), | 
|  |  | 
|  | // Same as above, but remove leading 0 | 
|  | C({T(0b1111'1000'0111'1000'0111'1000'0111'1000)}, T(0)), | 
|  | C({T(0b1011'1100'0011'1100'0011'1100'0011'1100)}, T(0)), | 
|  | C({T(0b1001'1110'0001'1110'0001'1110'0001'1110)}, T(0)), | 
|  | //... | 
|  | C({T(0b1000'0000'0000'0000'0000'0000'0000'0111)}, T(0)), | 
|  | C({T(0b1000'0000'0000'0000'0000'0000'0000'0011)}, T(0)), | 
|  | C({T(0b1000'0000'0000'0000'0000'0000'0000'0001)}, T(0)), | 
|  | C({T(0b1000'0000'0000'0000'0000'0000'0000'0000)}, T(0)), | 
|  |  | 
|  | // Vector tests | 
|  | C({Vec(B::Lsh(1, 31), B::Lsh(1, 30), B::Lsh(1, 29))}, Vec(T(0), T(1), T(2))), | 
|  | C({Vec(B::Lsh(1, 2), B::Lsh(1, 1), B::Lsh(1, 0))}, Vec(T(29), T(30), T(31))), | 
|  | }; | 
|  | } | 
|  | INSTANTIATE_TEST_SUITE_P(  // | 
|  | CountLeadingZeros, | 
|  | ResolverConstEvalBuiltinTest, | 
|  | testing::Combine(testing::Values(builtin::Function::kCountLeadingZeros), | 
|  | testing::ValuesIn(Concat(CountLeadingZerosCases<i32>(),  // | 
|  | CountLeadingZerosCases<u32>())))); | 
|  |  | 
|  | template <typename T> | 
|  | std::vector<Case> CountTrailingZerosCases() { | 
|  | using B = BitValues<T>; | 
|  | return { | 
|  | C({B::Lsh(1, 31)}, T(31)),  // | 
|  | C({B::Lsh(1, 30)}, T(30)),  // | 
|  | C({B::Lsh(1, 29)}, T(29)),  // | 
|  | C({B::Lsh(1, 28)}, T(28)), | 
|  | //... | 
|  | C({B::Lsh(1, 3)}, T(3)),  // | 
|  | C({B::Lsh(1, 2)}, T(2)),  // | 
|  | C({B::Lsh(1, 1)}, T(1)),  // | 
|  | C({B::Lsh(1, 0)}, T(0)), | 
|  |  | 
|  | C({T(0b0000'1111'0000'1111'0000'1111'0000'1111)}, T(0)), | 
|  | C({T(0b0001'1110'0001'1110'0001'1110'0001'1110)}, T(1)), | 
|  | C({T(0b0011'1100'0011'1100'0011'1100'0011'1100)}, T(2)), | 
|  | C({T(0b0111'1000'0111'1000'0111'1000'0111'1000)}, T(3)), | 
|  | //... | 
|  | C({T(0b1110'0000'0000'0000'0000'0000'0000'0000)}, T(29)), | 
|  | C({T(0b1100'0000'0000'0000'0000'0000'0000'0000)}, T(30)), | 
|  | C({T(0b1000'0000'0000'0000'0000'0000'0000'0000)}, T(31)), | 
|  | C({T(0b0000'0000'0000'0000'0000'0000'0000'0000)}, T(32)), | 
|  |  | 
|  | //// Same as above, but remove trailing 0 | 
|  | C({T(0b0001'1110'0001'1110'0001'1110'0001'1111)}, T(0)), | 
|  | C({T(0b0011'1100'0011'1100'0011'1100'0011'1101)}, T(0)), | 
|  | C({T(0b0111'1000'0111'1000'0111'1000'0111'1001)}, T(0)), | 
|  | //... | 
|  | C({T(0b1110'0000'0000'0000'0000'0000'0000'0001)}, T(0)), | 
|  | C({T(0b1100'0000'0000'0000'0000'0000'0000'0001)}, T(0)), | 
|  | C({T(0b1000'0000'0000'0000'0000'0000'0000'0001)}, T(0)), | 
|  | C({T(0b0000'0000'0000'0000'0000'0000'0000'0001)}, T(0)), | 
|  |  | 
|  | // Vector tests | 
|  | C({Vec(B::Lsh(1, 31), B::Lsh(1, 30), B::Lsh(1, 29))}, Vec(T(31), T(30), T(29))), | 
|  | C({Vec(B::Lsh(1, 2), B::Lsh(1, 1), B::Lsh(1, 0))}, Vec(T(2), T(1), T(0))), | 
|  | }; | 
|  | } | 
|  | INSTANTIATE_TEST_SUITE_P(  // | 
|  | CountTrailingZeros, | 
|  | ResolverConstEvalBuiltinTest, | 
|  | testing::Combine(testing::Values(builtin::Function::kCountTrailingZeros), | 
|  | testing::ValuesIn(Concat(CountTrailingZerosCases<i32>(),  // | 
|  | CountTrailingZerosCases<u32>())))); | 
|  |  | 
|  | template <typename T> | 
|  | std::vector<Case> CountOneBitsCases() { | 
|  | using B = BitValues<T>; | 
|  | return { | 
|  | C({T(0)}, T(0)),  // | 
|  |  | 
|  | C({B::Lsh(1, 31)}, T(1)),  // | 
|  | C({B::Lsh(1, 30)}, T(1)),  // | 
|  | C({B::Lsh(1, 29)}, T(1)),  // | 
|  | C({B::Lsh(1, 28)}, T(1)), | 
|  | //... | 
|  | C({B::Lsh(1, 3)}, T(1)),  // | 
|  | C({B::Lsh(1, 2)}, T(1)),  // | 
|  | C({B::Lsh(1, 1)}, T(1)),  // | 
|  | C({B::Lsh(1, 0)}, T(1)), | 
|  |  | 
|  | C({T(0b1010'1010'1010'1010'1010'1010'1010'1010)}, T(16)), | 
|  | C({T(0b0000'1111'0000'1111'0000'1111'0000'1111)}, T(16)), | 
|  | C({T(0b0101'0000'0000'0000'0000'0000'0000'0101)}, T(4)), | 
|  |  | 
|  | // Vector tests | 
|  | C({Vec(B::Lsh(1, 31), B::Lsh(1, 30), B::Lsh(1, 29))}, Vec(T(1), T(1), T(1))), | 
|  | C({Vec(B::Lsh(1, 2), B::Lsh(1, 1), B::Lsh(1, 0))}, Vec(T(1), T(1), T(1))), | 
|  |  | 
|  | C({Vec(T(0b1010'1010'1010'1010'1010'1010'1010'1010), | 
|  | T(0b0000'1111'0000'1111'0000'1111'0000'1111), | 
|  | T(0b0101'0000'0000'0000'0000'0000'0000'0101))}, | 
|  | Vec(T(16), T(16), T(4))), | 
|  | }; | 
|  | } | 
|  | INSTANTIATE_TEST_SUITE_P(  // | 
|  | CountOneBits, | 
|  | ResolverConstEvalBuiltinTest, | 
|  | testing::Combine(testing::Values(builtin::Function::kCountOneBits), | 
|  | testing::ValuesIn(Concat(CountOneBitsCases<i32>(),  // | 
|  | CountOneBitsCases<u32>())))); | 
|  |  | 
|  | template <typename T> | 
|  | std::vector<Case> CrossCases() { | 
|  | constexpr auto vec_x = [](T v) { return Vec(T(v), T(0), T(0)); }; | 
|  | constexpr auto vec_y = [](T v) { return Vec(T(0), T(v), T(0)); }; | 
|  | constexpr auto vec_z = [](T v) { return Vec(T(0), T(0), T(v)); }; | 
|  |  | 
|  | const auto zero = Vec(T(0), T(0), T(0)); | 
|  | const auto unit_x = vec_x(T(1)); | 
|  | const auto unit_y = vec_y(T(1)); | 
|  | const auto unit_z = vec_z(T(1)); | 
|  | const auto neg_unit_x = vec_x(-T(1)); | 
|  | const auto neg_unit_y = vec_y(-T(1)); | 
|  | const auto neg_unit_z = vec_z(-T(1)); | 
|  | const auto highest_x = vec_x(T::Highest()); | 
|  | const auto highest_y = vec_y(T::Highest()); | 
|  | const auto highest_z = vec_z(T::Highest()); | 
|  | const auto smallest_x = vec_x(T::Smallest()); | 
|  | const auto smallest_y = vec_y(T::Smallest()); | 
|  | const auto smallest_z = vec_z(T::Smallest()); | 
|  | const auto lowest_x = vec_x(T::Lowest()); | 
|  | const auto lowest_y = vec_y(T::Lowest()); | 
|  | const auto lowest_z = vec_z(T::Lowest()); | 
|  |  | 
|  | std::vector<Case> r = { | 
|  | C({zero, zero}, zero), | 
|  |  | 
|  | C({unit_x, unit_x}, zero), | 
|  | C({unit_y, unit_y}, zero), | 
|  | C({unit_z, unit_z}, zero), | 
|  |  | 
|  | C({smallest_x, smallest_x}, zero), | 
|  | C({smallest_y, smallest_y}, zero), | 
|  | C({smallest_z, smallest_z}, zero), | 
|  |  | 
|  | C({lowest_x, lowest_x}, zero), | 
|  | C({lowest_y, lowest_y}, zero), | 
|  | C({lowest_z, lowest_z}, zero), | 
|  |  | 
|  | C({highest_x, highest_x}, zero), | 
|  | C({highest_y, highest_y}, zero), | 
|  | C({highest_z, highest_z}, zero), | 
|  |  | 
|  | C({smallest_x, highest_x}, zero), | 
|  | C({smallest_y, highest_y}, zero), | 
|  | C({smallest_z, highest_z}, zero), | 
|  |  | 
|  | C({unit_x, neg_unit_x}, zero).PosOrNeg(), | 
|  | C({unit_y, neg_unit_y}, zero).PosOrNeg(), | 
|  | C({unit_z, neg_unit_z}, zero).PosOrNeg(), | 
|  |  | 
|  | C({unit_x, unit_y}, unit_z), | 
|  | C({unit_y, unit_x}, neg_unit_z), | 
|  |  | 
|  | C({unit_z, unit_x}, unit_y), | 
|  | C({unit_x, unit_z}, neg_unit_y), | 
|  |  | 
|  | C({unit_y, unit_z}, unit_x), | 
|  | C({unit_z, unit_y}, neg_unit_x), | 
|  |  | 
|  | C({vec_x(T(1)), vec_y(T(2))}, vec_z(T(2))), | 
|  | C({vec_y(T(1)), vec_x(T(2))}, vec_z(-T(2))), | 
|  | C({vec_x(T(2)), vec_y(T(3))}, vec_z(T(6))), | 
|  | C({vec_y(T(2)), vec_x(T(3))}, vec_z(-T(6))), | 
|  |  | 
|  | C({Vec(T(1), T(2), T(3)), Vec(T(1), T(5), T(7))}, Vec(T(-1), T(-4), T(3))), | 
|  | C({Vec(T(33), T(44), T(55)), Vec(T(13), T(42), T(39))}, Vec(T(-594), T(-572), T(814))), | 
|  | C({Vec(T(3.5), T(4), T(5.5)), Vec(T(1), T(4.5), T(3.5))}, | 
|  | Vec(T(-10.75), T(-6.75), T(11.75))), | 
|  | }; | 
|  |  | 
|  | std::string pos_error_msg = | 
|  | "12:34 error: " + OverflowErrorMessage(T::Highest(), "*", T::Highest()); | 
|  | std::string neg_error_msg = | 
|  | "12:34 error: " + OverflowErrorMessage(T::Lowest(), "*", T::Lowest()); | 
|  | ConcatInto(  // | 
|  | r, std::vector<Case>{ | 
|  | E({highest_x, highest_y}, pos_error_msg), | 
|  | E({highest_y, highest_x}, pos_error_msg), | 
|  | E({highest_z, highest_x}, pos_error_msg), | 
|  | E({highest_x, highest_z}, pos_error_msg), | 
|  | E({highest_y, highest_z}, pos_error_msg), | 
|  | E({highest_z, highest_y}, pos_error_msg), | 
|  | E({lowest_x, lowest_y}, neg_error_msg), | 
|  | E({lowest_y, lowest_x}, neg_error_msg), | 
|  | E({lowest_z, lowest_x}, neg_error_msg), | 
|  | E({lowest_x, lowest_z}, neg_error_msg), | 
|  | E({lowest_y, lowest_z}, neg_error_msg), | 
|  | E({lowest_z, lowest_y}, neg_error_msg), | 
|  | }); | 
|  |  | 
|  | return r; | 
|  | } | 
|  | INSTANTIATE_TEST_SUITE_P(  // | 
|  | Cross, | 
|  | ResolverConstEvalBuiltinTest, | 
|  | testing::Combine(testing::Values(builtin::Function::kCross), | 
|  | testing::ValuesIn(Concat(CrossCases<AFloat>(),  // | 
|  | CrossCases<f32>(),     // | 
|  | CrossCases<f16>())))); | 
|  |  | 
|  | template <typename T> | 
|  | std::vector<Case> DistanceCases() { | 
|  | auto error_msg = [](auto a, const char* op, auto b) { | 
|  | return "12:34 error: " + OverflowErrorMessage(a, op, b) + R"( | 
|  | 12:34 note: when calculating distance)"; | 
|  | }; | 
|  |  | 
|  | return { | 
|  | C({T(0), T(0)}, T(0)), | 
|  | // length(-5) -> 5 | 
|  | C({T(30), T(35)}, T(5)), | 
|  |  | 
|  | C({Vec(T(30), T(20)), Vec(T(25), T(15))}, Val(T(7.0710678119))).FloatComp(), | 
|  |  | 
|  | E({T::Lowest(), T::Highest()}, error_msg(T::Lowest(), "-", T::Highest())), | 
|  | E({Vec(T::Highest(), T::Highest()), Vec(T(1), T(1))}, | 
|  | error_msg(T(T::Highest() - T(1)), "*", T(T::Highest() - T(1)))), | 
|  | }; | 
|  | } | 
|  | INSTANTIATE_TEST_SUITE_P(  // | 
|  | Distance, | 
|  | ResolverConstEvalBuiltinTest, | 
|  | testing::Combine(testing::Values(builtin::Function::kDistance), | 
|  | testing::ValuesIn(Concat(DistanceCases<AFloat>(),  // | 
|  | DistanceCases<f32>(),     // | 
|  | DistanceCases<f16>())))); | 
|  |  | 
|  | template <typename T> | 
|  | std::vector<Case> DotCases() { | 
|  | auto r = std::vector<Case>{ | 
|  | C({Vec(T(0), T(0)), Vec(T(0), T(0))}, Val(T(0))), | 
|  | C({Vec(T(0), T(0), T(0)), Vec(T(0), T(0), T(0))}, Val(T(0))), | 
|  | C({Vec(T(0), T(0), T(0), T(0)), Vec(T(0), T(0), T(0), T(0))}, Val(T(0))), | 
|  | C({Vec(T(1), T(2), T(3), T(4)), Vec(T(5), T(6), T(7), T(8))}, Val(T(70))), | 
|  |  | 
|  | C({Vec(T(1), T(1)), Vec(T(1), T(1))}, Val(T(2))), | 
|  | C({Vec(T(1), T(2)), Vec(T(2), T(1))}, Val(T(4))), | 
|  | C({Vec(T(2), T(2)), Vec(T(2), T(2))}, Val(T(8))), | 
|  |  | 
|  | C({Vec(T::Highest(), T::Highest()), Vec(T(1), T(0))}, Val(T::Highest())), | 
|  | C({Vec(T::Lowest(), T::Lowest()), Vec(T(1), T(0))}, Val(T::Lowest())), | 
|  | }; | 
|  |  | 
|  | if constexpr (IsAbstract<T> || IsFloatingPoint<T>) { | 
|  | auto error_msg = [](auto a, const char* op, auto b) { | 
|  | return "12:34 error: " + OverflowErrorMessage(a, op, b) + R"( | 
|  | 12:34 note: when calculating dot)"; | 
|  | }; | 
|  | ConcatInto(  // | 
|  | r, std::vector<Case>{ | 
|  | E({Vec(T::Highest(), T::Highest()), Vec(T(1), T(1))}, | 
|  | error_msg(T::Highest(), "+", T::Highest())), | 
|  | E({Vec(T::Lowest(), T::Lowest()), Vec(T(1), T(1))}, | 
|  | error_msg(T::Lowest(), "+", T::Lowest())), | 
|  | }); | 
|  | } else { | 
|  | // Overflow is not an error for concrete integrals | 
|  | ConcatInto(  // | 
|  | r, std::vector<Case>{ | 
|  | C({Vec(T::Highest(), T::Highest()), Vec(T(1), T(1))}, | 
|  | Val(Add(T::Highest(), T::Highest()))), | 
|  | C({Vec(T::Lowest(), T::Lowest()), Vec(T(1), T(1))}, | 
|  | Val(Add(T::Lowest(), T::Lowest()))), | 
|  | }); | 
|  | } | 
|  | return r; | 
|  | } | 
|  | INSTANTIATE_TEST_SUITE_P(  // | 
|  | Dot, | 
|  | ResolverConstEvalBuiltinTest, | 
|  | testing::Combine(testing::Values(builtin::Function::kDot), | 
|  | testing::ValuesIn(Concat(DotCases<AInt>(),    // | 
|  | DotCases<i32>(),     // | 
|  | DotCases<u32>(),     // | 
|  | DotCases<AFloat>(),  // | 
|  | DotCases<f32>(),     // | 
|  | DotCases<f16>())))); | 
|  |  | 
|  | template <typename T> | 
|  | std::vector<Case> DeterminantCases() { | 
|  | auto error_msg = [](auto a, const char* op, auto b) { | 
|  | return "12:34 error: " + OverflowErrorMessage(a, op, b) + R"( | 
|  | 12:34 note: when calculating determinant)"; | 
|  | }; | 
|  |  | 
|  | auto r = std::vector<Case>{ | 
|  | // All zero == 0 | 
|  | C({Mat({T(0), T(0)},    // | 
|  | {T(0), T(0)})},  // | 
|  | Val(T(0))), | 
|  |  | 
|  | C({Mat({T(0), T(0), T(0)},    // | 
|  | {T(0), T(0), T(0)},    // | 
|  | {T(0), T(0), T(0)})},  // | 
|  | Val(T(0))), | 
|  |  | 
|  | C({Mat({T(0), T(0), T(0), T(0)},    // | 
|  | {T(0), T(0), T(0), T(0)},    // | 
|  | {T(0), T(0), T(0), T(0)},    // | 
|  | {T(0), T(0), T(0), T(0)})},  // | 
|  | Val(T(0))), | 
|  |  | 
|  | // All same == 0 | 
|  | C({Mat({T(42), T(42)},    // | 
|  | {T(42), T(42)})},  // | 
|  | Val(T(0))), | 
|  |  | 
|  | C({Mat({T(42), T(42), T(42)},    // | 
|  | {T(42), T(42), T(42)},    // | 
|  | {T(42), T(42), T(42)})},  // | 
|  | Val(T(0))), | 
|  |  | 
|  | C({Mat({T(42), T(42), T(42), T(42)},    // | 
|  | {T(42), T(42), T(42), T(42)},    // | 
|  | {T(42), T(42), T(42), T(42)},    // | 
|  | {T(42), T(42), T(42), T(42)})},  // | 
|  | Val(T(0))), | 
|  |  | 
|  | // Various values | 
|  | C({Mat({-T(2), T(17)},   // | 
|  | {T(5), T(45)})},  // | 
|  | Val(-T(175))), | 
|  |  | 
|  | C({Mat({T(4), T(6), -T(13)},    // | 
|  | {T(12), T(5), T(8)},     // | 
|  | {T(9), T(17), T(16)})},  // | 
|  | Val(-T(3011))), | 
|  |  | 
|  | C({Mat({T(2), T(9), T(8), T(1)},       // | 
|  | {-T(4), T(11), -T(3), T(7)},    // | 
|  | {T(6), T(5), T(12), -T(6)},     // | 
|  | {T(3), -T(10), T(4), -T(7)})},  // | 
|  | Val(T(469))), | 
|  |  | 
|  | // Overflow during multiply | 
|  | E({Mat({T::Highest(), T(0)},  // | 
|  | {T(0), T(2)})},        // | 
|  | error_msg(T::Highest(), "*", T(2))), | 
|  |  | 
|  | // Overflow during subtract | 
|  | E({Mat({T::Highest(), T::Lowest()},  // | 
|  | {T(1), T(1)})},               // | 
|  | error_msg(T::Highest(), "-", T::Lowest())), | 
|  | }; | 
|  |  | 
|  | return r; | 
|  | } | 
|  | INSTANTIATE_TEST_SUITE_P(  // | 
|  | Determinant, | 
|  | ResolverConstEvalBuiltinTest, | 
|  | testing::Combine(testing::Values(builtin::Function::kDeterminant), | 
|  | testing::ValuesIn(Concat(DeterminantCases<AFloat>(),  // | 
|  | DeterminantCases<f32>(),     // | 
|  | DeterminantCases<f16>())))); | 
|  |  | 
|  | template <typename T> | 
|  | std::vector<Case> FaceForwardCases() { | 
|  | // Rotate v by degs around Z axis | 
|  | auto rotate = [&](const Value& v, float degs) { | 
|  | auto x = builder::As<T>(v.args[0]); | 
|  | auto y = builder::As<T>(v.args[1]); | 
|  | auto z = builder::As<T>(v.args[2]); | 
|  | auto rads = T(degs) * kPi<T> / T(180); | 
|  | auto x2 = T(x * std::cos(rads) - y * std::sin(rads)); | 
|  | auto y2 = T(x * std::sin(rads) + y * std::cos(rads)); | 
|  | return Vec(x2, y2, z); | 
|  | }; | 
|  |  | 
|  | // An arbitrary input vector and its negation, used for e1 args to FaceForward | 
|  | auto pos_vec = Vec(T(1), T(2), T(3)); | 
|  | auto neg_vec = Vec(-T(1), -T(2), -T(3)); | 
|  |  | 
|  | // An arbitrary vector in the xy plane, used for e2 and e3 args to FaceForward. | 
|  | auto fwd_xy = Vec(T(1.23), T(4.56), T(0)); | 
|  |  | 
|  | std::vector<Case> r = { | 
|  | C({pos_vec, fwd_xy, rotate(fwd_xy, 85)}, neg_vec), | 
|  | C({pos_vec, fwd_xy, rotate(fwd_xy, 85)}, neg_vec), | 
|  | C({pos_vec, fwd_xy, rotate(fwd_xy, 95)}, pos_vec), | 
|  | C({pos_vec, fwd_xy, rotate(fwd_xy, -95)}, pos_vec), | 
|  | C({pos_vec, fwd_xy, rotate(fwd_xy, 180)}, pos_vec), | 
|  |  | 
|  | C({pos_vec, rotate(fwd_xy, 33), rotate(fwd_xy, 33 + 85)}, neg_vec), | 
|  | C({pos_vec, rotate(fwd_xy, 33), rotate(fwd_xy, 33 - 85)}, neg_vec), | 
|  | C({pos_vec, rotate(fwd_xy, 33), rotate(fwd_xy, 33 + 95)}, pos_vec), | 
|  | C({pos_vec, rotate(fwd_xy, 33), rotate(fwd_xy, 33 - 95)}, pos_vec), | 
|  | C({pos_vec, rotate(fwd_xy, 33), rotate(fwd_xy, 33 + 180)}, pos_vec), | 
|  |  | 
|  | C({pos_vec, rotate(fwd_xy, 234), rotate(fwd_xy, 234 + 85)}, neg_vec), | 
|  | C({pos_vec, rotate(fwd_xy, 234), rotate(fwd_xy, 234 - 85)}, neg_vec), | 
|  | C({pos_vec, rotate(fwd_xy, 234), rotate(fwd_xy, 234 + 95)}, pos_vec), | 
|  | C({pos_vec, rotate(fwd_xy, 234), rotate(fwd_xy, 234 - 95)}, pos_vec), | 
|  | C({pos_vec, rotate(fwd_xy, 234), rotate(fwd_xy, 234 + 180)}, pos_vec), | 
|  |  | 
|  | // Same, but swap input and result vectors | 
|  | C({neg_vec, fwd_xy, rotate(fwd_xy, 85)}, pos_vec), | 
|  | C({neg_vec, fwd_xy, rotate(fwd_xy, 85)}, pos_vec), | 
|  | C({neg_vec, fwd_xy, rotate(fwd_xy, 95)}, neg_vec), | 
|  | C({neg_vec, fwd_xy, rotate(fwd_xy, -95)}, neg_vec), | 
|  | C({neg_vec, fwd_xy, rotate(fwd_xy, 180)}, neg_vec), | 
|  |  | 
|  | C({neg_vec, rotate(fwd_xy, 33), rotate(fwd_xy, 33 + 85)}, pos_vec), | 
|  | C({neg_vec, rotate(fwd_xy, 33), rotate(fwd_xy, 33 - 85)}, pos_vec), | 
|  | C({neg_vec, rotate(fwd_xy, 33), rotate(fwd_xy, 33 + 95)}, neg_vec), | 
|  | C({neg_vec, rotate(fwd_xy, 33), rotate(fwd_xy, 33 - 95)}, neg_vec), | 
|  | C({neg_vec, rotate(fwd_xy, 33), rotate(fwd_xy, 33 + 180)}, neg_vec), | 
|  |  | 
|  | C({neg_vec, rotate(fwd_xy, 234), rotate(fwd_xy, 234 + 85)}, pos_vec), | 
|  | C({neg_vec, rotate(fwd_xy, 234), rotate(fwd_xy, 234 - 85)}, pos_vec), | 
|  | C({neg_vec, rotate(fwd_xy, 234), rotate(fwd_xy, 234 + 95)}, neg_vec), | 
|  | C({neg_vec, rotate(fwd_xy, 234), rotate(fwd_xy, 234 - 95)}, neg_vec), | 
|  | C({neg_vec, rotate(fwd_xy, 234), rotate(fwd_xy, 234 + 180)}, neg_vec), | 
|  | }; | 
|  |  | 
|  | auto error_msg = [](auto a, const char* op, auto b) { | 
|  | return "12:34 error: " + OverflowErrorMessage(a, op, b) + R"( | 
|  | 12:34 note: when calculating faceForward)"; | 
|  | }; | 
|  | ConcatInto(  // | 
|  | r, std::vector<Case>{ | 
|  | // Overflow the dot product operation | 
|  | E({pos_vec, Vec(T::Highest(), T::Highest(), T(0)), Vec(T(1), T(1), T(0))}, | 
|  | error_msg(T::Highest(), "+", T::Highest())), | 
|  | E({pos_vec, Vec(T::Lowest(), T::Lowest(), T(0)), Vec(T(1), T(1), T(0))}, | 
|  | error_msg(T::Lowest(), "+", T::Lowest())), | 
|  | }); | 
|  |  | 
|  | return r; | 
|  | } | 
|  | INSTANTIATE_TEST_SUITE_P(  // | 
|  | FaceForward, | 
|  | ResolverConstEvalBuiltinTest, | 
|  | testing::Combine(testing::Values(builtin::Function::kFaceForward), | 
|  | testing::ValuesIn(Concat(FaceForwardCases<AFloat>(),  // | 
|  | FaceForwardCases<f32>(),     // | 
|  | FaceForwardCases<f16>())))); | 
|  |  | 
|  | template <typename T> | 
|  | std::vector<Case> FirstLeadingBitCases() { | 
|  | using B = BitValues<T>; | 
|  | auto r = std::vector<Case>{ | 
|  | // Both signed and unsigned return T(-1) for input 0 | 
|  | C({T(0)}, T(-1)), | 
|  |  | 
|  | C({B::Lsh(1, 30)}, T(30)),  // | 
|  | C({B::Lsh(1, 29)}, T(29)),  // | 
|  | C({B::Lsh(1, 28)}, T(28)), | 
|  | //... | 
|  | C({B::Lsh(1, 3)}, T(3)),  // | 
|  | C({B::Lsh(1, 2)}, T(2)),  // | 
|  | C({B::Lsh(1, 1)}, T(1)),  // | 
|  | C({B::Lsh(1, 0)}, T(0)), | 
|  |  | 
|  | C({T(0b0000'0000'0100'1000'1000'1000'0000'0000)}, T(22)), | 
|  | C({T(0b0000'0000'0000'0100'1000'1000'0000'0000)}, T(18)), | 
|  |  | 
|  | // Vector tests | 
|  | C({Vec(B::Lsh(1, 30), B::Lsh(1, 29), B::Lsh(1, 28))}, Vec(T(30), T(29), T(28))), | 
|  | C({Vec(B::Lsh(1, 2), B::Lsh(1, 1), B::Lsh(1, 0))}, Vec(T(2), T(1), T(0))), | 
|  | }; | 
|  |  | 
|  | ConcatIntoIf<IsUnsignedIntegral<T>>(  // | 
|  | r, std::vector<Case>{ | 
|  | C({B::Lsh(1, 31)}, T(31)), | 
|  |  | 
|  | C({T(0b1111'1111'1111'1111'1111'1111'1111'1110)}, T(31)), | 
|  | C({T(0b1111'1111'1111'1111'1111'1111'1111'1100)}, T(31)), | 
|  | C({T(0b1111'1111'1111'1111'1111'1111'1111'1000)}, T(31)), | 
|  | //... | 
|  | C({T(0b1110'0000'0000'0000'0000'0000'0000'0000)}, T(31)), | 
|  | C({T(0b1100'0000'0000'0000'0000'0000'0000'0000)}, T(31)), | 
|  | C({T(0b1000'0000'0000'0000'0000'0000'0000'0000)}, T(31)), | 
|  | }); | 
|  |  | 
|  | ConcatIntoIf<IsSignedIntegral<T>>(  // | 
|  | r, std::vector<Case>{ | 
|  | // Signed returns -1 for input -1 | 
|  | C({T(-1)}, T(-1)), | 
|  |  | 
|  | C({B::Lsh(1, 31)}, T(30)), | 
|  |  | 
|  | C({T(0b1111'1111'1111'1111'1111'1111'1111'1110)}, T(0)), | 
|  | C({T(0b1111'1111'1111'1111'1111'1111'1111'1100)}, T(1)), | 
|  | C({T(0b1111'1111'1111'1111'1111'1111'1111'1000)}, T(2)), | 
|  | //... | 
|  | C({T(0b1110'0000'0000'0000'0000'0000'0000'0000)}, T(28)), | 
|  | C({T(0b1100'0000'0000'0000'0000'0000'0000'0000)}, T(29)), | 
|  | C({T(0b1000'0000'0000'0000'0000'0000'0000'0000)}, T(30)), | 
|  | }); | 
|  |  | 
|  | return r; | 
|  | } | 
|  | INSTANTIATE_TEST_SUITE_P(  // | 
|  | FirstLeadingBit, | 
|  | ResolverConstEvalBuiltinTest, | 
|  | testing::Combine(testing::Values(builtin::Function::kFirstLeadingBit), | 
|  | testing::ValuesIn(Concat(FirstLeadingBitCases<i32>(),  // | 
|  | FirstLeadingBitCases<u32>())))); | 
|  |  | 
|  | template <typename T> | 
|  | std::vector<Case> FirstTrailingBitCases() { | 
|  | using B = BitValues<T>; | 
|  | auto r = std::vector<Case>{ | 
|  | C({T(0)}, T(-1)), | 
|  |  | 
|  | C({B::Lsh(1, 31)}, T(31)),  // | 
|  | C({B::Lsh(1, 30)}, T(30)),  // | 
|  | C({B::Lsh(1, 29)}, T(29)),  // | 
|  | C({B::Lsh(1, 28)}, T(28)), | 
|  | //... | 
|  | C({B::Lsh(1, 3)}, T(3)),  // | 
|  | C({B::Lsh(1, 2)}, T(2)),  // | 
|  | C({B::Lsh(1, 1)}, T(1)),  // | 
|  | C({B::Lsh(1, 0)}, T(0)), | 
|  |  | 
|  | C({T(0b0000'0000'0100'1000'1000'1000'0000'0000)}, T(11)), | 
|  | C({T(0b0000'0100'1000'1000'1000'0000'0000'0000)}, T(15)), | 
|  |  | 
|  | // Vector tests | 
|  | C({Vec(B::Lsh(1, 31), B::Lsh(1, 30), B::Lsh(1, 29))}, Vec(T(31), T(30), T(29))), | 
|  | C({Vec(B::Lsh(1, 2), B::Lsh(1, 1), B::Lsh(1, 0))}, Vec(T(2), T(1), T(0))), | 
|  | }; | 
|  |  | 
|  | return r; | 
|  | } | 
|  | INSTANTIATE_TEST_SUITE_P(  // | 
|  | FirstTrailingBit, | 
|  | ResolverConstEvalBuiltinTest, | 
|  | testing::Combine(testing::Values(builtin::Function::kFirstTrailingBit), | 
|  | testing::ValuesIn(Concat(FirstTrailingBitCases<i32>(),  // | 
|  | FirstTrailingBitCases<u32>())))); | 
|  |  | 
|  | template <typename T> | 
|  | std::vector<Case> FloorCases() { | 
|  | return { | 
|  | C({T(0)}, T(0)), | 
|  | C({-T(0)}, -T(0)), | 
|  | C({-T(1.5)}, -T(2.0)), | 
|  | C({T(1.5)}, T(1.0)), | 
|  | C({T::Lowest()}, T::Lowest()), | 
|  | C({T::Highest()}, T::Highest()), | 
|  |  | 
|  | C({Vec(T(0), T(1.5), -T(1.5))}, Vec(T(0), T(1.0), -T(2.0))), | 
|  | }; | 
|  | } | 
|  | INSTANTIATE_TEST_SUITE_P(  // | 
|  | Floor, | 
|  | ResolverConstEvalBuiltinTest, | 
|  | testing::Combine(testing::Values(builtin::Function::kFloor), | 
|  | testing::ValuesIn(Concat(FloorCases<AFloat>(),  // | 
|  | FloorCases<f32>(), | 
|  | FloorCases<f16>())))); | 
|  |  | 
|  | template <typename T> | 
|  | std::vector<Case> FmaCases() { | 
|  | auto error_msg = [](auto a, const char* op, auto b) { | 
|  | return "12:34 error: " + OverflowErrorMessage(a, op, b) + R"( | 
|  | 12:34 note: when calculating fma)"; | 
|  | }; | 
|  | return { | 
|  | C({T(0), T(0), T(0)}, T(0)), | 
|  | C({T(1), T(2), T(3)}, T(5)), | 
|  | C({Vec(T(1), T(2.5), -T(1)), Vec(T(2), T(2.5), T(1)), Vec(T(4), T(3.75), -T(2))}, | 
|  | Vec(T(6), T(10), -T(3))), | 
|  |  | 
|  | E({T::Highest(), T::Highest(), T(0)}, error_msg(T::Highest(), "*", T::Highest())), | 
|  | E({T::Highest(), T(1), T::Highest()}, error_msg(T::Highest(), "+", T::Highest())), | 
|  | }; | 
|  | } | 
|  | INSTANTIATE_TEST_SUITE_P(  // | 
|  | Fma, | 
|  | ResolverConstEvalBuiltinTest, | 
|  | testing::Combine(testing::Values(builtin::Function::kFma), | 
|  | testing::ValuesIn(Concat(FmaCases<AFloat>(),  // | 
|  | FmaCases<f32>(), | 
|  | FmaCases<f16>())))); | 
|  |  | 
|  | template <typename T> | 
|  | std::vector<Case> FractCases() { | 
|  | auto r = std::vector<Case>{ | 
|  | C({T(0)}, T(0)), | 
|  | C({T(0.1)}, T(0.1)), | 
|  | C({T(-0.1)}, T(0.9)), | 
|  | C({T(0.0000001)}, T(0.0000001)), | 
|  | C({T(-0.0000001)}, T(0.9999999)), | 
|  | C({T(12.34567)}, T(0.34567)).FloatComp(0.002), | 
|  | C({T(-12.34567)}, T(0.65433)).FloatComp(0.002), | 
|  | C({T::Lowest()}, T(0)), | 
|  | C({T::Highest()}, T(0)), | 
|  | // Vector tests | 
|  | C({Vec(T(0.1), T(-0.1), T(-0.0000001))}, Vec(T(0.1), T(0.9), T(0.9999999))), | 
|  | }; | 
|  | // Note: Valid results are in the closed interval [0, 1.0]. For example, if e is a very small | 
|  | // negative number, then fract(e) may be 1.0. | 
|  | ConcatIntoIf<!std::is_same_v<T, f16>>(  // | 
|  | r, std::vector<Case>{ | 
|  | C({T(-0.000000000000000001)}, T(1)), | 
|  | }); | 
|  |  | 
|  | return r; | 
|  | } | 
|  | INSTANTIATE_TEST_SUITE_P(  // | 
|  | Fract, | 
|  | ResolverConstEvalBuiltinTest, | 
|  | testing::Combine(testing::Values(builtin::Function::kFract), | 
|  | testing::ValuesIn(Concat(FractCases<AFloat>(),  // | 
|  | FractCases<f32>(), | 
|  | FractCases<f16>())))); | 
|  |  | 
|  | template <typename T> | 
|  | std::vector<Case> FrexpCases() { | 
|  | using F = T;                                                         // fract type | 
|  | using E = std::conditional_t<std::is_same_v<T, AFloat>, AInt, i32>;  // exp type | 
|  |  | 
|  | std::vector<Case> cases = { | 
|  | // Scalar tests | 
|  | //  in         fract     exp | 
|  | C({T(-3.5)}, {F(-0.875), E(2)}),  // | 
|  | C({T(-3.0)}, {F(-0.750), E(2)}),  // | 
|  | C({T(-2.5)}, {F(-0.625), E(2)}),  // | 
|  | C({T(-2.0)}, {F(-0.500), E(2)}),  // | 
|  | C({T(-1.5)}, {F(-0.750), E(1)}),  // | 
|  | C({T(-1.0)}, {F(-0.500), E(1)}),  // | 
|  | C({T(+0.0)}, {F(+0.000), E(0)}),  // | 
|  | C({T(+1.0)}, {F(+0.500), E(1)}),  // | 
|  | C({T(+1.5)}, {F(+0.750), E(1)}),  // | 
|  | C({T(+2.0)}, {F(+0.500), E(2)}),  // | 
|  | C({T(+2.5)}, {F(+0.625), E(2)}),  // | 
|  | C({T(+3.0)}, {F(+0.750), E(2)}),  // | 
|  | C({T(+3.5)}, {F(+0.875), E(2)}),  // | 
|  |  | 
|  | // Vector tests | 
|  | //         in                 fract                    exp | 
|  | C({Vec(T(-2.5), T(+1.0))}, {Vec(F(-0.625), F(+0.500)), Vec(E(2), E(1))}), | 
|  | C({Vec(T(+3.5), T(-2.5))}, {Vec(F(+0.875), F(-0.625)), Vec(E(2), E(2))}), | 
|  | }; | 
|  |  | 
|  | ConcatIntoIf<std::is_same_v<T, f16>>(cases, std::vector<Case>{ | 
|  | C({T::Highest()}, {F(0x0.ffep0), E(16)}),  // | 
|  | C({T::Lowest()}, {F(-0x0.ffep0), E(16)}),  // | 
|  | C({T::Smallest()}, {F(0.5), E(-13)}),      // | 
|  | }); | 
|  |  | 
|  | ConcatIntoIf<std::is_same_v<T, f32>>(cases, | 
|  | std::vector<Case>{ | 
|  | C({T::Highest()}, {F(0x0.ffffffp0), E(128)}),  // | 
|  | C({T::Lowest()}, {F(-0x0.ffffffp0), E(128)}),  // | 
|  | C({T::Smallest()}, {F(0.5), E(-125)}),         // | 
|  | }); | 
|  |  | 
|  | ConcatIntoIf<std::is_same_v<T, AFloat>>( | 
|  | cases, std::vector<Case>{ | 
|  | C({T::Highest()}, {F(0x0.fffffffffffff8p0), E(1024)}),  // | 
|  | C({T::Lowest()}, {F(-0x0.fffffffffffff8p0), E(1024)}),  // | 
|  | C({T::Smallest()}, {F(0.5), E(-1021)}),                 // | 
|  | }); | 
|  | return cases; | 
|  | } | 
|  | INSTANTIATE_TEST_SUITE_P(  // | 
|  | Frexp, | 
|  | ResolverConstEvalBuiltinTest, | 
|  | testing::Combine(testing::Values(builtin::Function::kFrexp), | 
|  | testing::ValuesIn(Concat(FrexpCases<AFloat>(),  // | 
|  | FrexpCases<f32>(),     // | 
|  | FrexpCases<f16>())))); | 
|  |  | 
|  | template <typename T> | 
|  | std::vector<Case> InsertBitsCases() { | 
|  | using UT = Number<std::make_unsigned_t<UnwrapNumber<T>>>; | 
|  |  | 
|  | auto e = /* */ T(0b0101'1100'0011'1010'0101'1100'0011'1010); | 
|  | auto newbits = T{0b1010'0011'1100'0101'1010'0011'1100'0101}; | 
|  |  | 
|  | auto r = std::vector<Case>{ | 
|  | // args: e, newbits, offset, count | 
|  |  | 
|  | // If count is 0, result is e | 
|  | C({e, newbits, UT(0), UT(0)}, e),  // | 
|  | C({e, newbits, UT(1), UT(0)}, e),  // | 
|  | C({e, newbits, UT(2), UT(0)}, e),  // | 
|  | C({e, newbits, UT(3), UT(0)}, e),  // | 
|  | // ... | 
|  | C({e, newbits, UT(29), UT(0)}, e),  // | 
|  | C({e, newbits, UT(30), UT(0)}, e),  // | 
|  | C({e, newbits, UT(31), UT(0)}, e), | 
|  |  | 
|  | // Copy 1 to 32 bits of newbits to e at offset 0 | 
|  | C({e, newbits, UT(0), UT(1)}, T(0b0101'1100'0011'1010'0101'1100'0011'1011)), | 
|  | C({e, newbits, UT(0), UT(2)}, T(0b0101'1100'0011'1010'0101'1100'0011'1001)), | 
|  | C({e, newbits, UT(0), UT(3)}, T(0b0101'1100'0011'1010'0101'1100'0011'1101)), | 
|  | C({e, newbits, UT(0), UT(4)}, T(0b0101'1100'0011'1010'0101'1100'0011'0101)), | 
|  | C({e, newbits, UT(0), UT(5)}, T(0b0101'1100'0011'1010'0101'1100'0010'0101)), | 
|  | C({e, newbits, UT(0), UT(6)}, T(0b0101'1100'0011'1010'0101'1100'0000'0101)), | 
|  | // ... | 
|  | C({e, newbits, UT(0), UT(29)}, T(0b0100'0011'1100'0101'1010'0011'1100'0101)), | 
|  | C({e, newbits, UT(0), UT(30)}, T(0b0110'0011'1100'0101'1010'0011'1100'0101)), | 
|  | C({e, newbits, UT(0), UT(31)}, T(0b0010'0011'1100'0101'1010'0011'1100'0101)), | 
|  | C({e, newbits, UT(0), UT(32)}, T(0b1010'0011'1100'0101'1010'0011'1100'0101)), | 
|  |  | 
|  | // Copy at varying offsets and counts | 
|  | C({e, newbits, UT(3), UT(8)}, T(0b0101'1100'0011'1010'0101'1110'0010'1010)), | 
|  | C({e, newbits, UT(8), UT(8)}, T(0b0101'1100'0011'1010'1100'0101'0011'1010)), | 
|  | C({e, newbits, UT(15), UT(1)}, T(0b0101'1100'0011'1010'1101'1100'0011'1010)), | 
|  | C({e, newbits, UT(16), UT(16)}, T(0b1010'0011'1100'0101'0101'1100'0011'1010)), | 
|  |  | 
|  | // Vector tests | 
|  | C({Vec(T(0b1111'0000'1111'0000'1111'0000'1111'0000),  // | 
|  | T(0b0000'1111'0000'1111'0000'1111'0000'1111),  // | 
|  | T(0b1010'0101'1010'0101'1010'0101'1010'0101)), | 
|  | Vec(T(0b1111'1111'1111'1111'1111'1111'1111'1111),  // | 
|  | T(0b1111'1111'1111'1111'1111'1111'1111'1111),  // | 
|  | T(0b1111'1111'1111'1111'1111'1111'1111'1111)), | 
|  | Val(UT(3)), Val(UT(8))}, | 
|  | Vec(T(0b1111'0000'1111'0000'1111'0111'1111'1000),  // | 
|  | T(0b0000'1111'0000'1111'0000'1111'1111'1111),  // | 
|  | T(0b1010'0101'1010'0101'1010'0111'1111'1101))), | 
|  | }; | 
|  |  | 
|  | const char* error_msg = | 
|  | "12:34 error: 'offset + 'count' must be less than or equal to the bit width of 'e'"; | 
|  | ConcatInto(  // | 
|  | r, std::vector<Case>{ | 
|  | E({T(1), T(1), UT(33), UT(0)}, error_msg),         // | 
|  | E({T(1), T(1), UT(34), UT(0)}, error_msg),         // | 
|  | E({T(1), T(1), UT(1000), UT(0)}, error_msg),       // | 
|  | E({T(1), T(1), UT::Highest(), UT()}, error_msg),   // | 
|  | E({T(1), T(1), UT(0), UT(33)}, error_msg),         // | 
|  | E({T(1), T(1), UT(0), UT(34)}, error_msg),         // | 
|  | E({T(1), T(1), UT(0), UT(1000)}, error_msg),       // | 
|  | E({T(1), T(1), UT(0), UT::Highest()}, error_msg),  // | 
|  | E({T(1), T(1), UT(33), UT(33)}, error_msg),        // | 
|  | E({T(1), T(1), UT(34), UT(34)}, error_msg),        // | 
|  | E({T(1), T(1), UT(1000), UT(1000)}, error_msg),    // | 
|  | E({T(1), T(1), UT::Highest(), UT(1)}, error_msg), | 
|  | E({T(1), T(1), UT(1), UT::Highest()}, error_msg), | 
|  | E({T(1), T(1), UT::Highest(), u32::Highest()}, error_msg), | 
|  | }); | 
|  |  | 
|  | return r; | 
|  | } | 
|  | INSTANTIATE_TEST_SUITE_P(  // | 
|  | InsertBits, | 
|  | ResolverConstEvalBuiltinTest, | 
|  | testing::Combine(testing::Values(builtin::Function::kInsertBits), | 
|  | testing::ValuesIn(Concat(InsertBitsCases<i32>(),  // | 
|  | InsertBitsCases<u32>())))); | 
|  |  | 
|  | template <typename T> | 
|  | std::vector<Case> InverseSqrtCases() { | 
|  | return { | 
|  | C({T(25)}, T(.2)), | 
|  |  | 
|  | // Vector tests | 
|  | C({Vec(T(25), T(100))}, Vec(T(.2), T(.1))), | 
|  |  | 
|  | E({T(0)}, "12:34 error: inverseSqrt must be called with a value > 0"), | 
|  | E({-T(0)}, "12:34 error: inverseSqrt must be called with a value > 0"), | 
|  | E({-T(25)}, "12:34 error: inverseSqrt must be called with a value > 0"), | 
|  | }; | 
|  | } | 
|  | INSTANTIATE_TEST_SUITE_P(  // | 
|  | InverseSqrt, | 
|  | ResolverConstEvalBuiltinTest, | 
|  | testing::Combine(testing::Values(builtin::Function::kInverseSqrt), | 
|  | testing::ValuesIn(Concat(InverseSqrtCases<AFloat>(),  // | 
|  | InverseSqrtCases<f32>(), | 
|  | InverseSqrtCases<f16>())))); | 
|  |  | 
|  | template <typename T> | 
|  | std::vector<Case> DegreesAFloatCases() { | 
|  | return { | 
|  | C({T(0)}, T(0)),                             // | 
|  | C({-T(0)}, -T(0)),                           // | 
|  | C({T(0.698132)}, T(40)).FloatComp(),         // | 
|  | C({-T(1.5708)}, -T(90.000214)).FloatComp(),  // | 
|  | C({T(1.5708)}, T(90.000214)).FloatComp(),    // | 
|  | C({T(6.28319)}, T(360.00027)).FloatComp(), | 
|  | }; | 
|  | } | 
|  | INSTANTIATE_TEST_SUITE_P(  // | 
|  | DegreesAFloat, | 
|  | ResolverConstEvalBuiltinTest, | 
|  | testing::Combine(testing::Values(builtin::Function::kDegrees), | 
|  | testing::ValuesIn(DegreesAFloatCases<AFloat>()))); | 
|  |  | 
|  | template <typename T> | 
|  | std::vector<Case> DegreesF32Cases() { | 
|  | return { | 
|  | C({T(0)}, T(0)),                             // | 
|  | C({-T(0)}, -T(0)),                           // | 
|  | C({T(0.698132)}, T(40)).FloatComp(),         // | 
|  | C({-T(1.5708)}, -T(90.000206)).FloatComp(),  // | 
|  | C({T(1.5708)}, T(90.000206)).FloatComp(),    // | 
|  | C({T(6.28319)}, T(360.00024)).FloatComp(), | 
|  | }; | 
|  | } | 
|  | INSTANTIATE_TEST_SUITE_P(  // | 
|  | DegreesF32, | 
|  | ResolverConstEvalBuiltinTest, | 
|  | testing::Combine(testing::Values(builtin::Function::kDegrees), | 
|  | testing::ValuesIn(DegreesF32Cases<f32>()))); | 
|  |  | 
|  | template <typename T> | 
|  | std::vector<Case> DegreesF16Cases() { | 
|  | return { | 
|  | C({T(0)}, T(0)),                            // | 
|  | C({-T(0)}, -T(0)),                          // | 
|  | C({T(0.698132)}, T(39.96875)).FloatComp(),  // | 
|  | C({-T(1.5708)}, -T(89.9375)).FloatComp(),   // | 
|  | C({T(1.5708)}, T(89.9375)).FloatComp(),     // | 
|  | C({T(6.28319)}, T(359.75)).FloatComp(), | 
|  | }; | 
|  | } | 
|  | INSTANTIATE_TEST_SUITE_P(  // | 
|  | DegreesF16, | 
|  | ResolverConstEvalBuiltinTest, | 
|  | testing::Combine(testing::Values(builtin::Function::kDegrees), | 
|  | testing::ValuesIn(DegreesF16Cases<f16>()))); | 
|  |  | 
|  | template <typename T> | 
|  | std::vector<Case> ExpCases() { | 
|  | auto error_msg = [](auto a) { return "12:34 error: " + OverflowExpErrorMessage("e", a); }; | 
|  | return {C({T(0)}, T(1)),   // | 
|  | C({-T(0)}, T(1)),  // | 
|  | C({T(2)}, T(7.3890562)).FloatComp(), | 
|  | C({-T(2)}, T(0.13533528)).FloatComp(),  // | 
|  | C({T::Lowest()}, T(0)), | 
|  |  | 
|  | E({T::Highest()}, error_msg(T::Highest()))}; | 
|  | } | 
|  | INSTANTIATE_TEST_SUITE_P(  // | 
|  | Exp, | 
|  | ResolverConstEvalBuiltinTest, | 
|  | testing::Combine(testing::Values(builtin::Function::kExp), | 
|  | testing::ValuesIn(Concat(ExpCases<AFloat>(),  // | 
|  | ExpCases<f32>(), | 
|  | ExpCases<f16>())))); | 
|  |  | 
|  | template <typename T> | 
|  | std::vector<Case> Exp2Cases() { | 
|  | auto error_msg = [](auto a) { return "12:34 error: " + OverflowExpErrorMessage("2", a); }; | 
|  | return { | 
|  | C({T(0)}, T(1)),   // | 
|  | C({-T(0)}, T(1)),  // | 
|  | C({T(2)}, T(4.0)), | 
|  | C({-T(2)}, T(0.25)),  // | 
|  | C({T::Lowest()}, T(0)), | 
|  |  | 
|  | E({T::Highest()}, error_msg(T::Highest())), | 
|  | }; | 
|  | } | 
|  | INSTANTIATE_TEST_SUITE_P(  // | 
|  | Exp2, | 
|  | ResolverConstEvalBuiltinTest, | 
|  | testing::Combine(testing::Values(builtin::Function::kExp2), | 
|  | testing::ValuesIn(Concat(Exp2Cases<AFloat>(),  // | 
|  | Exp2Cases<f32>(), | 
|  | Exp2Cases<f16>())))); | 
|  |  | 
|  | template <typename T> | 
|  | std::vector<Case> ExtractBitsCases() { | 
|  | using UT = Number<std::make_unsigned_t<UnwrapNumber<T>>>; | 
|  |  | 
|  | // If T is signed, fills most significant bits of `val` with 1s | 
|  | auto set_msbs_if_signed = [](T val) { | 
|  | if constexpr (IsSignedIntegral<T>) { | 
|  | T result = T(~0); | 
|  | for (size_t b = 0; val; ++b) { | 
|  | if ((val & 1) == 0) { | 
|  | result = result & ~(1 << b);  // Clear bit b | 
|  | } | 
|  | val = val >> 1; | 
|  | } | 
|  | return result; | 
|  | } else { | 
|  | return val; | 
|  | } | 
|  | }; | 
|  |  | 
|  | auto e = T(0b10100011110001011010001111000101); | 
|  | auto f = T(0b01010101010101010101010101010101); | 
|  | auto g = T(0b11111010001111000101101000111100); | 
|  |  | 
|  | auto r = std::vector<Case>{ | 
|  | // args: e, offset, count | 
|  |  | 
|  | // If count is 0, result is 0 | 
|  | C({e, UT(0), UT(0)}, T(0)),  // | 
|  | C({e, UT(1), UT(0)}, T(0)),  // | 
|  | C({e, UT(2), UT(0)}, T(0)),  // | 
|  | C({e, UT(3), UT(0)}, T(0)), | 
|  | // ... | 
|  | C({e, UT(29), UT(0)}, T(0)),  // | 
|  | C({e, UT(30), UT(0)}, T(0)),  // | 
|  | C({e, UT(31), UT(0)}, T(0)), | 
|  |  | 
|  | // Extract at offset 0, varying counts | 
|  | C({e, UT(0), UT(1)}, set_msbs_if_signed(T(0b1))),    // | 
|  | C({e, UT(0), UT(2)}, T(0b01)),                       // | 
|  | C({e, UT(0), UT(3)}, set_msbs_if_signed(T(0b101))),  // | 
|  | C({e, UT(0), UT(4)}, T(0b0101)),                     // | 
|  | C({e, UT(0), UT(5)}, T(0b00101)),                    // | 
|  | C({e, UT(0), UT(6)}, T(0b000101)),                   // | 
|  | // ... | 
|  | C({e, UT(0), UT(28)}, T(0b0011110001011010001111000101)),                        // | 
|  | C({e, UT(0), UT(29)}, T(0b00011110001011010001111000101)),                       // | 
|  | C({e, UT(0), UT(30)}, set_msbs_if_signed(T(0b100011110001011010001111000101))),  // | 
|  | C({e, UT(0), UT(31)}, T(0b0100011110001011010001111000101)),                     // | 
|  | C({e, UT(0), UT(32)}, T(0b10100011110001011010001111000101)),                    // | 
|  |  | 
|  | // Extract at varying offsets and counts | 
|  | C({e, UT(0), UT(1)}, set_msbs_if_signed(T(0b1))),                   // | 
|  | C({e, UT(31), UT(1)}, set_msbs_if_signed(T(0b1))),                  // | 
|  | C({e, UT(3), UT(5)}, set_msbs_if_signed(T(0b11000))),               // | 
|  | C({e, UT(4), UT(7)}, T(0b0111100)),                                 // | 
|  | C({e, UT(10), UT(16)}, set_msbs_if_signed(T(0b1111000101101000))),  // | 
|  | C({e, UT(10), UT(22)}, set_msbs_if_signed(T(0b1010001111000101101000))), | 
|  |  | 
|  | // Vector tests | 
|  | C({Vec(e, f, g),                          // | 
|  | Val(UT(5)), Val(UT(8))},               // | 
|  | Vec(T(0b00011110),                      // | 
|  | set_msbs_if_signed(T(0b10101010)),  // | 
|  | set_msbs_if_signed(T(0b11010001)))), | 
|  | }; | 
|  |  | 
|  | const char* error_msg = | 
|  | "12:34 error: 'offset + 'count' must be less than or equal to the bit width of 'e'"; | 
|  | ConcatInto(  // | 
|  | r, std::vector<Case>{ | 
|  | E({T(1), UT(33), UT(0)}, error_msg), | 
|  | E({T(1), UT(34), UT(0)}, error_msg), | 
|  | E({T(1), UT(1000), UT(0)}, error_msg), | 
|  | E({T(1), UT::Highest(), UT(0)}, error_msg), | 
|  | E({T(1), UT(0), UT(33)}, error_msg), | 
|  | E({T(1), UT(0), UT(34)}, error_msg), | 
|  | E({T(1), UT(0), UT(1000)}, error_msg), | 
|  | E({T(1), UT(0), UT::Highest()}, error_msg), | 
|  | E({T(1), UT(33), UT(33)}, error_msg), | 
|  | E({T(1), UT(34), UT(34)}, error_msg), | 
|  | E({T(1), UT(1000), UT(1000)}, error_msg), | 
|  | E({T(1), UT::Highest(), UT(1)}, error_msg), | 
|  | E({T(1), UT(1), UT::Highest()}, error_msg), | 
|  | E({T(1), UT::Highest(), UT::Highest()}, error_msg), | 
|  | }); | 
|  |  | 
|  | return r; | 
|  | } | 
|  | INSTANTIATE_TEST_SUITE_P(  // | 
|  | ExtractBits, | 
|  | ResolverConstEvalBuiltinTest, | 
|  | testing::Combine(testing::Values(builtin::Function::kExtractBits), | 
|  | testing::ValuesIn(Concat(ExtractBitsCases<i32>(),  // | 
|  | ExtractBitsCases<u32>())))); | 
|  |  | 
|  | template <typename T> | 
|  | std::vector<Case> LdexpCases() { | 
|  | using T2 = std::conditional_t<std::is_same_v<T, AFloat>, AInt, i32>; | 
|  | T2 bias; | 
|  | if constexpr (std::is_same_v<T, f16>) { | 
|  | bias = 15; | 
|  | } else if constexpr (std::is_same_v<T, f32>) { | 
|  | bias = 127; | 
|  | } else { | 
|  | bias = 1023; | 
|  | } | 
|  |  | 
|  | auto compute = [](T e1, T2 e2) { return T{std::ldexp(e1.value, static_cast<int>(e2.value))}; }; | 
|  |  | 
|  | auto r = std::vector<Case>{ | 
|  | C({T(0), T2(0)}, T(0)),          // | 
|  | C({T(7), T2(4)}, T(112)),        // | 
|  | C({T(7), T2(5)}, T(224)),        // | 
|  | C({T(7), T2(6)}, T(448)),        // | 
|  | C({T(7), T2(-4)}, T(0.4375)),    // | 
|  | C({T(7), T2(-5)}, T(0.21875)),   // | 
|  | C({T(7), T2(-6)}, T(0.109375)),  // | 
|  | // With bias exponent | 
|  | C({T(0), T2(bias)}, T(0)),                           // | 
|  | C({T(0), T2(bias + 1)}, T(0)),                       // | 
|  | C({T(1), T2(bias)}, compute(T(1), T2(bias))),        // | 
|  | C({T(0.5), T2(bias)}, compute(T(0.5), T2(bias))),    // | 
|  | C({T(0.25), T2(bias)}, compute(T(0.25), T2(bias))),  // | 
|  | // The result may be zero if e2 + bias ≤ 0. | 
|  | C({T(0), T2(-bias)}, T(0)),      // | 
|  | C({T(0), T2(-bias - 1)}, T(0)),  // | 
|  | C({T(0), T2(-bias - 2)}, T(0)),  // | 
|  |  | 
|  | // Vector tests | 
|  | C({Vec(T(0), T(7), T(7)), Vec(T2(0), T2(4), T2(-4))}, Vec(T(0), T(112), T(0.4375))), | 
|  | }; | 
|  |  | 
|  | std::string e2_too_large_error_msg = | 
|  | "12:34 error: e2 must be less than or equal to " + std::to_string(bias + 1); | 
|  | auto val_overflow_error_msg = [](auto val) { | 
|  | return "12:34 error: " + OverflowErrorMessage(val, FriendlyName<T>()); | 
|  | }; | 
|  | ConcatInto(r, std::vector<Case>{ | 
|  | // e2 is > bias + 1 | 
|  | E({T(0), T2(bias + 2)}, e2_too_large_error_msg), | 
|  | E({T(0), T2(bias + 1000)}, e2_too_large_error_msg), | 
|  | E({T(0), T2::Highest()}, e2_too_large_error_msg), | 
|  | // Result is inf | 
|  | E({T(1), T2(bias + 1)}, val_overflow_error_msg(T::Inf())), | 
|  | E({T(2), T2(bias + 1)}, val_overflow_error_msg(T::Inf())), | 
|  | E({T::Highest(), T2(bias + 1)}, val_overflow_error_msg(T::Inf())), | 
|  | E({T(-1), T2(bias + 1)}, val_overflow_error_msg(-T::Inf())), | 
|  | E({T(-2), T2(bias + 1)}, val_overflow_error_msg(-T::Inf())), | 
|  | E({T::Lowest(), T2(bias + 1)}, val_overflow_error_msg(-T::Inf())), | 
|  | }); | 
|  | return r; | 
|  | } | 
|  | INSTANTIATE_TEST_SUITE_P(  // | 
|  | Ldexp, | 
|  | ResolverConstEvalBuiltinTest, | 
|  | testing::Combine(testing::Values(builtin::Function::kLdexp), | 
|  | testing::ValuesIn(Concat(LdexpCases<AFloat>(),  // | 
|  | LdexpCases<f32>(), | 
|  | LdexpCases<f16>())))); | 
|  |  | 
|  | template <typename T> | 
|  | std::vector<Case> LengthCases() { | 
|  | const auto kSqrtOfHighest = T(std::sqrt(T::Highest())); | 
|  | const auto kSqrtOfHighestSquared = T(kSqrtOfHighest * kSqrtOfHighest); | 
|  |  | 
|  | auto error_msg = [](auto a, const char* op, auto b) { | 
|  | return "12:34 error: " + OverflowErrorMessage(a, op, b) + R"( | 
|  | 12:34 note: when calculating length)"; | 
|  | }; | 
|  | return { | 
|  | C({T(0)}, T(0)), | 
|  | C({Vec(T(0), T(0))}, Val(T(0))), | 
|  | C({Vec(T(0), T(0), T(0))}, Val(T(0))), | 
|  | C({Vec(T(0), T(0), T(0), T(0))}, Val(T(0))), | 
|  |  | 
|  | C({T(1)}, T(1)), | 
|  | C({Vec(T(1), T(1))}, Val(T(std::sqrt(2)))), | 
|  | C({Vec(T(1), T(1), T(1))}, Val(T(std::sqrt(3)))), | 
|  | C({Vec(T(1), T(1), T(1), T(1))}, Val(T(std::sqrt(4)))), | 
|  |  | 
|  | C({T(2)}, T(2)), | 
|  | C({Vec(T(2), T(2))}, Val(T(std::sqrt(8)))), | 
|  | C({Vec(T(2), T(2), T(2))}, Val(T(std::sqrt(12)))), | 
|  | C({Vec(T(2), T(2), T(2), T(2))}, Val(T(std::sqrt(16)))), | 
|  |  | 
|  | C({Vec(T(2), T(3))}, Val(T(std::sqrt(13)))), | 
|  | C({Vec(T(2), T(3), T(4))}, Val(T(std::sqrt(29)))), | 
|  | C({Vec(T(2), T(3), T(4), T(5))}, Val(T(std::sqrt(54)))), | 
|  |  | 
|  | C({T(-5)}, T(5)), | 
|  | C({T::Highest()}, T::Highest()), | 
|  | C({T::Lowest()}, T::Highest()), | 
|  |  | 
|  | C({Vec(T(-2), T(-3), T(-4), T(-5))}, Val(T(std::sqrt(54)))), | 
|  | C({Vec(T(2), T(-3), T(4), T(-5))}, Val(T(std::sqrt(54)))), | 
|  | C({Vec(T(-2), T(3), T(-4), T(5))}, Val(T(std::sqrt(54)))), | 
|  |  | 
|  | C({Vec(kSqrtOfHighest, T(0))}, Val(kSqrtOfHighest)).FloatComp(0.2), | 
|  | C({Vec(T(0), kSqrtOfHighest)}, Val(kSqrtOfHighest)).FloatComp(0.2), | 
|  |  | 
|  | C({Vec(-kSqrtOfHighest, T(0))}, Val(kSqrtOfHighest)).FloatComp(0.2), | 
|  | C({Vec(T(0), -kSqrtOfHighest)}, Val(kSqrtOfHighest)).FloatComp(0.2), | 
|  |  | 
|  | // Overflow when squaring a term | 
|  | E({Vec(T::Highest(), T(0))}, error_msg(T::Highest(), "*", T::Highest())), | 
|  | E({Vec(T(0), T::Highest())}, error_msg(T::Highest(), "*", T::Highest())), | 
|  | // Overflow when adding squared terms | 
|  | E({Vec(kSqrtOfHighest, kSqrtOfHighest)}, | 
|  | error_msg(kSqrtOfHighestSquared, "+", kSqrtOfHighestSquared)), | 
|  | }; | 
|  | } | 
|  | INSTANTIATE_TEST_SUITE_P(  // | 
|  | Length, | 
|  | ResolverConstEvalBuiltinTest, | 
|  | testing::Combine(testing::Values(builtin::Function::kLength), | 
|  | testing::ValuesIn(Concat(LengthCases<AFloat>(),  // | 
|  | LengthCases<f32>(), | 
|  | LengthCases<f16>())))); | 
|  |  | 
|  | template <typename T> | 
|  | std::vector<Case> LogCases() { | 
|  | auto error_msg = [] { return "12:34 error: log must be called with a value > 0"; }; | 
|  | return {C({T(1)}, T(0)),                              // | 
|  | C({T(54.598150033)}, T(4)).FloatComp(0.002),  // | 
|  |  | 
|  | E({T::Lowest()}, error_msg()), E({T(0)}, error_msg()), E({-T(0)}, error_msg())}; | 
|  | } | 
|  | INSTANTIATE_TEST_SUITE_P(  // | 
|  | Log, | 
|  | ResolverConstEvalBuiltinTest, | 
|  | testing::Combine(testing::Values(builtin::Function::kLog), | 
|  | testing::ValuesIn(Concat(LogCases<AFloat>(),  // | 
|  | LogCases<f32>(), | 
|  | LogCases<f16>())))); | 
|  | template <typename T> | 
|  | std::vector<Case> LogF16Cases() { | 
|  | return { | 
|  | C({T::Highest()}, T(11.085938)).FloatComp(), | 
|  | }; | 
|  | } | 
|  | INSTANTIATE_TEST_SUITE_P(  // | 
|  | LogF16, | 
|  | ResolverConstEvalBuiltinTest, | 
|  | testing::Combine(testing::Values(builtin::Function::kLog), | 
|  | testing::ValuesIn(LogF16Cases<f16>()))); | 
|  | template <typename T> | 
|  | std::vector<Case> LogF32Cases() { | 
|  | return { | 
|  | C({T::Highest()}, T(88.722839)).FloatComp(), | 
|  | }; | 
|  | } | 
|  | INSTANTIATE_TEST_SUITE_P(  // | 
|  | LogF32, | 
|  | ResolverConstEvalBuiltinTest, | 
|  | testing::Combine(testing::Values(builtin::Function::kLog), | 
|  | testing::ValuesIn(LogF32Cases<f32>()))); | 
|  |  | 
|  | template <typename T> | 
|  | std::vector<Case> LogAbstractCases() { | 
|  | return { | 
|  | C({T::Highest()}, T(709.78271)).FloatComp(), | 
|  | }; | 
|  | } | 
|  | INSTANTIATE_TEST_SUITE_P(  // | 
|  | LogAbstract, | 
|  | ResolverConstEvalBuiltinTest, | 
|  | testing::Combine(testing::Values(builtin::Function::kLog), | 
|  | testing::ValuesIn(LogAbstractCases<AFloat>()))); | 
|  |  | 
|  | template <typename T> | 
|  | std::vector<Case> Log2Cases() { | 
|  | auto error_msg = [] { return "12:34 error: log2 must be called with a value > 0"; }; | 
|  | return { | 
|  | C({T(1)}, T(0)),  // | 
|  | C({T(4)}, T(2)),  // | 
|  |  | 
|  | E({T::Lowest()}, error_msg()), | 
|  | E({T(0)}, error_msg()), | 
|  | E({-T(0)}, error_msg()), | 
|  | }; | 
|  | } | 
|  | INSTANTIATE_TEST_SUITE_P(  // | 
|  | Log2, | 
|  | ResolverConstEvalBuiltinTest, | 
|  | testing::Combine(testing::Values(builtin::Function::kLog2), | 
|  | testing::ValuesIn(Concat(Log2Cases<AFloat>(),  // | 
|  | Log2Cases<f32>(), | 
|  | Log2Cases<f16>())))); | 
|  | template <typename T> | 
|  | std::vector<Case> Log2F16Cases() { | 
|  | return { | 
|  | C({T::Highest()}, T(15.9922)).FloatComp(), | 
|  | }; | 
|  | } | 
|  | INSTANTIATE_TEST_SUITE_P(  // | 
|  | Log2F16, | 
|  | ResolverConstEvalBuiltinTest, | 
|  | testing::Combine(testing::Values(builtin::Function::kLog2), | 
|  | testing::ValuesIn(Log2F16Cases<f16>()))); | 
|  | template <typename T> | 
|  | std::vector<Case> Log2F32Cases() { | 
|  | return { | 
|  | C({T::Highest()}, T(128)).FloatComp(), | 
|  | }; | 
|  | } | 
|  | INSTANTIATE_TEST_SUITE_P(  // | 
|  | Log2F32, | 
|  | ResolverConstEvalBuiltinTest, | 
|  | testing::Combine(testing::Values(builtin::Function::kLog2), | 
|  | testing::ValuesIn(Log2F32Cases<f32>()))); | 
|  | template <typename T> | 
|  | std::vector<Case> Log2AbstractCases() { | 
|  | return { | 
|  | C({T::Highest()}, T(1024)).FloatComp(), | 
|  | }; | 
|  | } | 
|  | INSTANTIATE_TEST_SUITE_P(  // | 
|  | Log2Abstract, | 
|  | ResolverConstEvalBuiltinTest, | 
|  | testing::Combine(testing::Values(builtin::Function::kLog2), | 
|  | testing::ValuesIn(Log2AbstractCases<AFloat>()))); | 
|  |  | 
|  | template <typename T> | 
|  | std::vector<Case> MaxCases() { | 
|  | return { | 
|  | C({T(0), T(0)}, T(0)), | 
|  | C({T(0), T::Highest()}, T::Highest()), | 
|  | C({T::Lowest(), T(0)}, T(0)), | 
|  | C({T::Highest(), T::Lowest()}, T::Highest()), | 
|  | C({T::Highest(), T::Highest()}, T::Highest()), | 
|  | C({T::Lowest(), T::Lowest()}, T::Lowest()), | 
|  |  | 
|  | // Vector tests | 
|  | C({Vec(T(0), T(0)), Vec(T(0), T(42))}, Vec(T(0), T(42))), | 
|  | C({Vec(T::Lowest(), T(0)), Vec(T(0), T::Lowest())}, Vec(T(0), T(0))), | 
|  | C({Vec(T::Lowest(), T::Highest()), Vec(T::Highest(), T::Lowest())}, | 
|  | Vec(T::Highest(), T::Highest())), | 
|  | }; | 
|  | } | 
|  | INSTANTIATE_TEST_SUITE_P(  // | 
|  | Max, | 
|  | ResolverConstEvalBuiltinTest, | 
|  | testing::Combine(testing::Values(builtin::Function::kMax), | 
|  | testing::ValuesIn(Concat(MaxCases<AInt>(),  // | 
|  | MaxCases<i32>(), | 
|  | MaxCases<u32>(), | 
|  | MaxCases<AFloat>(), | 
|  | MaxCases<f32>(), | 
|  | MaxCases<f16>())))); | 
|  |  | 
|  | template <typename T> | 
|  | std::vector<Case> MinCases() { | 
|  | return {C({T(0), T(0)}, T(0)),                // | 
|  | C({T(0), T(42)}, T(0)),               // | 
|  | C({T::Lowest(), T(0)}, T::Lowest()),  // | 
|  | C({T(0), T::Highest()}, T(0)),        // | 
|  | C({T::Highest(), T::Lowest()}, T::Lowest()), | 
|  | C({T::Highest(), T::Highest()}, T::Highest()), | 
|  | C({T::Lowest(), T::Lowest()}, T::Lowest()), | 
|  |  | 
|  | // Vector tests | 
|  | C({Vec(T(0), T(0)), Vec(T(0), T(42))}, Vec(T(0), T(0))), | 
|  | C({Vec(T::Lowest(), T(0), T(1)), Vec(T(0), T(42), T::Highest())}, | 
|  | Vec(T::Lowest(), T(0), T(1)))}; | 
|  | } | 
|  | INSTANTIATE_TEST_SUITE_P(  // | 
|  | Min, | 
|  | ResolverConstEvalBuiltinTest, | 
|  | testing::Combine(testing::Values(builtin::Function::kMin), | 
|  | testing::ValuesIn(Concat(MinCases<AInt>(),  // | 
|  | MinCases<i32>(), | 
|  | MinCases<u32>(), | 
|  | MinCases<AFloat>(), | 
|  | MinCases<f32>(), | 
|  | MinCases<f16>())))); | 
|  |  | 
|  | template <typename T> | 
|  | std::vector<Case> MixCases() { | 
|  | auto r = std::vector<Case>{ | 
|  | C({T(0), T(1), T(0)}, T(0)),                         // | 
|  | C({T(0), T(1), T(1)}, T(1)),                         // | 
|  | C({T(0), T(1), T(2)}, T(2)),                         // | 
|  | C({T(0), T(1), T::Highest()}, T::Highest()),         // | 
|  | C({T::Lowest(), T::Highest(), T(1)}, T::Highest()),  // | 
|  | C({T::Lowest(), T::Highest(), T(0)}, T::Lowest()),   // | 
|  | C({T(0), T(1), T(0.25)}, T(0.25)),                   // | 
|  | C({T(0), T(1), T(0.5)}, T(0.5)),                     // | 
|  | C({T(0), T(1), T(0.75)}, T(0.75)),                   // | 
|  | C({T(0), T(1000), T(0.25)}, T(250)),                 // | 
|  | C({T(0), T(1000), T(0.5)}, T(500)),                  // | 
|  | C({T(0), T(1000), T(0.75)}, T(750)),                 // | 
|  | // Swap e1 and e2// | 
|  | C({T(1), T(0), T(0)}, T(1)),                         // | 
|  | C({T(1), T(0), T(1)}, T(0)),                         // | 
|  | C({T(1), T(0), T(2)}, T(-1)),                        // | 
|  | C({T::Highest(), T::Lowest(), T(1)}, T::Lowest()),   // | 
|  | C({T::Highest(), T::Lowest(), T(0)}, T::Highest()),  // | 
|  | C({T(1), T(0), T(0.25)}, T(0.75)),                   // | 
|  | C({T(1), T(0), T(0.5)}, T(0.5)),                     // | 
|  | C({T(1), T(0), T(0.75)}, T(0.25)),                   // | 
|  | C({T(1000), T(0), T(0.25)}, T(750)),                 // | 
|  | C({T(1000), T(0), T(0.5)}, T(500)),                  // | 
|  | C({T(1000), T(0), T(0.75)}, T(250)), | 
|  |  | 
|  | // mix(vec, vec, vec) cases | 
|  | C({Vec(T(0), T(0), T(0)),  // | 
|  | Vec(T(1), T(1), T(1)),  // | 
|  | Vec(T(0), T(1), T(2))}, | 
|  | Vec(T(0), T(1), T(2))), | 
|  |  | 
|  | // mix(vec, vec, scalar) cases | 
|  | C({Vec(T(0), T(1), T(0)),     // | 
|  | Vec(T(1), T(0), T(1000)),  // | 
|  | Val(T(0.25))}, | 
|  | Vec(T(0.25), T(0.75), T(250))), | 
|  | }; | 
|  | // Can't interpolate lowest value for f16 because (1 - lowest) is not representable as f16. | 
|  | if constexpr (!std::is_same_v<T, f16>) { | 
|  | ConcatInto(r, std::vector<Case>{ | 
|  | C({T(0), T(1), T::Lowest()}, T::Lowest()), | 
|  | C({T(1), T(0), T::Highest()}, T::Lowest()), | 
|  | }); | 
|  | } | 
|  |  | 
|  | auto error_msg = [](auto a, const char* op, auto b) { | 
|  | return "12:34 error: " + OverflowErrorMessage(a, op, b) + R"( | 
|  | 12:34 note: when calculating mix)"; | 
|  | }; | 
|  | auto kLargeValue = T{T::Highest() / 2}; | 
|  | // Test f16 separately as it overflows for a different reason at the boundary inputs. | 
|  | // Specifically, (1 - lowest) fails for f16 because the result is not representable. | 
|  | if constexpr (!std::is_same_v<T, f16>) { | 
|  | ConcatInto(  // | 
|  | r, | 
|  | std::vector<Case>{ | 
|  | E({T(0), T::Highest(), T::Highest()}, error_msg(T::Highest(), "*", T::Highest())), | 
|  | E({T(0), T::Lowest(), T::Lowest()}, error_msg(T::Lowest(), "*", T::Lowest())), | 
|  | E({T::Highest(), T(0), T::Lowest()}, error_msg(T::Highest(), "*", T::Highest())), | 
|  | E({-kLargeValue, kLargeValue, T(2)}, | 
|  | error_msg(T{-kLargeValue * T(1 - 2)}, "+", T{kLargeValue * T(2)})), | 
|  | }); | 
|  | } else { | 
|  | ConcatInto(  // | 
|  | r, | 
|  | std::vector<Case>{ | 
|  | E({T(0), T::Highest(), T::Highest()}, error_msg(T::Highest(), "*", T::Highest())), | 
|  | E({T(0), T::Lowest(), T::Lowest()}, error_msg(T(1), "-", T::Lowest())), | 
|  | E({T::Highest(), T(0), T::Lowest()}, error_msg(T(1), "-", T::Lowest())), | 
|  | E({-kLargeValue, kLargeValue, T(2)}, | 
|  | error_msg(T{-kLargeValue * T(1 - 2)}, "+", T{kLargeValue * T(2)})), | 
|  | }); | 
|  | } | 
|  |  | 
|  | return r; | 
|  | } | 
|  | INSTANTIATE_TEST_SUITE_P(  // | 
|  | Mix, | 
|  | ResolverConstEvalBuiltinTest, | 
|  | testing::Combine(testing::Values(builtin::Function::kMix), | 
|  | testing::ValuesIn(Concat(MixCases<AFloat>(),  // | 
|  | MixCases<f32>(),     // | 
|  | MixCases<f16>())))); | 
|  |  | 
|  | template <typename T> | 
|  | std::vector<Case> ModfCases() { | 
|  | return { | 
|  | // Scalar tests | 
|  | //  in     fract    whole | 
|  | C({T(0.0)}, {T(0.0), T(0.0)}),              // | 
|  | C({T(1.0)}, {T(0.0), T(1.0)}),              // | 
|  | C({T(2.0)}, {T(0.0), T(2.0)}),              // | 
|  | C({T(1.5)}, {T(0.5), T(1.0)}),              // | 
|  | C({T(4.25)}, {T(0.25), T(4.0)}),            // | 
|  | C({T(-1.0)}, {T(0.0), T(-1.0)}),            // | 
|  | C({T(-2.0)}, {T(0.0), T(-2.0)}),            // | 
|  | C({T(-1.5)}, {T(-0.5), T(-1.0)}),           // | 
|  | C({T(-4.25)}, {T(-0.25), T(-4.0)}),         // | 
|  | C({T::Lowest()}, {T(0.0), T::Lowest()}),    // | 
|  | C({T::Highest()}, {T(0.0), T::Highest()}),  // | 
|  |  | 
|  | // Vector tests | 
|  | //         in                 fract                    whole | 
|  | C({Vec(T(0.0), T(0.0))}, {Vec(T(0.0), T(0.0)), Vec(T(0.0), T(0.0))}), | 
|  | C({Vec(T(1.0), T(2.0))}, {Vec(T(0.0), T(0.0)), Vec(T(1), T(2))}), | 
|  | C({Vec(T(-2.0), T(1.0))}, {Vec(T(0.0), T(0.0)), Vec(T(-2), T(1))}), | 
|  | C({Vec(T(1.5), T(-2.25))}, {Vec(T(0.5), T(-0.25)), Vec(T(1.0), T(-2.0))}), | 
|  | C({Vec(T::Lowest(), T::Highest())}, {Vec(T(0.0), T(0.0)), Vec(T::Lowest(), T::Highest())}), | 
|  | }; | 
|  | } | 
|  | INSTANTIATE_TEST_SUITE_P(  // | 
|  | Modf, | 
|  | ResolverConstEvalBuiltinTest, | 
|  | testing::Combine(testing::Values(builtin::Function::kModf), | 
|  | testing::ValuesIn(Concat(ModfCases<AFloat>(),  // | 
|  | ModfCases<f32>(),     // | 
|  | ModfCases<f16>())))); | 
|  |  | 
|  | template <typename T> | 
|  | std::vector<Case> NormalizeCases() { | 
|  | auto error_msg = [&](auto a) { | 
|  | return "12:34 error: " + OverflowErrorMessage(a, "*", a) + R"( | 
|  | 12:34 note: when calculating normalize)"; | 
|  | }; | 
|  |  | 
|  | return { | 
|  | C({Vec(T(2), T(4), T(2))}, Vec(T(0.4082482905), T(0.8164965809), T(0.4082482905))) | 
|  | .FloatComp(), | 
|  |  | 
|  | C({Vec(T(2), T(0), T(0))}, Vec(T(1), T(0), T(0))), | 
|  | C({Vec(T(0), T(2), T(0))}, Vec(T(0), T(1), T(0))), | 
|  | C({Vec(T(0), T(0), T(2))}, Vec(T(0), T(0), T(1))), | 
|  | C({Vec(-T(2), T(0), T(0))}, Vec(-T(1), T(0), T(0))), | 
|  | C({Vec(T(0), -T(2), T(0))}, Vec(T(0), -T(1), T(0))), | 
|  | C({Vec(T(0), T(0), -T(2))}, Vec(T(0), T(0), -T(1))), | 
|  |  | 
|  | E({Vec(T(0), T(0), T(0))}, "12:34 error: zero length vector can not be normalized"), | 
|  | E({Vec(T::Highest(), T::Highest(), T::Highest())}, error_msg(T::Highest())), | 
|  | }; | 
|  | } | 
|  | INSTANTIATE_TEST_SUITE_P(  // | 
|  | Normalize, | 
|  | ResolverConstEvalBuiltinTest, | 
|  | testing::Combine(testing::Values(builtin::Function::kNormalize), | 
|  | testing::ValuesIn(Concat(NormalizeCases<AFloat>(),  // | 
|  | NormalizeCases<f32>(),     // | 
|  | NormalizeCases<f16>())))); | 
|  |  | 
|  | std::vector<Case> Pack4x8snormCases() { | 
|  | return { | 
|  | C({Vec(f32(0), f32(0), f32(0), f32(0))}, Val(u32(0x0000'0000))), | 
|  | C({Vec(f32(0), f32(0), f32(0), f32(-1))}, Val(u32(0x8100'0000))), | 
|  | C({Vec(f32(0), f32(0), f32(0), f32(1))}, Val(u32(0x7f00'0000))), | 
|  | C({Vec(f32(0), f32(0), f32(-1), f32(0))}, Val(u32(0x0081'0000))), | 
|  | C({Vec(f32(0), f32(1), f32(0), f32(0))}, Val(u32(0x0000'7f00))), | 
|  | C({Vec(f32(-1), f32(0), f32(0), f32(0))}, Val(u32(0x0000'0081))), | 
|  | C({Vec(f32(1), f32(-1), f32(1), f32(-1))}, Val(u32(0x817f'817f))), | 
|  | C({Vec(f32::Highest(), f32(-0.5), f32(0.5), f32::Lowest())}, Val(u32(0x8140'c17f))), | 
|  | }; | 
|  | } | 
|  | INSTANTIATE_TEST_SUITE_P(  // | 
|  | Pack4x8snorm, | 
|  | ResolverConstEvalBuiltinTest, | 
|  | testing::Combine(testing::Values(builtin::Function::kPack4X8Snorm), | 
|  | testing::ValuesIn(Pack4x8snormCases()))); | 
|  |  | 
|  | std::vector<Case> Pack4x8unormCases() { | 
|  | return { | 
|  | C({Vec(f32(0), f32(0), f32(0), f32(0))}, Val(u32(0x0000'0000))), | 
|  | C({Vec(f32(0), f32(0), f32(0), f32(1))}, Val(u32(0xff00'0000))), | 
|  | C({Vec(f32(0), f32(0), f32(1), f32(0))}, Val(u32(0x00ff'0000))), | 
|  | C({Vec(f32(0), f32(1), f32(0), f32(0))}, Val(u32(0x0000'ff00))), | 
|  | C({Vec(f32(1), f32(0), f32(0), f32(0))}, Val(u32(0x0000'00ff))), | 
|  | C({Vec(f32(1), f32(0), f32(1), f32(0))}, Val(u32(0x00ff'00ff))), | 
|  | C({Vec(f32::Highest(), f32(0), f32(0.5), f32::Lowest())}, Val(u32(0x0080'00ff))), | 
|  | }; | 
|  | } | 
|  | INSTANTIATE_TEST_SUITE_P(  // | 
|  | Pack4x8unorm, | 
|  | ResolverConstEvalBuiltinTest, | 
|  | testing::Combine(testing::Values(builtin::Function::kPack4X8Unorm), | 
|  | testing::ValuesIn(Pack4x8unormCases()))); | 
|  |  | 
|  | std::vector<Case> Pack2x16floatCases() { | 
|  | return { | 
|  | C({Vec(f32(f16::Lowest()), f32(f16::Highest()))}, Val(u32(0x7bff'fbff))), | 
|  | C({Vec(f32(1), f32(-1))}, Val(u32(0xbc00'3c00))), | 
|  | C({Vec(f32(0), f32(0))}, Val(u32(0x0000'0000))), | 
|  | C({Vec(f32(10), f32(-10.5))}, Val(u32(0xc940'4900))), | 
|  |  | 
|  | E({Vec(f32(0), f32::Highest())}, | 
|  | "12:34 error: value 340282346638528859811704183484516925440.0 cannot be " | 
|  | "represented as 'f16'"), | 
|  | E({Vec(f32::Lowest(), f32(0))}, | 
|  | "12:34 error: value -340282346638528859811704183484516925440.0 cannot be " | 
|  | "represented as 'f16'"), | 
|  | }; | 
|  | } | 
|  | INSTANTIATE_TEST_SUITE_P(  // | 
|  | Pack2x16float, | 
|  | ResolverConstEvalBuiltinTest, | 
|  | testing::Combine(testing::Values(builtin::Function::kPack2X16Float), | 
|  | testing::ValuesIn(Pack2x16floatCases()))); | 
|  |  | 
|  | std::vector<Case> Pack2x16snormCases() { | 
|  | return { | 
|  | C({Vec(f32(0), f32(0))}, Val(u32(0x0000'0000))), | 
|  | C({Vec(f32(0), f32(-1))}, Val(u32(0x8001'0000))), | 
|  | C({Vec(f32(0), f32(1))}, Val(u32(0x7fff'0000))), | 
|  | C({Vec(f32(-1), f32(0))}, Val(u32(0x0000'8001))), | 
|  | C({Vec(f32(1), f32(0))}, Val(u32(0x0000'7fff))), | 
|  | C({Vec(f32(1), f32(-1))}, Val(u32(0x8001'7fff))), | 
|  | C({Vec(f32::Highest(), f32::Lowest())}, Val(u32(0x8001'7fff))), | 
|  | C({Vec(f32(-0.5), f32(0.5))}, Val(u32(0x4000'c001))), | 
|  | }; | 
|  | } | 
|  | INSTANTIATE_TEST_SUITE_P(  // | 
|  | Pack2x16snorm, | 
|  | ResolverConstEvalBuiltinTest, | 
|  | testing::Combine(testing::Values(builtin::Function::kPack2X16Snorm), | 
|  | testing::ValuesIn(Pack2x16snormCases()))); | 
|  |  | 
|  | std::vector<Case> Pack2x16unormCases() { | 
|  | return { | 
|  | C({Vec(f32(0), f32(1))}, Val(u32(0xffff'0000))), | 
|  | C({Vec(f32(1), f32(0))}, Val(u32(0x0000'ffff))), | 
|  | C({Vec(f32(0.5), f32(0))}, Val(u32(0x0000'8000))), | 
|  | C({Vec(f32::Highest(), f32::Lowest())}, Val(u32(0x0000'ffff))), | 
|  | }; | 
|  | } | 
|  | INSTANTIATE_TEST_SUITE_P(  // | 
|  | Pack2x16unorm, | 
|  | ResolverConstEvalBuiltinTest, | 
|  | testing::Combine(testing::Values(builtin::Function::kPack2X16Unorm), | 
|  | testing::ValuesIn(Pack2x16unormCases()))); | 
|  |  | 
|  | template <typename T> | 
|  | std::vector<Case> PowCases() { | 
|  | auto error_msg = [](auto base, auto exp) { | 
|  | return "12:34 error: " + OverflowErrorMessage(base, "^", exp); | 
|  | }; | 
|  | return { | 
|  | C({T(0), T(1)}, T(0)),          // | 
|  | C({T(0), T::Highest()}, T(0)),  // | 
|  | C({T(1), T(1)}, T(1)),          // | 
|  | C({T(1), T::Lowest()}, T(1)),   // | 
|  | C({T(2), T(2)}, T(4)),          // | 
|  | C({T(2), T(3)}, T(8)),          // | 
|  | // Positive base, negative exponent | 
|  | C({T(1), T::Highest()}, T(1)),  // | 
|  | C({T(1), -T(1)}, T(1)),         // | 
|  | C({T(2), -T(2)}, T(0.25)),      // | 
|  | C({T(2), -T(3)}, T(0.125)),     // | 
|  | // Decimal values | 
|  | C({T(2.5), T(3)}, T(15.625)),                      // | 
|  | C({T(2), T(3.5)}, T(11.313708498)).FloatComp(),    // | 
|  | C({T(2.5), T(3.5)}, T(24.705294220)).FloatComp(),  // | 
|  | C({T(2), -T(3.5)}, T(0.0883883476)).FloatComp(),   // | 
|  |  | 
|  | // Vector tests | 
|  | C({Vec(T(0), T(1), T(2)), Vec(T(2), T(2), T(2))}, Vec(T(0), T(1), T(4))), | 
|  | C({Vec(T(2), T(2), T(2)), Vec(T(2), T(3), T(4))}, Vec(T(4), T(8), T(16))), | 
|  |  | 
|  | // Error if base < 0 | 
|  | E({-T(1), T(1)}, error_msg(-T(1), T(1))), | 
|  | E({-T(1), T::Highest()}, error_msg(-T(1), T::Highest())), | 
|  | E({T::Lowest(), T(1)}, error_msg(T::Lowest(), T(1))), | 
|  | E({T::Lowest(), T::Highest()}, error_msg(T::Lowest(), T::Highest())), | 
|  | E({T::Lowest(), T::Lowest()}, error_msg(T::Lowest(), T::Lowest())), | 
|  |  | 
|  | // Error if base == 0 and exp <= 0 | 
|  | E({T(0), T(0)}, error_msg(T(0), T(0))), | 
|  | E({T(0), -T(1)}, error_msg(T(0), -T(1))), | 
|  | E({T(0), T::Lowest()}, error_msg(T(0), T::Lowest())), | 
|  | }; | 
|  | } | 
|  | INSTANTIATE_TEST_SUITE_P(  // | 
|  | Pow, | 
|  | ResolverConstEvalBuiltinTest, | 
|  | testing::Combine(testing::Values(builtin::Function::kPow), | 
|  | testing::ValuesIn(Concat(PowCases<AFloat>(),  // | 
|  | PowCases<f32>(),     // | 
|  | PowCases<f16>())))); | 
|  |  | 
|  | template <typename T> | 
|  | std::vector<Case> ReverseBitsCases() { | 
|  | using B = BitValues<T>; | 
|  | return { | 
|  | C({T(0)}, T(0)), | 
|  |  | 
|  | C({B::Lsh(1, 0)}, B::Lsh(1, 31)),  // | 
|  | C({B::Lsh(1, 1)}, B::Lsh(1, 30)),  // | 
|  | C({B::Lsh(1, 2)}, B::Lsh(1, 29)),  // | 
|  | C({B::Lsh(1, 3)}, B::Lsh(1, 28)),  // | 
|  | C({B::Lsh(1, 4)}, B::Lsh(1, 27)),  // | 
|  | //... | 
|  | C({B::Lsh(1, 27)}, B::Lsh(1, 4)),  // | 
|  | C({B::Lsh(1, 28)}, B::Lsh(1, 3)),  // | 
|  | C({B::Lsh(1, 29)}, B::Lsh(1, 2)),  // | 
|  | C({B::Lsh(1, 30)}, B::Lsh(1, 1)),  // | 
|  | C({B::Lsh(1, 31)}, B::Lsh(1, 0)),  // | 
|  |  | 
|  | C({/**/ T(0b00010001000100010000000000000000)}, | 
|  | /* */ T(0b00000000000000001000100010001000)), | 
|  |  | 
|  | C({/**/ T(0b00011000000110000000000000000000)}, | 
|  | /* */ T(0b00000000000000000001100000011000)), | 
|  |  | 
|  | C({/**/ T(0b00000100000000001111111111111111)}, | 
|  | /* */ T(0b11111111111111110000000000100000)), | 
|  |  | 
|  | C({/**/ T(0b10010101111000110000011111101010)}, | 
|  | /* */ T(0b01010111111000001100011110101001)), | 
|  |  | 
|  | // Vector tests | 
|  | C({/**/ Vec(T(0b00010001000100010000000000000000),  // | 
|  | T(0b00011000000110000000000000000000),  // | 
|  | T(0b00000000000000001111111111111111))}, | 
|  | /* */ Vec(T(0b00000000000000001000100010001000),  // | 
|  | T(0b00000000000000000001100000011000),  // | 
|  | T(0b11111111111111110000000000000000))), | 
|  | }; | 
|  | } | 
|  | INSTANTIATE_TEST_SUITE_P(  // | 
|  | ReverseBits, | 
|  | ResolverConstEvalBuiltinTest, | 
|  | testing::Combine(testing::Values(builtin::Function::kReverseBits), | 
|  | testing::ValuesIn(Concat(ReverseBitsCases<i32>(),  // | 
|  | ReverseBitsCases<u32>())))); | 
|  |  | 
|  | template <typename T> | 
|  | std::vector<Case> ReflectCases() { | 
|  | auto pos_y = Vec(T(0), T(1), T(0)); | 
|  | auto neg_y = Vec(T(0), -T(1), T(0)); | 
|  | auto pos_large_y = Vec(T(0), T(10000), T(0)); | 
|  | auto neg_large_y = Vec(T(0), -T(10000), T(0)); | 
|  |  | 
|  | auto cos_45 = T(0.70710678118654752440084436210485); | 
|  | auto pos_xyz = Vec(cos_45, cos_45, cos_45); | 
|  |  | 
|  | auto r = std::vector<Case>{ | 
|  | C({Vec(T(1), -T(1), T(0)), pos_y}, Vec(T(1), T(1), T(0))), | 
|  | C({Vec(T(24), -T(42), T(0)), pos_y}, Vec(T(24), T(42), T(0))), | 
|  | // Flipping reflection vector doesn't change the result | 
|  | C({Vec(T(1), -T(1), T(0)), neg_y}, Vec(T(1), T(1), T(0))), | 
|  | C({Vec(T(24), -T(42), T(0)), neg_y}, Vec(T(24), T(42), T(0))), | 
|  | // Parallel input and reflection vectors: result is negation of input | 
|  | C({pos_y, pos_y}, neg_y), | 
|  | C({neg_y, pos_y}, pos_y), | 
|  | C({pos_large_y, pos_y}, neg_large_y), | 
|  | C({neg_large_y, pos_y}, pos_large_y), | 
|  | // Input axis vectors reflected by normalized(vec(1,1,1)) vector. | 
|  | C({Vec(T(1), T(0), T(0)), pos_xyz}, Vec(T(0), -T(1), -T(1))).FloatComp(0.02), | 
|  | C({Vec(T(0), T(1), T(0)), pos_xyz}, Vec(-T(1), T(0), -T(1))).FloatComp(0.02), | 
|  | C({Vec(T(0), T(0), T(1)), pos_xyz}, Vec(-T(1), -T(1), T(0))).FloatComp(0.02), | 
|  | C({Vec(-T(1), T(0), T(0)), pos_xyz}, Vec(T(0), T(1), T(1))).FloatComp(0.02), | 
|  | C({Vec(T(0), -T(1), T(0)), pos_xyz}, Vec(T(1), T(0), T(1))).FloatComp(0.02), | 
|  | C({Vec(T(0), T(0), -T(1)), pos_xyz}, Vec(T(1), T(1), T(0))).FloatComp(0.02), | 
|  | }; | 
|  |  | 
|  | auto error_msg = [](auto a, const char* op, auto b) { | 
|  | return "12:34 error: " + OverflowErrorMessage(a, op, b) + R"( | 
|  | 12:34 note: when calculating reflect)"; | 
|  | }; | 
|  | ConcatInto(  // | 
|  | r, std::vector<Case>{ | 
|  | // Overflow the dot product operation | 
|  | E({Vec(T::Highest(), T::Highest(), T(0)), Vec(T(1), T(1), T(0))}, | 
|  | error_msg(T::Highest(), "+", T::Highest())), | 
|  | E({Vec(T::Lowest(), T::Lowest(), T(0)), Vec(T(1), T(1), T(0))}, | 
|  | error_msg(T::Lowest(), "+", T::Lowest())), | 
|  | }); | 
|  |  | 
|  | return r; | 
|  | } | 
|  | INSTANTIATE_TEST_SUITE_P(  // | 
|  | Reflect, | 
|  | ResolverConstEvalBuiltinTest, | 
|  | testing::Combine(testing::Values(builtin::Function::kReflect), | 
|  | testing::ValuesIn(Concat(ReflectCases<AFloat>(),  // | 
|  | ReflectCases<f32>(),     // | 
|  | ReflectCases<f16>())))); | 
|  |  | 
|  | template <typename T> | 
|  | std::vector<Case> RefractCases() { | 
|  | // Returns "eta" (Greek letter) that denotes the ratio of indices of refraction for the input | 
|  | // and output vector angles from the normal vector. | 
|  | auto eta = [](auto angle1, auto angle2) { | 
|  | // Snell's law: sin(angle1) / sin(angle2) == n2 / n1 | 
|  | // We want the ratio of n1 to n2, so sin(angle2) / sin(angle1) | 
|  | auto angle1_rads = T(angle1) * kPi<T> / T(180); | 
|  | auto angle2_rads = T(angle2) * kPi<T> / T(180); | 
|  | return T(std::sin(angle2_rads) / std::sin(angle1_rads)); | 
|  | }; | 
|  |  | 
|  | auto zero = Vec(T(0), T(0), T(0)); | 
|  | auto pos_y = Vec(T(0), T(1), T(0)); | 
|  | auto neg_y = Vec(T(0), -T(1), T(0)); | 
|  | auto pos_x = Vec(T(1), T(0), T(0)); | 
|  | auto neg_x = Vec(-T(1), T(0), T(0)); | 
|  | auto cos_45 = T(0.70710678118654752440084436210485); | 
|  | auto cos_30 = T(0.86602540378443864676372317075294); | 
|  | auto down_right = Vec(T(cos_45), -T(cos_45), T(0)); | 
|  | auto up_right = Vec(T(cos_45), T(cos_45), T(0)); | 
|  |  | 
|  | auto eps = 0.001; | 
|  | if constexpr (std::is_same_v<T, f16>) { | 
|  | eps = 0.1; | 
|  | } | 
|  |  | 
|  | auto r = std::vector<Case>{ | 
|  | // e3 (eta) == 1, no refraction, so input is same as output | 
|  | C({down_right, pos_y, Val(T(1))}, down_right), | 
|  | C({neg_y, pos_y, Val(T(1))}, neg_y), | 
|  | // Varying etas | 
|  | C({down_right, pos_y, Val(eta(45, 45))}, down_right).FloatComp(eps),  // e3 == 1 | 
|  | C({down_right, pos_y, Val(eta(45, 30))}, Vec(T(0.5), -T(cos_30), T(0))).FloatComp(eps), | 
|  | C({down_right, pos_y, Val(eta(45, 60))}, Vec(T(cos_30), -T(0.5), T(0))).FloatComp(eps), | 
|  | C({down_right, pos_y, Val(eta(45, 90))}, Vec(T(1), T(0), T(0))).FloatComp(eps), | 
|  | // Flip input and normal, same result | 
|  | C({up_right, neg_y, Val(eta(45, 45))}, up_right).FloatComp(eps),  // e3 == 1 | 
|  | C({up_right, neg_y, Val(eta(45, 30))}, Vec(T(0.5), T(cos_30), T(0))).FloatComp(eps), | 
|  | C({up_right, neg_y, Val(eta(45, 60))}, Vec(T(cos_30), T(0.5), T(0))).FloatComp(eps), | 
|  | C({up_right, neg_y, Val(eta(45, 90))}, Vec(T(1), T(0), T(0))).FloatComp(eps), | 
|  | // Flip only normal, result is flipped | 
|  | C({down_right, neg_y, Val(eta(45, 45))}, up_right).FloatComp(eps),  // e3 == 1 | 
|  | C({down_right, neg_y, Val(eta(45, 30))}, Vec(T(0.5), T(cos_30), T(0))).FloatComp(eps), | 
|  | C({down_right, neg_y, Val(eta(45, 60))}, Vec(T(cos_30), T(0.5), T(0))).FloatComp(eps), | 
|  | C({down_right, neg_y, Val(eta(45, 90))}, Vec(T(1), T(0), T(0))).FloatComp(eps), | 
|  |  | 
|  | // If k < 0.0, returns the refraction vector 0.0 | 
|  | C({down_right, pos_y, Val(T(2))}, zero).FloatComp(eps), | 
|  |  | 
|  | // A few more with a different normal (e2) | 
|  | C({down_right, neg_x, Val(eta(45, 45))}, down_right).FloatComp(eps),  // e3 == 1 | 
|  | C({down_right, neg_x, Val(eta(45, 30))}, Vec(cos_30, -T(0.5), T(0))).FloatComp(eps), | 
|  | C({down_right, neg_x, Val(eta(45, 60))}, Vec(T(0.5), -T(cos_30), T(0))).FloatComp(eps), | 
|  | }; | 
|  |  | 
|  | auto error_msg = [](auto a, const char* op, auto b) { | 
|  | return "12:34 error: " + OverflowErrorMessage(a, op, b) + R"( | 
|  | 12:34 note: when calculating refract)"; | 
|  | }; | 
|  | ConcatInto(  // | 
|  | r, | 
|  | std::vector<Case>{ | 
|  | // Overflow the dot product operation | 
|  | E({Vec(T::Highest(), T::Highest(), T(0)), Vec(T(1), T(1), T(0)), Val(T(1))}, | 
|  | error_msg(T::Highest(), "+", T::Highest())), | 
|  | E({Vec(T::Lowest(), T::Lowest(), T(0)), Vec(T(1), T(1), T(0)), Val(T(1))}, | 
|  | error_msg(T::Lowest(), "+", T::Lowest())), | 
|  | // Overflow the k^2 operation | 
|  | E({down_right, pos_y, Val(T::Highest())}, error_msg(T::Highest(), "*", T::Highest())), | 
|  | }); | 
|  | ConcatIntoIf<std::is_same_v<T, f32>>(  // | 
|  | r, std::vector<Case>{ | 
|  | // Overflow the final multiply by e2 operation | 
|  | // From https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=58526 | 
|  | E({Vec(T(-2.22218755e-15), T(0)), Vec(T(-198225753253481323832809619456.0), T(0)), | 
|  | Val(T(40.0313720703125))}, | 
|  | error_msg(T(35267222007971840.0), "*", T(-198225753253481323832809619456.0))), | 
|  | }); | 
|  |  | 
|  | return r; | 
|  | } | 
|  | INSTANTIATE_TEST_SUITE_P(  // | 
|  | Refract, | 
|  | ResolverConstEvalBuiltinTest, | 
|  | testing::Combine(testing::Values(builtin::Function::kRefract), | 
|  | testing::ValuesIn(Concat(RefractCases<AFloat>(),  // | 
|  | RefractCases<f32>(),     // | 
|  | RefractCases<f16>())))); | 
|  |  | 
|  | template <typename T> | 
|  | std::vector<Case> RadiansCases() { | 
|  | return { | 
|  | C({T(0)}, T(0)),                         // | 
|  | C({-T(0)}, -T(0)),                       // | 
|  | C({T(40)}, T(0.69813168)).FloatComp(),   // | 
|  | C({-T(90)}, -T(1.5707964)).FloatComp(),  // | 
|  | C({T(90)}, T(1.5707964)).FloatComp(),    // | 
|  | C({T(360)}, T(6.2831855)).FloatComp(), | 
|  | }; | 
|  | } | 
|  | INSTANTIATE_TEST_SUITE_P(  // | 
|  | Radians, | 
|  | ResolverConstEvalBuiltinTest, | 
|  | testing::Combine(testing::Values(builtin::Function::kRadians), | 
|  | testing::ValuesIn(Concat(RadiansCases<AFloat>(),  // | 
|  | RadiansCases<f32>())))); | 
|  |  | 
|  | template <typename T> | 
|  | std::vector<Case> RadiansF16Cases() { | 
|  | return { | 
|  | C({T(0)}, T(0)),                         // | 
|  | C({-T(0)}, -T(0)),                       // | 
|  | C({T(40)}, T(0.69726562)).FloatComp(),   // | 
|  | C({-T(90)}, -T(1.5693359)).FloatComp(),  // | 
|  | C({T(90)}, T(1.5693359)).FloatComp(),    // | 
|  | C({T(360)}, T(6.2773438)).FloatComp(), | 
|  | }; | 
|  | } | 
|  | INSTANTIATE_TEST_SUITE_P(  // | 
|  | RadiansF16, | 
|  | ResolverConstEvalBuiltinTest, | 
|  | testing::Combine(testing::Values(builtin::Function::kRadians), | 
|  | testing::ValuesIn(RadiansF16Cases<f16>()))); | 
|  |  | 
|  | template <typename T> | 
|  | std::vector<Case> RoundCases() { | 
|  | return { | 
|  | C({T(0.0)}, T(0.0)),      // | 
|  | C({-T(0.0)}, -T(0.0)),    // | 
|  | C({T(1.5)}, T(2.0)),      // | 
|  | C({T(2.5)}, T(2.0)),      // | 
|  | C({T(2.4)}, T(2.0)),      // | 
|  | C({T(2.6)}, T(3.0)),      // | 
|  | C({T(1.49999)}, T(1.0)),  // | 
|  | C({T(1.50001)}, T(2.0)),  // | 
|  | C({-T(1.5)}, -T(2.0)),    // | 
|  | C({-T(2.5)}, -T(2.0)),    // | 
|  | C({-T(2.6)}, -T(3.0)),    // | 
|  | C({-T(2.4)}, -T(2.0)),    // | 
|  |  | 
|  | // Vector tests | 
|  | C({Vec(T(0.0), T(1.5), T(2.5))}, Vec(T(0.0), T(2.0), T(2.0))), | 
|  | }; | 
|  | } | 
|  | INSTANTIATE_TEST_SUITE_P(  // | 
|  | Round, | 
|  | ResolverConstEvalBuiltinTest, | 
|  | testing::Combine(testing::Values(builtin::Function::kRound), | 
|  | testing::ValuesIn(Concat(RoundCases<AFloat>(),  // | 
|  | RoundCases<f32>(), | 
|  | RoundCases<f16>())))); | 
|  |  | 
|  | template <typename T> | 
|  | std::vector<Case> SaturateCases() { | 
|  | return { | 
|  | C({T(0)}, T(0)), | 
|  | C({T(1)}, T(1)), | 
|  | C({T::Lowest()}, T(0)), | 
|  | C({T::Highest()}, T(1)), | 
|  |  | 
|  | // Vector tests | 
|  | C({Vec(T(0), T(0))},                       // | 
|  | Vec(T(0), T(0))),                        // | 
|  | C({Vec(T(1), T(1))},                       // | 
|  | Vec(T(1), T(1))),                        // | 
|  | C({Vec(T::Lowest(), T(0), T::Highest())},  // | 
|  | Vec(T(0), T(0), T(1))), | 
|  | }; | 
|  | } | 
|  | INSTANTIATE_TEST_SUITE_P(  // | 
|  | Saturate, | 
|  | ResolverConstEvalBuiltinTest, | 
|  | testing::Combine(testing::Values(builtin::Function::kSaturate), | 
|  | testing::ValuesIn(Concat(SaturateCases<AFloat>(),  // | 
|  | SaturateCases<f32>(), | 
|  | SaturateCases<f16>())))); | 
|  |  | 
|  | template <typename T> | 
|  | std::vector<Case> SelectCases() { | 
|  | return { | 
|  | C({Val(T{1}), Val(T{2}), Val(false)}, Val(T{1})), | 
|  | C({Val(T{1}), Val(T{2}), Val(true)}, Val(T{2})), | 
|  |  | 
|  | C({Val(T{2}), Val(T{1}), Val(false)}, Val(T{2})), | 
|  | C({Val(T{2}), Val(T{1}), Val(true)}, Val(T{1})), | 
|  |  | 
|  | C({Vec(T{1}, T{2}), Vec(T{3}, T{4}), Vec(false, false)}, Vec(T{1}, T{2})), | 
|  | C({Vec(T{1}, T{2}), Vec(T{3}, T{4}), Vec(false, true)}, Vec(T{1}, T{4})), | 
|  | C({Vec(T{1}, T{2}), Vec(T{3}, T{4}), Vec(true, false)}, Vec(T{3}, T{2})), | 
|  | C({Vec(T{1}, T{2}), Vec(T{3}, T{4}), Vec(true, true)}, Vec(T{3}, T{4})), | 
|  |  | 
|  | C({Vec(T{1}, T{1}, T{2}, T{2}),     // | 
|  | Vec(T{2}, T{2}, T{1}, T{1}),     // | 
|  | Vec(false, true, false, true)},  // | 
|  | Vec(T{1}, T{2}, T{2}, T{1})),     // | 
|  | }; | 
|  | } | 
|  | static std::vector<Case> SelectBoolCases() { | 
|  | return { | 
|  | C({Val(true), Val(false), Val(false)}, Val(true)), | 
|  | C({Val(true), Val(false), Val(true)}, Val(false)), | 
|  |  | 
|  | C({Val(false), Val(true), Val(true)}, Val(true)), | 
|  | C({Val(false), Val(true), Val(false)}, Val(false)), | 
|  |  | 
|  | C({Vec(true, true, false, false),   // | 
|  | Vec(false, false, true, true),   // | 
|  | Vec(false, true, true, false)},  // | 
|  | Vec(true, false, true, false)),   // | 
|  | }; | 
|  | } | 
|  | INSTANTIATE_TEST_SUITE_P(  // | 
|  | Select, | 
|  | ResolverConstEvalBuiltinTest, | 
|  | testing::Combine(testing::Values(builtin::Function::kSelect), | 
|  | testing::ValuesIn(Concat(SelectCases<AInt>(),  // | 
|  | SelectCases<i32>(), | 
|  | SelectCases<u32>(), | 
|  | SelectCases<AFloat>(), | 
|  | SelectCases<f32>(), | 
|  | SelectCases<f16>(), | 
|  | SelectBoolCases())))); | 
|  |  | 
|  | template <typename T> | 
|  | std::vector<Case> SignCases() { | 
|  | std::vector<Case> cases = { | 
|  | C({T(0)}, T(0)), | 
|  | C({-T(0)}, T(0)), | 
|  |  | 
|  | C({-T(1)}, -T(1)), | 
|  | C({-T(10)}, -T(1)), | 
|  | C({-T(100)}, -T(1)), | 
|  | C({T(1)}, T(1)), | 
|  | C({T(10)}, T(1)), | 
|  | C({T(100)}, T(1)), | 
|  |  | 
|  | C({T::Highest()}, T(1.0)), | 
|  | C({T::Lowest()}, -T(1.0)), | 
|  |  | 
|  | // Vector tests | 
|  | C({Vec(T::Highest(), T::Lowest())}, Vec(T(1.0), -T(1.0))), | 
|  | }; | 
|  |  | 
|  | ConcatIntoIf<IsFloatingPoint<T>>( | 
|  | cases, std::vector<Case>{ | 
|  | C({-T(0.5)}, -T(1)), | 
|  | C({T(0.5)}, T(1)), | 
|  | C({Vec(-T(0.5), T(0), T(0.5))}, Vec(-T(1.0), T(0.0), T(1.0))), | 
|  | }); | 
|  |  | 
|  | return cases; | 
|  | } | 
|  | INSTANTIATE_TEST_SUITE_P(  // | 
|  | Sign, | 
|  | ResolverConstEvalBuiltinTest, | 
|  | testing::Combine(testing::Values(builtin::Function::kSign), | 
|  | testing::ValuesIn(Concat(SignCases<AInt>(),  // | 
|  | SignCases<i32>(), | 
|  | SignCases<AFloat>(), | 
|  | SignCases<f32>(), | 
|  | SignCases<f16>())))); | 
|  |  | 
|  | template <typename T> | 
|  | std::vector<Case> SinCases() { | 
|  | return { | 
|  | C({-T(0)}, -T(0)), | 
|  | C({T(0)}, T(0)), | 
|  | C({T(0.75)}, T(0.68163876)).FloatComp(), | 
|  | C({-T(0.75)}, -T(0.68163876)).FloatComp(), | 
|  |  | 
|  | // Vector test | 
|  | C({Vec(T(0), -T(0), T(0.75))}, Vec(T(0), -T(0), T(0.68163876))).FloatComp(), | 
|  | }; | 
|  | } | 
|  | INSTANTIATE_TEST_SUITE_P(  // | 
|  | Sin, | 
|  | ResolverConstEvalBuiltinTest, | 
|  | testing::Combine(testing::Values(builtin::Function::kSin), | 
|  | testing::ValuesIn(Concat(SinCases<AFloat>(),  // | 
|  | SinCases<f32>(), | 
|  | SinCases<f16>())))); | 
|  |  | 
|  | template <typename T> | 
|  | std::vector<Case> SinhCases() { | 
|  | auto error_msg = [](auto a) { | 
|  | return "12:34 error: " + OverflowErrorMessage(a, FriendlyName<decltype(a)>()); | 
|  | }; | 
|  | return { | 
|  | C({T(0)}, T(0)), | 
|  | C({-T(0)}, -T(0)), | 
|  | C({T(1)}, T(1.1752012)).FloatComp(), | 
|  | C({T(-1)}, -T(1.1752012)).FloatComp(), | 
|  |  | 
|  | // Vector tests | 
|  | C({Vec(T(0), -T(0), T(1))}, Vec(T(0), -T(0), T(1.1752012))).FloatComp(), | 
|  |  | 
|  | E({T(10000)}, error_msg(T::Inf())), | 
|  | }; | 
|  | } | 
|  | INSTANTIATE_TEST_SUITE_P(  // | 
|  | Sinh, | 
|  | ResolverConstEvalBuiltinTest, | 
|  | testing::Combine(testing::Values(builtin::Function::kSinh), | 
|  | testing::ValuesIn(Concat(SinhCases<AFloat>(),  // | 
|  | SinhCases<f32>(), | 
|  | SinhCases<f16>())))); | 
|  |  | 
|  | template <typename T> | 
|  | std::vector<Case> SmoothstepCases() { | 
|  | auto error_msg = [](auto a, const char* op, auto b) { | 
|  | return "12:34 error: " + OverflowErrorMessage(a, op, b) + R"( | 
|  | 12:34 note: when calculating smoothstep)"; | 
|  | }; | 
|  | return { | 
|  | // t == 0 | 
|  | C({T(4), T(6), T(2)}, T(0)), | 
|  | // t == 1 | 
|  | C({T(4), T(6), T(8)}, T(1)), | 
|  | // t == .5 | 
|  | C({T(4), T(6), T(5)}, T(.5)), | 
|  |  | 
|  | // Vector tests | 
|  | C({Vec(T(4), T(4)), Vec(T(6), T(6)), Vec(T(2), T(8))}, Vec(T(0), T(1))), | 
|  |  | 
|  | // `x - low` underflows | 
|  | E({T::Highest(), T(1), T::Lowest()}, error_msg(T::Lowest(), "-", T::Highest())), | 
|  | // `high - low` underflows | 
|  | E({T::Highest(), T::Lowest(), T(0)}, error_msg(T::Lowest(), "-", T::Highest())), | 
|  | // Divide by zero on `(x - low) / (high - low)` | 
|  | E({T(0), T(0), T(0)}, error_msg(T(0), "/", T(0))), | 
|  | }; | 
|  | } | 
|  | INSTANTIATE_TEST_SUITE_P(  // | 
|  | Smoothstep, | 
|  | ResolverConstEvalBuiltinTest, | 
|  | testing::Combine(testing::Values(builtin::Function::kSmoothstep), | 
|  | testing::ValuesIn(Concat(SmoothstepCases<AFloat>(),  // | 
|  | SmoothstepCases<f32>(), | 
|  | SmoothstepCases<f16>())))); | 
|  |  | 
|  | template <typename T> | 
|  | std::vector<Case> StepCases() { | 
|  | return { | 
|  | C({T(0), T(0)}, T(1.0)), | 
|  | C({T(0), T(0.5)}, T(1.0)), | 
|  | C({T(0.5), T(0)}, T(0.0)), | 
|  | C({T(1), T(0.5)}, T(0.0)), | 
|  | C({T(0.5), T(1)}, T(1.0)), | 
|  | C({T(1.5), T(1)}, T(0.0)), | 
|  | C({T(1), T(1.5)}, T(1.0)), | 
|  | C({T(-1), T(1)}, T(1.0)), | 
|  | C({T(-1), T(1)}, T(1.0)), | 
|  | C({T(1), T(-1)}, T(0.0)), | 
|  | C({T(-1), T(-1.5)}, T(0.0)), | 
|  | C({T(-1.5), T(-1)}, T(1.0)), | 
|  | C({T::Highest(), T::Lowest()}, T(0.0)), | 
|  | C({T::Lowest(), T::Highest()}, T(1.0)), | 
|  |  | 
|  | // Vector tests | 
|  | C({Vec(T(0), T(0)), Vec(T(0), T(0))}, Vec(T(1.0), T(1.0))), | 
|  | C({Vec(T(-1), T(1)), Vec(T(0), T(0))}, Vec(T(1.0), T(0.0))), | 
|  | C({Vec(T::Highest(), T::Lowest()), Vec(T::Lowest(), T::Highest())}, Vec(T(0.0), T(1.0))), | 
|  | }; | 
|  | } | 
|  | INSTANTIATE_TEST_SUITE_P(  // | 
|  | Step, | 
|  | ResolverConstEvalBuiltinTest, | 
|  | testing::Combine(testing::Values(builtin::Function::kStep), | 
|  | testing::ValuesIn(Concat(StepCases<AFloat>(),  // | 
|  | StepCases<f32>(), | 
|  | StepCases<f16>())))); | 
|  |  | 
|  | template <typename T> | 
|  | std::vector<Case> SqrtCases() { | 
|  | return { | 
|  | C({-T(0)}, -T(0)),  // | 
|  | C({T(0)}, T(0)),    // | 
|  | C({T(25)}, T(5)), | 
|  |  | 
|  | // Vector tests | 
|  | C({Vec(T(25), T(100))}, Vec(T(5), T(10))), | 
|  |  | 
|  | E({-T(25)}, "12:34 error: sqrt must be called with a value >= 0"), | 
|  | }; | 
|  | } | 
|  | INSTANTIATE_TEST_SUITE_P(  // | 
|  | Sqrt, | 
|  | ResolverConstEvalBuiltinTest, | 
|  | testing::Combine(testing::Values(builtin::Function::kSqrt), | 
|  | testing::ValuesIn(Concat(SqrtCases<AFloat>(),  // | 
|  | SqrtCases<f32>(), | 
|  | SqrtCases<f16>())))); | 
|  |  | 
|  | template <typename T> | 
|  | std::vector<Case> TanCases() { | 
|  | return { | 
|  | C({-T(0)}, -T(0)), | 
|  | C({T(0)}, T(0)), | 
|  | C({T(.75)}, T(0.9315964599)).FloatComp(), | 
|  |  | 
|  | // Vector test | 
|  | C({Vec(T(0), -T(0), T(.75))}, Vec(T(0), -T(0), T(0.9315964599))).FloatComp(), | 
|  | }; | 
|  | } | 
|  | INSTANTIATE_TEST_SUITE_P(  // | 
|  | Tan, | 
|  | ResolverConstEvalBuiltinTest, | 
|  | testing::Combine(testing::Values(builtin::Function::kTan), | 
|  | testing::ValuesIn(Concat(TanCases<AFloat>(),  // | 
|  | TanCases<f32>(), | 
|  | TanCases<f16>())))); | 
|  |  | 
|  | template <typename T> | 
|  | std::vector<Case> TanhCases() { | 
|  | return { | 
|  | C({T(0)}, T(0)), | 
|  | C({-T(0)}, -T(0)), | 
|  | C({T(1)}, T(0.761594156)).FloatComp(), | 
|  | C({T(-1)}, -T(0.761594156)).FloatComp(), | 
|  |  | 
|  | // Vector tests | 
|  | C({Vec(T(0), -T(0), T(1))}, Vec(T(0), -T(0), T(0.761594156))).FloatComp(), | 
|  | }; | 
|  | } | 
|  | INSTANTIATE_TEST_SUITE_P(  // | 
|  | Tanh, | 
|  | ResolverConstEvalBuiltinTest, | 
|  | testing::Combine(testing::Values(builtin::Function::kTanh), | 
|  | testing::ValuesIn(Concat(TanhCases<AFloat>(),  // | 
|  | TanhCases<f32>(), | 
|  | TanhCases<f16>())))); | 
|  |  | 
|  | template <typename T> | 
|  | std::vector<Case> TransposeCases() { | 
|  | return { | 
|  | // 2x2 | 
|  | C({Mat({T(1), T(2)},    // | 
|  | {T(3), T(4)})},  // | 
|  | Mat({T(1), T(3)},     // | 
|  | {T(2), T(4)})), | 
|  |  | 
|  | // 3x3 | 
|  | C({Mat({T(1), T(2), T(3)},    // | 
|  | {T(4), T(5), T(6)},    // | 
|  | {T(7), T(8), T(9)})},  // | 
|  | Mat({T(1), T(4), T(7)},     // | 
|  | {T(2), T(5), T(8)},     // | 
|  | {T(3), T(6), T(9)})), | 
|  |  | 
|  | // 4x4 | 
|  | C({Mat({T(1), T(2), T(3), T(4)},        // | 
|  | {T(5), T(6), T(7), T(8)},        // | 
|  | {T(9), T(10), T(11), T(12)},     // | 
|  | {T(13), T(14), T(15), T(16)})},  // | 
|  | Mat({T(1), T(5), T(9), T(13)},        // | 
|  | {T(2), T(6), T(10), T(14)},       // | 
|  | {T(3), T(7), T(11), T(15)},       // | 
|  | {T(4), T(8), T(12), T(16)})), | 
|  |  | 
|  | // 4x2 | 
|  | C({Mat({T(1), T(2), T(3), T(4)},    // | 
|  | {T(5), T(6), T(7), T(8)})},  // | 
|  | Mat({T(1), T(5)},                 // | 
|  | {T(2), T(6)},                 // | 
|  | {T(3), T(7)},                 // | 
|  | {T(4), T(8)})), | 
|  |  | 
|  | // 2x4 | 
|  | C({Mat({T(1), T(2)},             // | 
|  | {T(3), T(4)},             // | 
|  | {T(5), T(6)},             // | 
|  | {T(7), T(8)})},           // | 
|  | Mat({T(1), T(3), T(5), T(7)},  // | 
|  | {T(2), T(4), T(6), T(8)})), | 
|  |  | 
|  | // 3x2 | 
|  | C({Mat({T(1), T(2), T(3)},    // | 
|  | {T(4), T(5), T(6)})},  // | 
|  | Mat({T(1), T(4)},           // | 
|  | {T(2), T(5)},           // | 
|  | {T(3), T(6)})), | 
|  |  | 
|  | // 2x3 | 
|  | C({Mat({T(1), T(2)},       // | 
|  | {T(3), T(4)},       // | 
|  | {T(5), T(6)})},     // | 
|  | Mat({T(1), T(3), T(5)},  // | 
|  | {T(2), T(4), T(6)})), | 
|  | }; | 
|  | } | 
|  | INSTANTIATE_TEST_SUITE_P(  // | 
|  | Transpose, | 
|  | ResolverConstEvalBuiltinTest, | 
|  | testing::Combine(testing::Values(builtin::Function::kTranspose), | 
|  | testing::ValuesIn(Concat(TransposeCases<AFloat>(),  // | 
|  | TransposeCases<f32>(), | 
|  | TransposeCases<f16>())))); | 
|  |  | 
|  | template <typename T> | 
|  | std::vector<Case> TruncCases() { | 
|  | return {C({T(0)}, T(0)),    // | 
|  | C({-T(0)}, -T(0)),  // | 
|  | C({T(1.5)}, T(1)),  // | 
|  | C({-T(1.5)}, -T(1)), | 
|  |  | 
|  | // Vector tests | 
|  | C({Vec(T(0.0), T(1.5), -T(2.2))}, Vec(T(0), T(1), -T(2)))}; | 
|  | } | 
|  | INSTANTIATE_TEST_SUITE_P(  // | 
|  | Trunc, | 
|  | ResolverConstEvalBuiltinTest, | 
|  | testing::Combine(testing::Values(builtin::Function::kTrunc), | 
|  | testing::ValuesIn(Concat(TruncCases<AFloat>(),  // | 
|  | TruncCases<f32>(), | 
|  | TruncCases<f16>())))); | 
|  |  | 
|  | std::vector<Case> Unpack4x8snormCases() { | 
|  | return { | 
|  | C({Val(u32(0x0000'0000))}, Vec(f32(0), f32(0), f32(0), f32(0))), | 
|  | C({Val(u32(0x8100'0000))}, Vec(f32(0), f32(0), f32(0), f32(-1))), | 
|  | C({Val(u32(0x7f00'0000))}, Vec(f32(0), f32(0), f32(0), f32(1))), | 
|  | C({Val(u32(0x0081'0000))}, Vec(f32(0), f32(0), f32(-1), f32(0))), | 
|  | C({Val(u32(0x0000'7f00))}, Vec(f32(0), f32(1), f32(0), f32(0))), | 
|  | C({Val(u32(0x0000'0081))}, Vec(f32(-1), f32(0), f32(0), f32(0))), | 
|  | C({Val(u32(0x817f'817f))}, Vec(f32(1), f32(-1), f32(1), f32(-1))), | 
|  | C({Val(u32(0x816d'937f))}, | 
|  | Vec(f32(1), f32(-0.8582677165354), f32(0.8582677165354), f32(-1))), | 
|  | }; | 
|  | } | 
|  | INSTANTIATE_TEST_SUITE_P(  // | 
|  | Unpack4x8snorm, | 
|  | ResolverConstEvalBuiltinTest, | 
|  | testing::Combine(testing::Values(builtin::Function::kUnpack4X8Snorm), | 
|  | testing::ValuesIn(Unpack4x8snormCases()))); | 
|  |  | 
|  | std::vector<Case> Unpack4x8unormCases() { | 
|  | return { | 
|  | C({Val(u32(0x0000'0000))}, Vec(f32(0), f32(0), f32(0), f32(0))), | 
|  | C({Val(u32(0xff00'0000))}, Vec(f32(0), f32(0), f32(0), f32(1))), | 
|  | C({Val(u32(0x00ff'0000))}, Vec(f32(0), f32(0), f32(1), f32(0))), | 
|  | C({Val(u32(0x0000'ff00))}, Vec(f32(0), f32(1), f32(0), f32(0))), | 
|  | C({Val(u32(0x0000'00ff))}, Vec(f32(1), f32(0), f32(0), f32(0))), | 
|  | C({Val(u32(0x00ff'00ff))}, Vec(f32(1), f32(0), f32(1), f32(0))), | 
|  | C({Val(u32(0x0066'00ff))}, Vec(f32(1), f32(0), f32(0.4), f32(0))), | 
|  | }; | 
|  | } | 
|  | INSTANTIATE_TEST_SUITE_P(  // | 
|  | Unpack4x8unorm, | 
|  | ResolverConstEvalBuiltinTest, | 
|  | testing::Combine(testing::Values(builtin::Function::kUnpack4X8Unorm), | 
|  | testing::ValuesIn(Unpack4x8unormCases()))); | 
|  |  | 
|  | std::vector<Case> Unpack2x16floatCases() { | 
|  | return { | 
|  | C({Val(u32(0x7bff'fbff))}, Vec(f32(f16::Lowest()), f32(f16::Highest()))), | 
|  | C({Val(u32(0xbc00'3c00))}, Vec(f32(1), f32(-1))), | 
|  | C({Val(u32(0x0000'0000))}, Vec(f32(0), f32(0))), | 
|  | C({Val(u32(0xc940'4900))}, Vec(f32(10), f32(-10.5))), | 
|  | }; | 
|  | } | 
|  | INSTANTIATE_TEST_SUITE_P(  // | 
|  | Unpack2x16float, | 
|  | ResolverConstEvalBuiltinTest, | 
|  | testing::Combine(testing::Values(builtin::Function::kUnpack2X16Float), | 
|  | testing::ValuesIn(Unpack2x16floatCases()))); | 
|  |  | 
|  | std::vector<Case> Unpack2x16snormCases() { | 
|  | return { | 
|  | C({Val(u32(0x0000'0000))}, Vec(f32(0), f32(0))), | 
|  | C({Val(u32(0x8001'0000))}, Vec(f32(0), f32(-1))), | 
|  | C({Val(u32(0x7fff'0000))}, Vec(f32(0), f32(1))), | 
|  | C({Val(u32(0x0000'8001))}, Vec(f32(-1), f32(0))), | 
|  | C({Val(u32(0x0000'7fff))}, Vec(f32(1), f32(0))), | 
|  | C({Val(u32(0x8001'7fff))}, Vec(f32(1), f32(-1))), | 
|  | C({Val(u32(0x8001'7fff))}, Vec(f32(1), f32(-1))), | 
|  | C({Val(u32(0x4000'999a))}, Vec(f32(-0.80001220740379), f32(0.500015259254737))).FloatComp(), | 
|  | }; | 
|  | } | 
|  | INSTANTIATE_TEST_SUITE_P(  // | 
|  | Unpack2x16snorm, | 
|  | ResolverConstEvalBuiltinTest, | 
|  | testing::Combine(testing::Values(builtin::Function::kUnpack2X16Snorm), | 
|  | testing::ValuesIn(Unpack2x16snormCases()))); | 
|  |  | 
|  | std::vector<Case> Unpack2x16unormCases() { | 
|  | return { | 
|  | C({Val(u32(0xffff'0000))}, Vec(f32(0), f32(1))), | 
|  | C({Val(u32(0x0000'ffff))}, Vec(f32(1), f32(0))), | 
|  | C({Val(u32(0x0000'6666))}, Vec(f32(0.4), f32(0))), | 
|  | C({Val(u32(0x0000'ffff))}, Vec(f32(1), f32(0))), | 
|  | }; | 
|  | } | 
|  | INSTANTIATE_TEST_SUITE_P(  // | 
|  | Unpack2x16unorm, | 
|  | ResolverConstEvalBuiltinTest, | 
|  | testing::Combine(testing::Values(builtin::Function::kUnpack2X16Unorm), | 
|  | testing::ValuesIn(Unpack2x16unormCases()))); | 
|  |  | 
|  | std::vector<Case> QuantizeToF16Cases() { | 
|  | return { | 
|  | C({0_f}, 0_f),    // | 
|  | C({-0_f}, -0_f),  // | 
|  | C({1_f}, 1_f),    // | 
|  | C({-1_f}, -1_f),  // | 
|  |  | 
|  | //   0.00006106496 quantized to 0.000061035156 = 0x1p-14 | 
|  | C({0.00006106496_f}, 0.000061035156_f),    // | 
|  | C({-0.00006106496_f}, -0.000061035156_f),  // | 
|  |  | 
|  | //   1.0004883 quantized to 1.0 = 0x1p0 | 
|  | C({1.0004883_f}, 1.0_f),    // | 
|  | C({-1.0004883_f}, -1.0_f),  // | 
|  |  | 
|  | //   8196.0 quantized to 8192.0 = 0x1p13 | 
|  | C({8196_f}, 8192_f),    // | 
|  | C({-8196_f}, -8192_f),  // | 
|  |  | 
|  | // Value in subnormal f16 range | 
|  | C({0x0.034p-14_f}, 0x0.034p-14_f),    // | 
|  | C({-0x0.034p-14_f}, -0x0.034p-14_f),  // | 
|  | C({0x0.068p-14_f}, 0x0.068p-14_f),    // | 
|  | C({-0x0.068p-14_f}, -0x0.068p-14_f),  // | 
|  |  | 
|  | //   0x0.06b7p-14 quantized to 0x0.068p-14 | 
|  | C({0x0.06b7p-14_f}, 0x0.068p-14_f),    // | 
|  | C({-0x0.06b7p-14_f}, -0x0.068p-14_f),  // | 
|  |  | 
|  | // Vector tests | 
|  | C({Vec(0_f, -0_f)}, Vec(0_f, -0_f)),  // | 
|  | C({Vec(1_f, -1_f)}, Vec(1_f, -1_f)),  // | 
|  |  | 
|  | C({Vec(0.00006106496_f, -0.00006106496_f, 1.0004883_f, -1.0004883_f)}, | 
|  | Vec(0.000061035156_f, -0.000061035156_f, 1.0_f, -1.0_f)), | 
|  |  | 
|  | C({Vec(8196_f, 8192_f, 0x0.034p-14_f)}, Vec(8192_f, 8192_f, 0x0.034p-14_f)), | 
|  |  | 
|  | C({Vec(0x0.034p-14_f, -0x0.034p-14_f, 0x0.068p-14_f, -0x0.068p-14_f)}, | 
|  | Vec(0x0.034p-14_f, -0x0.034p-14_f, 0x0.068p-14_f, -0x0.068p-14_f)), | 
|  |  | 
|  | // Value out of f16 range | 
|  | E({65504.003_f}, "12:34 error: value 65504.00390625 cannot be represented as 'f16'"), | 
|  | E({-65504.003_f}, "12:34 error: value -65504.00390625 cannot be represented as 'f16'"), | 
|  | E({0x1.234p56_f}, "12:34 error: value 81979586966978560.0 cannot be represented as 'f16'"), | 
|  | E({0x4.321p65_f}, | 
|  | "12:34 error: value 154788719192723947520.0 cannot be represented as 'f16'"), | 
|  | E({Vec(65504.003_f, 0_f)}, | 
|  | "12:34 error: value 65504.00390625 cannot be represented as 'f16'"), | 
|  | E({Vec(0_f, -0x4.321p65_f)}, | 
|  | "12:34 error: value -154788719192723947520.0 cannot be represented as 'f16'"), | 
|  | }; | 
|  | } | 
|  | INSTANTIATE_TEST_SUITE_P(  // | 
|  | QuantizeToF16, | 
|  | ResolverConstEvalBuiltinTest, | 
|  | testing::Combine(testing::Values(builtin::Function::kQuantizeToF16), | 
|  | testing::ValuesIn(QuantizeToF16Cases()))); | 
|  |  | 
|  | }  // namespace | 
|  | }  // namespace tint::resolver |