/* Copyright 2015, Michele Santullo
* This file is part of DoorKeeper.
*
* DoorKeeper is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* DoorKeeper 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with DoorKeeper. If not, see .
*/
namespace dk {
namespace implem {
///----------------------------------------------------------------------
///Equivalent to (parA + parB) % parDiv
///----------------------------------------------------------------------
inline CoordinateScalarType sum_mod (CoordinateScalarType parA, CoordinateScalarType parB, CoordinateScalarType parDiv) {
//const auto sum = m_pos[index] + parValue;
//const auto sz = m_size[index] + 1;
//const auto remainder = sum - (sum / sz) * sz;
//m_pos[index] = remainder;
//parValue /= sz;
const auto rounded_sum = sum_div(parA, parB, parDiv) * parDiv;
return std::max(parA, parB) - rounded_sum + std::min(parA, parB);
}
///----------------------------------------------------------------------
///Equivalent to (parA + parB) / parDiv
///----------------------------------------------------------------------
inline CoordinateScalarType sum_div (CoordinateScalarType parA, CoordinateScalarType parB, CoordinateScalarType parDiv) {
DK_ASSERT(parA >= 0 and parB >= 0 and parDiv > 0);
DK_ASSERT(std::numeric_limits::max() - (parA % parDiv) >= (parB % parDiv));
const auto x = ((parA % parDiv) + (parB % parDiv)) / parDiv;
DK_ASSERT(std::numeric_limits::max() - parA / parDiv >= parB / parDiv);
DK_ASSERT(std::numeric_limits::max() - parA / parDiv - parB / parDiv >= x);
return parA / parDiv + parB / parDiv + x;
}
inline CoordinateScalarType sub_mod (CoordinateScalarType parA, CoordinateScalarType parB, CoordinateScalarType parDiv) {
typedef std::make_unsigned::type unsigned_coord;
const unsigned_coord rounded_sum = static_cast(sub_div(parA, parB, parDiv) * parDiv);
const unsigned_coord ret = static_cast(parA) - rounded_sum - static_cast(parB);
return static_cast(ret);
}
inline CoordinateScalarType sub_div (CoordinateScalarType parA, CoordinateScalarType parB, CoordinateScalarType parDiv) {
const auto x = ((parA % parDiv) + (parB % parDiv)) / parDiv;
return parA / parDiv - parB / parDiv + x;
}
} //namespace implem
template
inline CoordinateDistType to_index (const Vector& parPos, const Vector& parUpper) {
CoordinateDistType index = 0;
for (uint32_t d = 0; d < D; ++d) {
auto pos = static_cast(parPos[D - 1 - d]);
for (uint32_t p = 0; p < D - 1 - d; ++p) {
pos *= static_cast(parUpper[p] + 1);
}
index += pos;
}
return index;
}
#if defined(NDEBUG)
template <>
inline CoordinateDistType to_index<2> (const Vector<2>& parPos, const Vector<2>& parUpper) {
return parPos.y() * (parUpper.x() + 1) + parPos.x();
}
#endif
template
inline CoordinateDistType to_index (const TileCoords& parTC) {
return to_index(parTC.position(), parTC.upper());
}
template
TileCoords::TileCoords (const coords& parSize) :
m_pos(CoordinateScalarType(0)),
m_size(parSize)
{
DK_ASSERT(m_pos <= m_size);
DK_ASSERT(m_size < coords(std::numeric_limits::max()));
}
template
TileCoords::TileCoords (const coords& parValue, const coords& parSize) :
m_pos(parValue),
m_size(parSize)
{
DK_ASSERT(m_pos <= m_size);
DK_ASSERT(m_size < coords(std::numeric_limits::max()));
}
template
TileCoords& TileCoords::operator++ () {
static_assert(D > 0, "Dimention must be a positive number");
const coords lower(0);
for (uint32_t d = 0; d < D - 1; ++d) {
++m_pos[d];
if (m_pos[d] > m_size[d])
m_pos[d] = lower[d];
else
return *this;
}
++m_pos[D - 1];
return *this;
}
template
TileCoords TileCoords::operator++ (int) {
TileCoords retval(*this);
++(*this);
return retval;
}
template
TileCoords& TileCoords::operator-- () {
static_assert(D > 0, "Dimention must be a positive number");
const coords lower(0);
for (uint32_t d = 0; d < D; ++d) {
if (m_pos[d] > lower[d]) {
--m_pos[d];
return *this;
}
else {
m_pos[d] = m_size[d];
}
}
++m_pos[D - 1];
return *this;
}
template
TileCoords TileCoords::operator-- (int) {
TileCoords retval(*this);
--(*this);
return retval;
}
template
const TileCoords& TileCoords::operator-= (CoordinateScalarType parValue) {
uint32_t index = 0;
if (parValue > 0) {
while (parValue) {
const auto t = parValue % (m_size[index] + 1);
DK_ASSERT(t >= 0);
const CoordinateScalarType r = (t > m_pos[index] ? 1 : 0);
m_pos[index] = (m_size[index] + 1) * r + m_pos[index] - t;
parValue /= (m_size[index] + 1);
parValue += r;
++index;
}
}
else if (parValue < 0) {
while (parValue) {
const auto new_pos = implem::sub_mod(m_pos[index], parValue, m_size[index] + 1);
parValue = -implem::sub_div(m_pos[index], parValue, m_size[index] + 1);
m_pos[index] = new_pos;
++index;
}
}
return *this;
}
template
const TileCoords& TileCoords::operator+= (CoordinateScalarType parValue) {
uint32_t index = 0;
if (parValue > 0) {
while (parValue) {
//naïve implementation
//parValue += m_pos[index];
//m_pos[index] = parValue % (m_size[index] + 1);
//parValue /= (m_size[index] + 1);
//overflow-aware implementation
const auto new_pos = implem::sum_mod(m_pos[index], parValue, m_size[index] + 1);
parValue = implem::sum_div(m_pos[index], parValue, m_size[index] + 1);
m_pos[index] = new_pos;
++index;
}
}
else if (parValue < 0) {
while (parValue) {
//const auto t = (-parValue) % (m_size[index] + 1);
const auto t = -(parValue % (m_size[index] + 1));
DK_ASSERT(t >= 0);
const CoordinateScalarType r = (t > m_pos[index] ? 1 : 0);
m_pos[index] = (m_size[index] + 1) * r + m_pos[index] - t;
parValue /= (m_size[index] + 1);
parValue -= r;
++index;
}
}
return *this;
}
template
bool TileCoords::operator== (const TileCoords& parOther) const {
return m_pos == parOther.m_pos and m_size == parOther.m_size;
}
template
bool TileCoords::operator!= (const TileCoords& parOther) const {
return not(*this == parOther);
}
template
const CoordinateScalarType& TileCoords::operator[] (int parIndex) const {
DK_ASSERT(parIndex >= 0);
DK_ASSERT(static_cast(parIndex) < D);
return m_pos[parIndex];
}
template
CoordinateScalarType& TileCoords::operator[] (int parIndex) {
DK_ASSERT(parIndex >= 0);
DK_ASSERT(static_cast(parIndex) < D);
return m_pos[parIndex];
}
template
auto TileCoords::position() const -> const coords& {
return m_pos;
}
template
auto TileCoords::upper() const -> const coords& {
return m_size;
}
} //namespace dk