/* ---------------------------------------------------------------------
 *
 * Copyright (C) 2006 - 2015 by the deal.II authors
 *
 * This file is part of the deal.II library.
 *
 * The deal.II library is free software; you can use it, redistribute
 * it, and/or modify it under the terms of the GNU Lesser General
 * Public License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 * The full text of the license can be found in the file LICENSE at
 * the top level of the deal.II distribution.
 *
 * ---------------------------------------------------------------------

 *
 * Authors: Yan Li, Wolfgang Bangerth, Texas A&M University, 2006
 */

// This program is an adaptation of step-20 and includes some technique of DG
// methods from step-12. A good part of the program is therefore very similar
// to step-20 and we will not comment again on these parts. Only the new stuff
// will be discussed in more detail.

// @sect3{Include files}

// All of these include files have been used before:
#include <deal.II/base/quadrature_lib.h>
#include <deal.II/base/logstream.h>
#include <deal.II/base/function.h>

#include <deal.II/lac/block_vector.h>
#include <deal.II/lac/full_matrix.h>
#include <deal.II/lac/block_sparse_matrix.h>
#include <deal.II/lac/solver_cg.h>
#include <deal.II/lac/precondition.h>
#include <deal.II/lac/constraint_matrix.h>
#include <deal.II/lac/sparse_direct.h>

#include <deal.II/grid/tria.h>
#include <deal.II/grid/grid_generator.h>
#include <deal.II/grid/tria_accessor.h>
#include <deal.II/grid/tria_iterator.h>
#include <deal.II/grid/grid_tools.h>

#include <deal.II/dofs/dof_handler.h>
#include <deal.II/dofs/dof_renumbering.h>
#include <deal.II/dofs/dof_accessor.h>
#include <deal.II/dofs/dof_tools.h>

#include <deal.II/fe/fe_raviart_thomas.h>
#include <deal.II/fe/fe_dgq.h>
#include <deal.II/fe/fe_system.h>
#include <deal.II/fe/fe_values.h>

#include <deal.II/numerics/vector_tools.h>
#include <deal.II/numerics/matrix_tools.h>
#include <deal.II/numerics/data_out.h>

#include <iostream>
#include <fstream>
#include <sstream>

// In this program, we use a tensor-valued coefficient. Since it may have a
// spatial dependence, we consider it a tensor-valued function. The following
// include file provides the TensorFunction class that offers
// such functionality:
#include <deal.II/base/tensor_function.h>

// The last step is as in all previous programs:
namespace Step21
{
  using namespace dealii;

  // @sect3{The TwoPhaseFlowProblem class}

  // This is the main class of the program. It is close to the one of step-20,
  // but with a few additional functions:
  //
  //

assemble_rhs_S assembles the right hand side of the
  //   saturation equation. As explained in the introduction, this can't be
  //   integrated into assemble_rhs since it depends on the
  //   velocity that is computed in the first part of the time step.
  //
  //
get_maximal_velocity does as its name suggests. This
  //   function is used in the computation of the time step size.
  //
  //
project_back_saturation resets all saturation degrees
  //   of freedom with values less than zero to zero, and all those with
  //   saturations greater than one to one.

  //
  // The rest of the class should be pretty much obvious. The
  // viscosity variable stores the viscosity $\mu$ that enters
  // several of the formulas in the nonlinear equations.
  template <int dim>
  class TwoPhaseFlowProblem
  {
  public:
    TwoPhaseFlowProblem (const unsigned int degree);
    void run ();

  private:
    void make_grid_and_dofs ();
    void assemble_system ();
    //void assemble_rhs_S ();
    void assemble_system_HS();
    void solve();

    void ROSENBROCK_METHOD (const unsigned int n_time_steps,
        		            const double intial_time,
                            const double final_time);

    void NEWTON_METHOD(const unsigned int n_time_steps,
        		       const double intial_time,
                       const double final_time);

    //BlockVector<double> FV (const double time_step, BlockVector<double>&);
    BlockVector<double> FV (BlockVector<double>&);
    BlockVector<double> LV (BlockVector<double>&);

    BlockVector<double> source (BlockVector<double>&);

    void solve1();
    void solve2();

    //double get_maximal_velocity () const;

    void project_back_saturation ();
    void output_results () const;

    const unsigned int   degree;

    Triangulation<dim>   triangulation;
    FESystem<dim>        fe;
    DoFHandler<dim>      dof_handler;

    BlockSparsityPattern      sparsity_pattern;
    BlockSparseMatrix<double> system_matrix, system_matrix_L, L_plus_gamma_jacobian, L_minus_delta_t_jacobian;

    SparseDirectUMFPACK  inverse_L_minus_gamma_jacobian;
    SparseDirectUMFPACK  inverse_L_minus_delta_t_jacobian;

    const unsigned int n_refinement_steps;

    //double time_step;
    unsigned int timestep_number;
    double viscosity;

    //const unsigned int stage;
    //double gamma;
    //double c[2];
    //double alpha[2][2];

    BlockVector<double> solution, vn;
    //BlockVector<double> old_solution;
    BlockVector<double> system_rhs;
    BlockVector<double> r_h_s, L_s;
    BlockVector<double> F_V, L_V, delta_V;
  };

  // @sect3{Equation data}

  // @sect4{Pressure right hand side}

  // At present, the right hand side of the pressure equation is simply the
  // zero function. However, the rest of the program is fully equipped to deal
  // with anything else, if this is desired:
  template <int dim>
  class PressureRightHandSide : public Function<dim>
  {
  public:
    PressureRightHandSide () : Function<dim>(1) {}

    virtual double value (const Point<dim>   &p,
                          const unsigned int  component = 0) const;
  };

  template <int dim>
  double
  PressureRightHandSide<dim>::value (const Point<dim>  &/*p*/,
                                     const unsigned int /*component*/) const
  {
    return 0;
  }

  // @sect4{Pressure boundary values}

  // The next are pressure boundary values. As mentioned in the introduction,
  // we choose a linear pressure field:
  template <int dim>
  class PressureBoundaryValues : public Function<dim>
  {
  public:
    PressureBoundaryValues () : Function<dim>(1) {}

    virtual double value (const Point<dim>   &p,
                          const unsigned int  component = 0) const;
  };

  template <int dim>
  double
  PressureBoundaryValues<dim>::value (const Point<dim>  &p,
                                      const unsigned int /*component*/) const
  {
    return 1-p[0];
  }

  // @sect4{Saturation boundary values}

  // Then we also need boundary values on the inflow portions of the
  // boundary. The question whether something is an inflow part is decided
  // when assembling the right hand side, we only have to provide a functional
  // description of the boundary values. This is as explained in the
  // introduction:
  template <int dim>
  class SaturationBoundaryValues : public Function<dim>
  {
  public:
    SaturationBoundaryValues () : Function<dim>(1) {}

    virtual double value (const Point<dim>   &p,
                          const unsigned int  component = 0) const;
  };

  template <int dim>
  double
  SaturationBoundaryValues<dim>::value (const Point<dim> &p,
                                        const unsigned int /*component*/) const
  {
    if (p[0] == 0)
      return 1;
    else
      return 0;
  }

  // @sect4{Initial data}

  // Finally, we need initial data. In reality, we only need initial data for
  // the saturation, but we are lazy, so we will later, before the first time
  // step, simply interpolate the entire solution for the previous time step
  // from a function that contains all vector components.
  //
  // We therefore simply create a function that returns zero in all
  // components. We do that by simply forward every function to the
  // ZeroFunction class. Why not use that right away in the places of this
  // program where we presently use the InitialValues class?
  // Because this way it is simpler to later go back and choose a different
  // function for initial values.
  template <int dim>
  class InitialValues : public Function<dim>
  {
  public:
    InitialValues () : Function<dim>(dim+2) {}

    virtual double value (const Point<dim>   &p,
                          const unsigned int  component = 0) const;

    virtual void vector_value (const Point<dim> &p,
                               Vector<double>   &value) const;

  };

  template <int dim>
  double
  InitialValues<dim>::value (const Point<dim>  &p,
                             const unsigned int component) const
  {
    return ZeroFunction<dim>(dim+2).value (p, component);
  }

  template <int dim>
  void
  InitialValues<dim>::vector_value (const Point<dim> &p,
                                    Vector<double>   &values) const
  {
    ZeroFunction<dim>(dim+2).vector_value (p, values);
  }

  // @sect3{The inverse permeability tensor}

  // As announced in the introduction, we implement two different permeability
  // tensor fields. Each of them we put into a namespace of its own, so that
  // it will be easy later to replace use of one by the other in the code.

  // @sect4{Single curving crack permeability}

  // The first function for the permeability was the one that models a single
  // curving crack. It was already used at the end of step-20, and its
  // functional form is given in the introduction of the present tutorial
  // program. As in some previous programs, we have to declare a (seemingly
  // unnecessary) default constructor of the KInverse class to avoid warnings
  // from some compilers:
  namespace SingleCurvingCrack
  {
    template <int dim>
    class KInverse : public TensorFunction<2,dim>
    {
    public:
      KInverse ()
        :
        TensorFunction<2,dim> ()
      {}

      virtual void value_list (const std::vector<Point<dim> > &points,
                               std::vector<Tensor<2,dim> >    &values) const;
    };

    template <int dim>
    void
    KInverse<dim>::value_list (const std::vector<Point<dim> > &points,
                               std::vector<Tensor<2,dim> >    &values) const
    {
      Assert (points.size() == values.size(),
              ExcDimensionMismatch (points.size(), values.size()));

      for (unsigned int p=0; p<points.size(); ++p)
        {
          values[p].clear ();

          const double distance_to_flowline
            = std::fabs(points[p][1]-0.5-0.1*std::sin(10*points[p][0]));

          const double permeability = std::max(std::exp(-(distance_to_flowline*
                                                          distance_to_flowline)
                                                        / (0.1 * 0.1)),
                                               0.01);

          for (unsigned int d=0; d<dim; ++d)
            values[p][d][d] = 1./permeability;
        }
    }
  }

  // @sect4{Random medium permeability}

  // This function does as announced in the introduction, i.e. it creates an
  // overlay of exponentials at random places. There is one thing worth
  // considering for this class. The issue centers around the problem that the
  // class creates the centers of the exponentials using a random function. If
  // we therefore created the centers each time we create an object of the
  // present type, we would get a different list of centers each time. That's
  // not what we expect from classes of this type: they should reliably
  // represent the same function.
  //
  // The solution to this problem is to make the list of centers a static
  // member variable of this class, i.e. there exists exactly one such
  // variable for the entire program, rather than for each object of this
  // type. That's exactly what we are going to do.
  //
  // The next problem, however, is that we need a way to initialize this
  // variable. Since this variable is initialized at the beginning of the
  // program, we can't use a regular member function for that since there may
  // not be an object of this type around at the time. The C++ standard
  // therefore says that only non-member and static member functions can be
  // used to initialize a static variable. We use the latter possibility by
  // defining a function get_centers that computes the list of
  // center points when called.
  //
  // Note that this class works just fine in both 2d and 3d, with the only
  // difference being that we use more points in 3d: by experimenting we find
  // that we need more exponentials in 3d than in 2d (we have more ground to
  // cover, after all, if we want to keep the distance between centers roughly
  // equal), so we choose 40 in 2d and 100 in 3d. For any other dimension, the
  // function does presently not know what to do so simply throws an exception
  // indicating exactly this.
  namespace RandomMedium
  {
    template <int dim>
    class KInverse : public TensorFunction<2,dim>
    {
    public:
      KInverse ()
        :
        TensorFunction<2,dim> ()
      {}

      virtual void value_list (const std::vector<Point<dim> > &points,
                               std::vector<Tensor<2,dim> >    &values) const;

    private:
      static std::vector<Point<dim> > centers;

      static std::vector<Point<dim> > get_centers ();
    };

    template <int dim>
    std::vector<Point<dim> >
    KInverse<dim>::centers = KInverse<dim>::get_centers();

