// Copyright (c) Team CharLS.
// SPDX-License-Identifier: BSD-3-Clause
#include "pch.h"
#include "util.h"
#include "../src/scan.h"
using Microsoft::VisualStudio::CppUnitTestFramework::Assert;
using std::numeric_limits;
namespace charls { namespace test {
namespace {
///
/// This is the original algorithm of ISO/IEC 14495-1, A.5.2, Code Segment A.11 (second else branch)
/// It will map signed values to unsigned values.
///
int32_t map_error_value_original(const int32_t error_value) noexcept
{
if (error_value >= 0)
return 2 * error_value;
return -2 * error_value - 1;
}
///
/// This version will be auto optimized by GCC(trunk, not 10.2) and clang(11.0). MSVC will create branches.
///
int32_t map_error_value_alternative1(const int32_t error_value) noexcept
{
const int32_t mapped_value{error_value * 2};
if (error_value >= 0)
return mapped_value;
return (-1 * mapped_value) - 1;
}
///
/// This is the original inverse algorithm of ISO/IEC 14495-1, A.5.2, Code Segment A.11 (second else branch)
/// It will map unsigned values back to unsigned values.
///
int32_t unmap_error_value_original(const int32_t mapped_error_value) noexcept
{
if (mapped_error_value % 2 == 0)
return mapped_error_value / 2;
return (mapped_error_value / -2) - 1;
}
///
/// This version will be auto optimized by GCC(trunk, not 10.2) using a cmove,
/// and clang(11.0) 8 instructions,. MSVC will create branches.
/// Optimized version uses 6 to 5 instructions.
///
int32_t unmap_error_value_alternative1(const int32_t mapped_error_value) noexcept
{
const int32_t error_value{mapped_error_value / 2};
if (mapped_error_value % 2 == 0)
return error_value;
return (-1 * error_value) - 1;
}
}
TEST_CLASS(scan_test)
{
public:
TEST_METHOD(map_error_value_algorithm) // NOLINT
{
map_error_value_algorithm(0);
map_error_value_algorithm(1);
map_error_value_algorithm(-1);
map_error_value_algorithm(numeric_limits::max());
map_error_value_algorithm(numeric_limits::min());
map_error_value_algorithm(numeric_limits::max() / 2);
map_error_value_algorithm(numeric_limits::min() / 2);
}
TEST_METHOD(unmap_error_value_algorithm) // NOLINT
{
unmap_error_value_algorithm(0);
unmap_error_value_algorithm(1);
unmap_error_value_algorithm(2);
unmap_error_value_algorithm(numeric_limits::max());
unmap_error_value_algorithm(numeric_limits::max() - 2);
unmap_error_value_algorithm(numeric_limits::max() - 1);
unmap_error_value_algorithm(numeric_limits::max());
}
TEST_METHOD(map_unmap_error_value_algorithm) // NOLINT
{
map_unmap_error_value_algorithm(0);
map_unmap_error_value_algorithm(1);
map_unmap_error_value_algorithm(-1);
map_unmap_error_value_algorithm(numeric_limits::max());
map_unmap_error_value_algorithm(numeric_limits::min());
map_unmap_error_value_algorithm(numeric_limits::max() / 2);
map_unmap_error_value_algorithm(numeric_limits::min() / 2);
}
private:
static void map_error_value_algorithm(const int32_t error_value)
{
const int32_t actual{map_error_value(error_value)};
const int32_t expected1{map_error_value_original(error_value)};
const int32_t expected2{map_error_value_alternative1(error_value)};
Assert::IsTrue(actual >= 0);
Assert::AreEqual(expected1, actual);
Assert::AreEqual(expected2, actual);
}
static void unmap_error_value_algorithm(const int32_t mapped_error_value)
{
const int32_t actual{unmap_error_value(mapped_error_value)};
const int32_t expected1{unmap_error_value_original(mapped_error_value)};
const int32_t expected2{unmap_error_value_alternative1(mapped_error_value)};
Assert::AreEqual(expected1, actual);
Assert::AreEqual(expected2, actual);
}
static void map_unmap_error_value_algorithm(const int32_t error_value)
{
const int32_t mapped_error_value{map_error_value(error_value)};
const int32_t actual{unmap_error_value(mapped_error_value)};
Assert::AreEqual(error_value, actual);
}
};
}} // namespace charls::test