/* Crafter® Build Copyright (C) 2026 Catcrafts® Catcrafts.net This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License version 3.0 as published by the Free Software Foundation; This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include import Crafter.Math; import std; using namespace Crafter; // Helper function to compare floating point values with tolerance template constexpr bool FloatEquals(T a, T b, T epsilon = 0.01f) { return std::abs(static_cast(a) - static_cast(b)) < static_cast(epsilon); } template consteval std::array AlternateTrueFalse() { std::array result = {}; for (std::uint8_t i = 0; i < Len; ++i) { result[i] = (i % 2 == 0); // Set true for even indices, false for odd indices } return result; } template consteval std::array GetCountReverse() { std::array result = {}; for (std::uint8_t i = 0; i < Len; ++i) { result[Len - 1 - i] = i; } return result; } template class VectorType, std::uint32_t MaxSize, std::uint32_t Len = 1, std::uint32_t Packing = 1> std::string* TestAllCombinations() { if constexpr (Len > MaxSize) { return nullptr; } else if constexpr (Len * Packing > MaxSize) { return TestAllCombinations(); } else { T floats[VectorType::Alignment]; T floats1[VectorType::Alignment]; T floats2[VectorType::Alignment]; for (std::uint32_t i = 0; i < VectorType::Alignment; i++) { floats[i] = static_cast(i+1); } for (std::uint32_t i = 0; i < Packing*Len; i++) { floats1[i] = static_cast(i+1); } for (std::uint32_t i = 0; i < Packing*Len; i++) { floats2[i] = static_cast(i+1+Len); } for (std::uint32_t i = Len*Packing; i < VectorType::Alignment; i++) { floats1[i] = 0; floats2[i] = 0; } T expectedLength[Packing] = {0}; for (std::uint32_t i2 = 0; i2 < Packing; i2++) { for (std::uint32_t i = 0; i < Len; i++) { expectedLength[i2] += floats[i2*Len+i] * floats[i2*Len+i]; } expectedLength[i2] = T(std::sqrt(float(expectedLength[i2]))); } std::string* result = nullptr; constexpr auto total = Len * Packing; if constexpr(total > 0 && (total & (total - 1)) == 0) { { VectorType vec(floats); Vector::Alignment> stored = vec.Store(); for (std::uint32_t i = 0; i < Len * Packing; i++) { if (!FloatEquals(stored.v[i], floats[i])) { return new std::string(std::format("Load/Store mismatch at Len={} Packing={}, Expected: {}, Got: {}", Len, Packing, (float)(floats[i]), (float)stored.v[i])); } } } { VectorType vec(floats); vec = vec + vec; Vector::Alignment> stored = vec.Store(); for (std::uint32_t i = 0; i < Len * Packing; i++) { if (!FloatEquals(stored.v[i], floats[i] + floats[i])) { return new std::string(std::format("Add mismatch at Len={} Packing={}, Expected: {}, Got: {}", Len, Packing, (float)(floats[i] + floats[i]), (float)stored.v[i])); } } } { VectorType vec(floats); vec = vec - vec; Vector::Alignment> stored = vec.Store(); for (std::uint32_t i = 0; i < Len * Packing; i++) { if (!FloatEquals(stored.v[i], T(0))) { return new std::string(std::format("Subtract mismatch at Len={} Packing={}, Expected: 0, Got: {}", Len, Packing, (float)stored.v[i])); } } } { VectorType vec(floats); vec = vec * vec; Vector::Alignment> stored = vec.Store(); for (std::uint32_t i = 0; i < Len * Packing; i++) { if (!FloatEquals(stored.v[i], floats[i] * floats[i])) { return new std::string(std::format("Multiply mismatch at Len={} Packing={}, Expected: {}, Got: {}", Len, Packing, (float)(floats[i] * floats[i]), (float)stored.v[i])); } } } { VectorType vec(floats); vec = vec / vec; Vector::Alignment> stored = vec.Store(); for (std::uint32_t i = 0; i < Len * Packing; i++) { if (!FloatEquals(stored.v[i], T(1))) { return new std::string(std::format("Divide mismatch at Len={} Packing={}, Expected: 1, Got: {}", Len, Packing, (float)stored.v[i])); } } } { VectorType vec(floats); vec = vec + T(2); Vector::Alignment> stored = vec.Store(); for (std::uint32_t i = 0; i < Len * Packing; i++) { if (!FloatEquals(stored.v[i], floats[i] + T(2))) { return new std::string(std::format("Scalar add mismatch at Len={} Packing={}, Expected: {}, Got: {}", Len, Packing, (float)(floats[i] + T(2)), (float)stored.v[i])); } } } { VectorType vec(floats); vec = vec - T(2); Vector::Alignment> stored = vec.Store(); for (std::uint32_t i = 0; i < Len * Packing; i++) { if (!FloatEquals(stored.v[i], floats[i] - T(2))) { return new std::string(std::format("Scalar add mismatch at Len={} Packing={}, Expected: {}, Got: {}", Len, Packing, (float)(floats[i] + T(2)), (float)stored.v[i])); } } } { VectorType vec(floats); vec = vec * T(2); Vector::Alignment> stored = vec.Store(); for (std::uint32_t i = 0; i < Len * Packing; i++) { if (!FloatEquals(stored.v[i], floats[i] * T(2))) { return new std::string(std::format("Scalar multiply mismatch at Len={} Packing={}, Expected: {}, Got: {}", Len, Packing, (float)(floats[i] * T(2)), (float)stored.v[i])); } } } { VectorType vec(floats); vec = vec / T(2); Vector::Alignment> stored = vec.Store(); for (std::uint32_t i = 0; i < Len * Packing; i++) { if (!FloatEquals(stored.v[i], floats[i] / T(2))) { return new std::string(std::format("Scalar divide mismatch at Len={} Packing={}, Expected: {}, Got: {}", Len, Packing, (float)(floats[i] * T(2)), (float)stored.v[i])); } } } { VectorType vec1(floats); VectorType vec2(floats); if (!(vec1 == vec2)) { return new std::string(std::format("Equality 1 test failed at Len={} Packing={}", Len, Packing)); } } { VectorType vec1(floats); VectorType vec2(floats); vec2 *= 2; if (vec1 == vec2) { return new std::string(std::format("Equality 2 test failed at Len={} Packing={}", Len, Packing)); } } { VectorType vec1(floats); VectorType vec2(floats); if ((vec1 != vec2)) { return new std::string(std::format("Inequality 1 test failed at Len={} Packing={}", Len, Packing)); } } { VectorType vec1(floats); VectorType vec2(floats); vec2 *= 2; if (!(vec1 != vec2)) { return new std::string(std::format("Inequality 2 test failed at Len={} Packing={}", Len, Packing)); } } { VectorType vec(floats); vec = -vec; Vector::Alignment> result = vec.Store(); for (std::uint32_t i = 0; i < Len * Packing; i++) { if (!FloatEquals(result.v[i], -floats[i])) { return new std::string(std::format("Negate mismatch at Len={} Packing={}, Expected: {}, Got: {}", Len, Packing, (float)(-floats[i]), (float)result.v[i])); } } } { VectorType vecA(floats1); VectorType vecB(floats2); VectorType result = VectorType::template Blend()>(vecA, vecB); Vector::Alignment> stored = result.Store(); for (std::uint32_t i = 0; i < Len; i++) { bool useB = (i % 2 == 0); T expected = useB ? floats2[i]: floats1[i]; if (!FloatEquals(stored.v[i], expected)) { return new std::string(std::format("Blend mismatch at Len={} Packing={}, Index={}, Expected: {}, Got: {}", Len, Packing, i, (float)expected, (float)stored.v[i])); } } } { VectorType vecA(floats); VectorType vecB(floats); VectorType vecAdd(floats); VectorType result = VectorType::MulitplyAdd(vecA, vecB, vecAdd); Vector::Alignment> stored = result.Store(); for (std::uint32_t i = 0; i < Len; i++) { T expected = floats[i] * floats[i] + floats[i]; if (!FloatEquals(stored.v[i], expected)) { return new std::string(std::format("MulitplyAdd mismatch at Len={} Packing={}, Index={}, Expected: {}, Got: {}", Len, Packing, i, (float)expected, (float)stored.v[i])); } } } { VectorType vecA(floats); VectorType vecB(floats); VectorType vecSub(floats); VectorType result = VectorType::MulitplySub(vecA, vecB, vecSub); Vector::Alignment> stored = result.Store(); for (std::uint32_t i = 0; i < Len; i++) { T expected = floats[i] * floats[i] - floats[i]; if (!FloatEquals(stored.v[i], expected)) { return new std::string(std::format("MulitplySub mismatch at Len={} Packing={}, Index={}, Expected: {}, Got: {}", Len, Packing, i, (float)expected, (float)stored.v[i])); } } } if constexpr(Len > 2){ VectorType vec(floats); VectorType result = vec.template ExtractLo(); Vector::Alignment> stored = result.Store(); for(std::uint32_t i2 = 0; i2 < Packing; i2++){ for (std::uint32_t i = 0; i < Len-1; i++) { T expected = floats[i2*(Len)+i]; if (!FloatEquals(stored.v[i2*(Len-1)+i], expected)) { return new std::string(std::format("ExtractLo mismatch at Len={} Packing={}, Index={}, Expected: {}, Got: {}", Len, Packing, i, (float)expected, (float)stored.v[i2*(Len-1)+i])); } } } } { VectorType vec(floats); VectorType result = vec.Sin(); Vector::Alignment> stored = result.Store(); for(std::uint32_t i2 = 0; i2 < Packing; i2++){ for (std::uint32_t i = 0; i < Len; i++) { T expected = (T)std::sin((float)floats[i2*Len+i]); if (!FloatEquals(stored.v[i2*Len+i], expected)) { return new std::string(std::format("Sin mismatch at Len={} Packing={}, Index={}, Expected: {}, Got: {}", Len, Packing, i, (float)expected, (float)stored.v[i2*(Len-1)+i])); } } } } { VectorType vec(floats); VectorType result = vec.Cos(); Vector::Alignment> stored = result.Store(); for(std::uint32_t i2 = 0; i2 < Packing; i2++){ for (std::uint32_t i = 0; i < Len; i++) { T expected = (T)std::cos((float)floats[i2*Len+i]); if (!FloatEquals(stored.v[i2*Len+i], expected)) { return new std::string(std::format("Cos mismatch at Len={} Packing={}, Index={}, Expected: {}, Got: {}", Len, Packing, i, (float)expected, (float)stored.v[i2*(Len-1)+i])); } } } } { VectorType vec(floats); auto result = vec.SinCos(); Vector::Alignment> storedSin = std::get<0>(result).Store(); Vector::Alignment> storedCos = std::get<1>(result).Store(); for(std::uint32_t i2 = 0; i2 < Packing; i2++){ for (std::uint32_t i = 0; i < Len; i++) { T expected = (T)std::sin((float)floats[i2*Len+i]); if (!FloatEquals(storedSin.v[i2*Len+i], expected)) { return new std::string(std::format("SinCos sin mismatch at Len={} Packing={}, Index={}, Expected: {}, Got: {}", Len, Packing, i, (float)expected, (float)storedSin.v[i2*(Len-1)+i])); } expected = (T)std::cos((float)floats[i2*Len+i]); if (!FloatEquals(storedCos.v[i2*Len+i], expected)) { return new std::string(std::format("SinCos cos mismatch at Len={} Packing={}, Index={}, Expected: {}, Got: {}", Len, Packing, i, (float)expected, (float)storedCos.v[i2*(Len-1)+i])); } } } } { VectorType vec(floats); VectorType result = vec.template Shuffle()>(); Vector::Alignment> stored = result.Store(); for (std::uint32_t i = 0; i < Len; i++) { T expected = floats[Len - 1 - i]; if (!FloatEquals(stored.v[i], expected)) { return new std::string(std::format("Shuffle mismatch at Len={} Packing={}, Index={}, Expected: {}, Got: {}", Len, Packing, i, (float)expected, (float)stored.v[i])); } } } } if constexpr(Packing == 1) { T expectedLengthSq = T(0); for (std::uint32_t i = 0; i < VectorType::Alignment; i++) { expectedLengthSq += floats[i] * floats[i]; } { VectorType vec(floats); T dot = VectorType::Dot(vec, vec); if (!FloatEquals(dot, expectedLengthSq)) { return new std::string(std::format("Dot product mismatch at Len={} Packing={}, Expected: {}, Got: {}", Len, Packing, (float)expectedLengthSq, (float)dot)); } } { VectorType vec(floats); T lengthSq = vec.LengthSq(); if (!FloatEquals(lengthSq, expectedLengthSq)) { return new std::string(std::format("LengthSq mismatch at Len={} Packing={}, Expected: {}, Got: {}", Len, Packing, (float)expectedLengthSq, (float)lengthSq)); } } { VectorType vec(floats); T length = vec.Length(); T expected = static_cast(std::sqrtf(static_cast(expectedLengthSq))); if (!FloatEquals(length, expected)) { return new std::string(std::format("Length mismatch at Len={} Packing={}, Expected: {}, Got: {}", Len, Packing, (float)expected, (float)length)); } } { VectorType vec(floats); vec.Normalize(); T length = vec.Length(); if (!FloatEquals(length, static_cast(1))) { return new std::string(std::format("Normalize mismatch at Len={} Packing={}, Expected: {}, Got: {}", Len, Packing, 1, (float)length)); } } } if constexpr(Len == 3) { { VectorType vec1(floats1); VectorType vec2(floats2); VectorType result = VectorType::Cross(vec1, vec2); Vector::Alignment> stored = result.Store(); if (!FloatEquals(stored.v[0], T(-3)) || !FloatEquals(stored.v[1], T(6)) || !FloatEquals(stored.v[2], T(-3))) { return new std::string(std::format("Cross mismatch at Len={} Packing={}, Expected: -3,6,-3, Got: {},{},{}", Len, Packing, (float)stored.v[0], (float)stored.v[1], (float)stored.v[2])); } } if constexpr(4 * Packing < VectorType<1, 1>::MaxSize) { T qData[VectorType<4, Packing>::Alignment]; qData[0] = T(0); qData[1] = T(0); qData[2] = T(0); qData[3] = T(1); VectorType<3, Packing> vecV(floats); VectorType<4, Packing> vecQ(qData); VectorType<3, Packing> result = VectorType<3, Packing>::Rotate(vecV, vecQ); Vector::Alignment> stored = result.Store(); for (std::uint32_t i = 0; i < 3; i++) { if (!FloatEquals(stored.v[i], floats[i])) { return new std::string(std::format("Rotate mismatch at Len={} Packing={}, Index={}, Expected: {}, Got: {}", Len, Packing, i, (float)floats[i], (float)stored.v[i])); } } } } if constexpr(Len == 4) { T eulerData[VectorType<3, Packing>::Alignment]; for(std::uint8_t i = 0; i < Packing; i++) { eulerData[i*3] = T(0.7853981); eulerData[i*3+1] = T(0.1243412); eulerData[i*3+2] = T(0.3245312); } VectorType<3, Packing> eulerVec(eulerData); VectorType<4, Packing> result = VectorType<4, Packing>::QuanternionFromEuler(eulerVec); Vector::Alignment> stored = result.Store(); if (!FloatEquals(stored.v[0], T(0.63720703)) || !FloatEquals(stored.v[1], T(0.30688477)) || !FloatEquals(stored.v[2], T(0.14074707)) || !FloatEquals(stored.v[3], T(0.6933594))) { return new std::string(std::format("QuanternionFromEuler mismatch at Len={} Packing={}, Expected: 0.63720703,0.30688477,0.14074707,0.6933594, Got: {},{},{},{}", Len, Packing, (float)stored.v[0], (float)stored.v[1], (float)stored.v[2], (float)stored.v[3])); } } if constexpr(Len == 2 && Packing*Len == VectorType::Alignment) { { VectorType vecA(floats); VectorType vecE = vecA *2; VectorType<1, Packing*2> result = VectorType::Length(vecA, vecE); Vector::Alignment> stored = result.Store(); if (!FloatEquals(stored.v[0], expectedLength[0])) { return new std::string(std::format("Length 2 vecA test failed at Len={} Packing={} Expected: {}, Got: {}", Len, Packing, (float)expectedLength[0], (float)stored.v[0])); } if (!FloatEquals(stored.v[(Len*Packing)/2], expectedLength[0] * 2)) { return new std::string(std::format("Length 2 vecE test failed at Len={} Packing={} Expected: {}, Got: {}", Len, Packing, (float)expectedLength[0] * 2, (float)stored.v[(Len*Packing)/2])); } } { VectorType vecA(floats); VectorType vecE = vecA * 2; auto result = VectorType::Normalize(vecA, vecE); VectorType<1, Packing*2> result2 = VectorType::Length(std::get<0>(result), std::get<1>(result)); Vector::Alignment> stored = result2.Store(); for(std::uint8_t i = 0; i < Len*Packing; i++) { if (!FloatEquals(stored.v[i], T(1))) { return new std::string(std::format("Normalize {} test failed at Len={} Packing={} Expected: {}, Got: {}", i, Len, Packing, 1, (float)stored.v[i])); } } } } if constexpr(Len == 4 && Packing*Len == VectorType::Alignment) { { VectorType vecA(floats); VectorType vecC = vecA * 2; VectorType vecE = vecA * 3; VectorType vecG = vecA * 4; VectorType<1, Packing*4> result = VectorType::Length(vecA, vecC, vecE, vecG); Vector::Alignment> stored = result.Store(); if (!FloatEquals(stored.v[0], expectedLength[0])) { return new std::string(std::format("Length 4 vecA test failed at Len={} Packing={} Expected: {}, Got: {}", Len, Packing, (float)expectedLength[0], (float)stored.v[0])); } if (!FloatEquals(stored.v[Packing], expectedLength[0] * 2)) { return new std::string(std::format("Length 4 vecC test failed at Len={} Packing={} Expected: {}, Got: {}", Len, Packing, (float)expectedLength[0] * 2, (float)stored.v[Packing])); } if (!FloatEquals(stored.v[Packing*2], expectedLength[0] * 3)) { return new std::string(std::format("Length 4 vecE test failed at Len={} Packing={} Expected: {}, Got: {}", Len, Packing, (float)expectedLength[0] * 3, (float)stored.v[Packing*2])); } if (!FloatEquals(stored.v[Packing*3], expectedLength[0] * 4)) { return new std::string(std::format("Length 4 vecG test failed at Len={} Packing={} Expected: {}, Got: {}", Len, Packing, (float)expectedLength[0] * 4, (float)stored.v[Packing*3])); } } { VectorType vecA(floats); VectorType vecC = vecA * 2; VectorType vecE = vecA * 3; VectorType vecG = vecA * 4; auto result = VectorType::Normalize(vecA, vecC, vecE, vecG); VectorType<1, Packing*4> result2 = VectorType::Length(std::get<0>(result), std::get<1>(result), std::get<2>(result), std::get<3>(result)); Vector::Alignment> stored = result2.Store(); for(std::uint8_t i = 0; i < Len*Packing; i++) { if (!FloatEquals(stored.v[i], T(1))) { return new std::string(std::format("Normalize {} test failed at Len={} Packing={} Expected: {}, Got: {}", i, Len, Packing, 1, (float)stored.v[i])); } } } } return TestAllCombinations(); } } extern "C" { std::string* RunTest() { std::string* err = TestAllCombinations<_Float16, VectorF16, VectorF16<1, 1>::MaxSize>(); if (err) { return err; } return nullptr; } }