    template <int dim>
    std::vector<Point<dim> >
    KInverse<dim>::get_centers ()
    {
      const unsigned int N = (dim == 2 ?
                              40 :
                              (dim == 3 ?
                               100 :
                               throw ExcNotImplemented()));

      std::vector<Point<dim> > centers_list (N);
      for (unsigned int i=0; i<N; ++i)
        for (unsigned int d=0; d<dim; ++d)
          centers_list[i][d] = static_cast<double>(rand())/RAND_MAX;

      return centers_list;
    }

    template <int dim>
    void
    KInverse<dim>::value_list (const std::vector<Point<dim> > &points,
                               std::vector<Tensor<2,dim> >    &values) const
    {
      Assert (points.size() == values.size(),
              ExcDimensionMismatch (points.size(), values.size()));

      for (unsigned int p=0; p<points.size(); ++p)
        {
          values[p].clear ();

          double permeability = 0;
          for (unsigned int i=0; i<centers.size(); ++i)
            permeability += std::exp(-(points[p]-centers[i]).norm_square()
                                     / (0.05 * 0.05));

          const double normalized_permeability
            = std::min (std::max(permeability, 0.01), 4.);

          for (unsigned int d=0; d<dim; ++d)
            values[p][d][d] = 1./normalized_permeability;
        }
    }
  }

  // @sect3{The inverse mobility and saturation functions}

  // There are two more pieces of data that we need to describe, namely the
  // inverse mobility function and the saturation curve. Their form is also
  // given in the introduction:
  double mobility_inverse (const double S,
                           const double viscosity)
  {
    return 1.0 / (1.0/viscosity * S * S + (1-S) * (1-S));
  }

  double fractional_flow (const double S,
                          const double viscosity)
  {
    return S*S / (S * S + viscosity * (1-S) * (1-S));
  }

  // @sect3{Linear solvers and preconditioners}

  // The linear solvers we use are also completely analogous to the ones used
  // in step-20. The following classes are therefore copied verbatim from
  // there. Note that the classes here are not only copied from
  // step-20, but also duplicate classes in deal.II. In a future version of this example, they should be
  // replaced by an efficient method, though. There is a single change: if the size of a linear system is small,
  // i.e. when the mesh is very coarse, then it is sometimes not sufficient to
  // set a maximum of src.size() CG iterations before the solver
  // in the vmult() function converges. (This is, of course, a
  // result of numerical round-off, since we know that on paper, the CG method
  // converges in at most src.size() steps.) As a consequence, we
  // set the maximum number of iterations equal to the maximum of the size of
  // the linear system and 200.
  template <class Matrix>
  class InverseMatrix : public Subscriptor
  {
  public:
    InverseMatrix (const Matrix &m);

    void vmult (Vector<double>       &dst,
                const Vector<double> &src) const;

  private:
    const SmartPointer<const Matrix> matrix;
  };

  template <class Matrix>
  InverseMatrix<Matrix>::InverseMatrix (const Matrix &m)
    :
    matrix (&m)
  {}

  template <class Matrix>
  void InverseMatrix<Matrix>::vmult (Vector<double>       &dst,
                                     const Vector<double> &src) const
  {
    SolverControl solver_control (std::max(src.size(), static_cast<std::size_t> (200)),
                                  1e-8*src.l2_norm());
    SolverCG<>    cg (solver_control);

    dst = 0;

    cg.solve (*matrix, dst, src, PreconditionIdentity());
  }

  class SchurComplement : public Subscriptor
  {
  public:
    SchurComplement (const BlockSparseMatrix<double> &A,
                     const InverseMatrix<SparseMatrix<double> > &Minv);

    void vmult (Vector<double>       &dst,
                const Vector<double> &src) const;

  private:
    const SmartPointer<const BlockSparseMatrix<double> > system_matrix;
    const SmartPointer<const InverseMatrix<SparseMatrix<double> > > m_inverse;

    mutable Vector<double> tmp1, tmp2;
  };

  SchurComplement::
  SchurComplement (const BlockSparseMatrix<double> &A,
                   const InverseMatrix<SparseMatrix<double> > &Minv)
    :
    system_matrix (&A),
    m_inverse (&Minv),
    tmp1 (A.block(0,0).m()),
    tmp2 (A.block(0,0).m())
  {}

  void SchurComplement::vmult (Vector<double>       &dst,
                               const Vector<double> &src) const
  {
    system_matrix->block(0,1).vmult (tmp1, src);
    m_inverse->vmult (tmp2, tmp1);
    system_matrix->block(1,0).vmult (dst, tmp2);
  }

  class ApproximateSchurComplement : public Subscriptor
  {
  public:
    ApproximateSchurComplement (const BlockSparseMatrix<double> &A);

    void vmult (Vector<double>       &dst,
                const Vector<double> &src) const;

  private:
    const SmartPointer<const BlockSparseMatrix<double> > system_matrix;

    mutable Vector<double> tmp1, tmp2;
  };

  ApproximateSchurComplement::
  ApproximateSchurComplement (const BlockSparseMatrix<double> &A)
    :
    system_matrix (&A),
    tmp1 (A.block(0,0).m()),
    tmp2 (A.block(0,0).m())
  {}

  void ApproximateSchurComplement::vmult (Vector<double>       &dst,
                                          const Vector<double> &src) const
  {
    system_matrix->block(0,1).vmult (tmp1, src);
    system_matrix->block(0,0).precondition_Jacobi (tmp2, tmp1);
    system_matrix->block(1,0).vmult (dst, tmp2);
  }

  // @sect3{TwoPhaseFlowProblem class implementation}

  // Here now the implementation of the main class. Much of it is actually
  // copied from step-20, so we won't comment on it in much detail. You should
  // try to get familiar with that program first, then most of what is
  // happening here should be mostly clear.

  // @sect4{TwoPhaseFlowProblem::TwoPhaseFlowProblem}

  // First for the constructor. We use $RT_k \times DQ_k \times DQ_k$
  // spaces. The time step is set to zero initially, but will be computed
  // before it is needed first, as described in a subsection of the
  // introduction.
  template <int dim>
  TwoPhaseFlowProblem<dim>::TwoPhaseFlowProblem (const unsigned int degree)
    :
    degree (degree),
    fe (FE_RaviartThomas<dim>(degree), 1,
        FE_DGQ<dim>(degree), 1,
        FE_DGQ<dim>(degree), 1),
    dof_handler (triangulation),
    n_refinement_steps (6),
    //time_step (0),
    viscosity (0.2)

    /*c{0.8786796564403575,0.2928932188134525},
    alpha{0,0,0.5857864376269050,0},
    stage (2)*/
  {}

  // @sect4{TwoPhaseFlowProblem::make_grid_and_dofs}

  // This next function starts out with well-known functions calls that create
  // and refine a mesh, and then associate degrees of freedom with it. It does
  // all the same things as in step-20, just now for three components instead
  // of two.
  template <int dim>
  void TwoPhaseFlowProblem<dim>::make_grid_and_dofs ()
  {
    GridGenerator::hyper_cube (triangulation, 0, 1);
    triangulation.refine_global (n_refinement_steps);

    dof_handler.distribute_dofs (fe);
    DoFRenumbering::component_wise (dof_handler);

    std::vector<types::global_dof_index> dofs_per_component (dim+2);
    DoFTools::count_dofs_per_component (dof_handler, dofs_per_component);
    const unsigned int n_u = dofs_per_component[0],
                       n_p = dofs_per_component[dim],
                       n_s = dofs_per_component[dim+1];

    std::cout << "Number of active cells: "
              << triangulation.n_active_cells()
              << std::endl
              << "Number of degrees of freedom: "
              << dof_handler.n_dofs()
              << " (" << n_u << '+' << n_p << '+'<< n_s <<')'
              << std::endl
              << std::endl;

    const unsigned int
    n_couplings = dof_handler.max_couplings_between_dofs();

    sparsity_pattern.reinit (3,3);
    sparsity_pattern.block(0,0).reinit (n_u, n_u, n_couplings);
    sparsity_pattern.block(1,0).reinit (n_p, n_u, n_couplings);
    sparsity_pattern.block(2,0).reinit (n_s, n_u, n_couplings);
    sparsity_pattern.block(0,1).reinit (n_u, n_p, n_couplings);
    sparsity_pattern.block(1,1).reinit (n_p, n_p, n_couplings);
    sparsity_pattern.block(2,1).reinit (n_s, n_p, n_couplings);
    sparsity_pattern.block(0,2).reinit (n_u, n_s, n_couplings);
    sparsity_pattern.block(1,2).reinit (n_p, n_s, n_couplings);
    sparsity_pattern.block(2,2).reinit (n_s, n_s, n_couplings);

    sparsity_pattern.collect_sizes();

    DoFTools::make_sparsity_pattern (dof_handler, sparsity_pattern);
    sparsity_pattern.compress();

    system_matrix.reinit (sparsity_pattern);
    system_matrix_L.reinit (sparsity_pattern);
    L_plus_gamma_jacobian.reinit (sparsity_pattern);
    L_minus_delta_t_jacobian.reinit(sparsity_pattern);

    solution.reinit (3);
    solution.block(0).reinit (n_u);
    solution.block(1).reinit (n_p);
    solution.block(2).reinit (n_s);
    solution.collect_sizes ();

    /*solutionr.reinit (3);
    solutionr.block(0).reinit (n_u);
    solutionr.block(1).reinit (n_p);
    solutionr.block(2).reinit (n_s);
    solutionr.collect_sizes ();*/

    /*v0.reinit (3);
    v0.block(0).reinit (n_u);
    v0.block(1).reinit (n_p);
    v0.block(2).reinit (n_s);
    v0.collect_sizes ();*/

    vn.reinit (3);
    vn.block(0).reinit (n_u);
    vn.block(1).reinit (n_p);
    vn.block(2).reinit (n_s);
    vn.collect_sizes ();

    /*vnew.reinit (3);
    vnew.block(0).reinit (n_u);
    vnew.block(1).reinit (n_p);
    vnew.block(2).reinit (n_s);
    vnew.collect_sizes ();*/

    /*old_solution.reinit (3);
    old_solution.block(0).reinit (n_u);
    old_solution.block(1).reinit (n_p);
    old_solution.block(2).reinit (n_s);
    old_solution.collect_sizes ();*/

    system_rhs.reinit (3);
    system_rhs.block(0).reinit (n_u);
    system_rhs.block(1).reinit (n_p);
    system_rhs.block(2).reinit (n_s);
    system_rhs.collect_sizes ();

  }

  // @sect4{TwoPhaseFlowProblem::assemble_system}

  // This is the function that assembles the linear system, or at least
  // everything except the (1,3) block that depends on the still-unknown
  // velocity computed during this time step (we deal with this in
  // assemble_rhs_S). Much of it is again as in step-20, but we
  // have to deal with some nonlinearity this time.  However, the top of the
  // function is pretty much as usual (note that we set matrix and right hand
  // side to zero at the beginning — something we didn't have to do for
  // stationary problems since there we use each matrix object only once and
  // it is empty at the beginning anyway).
  //
  // Note that in its present form, the function uses the permeability
  // implemented in the RandomMedium::KInverse class. Switching to the single
  // curved crack permeability function is as simple as just changing the
  // namespace name.

