/* 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