11 #ifndef CUBBYFLOW_MATRIX_CSR_IMPL_HPP 12 #define CUBBYFLOW_MATRIX_CSR_IMPL_HPP 20 template <
typename T,
typename ME>
25 m_nnz(m1.NonZeroData()),
26 m_rp(m1.RowPointersData()),
27 m_ci(m1.ColumnIndicesData())
32 template <
typename T,
typename ME>
35 return m_m1.GetRows();
38 template <
typename T,
typename ME>
41 return m_m2.GetCols();
44 template <
typename T,
typename ME>
47 const size_t colBegin = m_rp[i];
48 const size_t colEnd = m_rp[i + 1];
52 for (
size_t kk = colBegin; kk < colEnd; ++kk)
55 sum += m_nnz[kk] * m_m2(k, j);
82 const std::initializer_list<std::initializer_list<T>>& lst, T epsilon)
88 template <
size_t R,
size_t C,
typename ME>
100 template <
typename T>
102 : m_size(std::exchange(other.m_size,
Vector2UZ{})),
103 m_nonZeros(std::move(other.m_nonZeros)),
104 m_rowPointers(std::move(other.m_rowPointers)),
105 m_columnIndices(std::move(other.m_columnIndices))
110 template <
typename T>
118 template <
typename T>
121 m_size = other.m_size;
123 m_nonZeros = std::move(other.m_nonZeros);
124 m_rowPointers = std::move(other.m_rowPointers);
125 m_columnIndices = std::move(other.m_columnIndices);
130 template <
typename T>
135 m_rowPointers.clear();
136 m_columnIndices.clear();
137 m_rowPointers.push_back(0);
140 template <
typename T>
143 std::fill(m_nonZeros.begin(), m_nonZeros.end(), s);
146 template <
typename T>
149 m_size = other.m_size;
150 m_nonZeros = other.m_nonZeros;
151 m_rowPointers = other.m_rowPointers;
152 m_columnIndices = other.m_columnIndices;
155 template <
typename T>
159 m_rowPointers.resize(m_size.x + 1);
160 m_nonZeros.resize(numNonZeros);
161 m_columnIndices.resize(numNonZeros);
164 template <
typename T>
166 const std::initializer_list<std::initializer_list<T>>& lst, T epsilon)
168 const size_t numRows = lst.size();
169 const size_t numCols = (numRows > 0) ? lst.begin()->size() : 0;
171 m_size = { numRows, numCols };
173 m_rowPointers.clear();
174 m_columnIndices.clear();
176 auto rowIter = lst.begin();
177 for (
size_t i = 0;
i < numRows; ++
i)
179 assert(numCols == rowIter->size());
181 m_rowPointers.push_back(m_nonZeros.size());
183 auto colIter = rowIter->begin();
184 for (
size_t j = 0;
j < numCols; ++
j)
186 if (std::fabs(*colIter) > epsilon)
188 m_nonZeros.push_back(*colIter);
189 m_columnIndices.push_back(
j);
198 m_rowPointers.push_back(m_nonZeros.size());
201 template <
typename T>
202 template <
size_t R,
size_t C,
typename E>
206 const size_t numRows = other.
GetRows();
207 const size_t numCols = other.
GetCols();
209 m_size = { numRows, numCols };
211 m_columnIndices.clear();
215 for (
size_t i = 0;
i < numRows; ++
i)
217 m_rowPointers.push_back(m_nonZeros.size());
219 for (
size_t j = 0;
j < numCols; ++
j)
221 if (T val = expression(
i,
j); std::fabs(val) > epsilon)
223 m_nonZeros.push_back(val);
224 m_columnIndices.push_back(
j);
229 m_rowPointers.push_back(m_nonZeros.size());
232 template <
typename T>
238 template <
typename T>
241 if (
const ssize_t numRowsToAdd = static_cast<ssize_t>(element.i) -
242 static_cast<ssize_t>(m_size.x) + 1;
245 for (ssize_t
i = 0;
i < numRowsToAdd; ++
i)
251 m_size.y = std::max(m_size.y, element.j + 1);
253 const size_t rowBegin = m_rowPointers[element.i];
254 const size_t rowEnd = m_rowPointers[element.i + 1];
257 std::lower_bound(m_columnIndices.begin() + rowBegin,
258 m_columnIndices.begin() + rowEnd, element.j);
259 auto offset = colIdxIter - m_columnIndices.begin();
261 m_columnIndices.insert(colIdxIter, element.j);
262 m_nonZeros.insert(m_nonZeros.begin() + offset, element.value);
264 for (
size_t i = element.i + 1;
i < m_rowPointers.size(); ++
i)
270 template <
typename T>
274 assert(nonZeros.size() == columnIndices.size());
279 std::vector<std::pair<T, size_t>> zipped;
280 for (
size_t i = 0;
i < nonZeros.size(); ++
i)
282 zipped.emplace_back(nonZeros[
i], columnIndices[i]);
283 m_size.y = std::max(m_size.y, columnIndices[i] + 1);
286 std::sort(zipped.begin(), zipped.end(),
287 [](std::pair<T, size_t> a, std::pair<T, size_t> b) {
288 return a.second < b.second;
291 for (
size_t i = 0;
i < zipped.size(); ++
i)
293 m_nonZeros.push_back(zipped[
i].first);
294 m_columnIndices.push_back(zipped[
i].second);
297 m_rowPointers.push_back(m_nonZeros.size());
300 template <
typename T>
306 template <
typename T>
309 if (
size_t nzIndex = HasElement(element.i, element.j);
310 nzIndex == std::numeric_limits<size_t>::max())
316 m_nonZeros[nzIndex] = element.value;
320 template <
typename T>
323 if (m_size != other.m_size)
328 if (m_nonZeros.size() != other.m_nonZeros.size())
333 for (
size_t i = 0;
i < m_nonZeros.size(); ++
i)
335 if (m_nonZeros[
i] != other.m_nonZeros[
i])
340 if (m_columnIndices[
i] != other.m_columnIndices[
i])
346 for (
size_t i = 0;
i < m_rowPointers.size(); ++
i)
348 if (m_rowPointers[
i] != other.m_rowPointers[
i])
357 template <
typename T>
360 if (m_size != other.m_size)
365 if (m_nonZeros.size() != other.m_nonZeros.size())
370 for (
size_t i = 0;
i < m_nonZeros.size(); ++
i)
372 if (std::fabs(m_nonZeros[
i] - other.m_nonZeros[
i]) > tol)
377 if (m_columnIndices[
i] != other.m_columnIndices[
i])
383 for (
size_t i = 0;
i < m_rowPointers.size(); ++
i)
385 if (m_rowPointers[
i] != other.m_rowPointers[
i])
394 template <
typename T>
400 template <
typename T>
406 template <
typename T>
412 template <
typename T>
418 template <
typename T>
421 return m_nonZeros.size();
424 template <
typename T>
427 return m_nonZeros[
i];
430 template <
typename T>
433 return m_nonZeros[
i];
436 template <
typename T>
439 return m_rowPointers[
i];
442 template <
typename T>
445 return m_columnIndices[
i];
448 template <
typename T>
451 return m_nonZeros.data();
454 template <
typename T>
457 return m_nonZeros.data();
460 template <
typename T>
463 return m_rowPointers.data();
466 template <
typename T>
469 return m_columnIndices.data();
472 template <
typename T>
475 return m_nonZeros.begin();
478 template <
typename T>
481 return m_nonZeros.cbegin();
484 template <
typename T>
487 return m_nonZeros.end();
490 template <
typename T>
493 return m_nonZeros.cend();
496 template <
typename T>
499 return m_rowPointers.begin();
502 template <
typename T>
505 return m_rowPointers.cbegin();
508 template <
typename T>
511 return m_rowPointers.end();
514 template <
typename T>
517 return m_rowPointers.cend();
520 template <
typename T>
523 return m_columnIndices.begin();
526 template <
typename T>
530 return m_columnIndices.cbegin();
533 template <
typename T>
536 return m_columnIndices.end();
539 template <
typename T>
542 return m_columnIndices.cend();
545 template <
typename T>
551 [&](
size_t i) { ret.m_nonZeros[
i] += s; });
556 template <
typename T>
559 return BinaryOp(m, std::plus<T>());
562 template <
typename T>
568 [&](
size_t i) { ret.m_nonZeros[
i] -= s; });
573 template <
typename T>
576 return BinaryOp(m, std::minus<T>());
579 template <
typename T>
585 [&](
size_t i) { ret.m_nonZeros[
i] *= s; });
590 template <
typename T>
591 template <
size_t R,
size_t C,
typename ME>
598 template <
typename T>
604 [&](
size_t i) { ret.m_nonZeros[
i] /= s; });
609 template <
typename T>
615 template <
typename T>
621 template <
typename T>
627 [&](
size_t i) { ret.m_nonZeros[
i] = s - ret.m_nonZeros[
i]; });
632 template <
typename T>
638 template <
typename T>
644 template <
typename T>
650 [&](
size_t i) { ret.m_nonZeros[
i] = s / ret.m_nonZeros[
i]; });
655 template <
typename T>
659 [&](
size_t i) { m_nonZeros[
i] += s; });
662 template <
typename T>
668 template <
typename T>
672 [&](
size_t i) { m_nonZeros[
i] -= s; });
675 template <
typename T>
681 template <
typename T>
685 [&](
size_t i) { m_nonZeros[
i] *= s; });
688 template <
typename T>
689 template <
size_t R,
size_t C,
typename ME>
694 *
this = std::move(result);
697 template <
typename T>
701 [&](
size_t i) { m_nonZeros[
i] /= s; });
704 template <
typename T>
709 [&](
size_t start,
size_t end, T init) {
712 for (
size_t i = start;
i < end; ++
i)
714 result += m_nonZeros[
i];
722 template <
typename T>
728 template <
typename T>
731 const T& (*_min)(
const T&,
const T&) = std::min<T>;
735 [&](
size_t start,
size_t end, T init) {
738 for (
size_t i = start;
i < end; ++
i)
740 result = std::min(result, m_nonZeros[
i]);
748 template <
typename T>
751 const T& (*_max)(
const T&,
const T&) = std::max<T>;
755 [&](
size_t start,
size_t end, T init) {
758 for (
size_t i = start;
i < end; ++
i)
760 result = std::max(result, m_nonZeros[
i]);
768 template <
typename T>
773 [&](
size_t start,
size_t end, T init) {
776 for (
size_t i = start;
i < end; ++
i)
783 CubbyFlow::AbsMin<T>);
786 template <
typename T>
791 [&](
size_t start,
size_t end, T init) {
794 for (
size_t i = start;
i < end; ++
i)
801 CubbyFlow::AbsMax<T>);
804 template <
typename T>
811 [&](
size_t start,
size_t end, T init) {
814 for (
size_t i = start;
i < end; ++
i)
816 result += (*this)(
i,
i);
824 template <
typename T>
825 template <
typename U>
836 nnz[
i] =
static_cast<U
>(m_nonZeros[
i]);
837 ci[
i] = m_columnIndices[
i];
841 [&](
size_t i) { rp[
i] = m_rowPointers[
i]; });
846 template <
typename T>
847 template <
size_t R,
size_t C,
typename E>
855 template <
typename T>
863 template <
typename T>
871 template <
typename T>
879 template <
typename T>
887 template <
typename T>
895 template <
typename T>
896 template <
size_t R,
size_t C,
typename ME>
904 template <
typename T>
912 template <
typename T>
915 size_t nzIndex = HasElement(i, j);
917 if (nzIndex == std::numeric_limits<size_t>::max())
922 return m_nonZeros[nzIndex];
925 template <
typename T>
931 template <
typename T>
937 template <
typename T>
943 ret.m_nonZeros.resize(m, 1.0);
944 ret.m_columnIndices.resize(m);
945 std::iota(ret.m_columnIndices.begin(), ret.m_columnIndices.end(), 0);
946 ret.m_rowPointers.resize(m + 1);
947 std::iota(ret.m_rowPointers.begin(), ret.m_rowPointers.end(), 0);
952 template <
typename T>
955 if (i >= m_size.x || j >= m_size.y)
957 return std::numeric_limits<size_t>::max();
960 const size_t rowBegin = m_rowPointers[
i];
961 const size_t rowEnd = m_rowPointers[i + 1];
963 if (
const auto iter =
BinaryFind(m_columnIndices.begin() + rowBegin,
964 m_columnIndices.begin() + rowEnd,
j);
965 iter != m_columnIndices.begin() + rowEnd)
967 return static_cast<size_t>(iter - m_columnIndices.begin());
970 return std::numeric_limits<size_t>::max();
973 template <
typename T>
974 template <
typename Op>
977 assert(m_size == m.m_size);
981 for (
size_t i = 0; i < m_size.x; ++
i)
983 std::vector<size_t> col;
984 std::vector<double> nnz;
986 auto colIterA = m_columnIndices.begin() + m_rowPointers[
i];
987 auto colIterB = m.m_columnIndices.begin() + m.m_rowPointers[
i];
988 auto colEndA = m_columnIndices.begin() + m_rowPointers[i + 1];
989 auto colEndB = m.m_columnIndices.begin() + m.m_rowPointers[i + 1];
990 auto nnzIterA = m_nonZeros.begin() + m_rowPointers[
i];
991 auto nnzIterB = m.m_nonZeros.begin() + m.m_rowPointers[
i];
993 while (colIterA != colEndA || colIterB != colEndB)
995 if (colIterB == colEndB || *colIterA < *colIterB)
997 col.push_back(*colIterA);
998 nnz.push_back(op(*nnzIterA, 0));
1002 else if (colIterA == colEndA || *colIterA > *colIterB)
1004 col.push_back(*colIterB);
1005 nnz.push_back(op(0, *nnzIterB));
1011 assert(*colIterA == *colIterB);
1013 col.push_back(*colIterB);
1014 nnz.push_back(op(*nnzIterA, *nnzIterB));
1028 template <
typename T>
1034 template <
typename T>
1040 template <
typename T>
1046 template <
typename T>
1052 template <
typename T>
1058 template <
typename T>
1064 template <
typename T>
1070 template <
typename T>
1076 template <
typename T>
1082 template <
typename T,
size_t R,
size_t C,
typename ME>
1089 template <
typename T>
1095 template <
typename T>
void ISub(const T &s)
Subtracts input scalar from this matrix.
Definition: MatrixCSR-Impl.hpp:669
bool operator!=(const MatrixCSR &m) const
Returns true if is not equal to m.
Definition: MatrixCSR-Impl.hpp:932
T Trace() const
Definition: MatrixCSR-Impl.hpp:805
MatrixCSR< U > CastTo() const
Type-casts to different value-typed matrix.
Definition: MatrixCSR-Impl.hpp:826
MatrixCSR< T > operator-(const MatrixCSR< T > &a)
Definition: MatrixCSR-Impl.hpp:1029
void Clear()
Clears the matrix and make it zero-dimensional.
Definition: MatrixCSR-Impl.hpp:131
constexpr size_t GetCols() const
Returns the number of columns.
Definition: MatrixExpression-Impl.hpp:27
Vector2UZ Size() const
Returns the size of this matrix.
Definition: MatrixCSR-Impl.hpp:401
T operator()(size_t i, size_t j) const
Returns matrix element at (i, j).
Definition: MatrixCSR-Impl.hpp:45
const T & NonZero(size_t i) const
Returns i-th non-zero element.
Definition: MatrixCSR-Impl.hpp:425
Matrix expression for CSR matrix-matrix multiplication.
Definition: MatrixCSR.hpp:32
const size_t * RowPointersData() const
Returns constant pointer of the row pointers data.
Definition: MatrixCSR-Impl.hpp:461
T AbsMax() const
Returns absolute maximum among all elements.
Definition: MatrixCSR-Impl.hpp:787
constexpr size_t GetRows() const
Returns the number of rows.
Definition: MatrixExpression-Impl.hpp:21
MatrixCSR & operator+=(const T &s)
Addition assignment with input scalar.
Definition: MatrixCSR-Impl.hpp:856
bool IsEqual(const MatrixCSR &other) const
Definition: MatrixCSR-Impl.hpp:321
MatrixCSRMatrixMul(const MatrixCSR< T > &m1, const ME &m2)
Definition: MatrixCSR-Impl.hpp:21
Vector2< size_t > Vector2UZ
Definition: Matrix.hpp:776
std::vector< size_t > IndexContainerType
Definition: MatrixCSR.hpp:91
void IMul(const T &s)
Multiplies input scalar to this matrix.
Definition: MatrixCSR-Impl.hpp:682
size_t i
Definition: MatrixCSR.hpp:78
T Sum() const
Returns sum of all elements.
Definition: MatrixCSR-Impl.hpp:705
const size_t * ColumnIndicesData() const
Returns constant pointer of the column indices data.
Definition: MatrixCSR-Impl.hpp:467
IndexIterator ColumnIndicesEnd()
Returns the end iterator of the column indices.
Definition: MatrixCSR-Impl.hpp:534
IndexContainerType::const_iterator ConstIndexIterator
Definition: MatrixCSR.hpp:93
T Min() const
Returns minimum among all elements.
Definition: MatrixCSR-Impl.hpp:729
T * NonZeroData()
Returns pointer of the non-zero elements data.
Definition: MatrixCSR-Impl.hpp:449
void IDiv(const T &s)
Divides this matrix with input scalar.
Definition: MatrixCSR-Impl.hpp:698
const size_t & RowPointer(size_t i) const
Returns i-th row pointer.
Definition: MatrixCSR-Impl.hpp:437
MatrixCSR Div(const T &s) const
Returns this matrix / input scalar.
Definition: MatrixCSR-Impl.hpp:599
size_t GetCols() const
Returns number of columns of this matrix.
Definition: MatrixCSR-Impl.hpp:413
bool IsSimilar(const MatrixCSR &other, double tol=std::numeric_limits< double >::epsilon()) const
Definition: MatrixCSR-Impl.hpp:358
ForwardIter BinaryFind(ForwardIter first, ForwardIter last, const T &value, Compare comp)
Definition: CppUtils-Impl.hpp:21
MatrixCSR< T > operator/(const MatrixCSR< T > &a, T b)
Definition: MatrixCSR-Impl.hpp:1090
void Set(const T &s)
Sets whole matrix with input scalar.
Definition: MatrixCSR-Impl.hpp:141
MatrixCSR Add(const T &s) const
Returns this matrix + input scalar.
Definition: MatrixCSR-Impl.hpp:546
MatrixCSR Mul(const T &s) const
Returns this matrix * input scalar.
Definition: MatrixCSR-Impl.hpp:580
Definition: Matrix.hpp:27
void AddElement(size_t i, size_t j, const T &value)
Adds non-zero element to (i, j).
Definition: MatrixCSR-Impl.hpp:233
void SetElement(size_t i, size_t j, const T &value)
Sets non-zero element to (i, j).
Definition: MatrixCSR-Impl.hpp:301
IndexContainerType::iterator IndexIterator
Definition: MatrixCSR.hpp:92
Definition: pybind11Utils.hpp:20
void ParallelFor(IndexType beginIndex, IndexType endIndex, const Function &function, ExecutionPolicy policy)
Makes a for-loop from beginIndex to endIndex in parallel.
Definition: Parallel-Impl.hpp:212
bool IsSquare() const
Returns true if this matrix is a square matrix.
Definition: MatrixCSR-Impl.hpp:395
IndexIterator ColumnIndicesBegin()
Returns the begin iterator of the column indices.
Definition: MatrixCSR-Impl.hpp:521
size_t NumberOfNonZeros() const
Returns the number of non-zero elements.
Definition: MatrixCSR-Impl.hpp:419
MatrixCSR Sub(const T &s) const
Returns this matrix - input scalar.
Definition: MatrixCSR-Impl.hpp:563
NonZeroIterator NonZeroEnd()
Returns the end iterator of the non-zero elements.
Definition: MatrixCSR-Impl.hpp:485
IndexIterator RowPointersEnd()
Returns the end iterator of the row pointers.
Definition: MatrixCSR-Impl.hpp:509
MatrixCSR RMul(const T &s) const
Returns input scalar * this matrix.
Definition: MatrixCSR-Impl.hpp:639
typename NonZeroContainerType::iterator NonZeroIterator
Definition: MatrixCSR.hpp:88
void Compress(const std::initializer_list< std::initializer_list< T >> &lst, T epsilon=std::numeric_limits< T >::epsilon())
Compresses given initializer list lst into a sparse matrix.
Definition: MatrixCSR-Impl.hpp:165
MatrixCSR & operator*=(const T &s)
Multiplication assignment with input scalar.
Definition: MatrixCSR-Impl.hpp:888
static MatrixCSR< T > MakeIdentity(size_t m)
Definition: MatrixCSR-Impl.hpp:938
Element()
Definition: MatrixCSR-Impl.hpp:62
Compressed Sparse Row (CSR) matrix class.
Definition: MatrixCSR.hpp:19
size_t GetCols() const
Number of columns.
Definition: MatrixCSR-Impl.hpp:39
MatrixCSR< T > operator+(const MatrixCSR< T > &a, const MatrixCSR< T > &b)
Definition: MatrixCSR-Impl.hpp:1035
typename NonZeroContainerType::const_iterator ConstNonZeroIterator
Definition: MatrixCSR.hpp:89
MatrixCSR RAdd(const T &s) const
Returns input scalar + this matrix.
Definition: MatrixCSR-Impl.hpp:610
Derived & GetDerived()
Returns actual implementation (the subclass).
Definition: MatrixExpression-Impl.hpp:509
constexpr size_t ZERO_SIZE
Zero size_t.
Definition: Constants.hpp:20
void Reserve(size_t rows, size_t cols, size_t numNonZeros)
Reserves memory space of this matrix.
Definition: MatrixCSR-Impl.hpp:156
void IAdd(const T &s)
Adds input scalar to this matrix.
Definition: MatrixCSR-Impl.hpp:656
T Max() const
Returns maximum among all elements.
Definition: MatrixCSR-Impl.hpp:749
Base class for matrix expression.
Definition: MatrixExpression.hpp:93
std::enable_if_t< std::is_arithmetic< T >::value, T > AbsMax(T x, T y)
Returns the absolute maximum value among the two inputs.
Definition: MathUtils-Impl.hpp:84
std::enable_if_t< std::is_arithmetic< T >::value, T > AbsMin(T x, T y)
Returns the absolute minimum value among the two inputs.
Definition: MathUtils-Impl.hpp:78
size_t GetRows() const
Number of rows.
Definition: MatrixCSR-Impl.hpp:33
MatrixCSR & operator/=(const T &s)
Division assignment with input scalar.
Definition: MatrixCSR-Impl.hpp:905
MatrixCSR RDiv(const T &s) const
Returns input matrix / this scalar.
Definition: MatrixCSR-Impl.hpp:645
Vector< T, 3 > operator*(const Quaternion< T > &q, const Vector< T, 3 > &v)
Returns quaternion q * vector v.
Definition: Quaternion-Impl.hpp:543
const size_t & ColumnIndex(size_t i) const
Returns i-th column index.
Definition: MatrixCSR-Impl.hpp:443
MatrixCSR & operator-=(const T &s)
Subtraction assignment with input scalar.
Definition: MatrixCSR-Impl.hpp:872
MatrixCSR & operator=(const MatrixCSR &other)
Copies to this matrix.
Definition: MatrixCSR-Impl.hpp:111
MatrixCSR()
Constructs an empty matrix.
Definition: MatrixCSR-Impl.hpp:75
MatrixCSR RSub(const T &s) const
Returns input scalar - this matrix.
Definition: MatrixCSR-Impl.hpp:622
T value
Definition: MatrixCSR.hpp:80
T Avg() const
Returns average of all elements.
Definition: MatrixCSR-Impl.hpp:723
bool operator==(const MatrixCSR &m) const
Returns true if is equal to m.
Definition: MatrixCSR-Impl.hpp:926
T operator()(size_t i, size_t j) const
Returns (i,j) element.
Definition: MatrixCSR-Impl.hpp:913
Value ParallelReduce(IndexType beginIndex, IndexType endIndex, const Value &identity, const Function &function, const Reduce &reduce, ExecutionPolicy policy)
Performs reduce operation in parallel.
Definition: Parallel-Impl.hpp:436
NonZeroIterator NonZeroBegin()
Returns the begin iterator of the non-zero elements.
Definition: MatrixCSR-Impl.hpp:473
T AbsMin() const
Returns absolute minimum among all elements.
Definition: MatrixCSR-Impl.hpp:769
void AddRow(const NonZeroContainerType &nonZeros, const IndexContainerType &columnIndices)
Definition: MatrixCSR-Impl.hpp:271
IndexIterator RowPointersBegin()
Returns the begin iterator of the row pointers.
Definition: MatrixCSR-Impl.hpp:497
std::vector< double > NonZeroContainerType
Definition: MatrixCSR.hpp:87
size_t j
Definition: MatrixCSR.hpp:79
size_t GetRows() const
Returns number of rows of this matrix.
Definition: MatrixCSR-Impl.hpp:407