  /*template <int dim>
  void TwoPhaseFlowProblem<dim>::assemble_system ()
  {
    system_matrix=0;
    system_rhs=0;

    QGauss<dim>   quadrature_formula(degree+2);
    QGauss<dim-1> face_quadrature_formula(degree+2);

    FEValues<dim> fe_values (fe, quadrature_formula,
                             update_values    | update_gradients |
                             update_quadrature_points  | update_JxW_values);
    FEFaceValues<dim> fe_face_values (fe, face_quadrature_formula,
                                      update_values    | update_normal_vectors |
                                      update_quadrature_points  | update_JxW_values);

    const unsigned int   dofs_per_cell   = fe.dofs_per_cell;

    const unsigned int   n_q_points      = quadrature_formula.size();
    const unsigned int   n_face_q_points = face_quadrature_formula.size();

    FullMatrix<double>   local_matrix (dofs_per_cell, dofs_per_cell);

    Vector<double>       local_rhs (dofs_per_cell);

    std::vector<types::global_dof_index> local_dof_indices (dofs_per_cell);

    const PressureRightHandSide<dim>  pressure_right_hand_side;
    const PressureBoundaryValues<dim> pressure_boundary_values;
    const RandomMedium::KInverse<dim> k_inverse;

    std::vector<double>               pressure_rhs_values (n_q_points);
    std::vector<double>               boundary_values (n_face_q_points);
    std::vector<Tensor<2,dim> >       k_inverse_values (n_q_points);

    std::vector<Vector<double> >      solution_values(n_q_points, Vector<double>(dim+2));
    std::vector<std::vector<Tensor<1,dim> > >  solution_grads(n_q_points,
        std::vector<Tensor<1,dim> > (dim+2));

    const FEValuesExtractors::Vector velocities (0);
    const FEValuesExtractors::Scalar pressure (dim);
    const FEValuesExtractors::Scalar saturation (dim+1);

    typename DoFHandler<dim>::active_cell_iterator
    cell = dof_handler.begin_active(),
    endc = dof_handler.end();
    for (; cell!=endc; ++cell)
      {
        fe_values.reinit (cell);
        local_matrix = 0;

        local_rhs = 0;

        // Here's the first significant difference: We have to get the values
        // of the saturation function of the previous time step at the
        // quadrature points. To this end, we can use the
        // FEValues::get_function_values (previously already used in step-9,
        // step-14 and step-15), a function that takes a solution vector and
        // returns a list of function values at the quadrature points of the
        // present cell. In fact, it returns the complete vector-valued
        // solution at each quadrature point, i.e. not only the saturation but
        // also the velocities and pressure:
        //fe_values.get_function_values (old_solution, old_solution_values);

        fe_values.get_function_values (solution, solution_values);

        // Then we also have to get the values of the pressure right hand side
        // and of the inverse permeability tensor at the quadrature points:
        pressure_right_hand_side.value_list (fe_values.get_quadrature_points(),
                                             pressure_rhs_values);
        k_inverse.value_list (fe_values.get_quadrature_points(),
                              k_inverse_values);

        // With all this, we can now loop over all the quadrature points and
        // shape functions on this cell and assemble those parts of the matrix
        // and right hand side that we deal with in this function. The
        // individual terms in the contributions should be self-explanatory
        // given the explicit form of the bilinear form stated in the
        // introduction:
        for (unsigned int q=0; q<n_q_points; ++q)
          for (unsigned int i=0; i<dofs_per_cell; ++i)
            {
              const double sat_q = solution_values[q](dim+1);

              const Tensor<1,dim> phi_i_u      = fe_values[velocities].value (i, q);
              const double        div_phi_i_u  = fe_values[velocities].divergence (i, q);
              const double        phi_i_p      = fe_values[pressure].value (i, q);
              //const double        phi_i_s      = fe_values[saturation].value (i, q);

              for (unsigned int j=0; j<dofs_per_cell; ++j)
                {
                  const Tensor<1,dim> phi_j_u     = fe_values[velocities].value (j, q);
                  const double        div_phi_j_u = fe_values[velocities].divergence (j, q);
                  const double        phi_j_p     = fe_values[pressure].value (j, q);
                  //const double        phi_j_s     = fe_values[saturation].value (j, q);

                  local_matrix(i,j) += (phi_i_u * k_inverse_values[q] *
                                        mobility_inverse(sat_q,viscosity) * phi_j_u
                                        - div_phi_i_u * phi_j_p
                                        - phi_i_p * div_phi_j_u)
                                        //+ phi_i_s * phi_j_s)
                                       * fe_values.JxW(q);

                }

              local_rhs(i) += (-phi_i_p * pressure_rhs_values[q])*
                              fe_values.JxW(q);
            }

        // Next, we also have to deal with the pressure boundary values. This,
        // again is as in step-20:
        for (unsigned int face_no=0;face_no<GeometryInfo<dim>::faces_per_cell; ++face_no)

          if (cell->at_boundary(face_no))
            {
              fe_face_values.reinit (cell, face_no);

              pressure_boundary_values.value_list (fe_face_values.get_quadrature_points(),
                           boundary_values);

              for (unsigned int q=0; q<n_face_q_points; ++q)
                for (unsigned int i=0; i<dofs_per_cell; ++i)
                  {
                    const Tensor<1,dim>
                    phi_i_u = fe_face_values[velocities].value (i, q);

                    local_rhs(i) += -(phi_i_u *
                                      fe_face_values.normal_vector(q) *
                                      boundary_values[q] *
                                      fe_face_values.JxW(q));
                  }
            }

        // The final step in the loop over all cells is to transfer local
        // contributions into the global matrix and right hand side vector:
        cell->get_dof_indices (local_dof_indices);
        for (unsigned int i=0; i<dofs_per_cell; ++i)
          for (unsigned int j=0; j<dofs_per_cell; ++j)
            system_matrix.add (local_dof_indices[i],
                               local_dof_indices[j],
                               local_matrix(i,j));

        for (unsigned int i=0; i<dofs_per_cell; ++i)
          system_rhs(local_dof_indices[i]) += local_rhs(i);
      }

  }
*/

  template <int dim>
  void TwoPhaseFlowProblem<dim>::assemble_system ()
  {
    system_matrix=0;
    system_rhs=0;
    system_matrix_L=0;

    QGauss<dim>   quadrature_formula(degree+2);
    QGauss<dim-1> face_quadrature_formula(degree+2);

    FEValues<dim> fe_values (fe, quadrature_formula,
                             update_values    | update_gradients |
                             update_quadrature_points  | update_JxW_values);
    FEFaceValues<dim> fe_face_values (fe, face_quadrature_formula,
                                      update_values    | update_normal_vectors |
                                      update_quadrature_points  | update_JxW_values);

    const unsigned int   dofs_per_cell   = fe.dofs_per_cell;

    const unsigned int   n_q_points      = quadrature_formula.size();
    const unsigned int   n_face_q_points = face_quadrature_formula.size();

    FullMatrix<double>   local_matrix (dofs_per_cell, dofs_per_cell);
    FullMatrix<double>   local_matrix_L (dofs_per_cell, dofs_per_cell);
    Vector<double>       local_rhs (dofs_per_cell);

    std::vector<types::global_dof_index> local_dof_indices (dofs_per_cell);

    const PressureRightHandSide<dim>  pressure_right_hand_side;
    const PressureBoundaryValues<dim> pressure_boundary_values;
    //const RandomMedium::KInverse<dim> k_inverse;

    const SingleCurvingCrack::KInverse<dim> k_inverse;

    std::vector<double>               pressure_rhs_values (n_q_points);
    std::vector<double>               boundary_values (n_face_q_points);
    std::vector<Tensor<2,dim> >       k_inverse_values (n_q_points);

    std::vector<Vector<double> >      solution_values(n_q_points, Vector<double>(dim+2));
    std::vector<std::vector<Tensor<1,dim> > >  solution_grads(n_q_points,
        std::vector<Tensor<1,dim> > (dim+2));

    const FEValuesExtractors::Vector velocities (0);
    const FEValuesExtractors::Scalar pressure (dim);
    const FEValuesExtractors::Scalar saturation (dim+1);

    typename DoFHandler<dim>::active_cell_iterator
    cell = dof_handler.begin_active(),
    endc = dof_handler.end();
    for (; cell!=endc; ++cell)
      {
        fe_values.reinit (cell);
        local_matrix = 0;
        local_matrix_L =0;
        local_rhs = 0;

        // Here's the first significant difference: We have to get the values
        // of the saturation function of the previous time step at the
        // quadrature points. To this end, we can use the
        // FEValues::get_function_values (previously already used in step-9,
        // step-14 and step-15), a function that takes a solution vector and
        // returns a list of function values at the quadrature points of the
        // present cell. In fact, it returns the complete vector-valued
        // solution at each quadrature point, i.e. not only the saturation but
        // also the velocities and pressure:
        //fe_values.get_function_values (old_solution, old_solution_values);

        fe_values.get_function_values (solution, solution_values);

        // Then we also have to get the values of the pressure right hand side
        // and of the inverse permeability tensor at the quadrature points:
        pressure_right_hand_side.value_list (fe_values.get_quadrature_points(),
                                             pressure_rhs_values);
        k_inverse.value_list (fe_values.get_quadrature_points(),
                              k_inverse_values);

        // With all this, we can now loop over all the quadrature points and
        // shape functions on this cell and assemble those parts of the matrix
        // and right hand side that we deal with in this function. The
        // individual terms in the contributions should be self-explanatory
        // given the explicit form of the bilinear form stated in the
        // introduction:
        for (unsigned int q=0; q<n_q_points; ++q)
          for (unsigned int i=0; i<dofs_per_cell; ++i)
            {
              const double sat_q = solution_values[q](dim+1);

              const Tensor<1,dim> phi_i_u      = fe_values[velocities].value (i, q);
              const double        div_phi_i_u  = fe_values[velocities].divergence (i, q);
              const double        phi_i_p      = fe_values[pressure].value (i, q);
              const double        phi_i_s      = fe_values[saturation].value (i, q);

              for (unsigned int j=0; j<dofs_per_cell; ++j)
                {
                  const Tensor<1,dim> phi_j_u     = fe_values[velocities].value (j, q);
                  const double        div_phi_j_u = fe_values[velocities].divergence (j, q);
                  const double        phi_j_p     = fe_values[pressure].value (j, q);
                  const double        phi_j_s     = fe_values[saturation].value (j, q);

                  local_matrix(i,j) += (phi_i_u * k_inverse_values[q] *
                                        mobility_inverse(sat_q,viscosity) * phi_j_u
                                        - div_phi_i_u * phi_j_p
                                        - phi_i_p * div_phi_j_u)
                                        //+ phi_i_s * phi_j_s)
                                       * fe_values.JxW(q);
                  local_matrix_L(i,j) += (phi_i_s * phi_j_s)
                                         * fe_values.JxW(q);
                }

                  local_rhs(i) += (-phi_i_p * pressure_rhs_values[q])*
                              fe_values.JxW(q);
            }

        // Next, we also have to deal with the pressure boundary values. This,
        // again is as in step-20:
        for (unsigned int face_no=0;face_no<GeometryInfo<dim>::faces_per_cell; ++face_no)

          if (cell->at_boundary(face_no))
            {
              fe_face_values.reinit (cell, face_no);

              pressure_boundary_values.value_list (fe_face_values.get_quadrature_points(),
                           boundary_values);

              for (unsigned int q=0; q<n_face_q_points; ++q)
                for (unsigned int i=0; i<dofs_per_cell; ++i)
                  {
                    const Tensor<1,dim>
                    phi_i_u = fe_face_values[velocities].value (i, q);

                    local_rhs(i) += -(phi_i_u *
                                      fe_face_values.normal_vector(q) *
                                      boundary_values[q] *
                                      fe_face_values.JxW(q));
                  }
            }

        // The final step in the loop over all cells is to transfer local
        // contributions into the global matrix and right hand side vector:
        cell->get_dof_indices (local_dof_indices);
        for (unsigned int i=0; i<dofs_per_cell; ++i)
          for (unsigned int j=0; j<dofs_per_cell; ++j)
            system_matrix.add (local_dof_indices[i],
                               local_dof_indices[j],
                               local_matrix(i,j));

        for (unsigned int i=0; i<dofs_per_cell; ++i)
        	for (unsigned int j=0; j<dofs_per_cell; ++j)
        		system_matrix_L.add (local_dof_indices[i],
        				             local_dof_indices[j],
                                     local_matrix_L(i,j));

        for (unsigned int i=0; i<dofs_per_cell; ++i)
          system_rhs(local_dof_indices[i]) += local_rhs(i);
      }
   //L_plus_gamma_jacobian.copy_from(system_matrix_L);
  }

