Commit 053c328e authored by MIJIEUX Thomas's avatar MIJIEUX Thomas

Add template alias to choose a correct Arnoldi/Hessenberg combination

parent 2e781393
......@@ -6,6 +6,7 @@
#include "fabulous/data/Block.hpp"
#include "fabulous/utils/Utils.hpp"
#include "fabulous/algo/KrylovAlgo.hpp"
#include "fabulous/restart/DeflatedRestart.hpp"
namespace fabulous {
......@@ -22,7 +23,7 @@ public:
std::string get_name() const override { return "Block GMRES"; }
template< template<class>class ARNOLDI, class Matrix >
template< template<class> class ARNOLDI_HESS, class Matrix >
int solve(const Matrix &A,
int m, int n,
S *B_, int ldb,
......@@ -36,8 +37,10 @@ public:
Block<S> B{m, n, ldb, B_};
Block<S> X{m, n, ldx, X_};
return solve_block<ARNOLDI>( A, B, X, maxMVP, max_krylov_space_size,
epsilon, ortho_scheme, ortho_type );
return solve_block<ARNOLDI_HESS>(
A, B, X, maxMVP, max_krylov_space_size,
epsilon, ortho_scheme, ortho_type
);
}
/**
......@@ -61,7 +64,7 @@ public:
* \return Total Number of Matrix vector product done
* (cumulated size of the spanned Krylov Spaces)
*/
template< template <class> class ARNOLDI, class Matrix, class Block >
template< template<class> class ARNOLDI_HESS, class Matrix, class Block >
int solve_block( Matrix &A, Block &B, Block &X,
const int maxMVP, const int max_krylov_space_size,
const std::vector<P> &epsilon,
......@@ -87,7 +90,7 @@ public:
Base<S> base{dim, size_to_span+nbRHS};
ClassicRestarting<S> restarter{base};
ARNOLDI<S> arnoldi{_logger, restarter, dim, nbRHS, size_to_span};
ARNOLDI_HESS<S> arnoldi{_logger, restarter, dim, nbRHS, size_to_span};
finished = arnoldi.run( A, X, B, size_to_span, epsilon, restarter,
ortho_scheme, ortho_type );
......
#ifndef FABULOUS_ARNOLDI_IB_HPP
#define FABULOUS_ARNOLDI_IB_HPP
namespace fabulous {
template<template<class> class HESSENBERG, class S> class Arnoldi_IB;
};
#include "fabulous/data/Base.hpp"
#include "fabulous/data/BlockWP.hpp"
#include "fabulous/utils/Utils.hpp"
#include "fabulous/utils/Logger.hpp"
#include "fabulous/hessenberg/Hess_IB.hpp" // TODO: templatize HESSENBERGS
#include "fabulous/utils/ClassCompatibility.hpp"
namespace fabulous {
......@@ -17,8 +20,12 @@ namespace fabulous {
*
* \warning This class does NOT support DeflatedRestarting (not implemented)
*/
template <class S> class Arnoldi_IB
template <template<class> class HESSENBERG, class S> class Arnoldi_IB
{
static_assert(
arnoldiXhessenberg<fabulous::Arnoldi_IB, HESSENBERG>::value,
"Arnoldi_IB cannot be combined with this Hessenberg"
);
public:
using value_type = typename Arithmetik<S>::value_type;
using primary_type = typename Arithmetik<S>::primary_type;
......@@ -41,7 +48,7 @@ private:
private:
Logger<P> &_logger;
HessExtended<S> L;
HESSENBERG<S> L;
bool _solution_computed;
Block<S> _solution;
Block<S> _last_Y;
......
......@@ -9,11 +9,11 @@ template<template<class> class HESSENBERG, class S > class Arnoldi;
#include "fabulous/data/Block.hpp"
#include "fabulous/utils/Utils.hpp"
#include "fabulous/utils/Logger.hpp"
#include "fabulous/utils/ClassCompatibility.hpp"
#include "fabulous/restart/DeflatedRestart.hpp"
#include "fabulous/orthogonalization/Arnoldi_Ortho.hpp"
#include "fabulous/hessenberg/Hess_Std_DR.hpp" // TODO templatize HESSENBERGS
namespace fabulous {
/**
......@@ -25,6 +25,10 @@ namespace fabulous {
*/
template<template<class> class HESSENBERG, class S > class Arnoldi
{
static_assert(
arnoldiXhessenberg<fabulous::Arnoldi, HESSENBERG>::value,
"Arnoldi cannot be combined with this Hessenberg"
);
public:
using value_type = typename Arithmetik<S>::value_type;
using primary_type = typename Arithmetik<S>::primary_type;
......
......@@ -22,8 +22,8 @@ namespace fabulous {
*/
struct ChameleonKernI
{
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wunused-parameter"
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wunused-parameter"
/**
* \brief Solve Least square problem \f$ A*Y = B \f$
......@@ -110,7 +110,7 @@ struct ChameleonKernI
return -1;
}
#pragma clang diagnostic pop
#pragma GCC diagnostic pop
}; // end class ChameleonKernI
/*************************************/
......
......@@ -18,8 +18,8 @@ namespace fabulous {
*/
struct ChameleonKernTopLevelI
{
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wunused-parameter"
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wunused-parameter"
/**
* \brief Solve Least square problem
......@@ -42,7 +42,7 @@ struct ChameleonKernTopLevelI
return -1;
}
#pragma clang diagnostic pop
#pragma GCC diagnostic pop
};
/*************************************/
......
......@@ -24,8 +24,8 @@ namespace fabulous {
*/
struct LapackKernI
{
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wunused-parameter"
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wunused-parameter"
template< class S >
static void Tgemm(int m, int n, int k,
......@@ -246,7 +246,7 @@ struct LapackKernI
return -1;
}
#pragma clang diagnostic pop
#pragma GCC diagnostic pop
}; // end class LapackKernI
......
......@@ -110,15 +110,15 @@ void GetKIndexesReal( Block<std::complex<P>> &alpha, Block<S> &beta,
}
}
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wunused-parameter"
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wunused-parameter"
template< class S, class P >
void GetKEigenPairs(Block<std::complex<P>> & alpha,
Block<S> &beta, int k, S target, std::vector<int> &ind)
{
FABULOUS_FATAL_ERROR("Should never be there\nExiting anyways\n");
}
#pragma clang diagnostic pop
#pragma GCC diagnostic pop
template<> //S = float AND P = float
void GetKEigenPairs(Block<std::complex<float>> &alpha, Block<float> &beta,
......
#ifndef FABULOUS_ARNOLDI_HESSENBERG_PAIR_H
#define FABULOUS_ARNOLDI_HESSENBERG_PAIR_H
#include <fabulous/arnoldi/Arnoldi_IB.hpp>
#include <fabulous/arnoldi/Arnoldi_Std_DR.hpp>
#include <fabulous/hessenberg/Hess_Std_DR.hpp>
#include <fabulous/hessenberg/Hess_Std_QR.hpp>
#include <fabulous/hessenberg/Hess_Std_QRDR.hpp>
#include <fabulous/hessenberg/Hess_Std_chamTL_DR.hpp>
#include <fabulous/hessenberg/Hess_Std_cham_QR.hpp>
#include <fabulous/hessenberg/Hess_IB.hpp>
namespace fabulous {
template<class S> using ARNOLDI_STD = Arnoldi<HessStandard, S>;
template<class S> using ARNOLDI_QR = Arnoldi<HessQR, S>;
template<class S> using ARNOLDI_QRDR = Arnoldi<HessQRDR, S>;
template<class S> using ARNOLDI_IB = Arnoldi_IB<HessExtended, S>;
#ifdef FABULOUS_USE_CHAMELEON
template<class S> using ARNOLDI_CHAM_QR = Arnoldi<Hess_cham_QR, S>;
template<class S> using ARNOLDI_CHAM_TL = Arnoldi<HessChamTopLevel, S>;
#endif
};
#endif // FABULOUS_ARNOLDI_HESSENBERG_PAIR_H
#ifndef FABULOUS_CLASS_COMPATIBILITY_H
#define FABULOUS_CLASS_COMPATIBILITY_H
namespace fabulous {
template< template<template<class> class, class> class ARNOLDI,
template<class> class HESSENBERG> struct arnoldiXhessenberg;
template< template<class> class HESSENBERG,
template<class> class RESTARTER > struct hessenbergXrestarter;
};
#include <fabulous/arnoldi/Arnoldi_IB.hpp>
#include <fabulous/arnoldi/Arnoldi_Std_DR.hpp>
#include <fabulous/hessenberg/Hess_Std_DR.hpp>
#include <fabulous/hessenberg/Hess_Std_QR.hpp>
#include <fabulous/hessenberg/Hess_Std_QRDR.hpp>
#include <fabulous/hessenberg/Hess_Std_chamTL_DR.hpp>
#include <fabulous/hessenberg/Hess_Std_cham_QR.hpp>
#include <fabulous/hessenberg/Hess_IB.hpp>
#include <fabulous/restart/DeflatedRestart.hpp>
namespace fabulous {
template<
template<template<class> class, class> class ARNOLDI,
template<class> class HESSENBERG
>
struct arnoldiXhessenberg : public std::false_type {};
#define ARNOLDI_X_HESSENBERG(ARNOLDI, HESSENBERG) \
template<> \
struct arnoldiXhessenberg<ARNOLDI, HESSENBERG> \
: public std::true_type {}; \
ARNOLDI_X_HESSENBERG(Arnoldi, HessStandard);
ARNOLDI_X_HESSENBERG(Arnoldi, HessQR);
ARNOLDI_X_HESSENBERG(Arnoldi, HessQRDR);
#ifdef FABULOUS_USE_CHAMELEON
ARNOLDI_X_HESSENBERG(Arnoldi, Hess_cham_QR);
ARNOLDI_X_HESSENBERG(Arnoldi, HessChamTopLevel);
#endif
ARNOLDI_X_HESSENBERG(Arnoldi_IB, HessExtended);
template<
template<class> class HESSENBERG,
template<class> class RESTARTER
>
struct hessenbergXrestarter : public std::false_type {};
#define HESSENBERG_X_RESTARTER(HESSENBERG, RESTARTER) \
template<> \
struct hessenbergXrestarter<HESSENBERG, RESTARTER> \
: public std::true_type {}; \
HESSENBERG_X_RESTARTER(HessStandard, ClassicRestarting);
HESSENBERG_X_RESTARTER(HessStandard, DeflatedRestarting);
HESSENBERG_X_RESTARTER(HessQRDR, ClassicRestarting);
HESSENBERG_X_RESTARTER(HessQRDR, DeflatedRestarting);
HESSENBERG_X_RESTARTER(HessQR, ClassicRestarting);
#ifdef FABULOUS_USE_CHAMELEON
HESSENBERG_X_RESTARTER(Hess_cham_QR, ClassicRestarting);
HESSENBERG_X_RESTARTER(HessChamTopLevel, ClassicRestarting);
HESSENBERG_X_RESTARTER(HessChamTopLevel, DeflatedRestarting);
#endif
HESSENBERG_X_RESTARTER(HessExtended, ClassicRestarting);
}; // end namespace fabulous
#endif // FABULOUS_CLASS_COMPATIBILITY_H
......@@ -6,9 +6,13 @@
#include <string>
#include <sstream>
#include <complex>
#include <type_traits>
namespace fabulous {
template<bool B> using enable_if_t = typename std::enable_if<B>::type;
#define FABULOUS_DELETE_COPY_MOVE(KLASS_) \
KLASS_& operator=(const KLASS_&) = delete; \
KLASS_& operator=(KLASS_&&) = delete; \
......
......@@ -6,17 +6,11 @@
#include "fabulous/orthogonalization/Arnoldi_Ortho.hpp"
#include "fabulous/arnoldi/Arnoldi_Std_DR.hpp"
#include "fabulous/hessenberg/Hess_Std_DR.hpp"
#include "fabulous/hessenberg/Hess_Std_QRDR.hpp"
#include "fabulous/hessenberg/Hess_Std_QR.hpp"
#include "fabulous/arnoldi/Arnoldi_IB.hpp"
#include "fabulous/hessenberg/Hess_IB.hpp"
#include "fabulous/algo/BGMRes.hpp"
#include "fabulous/algo/BGMResDR.hpp"
#include "fabulous/utils/ArnoldiHessenbergPair.hpp"
#include "fabulous.h"
#include "MatrixApi.hpp"
......@@ -151,10 +145,6 @@ public:
);
}
template<class T> using ARNOLDI_STD = Arnoldi<HessStandard, T>;
template<class T> using ARNOLDI_QR = Arnoldi<HessQR, T>;
template<class T> using ARNOLDI_QRDR = Arnoldi<HessQRDR, T>;
int solve(int nrhs, void *B, int ldb, void *X, int ldx) override
{
S *B_ = reinterpret_cast<S*>(B);
......@@ -174,7 +164,7 @@ public:
{
S *B_ = reinterpret_cast<S*>(B);
S *X_ = reinterpret_cast<S*>(X);
return call_solve<Arnoldi_IB>(nrhs, B_, ldb, X_, ldx);
return call_solve<ARNOLDI_IB>(nrhs, B_, ldb, X_, ldx);
}
int solve_DR(int nrhs, void *B, int ldb, void *X, int ldx,
......
......@@ -5,8 +5,6 @@
using namespace fabulous;
template<class T> using ARNOLDI_TL_CHAM = Arnoldi<HessChamTopLevel, T>;
int main(int argc, char *argv[])
{
std::vector<std::string> args{argv, argv+argc};
......@@ -49,7 +47,7 @@ int main(int argc, char *argv[])
// RHS.display("RHS=");
auto r = runTest_BGMRES_filelog<ARNOLDI_TL_CHAM>(
auto r = runTest_BGMRES_filelog<ARNOLDI_CHAM_TL>(
"young1c_RANDOM_RHS__CHAM_DESC",
mat, RHS, XExact,
maxMVP, max_krylov_space_size, epsilon,
......
......@@ -11,13 +11,7 @@
#include "fabulous/algo/BGMResDR.hpp"
#include "fabulous/algo/BGMRes.hpp"
#include "fabulous/arnoldi/Arnoldi_IB.hpp"
#include "fabulous/arnoldi/Arnoldi_Std_DR.hpp"
#include "fabulous/hessenberg/Hess_Std_DR.hpp"
#include "fabulous/hessenberg/Hess_Std_QR.hpp"
#include "fabulous/hessenberg/Hess_Std_QRDR.hpp"
#include "fabulous/hessenberg/Hess_Std_cham_QR.hpp"
#include "fabulous/hessenberg/Hess_Std_chamTL_DR.hpp"
#include "fabulous/utils/ArnoldiHessenbergPair.hpp"
#include "fabulous/kernel/LapackInterface.hpp"
#define MARK_AS_USED(var_) ((void)(var_))
......@@ -64,7 +58,7 @@ static void check_solution(Matrix &mat, const Block &RHS,
}
}
template<template <class> class ARNOLDI,
template<template <class> class ARNOLDI_HESS,
class Matrix, class Block,
class S = typename Block::value_type,
class P = typename Block::primary_type >
......@@ -80,7 +74,7 @@ static Logger<P> runTest_BGMRES(Matrix &mat, Block &RHS, Block &X_Exact,
Block X{dim, nbRHS};
BGMRes<S> bgmres;
int nb = bgmres.template solve_block<ARNOLDI>(
int nb = bgmres.template solve_block<ARNOLDI_HESS>(
mat, RHS, X,
maxMVP, restart, epsilon,
ortho_scheme, ortho_type
......@@ -93,7 +87,7 @@ static Logger<P> runTest_BGMRES(Matrix &mat, Block &RHS, Block &X_Exact,
return logger;
}
template<template<class> class ARNOLDI,
template<template<class> class ARNOLDI_HESS,
class Matrix, class Block,
class S = typename Block::value_type,
class P = typename Block::primary_type >
......@@ -109,7 +103,7 @@ static Logger<P> runTest_BGMRES_DR(Matrix &mat, Block &RHS, Block &X_Exact,
Block X{dim, nbRHS};
BGMResDR<S> bgmres;
int nb = bgmres.template solve_block<ARNOLDI>(
int nb = bgmres.template solve_block<ARNOLDI_HESS>(
mat, RHS, X,
maxMVP, restart, epsilon,
nbEigenVector, target,
......@@ -146,7 +140,8 @@ template<class P> struct TestRet
int maxMVP; /**< max matrix vector product scheduled */
};
template<template <class> class ARNOLDI, class Matrix, class Block,
template<template <class> class ARNOLDI_HESS,
class Matrix, class Block,
class S = typename Block::value_type,
class P = typename Block::primary_type >
TestRet<P> runTest_BGMRES_filelog(
......@@ -167,7 +162,7 @@ TestRet<P> runTest_BGMRES_filelog(
double tic = Timer::get_time();
BGMRes<S> bgmres;
int nb = bgmres.template solve_block<ARNOLDI>(
int nb = bgmres.template solve_block<ARNOLDI_HESS>(
mat, RHS, X,
maxMVP, restart, epsilon,
ortho_scheme, ortho_type
......@@ -190,7 +185,7 @@ TestRet<P> runTest_BGMRES_filelog(
return TestRet<P>{elapsed, nb, minmax, maxMVP};
}
template<template <class> class ARNOLDI,
template<template <class> class ARNOLDI_HESS,
class Matrix, class Block,
class S = typename Block::value_type,
class P = typename Block::primary_type >
......@@ -212,7 +207,7 @@ TestRet<P> runTest_BGMRES_DR_filelog(
double tic = Timer::get_time();
BGMResDR<S> bgmres;
int nb = bgmres.template solve_block<ARNOLDI>(
int nb = bgmres.template solve_block<ARNOLDI_HESS>(
mat, RHS, X,
maxMVP, restart, epsilon,
nbEigenVector, target,
......
......@@ -4,7 +4,6 @@
using namespace fabulous;
template<class S> using ARNOLDI_STD = Arnoldi<HessStandard, S>;
int main(int argc, char *argv[])
{
......
......@@ -3,7 +3,6 @@
#include "../test_common/UserInputMatrix.hpp"
using namespace fabulous;
template<class S> using ARNOLDI_STD = Arnoldi<HessStandard, S>;
int main(int argc, char *argv[])
{
......
......@@ -3,7 +3,6 @@
#include "../test_common/RandomMatrixLoader.hpp"
using namespace fabulous;
template<class S> using ARNOLDI_STD = Arnoldi<HessStandard, S>;
template<class Matrix>
static void diag_matrix(Matrix &mat) // Not used
......
......@@ -8,7 +8,6 @@
#include "../test_common/MatrixIWLoader.hpp"
using namespace fabulous;
template<class S> using ARNOLDI_STD = Arnoldi<HessStandard, S>;
int main(int argc, char *argv[])
{
......
......@@ -40,7 +40,7 @@ int main(int argc, char *argv[])
MARK_AS_USED(SizeBlock);
MARK_AS_USED(max_ite);
auto r2 = runTest_BGMRES_filelog<Arnoldi_IB>(
auto r2 = runTest_BGMRES_filelog<ARNOLDI_IB>(
"MatCone_IB",
mat, RHS, XExact,
maxiteIB, restart, epsilon,
......
......@@ -5,8 +5,6 @@
using namespace fabulous;
template<class S> using ARNOLDI_STD = Arnoldi<HessStandard, S>;
int main(int argc, char *argv[])
{
std::vector<std::string> args{argv, argv+argc};
......@@ -86,7 +84,7 @@ int main(int argc, char *argv[])
// std::cout<<std::endl;
// }
auto r3 = runTest_BGMRES_filelog<Arnoldi_IB>(
auto r3 = runTest_BGMRES_filelog<ARNOLDI_IB>(
"MatCone_IB",
mat, RHS, XExact,
max_ite, restart, epsilon,
......
......@@ -6,7 +6,6 @@
#include "../test_common/MatrixIWLoader.hpp"
using namespace fabulous;
template<class S> using ARNOLDI_QR = Arnoldi<HessQR, S>;
int main(int argc, char *argv[])
{
......@@ -56,7 +55,7 @@ int main(int argc, char *argv[])
// OrthoScheme::ICGS, OrthoType::BLOCK
// );
auto r3 = runTest_BGMRES_filelog<Arnoldi_IB>(
auto r3 = runTest_BGMRES_filelog<ARNOLDI_IB>(
"MatCone_IB",
mat, RHS, XExact,
max_ite, restart, epsilon,
......
......@@ -4,7 +4,6 @@
#include "../test_common/RandomMatrixLoader.hpp"
using namespace fabulous;
template<class S> using ARNOLDI_QR = Arnoldi<HessQR, S>;
int main(int argc, char *argv[])
{
......
......@@ -4,7 +4,6 @@
#include "../test_common/UserInputMatrix.hpp"
using namespace fabulous;
template<class S> using ARNOLDI_STD = Arnoldi<HessStandard, S>;
int main(int argc, char *argv[])
{
......@@ -53,21 +52,21 @@ int main(int argc, char *argv[])
OrthoScheme::MGS, OrthoType::BLOCK
);
// auto r2 = runTest_BGMRES_filelog<Arnoldi_IB>(
// auto r2 = runTest_BGMRES_filelog<ARNOLDI_IB>(
// "sherman4_IB",
// mat, RHS, XExact,
// maxNbMatVect, restart, epsilon,
// OrthoScheme::MGS, OrthoType::BLOCK
// );
//
// auto r3 = runTest_BGMRES_filelog<Arnoldi_QRInc>(
// auto r3 = runTest_BGMRES_filelog<ARNOLDI_QR>(
// "sherman4_QR",
// mat, RHS, XExact,
// maxNbMatVect, restart, epsilon,
// OrthoScheme::MGS, OrthoType::BLOCK
// );
//
// auto r4 = runTest_BGMRES_DR_filelog<Arnoldi>(
// auto r4 = runTest_BGMRES_DR_filelog<ARNOLDI_STD>(
// "sherman4_DR",
// mat, RHS, XExact,
// maxNbMatVect, restart, epsilon,
......
......@@ -48,7 +48,7 @@ int main(int argc, char *argv[])
if (args.size() > 3)
nbEigenVector = std::stoi(args[3]);
auto r1 = runTest_BGMRES_filelog<Arnoldi_IB>(
auto r1 = runTest_BGMRES_filelog<ARNOLDI_IB>(
"young1c_IB",
mat, RHS, XExact,
max_ite, restart, epsilon,
......@@ -56,7 +56,7 @@ int main(int argc, char *argv[])
);
MARK_AS_USED(nbEigenVector);
// auto r2 = runTest_BGMRES_DR_filelog<Arnoldi>(
// auto r2 = runTest_BGMRES_DR_filelog<ARNOLDI_STD>(
// "younc1c_DR_RUHE",
// mat, RHS, XExact,
// max_ite, restart, epsilon,
......@@ -64,7 +64,7 @@ int main(int argc, char *argv[])
// OrthoScheme::CGS, OrthoType::RUHE
// );
// auto r3 = runTest_BGMRES_DR_filelog<Arnoldi>(
// auto r3 = runTest_BGMRES_DR_filelog<ARNOLDI_STD>(
// "younc1c_DR_BLOCK",
// mat, RHS, XExact,
// max_ite, restart, epsilon,
......
......@@ -6,7 +6,8 @@
#include "../test_common/MatrixMarketLoader.hpp"
using namespace fabulous;
template<class S> using ARNOLDI_STD = Arnoldi<HessStandard, S>;
//template<class S> using ARNOLDI_STD = Arnoldi<HessStandard, S>;
int main(int argc, char *argv[])
{
......
......@@ -63,7 +63,7 @@ int main(int argc, char *argv[])
std::stringstream ss;
ss << matrix_name <<"_Ortho_IB_" << scheme << "_" << choice;
runTest_BGMRES_filelog<Arnoldi_IB>(
runTest_BGMRES_filelog<ARNOLDI_IB>(
ss.str(),
A, RHS, XExact,
MaxNbMVP, restart, epsilon ,
......@@ -106,7 +106,7 @@ int main(int argc, char *argv[])
std::stringstream ss;
ss << matrix_name <<"_Ortho_IB_" << std::to_string(restart);
runTest_BGMRES_filelog<Arnoldi_IB>(
runTest_BGMRES_filelog<ARNOLDI_IB>(
ss.str(),
A, RHS, XExact,
MaxNbMVP, restart, epsilon ,
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment