/* Daniel R. Reynolds
   SMU Mathematics
   6 August 2020 */

// Inclusions
#include <stdlib.h>
#include <stdio.h>
#include <iostream>
#include <iomanip>
#include <fstream>
#include <armadillo>
using namespace std;

// prototypes of other functions
int GramSchmidt(arma::mat& X);

// Example routine to test the Mat class
int main(int argc, char* argv[]) {

  // create a row vector of length 5
  arma::rowvec a(5);
  a.fill(0.0);

  // create a row vec with an existing data array
  double dat1[5] = {0.1, 0.2, 0.3, 0.4, 0.5};
  arma::rowvec b(dat1, 5);

  // create a column vec with an existing vector
  double dat2[5] = {0.1, 0.2, 0.3, 0.4, 0.5};
  arma::vec b2(dat2, 5);

  // create a row vector using linspace
  arma::rowvec c = arma::linspace<arma::rowvec>(1.0, 5.0, 5);

  // create a column vector using the single integer constructor
  arma::vec h(7);

  // output vectors above to screen
  cout << "writing array of zeros:\n";
  cout << a << endl;
  cout << "writing array of 0.1,0.2,0.3,0.4,0.5:\n";
  cout << b << endl;
  cout << "writing (column) array of 0.1,0.2,0.3,0.4,0.5:\n";
  cout << b2 << endl;
  cout << "writing array of 1,2,3,4,5:\n";
  cout << c << endl;
  cout << "writing a column vector of 7 zeros:\n";
  cout << h << endl;

  // verify that b has size 5
  if (b.n_elem != 5)
    cerr << "error: incorrect matrix size\n";
  if (b.n_cols != 5)
    cerr << "error: incorrect matrix columns\n";
  if (b.n_rows != 1)
    cerr << "error: incorrect matrix rows\n";

  // edit entries of a in both matrix forms, and write each entry of a to screen
  a(0)  = 10.0;
  a(1)  = 15.0;
  a[2] = 20.0;
  a(0,3) = 25.0;
  a.at(4) = 30.0;
  cout << "entries of a, one at a time: should give 10, 15, 20, 25, 30\n";
  for (size_t i=0; i<a.n_elem; i++)
    cout << "  " << a[i] << endl;

  // write the values to file
  cout << "writing this same vector to the file 'a_data':\n";
  a.save("a_data", arma::raw_ascii);

  // Testing MatrixRead() constructor
  double tol = 2.0e-15;
  arma::mat read_test1a = arma::randu(3,4);
  read_test1a.save("tmp.txt", arma::raw_ascii);
  arma::mat read_test1b;
  read_test1b.load("tmp.txt");
  arma::mat read_test1_error = read_test1a - read_test1b;
  if (norm(read_test1_error,"inf") < tol)
    cout << "save/load test 1 passed\n";
  else {
    cout << "save/load test 1 failed, ||error|| = " << norm(read_test1_error,"inf") << endl;
    cout << "  read_test1a = \n" << read_test1a << endl;
    cout << "  read_test1b = \n" << read_test1b << endl;
  }

  arma::mat read_test2a = arma::randu(12,1);
  read_test2a.save("tmp.txt", arma::raw_ascii);
  arma::mat read_test2b;
  read_test2b.load("tmp.txt");
  arma::mat read_test2_error = read_test2a - read_test2b;
  if (norm(read_test2_error,"inf") < tol)
    cout << "save/load test 2 passed\n";
  else {
    cout << "save/load test 2 failed, ||error|| = " << norm(read_test2_error,"inf") << endl;
    cout << "  read_test2a = \n" << read_test2a << endl;
    cout << "  read_test2b = \n" << read_test2b << endl;
  }

  arma::mat read_test3a = arma::randu(1,7);
  read_test3a.save("tmp.txt", arma::raw_ascii);
  arma::mat read_test3b;
  read_test3b.load("tmp.txt");
  arma::mat read_test3_error = read_test3a - read_test3b;
  if (norm(read_test3_error,"inf") < tol)
    cout << "save/load test 3 passed\n";
  else {
    cout << "save/load test 3 failed, ||error|| = " << norm(read_test3_error,"inf") << endl;
    cout << "  read_test3a = \n" << read_test3a << endl;
    cout << "  read_test3b = \n" << read_test3b << endl;
  }

  // Testing copy constructor
  arma::mat B = a;
  cout << "arma::mat B = a uses copy constructor, should give 10, 15, 20, 25, 30\n";
  cout << B << endl;
  // update one entry of a
  cout << "updating the 5th entry of a to be 31:\n";
  a(4) = 31.0;
  cout << "   a = " << a << endl;

  cout << "B should not have changed" << endl;
  cout << "   B = " << B << endl;
  a(4) = 30.0;  // reset to original

  // Testing submatrix copy constructor
  arma::mat B2 = a.submat(0,1,0,3);  // B2 = a(0:0,1:3)
  cout << "arma::mat B2 = a.submat(0,1,0,3) uses submatrix copy constructor" << endl;
  cout << B2 << endl;
  // update entries of B2
  B2(0) = 4.0;
  B2(1) = 3.0;
  B2(2) = 2.0;
  // copy B2 back into a using submatrix copy
  a(0,arma::span(1,3)) = B2;  // a(0,1:3) = B2
  cout << "span copy back into a, should have entries 10 4 3 2 30" << endl;
  cout << a << endl;
  a(1) = 15.0;  // reset to original
  a(2) = 20.0;
  a(3) = 25.0;

  // Test arithmetic operators
  cout << "Testing vector add, should give 1.1, 2.2, 3.3, 4.4, 5.5\n";
  b += c;  // b = b + c
  cout << b << endl;

  cout << "Testing scalar add, should give 2, 3, 4, 5, 6\n";
  c += 1.0;  // c = c + 1
  cout << c << endl;

  cout << "Testing vector subtract, should be 8, 12, 16, 20, 24\n";
  a -= c;  // a = a - c
  cout << a << endl;

  cout << "Testing scalar subtract, should be 0, 1, 2, 3, 4\n";
  c -= 2.0;  // c = c - 2
  cout << c << endl;

  cout << "Testing vector fill, should all be -1\n";
  b.fill(-1.0);  // b = -1*ones(size(b))
  cout << b << endl;

  cout << "Testing vector copy, should be 0, 1, 2, 3, 4\n";
  a = c;
  cout << a << endl;

  cout << "Testing scalar multiply, should be 0, 5, 10, 15, 20\n";
  c *= 5.0;  // c = c * 5
  cout << c << endl;

  cout << "Testing deep copy, should be 0, 1, 2, 3, 4\n";
  cout << a << endl;

  cout << "Testing vector multiply, should be 0, -1, -2, -3, -4\n";
  b %= a;   // b = b.*a
  cout << b << endl;

  cout << "Testing vector divide, should be 0, -2.5, -3.3333, -3.75, -4\n";
  arma::mat j(c);  // j = c
  b += -1.0;
  j /= b;   // j = j ./ b
  b += 1.0;
  cout << j << endl;

  cout << "Testing vector +=, should be 0, 4, 8, 12, 16\n";
  b += c;
  cout << b << endl;

  cout << "Testing scalar +=, should be 1, 6, 11, 16, 21\n";
  c += 1.0;
  cout << c << endl;

  cout << "Testing vector -=, should be 1, 2, 3, 4, 5\n";
  c -= b;
  cout << c << endl;

  cout << "Testing scalar -=, should be -2, -1, 0, 1, 2\n";
  a -= 2.0;
  cout << a << endl;

  cout << "Testing vector %=, should be 0, -4, 0, 12, 32\n";
  a %= b;
  cout << a << endl;

  cout << "Testing scalar *=, should be 2, 4, 6, 8, 10\n";
  c *= 2.0;
  cout << c << endl;

  cout << "Testing vector /=, should be 0, -1, 0, 1.5, 3.2\n";
  j = a;
  j /= c;
  cout << j << endl;

  cout << "Testing scalar /=, should be 1, 2, 3, 4, 5\n";
  j = c;
  j /= 2.0;
  cout << j << endl;

  cout << "Testing vector =, should be 2, 4, 6, 8, 10\n";
  b = c;
  cout << b << endl;

  cout << "Testing scalar fill, should be 3, 3, 3, 3, 3\n";
  a.fill(3.0);
  cout << a << endl;

  cout << "Testing vector norm, should be 14.8324\n";
  cout << "  " << arma::norm(b) << endl;

  cout << "Testing vector infinity norm, should be 10\n";
  cout << "  " << arma::norm(b,"inf") << endl;

  cout << "Testing vector one norm, should be 30\n";
  cout << "  " << arma::norm(b,1) << endl;

  cout << "Testing vector min, should be 2\n";
  cout << "  " << b.min() << endl;

  cout << "Testing vector max, should be 10\n";
  cout << "  " << b.max() << endl;

  B = arma::mat(2,5);
  B(0,arma::span(0,4)) = c;   // B(0,:) = c
  B(1,arma::span(0,4)) = a;   // B(1,:) = a
  B += 2.0;

  cout << "Testing matrix infinity norm, should be 40\n";
  cout << "  " << arma::norm(B,"inf") << endl;

  cout << "Testing matrix one norm, should be 17\n";
  cout << "  " << arma::norm(B,1) << endl;

  cout << "Testing matrix two norm, should be 21.7821\n";
  cout << "  " << arma::norm(B,2) << endl;

  cout << "Testing matrix min, should be 4\n";
  cout << "  " << B.min() << endl;

  cout << "Testing matrix max, should be 12\n";
  cout << "  " << B.max() << endl;

  cout << "Testing dot, should be 90\n";
  cout << "  " << dot(a, c) << endl;

  cout << "Testing logspace, should be 0.01 0.1 1 10 100\n";
  arma::mat e = arma::logspace<arma::rowvec>(-2.0, 2.0, 5);
  cout << e << endl;

  ofstream out;
  out.open("e.txt");
  if(out.is_open())
  {
    out << e;
    cout << "Wrote to file e.txt:\n" << e;
  }
  out.close();

  cout << "Testing randu\n";
  arma::mat f = arma::randu(3,3);
  cout << "f = " << f << endl;
  cout << "Testing write with a temporary result" << endl;
  cout << (f*f+f) << endl;
  cout << "f should be unchanged from above" << endl;
  cout << "f = " << f << endl;
  cout << "Testing f==f, should be 3x3 matrix of ones\n" << (f==f) << endl;
  b.fill(1.0);
  cout << "Testing e==b, should be 0 0 1 0 0\n  " << (e==b) << endl;

  // create and fill in a 10x5 matrix
  arma::mat Y(10,5);
  for (size_t i=0; i<10; i++) {
    Y(i,0) = 1.0*i;
    Y(i,1) = -5.0 + 1.0*i;
    Y(i,2) = 2.0 + 2.0*i;
    Y(i,3) = 20.0 - 1.0*i;
    Y(i,4) = -20.0 + 1.0*i;
  }

  // extract columns from matrix (both ways)
  arma::vec Y0 = Y.col(0);
  arma::mat Y1(Y.col(1));
  arma::vec Y2(Y(arma::span(0,9),2));
  arma::vec Y3 = Y.col(3);
  arma::vec Y4 = Y(arma::span(0,9),4);

  // check the LinearSum routine
  Y4 += Y3;
  cout << "Testing column extraction, should be all zeros:\n";
  cout << Y4 << endl;

  // check linear sum
  arma::mat d = arma::linspace<arma::rowvec>(0.0, 4.0, 5);
  cout << "Testing LinearSum, should be 0.02 1.2 4 23 204:\n";
  arma::mat g = 1.0*d + 2.0*e;
  cout << g << endl;

  // check the pow routine
  d = arma::pow(d,2.0);   // d = d.^2
  cout << "Testing pow, should be 0 1 4 9 16:\n";
  cout << d << endl;
  d = arma::pow(d,0.5);   // d = sqrt(d)
  cout << "Testing pow, should be 0 1 2 3 4:\n";
  cout << d << endl;

  // check the abs routine
  Y1 = arma::abs(Y1);
  cout << "Testing abs, should be the column 5 4 3 2 1 0 1 2 3 4:\n";
  cout << Y1 << endl;

  // check the inplace_trans routine
  cout << "Testing inplace_trans, should be the row 5 4 3 2 1 0 1 2 3 4:\n";
  inplace_trans(Y1);
  cout << Y1 << endl;

  // check the copy-based transpose routine
  cout << "Testing copy-based transpose, should be the column 5 4 3 2 1 0 1 2 3 4:\n";
  Y2 = Y1.t();
  cout << Y2 << endl;

  cout << "Testing GramSchmidt, should work\n";
  arma::mat X = arma::randu(20,3);
  int iret = GramSchmidt(X);
  cout << "  GramSchmidt returned " << iret << ", dot-products are:\n";
  cout << "     <X0,X0> = " << arma::dot(X.col(0),X.col(0)) << endl;
  cout << "     <X0,X1> = " << arma::dot(X.col(0),X.col(1)) << endl;
  cout << "     <X0,X2> = " << arma::dot(X.col(0),X.col(2)) << endl;
  cout << "     <X1,X1> = " << arma::dot(X.col(1),X.col(1)) << endl;
  cout << "     <X1,X2> = " << arma::dot(X.col(1),X.col(2)) << endl;
  cout << "     <X2,X2> = " << arma::dot(X.col(2),X.col(2)) << endl << endl;

  cout << "Testing GramSchmidt, should fail\n";
  arma::mat V = arma::randu(20,3);
  V.col(2) = 2.0*V.col(1);
  iret = GramSchmidt(V);
  cout << "  GramSchmidt returned " << iret << ", dot-products are:\n";
  cout << "     <V0,V0> = " << arma::dot(V.col(0),V.col(0)) << endl;
  cout << "     <V0,V1> = " << arma::dot(V.col(0),V.col(1)) << endl;
  cout << "     <V0,V2> = " << arma::dot(V.col(0),V.col(2)) << endl;
  cout << "     <V1,V1> = " << arma::dot(V.col(1),V.col(1)) << endl;
  cout << "     <V1,V2> = " << arma::dot(V.col(1),V.col(2)) << endl;
  cout << "     <V2,V2> = " << arma::dot(V.col(2),V.col(2)) << endl << endl;

  cout << "Testing matrix product, should be: 9 -1 9 -8 11 6\n";
  arma::mat A_ = arma::eye(6,6);
  A_(0,3) = 2.0;
  A_(1,2) = -1.0;
  A_(2,5) = 1.0;
  A_(3,5) = -2.0;
  A_(4,5) = 1.0;
  arma::mat xtrue_ = arma::linspace(1.0, 6.0, 6);
  arma::mat b_ = A_*xtrue_;
  cout << b_ << endl;

  cout << "Testing backwards substitution solve with provided solution array:\n";
  arma::vec x_(6);
  if (arma::solve(x_, A_, b_)) {
    cout << "  solve succeeded\n";
  } else {
    cout << "  solve failed\n";
  }
  cout << "  ||x - xtrue|| = " << arma::norm(x_ - xtrue_, "inf") << "\n\n";

  cout << "Testing forwards substitution with provided solution array:\n";
  A_.eye();
  A_(3,0) = 2.0;
  A_(2,1) = -1.0;
  A_(5,2) = 1.0;
  A_(5,3) = -2.0;
  A_(5,4) = 1.0;
  b_ = A_*xtrue_;
  x_ = 0.0;
  if (arma::solve(x_, A_, b_)) {
    cout << "  solve succeeded\n";
  } else {
    cout << "  solve failed\n";
  }
  cout << "  ||x - xtrue|| = " << arma::norm(x_ - xtrue_, "inf") << "\n\n";

  cout << "Testing general solver:\n";
  arma::mat C_ = 100.0*arma::eye(9,9) + arma::randu(9,9);
  arma::vec z_ = arma::logspace(-4.0, 4.0, 9);
  arma::mat f_ = C_*z_;
  arma::mat g_ = arma::solve(C_, f_);
  cout << "  ||x - xtrue|| = " << arma::norm(g_ - z_, "inf") << "\n\n";

  cout << "Testing copy-into-col, should be: \n";
  cout << "    0.01   0.02   0.04   0.08\n";
  cout << "    0.1    0.2    0.4    0.8\n";
  cout << "    1      2      4      8\n";
  cout << "   10     20     40     80\n";
  cout << " Actually is:\n";
  arma::mat z2_ = arma::logspace(-2.0, 1.0, 4);
  arma::mat B_(4,4);
  B_.col(0) = z2_;
  z2_ *= 2.0;
  B_.col(1) = z2_;
  z2_ *= 2.0;
  B_.col(2) = z2_;
  z2_ *= 2.0;
  B_.col(3) = z2_;
  cout << B_ << endl;

  cout << "Testing general solver with matrix-valued rhs:\n";
  arma::mat E_ = 100.0*arma::eye(4,4) + arma::randu(4,4);
  arma::mat F_ = E_*B_;
  arma::mat X_ = arma::solve(E_, F_);
  cout << "  ||X - Xtrue|| = " << arma::norm(X_ - B_,"inf") << "\n\n";

  cout << "Testing matrix inverse:\n";
  arma::mat D_ = 10.0*arma::eye(8,8) + arma::randu(8,8);
  arma::mat DDinv_(D_);
  arma::mat Dinv_ = D_.i();
  DDinv_ = D_*Dinv_;
  cout << "  ||I - D*Dinv|| = " << arma::norm(arma::eye(8,8) - DDinv_,"inf") << endl;
  DDinv_ = Dinv_*D_;
  cout << "  ||I - Dinv*D|| = " << arma::norm(arma::eye(8,8) - DDinv_,"inf") << endl;

  return 0;
} // end main