  // So much for assembly of matrix and right hand side. Note that we do not
  // have to interpolate and apply boundary values since they have all been
  // taken care of in the weak form already.

  // @sect4{TwoPhaseFlowProblem::assemble_rhs_S}

  // As explained in the introduction, we can only evaluate the right hand
  // side of the saturation equation once the velocity has been computed. We
  // therefore have this separate function to this end.

template <int dim>
  void TwoPhaseFlowProblem<dim>::assemble_system_HS ()
   {
     QGauss<dim>   quadrature_formula(degree+2);
     QGauss<dim-1> face_quadrature_formula(degree+2);

     FEValues<dim> fe_values (fe, quadrature_formula,
                              update_values    | update_gradients |
                              update_quadrature_points  | update_JxW_values);
     FEFaceValues<dim> fe_face_values (fe, face_quadrature_formula,
                                       update_values    | update_normal_vectors |
                                       update_quadrature_points  | update_JxW_values);
     FEFaceValues<dim> fe_face_values_neighbor (fe, face_quadrature_formula,
                                                    update_values);

     const unsigned int   dofs_per_cell   = fe.dofs_per_cell;

     const unsigned int   n_q_points      = quadrature_formula.size();
     const unsigned int   n_face_q_points = face_quadrature_formula.size();

     FullMatrix<double>   local_matrix (dofs_per_cell, dofs_per_cell);
     FullMatrix<double>   local_matrix_div (dofs_per_cell, dofs_per_cell);

     Vector<double>  local_rhs (dofs_per_cell);

     std::vector<types::global_dof_index> local_dof_indices (dofs_per_cell);

     const PressureRightHandSide<dim>  pressure_right_hand_side;
     const PressureBoundaryValues<dim> pressure_boundary_values;
     //const RandomMedium::KInverse<dim> k_inverse;
     const SingleCurvingCrack::KInverse<dim> k_inverse;

     // there is no more old_solution, old_solutions_values, present_solution... I only have in input vin_U_P_S blockvector

     std::vector<Vector<double> > solution_values(n_q_points, Vector<double>(dim+2));
     std::vector<Vector<double> > solution_values_face(n_face_q_points, Vector<double>(dim+2));
     std::vector<Vector<double> > solution_values_face_neighbor(n_face_q_points, Vector<double>(dim+2));

     //std::vector<Vector<double> > present_solution_values(n_q_points, Vector<double>(dim+2));
     //std::vector<Vector<double> > present_solution_values_face(n_face_q_points, Vector<double>(dim+2));

     std::vector<double> neighbor_saturation (n_face_q_points);

     SaturationBoundaryValues<dim> saturation_boundary_values;

     std::vector<double>               pressure_rhs_values (n_q_points);
     std::vector<double>               boundary_values (n_face_q_points);
     std::vector<Tensor<2,dim> >       k_inverse_values (n_q_points);

     std::vector<std::vector<Tensor<1,dim> > >  solution_grads(n_q_points, std::vector<Tensor<1,dim> > (dim+2));

     std::vector<Tensor<1,dim> > local_velocity_values (n_q_points);

     const FEValuesExtractors::Vector velocities (0);
     const FEValuesExtractors::Scalar pressure (dim);
     const FEValuesExtractors::Scalar saturation (dim+1);

     typename DoFHandler<dim>::active_cell_iterator
     cell = dof_handler.begin_active(),
     endc = dof_handler.end();
     for (; cell!=endc; ++cell)
       {

         local_matrix = 0;
         //local_matrix_div = 0;
         fe_values.reinit (cell);

         //fe_values[saturation].get_function_values (solution, local_saturation_values);
         fe_values.get_function_values (solution, solution_values);

         for (unsigned int q=0; q<n_q_points; ++q)
           {
           for (unsigned int i=0; i<dofs_per_cell; ++i)
             {
               const double sat_q_points = solution_values[q](dim+1);

               /*Tensor<1,dim> velocity_q_points;
               for (unsigned int d=0; d<dim; ++d)
                  velocity_q_points[d] = solution_values[q](d)*/

               //const double        phi_i_s      = fe_values[saturation].value (i, q);
              //const Tensor<1,dim> grad_phi_i_s = fe_values[saturation].gradient (i, q);

               const Tensor<1,dim> phi_i_u      = fe_values[velocities].value (i, q);
               //const double        div_phi_i_u  = fe_values[velocities].divergence (i, q);
               //const double        phi_i_p      = fe_values[pressure].value (i, q);
               //const double        phi_i_s      = fe_values[saturation].value (i, q);

               for (unsigned int j=0; j<dofs_per_cell; ++j)
                 {
                   //const Tensor<1,dim> phi_j_u     = fe_values[velocities].value (j, q);
                   //const double        div_phi_j_u = fe_values[velocities].divergence (j, q);
                   //const double        phi_j_p     = fe_values[pressure].value (j, q);
                   //const double        phi_j_s     = fe_values[saturation].value (j, q);
                   const Tensor<1,dim> grad_phi_j_s = fe_values[saturation].gradient (j, q);

                   local_matrix(i,j) += -fractional_flow(sat_q_points,viscosity) *
                                         phi_i_u *
                                         grad_phi_j_s
                                         * fe_values.JxW(q);

                   /*local_matrix_div(i,j) += -fractional_flow_derivative (sat_q_points,viscosity) *
                                            phi_i_u *
                                            grad_phi_j_s
                                            * fe_values.JxW(q);*/     //+ phi_i_s * phi_j_s) // I do not need    + phi_i_s * phi_j_s

                 }

               // I don't need time_step so I remove it
               //local_HS(i) += (fractional_flow(sat_q_points,viscosity) *
                              //velocity_q_points *
                              //grad_phi_i_s)*fe_values.JxW(q);

               //local_rhs(i) += (-phi_i_p * pressure_rhs_values[q])* fe_values.JxW(q);
             }
           }

         for (unsigned int face_no=0; face_no<GeometryInfo<dim>::faces_per_cell; ++face_no)
             {
                   fe_face_values.reinit (cell, face_no);

                    //fe_face_values.get_function_values (old_solution, old_solution_values_face);

                    //fe_face_values[saturation].get_function_values (solution, local_saturation_values_face);

                   fe_face_values.get_function_values (solution, solution_values_face);

                   if (cell->at_boundary(face_no))
                   {
                 	//pressure_boundary_values.value_list (fe_face_values.get_quadrature_points(), boundary_values);

                      saturation_boundary_values.value_list (fe_face_values.get_quadrature_points(),
                                   neighbor_saturation);

                      // dealing with the pressure boundary value
                      /*for (unsigned int q=0; q<n_face_q_points; ++q)
                      {
 							 for (unsigned int i=0; i<dofs_per_cell; ++i)
 							   {
 								 const Tensor<1,dim>
 								 phi_i_u = fe_face_values[velocities].value (i, q);

 								 local_rhs(i) += -(phi_i_u *
 												   fe_face_values.normal_vector(q) *
 												   boundary_values[q] *
 												   fe_face_values.JxW(q));
 							   }

                      }*/

                   }
                    else
                      {
                        const typename DoFHandler<dim>::active_cell_iterator
                        neighbor = cell->neighbor(face_no);
                        const unsigned int
                        neighbor_face = cell->neighbor_of_neighbor(face_no);

                        fe_face_values_neighbor.reinit (neighbor, neighbor_face);

                        fe_face_values_neighbor.get_function_values (solution, solution_values_face_neighbor);

                        for (unsigned int q=0; q<n_face_q_points; ++q)
                     	  neighbor_saturation[q] = solution_values_face_neighbor[q](dim+1);
                          //neighbor_saturation[q] = saturation_values_face_neighbor[q];
                          //neighbor_saturation[q] = solution[q](dim+1);
                      }

                    for (unsigned int q=0; q<n_face_q_points; ++q)
                      {
                        //Tensor<1,dim> velocity_u_face;
                        //for (unsigned int d=0; d<dim; ++d)
                        //  velocity_u_face[d] = solution_values_face[q](d);

                        fe_values[velocities].get_function_values (solution, local_velocity_values);

                        const double normal_flux = local_velocity_values[q] *
                                                   fe_face_values.normal_vector(q);

                        //const bool is_outflow_q_point = (normal_flux >= 0);

                        if (normal_flux > 0)
                        {
                        for (unsigned int i=0; i<dofs_per_cell; ++i)
                     	   for (unsigned int j=0; j<dofs_per_cell; ++j)

                     		   local_matrix(i,j) +=   (fe_face_values.normal_vector(q) *
                     				                  fe_face_values[saturation].value (i,q) *
                     		                          fe_face_values[velocities].value(j,q))*
                                                      fractional_flow(solution_values_face[q](dim+1),viscosity)*
                                                      fe_face_values.JxW(q);

                        /*for (unsigned int i=0; i<dofs_per_cell; ++i)
                        	for (unsigned int j=0; j<dofs_per_cell; ++j)

                               local_matrix_div(i,j) += (fe_face_values.normal_vector(q) *
      				                                     fe_face_values[saturation].value (i,q) *
      		                                             fe_face_values[velocities].value(j,q))*
                                                         fractional_flow_derivative(solution_values_face[q](dim+1),viscosity)*
                                                         fe_face_values.JxW(q);*/

                        }
                        else
                        {
                     	   for (unsigned int i=0; i<dofs_per_cell; ++i)
                     		   for (unsigned int j=0; j<dofs_per_cell; ++j)

                     			   local_matrix(i,j) += (fe_face_values.normal_vector(q) *
                     					                  fe_face_values[saturation].value (i,q) *
                     	                       		      fe_face_values[velocities].value(j,q))*
                     	                                  fractional_flow(neighbor_saturation[q],viscosity)*
                     	                                  fe_face_values.JxW(q);

                     	  /*for (unsigned int i=0; i<dofs_per_cell; ++i)
                     		  for (unsigned int j=0; j<dofs_per_cell; ++j)

                     	           local_matrix_div(i,j) += (fe_face_values.normal_vector(q) *
                     	                       		     fe_face_values[saturation].value (i,q) *
                     	                       	         fe_face_values[velocities].value(j,q))*
                     	                       	         fractional_flow_derivative(neighbor_saturation[q],viscosity)*
                     	                       	         fe_face_values.JxW(q);*/

                        }

                  }

                  }

         cell->get_dof_indices (local_dof_indices);
         for (unsigned int i=0; i<dofs_per_cell; ++i)
           for (unsigned int j=0; j<dofs_per_cell; ++j)
             system_matrix.add (local_dof_indices[i],
                                local_dof_indices[j],
                                local_matrix(i,j));

         /*for (unsigned int i=0; i<dofs_per_cell; ++i)
        	 for (unsigned int j=0; j<dofs_per_cell; ++j)
        		 system_matrix_derivative.add (local_dof_indices[i],
                                               local_dof_indices[j],
                                               local_matrix_div(i,j));*/

         //for (unsigned int i=0; i<dofs_per_cell; ++i)
         //  system_rhs(local_dof_indices[i]) += local_rhs(i);

       }
     //system_matrix.print_formatted(std::cout,2,true,2,"0",1);
     //system_matrix_derivative.print_formatted(std::cout,2,true,2,"0",1);
     //system_matrix.block(0,2).copy_from(system_matrix_derivative.block(0,0));

     //std::cout << system_matrix_derivative.block(0,0).m() << std::endl;
     //std::cout << system_matrix_derivative.block(0,0).n() << std::endl;

     //system_rhs.block(2).print(std::cout);
     //system_rhs.print(std::cout);
   }

/*  template <int dim>
  void TwoPhaseFlowProblem<dim>::assemble_rhs_S ()
  {
    QGauss<dim>   quadrature_formula(degree+2);
    QGauss<dim-1> face_quadrature_formula(degree+2);
    FEValues<dim> fe_values (fe, quadrature_formula,
                             update_values    | update_gradients |
                             update_quadrature_points  | update_JxW_values);
    FEFaceValues<dim> fe_face_values (fe, face_quadrature_formula,
                                      update_values    | update_normal_vectors |
                                      update_quadrature_points  | update_JxW_values);
    FEFaceValues<dim> fe_face_values_neighbor (fe, face_quadrature_formula,
                                               update_values);

    const unsigned int   dofs_per_cell   = fe.dofs_per_cell;
    const unsigned int   n_q_points      = quadrature_formula.size();
    const unsigned int   n_face_q_points = face_quadrature_formula.size();

    Vector<double>       local_rhs (dofs_per_cell);

    std::vector<Vector<double> > solution_values(n_q_points, Vector<double>(dim+2));
    std::vector<Vector<double> > solution_values_face(n_face_q_points, Vector<double>(dim+2));
    std::vector<Vector<double> > solution_values_face_neighbor(n_face_q_points, Vector<double>(dim+2));

    std::vector<Vector<double> > old_solution_values(n_q_points, Vector<double>(dim+2));
    std::vector<Vector<double> > old_solution_values_face(n_face_q_points, Vector<double>(dim+2));
    std::vector<Vector<double> > old_solution_values_face_neighbor(n_face_q_points, Vector<double>(dim+2));

    std::vector<Vector<double> > present_solution_values(n_q_points, Vector<double>(dim+2));
    std::vector<Vector<double> > present_solution_values_face(n_face_q_points, Vector<double>(dim+2));

    std::vector<double> neighbor_saturation (n_face_q_points);
    std::vector<types::global_dof_index> local_dof_indices (dofs_per_cell);

    SaturationBoundaryValues<dim> saturation_boundary_values;

    const FEValuesExtractors::Scalar saturation (dim+1);

    typename DoFHandler<dim>::active_cell_iterator
    cell = dof_handler.begin_active(),
    endc = dof_handler.end();
    for (; cell!=endc; ++cell)
      {
        local_rhs = 0;
        fe_values.reinit (cell);

        //fe_values.get_function_values (old_solution, old_solution_values);
        fe_values.get_function_values (solution, solution_values);

        // First for the cell terms. These are, following the formulas in the
        // introduction, $(S^n,\sigma)-(F(S^n) \mathbf{v}^{n+1},\nabla
        // \sigma)$, where $\sigma$ is the saturation component of the test
        // function:
        for (unsigned int q=0; q<n_q_points; ++q)
          for (unsigned int i=0; i<dofs_per_cell; ++i)
            {
              const double sat_q = solution_values[q](dim+1);
              Tensor<1,dim> present_u;
              for (unsigned int d=0; d<dim; ++d)
                present_u[d] = solution_values[q](d);

              //const double        phi_i_s      = fe_values[saturation].value (i, q);
              const Tensor<1,dim> grad_phi_i_s = fe_values[saturation].gradient (i, q);

              local_rhs(i) += (fractional_flow(sat_q,viscosity) *
                               present_u *
                               grad_phi_i_s)

                               //+old_s * phi_i_s)
                              *
                              fe_values.JxW(q);
            }

        // Secondly, we have to deal with the flux parts on the face
        // boundaries. This was a bit more involved because we first have to
        // determine which are the influx and outflux parts of the cell
        // boundary. If we have an influx boundary, we need to evaluate the
        // saturation on the other side of the face (or the boundary values,
        // if we are at the boundary of the domain).
        //
        // All this is a bit tricky, but has been explained in some detail
        // already in step-9. Take a look there how this is supposed to work!
        for (unsigned int face_no=0; face_no<GeometryInfo<dim>::faces_per_cell;
             ++face_no)
          {
            fe_face_values.reinit (cell, face_no);

            fe_face_values.get_function_values (old_solution, old_solution_values_face);
            fe_face_values.get_function_values (solution, present_solution_values_face);

            fe_face_values.get_function_values (solution, solution_values_face);

            if (cell->at_boundary(face_no))
              saturation_boundary_values
              .value_list (fe_face_values.get_quadrature_points(),
                           neighbor_saturation);
            else
              {
                const typename DoFHandler<dim>::active_cell_iterator
                neighbor = cell->neighbor(face_no);
                const unsigned int
                neighbor_face = cell->neighbor_of_neighbor(face_no);

                fe_face_values_neighbor.reinit (neighbor, neighbor_face);

                fe_face_values_neighbor
                .get_function_values (old_solution,
                                      old_solution_values_face_neighbor);

                fe_face_values_neighbor
                                .get_function_values (solution,
                                                      solution_values_face_neighbor);

                for (unsigned int q=0; q<n_face_q_points; ++q)
                  neighbor_saturation[q] = solution_values_face_neighbor[q](dim+1);
              }

            for (unsigned int q=0; q<n_face_q_points; ++q)
              {
                Tensor<1,dim> present_u_face;
                for (unsigned int d=0; d<dim; ++d)
                  present_u_face[d] = solution_values_face[q](d);

                const double normal_flux = present_u_face *
                                           fe_face_values.normal_vector(q);

                const bool is_outflow_q_point = (normal_flux >= 0);

                for (unsigned int i=0; i<dofs_per_cell; ++i)
                  local_rhs(i) -= normal_flux *
                                  fractional_flow((is_outflow_q_point == true
                                                   ?
                                                   solution_values_face[q](dim+1)
                                                   :
                                                   neighbor_saturation[q]),
                                                  viscosity) *
                                  fe_face_values[saturation].value (i,q) *
                                  fe_face_values.JxW(q);
              }
          }

        cell->get_dof_indices (local_dof_indices);
        for (unsigned int i=0; i<dofs_per_cell; ++i)
          system_rhs(local_dof_indices[i]) += local_rhs(i);
      }
    //system_rhs.print(std::cout);
  }*/

  // @sect4{TwoPhaseFlowProblem::solve}

  // After all these preparations, we finally solve the linear system for
  // velocity and pressure in the same way as in step-20. After that, we have
  // to deal with the saturation equation (see below):
  template <int dim>
  void TwoPhaseFlowProblem<dim>::solve ()
  {
    const InverseMatrix<SparseMatrix<double> >
    m_inverse (system_matrix.block(0,0));
    Vector<double> tmp (solution.block(0).size());
    Vector<double> schur_rhs (solution.block(1).size());
    Vector<double> tmp2 (solution.block(2).size());

    // First the pressure, using the pressure Schur complement of the first
    // two equations:
    {
      m_inverse.vmult (tmp, system_rhs.block(0));
      system_matrix.block(1,0).vmult (schur_rhs, tmp);
      schur_rhs -= system_rhs.block(1);

      SchurComplement
      schur_complement (system_matrix, m_inverse);

      ApproximateSchurComplement
      approximate_schur_complement (system_matrix);

      InverseMatrix<ApproximateSchurComplement>
      preconditioner (approximate_schur_complement);

      //SolverControl solver_control (solution.block(1).size(), 1e-24*schur_rhs.l2_norm());
      SolverControl solver_control (1000, 1e-24*schur_rhs.l2_norm());
      SolverCG<>    cg (solver_control);

      cg.solve (schur_complement, solution.block(1), schur_rhs,
                preconditioner);

      //solution.block(1).print(std::cout);

      /*std::cout << "   "
                << solver_control.last_step()
                << " CG Schur complement iterations for pressure."
                << std::endl;*/
    }

    // Now the velocity:
    {
      system_matrix.block(0,1).vmult (tmp, solution.block(1));
      tmp *= -1;
      tmp += system_rhs.block(0);

      m_inverse.vmult (solution.block(0), tmp);
      //solution.block(0).print(std::cout);
      //solution.block(2).print(std::cout);
      //solution.print(std::cout);
    }

    // Finally, we have to take care of the saturation equation. The first
    // business we have here is to determine the time step using the formula
    // in the introduction. Knowing the shape of our domain and that we
    // created the mesh by regular subdivision of cells, we can compute the
    // diameter of each of our cells quite easily (in fact we use the linear
    // extensions in coordinate directions of the cells, not the
    // diameter). Note that we will learn a more general way to do this in
    // step-24, where we use the GridTools::minimal_cell_diameter function.
    //
    // The maximal velocity we compute using a helper function to compute the
    // maximal velocity defined below, and with all this we can evaluate our
    // new time step length:

    //time_step = std::pow(0.5, double(n_refinement_steps)) / get_maximal_velocity();

    // The next step is to assemble the right hand side, and then to pass
    // everything on for solution. At the end, we project back saturations
    // onto the physically reasonable range:

    //assemble_rhs_S ();

    /*{

      SolverControl solver_control (system_matrix.block(2,2).m(),
                                    1e-8*system_rhs.block(2).l2_norm());
      SolverCG<>   cg (solver_control);
      cg.solve (system_matrix.block(2,2), solution.block(2), system_rhs.block(2),
                PreconditionIdentity());

      project_back_saturation ();

      std::cout << "   "
                << solver_control.last_step()
                << " CG iterations for saturation."
                << std::endl;
    }
    old_solution = solution;*/
    project_back_saturation ();

  }

  // @sect4{TwoPhaseFlowProblem::output_results}

  // There is nothing surprising here. Since the program will do a lot of time
  // steps, we create an output file only every fifth time step.

  template <int dim>
  void TwoPhaseFlowProblem<dim>::output_results ()  const
  {
    if (timestep_number % 5 != 0)
      return;

    std::vector<std::string> solution_names;
    switch (dim)
      {
      case 2:
        solution_names.push_back ("u");
        solution_names.push_back ("v");
        solution_names.push_back ("p");
        solution_names.push_back ("S");
        break;

      case 3:
        solution_names.push_back ("u");
        solution_names.push_back ("v");
        solution_names.push_back ("w");
        solution_names.push_back ("p");
        solution_names.push_back ("S");
        break;

      default:
        Assert (false, ExcNotImplemented());
      }

    DataOut<dim> data_out;

    data_out.attach_dof_handler (dof_handler);
    data_out.add_data_vector (solution, solution_names);

    data_out.build_patches (degree+1);

    std::ostringstream filename;
    filename << "solution-" << timestep_number << ".vtk";

    std::ofstream output (filename.str().c_str());
    data_out.write_vtk (output);
  }

  // @sect4{TwoPhaseFlowProblem::project_back_saturation}

  // In this function, we simply run over all saturation degrees of freedom
  // and make sure that if they should have left the physically reasonable
  // range, that they be reset to the interval $[0,1]$. To do this, we only
  // have to loop over all saturation components of the solution vector; these
  // are stored in the block 2 (block 0 are the velocities, block 1 are the
  // pressures).
  //
  // It may be instructive to note that this function almost never triggers
  // when the time step is chosen as mentioned in the introduction. However,
  // if we choose the timestep only slightly larger, we get plenty of values
  // outside the proper range. Strictly speaking, the function is therefore
  // unnecessary if we choose the time step small enough. In a sense, the
  // function is therefore only a safety device to avoid situations where our
  // entire solution becomes unphysical because individual degrees of freedom
  // have become unphysical a few time steps earlier.

  template <int dim>
  void
  TwoPhaseFlowProblem<dim>::project_back_saturation ()
  {
    for (unsigned int i=0; i<solution.block(2).size(); ++i)
      if (solution.block(2)(i) < 0)
        solution.block(2)(i) = 0;
      else if (solution.block(2)(i) > 1)
        solution.block(2)(i) = 1;
  }

  // @sect4{TwoPhaseFlowProblem::get_maximal_velocity}

  // The following function is used in determining the maximal allowable time
  // step. What it does is to loop over all quadrature points in the domain
  // and find what the maximal magnitude of the velocity is.

