diff --git a/include/doorkeeper/components/tilecoords.hpp b/include/doorkeeper/components/tilecoords.hpp index 319337d..8c8ad25 100644 --- a/include/doorkeeper/components/tilecoords.hpp +++ b/include/doorkeeper/components/tilecoords.hpp @@ -5,6 +5,7 @@ #include "doorkeeper/implem/vector.hpp" #include #include +#include namespace dk { template @@ -28,11 +29,18 @@ namespace dk { bool operator== ( const TileCoords& parOther ) const; bool operator!= ( const TileCoords& parOther ) const; TileCoords& operator= ( const TileCoords& ) = default; + const CoordinateScalarType& operator[] ( int parIndex ) const; + CoordinateScalarType& operator[] ( int parIndex ); private: coords m_pos; coords m_size; }; + + namespace implem { + CoordinateScalarType sum_mod ( CoordinateScalarType parA, CoordinateScalarType parB, CoordinateScalarType parDiv ) a_pure; + CoordinateScalarType sum_div ( CoordinateScalarType parA, CoordinateScalarType parB, CoordinateScalarType parDiv ) a_pure; + } //namespace implem } //namespace dk #include "doorkeeper/implem/tilecoords.inl" diff --git a/include/doorkeeper/implem/tilecoords.inl b/include/doorkeeper/implem/tilecoords.inl index e7768dd..9c6ff8b 100644 --- a/include/doorkeeper/implem/tilecoords.inl +++ b/include/doorkeeper/implem/tilecoords.inl @@ -1,10 +1,30 @@ namespace dk { + namespace implem { + 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); + } + + inline CoordinateScalarType sum_div (CoordinateScalarType parA, CoordinateScalarType parB, CoordinateScalarType parDiv) { + const auto x = ((parA % parDiv) + (parB % parDiv)) / parDiv; + return parA / parDiv + parB / parDiv + x; + } + } //namespace implem + 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 @@ -13,6 +33,7 @@ namespace dk { m_size(parSize) { DK_ASSERT(m_pos <= m_size); + DK_ASSERT(m_size < coords(std::numeric_limits::max())); } template @@ -61,13 +82,20 @@ namespace dk { template const TileCoords& TileCoords::operator+= (CoordinateScalarType parValue) { - CoordinateScalarType acc(parValue); - CoordinateScalarType div(1); - for (std::size_t z = 0; z < D; ++z) { - m_pos[z] = (m_pos[z] + acc / div) % m_size[z]; - div *= m_size[z]; - acc += m_pos[z]; + std::size_t index = 0; + while (parValue) { + //naïve implementation + //parValue += m_pos[index]; + //m_pos[index] = parValue % (m_size[index] + 1); + //parValue /= (m_size[index] + 1); + //++index; + + //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; } + return *this; } template @@ -79,4 +107,17 @@ namespace dk { 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]; + } } //namespace dk diff --git a/test/unit/tilecoords.cpp b/test/unit/tilecoords.cpp index 56714e5..e64d6a6 100644 --- a/test/unit/tilecoords.cpp +++ b/test/unit/tilecoords.cpp @@ -1,5 +1,6 @@ #include #include "doorkeeper/components/tilecoords.hpp" +#include TEST(increment, tilecoords) { typedef dk::TileCoords<2> tcoords2; @@ -30,3 +31,63 @@ TEST(increment, tilecoords) { EXPECT_EQ(tcoords5(max_coords, max_coords), test); } } + +TEST(advance, tilecoords) { + typedef dk::TileCoords<4> tcoords4; + typedef dk::TileCoords<4>::coords coords4; + + { + const coords4 max_coords(3); + tcoords4 test(max_coords); + + EXPECT_EQ(0, test[0]); + EXPECT_EQ(0, test[1]); + EXPECT_EQ(0, test[2]); + EXPECT_EQ(0, test[3]); + + test += 3; + EXPECT_EQ(3, test[0]); + EXPECT_EQ(0, test[1]); + EXPECT_EQ(0, test[2]); + EXPECT_EQ(0, test[3]); + + test += 3; + EXPECT_EQ(2, test[0]); + EXPECT_EQ(1, test[1]); + EXPECT_EQ(0, test[2]); + EXPECT_EQ(0, test[3]); + + test += 20; + EXPECT_EQ(2, test[0]); + EXPECT_EQ(2, test[1]); + EXPECT_EQ(1, test[2]); + EXPECT_EQ(0, test[3]); + } + + const auto max_scalar = std::numeric_limits::max(); + ASSERT_EQ(std::numeric_limits::max(), max_scalar); + { + coords4 max_coords(max_scalar - 10); + max_coords[1] -= 1; + max_coords[2] -= 2; + max_coords[3] -= 3; + + tcoords4 test(coords4(540, 800, 1200, 710), max_coords); + test += max_scalar; + EXPECT_EQ(549, test[0]); + EXPECT_EQ(801, test[1]); + EXPECT_EQ(1200, test[2]); + EXPECT_EQ(710, test[3]); + } + + { + const coords4 max_coords(max_scalar, 800, max_scalar, max_scalar); + + tcoords4 test(coords4(max_scalar, 800, 1200, 710), max_coords); + test += max_scalar; + EXPECT_EQ(2147483646, test[0]); + EXPECT_EQ(0, test[1]); + EXPECT_EQ(1201, test[2]); + EXPECT_EQ(710, test[3]); + } +} diff --git a/tools/calc_advance.rb b/tools/calc_advance.rb new file mode 100644 index 0000000..590c2d3 --- /dev/null +++ b/tools/calc_advance.rb @@ -0,0 +1,20 @@ +#!/usr/bin/env ruby + +M = 2147483647 + +def calc(arr, m) + v = M + index = 0 + out = arr.dup + while (v > 0) do + v += arr[index] + #puts "index #{index} - v is #{v}, m is #{m[index]}" + out[index] = v % (m[index] + 1) + v /= (m[index] + 1) + index += 1 + end + out +end + +p calc([540, 800, 1200, 710], [M - 10, M - 11, M - 12, M - 13]) +p calc([M, 800, 1200, 710], [M, 800, M, M])