/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
/*                                                                           */
/*  This file is part of the library KASKADE 7                               */
/*    see http://www.zib.de/projects/kaskade7-finite-element-toolbox         */
/*                                                                           */
/*  Copyright (C) 2002-2016 Zuse Institute Berlin                            */
/*                                                                           */
/*  KASKADE 7 is distributed under the terms of the ZIB Academic License.    */
/*    see $KASKADE/academic.txt                                              */
/*                                                                           */
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */

#include <iostream>

#ifndef GRIDTYPE
#define GRIDTYPE 1 // UGGrid
#endif

#if ( GRIDTYPE < 1 ) || ( GRIDTYPE > 4 )
#error "GRIDTYPE must be 1 (UGGrid), 2 (ALUSImplexGrid), 3 (ALUConformGrid) or 4 (AlbertaGrid)"
#endif

#if GRIDTYPE==1
#define CHARGRIDTYPE "UGGrid"
#elif  GRIDTYPE==2
#define CHARGRIDTYPE "ALUSimplexGrid"
#elif  GRIDTYPE==3
#define CHARGRIDTYPE "ALUConformGrid"
#elif  GRIDTYPE==4
#define CHARGRIDTYPE "AlbertaGrid"
#endif

#include <boost/timer/timer.hpp>

#include "dune/grid/config.h"
#include "dune/grid/uggrid.hh"