  /*template <int dim>
  double
  TwoPhaseFlowProblem<dim>::get_maximal_velocity () const
  {
    QGauss<dim>   quadrature_formula(degree+2);
    const unsigned int   n_q_points
      = quadrature_formula.size();

    FEValues<dim> fe_values (fe, quadrature_formula,
                             update_values);
    std::vector<Vector<double> > solution_values(n_q_points,
                                                 Vector<double>(dim+2));
    double max_velocity = 0;

    typename DoFHandler<dim>::active_cell_iterator
    cell = dof_handler.begin_active(),
    endc = dof_handler.end();
    for (; cell!=endc; ++cell)
      {
        fe_values.reinit (cell);
        fe_values.get_function_values (solution, solution_values);

        for (unsigned int q=0; q<n_q_points; ++q)
          {
            Tensor<1,dim> velocity;
            for (unsigned int i=0; i<dim; ++i)
              velocity[i] = solution_values[q](i);

            max_velocity = std::max (max_velocity,
                                     velocity.norm());
          }
      }

    return max_velocity;
  }*/

  // @sect4{TwoPhaseFlowProblem::run}

  // This is the final function of our main class. Its brevity speaks for
  // itself. There are only two points worth noting: First, the function
  // projects the initial values onto the finite element space at the
  // beginning; the VectorTools::project function doing this requires an
  // argument indicating the hanging node constraints. We have none in this
  // program (we compute on a uniformly refined mesh), but the function
  // requires the argument anyway, of course. So we have to create a
  // constraint object. In its original state, constraint objects are
  // unsorted, and have to be sorted (using the ConstraintMatrix::close
  // function) before they can be used. This is what we do here, and which is
  // why we can't simply call the VectorTools::project function with an
  // anonymous temporary object ConstraintMatrix() as the second
  // argument.
  //
  // The second point worth mentioning is that we only compute the length of
  // the present time step in the middle of solving the linear system
  // corresponding to each time step. We can therefore output the present end
  // time of a time step only at the end of the time step.
  //
  // The function as it is here does actually not compute the results
  // found on the web page. The reason is, that even on a decent
  // computer it runs more than a day. If you want to reproduce these
  // results, set the final time at the end of the do loop to 250. template<int dim>

  template<int dim>
  BlockVector<double> TwoPhaseFlowProblem<dim>::source(BlockVector<double>& vec)
   {
     BlockVector<double> tempo(vec);
     tempo=0;
     system_matrix.vmult(tempo,vec);
     system_rhs.add(-1,tempo);
     //system_rhs.print(std::cout);
     return system_rhs;
    }

  template<int dim>
  BlockVector<double> TwoPhaseFlowProblem<dim>::FV(BlockVector<double>& vec)
   {
	  BlockVector<double> temp5(vec);
	  temp5=0;
	  system_matrix.vmult(temp5,vec);
	  system_rhs.add(-1,temp5);

	  /*system_matrix_L.vmult(temp2,vec);
	  temp2.add(-time_step,system_rhs);
	  return temp2;*/
      return system_rhs;
   }

  template<int dim>
    BlockVector<double> TwoPhaseFlowProblem<dim>::LV(BlockVector<double>& vec)
     {
	  BlockVector<double> temp2(vec);
	  temp2 = 0;

	  system_matrix_L.vmult(temp2,vec);

	  return temp2;

     }

  template<int dim>
  void TwoPhaseFlowProblem<dim>::NEWTON_METHOD (const unsigned int  n_time_steps,
                                                const double initial_time,
                                                const double final_time)
      { const double time_step = (final_time-initial_time)/static_cast<double> (n_time_steps);

        double time = initial_time;
        timestep_number = 1;
        double  tol = 0.01;
        unsigned int maxiter = 100;

      do{

    	  //double fail = 0;
          double qt;
    	  // we solving using Newton method an equation of the form
    	  //G(X) = LX- delta t * f(X) - LV^n =0,  n = 0,1,2....
    	  vn =  solution;
    	  BlockVector<double> temp3(vn);
    	  temp3 = 0;
    	  system_matrix_L.vmult(temp3,vn);

    	  BlockVector<double> vnew(vn);
    	  vnew = 0;

    	  F_V = FV(vn);
    	  L_V = LV(vn);
    	  L_V.add(-time_step,F_V);
    	  L_V.add(-1,temp3);
    	  L_V.operator*=(-1); // -G(X)

          unsigned int iter = 0;
    	  qt = 1; // L_V.linfty_norm();

        	while (qt > tol)
        	{
        		// computing the jacobian  of the linear ssystem to be solved for delta_V
        		L_minus_delta_t_jacobian.copy_from(system_matrix_L);
        		L_minus_delta_t_jacobian.add(time_step,system_matrix);

        		iter = iter +1;
        		solve2();
        		vnew.sadd(1,vn,1,delta_V);

        		//vn = vnew;

        		F_V = FV(vnew);
        		L_V = LV(vnew);
        		L_V.add(-time_step,F_V);
        		L_V.add(-1,temp3);
        		L_V.operator*=(-1);

        		qt = L_V.linfty_norm();

        		solution = vnew;
        		assemble_system();
        		assemble_system_HS();

        		if (iter > maxiter)
        				break;
         	}

           solution = vnew;
           output_results ();

           //std::cout << time << std::endl;
           time +=time_step;
           ++timestep_number;
           assemble_system();
           assemble_system_HS();
       }
        while(time <= final_time);
      //std::cout << "fail = " << fail << std::endl;

 }

  /*template<int dim>
  void TwoPhaseFlowProblem<dim>::ROSENBROCK_METHOD (const unsigned int  n_time_steps,
                                                    const double initial_time,
                                                    const double final_time)
    { const double time_step = (final_time-initial_time)/static_cast<double> (n_time_steps);

      double time = initial_time;
      double c[2] {0.8786796564403575,0.2928932188134525};
      double alpha[2][2] {0,0,0.5857864376269050,0};
      unsigned int stage= 2;
      double gamma = 1.707106781186547;

      const unsigned int s =1;
      const unsigned int a =1;

      solutionr = solution;

      system_matrix.block(2,0).operator*=(time_step);
      L_plus_gamma_jacobian.copy_from(system_matrix_L);
      L_plus_gamma_jacobian.add(gamma,system_matrix);

      timestep_number = 1;

      do{
    	system_matrix.block(2,0).operator*=(time_step);
        L_plus_gamma_jacobian.copy_from(system_matrix_L);
        L_plus_gamma_jacobian.add(gamma,system_matrix);

        BlockVector<double> local_sum(solution);
           	 local_sum =0;
       for (unsigned int i=0; i<= stage-1; ++i)
       {
           	unsigned int j=0;

         if (j==i)
       {

              r_h_s = source(solution);

              r_h_s.block(2).operator*=(time_step);

              solve1();

              local_sum.sadd(1,c[i],L_s);
              //solution.add(1,local_sum);
              //L_s.print(std::cout);
              //local_sum.block(2).print(std::cout);

        }

         else

         {
               BlockVector<double> temp1(solution);
               temp1=0;
               BlockVector<double> abc(solution);
               abc=0;
               system_matrix.vmult(temp1,L_s);

               abc.sadd(s,a,solution,-alpha[i][j],L_s);
               r_h_s = source(abc);
               r_h_s.block(2).operator*=(time_step);

               r_h_s.sadd(1,-alpha[i][j],temp1);
               solve1();
               local_sum.sadd(1,c[i],L_s);
               //L_s.print(std::cout);
               //solution.add(1,local_sum);
               //solution.print(std::cout);
               //local_sum.block(2).print(std::cout);

         }
         //solutionr.add(1,local_sum);

       }
       solutionr.add(1,local_sum);
       //solutionr.add(1,local_sum);
       //solution.print(std::cout);
       output_results ();
       solution  = solutionr;

      time += time_step; //
      ++timestep_number;
      assemble_system();
      //solve();
      assemble_rhs_S();
      //solution.print(std::cout);
     }

     while(time <= final_time);
     //solution.print(std::cout);
    }
*/

  template <int dim>
  void TwoPhaseFlowProblem<dim>::solve1 ()
    { inverse_L_minus_gamma_jacobian.initialize(L_plus_gamma_jacobian);
      inverse_L_minus_gamma_jacobian.vmult(L_s,r_h_s);
      //SolverControl           solver_control (1000, 1e-12);
      //SolverCG<>              solver (solver_control);
      //solver.solve (L_minus_gamma_jacobian, L_s, r_h_s,PreconditionIdentity());
    }

  template <int dim>
  void TwoPhaseFlowProblem<dim>::solve2 ()
     { inverse_L_minus_delta_t_jacobian.initialize(L_minus_delta_t_jacobian);
       inverse_L_minus_delta_t_jacobian.vmult(delta_V,L_V);

       /*SolverControl           solver_control (1000, 1e-12);
       SolverCG<>              solver (solver_control);
       solver.solve (L_minus_delta_t_jacobian, delta_V, F_V, PreconditionIdentity());*/
     }

  template <int dim>
  void TwoPhaseFlowProblem<dim>::run ()
  {
    make_grid_and_dofs();
    assemble_system();
    solve();
    //assemble_rhs_S ();
    assemble_system_HS();
    output_results ();

    //const unsigned int stage =2;
    const unsigned int n_time_steps = 100000;
    const double initial_time = 0.;
    const double final_time =100;
    //ROSENBROCK_METHOD(n_time_steps, initial_time, final_time);
    NEWTON_METHOD(n_time_steps, initial_time, final_time);

   /* {
      ConstraintMatrix constraints;
      constraints.close();

      VectorTools::project (dof_handler,
                            constraints,
                            QGauss<dim>(degree+2),
                            InitialValues<dim>(),
                            old_solution);

    }*/

    /*timestep_number = 1;
    double time = 0;

    do
      {
        std::cout << "Timestep " << timestep_number
                  << std::endl;

        assemble_system ();

        solve ();

        output_results ();

        time += time_step;
        ++timestep_number;
        std::cout << "   Now at t=" << time
                  << ", dt=" << time_step << '.'
                  << std::endl
                  << std::endl;
      }
    while (time <= 1.);*/

  }
}

// @sect3{The main function}

// That's it. In the main function, we pass the degree of the finite element
// space to the constructor of the TwoPhaseFlowProblem object.  Here, we use
// zero-th degree elements, i.e. $RT_0\times DQ_0 \times DQ_0$. The rest is as
// in all the other programs.
int main ()
{
  try
    {
      using namespace dealii;
      using namespace Step21;

      deallog.depth_console (0);

      TwoPhaseFlowProblem<2> two_phase_flow_problem(0);
      two_phase_flow_problem.run ();
    }
  catch (std::exception &exc)
    {
      std::cerr << std::endl << std::endl
                << "----------------------------------------------------"
                << std::endl;
      std::cerr << "Exception on processing: " << std::endl
                << exc.what() << std::endl
                << "Aborting!" << std::endl
                << "----------------------------------------------------"
                << std::endl;

      return 1;
    }
  catch (...)
    {
      std::cerr << std::endl << std::endl
                << "----------------------------------------------------"
                << std::endl;
      std::cerr << "Unknown exception!" << std::endl
                << "Aborting!" << std::endl
                << "----------------------------------------------------"
                << std::endl;
      return 1;
    }

  return 0;
}


#include <stdexcept>
#include <boost/test/unit_test.hpp>
#include "composite-shape.hpp"
#include "base-types.hpp"
#include "rectangle.hpp"
#include "circle.hpp"

const double EPSILON = 0.00000001;

