10 #ifndef EIGEN_SUPERLUSUPPORT_H
11 #define EIGEN_SUPERLUSUPPORT_H
15 #define DECL_GSSVX(PREFIX,FLOATTYPE,KEYTYPE) \
17 typedef struct { FLOATTYPE for_lu; FLOATTYPE total_needed; int expansions; } PREFIX##mem_usage_t; \
18 extern void PREFIX##gssvx(superlu_options_t *, SuperMatrix *, int *, int *, int *, \
19 char *, FLOATTYPE *, FLOATTYPE *, SuperMatrix *, SuperMatrix *, \
20 void *, int, SuperMatrix *, SuperMatrix *, \
21 FLOATTYPE *, FLOATTYPE *, FLOATTYPE *, FLOATTYPE *, \
22 PREFIX##mem_usage_t *, SuperLUStat_t *, int *); \
24 inline float SuperLU_gssvx(superlu_options_t *options, SuperMatrix *A, \
25 int *perm_c, int *perm_r, int *etree, char *equed, \
26 FLOATTYPE *R, FLOATTYPE *C, SuperMatrix *L, \
27 SuperMatrix *U, void *work, int lwork, \
28 SuperMatrix *B, SuperMatrix *X, \
29 FLOATTYPE *recip_pivot_growth, \
30 FLOATTYPE *rcond, FLOATTYPE *ferr, FLOATTYPE *berr, \
31 SuperLUStat_t *stats, int *info, KEYTYPE) { \
32 PREFIX##mem_usage_t mem_usage; \
33 PREFIX##gssvx(options, A, perm_c, perm_r, etree, equed, R, C, L, \
34 U, work, lwork, B, X, recip_pivot_growth, rcond, \
35 ferr, berr, &mem_usage, stats, info); \
36 return mem_usage.for_lu; \
39 DECL_GSSVX(s,
float,
float)
40 DECL_GSSVX(c,
float,
std::complex<
float>)
41 DECL_GSSVX(d,
double,
double)
42 DECL_GSSVX(z,
double,
std::complex<
double>)
45 #define EIGEN_SUPERLU_HAS_ILU
48 #ifdef EIGEN_SUPERLU_HAS_ILU
51 #define DECL_GSISX(PREFIX,FLOATTYPE,KEYTYPE) \
53 extern void PREFIX##gsisx(superlu_options_t *, SuperMatrix *, int *, int *, int *, \
54 char *, FLOATTYPE *, FLOATTYPE *, SuperMatrix *, SuperMatrix *, \
55 void *, int, SuperMatrix *, SuperMatrix *, FLOATTYPE *, FLOATTYPE *, \
56 PREFIX##mem_usage_t *, SuperLUStat_t *, int *); \
58 inline float SuperLU_gsisx(superlu_options_t *options, SuperMatrix *A, \
59 int *perm_c, int *perm_r, int *etree, char *equed, \
60 FLOATTYPE *R, FLOATTYPE *C, SuperMatrix *L, \
61 SuperMatrix *U, void *work, int lwork, \
62 SuperMatrix *B, SuperMatrix *X, \
63 FLOATTYPE *recip_pivot_growth, \
65 SuperLUStat_t *stats, int *info, KEYTYPE) { \
66 PREFIX##mem_usage_t mem_usage; \
67 PREFIX##gsisx(options, A, perm_c, perm_r, etree, equed, R, C, L, \
68 U, work, lwork, B, X, recip_pivot_growth, rcond, \
69 &mem_usage, stats, info); \
70 return mem_usage.for_lu; \
73 DECL_GSISX(s,
float,
float)
74 DECL_GSISX(c,
float,
std::complex<
float>)
75 DECL_GSISX(d,
double,
double)
76 DECL_GSISX(z,
double,
std::complex<
double>)
80 template<
typename MatrixType>
81 struct SluMatrixMapHelper;
90 struct SluMatrix : SuperMatrix
97 SluMatrix(
const SluMatrix& other)
101 storage = other.storage;
104 SluMatrix& operator=(
const SluMatrix& other)
106 SuperMatrix::operator=(static_cast<const SuperMatrix&>(other));
108 storage = other.storage;
114 union {
int nnz;
int lda;};
120 void setStorageType(Stype_t t)
123 if (t==SLU_NC || t==SLU_NR || t==SLU_DN)
127 eigen_assert(
false &&
"storage type not supported");
132 template<
typename Scalar>
135 if (internal::is_same<Scalar,float>::value)
137 else if (internal::is_same<Scalar,double>::value)
139 else if (internal::is_same<Scalar,std::complex<float> >::value)
141 else if (internal::is_same<Scalar,std::complex<double> >::value)
145 eigen_assert(
false &&
"Scalar type not supported by SuperLU");
149 template<
typename MatrixType>
150 static SluMatrix Map(MatrixBase<MatrixType>& _mat)
152 MatrixType& mat(_mat.derived());
153 eigen_assert( ((MatrixType::Flags&
RowMajorBit)!=RowMajorBit) &&
"row-major dense matrices are not supported by SuperLU");
155 res.setStorageType(SLU_DN);
156 res.setScalarType<
typename MatrixType::Scalar>();
159 res.nrow = internal::convert_index<int>(mat.rows());
160 res.ncol = internal::convert_index<int>(mat.cols());
162 res.storage.lda = internal::convert_index<int>(MatrixType::IsVectorAtCompileTime ? mat.size() : mat.outerStride());
163 res.storage.values = (
void*)(mat.data());
167 template<
typename MatrixType>
168 static SluMatrix Map(SparseMatrixBase<MatrixType>& a_mat)
170 MatrixType &mat(a_mat.derived());
174 res.setStorageType(SLU_NR);
175 res.nrow = internal::convert_index<int>(mat.cols());
176 res.ncol = internal::convert_index<int>(mat.rows());
180 res.setStorageType(SLU_NC);
181 res.nrow = internal::convert_index<int>(mat.rows());
182 res.ncol = internal::convert_index<int>(mat.cols());
187 res.storage.nnz = internal::convert_index<int>(mat.nonZeros());
188 res.storage.values = mat.valuePtr();
189 res.storage.innerInd = mat.innerIndexPtr();
190 res.storage.outerInd = mat.outerIndexPtr();
192 res.setScalarType<
typename MatrixType::Scalar>();
195 if (MatrixType::Flags &
Upper)
197 if (MatrixType::Flags &
Lower)
200 eigen_assert(((MatrixType::Flags &
SelfAdjoint)==0) &&
"SelfAdjoint matrix shape not supported by SuperLU");
206 template<
typename Scalar,
int Rows,
int Cols,
int Options,
int MRows,
int MCols>
207 struct SluMatrixMapHelper<Matrix<Scalar,Rows,Cols,Options,MRows,MCols> >
209 typedef Matrix<Scalar,Rows,Cols,Options,MRows,MCols> MatrixType;
210 static void run(MatrixType& mat, SluMatrix& res)
212 eigen_assert( ((Options&
RowMajor)!=RowMajor) &&
"row-major dense matrices is not supported by SuperLU");
213 res.setStorageType(SLU_DN);
214 res.setScalarType<Scalar>();
217 res.nrow = mat.rows();
218 res.ncol = mat.cols();
220 res.storage.lda = mat.outerStride();
221 res.storage.values = mat.data();
225 template<
typename Derived>
226 struct SluMatrixMapHelper<SparseMatrixBase<Derived> >
228 typedef Derived MatrixType;
229 static void run(MatrixType& mat, SluMatrix& res)
231 if ((MatrixType::Flags&RowMajorBit)==RowMajorBit)
233 res.setStorageType(SLU_NR);
234 res.nrow = mat.cols();
235 res.ncol = mat.rows();
239 res.setStorageType(SLU_NC);
240 res.nrow = mat.rows();
241 res.ncol = mat.cols();
246 res.storage.nnz = mat.nonZeros();
247 res.storage.values = mat.valuePtr();
248 res.storage.innerInd = mat.innerIndexPtr();
249 res.storage.outerInd = mat.outerIndexPtr();
251 res.setScalarType<
typename MatrixType::Scalar>();
254 if (MatrixType::Flags & Upper)
256 if (MatrixType::Flags & Lower)
259 eigen_assert(((MatrixType::Flags &
SelfAdjoint)==0) &&
"SelfAdjoint matrix shape not supported by SuperLU");
265 template<
typename MatrixType>
266 SluMatrix asSluMatrix(MatrixType& mat)
268 return SluMatrix::Map(mat);
272 template<
typename Scalar,
int Flags,
typename Index>
273 MappedSparseMatrix<Scalar,Flags,Index> map_superlu(SluMatrix& sluMat)
275 eigen_assert((Flags&
RowMajor)==RowMajor && sluMat.Stype == SLU_NR
276 || (Flags&
ColMajor)==ColMajor && sluMat.Stype == SLU_NC);
280 return MappedSparseMatrix<Scalar,Flags,Index>(
281 sluMat.nrow, sluMat.ncol, sluMat.storage.outerInd[outerSize],
282 sluMat.storage.outerInd, sluMat.storage.innerInd, reinterpret_cast<Scalar*>(sluMat.storage.values) );
291 template<
typename _MatrixType,
typename Derived>
297 using Base::m_isInitialized;
299 typedef _MatrixType MatrixType;
300 typedef typename MatrixType::Scalar Scalar;
301 typedef typename MatrixType::RealScalar RealScalar;
302 typedef typename MatrixType::StorageIndex StorageIndex;
318 inline Index rows()
const {
return m_matrix.
rows(); }
319 inline Index cols()
const {
return m_matrix.
cols(); }
322 inline superlu_options_t&
options() {
return m_sluOptions; }
331 eigen_assert(m_isInitialized &&
"Decomposition is not initialized.");
338 derived().analyzePattern(matrix);
339 derived().factorize(matrix);
350 m_isInitialized =
true;
352 m_analysisIsOk =
true;
353 m_factorizationIsOk =
false;
356 template<
typename Stream>
357 void dumpMemory(Stream& )
362 void initFactorization(
const MatrixType& a)
364 set_default_options(&this->m_sluOptions);
366 const Index size = a.rows();
369 m_sluA = internal::asSluMatrix(m_matrix);
376 m_sluEtree.resize(size);
379 m_sluB.setStorageType(SLU_DN);
380 m_sluB.setScalarType<Scalar>();
381 m_sluB.Mtype = SLU_GE;
382 m_sluB.storage.values = 0;
385 m_sluB.storage.lda = internal::convert_index<int>(size);
388 m_extractedDataAreDirty =
true;
394 m_isInitialized =
false;
399 void extractData()
const;
404 Destroy_SuperNode_Matrix(&m_sluL);
406 Destroy_CompCol_Matrix(&m_sluU);
411 memset(&m_sluL,0,
sizeof m_sluL);
412 memset(&m_sluU,0,
sizeof m_sluU);
416 mutable LUMatrixType m_l;
417 mutable LUMatrixType m_u;
418 mutable IntColVectorType m_p;
419 mutable IntRowVectorType m_q;
421 mutable LUMatrixType m_matrix;
422 mutable SluMatrix m_sluA;
423 mutable SuperMatrix m_sluL, m_sluU;
424 mutable SluMatrix m_sluB, m_sluX;
425 mutable SuperLUStat_t m_sluStat;
426 mutable superlu_options_t m_sluOptions;
427 mutable std::vector<int> m_sluEtree;
428 mutable Matrix<RealScalar,Dynamic,1> m_sluRscale, m_sluCscale;
429 mutable Matrix<RealScalar,Dynamic,1> m_sluFerr, m_sluBerr;
430 mutable char m_sluEqued;
433 int m_factorizationIsOk;
435 mutable bool m_extractedDataAreDirty;
438 SuperLUBase(SuperLUBase& ) { }
454 template<
typename _MatrixType>
459 typedef _MatrixType MatrixType;
460 typedef typename Base::Scalar Scalar;
461 typedef typename Base::RealScalar RealScalar;
462 typedef typename Base::StorageIndex StorageIndex;
471 using Base::_solve_impl;
475 explicit SuperLU(
const MatrixType& matrix) : Base()
494 m_isInitialized =
false;
504 void factorize(
const MatrixType& matrix);
507 template<
typename Rhs,
typename Dest>
510 inline const LMatrixType& matrixL()
const
512 if (m_extractedDataAreDirty) this->extractData();
516 inline const UMatrixType& matrixU()
const
518 if (m_extractedDataAreDirty) this->extractData();
522 inline const IntColVectorType& permutationP()
const
524 if (m_extractedDataAreDirty) this->extractData();
528 inline const IntRowVectorType& permutationQ()
const
530 if (m_extractedDataAreDirty) this->extractData();
534 Scalar determinant()
const;
538 using Base::m_matrix;
539 using Base::m_sluOptions;
545 using Base::m_sluEtree;
546 using Base::m_sluEqued;
547 using Base::m_sluRscale;
548 using Base::m_sluCscale;
551 using Base::m_sluStat;
552 using Base::m_sluFerr;
553 using Base::m_sluBerr;
557 using Base::m_analysisIsOk;
558 using Base::m_factorizationIsOk;
559 using Base::m_extractedDataAreDirty;
560 using Base::m_isInitialized;
567 set_default_options(&this->m_sluOptions);
568 m_sluOptions.PrintStat = NO;
569 m_sluOptions.ConditionNumber = NO;
570 m_sluOptions.Trans = NOTRANS;
571 m_sluOptions.ColPerm = COLAMD;
576 SuperLU(SuperLU& ) { }
579 template<
typename MatrixType>
582 eigen_assert(m_analysisIsOk &&
"You must first call analyzePattern()");
589 this->initFactorization(a);
591 m_sluOptions.ColPerm = COLAMD;
593 RealScalar recip_pivot_growth, rcond;
594 RealScalar ferr, berr;
596 StatInit(&m_sluStat);
597 SuperLU_gssvx(&m_sluOptions, &m_sluA, m_q.data(), m_p.data(), &m_sluEtree[0],
598 &m_sluEqued, &m_sluRscale[0], &m_sluCscale[0],
602 &recip_pivot_growth, &rcond,
604 &m_sluStat, &info, Scalar());
605 StatFree(&m_sluStat);
607 m_extractedDataAreDirty =
true;
611 m_factorizationIsOk =
true;
614 template<
typename MatrixType>
615 template<
typename Rhs,
typename Dest>
618 eigen_assert(m_factorizationIsOk &&
"The decomposition is not in a valid state for solving, you must first call either compute() or analyzePattern()/factorize()");
620 const Index size = m_matrix.rows();
621 const Index rhsCols = b.cols();
622 eigen_assert(size==b.rows());
624 m_sluOptions.Trans = NOTRANS;
625 m_sluOptions.Fact = FACTORED;
626 m_sluOptions.IterRefine = NOREFINE;
629 m_sluFerr.resize(rhsCols);
630 m_sluBerr.resize(rhsCols);
635 m_sluB = SluMatrix::Map(b_ref.const_cast_derived());
636 m_sluX = SluMatrix::Map(x_ref.const_cast_derived());
638 typename Rhs::PlainObject b_cpy;
642 m_sluB = SluMatrix::Map(b_cpy.const_cast_derived());
645 StatInit(&m_sluStat);
647 RealScalar recip_pivot_growth, rcond;
648 SuperLU_gssvx(&m_sluOptions, &m_sluA,
649 m_q.data(), m_p.data(),
650 &m_sluEtree[0], &m_sluEqued,
651 &m_sluRscale[0], &m_sluCscale[0],
655 &recip_pivot_growth, &rcond,
656 &m_sluFerr[0], &m_sluBerr[0],
657 &m_sluStat, &info, Scalar());
658 StatFree(&m_sluStat);
660 if(&x.coeffRef(0) != x_ref.data())
673 template<
typename MatrixType,
typename Derived>
674 void SuperLUBase<MatrixType,Derived>::extractData()
const
676 eigen_assert(m_factorizationIsOk &&
"The decomposition is not in a valid state for extracting factors, you must first call either compute() or analyzePattern()/factorize()");
677 if (m_extractedDataAreDirty)
680 int fsupc, istart, nsupr;
681 int lastl = 0, lastu = 0;
682 SCformat *Lstore =
static_cast<SCformat*
>(m_sluL.Store);
683 NCformat *Ustore =
static_cast<NCformat*
>(m_sluU.Store);
686 const Index size = m_matrix.rows();
687 m_l.resize(size,size);
688 m_l.resizeNonZeros(Lstore->nnz);
689 m_u.resize(size,size);
690 m_u.resizeNonZeros(Ustore->nnz);
692 int* Lcol = m_l.outerIndexPtr();
693 int* Lrow = m_l.innerIndexPtr();
694 Scalar* Lval = m_l.valuePtr();
696 int* Ucol = m_u.outerIndexPtr();
697 int* Urow = m_u.innerIndexPtr();
698 Scalar* Uval = m_u.valuePtr();
704 for (
int k = 0; k <= Lstore->nsuper; ++k)
706 fsupc = L_FST_SUPC(k);
707 istart = L_SUB_START(fsupc);
708 nsupr = L_SUB_START(fsupc+1) - istart;
712 for (
int j = fsupc; j < L_FST_SUPC(k+1); ++j)
714 SNptr = &((Scalar*)Lstore->nzval)[L_NZ_START(j)];
717 for (
int i = U_NZ_START(j); i < U_NZ_START(j+1); ++i)
719 Uval[lastu] = ((Scalar*)Ustore->nzval)[i];
721 if (Uval[lastu] != 0.0)
722 Urow[lastu++] = U_SUB(i);
724 for (
int i = 0; i < upper; ++i)
727 Uval[lastu] = SNptr[i];
729 if (Uval[lastu] != 0.0)
730 Urow[lastu++] = L_SUB(istart+i);
736 Lrow[lastl++] = L_SUB(istart + upper - 1);
737 for (
int i = upper; i < nsupr; ++i)
739 Lval[lastl] = SNptr[i];
741 if (Lval[lastl] != 0.0)
742 Lrow[lastl++] = L_SUB(istart+i);
752 m_l.resizeNonZeros(lastl);
753 m_u.resizeNonZeros(lastu);
755 m_extractedDataAreDirty =
false;
759 template<
typename MatrixType>
760 typename SuperLU<MatrixType>::Scalar SuperLU<MatrixType>::determinant()
const
762 eigen_assert(m_factorizationIsOk &&
"The decomposition is not in a valid state for computing the determinant, you must first call either compute() or analyzePattern()/factorize()");
764 if (m_extractedDataAreDirty)
767 Scalar det = Scalar(1);
768 for (
int j=0; j<m_u.cols(); ++j)
770 if (m_u.outerIndexPtr()[j+1]-m_u.outerIndexPtr()[j] > 0)
772 int lastId = m_u.outerIndexPtr()[j+1]-1;
773 eigen_assert(m_u.innerIndexPtr()[lastId]<=j);
774 if (m_u.innerIndexPtr()[lastId]==j)
775 det *= m_u.valuePtr()[lastId];
778 if(PermutationMap(m_p.data(),m_p.size()).determinant()*PermutationMap(m_q.data(),m_q.size()).determinant()<0)
781 return det/m_sluRscale.prod()/m_sluCscale.prod();
786 #ifdef EIGEN_PARSED_BY_DOXYGEN
787 #define EIGEN_SUPERLU_HAS_ILU
790 #ifdef EIGEN_SUPERLU_HAS_ILU
806 template<
typename _MatrixType>
811 typedef _MatrixType MatrixType;
812 typedef typename Base::Scalar Scalar;
813 typedef typename Base::RealScalar RealScalar;
816 using Base::_solve_impl;
820 SuperILU(
const MatrixType& matrix) : Base()
847 void factorize(
const MatrixType& matrix);
849 #ifndef EIGEN_PARSED_BY_DOXYGEN
851 template<
typename Rhs,
typename Dest>
853 #endif // EIGEN_PARSED_BY_DOXYGEN
857 using Base::m_matrix;
858 using Base::m_sluOptions;
864 using Base::m_sluEtree;
865 using Base::m_sluEqued;
866 using Base::m_sluRscale;
867 using Base::m_sluCscale;
870 using Base::m_sluStat;
871 using Base::m_sluFerr;
872 using Base::m_sluBerr;
876 using Base::m_analysisIsOk;
877 using Base::m_factorizationIsOk;
878 using Base::m_extractedDataAreDirty;
879 using Base::m_isInitialized;
886 ilu_set_default_options(&m_sluOptions);
887 m_sluOptions.PrintStat = NO;
888 m_sluOptions.ConditionNumber = NO;
889 m_sluOptions.Trans = NOTRANS;
890 m_sluOptions.ColPerm = MMD_AT_PLUS_A;
893 m_sluOptions.ILU_MILU = SILU;
897 m_sluOptions.ILU_DropRule = DROP_BASIC;
902 SuperILU(SuperILU& ) { }
905 template<
typename MatrixType>
908 eigen_assert(m_analysisIsOk &&
"You must first call analyzePattern()");
915 this->initFactorization(a);
918 RealScalar recip_pivot_growth, rcond;
920 StatInit(&m_sluStat);
921 SuperLU_gsisx(&m_sluOptions, &m_sluA, m_q.data(), m_p.data(), &m_sluEtree[0],
922 &m_sluEqued, &m_sluRscale[0], &m_sluCscale[0],
926 &recip_pivot_growth, &rcond,
927 &m_sluStat, &info, Scalar());
928 StatFree(&m_sluStat);
932 m_factorizationIsOk =
true;
935 template<
typename MatrixType>
936 template<
typename Rhs,
typename Dest>
939 eigen_assert(m_factorizationIsOk &&
"The decomposition is not in a valid state for solving, you must first call either compute() or analyzePattern()/factorize()");
941 const int size = m_matrix.rows();
942 const int rhsCols = b.cols();
943 eigen_assert(size==b.rows());
945 m_sluOptions.Trans = NOTRANS;
946 m_sluOptions.Fact = FACTORED;
947 m_sluOptions.IterRefine = NOREFINE;
949 m_sluFerr.resize(rhsCols);
950 m_sluBerr.resize(rhsCols);
955 m_sluB = SluMatrix::Map(b_ref.const_cast_derived());
956 m_sluX = SluMatrix::Map(x_ref.const_cast_derived());
958 typename Rhs::PlainObject b_cpy;
962 m_sluB = SluMatrix::Map(b_cpy.const_cast_derived());
966 RealScalar recip_pivot_growth, rcond;
968 StatInit(&m_sluStat);
969 SuperLU_gsisx(&m_sluOptions, &m_sluA,
970 m_q.data(), m_p.data(),
971 &m_sluEtree[0], &m_sluEqued,
972 &m_sluRscale[0], &m_sluCscale[0],
976 &recip_pivot_growth, &rcond,
977 &m_sluStat, &info, Scalar());
978 StatFree(&m_sluStat);
980 if(&x.coeffRef(0) != x_ref.data())
989 #endif // EIGEN_SUPERLUSUPPORT_H
Index cols() const
Definition: SparseMatrix.h:132
Definition: Constants.h:314
void compute(const MatrixType &matrix)
Definition: SuperLUSupport.h:336
void analyzePattern(const MatrixType &matrix)
Definition: SuperLUSupport.h:836
A sparse direct LU factorization and solver based on the SuperLU library.
Definition: SuperLUSupport.h:455
A matrix or vector expression mapping an existing array of data.
Definition: Map.h:89
A sparse direct incomplete LU factorization and solver based on the SuperLU library.
Definition: SuperLUSupport.h:807
Definition: Constants.h:196
A base class for sparse solvers.
Definition: SparseSolverBase.h:53
Definition: StdDeque.h:58
Holds information about the various numeric (i.e. scalar) types allowed by Eigen. ...
Definition: NumTraits.h:107
ComputationInfo info() const
Reports whether previous computation was successful.
Definition: SuperLUSupport.h:329
const unsigned int RowMajorBit
Definition: Constants.h:53
void resize(Index rows, Index cols)
Definition: PlainObjectBase.h:252
void analyzePattern(const MatrixType &)
Definition: SuperLUSupport.h:348
Definition: Constants.h:198
void factorize(const MatrixType &matrix)
Definition: SuperLUSupport.h:906
Definition: Constants.h:426
The base class for the direct and incomplete LU factorization of SuperLU.
Definition: SuperLUSupport.h:292
Definition: Constants.h:431
Definition: Constants.h:424
A matrix or vector expression mapping an existing expression.
Definition: Ref.h:186
Definition: Eigen_Colamd.h:54
Expression of a triangular part in a matrix.
Definition: TriangularMatrix.h:186
Definition: Constants.h:312
void factorize(const MatrixType &matrix)
Definition: SuperLUSupport.h:580
superlu_options_t & options()
Definition: SuperLUSupport.h:322
ComputationInfo
Definition: Constants.h:422
Base class for all dense matrices, vectors, and expressions.
Definition: MatrixBase.h:48
void analyzePattern(const MatrixType &matrix)
Definition: SuperLUSupport.h:491
Index rows() const
Definition: SparseMatrix.h:130
Definition: Constants.h:212