#ifndef SPACEDIM
#define SPACEDIM 2
#endif

#if ( SPACEDIM < 2 ) || ( SPACEDIM > 3 )
#error 'Dimension SPACEDIM must be 2 or 3'
#endif

#if ( GRIDTYPE==2 ) || ( GRIDTYPE==3 )
#define HAVE_DUNE_ALUGRID 1
#define ENABLE_DUNE_ALUGRID 1
#if SPACEDIM==2
#include "dune/alugrid/2d/alu2dinclude.hh"
#include "dune/alugrid/2d/alugrid.hh"
#include "dune/alugrid/2d/gridfactory.hh"
#else
#include "dune/alugrid/3d/alu3dinclude.hh"
#include "dune/alugrid/3d/alugrid.hh"
#include "dune/alugrid/3d/gridfactory.hh"
#endif
#endif

#if GRIDTYPE==4
#define ENABLE_ALBERTA 1
#define ALBERTA_DIM SPACEDIM
#include "dune/grid/albertagrid/agrid.hh"
#include "dune/grid/albertagrid/gridfactory.hh"
#endif

#include "utilities/enums.hh"

#include "fem/assemble.hh"
#include "fem/norms.hh"
#include "fem/lagrangespace.hh"
//#include "fem/hierarchicspace.hh"   // ContinuousHierarchicMapper
#include "linalg/direct.hh"
#include "linalg/trivialpreconditioner.hh"
//#include "linalg/partialDirectPreconditioner.hh"
#include "linalg/additiveschwarz.hh"
#include "linalg/iluprecond.hh"      // ILUT, ILUK, ARMS
#include "linalg/iccprecond.hh"
#include "linalg/icc0precond.hh"
#include "linalg/hyprecond.hh"       // BoomerAMG, Euclid
#include "linalg/jacobiPreconditioner.hh"
#include "linalg/cg.hh"
#include "mg/hb.hh"
#include "io/vtk.hh"
//#include "io/amira.hh"
#include "utilities/kaskopt.hh"