BOOST_AUTO_TEST_SUITE(CompositeShapeTest)

  BOOST_AUTO_TEST_SUITE(CompositeShape_CompositeShapeConstructor)

    BOOST_AUTO_TEST_CASE(CompositeShape_NoParameters_NoException)
    {
      BOOST_CHECK_NO_THROW(samokhin::CompositeShape());
    }

    BOOST_AUTO_TEST_CASE(CompositeShape_ValidCompositeShapeAsParameter_NoException)
    {
      double width = 1.2;
      double height = 5.3;
      samokhin::point_t pos = {3.2, 7.4};

      samokhin::Rectangle rectangle(width, height, pos);
      samokhin::CompositeShape compositeShape{};
      compositeShape.add(std::make_shared<samokhin::Rectangle>(rectangle));

      BOOST_CHECK_NO_THROW(samokhin::CompositeShape{compositeShape});
    }

    BOOST_AUTO_TEST_CASE(CompositeShape_EmptyCompositeShapeAsParameter_NoException)
    {
      samokhin::CompositeShape compositeShape{};
      BOOST_CHECK_NO_THROW(samokhin::CompositeShape{compositeShape});
    }

    BOOST_AUTO_TEST_CASE(CompositeShape_RectangleAsParameter_NoException)
    {
      double width = 0.2;
      double height = 4.3;
      samokhin::point_t pos = {-3.1, 3.4};

      samokhin::Rectangle rectangle(width, height, pos);

      samokhin::CompositeShape compositeShape{};
      compositeShape.add(std::make_shared<samokhin::Rectangle>(rectangle));
    }

    BOOST_AUTO_TEST_CASE(CompositeShape_CircleAsParameter_NoException)
    {
      double radius = 3.23;
      samokhin::point_t pos = {4.1, 3.4};

      std::shared_ptr<samokhin::Circle> circle(new samokhin::Circle(radius, pos));
      samokhin::CompositeShape compositeShape{};

      BOOST_CHECK_NO_THROW(compositeShape.add(circle));
    }

  BOOST_AUTO_TEST_SUITE_END()

  BOOST_AUTO_TEST_SUITE(CompositeShape_GetArea)

    BOOST_AUTO_TEST_CASE(GetArea_RightArea)
    {
      double width = 4.3;
      double height = 2.5;
      samokhin::point_t center = {5.1, 3.2};
      std::shared_ptr<samokhin::Rectangle> rectangle(new samokhin::Rectangle(width, height, center));

      double radius = 7.2;
      double x = 2.4;
      double y = -9.6;
      std::shared_ptr<samokhin::Circle> circle(new samokhin::Circle(radius, {x, y}));

      samokhin::CompositeShape compositeShape{};
      compositeShape.add(rectangle);
      compositeShape.add(circle);

      double expectedArea = rectangle->getArea() + circle->getArea();

      BOOST_CHECK_CLOSE(compositeShape.getArea(), expectedArea, EPSILON);
    }

    BOOST_AUTO_TEST_CASE(GetArea_ShapeAfterMove_ExcepitonThrown)
    {
      double width = 1.8;
      double height = 9.2;
      samokhin::point_t center = {2.1, 3.8};
      std::shared_ptr<samokhin::Rectangle> rectangle(new samokhin::Rectangle(width, height, center));

      samokhin::CompositeShape compositeShape{};
      compositeShape.add(rectangle);

      samokhin::CompositeShape compositeShape2 = std::move(compositeShape);

      BOOST_CHECK_THROW(compositeShape.getArea(), std::domain_error);
    }

  BOOST_AUTO_TEST_SUITE_END()

  BOOST_AUTO_TEST_SUITE(CompositeShape_GetFrameRect)

    BOOST_AUTO_TEST_CASE(GetFrameRect_EmptyCompositeShape_NegativeWidthAndHeight)
    {
      samokhin::CompositeShape compositeShape{};
      BOOST_CHECK_CLOSE(compositeShape.getFrameRect().width, -1, EPSILON);
      BOOST_CHECK_CLOSE(compositeShape.getFrameRect().height, -1, EPSILON);
    }

    BOOST_AUTO_TEST_CASE(GetFrameRect_ValidCompositeShape_RightFrameRect)
    {
      double width = 5.0;
      double height = 3.0;
      samokhin::point_t center = {4.5, 3.5};
      std::shared_ptr<samokhin::Rectangle> rectangle(new samokhin::Rectangle(width, height, center));

      double radius = 2.0;
      double x = 7.0;
      double y = 6.0;
      std::shared_ptr<samokhin::Circle> circle(new samokhin::Circle(radius, {x, y}));

      samokhin::CompositeShape compositeShape{};
      compositeShape.add(rectangle);
      compositeShape.add(circle);

      samokhin::rectangle_t expectedFrame = {{5.5, 5.0}, 7.0, 6.0};
      samokhin::rectangle_t frame = compositeShape.getFrameRect();
      BOOST_CHECK_CLOSE(expectedFrame.pos.x, frame.pos.x, EPSILON);
      BOOST_CHECK_CLOSE(expectedFrame.pos.y, frame.pos.y, EPSILON);
      BOOST_CHECK_CLOSE(expectedFrame.width, frame.width, EPSILON);
      BOOST_CHECK_CLOSE(expectedFrame.height, frame.height, EPSILON);
    }

    BOOST_AUTO_TEST_CASE(GetFrameRect_ShapeAfterMove_ExcepitonThrown)
    {
      double width = 6.8;
      double height = 9.3;
      samokhin::point_t center = {2.7, 3.22};
      std::shared_ptr<samokhin::Rectangle> rectangle(new samokhin::Rectangle(width, height, center));

      samokhin::CompositeShape compositeShape{};
      compositeShape.add(rectangle);

      samokhin::CompositeShape compositeShape2 = std::move(compositeShape);

      BOOST_CHECK_THROW(compositeShape.getFrameRect(), std::domain_error);
    }

  BOOST_AUTO_TEST_SUITE_END()

  BOOST_AUTO_TEST_SUITE(CompositeShape_Move_ByCords)

    BOOST_AUTO_TEST_CASE(Move_EmptyCompositeShapeAtMovingByCords_NoException)
    {
      double dx = 3.0;
      double dy = 2.6;
      samokhin::CompositeShape compositeShape{};
      BOOST_CHECK_NO_THROW(compositeShape.move(dx, dy));
    }

    BOOST_AUTO_TEST_CASE(Move_CompositeShapesFrameAtMovingByCords_ConstantFrame)
    {
      double width = 4.6;
      double height = 3.5;
      samokhin::point_t center = {8.1, 1.2};
      std::shared_ptr<samokhin::Rectangle> rectangle(new samokhin::Rectangle(width, height, center));

      double radius = 1.2;
      double x = 2.8;
      double y = 9.6;
      std::shared_ptr<samokhin::Circle> circle(new samokhin::Circle(radius, {x, y}));

      samokhin::CompositeShape compositeShape{};
      compositeShape.add(rectangle);
      compositeShape.add(circle);

      double dx = 3.0;
      double dy = 2.6;
      samokhin::rectangle_t frame = compositeShape.getFrameRect();
      compositeShape.move(dx, dy);
      samokhin::rectangle_t newFrame = compositeShape.getFrameRect();
      samokhin::point_t newCentre = {frame.pos.x + dx, frame.pos.y + dy};

      BOOST_CHECK_CLOSE(frame.width, newFrame.width, EPSILON);
      BOOST_CHECK_CLOSE(frame.height, newFrame.height, EPSILON);

      BOOST_CHECK_CLOSE(newCentre.x, newFrame.pos.x, EPSILON);
      BOOST_CHECK_CLOSE(newCentre.y, newFrame.pos.y, EPSILON);
    }

    BOOST_AUTO_TEST_CASE(Move_CompositeShapesAreaAtMovingByCords_ConstantArea)
    {
      double width = 6.3;
      double height = 5.1;
      samokhin::point_t center = {1.9, 4.19};
      std::shared_ptr<samokhin::Rectangle> rectangle(new samokhin::Rectangle(width, height, center));

      double radius = 1.2;
      double x = 2.2;
      double y = 9.6;
      std::shared_ptr<samokhin::Circle> circle(new samokhin::Circle(radius, {x, y}));

      samokhin::CompositeShape compositeShape{};
      compositeShape.add(rectangle);
      compositeShape.add(circle);

      double area = compositeShape.getArea();
      double dx = 2.6;
      double dy = 9.4;
      compositeShape.move(dx, dy);
      BOOST_CHECK_CLOSE(compositeShape.getArea(), area, EPSILON);
    }

    BOOST_AUTO_TEST_CASE(Move_ShapeAfterMove_ExcepitonThrown)
    {
      double width = 1.2;
      double height = 6.1;
      samokhin::point_t center = {4.1, 3.2};
      std::shared_ptr<samokhin::Rectangle> rectangle(new samokhin::Rectangle(width, height, center));

      samokhin::CompositeShape compositeShape{};
      compositeShape.add(rectangle);

      samokhin::CompositeShape compositeShape2 = std::move(compositeShape);

      double dx = 7.6;
      double dy = 5.4;
      BOOST_CHECK_THROW(compositeShape.move(dx, dy), std::domain_error);
    }

  BOOST_AUTO_TEST_SUITE_END()

  BOOST_AUTO_TEST_SUITE(CompositeShape_Move_AtPoint)

    BOOST_AUTO_TEST_CASE(Move_EmptyCompositeShapeAtMovingAtPoint_ExceptionThrown)
    {
      samokhin::point_t center = {6.1, 6.2};
      samokhin::CompositeShape compositeShape{};
      BOOST_CHECK_NO_THROW(compositeShape.move(center));
    }

    BOOST_AUTO_TEST_CASE(Move_CompositeShapesFrameAtMovingAtPoint_ConstantFrame)
    {
      double width = 1.6;
      double height = 3.1;
      samokhin::point_t center = {6.1, 6.2};
      std::shared_ptr<samokhin::Rectangle> rectangle(new samokhin::Rectangle(width, height, center));

      double radius = 1.3;
      double x = 2.2;
      double y = 3.6;
      std::shared_ptr<samokhin::Circle> circle(new samokhin::Circle(radius, {x, y}));

      samokhin::CompositeShape compositeShape{};
      compositeShape.add(rectangle);
      compositeShape.add(circle);

      samokhin::point_t newCentre = {3.0, 2.6};
      samokhin::rectangle_t frame = compositeShape.getFrameRect();
      compositeShape.move(newCentre);
      samokhin::rectangle_t newFrame = compositeShape.getFrameRect();

      BOOST_CHECK_CLOSE(frame.width, newFrame.width, EPSILON);
      BOOST_CHECK_CLOSE(frame.height, newFrame.height, EPSILON);

      BOOST_CHECK_CLOSE(newCentre.x, newFrame.pos.x, EPSILON);
      BOOST_CHECK_CLOSE(newCentre.y, newFrame.pos.y, EPSILON);
    }

    BOOST_AUTO_TEST_CASE(Move_CompositeShapesAreaAtMovingAtPoint_ConstantArea)
    {
      double width = 1.6;
      double height = 3.1;
      samokhin::point_t center = {6.1, 6.2};
      std::shared_ptr<samokhin::Rectangle> rectangle(new samokhin::Rectangle(width, height, center));

      double radius = 1.3;
      double x = 2.2;
      double y = 3.6;
      std::shared_ptr<samokhin::Circle> circle(new samokhin::Circle(radius, {x, y}));

      samokhin::CompositeShape compositeShape{};
      compositeShape.add(rectangle);
      compositeShape.add(circle);

      samokhin::point_t newCentre = {3.0, 2.6};

      double area = compositeShape.getArea();
      compositeShape.move(newCentre);

      BOOST_CHECK_CLOSE(compositeShape.getArea(), area, EPSILON);
    }

    BOOST_AUTO_TEST_CASE(Move_ShapeAfterMove_ExcepitonThrown)
    {
      double width = 22.2;
      double height = 4.1;
      samokhin::point_t center = {4.1, 3.2};
      std::shared_ptr<samokhin::Rectangle> rectangle(new samokhin::Rectangle(width, height, center));

      samokhin::CompositeShape compositeShape{};
      compositeShape.add(rectangle);

      samokhin::CompositeShape compositeShape2 = std::move(compositeShape);

      samokhin::point_t newCenter = {6.1, 6.2};
      BOOST_CHECK_THROW(compositeShape.move(newCenter), std::domain_error);
    }

  BOOST_AUTO_TEST_SUITE_END()

  BOOST_AUTO_TEST_SUITE(CompositeShape_Scale)

    BOOST_AUTO_TEST_CASE(Scale_NegativeNumberAsScaleCoefficient_ExceptionThrown)
    {
      double width = 8.6;
      double height = 3.8;
      samokhin::point_t center = {6.2, 5.2};
      std::shared_ptr<samokhin::Rectangle> rectangle(new samokhin::Rectangle(width, height, center));

      double radius = 1.1;
      double x = 23.6;
      double y = 3.6;
      std::shared_ptr<samokhin::Circle> circle(new samokhin::Circle(radius, {x, y}));

      samokhin::CompositeShape compositeShape{};
      compositeShape.add(rectangle);
      compositeShape.add(circle);

      double k = -9;
      BOOST_CHECK_THROW(compositeShape.scale(k), std::invalid_argument);
    }

    BOOST_AUTO_TEST_CASE(Scale_PositiveNumberAsScaleCoefficient_NoException)
    {
      double width = 1.5;
      double height = 9.8;
      samokhin::point_t center = {6.2, 5.2};
      std::shared_ptr<samokhin::Rectangle> rectangle(new samokhin::Rectangle(width, height, center));

      double radius = 1.2;
      double x = 23.6;
      double y = 3.6;
      std::shared_ptr<samokhin::Circle> circle(new samokhin::Circle(radius, {x, y}));

      samokhin::CompositeShape compositeShape{};
      compositeShape.add(rectangle);
      compositeShape.add(circle);

      double k = 6.1;
      BOOST_CHECK_NO_THROW(compositeShape.scale(k));
    }

    BOOST_AUTO_TEST_CASE(Scale_ZeroAsScaleCoefficient_NoException)
    {
      double width = 9.8;
      double height = 1.5;
      samokhin::point_t center = {1.2, 5.2};
      std::shared_ptr<samokhin::Rectangle> rectangle(new samokhin::Rectangle(width, height, center));

      double radius = 7.2;
      double x = 32.6;
      double y = 3.6;
      std::shared_ptr<samokhin::Circle> circle(new samokhin::Circle(radius, {x, y}));

      samokhin::CompositeShape compositeShape{};
      compositeShape.add(rectangle);
      compositeShape.add(circle);

      double k = 0.0;
      BOOST_CHECK_NO_THROW(compositeShape.scale(k));
    }

    BOOST_AUTO_TEST_CASE(Scale_CompositeShapeArea_SquareAreaChange)
    {
      double width = 2.5;
      double height = 9.2;
      samokhin::point_t center = {1.2, 5.2};
      std::shared_ptr<samokhin::Rectangle> rectangle(new samokhin::Rectangle(width, height, center));

      double radius = 9.3;
      double x = 5.2;
      double y = 1.2;
      std::shared_ptr<samokhin::Circle> circle(new samokhin::Circle(radius, {x, y}));

      samokhin::CompositeShape compositeShape{};
      compositeShape.add(rectangle);
      compositeShape.add(circle);

      double area = compositeShape.getArea();
      double k = 1.1;
      compositeShape.scale(k);

      BOOST_CHECK_CLOSE(compositeShape.getArea(), area * k * k, EPSILON);
    }

    BOOST_AUTO_TEST_CASE(Scale_ShapeAfterMove_ExcepitonThrown)
    {
      double width = 1.2;
      double height = 63.1;
      samokhin::point_t center = {7.1, 3.1};
      std::shared_ptr<samokhin::Rectangle> rectangle(new samokhin::Rectangle(width, height, center));

      samokhin::CompositeShape compositeShape{};
      compositeShape.add(rectangle);

      samokhin::CompositeShape compositeShape2 = std::move(compositeShape);

      double k = 0.1;
      BOOST_CHECK_THROW(compositeShape.scale(k), std::domain_error);
    }

  BOOST_AUTO_TEST_SUITE_END()

  BOOST_AUTO_TEST_SUITE(CompositeShape_Add)

    BOOST_AUTO_TEST_CASE(Add_ValidShapesAsParameter_NoException)
    {
      double width = 2.5;
      double height = 9.2;
      samokhin::point_t center = {1.2, 5.2};
      std::shared_ptr<samokhin::Rectangle> rectangle(new samokhin::Rectangle(width, height, center));

      double radius = 9.3;
      double x = 5.2;
      double y = 1.2;
      std::shared_ptr<samokhin::Circle> circle(new samokhin::Circle(radius, {x, y}));

      samokhin::CompositeShape compositeShape{};

      BOOST_CHECK_NO_THROW(compositeShape.add(circle));
      BOOST_CHECK_NO_THROW(compositeShape.add(rectangle));
    }

    BOOST_AUTO_TEST_CASE(Add_EmptyCompositeShapeAsParameter_NoException)
    {
      samokhin::CompositeShape emptyCompositeShape{};
      samokhin::CompositeShape compositeShape{};
      BOOST_CHECK_NO_THROW(compositeShape.add(std::make_shared<samokhin::CompositeShape>(emptyCompositeShape)));
    }

    BOOST_AUTO_TEST_CASE(Add_ValidShapesAsParameter_CorrectAddition)
    {
      double radius = 9.3;
      double x = 5.2;
      double y = 1.2;
      std::shared_ptr<samokhin::Circle> circle(new samokhin::Circle(radius, {x, y}));

      samokhin::CompositeShape compositeShape{};

      compositeShape.add(circle);

      BOOST_CHECK_EQUAL(compositeShape.getSize(), 1);
      BOOST_CHECK_EQUAL(compositeShape[0], circle);
    }

  BOOST_AUTO_TEST_SUITE_END()

  BOOST_AUTO_TEST_SUITE(CompositeShape_Remove)

    BOOST_AUTO_TEST_CASE(Remove_TooBigIndexAsParameter_ExceptionThrown)
    {
      double radius = 9.3;
      double x = 5.2;
      double y = 1.2;
      std::shared_ptr<samokhin::Circle> circle(new samokhin::Circle(radius, {x, y}));

      samokhin::CompositeShape compositeShape{};
      compositeShape.add(circle);

      BOOST_CHECK_THROW(compositeShape.remove(1), std::out_of_range);
    }

    BOOST_AUTO_TEST_CASE(Remove_RemoveFromEmptyShape_ExceptionThrown)
    {
      samokhin::CompositeShape compositeShape{};
      BOOST_CHECK_THROW(compositeShape.remove(1), std::out_of_range);
    }

    BOOST_AUTO_TEST_CASE(Remove_ValidIndexAsParameter_NoException)
    {
      double radius = 9.3;
      double x = 5.2;
      double y = 1.2;
      std::shared_ptr<samokhin::Circle> circle(new samokhin::Circle(radius, {x, y}));

      samokhin::CompositeShape compositeShape{};
      compositeShape.add(circle);

      BOOST_CHECK_NO_THROW(compositeShape.remove(0));
    }

    BOOST_AUTO_TEST_CASE(Remove_ValidIndexAsParameter_CorrectRemove)
    {
      double radius = 9.3;
      double x = 5.2;
      double y = 1.2;
      std::shared_ptr<samokhin::Circle> circle(new samokhin::Circle(radius, {x, y}));

      samokhin::CompositeShape compositeShape{};
      compositeShape.add(circle);
      compositeShape.remove(0);

      BOOST_CHECK_EQUAL(compositeShape.getSize(), 0);
    }

  BOOST_AUTO_TEST_SUITE_END()

  BOOST_AUTO_TEST_SUITE(CompositeShape_IndexOperator)

    BOOST_AUTO_TEST_CASE(IndexOperator_InvalidIndexAsParameter_ExceptionThrown)
    {
      double radius = 9.3;
      double x = 5.2;
      double y = 1.2;
      std::shared_ptr<samokhin::Circle> circle(new samokhin::Circle(radius, {x, y}));

      samokhin::CompositeShape compositeShape{};
      compositeShape.add(circle);
      size_t invalidIndex = 2;
      BOOST_CHECK_THROW(compositeShape[invalidIndex], std::out_of_range);
    }

    BOOST_AUTO_TEST_CASE(IndexOperator_RightIndexAsParameter_NoException)
    {
      double radius = 9.3;
      double x = 5.2;
      double y = 1.2;
      std::shared_ptr<samokhin::Circle> circle(new samokhin::Circle(radius, {x, y}));

      samokhin::CompositeShape compositeShape{};
      compositeShape.add(circle);

      size_t validIndex = 0;
      BOOST_CHECK_NO_THROW(compositeShape[validIndex]);
    }

    BOOST_AUTO_TEST_CASE(IndexOperator_RightIndexAsParameter_RightShape)
    {
      double radius = 9.3;
      double x = 5.2;
      double y = 1.2;
      std::shared_ptr<samokhin::Circle> circle(new samokhin::Circle(radius, {x, y}));

      samokhin::CompositeShape compositeShape{};
      compositeShape.add(circle);

      samokhin::rectangle_t frame = compositeShape[0]->getFrameRect();
      samokhin::point_t centre = frame.pos;

      BOOST_CHECK_CLOSE(compositeShape[0]->getArea(), circle->getArea(), EPSILON);
      BOOST_CHECK_CLOSE(frame.width, 2 * radius, EPSILON);
      BOOST_CHECK_CLOSE(frame.height, 2 * radius, EPSILON);
      BOOST_CHECK_CLOSE(centre.x, x, EPSILON);
      BOOST_CHECK_CLOSE(centre.y, y, EPSILON);
    }

  BOOST_AUTO_TEST_SUITE_END()

  BOOST_AUTO_TEST_SUITE(CompositeShape_Rotate)

    BOOST_AUTO_TEST_CASE(Rotate_AnyAngleAsParameter_ConstantArea)
    {
      double width = 2.1;
      double height = 9.3;
      samokhin::point_t center = {2.2, 1.2};
      std::shared_ptr<samokhin::Rectangle> rectangle(new samokhin::Rectangle(width, height, center));

      double radius = 1.3;
      double x = 1.2;
      double y = 3.2;
      std::shared_ptr<samokhin::Circle> circle(new samokhin::Circle(radius, {x, y}));

      samokhin::CompositeShape compositeShape{};
      compositeShape.add(rectangle);
      compositeShape.add(circle);

      double area = compositeShape.getArea();
      double angle = 80.0;
      compositeShape.rotate(angle);
      BOOST_CHECK_CLOSE(area, compositeShape.getArea(), EPSILON);
    }

    BOOST_AUTO_TEST_CASE(Rotate_AnyAngleAsParameter_RightFrameChange)
    {
      double width = 3.1;
      double height = 9.2;
      samokhin::point_t center = {1.2, 1.8};
      std::shared_ptr<samokhin::Rectangle> rectangle(new samokhin::Rectangle(width, height, center));

      double radius = 1.3;
      double x = 6.2;
      double y = 3.3;
      std::shared_ptr<samokhin::Circle> circle(new samokhin::Circle(radius, {x, y}));

      samokhin::CompositeShape compositeShape{};
      compositeShape.add(rectangle);
      compositeShape.add(circle);

      double angle = -90;
      samokhin::rectangle_t frame = compositeShape.getFrameRect();
      compositeShape.rotate(angle);
      samokhin::rectangle_t newFrame = compositeShape.getFrameRect();
      BOOST_CHECK_CLOSE(frame.width, newFrame.height, EPSILON);
      BOOST_CHECK_CLOSE(frame.height, newFrame.width, EPSILON);
    }

    BOOST_AUTO_TEST_CASE(Rotate_ShapeAfterMove_ExcepitonThrown)
    {
      double width = 4.5;
      double height = 5.1;
      samokhin::point_t center = {-3.1, 3.2};
      std::shared_ptr<samokhin::Rectangle> rectangle(new samokhin::Rectangle(width, height, center));

      samokhin::CompositeShape compositeShape{};
      compositeShape.add(rectangle);

      samokhin::CompositeShape compositeShape2 = std::move(compositeShape);

      double angle = 9;
      BOOST_CHECK_THROW(compositeShape.rotate(angle), std::domain_error);
    }

  BOOST_AUTO_TEST_SUITE_END()

BOOST_AUTO_TEST_SUITE_END()