Crafter.Math/tests/Vector.cpp

472 lines
23 KiB
C++
Raw Normal View History

2026-03-25 00:42:04 +01:00
/*
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 <cmath>
import Crafter.Math;
import std;
using namespace Crafter;
// Helper function to compare floating point values with tolerance
template <typename T>
2026-03-25 02:51:02 +01:00
constexpr bool FloatEquals(T a, T b, T epsilon = 0.01f) {
2026-03-25 00:42:04 +01:00
return std::abs(static_cast<float>(a) - static_cast<float>(b)) < static_cast<float>(epsilon);
}
template <std::uint8_t Len>
consteval std::array<bool, Len> AlternateTrueFalse() {
std::array<bool, Len> 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;
}
2026-03-26 03:53:30 +01:00
template <std::uint8_t Len>
consteval std::array<std::uint8_t, Len> GetCountReverse() {
std::array<std::uint8_t, Len> result = {};
for (std::uint8_t i = 0; i < Len; ++i) {
result[Len - 1 - i] = i;
}
return result;
}
2026-03-25 00:42:04 +01:00
template <typename T, template<std::uint32_t, std::uint32_t> 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<T, VectorType, MaxSize, Len + 1, 1>();
} else {
T floats[VectorType<Len, Packing>::Alignment];
T floats1[VectorType<Len, Packing>::Alignment];
T floats2[VectorType<Len, Packing>::Alignment];
for (std::uint32_t i = 0; i < VectorType<Len, Packing>::Alignment; i++) {
floats[i] = static_cast<T>(i+1);
}
for (std::uint32_t i = 0; i < Packing*Len; i++) {
floats1[i] = static_cast<T>(i+1);
}
for (std::uint32_t i = 0; i < Packing*Len; i++) {
floats2[i] = static_cast<T>(i+1+Len);
}
for (std::uint32_t i = Len*Packing; i < VectorType<Len, Packing>::Alignment; i++) {
floats1[i] = 0;
floats2[i] = 0;
}
2026-03-26 03:53:30 +01:00
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])));
}
2026-03-25 00:42:04 +01:00
std::string* result = nullptr;
constexpr auto total = Len * Packing;
if constexpr(total > 0 && (total & (total - 1)) == 0) {
{
VectorType<Len, Packing> vec(floats);
Vector<T, Len*Packing, VectorType<Len, Packing>::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<Len, Packing> vec(floats);
vec = vec + vec;
Vector<T, Len*Packing, VectorType<Len, Packing>::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<Len, Packing> vec(floats);
vec = vec - vec;
Vector<T, Len*Packing, VectorType<Len, Packing>::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<Len, Packing> vec(floats);
vec = vec * vec;
Vector<T, Len*Packing, VectorType<Len, Packing>::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<Len, Packing> vec(floats);
vec = vec / vec;
Vector<T, Len*Packing, VectorType<Len, Packing>::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<Len, Packing> vec(floats);
vec = vec + T(2);
Vector<T, Len*Packing, VectorType<Len, Packing>::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<Len, Packing> vec(floats);
vec = vec - T(2);
Vector<T, Len*Packing, VectorType<Len, Packing>::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<Len, Packing> vec(floats);
vec = vec * T(2);
Vector<T, Len*Packing, VectorType<Len, Packing>::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<Len, Packing> vec(floats);
vec = vec / T(2);
Vector<T, Len*Packing, VectorType<Len, Packing>::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<Len, Packing> vec1(floats);
VectorType<Len, Packing> vec2(floats);
if (!(vec1 == vec2)) {
return new std::string(std::format("Equality 1 test failed at Len={} Packing={}", Len, Packing));
}
}
{
VectorType<Len, Packing> vec1(floats);
VectorType<Len, Packing> vec2(floats);
vec2 *= 2;
if (vec1 == vec2) {
return new std::string(std::format("Equality 2 test failed at Len={} Packing={}", Len, Packing));
}
}
{
VectorType<Len, Packing> vec1(floats);
VectorType<Len, Packing> vec2(floats);
if ((vec1 != vec2)) {
return new std::string(std::format("Inequality 1 test failed at Len={} Packing={}", Len, Packing));
}
}
{
VectorType<Len, Packing> vec1(floats);
VectorType<Len, Packing> vec2(floats);
vec2 *= 2;
if (!(vec1 != vec2)) {
return new std::string(std::format("Inequality 2 test failed at Len={} Packing={}", Len, Packing));
}
}
{
VectorType<Len, Packing> vec(floats);
vec = -vec;
Vector<T, Len*Packing, VectorType<Len, Packing>::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<Len, Packing> vecA(floats1);
VectorType<Len, Packing> vecB(floats2);
VectorType<Len, Packing> result = VectorType<Len, Packing>::template Blend<AlternateTrueFalse<Len>()>(vecA, vecB);
Vector<T, Len*Packing, VectorType<Len, Packing>::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<Len, Packing> vecA(floats);
VectorType<Len, Packing> vecB(floats);
VectorType<Len, Packing> vecAdd(floats);
VectorType<Len, Packing> result = VectorType<Len, Packing>::MulitplyAdd(vecA, vecB, vecAdd);
Vector<T, Len*Packing, VectorType<Len, Packing>::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<Len, Packing> vecA(floats);
VectorType<Len, Packing> vecB(floats);
VectorType<Len, Packing> vecSub(floats);
VectorType<Len, Packing> result = VectorType<Len, Packing>::MulitplySub(vecA, vecB, vecSub);
Vector<T, Len*Packing, VectorType<Len, Packing>::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]));
}
}
}
2026-03-25 02:51:02 +01:00
if constexpr(Len > 2){
VectorType<Len, Packing> vec(floats);
VectorType<Len-1, Packing> result = vec.template ExtractLo<Len-1>();
Vector<T, (Len-1)*Packing, VectorType<Len-1, Packing>::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]));
}
}
}
}
2026-03-25 00:42:04 +01:00
}
if constexpr(Packing == 1) {
T expectedLengthSq = T(0);
for (std::uint32_t i = 0; i < VectorType<Len, Packing>::Alignment; i++) {
expectedLengthSq += floats[i] * floats[i];
}
{
VectorType<Len, Packing> vec(floats);
T dot = VectorType<Len, Packing>::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<Len, Packing> 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<Len, Packing> vec(floats);
T length = vec.Length();
2026-03-25 02:51:02 +01:00
T expected = static_cast<T>(std::sqrtf(static_cast<float>(expectedLengthSq)));
if (!FloatEquals(length, expected)) {
return new std::string(std::format("Length mismatch at Len={} Packing={}, Expected: {}, Got: {}", Len, Packing, (float)expected, (float)length));
2026-03-25 00:42:04 +01:00
}
}
{
VectorType<Len, Packing> vec(floats);
vec.Normalize();
T length = vec.Length();
if (!FloatEquals(length, static_cast<T>(1))) {
return new std::string(std::format("Normalize mismatch at Len={} Packing={}, Expected: {}, Got: {}", Len, Packing, 1, (float)length));
}
}
2026-03-26 03:53:30 +01:00
{
VectorType<Len, Packing> vec(floats);
VectorType<Len, Packing> result = vec.template Shuffle<GetCountReverse<Len>()>();
Vector<T, Len*Packing, VectorType<Len, Packing>::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]));
}
}
}
2026-03-25 00:42:04 +01:00
}
2026-03-26 03:53:30 +01:00
if constexpr(Len == 3) {
{
VectorType<Len, Packing> vec1(floats1);
VectorType<Len, Packing> vec2(floats2);
VectorType<Len, Packing> result = VectorType<Len, Packing>::Cross(vec1, vec2);
Vector<T, Len*Packing, VectorType<Len, Packing>::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);
2026-03-25 00:42:04 +01:00
2026-03-26 03:53:30 +01:00
VectorType<3, Packing> vecV(floats);
VectorType<4, Packing> vecQ(qData);
VectorType<3, Packing> result = VectorType<3, Packing>::Rotate(vecV, vecQ);
Vector<T, 3*Packing, VectorType<3, Packing>::Alignment> stored = result.Store();
2026-03-25 00:42:04 +01:00
2026-03-26 03:53:30 +01:00
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<T, 4*Packing, VectorType<4, Packing>::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<Len, Packing>::Alignment) {
{
VectorType<Len, Packing> vecA(floats);
VectorType<Len, Packing> vecE = vecA *2;
VectorType<1, Packing*2> result = VectorType<Len, Packing>::Length(vecA, vecE);
Vector<T, Packing*2, VectorType<Len, Packing>::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<Len, Packing> vecA(floats);
VectorType<Len, Packing> vecE = vecA * 2;
auto result = VectorType<Len, Packing>::Normalize(vecA, vecE);
VectorType<1, Packing*2> result2 = VectorType<Len, Packing>::Length(std::get<0>(result), std::get<1>(result));
Vector<T, Packing*2, VectorType<Len, Packing>::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]));
}
}
}
}
2026-03-25 00:42:04 +01:00
2026-03-27 05:54:37 +01:00
if constexpr(Len == 4 && Packing*Len == VectorType<Len, Packing>::Alignment) {
{
VectorType<Len, Packing> vecA(floats);
VectorType<Len, Packing> vecC = vecA * 2;
VectorType<Len, Packing> vecE = vecA * 3;
VectorType<Len, Packing> vecG = vecA * 4;
VectorType<1, Packing*4> result = VectorType<Len, Packing>::Length(vecA, vecC, vecE, vecG);
Vector<T, Packing*4, VectorType<Len, Packing>::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<Len, Packing> vecA(floats);
VectorType<Len, Packing> vecC = vecA * 2;
VectorType<Len, Packing> vecE = vecA * 3;
VectorType<Len, Packing> vecG = vecA * 4;
auto result = VectorType<Len, Packing>::Normalize(vecA, vecC, vecE, vecG);
VectorType<1, Packing*4> result2 = VectorType<Len, Packing>::Length(std::get<0>(result), std::get<1>(result), std::get<2>(result), std::get<3>(result));
Vector<T, Packing*4, VectorType<Len, Packing>::Alignment> stored = result2.Store();
//std::println("{}", stored);
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]));
}
}
}
}
2026-03-25 00:42:04 +01:00
return TestAllCombinations<T, VectorType, MaxSize, Len, Packing + 1>();
}
}
extern "C" {
std::string* RunTest() {
std::string* err = TestAllCombinations<_Float16, VectorF16, VectorF16<1, 1>::MaxSize>();
if (err) {
return err;
}
return nullptr;
}
}