using namespace Kaskade;
#include "ht.hh"

#if SPACEDIM==3
#include "cubus.hh"
#endif

#if GRIDTYPE==4
#define DEFAULT_REFINEMENTS 14
#else
#if GRIDTYPE==3
#define DEFAULT_REFINEMENTS 7
#else
#if SPACEDIM==2
#define DEFAULT_REFINEMENTS 5
#endif
#if SPACEDIM==3
#define DEFAULT_REFINEMENTS 3
#endif
#endif
#endif

int main(int argc, char *argv[])
{
  using namespace boost::fusion;

  std::cout << "Start heat transfer tutorial program (with GridType=" << CHARGRIDTYPE <<
              " and SpaceDimension=" << SPACEDIM << ")" << std::endl;

  boost::timer::cpu_timer totalTimer;

  int verbosityOpt = 1;
  bool dump = true;
  std::unique_ptr<boost::property_tree::ptree> pt = getKaskadeOptions(argc, argv, verbosityOpt, dump);

  int  refinements = getParameter(pt, "refinements", DEFAULT_REFINEMENTS),
       order       =  getParameter(pt, "order", 2),
       verbosity   = getParameter(pt, "verbosity", 1);
  std::cout << "original mesh shall be refined : " << refinements << " times" << std::endl;
  std::cout << "discretization order           : " << order << std::endl;
  std::cout << "output level (verbosity)       : " << verbosity << std::endl;

  int  direct, onlyLowerTriangle = false;

  DirectType directType;
//  IterateType iterateType = IterateType::CG;
  MatrixProperties property = MatrixProperties::SYMMETRIC;
  PrecondType precondType = PrecondType::NONE;
  std::string empty;

  std::string s("names.type.");
  s += getParameter(pt, "solver.type", empty);
  direct = getParameter(pt, s, 0);

  s = "names.direct." + getParameter(pt, "solver.direct", empty);
  directType = static_cast<DirectType>(getParameter(pt, s, 0));

//  s = "names.iterate." + getParameter(pt, "solver.iterate", empty);
//  iterateType = static_cast<IterateType>(getParameter(pt, s, 0));
  s = "names.preconditioner." + getParameter(pt, "solver.preconditioner", empty);
  precondType = static_cast<PrecondType>(getParameter(pt, s, 0));

  int blocks = getParameter(pt,"blocks",40);
  int nthreads = getParameter(pt,"threads",4);
  double rowBlockFactor = getParameter(pt,"rowBlockFactor",2.0);

  property = MatrixProperties::SYMMETRIC;

  if ( (directType == DirectType::MUMPS)||(directType == DirectType::PARDISO) || ( (precondType == PrecondType::ICC) && !direct ) )
  {
    onlyLowerTriangle = true;
    std::cout <<
      "Note: direct solver MUMPS/PARADISO or PrecondType::ICC preconditioner ===> onlyLowerTriangle is set to true!"
      << std::endl;
  }

  boost::timer::cpu_timer gridTimer;
#if SPACEDIM==2
  //   two-dimensional space: dim=2
  constexpr int dim=2;
#if GRIDTYPE==1
  using Grid = Dune::UGGrid<dim>;
#endif
  // There are alternatives to UGGrid: ALUSimplexGrid (red refinement), ALUConformGrid (bisection)
  // and AlbertaGrid
#if GRIDTYPE==2
  using Grid = Dune::ALUGrid<dim,dim,Dune::ALUGridElementType::simplex,Dune::ALUGridRefinementType::nonconforming>;
#endif
#if GRIDTYPE==3
  using Grid = Dune::ALUGrid<dim,dim,Dune::ALUGridElementType::simplex,Dune::ALUGridRefinementType::conforming>;
#endif
#if GRIDTYPE==4
  using Grid = Dune::AlbertaGrid<dim,dim>;
#endif
  Dune::GridFactory<Grid> factory;

  // vertex coordinates v[0], v[1]
  Dune::FieldVector<double,dim> v;
  v[0]=0; v[1]=0; factory.insertVertex(v);
  v[0]=1; v[1]=0; factory.insertVertex(v);
  v[0]=1; v[1]=1; factory.insertVertex(v);
  v[0]=0; v[1]=1; factory.insertVertex(v);
  // triangle defined by 3 vertex indices
  std::vector<unsigned int> vid(3);
  Dune::GeometryType gt(Dune::GeometryType::simplex,2);
  vid[0]=0; vid[1]=1; vid[2]=2; factory.insertElement(gt,vid);
  vid[0]=0; vid[1]=2; vid[2]=3; factory.insertElement(gt,vid);
  std::unique_ptr<Grid> grid( factory.createGrid() ) ;
  // the coarse grid will be refined three times
  grid->globalRefine(refinements);
  // some information on the refined mesh
  std::cout << std::endl << "Grid: " << grid->size(0) << " triangles, " << std::endl;
  std::cout << "      " << grid->size(1) << " edges, " << std::endl;
  std::cout << "      " << grid->size(2) << " points" << std::endl;

  // a gridmanager is constructed
  // as connector between geometric and algebraic information
  GridManager<Grid> gridManager(std::move(grid));
#else
  //  three-dimensional space: dim=3
  //  we offer 2 geometries:
  //  - very simple:  1 tetrahedron defined by 4 vertices
  //  - more complex: 1 cube defined by 48 tetrahedra, provided in cubus.hh
  constexpr int dim=3;

  //
  //definition of 1 tetrahedron by 4 vertices
#if GRIDTYPE==4
  using Grid = Dune::AlbertaGrid<dim,dim>;
  Dune::GridFactory<Grid> factory;
  // vertex coordinates v[0], v[1]
  Dune::FieldVector<double,dim> v;
  v[0]=0; v[1]=0; v[2]=0; factory.insertVertex(v);
  v[0]=1; v[1]=0; v[2]=0; factory.insertVertex(v);
  v[0]=0; v[1]=1; v[2]=0; factory.insertVertex(v);
  v[0]=0; v[1]=0; v[2]=1; factory.insertVertex(v);
  // tetrahedron defined by 4 vertex indices
  std::vector<unsigned int> vid(4);
  Dune::GeometryType gt(Dune::GeometryType::simplex,dim);
  vid[0]=0; vid[1]=1; vid[2]=2; vid[3]=3; factory.insertElement(gt,vid);
  std::unique_ptr<Grid> grid( factory.createGrid() ) ;
  // the coarse grid will be refined three times
  grid->globalRefine(refinements);
  //
#else
  // definition of a more complex mesh using cubus.hh
  // note: trying to use the following code with AlbertaGrid will lead to a crash
  // during runtime due to a bug in the AlbertaGrid refinement routine
  int heapSize=1024;
#if GRIDTYPE==1
  using Grid = Dune::UGGrid<dim>;
#endif
  // There are alternatives to UGGrid: ALUSimplexGrid (red refinement)
#if GRIDTYPE==2
  using Grid = Dune::ALUGrid<dim,dim,Dune::ALUGridElementType::simplex,Dune::ALUGridRefinementType::nonconforming>;
#endif
#if GRIDTYPE==3
#error ALUCONFORM GridType not supported by DUNE for Dimension=3
#endif
  std::unique_ptr<Grid> grid( RefineGrid<Grid>(refinements, heapSize) );
#endif

  // some information on the refined mesh
  std::cout << std::endl << "Grid: " << grid->size(0) << " tetrahedra, " << std::endl;
  std::cout << "      " << grid->size(1) << " triangles, " << std::endl;
  std::cout << "      " << grid->size(dim-1) << " edges, " << std::endl;
  std::cout << "      " << grid->size(dim) << " points" << std::endl;
  // a gridmanager is constructed
  // as connector between geometric and algebraic information
  GridManager<Grid> gridManager(std::move(grid));
#endif
  std::cout << "computing time for generation of initial mesh: " << boost::timer::format(gridTimer.elapsed()) << "\n";

  using LeafView = Grid::LeafGridView;

  // construction of finite element space for the scalar solution T.
  using H1Space = FEFunctionSpace<ContinuousLagrangeMapper<double,LeafView> >;
  // using H1Space = FEFunctionSpace<ContinuousHierarchicMapper<double,LeafView> >;
  using Spaces = boost::fusion::vector<H1Space const*>;
  using VariableDescriptions = boost::fusion::vector<Variable<SpaceIndex<0>,Components<1>,VariableId<0> > >;
  using VariableSet = VariableSetDescription<Spaces,VariableDescriptions>;
  using Functional = HeatFunctional<double,VariableSet>;
  using Assembler = VariationalFunctionalAssembler<LinearizationAt<Functional> >;
  constexpr int neq = Functional::TestVars::noOfVariables;
  using CoefficientVectors = VariableSet::CoefficientVectorRepresentation<0,neq>::type;
  using LinearSpace = VariableSet::CoefficientVectorRepresentation<0,neq>::type;
  // avoid collision of reference to the Kaskade CG with an equal named enum value in the Alberta headers
  using CG = Kaskade::CG<LinearSpace,LinearSpace>;

  // construction of finite element space for the scalar solution T.
  H1Space temperatureSpace(gridManager,gridManager.grid().leafGridView(),order);

  Spaces spaces(&temperatureSpace);

  // construct variable list.
  // VariableDescription<int spaceId, int components, int Id>
  // spaceId: number of associated FEFunctionSpace
  // components: number of components in this variable
  // Id: number of this variable

  std::string varNames[1] = { "u" };

  VariableSet variableSet(spaces,varNames);

  // construct variational functional

  double kappa = 1.0;
  double q = 1.0;
  Functional F(kappa,q);
  constexpr int nvars = Functional::AnsatzVars::noOfVariables;
  std::cout << std::endl << "no of variables = " << nvars << std::endl;
  std::cout << "no of equations = " << neq   << std::endl;
  size_t dofs = variableSet.degreesOfFreedom(0,nvars);
  std::cout << "number of degrees of freedom = " << dofs   << std::endl;

  //construct Galerkin representation
  Assembler assembler(gridManager,spaces);
  VariableSet::VariableSet u(variableSet);
  VariableSet::VariableSet du(variableSet);

  size_t nnz = assembler.nnz(0,neq,0,nvars,onlyLowerTriangle);
  std::cout << "number of nonzero elements in the stiffness matrix: " << nnz << std::endl << std::endl;

  boost::timer::cpu_timer assembTimer;
  CoefficientVectors solution(VariableSet::CoefficientVectorRepresentation<0,neq>::init(spaces));
  solution = 0;

  // UG seems to admit concurrent reads while claiming not to be thread safe. In this case we enforce multithreading during assembly.
  gridManager.enforceConcurrentReads(std::is_same<Grid,Dune::UGGrid<dim> >::value);
  assembler.setNSimultaneousBlocks(blocks);
  assembler.setRowBlockFactor(rowBlockFactor);
  assembler.assemble(linearization(F,u),assembler.MATRIX|assembler.RHS|assembler.VALUE,nthreads,verbosity);
  std::cout << "computing time for assemble: " << boost::timer::format(assembTimer.elapsed()) << "\n";

  CoefficientVectors rhs(assembler.rhs());
  AssembledGalerkinOperator<Assembler,0,neq,0,nvars> A(assembler, onlyLowerTriangle);

  // matrix may be used in triplet format, e.g.,
  // MatrixAsTriplet<double> tri = A.get<MatrixAsTriplet<double> >();
  //     for (k=0; k< nnz; k++)
  //       {
  //         printf("%3d %3d %e\n", tri.ridx[k], tri.cidx[k], tri.data[k]);
  //       }

  if (direct)
  {
    boost::timer::cpu_timer directTimer;
    directInverseOperator(A,directType,property).applyscaleadd(-1.0,rhs,solution);
    u.data = solution.data;
    std::cout << "computing time for direct solve: " << boost::timer::format(directTimer.elapsed()) << "\n";
  }
  else
  {
    boost::timer::cpu_timer iteTimer;
    Dune::InverseOperatorResult res;
    const DefaultDualPairing<LinearSpace,LinearSpace> defaultScalarProduct{};
    int iteSteps = getParameter(pt, "solver.iteMax", 2000);
    double iteEps = getParameter(pt, "solver.iteEps", 1.0e-10);
    StrakosTichyPTerminationCriterion<double> termination(iteEps,iteSteps);
    int lookAhead;
    switch (precondType)
    {
      case PrecondType::NONE:
      case PrecondType::HB:   lookAhead=50; break;
      default:                lookAhead=3; break;
    }
    lookAhead = getParameter(pt, "solver.lookAhead", lookAhead);
    termination.setLookAhead(lookAhead);

    switch (precondType)
    {
      case PrecondType::NONE:
      {
        std::cout << "selected preconditioner: NONE" << std::endl;
        TrivialPreconditioner<AssembledGalerkinOperator<Assembler,0,neq,0,nvars> > trivial;
        CG cg(A,trivial,defaultScalarProduct,termination,verbosity);
        cg.apply(solution,rhs,res);
      }
      break;
      case PrecondType::ADDITIVESCHWARZ:
      {
        std::cout << "selected preconditioner: ADDITIVESCHWARZ" << std::endl;
        std::pair<size_t,size_t> idx = temperatureSpace.mapper().globalIndexRange(gridManager.grid().leafIndexSet().geomTypes(dim)[0]);
        AdditiveSchwarzPreconditioner<AssembledGalerkinOperator<Assembler,0,neq,0,nvars> > addschwarz(A,idx.first,idx.second,verbosity);
        CG cg(A,addschwarz,defaultScalarProduct,termination,verbosity);
        cg.apply(solution,rhs,res);
      }
      break;
      case PrecondType::ILUT:
      {
        std::cout << "selected preconditioner: ILUT" << std::endl;
//        std::cout << "Note that this preconditioner combined with the BICGSTAB solver" << std::endl;
        std::cout << "needs matrix.property = GENERAL" << std::endl;
        int lfil = getParameter(pt, "solver.ILUT.lfil", 140);
        double dropTol = getParameter(pt, "solver.ILUT.dropTol", 0.01);
        ILUTPreconditioner<AssembledGalerkinOperator<Assembler,0,neq,0,nvars> > ilut(A,lfil,dropTol,verbosity);
        CG cg(A,ilut,defaultScalarProduct,termination,verbosity);
        cg.apply(solution,rhs,res);
//         Dune::BiCGSTABSolver<LinearSpace> cg(A,ilut,iteEps,iteSteps,verbosity);
//         cg.apply(solution,rhs,res);
      }
      break;
      case PrecondType::ILUK:
      {
        std::cout << "selected preconditioner: ILUK" << std::endl;
//        std::cout << "Note that this preconditioner combined with the BICGSTAB solver" << std::endl;
        std::cout << "needs matrix.property = GENERAL" << std::endl;
        int fill_lev = getParameter(pt, "solver.ILUK.fill_lev", 3);
        ILUKPreconditioner<AssembledGalerkinOperator<Assembler,0,neq,0,nvars> > iluk(A,fill_lev,verbosity);
        CG cg(A,iluk,defaultScalarProduct,termination,verbosity);
        cg.apply(solution,rhs,res);
//         Dune::BiCGSTABSolver<LinearSpace> cg(A,iluk,iteEps,iteSteps,verbosity);
//         cg.apply(solution,rhs,res);
      }
      break;
      case PrecondType::ARMS:
      {
        int lfil = getParameter(pt, "solver.ARMS.lfil", 140);
        int lev_reord = getParameter(pt, "solver.ARMS.lev_reord", 1);
        double dropTol = getParameter(pt, "solver.ARMS.dropTol", 0.01);
        double tolind = getParameter(pt, "solver.ARMS.tolind", 0.2);
        ARMSPreconditioner<AssembledGalerkinOperator<Assembler,0,neq,0,nvars> > iluk(A,lfil,dropTol,lev_reord,tolind,verbosity);
        CG cg(A,iluk,defaultScalarProduct,termination,verbosity);
        cg.apply(solution,rhs,res);
      }
      break;
      case PrecondType::ICC:
      {
        std::cout << "selected preconditioner: ICC" << std::endl;
        if (property != MatrixProperties::SYMMETRIC)
        {
          std::cout << "PrecondType::ICC preconditioner of TAUCS lib has to be used with matrix.property==MatrixProperties::SYMMETRIC\n";
          std::cout << "i.e., call the executable with option --solver.property MatrixProperties::SYMMETRIC\n\n";
        }
        double dropTol = getParameter(pt, "solver.ICC.dropTol", 0.01);;
        ICCPreconditioner<AssembledGalerkinOperator<Assembler,0,neq,0,nvars> > icc(A,dropTol);
        CG cg(A,icc,defaultScalarProduct,termination,verbosity);
        cg.apply(solution,rhs,res);
      }
      break;
      case PrecondType::ICC0:
      {
        std::cout << "selected preconditioner: ICC0" << std::endl;
        ICC_0Preconditioner<AssembledGalerkinOperator<Assembler,0,neq,0,nvars> > icc0(A);
        CG cg(A,icc0,defaultScalarProduct,termination,verbosity);
        cg.apply(solution,rhs,res);
      }
      break;
      case PrecondType::HB:
      {
        std::cout << "selected preconditioner: HB" << std::endl;
        HierarchicalBasisPreconditioner<Grid,AssembledGalerkinOperator<Assembler,0,neq,0,nvars>::range_type, AssembledGalerkinOperator<Assembler,0,neq,0,nvars>::range_type > hb(gridManager.grid());
        CG cg(A,hb,defaultScalarProduct,termination,verbosity);
        cg.apply(solution,rhs,res);
      }
      break;
      case PrecondType::BOOMERAMG:
      {
        int steps = getParameter(pt, "solver.BOOMERAMG.steps", iteSteps);
        int coarsentype = getParameter(pt, "solver.BOOMERAMG.coarsentype", 21);
        int interpoltype = getParameter(pt, "solver.BOOMERAMG.interpoltype", 0);
        int cycleType = getParameter(pt, "solver.BOOMERAMG.cycleType", 1);
        int relaxType = getParameter(pt, "solver.BOOMERAMG.relaxType", 3);
        int variant = getParameter(pt, "solver.BOOMERAMG.variant", 0);
        int overlap = getParameter(pt, "solver.BOOMERAMG.overlap", 1);
        double tol = getParameter(pt, "solver.BOOMERAMG.tol", iteEps);
        double strongThreshold = getParameter(pt, "solver.BOOMERAMG.strongThreshold", (dim==2)?0.25:0.6);
        BoomerAMG<AssembledGalerkinOperator<Assembler,0,neq,0,nvars> >
                  boomerAMGPrecon(A,steps,coarsentype,interpoltype,tol,cycleType,relaxType,
                  strongThreshold,variant,overlap,1,verbosity);
        CG cg(A,boomerAMGPrecon,defaultScalarProduct,termination,verbosity);
        cg.apply(solution,rhs,res);
//         Dune::LoopSolver<LinearSpace> cg(A,boomerAMGPrecon,iteEps,iteSteps,verbosity);
//         cg.apply(solution,rhs,res);
      }
      break;
      case PrecondType::EUCLID:
      {
        std::cout << "selected preconditioner: EUCLID" << std::endl;
        int level      = getParameter(pt, "solver.EUCLID.level",1);
        double droptol = getParameter(pt, "solver.EUCLID.droptol",0.01);
        int printlevel = 0;
        if (verbosity>2) printlevel=verbosity-2;
        printlevel = getParameter(pt,"solver.EUCLID.printlevel",printlevel);
        int bj = getParameter(pt, "solver.EUCLID.bj",0);
        Euclid<AssembledGalerkinOperator<Assembler,0,neq,0,nvars> > EuclidPrecon(A,level,droptol,printlevel,bj,verbosity);
        CG cg(A,EuclidPrecon,defaultScalarProduct,termination,verbosity);
        cg.apply(solution,rhs,res);
     }
      break;
      case PrecondType::JACOBI:
      default:
      {
        std::cout << "selected preconditioner: JACOBI" << std::endl;
        JacobiPreconditioner<AssembledGalerkinOperator<Assembler,0,neq,0,nvars> > jacobi(A,1.0);
        CG cg(A,jacobi,defaultScalarProduct,termination,verbosity);
        cg.apply(solution,rhs,res);
      }
      break;
    }
    solution *= -1.0;
    u.data = solution.data;

    std::cout << "iterative solve eps= " << iteEps << ": "
              << (res.converged?"converged":"failed") << " after "
              << res.iterations << " steps, rate="
              << res.conv_rate << ", computing time=" << (double)(iteTimer.elapsed().user)/1e9 << "s\n";
  }

  // compute L2 norm of the solution
  boost::timer::cpu_timer outputTimer;
  L2Norm l2Norm;
  std::cout << "L2norm(solution) = " << l2Norm(boost::fusion::at_c<0>(u.data)) << std::endl;

  // output of solution in VTK format for visualization,
  // the data are written as ascii stream into file temperature.vtu,
  // possible is also binary
  writeVTKFile(u,"temperature",IoOptions().setOrder(order).setPrecision(7));

  std::cout << "graphical output finished, data in VTK format is written into file temperature.vtu \n";

  // output of solution for Amira visualization,
  // the data are written in binary format into file temperature.am,
  // possible is also ascii
  // IoOptions options;
  // options.outputType = IoOptions::ascii;
  // LeafView leafGridView = gridManager.grid().leafGridView();
  // writeAMIRAFile(leafGridView,variableSet,u,"temperature",options);

  std::cout << "computing time for output: " << boost::timer::format(outputTimer.elapsed()) << "\n";

  std::cout << "total computing time: " << boost::timer::format(totalTimer.elapsed()) << "\n";
  std::cout << "End heat transfer tutorial program" << std::endl;
}


