// Copyright (C) 2026 Davis E. King (davis@dlib.net)
// License: Boost Software License See LICENSE.txt for the full license.
#include "tester.h"
#include <dlib/simd.h>
#include <dlib/array2d.h>
#include <dlib/image_transforms/interpolation.h>
#include <type_traits>
namespace
{
using namespace test;
using namespace dlib;
template <typename float_vector, typename int_vector>
void test_conversions()
{
const float input[] = {
1.75f, -2.5f, 0.0f, 17.0f,
0.6875f, -0.6875f, 1.0625f, -1.0625f,
2147483520.0f, -2147483648.0f, 16777215.0f, -16777215.0f,
0.0f, -0.0f, 123.875f, -123.875f
};
float_vector values;
for (unsigned int offset = 0; offset < 16; offset += values.size())
{
values.load(input + offset);
const int_vector converted(values);
const float_vector restored(converted);
const float_vector fractions = values - converted;
for (unsigned int i = 0; i < values.size(); ++i)
{
const int32 expected = static_cast<int32>(input[offset+i]);
DLIB_TEST(converted[i] == expected);
DLIB_TEST(restored[i] == static_cast<float>(expected));
DLIB_TEST(fractions[i] == input[offset+i] - static_cast<float>(expected));
}
}
}
void test_resize()
{
// Bilinear interpolation must preserve a linear ramp. These dimensions
// exercise fractional coordinates, complete SIMD groups, and the scalar tail.
array2d<float> gray(17, 19), gray_out(12, 14);
array2d<rgb_pixel> rgb(17, 19), rgb_out(12, 14);
for (long r = 0; r < gray.nr(); ++r)
{
for (long c = 0; c < gray.nc(); ++c)
{
gray[r][c] = static_cast<float>(8*r + c);
rgb[r][c] = rgb_pixel(
static_cast<unsigned char>(4*r + 2*c),
static_cast<unsigned char>(2*r + 4*c),
static_cast<unsigned char>(3*r + c));
}
}
resize_image(gray, gray_out, interpolate_bilinear());
resize_image(rgb, rgb_out, interpolate_bilinear());
for (long r = 0; r < gray_out.nr(); ++r)
{
for (long c = 0; c < gray_out.nc(); ++c)
{
const double y = r*16.0/11;
const double x = c*18.0/13;
DLIB_TEST(std::abs(gray_out[r][c] - (8*y + x)) < 1e-4);
DLIB_TEST(std::abs(rgb_out[r][c].red - (4*y + 2*x)) <= 1);
DLIB_TEST(std::abs(rgb_out[r][c].green - (2*y + 4*x)) <= 1);
DLIB_TEST(std::abs(rgb_out[r][c].blue - (3*y + x)) <= 1);
}
}
}
class simd_tester : public tester
{
public:
simd_tester() : tester("test_simd", "Run tests on SIMD conversions.") {}
void perform_test() override
{
test_conversions<simd4f, simd4i>();
test_conversions<simd8f, simd8i>();
test_resize();
const simd4f values(1.75f, -2.5f, 0.0f, 17.0f);
const simd4i converted(values);
DLIB_TEST(converted[0] == 1);
DLIB_TEST(converted[1] == -2);
DLIB_TEST(converted[2] == 0);
DLIB_TEST(converted[3] == 17);
const simd4i integers(1, -2, 0, 17);
for (unsigned int i = 0; i < 4; ++i)
DLIB_TEST(integers[i] == converted[i]);
#ifdef DLIB_HAVE_NEON
simd4i assigned;
assigned = values;
for (unsigned int i = 0; i < 4; ++i)
DLIB_TEST(assigned[i] == converted[i]);
// Both raw integer conversions remain available, even if their types alias.
const int32x4_t signed_vector = integers;
const uint32x4_t unsigned_vector = integers;
int32 signed_lanes[4];
uint32 unsigned_lanes[4];
vst1q_s32(signed_lanes, signed_vector);
vst1q_u32(unsigned_lanes, unsigned_vector);
for (unsigned int i = 0; i < 4; ++i)
DLIB_TEST(unsigned_lanes[i] == static_cast<uint32>(signed_lanes[i]));
const float32x4_t float_vector = values;
float float_lanes[4];
vst1q_f32(float_lanes, float_vector);
for (unsigned int i = 0; i < 4; ++i)
DLIB_TEST(float_lanes[i] == values[i]);
// A separate raw float-to-int conversion is possible only with distinct types.
if (!std::is_same<float32x4_t, int32x4_t>::value)
{
const int32x4_t raw_converted = values;
vst1q_s32(signed_lanes, raw_converted);
for (unsigned int i = 0; i < 4; ++i)
DLIB_TEST(signed_lanes[i] == converted[i]);
}
#endif
}
} a;
}