/*!
 * @file right_hand_side.hpp
 * @brief Contains implementation of right-hand side.
 * @author Konrad Simon
 * @date August 2019
 */

#ifndef INCLUDE_RIGHT_HAND_SIDE_HPP_
#define INCLUDE_RIGHT_HAND_SIDE_HPP_

// Deal.ii
#include <deal.II/base/function.h>

// STL
#include <cmath>
#include <fstream>

// My Headers
#include "coefficients.h"

namespace Coefficients
{
using namespace dealii;

/*!
 * @class RightHandSide
 * @brief Class implements scalar right-hand side function.
 *
 * The right-hand side represents some external forcing parameter.
 */
template <int dim>
class RightHandSide : public Function<dim>
{
public:
	RightHandSide() : Function<dim>() {}

	virtual double value(const Point<dim> &p,
						const unsigned int component = 0) const override;
	virtual void value_list(const std::vector<Point<dim>> &points,
							std::vector<double>  &values,
							const unsigned int component = 0) const override;
};

template <int dim>
double
RightHandSide<dim>::value(const Point<dim>& /*p*/,
							   const unsigned int /*component*/) const
{
	double return_value = 2.0;

	return return_value;
}

template <int dim>
void
RightHandSide<dim>::value_list(const std::vector<Point<dim>> &points,
								std::vector<double>  &values,
								const unsigned int /*component = 0*/) const
{
	Assert (points.size() == values.size(),
			ExcDimensionMismatch (points.size(), values.size()) );

	for ( unsigned int p=0; p<points.size(); ++p)
	{
		values[p] = 2.0;
	} // end ++p
}

} // end namespace Coefficients

#endif /* INCLUDE_RIGHT_HAND_SIDE_HPP_ */


/**
 * @file laxwendroffscheme_main.cc
 * @brief NPDE homework "LaxWendroffScheme" code
 * @author Oliver Rietmann
 * @date 29.04.2019
 * @copyright Developed at ETH Zurich
 */

#include <Eigen/Core>
#include <fstream>
#include <iostream>

#include "laxwendroffscheme.h"

using namespace LaxWendroffScheme;

const static Eigen::IOFormat CSVFormat(Eigen::FullPrecision,
                                       Eigen::DontAlignCols, ", ", "\n");

int main() {
  Eigen::VectorXi M(6);
  M << 20, 40, 80, 160, 320, 640;

  Eigen::VectorXd error_LaxWendroffRP = numexpLaxWendroffRP(M);
  Eigen::VectorXd error_LaxWendroffSmoothU0 = numexpLaxWendroffSmoothU0(M);
  Eigen::VectorXd error_GodunovSmoothU0 = numexpGodunovSmoothU0(M);

  std::ofstream file;
  file.open("convergence.csv");
  file << M.transpose().format(CSVFormat) << std::endl;
  file << error_LaxWendroffRP.transpose().format(CSVFormat) << std::endl;
  file << error_LaxWendroffSmoothU0.transpose().format(CSVFormat) << std::endl;
  file << error_GodunovSmoothU0.transpose().format(CSVFormat) << std::endl;
  file.close();
  std::cout << "Generated " CURRENT_BINARY_DIR "/convergence.csv" << std::endl;

  // To plot from convergence.csv uncomment this:
  // std::system("python3 " CURRENT_SOURCE_DIR "/plot.py " CURRENT_BINARY_DIR
  // "/convergence.csv " CURRENT_BINARY_DIR "/convergence.eps");

  return 0;
}


//C++ implementation code of finding the product of large numbers using Boost Lib.
//The Boost.Multiprecision library can be used for computations requiring precision exceeding that of standard built-in types such as float, double and long double.
//For extended-precision calculations, Boost.Multiprecision supplies a template data type called cpp_dec_float.

#include <boost/multiprecision/cpp_int.hpp>
using namespace boost::multiprecision; //here we are using boost as namespace that includes boost libarary
using namespace std;

//the below line is the function where the product of two numbers will take place.
int128_t boost_product(long long A, long long B)
{
    int128_t ans = (int128_t) A * B;
    return ans;
}

//in main function we tooked two long data type numbers and then called our boost_product() function.
int main()
{
    long long first = 98745636214564698;
    long long second=7459874565236544789;
    cout << "Product of "<< first << " * "
         << second << " = \n"
         << boost_product(first,second) ;
    return 0;
}