#include <vector>
#include <QOpenGLShaderProgram>
#include <QScreen>
#include <QCloseEvent>
#include <QDateTime>
#include <QOpenGLVertexArrayObject>
#include <QOpenGLBuffer>
#include <iostream>
#include <fstream>
#include <QOpenGLPaintDevice>
#include <QtMath>

#include "NMatrixOperations.h"
#include "Camera3D.h"
#include "CameraND.h"
#include "ObjectND.h"
#include "ObjectNDManager.h"

#include <Eigen/Dense>

#include "RenderingWindow.h"

RenderingWindow::RenderingWindow() : shaderProgram(0), m_frame(0) {
	//setting up 3d camera
	camera3D = new Camera3D(QVector3D(0.0f, 0.0f, 3.0f));
	objectManager = nullptr;
}

RenderingWindow::~RenderingWindow() {}

void RenderingWindow::initialize() {
	glViewport(0, 0, width(), height());
	glEnable(GL_DEPTH_TEST);

	this->setCursor(Qt::BlankCursor);

	lastX = 0;
	lastY = 0;
	wasActive = this->isActive();

	shaderProgram = new QOpenGLShaderProgram(this);
	shaderProgram->addShaderFromSourceFile(QOpenGLShader::Vertex, "simple_color_shader.vs");
	shaderProgram->addShaderFromSourceFile(QOpenGLShader::Fragment, "simple_color_shader.frag");
	shaderProgram->link();

	inColor_uniform = shaderProgram->uniformLocation("inColor");
	model_uniform = shaderProgram->uniformLocation("model");
	view_uniform = shaderProgram->uniformLocation("view");
	proj_uniform = shaderProgram->uniformLocation("projection");

	this->openFile("hypercube.ndp");

}

void RenderingWindow::render() {
	glClearColor(backgroundColor[0], backgroundColor[1], backgroundColor[2], 1.0f);
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);

	//Get delta time to check how much to move
	qint64 currentFrame = QDateTime::currentMSecsSinceEpoch();
	delta_time = ((double)(currentFrame - last_frame))/1000.f;
	last_frame = currentFrame;

	doMovement();

	//Set current shader
	//TODO: Get this piece out of here. It probably could be on initialization since it is the only shader we're using (maybe)
	shaderProgram->bind();

	//Set color of wireframe
	QVector4D wireColor = { 1, 0, 0, 1 };
	shaderProgram->setUniformValue(inColor_uniform, wireColor);

	//Get camera transformation
	QMatrix4x4 view, projection, model, asd;
	view = camera3D->GetViewMatrix();

	//For some reason, the commented line below using Qt gives us the wrong matrix
	//projection.perspective(camera3D->Zoom, ((double)width()) / ((double)height()), 0.1f, 1000.f);
	//So we're going to set up our projection matrix by hand
	double one_over_tan_eyeangle = 1 / qTan(camera3D->Zoom / 2);
	double aspect = ((double)width()) / ((double)height());

	projection(0, 0) = one_over_tan_eyeangle / aspect;
	projection(1, 1) = one_over_tan_eyeangle;
	projection(2, 2) = (0.1f + 1000.f) / (0.1f - 1000.f);
	projection(3, 3) = 0;
	projection(2, 3) = 2 * (0.1f * 1000.f) / (0.1f - 1000.f);
	projection(3, 2) = -1;

	model.translate(0.f, 0.f, 0.f);

	//Pass matrices to shader
	shaderProgram->setUniformValue(model_uniform, model);
	shaderProgram->setUniformValue(view_uniform, view);
	shaderProgram->setUniformValue(proj_uniform, projection);

	objectManager->render();

	QVector3D v = camera3D->Position + camera3D->Front;
	emit signal3DLocation(camera3D->Position.x(), camera3D->Position.y(), camera3D->Position.z(), v.x(), v.y(), v.z());
}

#define print(something) {std::stringstream ss; ss << something; qDebug() << ss.str().c_str(); }

void RenderingWindow::doMovement() {
	// Camera controls
	//int mask = 1 << 24;

	if (keys[OPENGL_WINDOW_W]) {
		camera3D->ProcessKeyboard(FORWARD, delta_time);
	}
	if (keys[OPENGL_WINDOW_S]) {
		camera3D->ProcessKeyboard(BACKWARD, delta_time);
	}
	if (keys[OPENGL_WINDOW_A]) {
		camera3D->ProcessKeyboard(LEFT, delta_time);
	}
	if (keys[OPENGL_WINDOW_D]) {
		camera3D->ProcessKeyboard(RIGHT, delta_time);
	}
	if (keys[OPENGL_WINDOW_SPACE]) {
		camera3D->ProcessKeyboard(UP, delta_time);
	}
	if (keys[OPENGL_WINDOW_SHIFT]) {
		camera3D->ProcessKeyboard(DOWN, delta_time);
	}

	if (keys[OPENGL_WINDOW_Q]) {
		if (objectManager->composingPolytopes.size()) {
			for (int i = 0; i < objectManager->composingPolytopes.at(0)->size(); i++) {
				objectManager->composingPolytopes.at(0)->at(i) = rotateMatrixN(objectManager->numberOfDimensions, rotPlane2, rotPlane1, delta_time) * objectManager->composingPolytopes.at(0)->at(i);
			}
			objectManager->updateObjects(cutLocations, camerasND);
			emit signalRotation(-delta_time);
		}
	}

	if (keys[OPENGL_WINDOW_E]) {
		if (objectManager->composingPolytopes.size()) {
			for (int i = 0; i < objectManager->composingPolytopes.at(0)->size(); i++) {
				objectManager->composingPolytopes.at(0)->at(i) = rotateMatrixN(objectManager->numberOfDimensions, rotPlane1, rotPlane2, delta_time) * objectManager->composingPolytopes.at(0)->at(i);
			}
			objectManager->updateObjects(cutLocations, camerasND);

			emit signalRotation(delta_time);
		}
	}

	if (keys[OPENGL_WINDOW_O]) {
		qDebug() << "CurrentVertexes:";
		objectManager->printStructure();
	}

}

void RenderingWindow::mouseMoveEvent(QMouseEvent *event) {
	if (!this->isActive()) {
		//If it has just changed away from this window, change the cursor back to arrow
		if (this->wasActive) {
			this->setCursor(Qt::ArrowCursor);
		}

		this->wasActive = false;
		return;
	}

	//if has just changed into this window, change cursor to blank
	if (!this->wasActive) {
		this->setCursor(Qt::BlankCursor);

		QPoint glob = mapToGlobal(QPoint(width() / 2, height() / 2));
		QCursor::setPos(glob);
		lastX = width() / 2;
		lastY = height() / 2;
		this->wasActive = true;
		return;
	}

	int deltaX = event->pos().x() - lastX;
	int deltaY = lastY - event->pos().y();

	int signalX = deltaX > 0 ? 1 : -1;
	int signalY = deltaY > 0 ? 1 : -1;

	if (!(signalX * deltaX > 500 || signalY * deltaY > 500)) {
		camera3D->ProcessMouseMovement(deltaX, deltaY);
	}

	QPoint glob = mapToGlobal(QPoint(width() / 2, height() / 2));
	QCursor::setPos(glob);
	lastX = width() / 2;
	lastY = height() / 2;

	this->wasActive = true;
}

void RenderingWindow::moveCameraOrigin(int currentCamera, int changedIndex, double newValue) {
	//Camera should never be smaller than four,
	//Because it receives the Dimension of the camera and not its index
	if (currentCamera > 3) {
		currentCamera -= 4;
		camerasND.at(camerasND.size() - currentCamera - 1).Position(changedIndex) = newValue;

		for (ObjectND* obj : (objectManager->currentObjects)) {
			obj->updateVertexMatrix(camerasND);
		}

		qDebug() << "Changing Origin of Dimension" << camerasND.at(camerasND.size() - currentCamera - 1).N;
		qDebug() << "Position " << changedIndex << " = " << newValue;
	}
}

void RenderingWindow::moveCameraTarget(int currentCamera, int changedIndex, double newValue) {
	//Camera should never be smaller than four,
	//Because it receives the Dimension of the camera and not its index
	if (currentCamera > 3) {
		currentCamera -= 4;
		camerasND.at(camerasND.size() - currentCamera - 1).Target(changedIndex) = newValue;
		for (ObjectND* obj : (objectManager->currentObjects)) {
			obj->updateVertexMatrix(camerasND);
		}

		qDebug() << "Changing Target of Dimension" << camerasND.at(camerasND.size() - currentCamera - 1).N;
		qDebug() << "Position " << changedIndex << " = " << newValue;
	}
}

void RenderingWindow::changeCameraPerspective(int currentCamera, bool newValue) {
	if (currentCamera == 3) {
		//This should never occur
	}
	else {
		currentCamera -= 4;
		camerasND.at(camerasND.size() - currentCamera - 1).usePerspective = newValue;
		for (ObjectND* obj : (objectManager->currentObjects)) {
			obj->updateVertexMatrix(camerasND);
		}

		qDebug() << "Changing Perspective of Dimension" << camerasND.at(camerasND.size() - currentCamera - 1).N;
		qDebug() << "New Value is " << newValue;
	}
}

void RenderingWindow::closeEvent(QEvent *event) {
	if (isClosing) return;

	isClosing = true;

	emit signalClose();
}

void RenderingWindow::resizeEvent(QResizeEvent* event) {
	if (m_context) {
		glViewport(0, 0, width(), height());
	}
}

void RenderingWindow::doClose() {
	QWindow::close();
}

bool RenderingWindow::event(QEvent *event) {
	return OpenGLWindow::event(event);
}

void RenderingWindow::receiveTest() {
	for (ObjectND* obj : (objectManager->currentObjects)) {
		if (obj->getRenderingMode() == OBJECTND_RENDERINGMODE_FACES) {
			obj->setRenderingMode(OBJECTND_RENDERINGMODE_LINES);
		} else {
			obj->setRenderingMode(OBJECTND_RENDERINGMODE_FACES);
		}
	}
}

void RenderingWindow::receiveChangeFaceCulling(bool newValue){
	objectManager->setUsingCulling(newValue);
}

void RenderingWindow::receiveChangeWireframe(bool newValue) {
	objectManager->setRenderingMode(newValue ? OBJECTND_RENDERINGMODE_LINES : OBJECTND_RENDERINGMODE_FACES);
}

void RenderingWindow::receiveOpenFile(QString fileName) {
	this->openFile(fileName);
}

bool hasEnding(std::string const &fullString, std::string const &ending) {
	if (fullString.length() >= ending.length()) {
		return (0 == fullString.compare(fullString.length() - ending.length(), ending.length(), ending));
	}
	else {
		return false;
	}
}

void RenderingWindow::generateCameras(int numCameras, double xOffset) {
	if (objectManager) {
		camerasND.clear();
		for (int i = numCameras; i > 3; i--) {
			//adding a ND camera to our array of cameras
			Eigen::VectorXd position = Eigen::VectorXd::Zero(i);
			position(i - 1) = xOffset;
			Eigen::VectorXd target = Eigen::VectorXd::Zero(i);
			camerasND.push_back(CameraND(i, position, target));
		}
	}
}

void RenderingWindow::receiveNewNumberOfCuts(int numberOfCuts) {
	generateCameras(objectManager->numberOfDimensions - numberOfCuts);
	cutLocations = Eigen::VectorXd::Zero(numberOfCuts);
	objectManager->updateObjects(cutLocations, camerasND);

	std::vector<int> cutLocationsVector;
	for (int i = 0; i < cutLocations.size(); i++) {
		cutLocationsVector.push_back(cutLocations(i));
	}

	qDebug() << "New Number Of Cuts: " << numberOfCuts;

	double maxValue = 0.0;
	for (int i = 0; i < objectManager->composingPolytopes.at(0)->size(); i++) {
		double maxCoeff = objectManager->composingPolytopes.at(0)->at(i).norm();
		if (abs(maxCoeff) > maxValue) {
			maxValue = maxCoeff;
		}
	}

	emit signalGenerateDimensionVieweing(cutLocationsVector, &camerasND, camera3D, true, maxValue);

}

void RenderingWindow::receiveCutLocationChange(int index, double newValue) {
	qDebug() << "Change Cut " << index << " to " << newValue;

	cutLocations(index) = newValue;
	objectManager->updateObjects(cutLocations, camerasND);
}

void RenderingWindow::openFile(QString openFile) {
	if (objectManager) {
		delete objectManager;
	}

	rotPlane1 = 0;
	rotPlane2 = 1;

	std::string asStdString = openFile.toLower().toStdString();
	print("Opening " << asStdString);
	std::ifstream inStream(asStdString);
	if (hasEnding(asStdString, ".ndp")) {
		objectManager = new ObjectNDManager(OBJECTNDMANAGER_NDP_FORMAT, inStream, this);
		isAllowingFaceculling = false;
	} else if (hasEnding(asStdString, ".pol")) {
		objectManager = new ObjectNDManager(OBJECTNDMANAGER_POL_FORMAT, inStream, this);
		isAllowingFaceculling = true;
	} else {
		objectManager = new ObjectNDManager(OBJECTNDMANAGER_NO_FORMAT, inStream, this);
		isAllowingFaceculling = false;
	}

	generateCameras(objectManager->numberOfDimensions);

	double maxValue = 0.0;

	if (objectManager->composingPolytopes.size()) {
		if (objectManager->composingPolytopes.at(0)->size()) {
			int polyD = objectManager->composingPolytopes.size() - 1;
			int spaceD = objectManager->composingPolytopes.at(0)->at(0).size();
			int maxPossibleCutsByPoly = polyD - 1;
			int numCutsBySpace = spaceD - 3;

			if (maxPossibleCutsByPoly < 0 && numCutsBySpace < 0) {
				emit signalPossibleNumberOfCuts(0);
			}
			else {
				emit signalPossibleNumberOfCuts(maxPossibleCutsByPoly > numCutsBySpace ? numCutsBySpace : maxPossibleCutsByPoly);
			}
		}

		for (int i = 0; i < objectManager->composingPolytopes.at(0)->size(); i++) {
			double maxCoeff = objectManager->composingPolytopes.at(0)->at(i).norm();
			if (abs(maxCoeff) > maxValue) {
				maxValue = maxCoeff;
			}
		}
	} else {
		emit signalPossibleNumberOfCuts(0);
	}

	std::vector<int> cutLocationsVector;
	emit signalGenerateDimensionVieweing(cutLocationsVector, &camerasND, camera3D, true, maxValue);

	Eigen::VectorXd aux(0);
	cutLocations = aux;
	objectManager->updateObjects(cutLocations, camerasND);
}

void RenderingWindow::receiveRotationPlaneChange(int axis1, int axis2) {
	qDebug() << "New Planes of Rotation: " << axis1 << " and " << axis2;
	rotPlane1 = axis1;
	rotPlane2 = axis2;
}

void RenderingWindow::setBackgroundColor(float newRed, float newGreen, float newBlue) {
	backgroundColor[0] = newRed;
	backgroundColor[1] = newGreen;
	backgroundColor[2] = newBlue;
}

void RenderingWindow::receiveChangeBackgroundColor(float newRed, float newGreen, float newBlue) {
	setBackgroundColor(newRed, newGreen, newBlue);

	qDebug() << "New Background Color: " << newRed << ", " << newGreen << ", " << newBlue;
}


//==================================================================================================
/*!

  Copyright 2015 NumScale SAS

  Distributed under the Boost Software License, Version 1.0.
  (See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt)
*/
//==================================================================================================
#include <boost/simd/constant/sqrt_2o_2.hpp>
#include <boost/simd/constant/half.hpp>
#include <boost/simd/as.hpp>
#include <simd_test.hpp>

STF_CASE_TPL( "Check sqrt_2o_2 behavior for integral types"
            , (std::uint8_t)(std::uint16_t)(std::uint32_t)(std::uint64_t)
              (std::int8_t)(std::int16_t)(std::int32_t)(std::int64_t)
            )
{
  using boost::simd::as;
  using boost::simd::detail::sqrt_2o_2;
  using boost::simd::Sqrt_2o_2;

  STF_TYPE_IS(decltype(Sqrt_2o_2<T>()), T);
  STF_EQUAL(Sqrt_2o_2<T>(), T(0));
  STF_EQUAL(sqrt_2o_2( as(T{}) ),T(0));
}

STF_CASE_TPL( "Check sqrt_2o_2 behavior for floating types"
            , (double)(float)
            )
{
  using boost::simd::as;
  using boost::simd::detail::sqrt_2o_2;
  using boost::simd::Sqrt_2o_2;
  using boost::simd::Half;

  STF_TYPE_IS(decltype(Sqrt_2o_2<T>()), T);
  auto z1 = Sqrt_2o_2<T>();
  STF_ULP_EQUAL(z1*z1, Half<T>(), 0.5);
  auto z2 = sqrt_2o_2( as(T{}) );
  STF_ULP_EQUAL(z2*z2, Half<T>(), 0.5);

}


#pragma once

#if (__GNUC__ == 4 && __GNUC_MINOR__ > 8)
#define USE_STD_REGEX
#else
#define USE_BOOST_REGEX
#endif

// system includes ---------------------------------------------------------
#include <fstream>
#include <iostream>

#include <boost/static_assert.hpp>
#include <tuple>
#include "base/exceptions.hpp"

#ifdef USE_STD_REGEX
#include <regex>
#elif defined(USE_BOOST_REGEX)
#include <boost/regex.hpp>
#endif

// own includes ------------------------------------------------------------
#include "enum/enum.hpp"
#include "spectral_basis.hpp"
#include "spectral_basis_factory_base.hpp"
#include "spectral_elem.hpp"
#include "spectral_elem_accessor.hpp"
#include "spectral_function.hpp"

namespace boltzmann {

namespace local_ {
template <typename BASIS>
struct CMPKS
{
  /**
   * @brief lexicographical ordering for basis elements
   *
   *
   * @return
   */
  template <typename E>
  bool operator()(const E &e1, const E &e2) const
  {
    int l1 = get_xi(e1).get_id().l;
    int l2 = get_xi(e2).get_id().l;

    int t1 = get_xi(e1).get_id().t;
    int t2 = get_xi(e2).get_id().t;

    int k1 = get_phi(e1).get_id().k;
    int k2 = get_phi(e2).get_id().k;

    int j1 = get_phi(e1).get_id().j;
    int j2 = get_phi(e2).get_id().j;

    return std::tie(l1, t1, k1, j1) < std::tie(l2, t2, k2, j2);
  }

 private:
  // angle elment type
  typedef typename std::tuple_element<0, typename BASIS::elem_t::container_t>::type ea_t;
  // radial element type
  typedef typename std::tuple_element<1, typename BASIS::elem_t::container_t>::type er_t;

  typename BASIS::elem_t::Acc::template get<ea_t> get_xi;
  typename BASIS::elem_t::Acc::template get<er_t> get_phi;
};
}  // end namespace local_

// ----------------------------------------------------------------------
class SpectralBasisFactoryKS : public SpectralBasisFactoryBase<XiR, LaguerreKS>
{
 public:
  /**
 *  @brief create the Polar-Laguerre basis with polynomial degree <K, sort basis
 * by (l, t, k)
 *         l: angular index, t: sin/cos, k: polynomial degree
 *
 *  Detailed description
 *
 *  @param[out] basis
 *  @param[in]  K
 */
  static void create(basis_type &basis, int K);
  /// definition of element ordering, TODO: define this at a global place
  /**
   *
   *
   * @param[out] basis
   * @param[in] K
   * @param[in] L
   * @param[in] beta
   * @param[in] sorted sort basis functions by `l`, the angular index
   */
  static void create(basis_type &basis, int K, int L, double beta, bool sorted = false);
  /**
   *
   *
   * @param basis
   * @param K
   * @param L
   * @param beta
   * @param sorted sort basis functions by `l`, the angular index
   */
  static void create_test(basis_type &basis, int K, int L, double beta, bool sorted = false);

  static elem_t make_elem(int j, int k, enum TRIG t, double w = 0.5);
  static elem_t get_mass_elem();
  static elem_t get_ux_elem();
  static elem_t get_uy_elem();
  static elem_t get_energy_elem();

  static void write_basis_descriptor(const basis_type &basis,
                                     std::string fname = "spectral_basis.desc");

  /**
   * @brief read basis_descriptor file
   *
   * @param basis
   * @param descriptor_file
   */
  static void create(basis_type &basis, std::string descriptor_file);
};

// ----------------------------------------------------------------------
inline typename SpectralBasisFactoryKS::elem_t
SpectralBasisFactoryKS::make_elem(int j, int k, enum TRIG t, double w)
{
  // if ( j > k/2  || (t == TRIG::SIN && j == 0))
  //   throw std::runtime_error("wrong arguments");

  fa_type fa(t, 2 * j + k % 2);
  fr_type fr(k, j, w);
  return elem_t(fa, fr);
}

inline typename SpectralBasisFactoryKS::elem_t
SpectralBasisFactoryKS::get_mass_elem()
{
  return SpectralBasisFactoryKS::make_elem(0, 0, TRIG::COS, 0);
}

inline typename SpectralBasisFactoryKS::elem_t
SpectralBasisFactoryKS::get_ux_elem()
{
  return SpectralBasisFactoryKS::make_elem(0, 1, TRIG::COS, 0);
}

inline typename SpectralBasisFactoryKS::elem_t
SpectralBasisFactoryKS::get_uy_elem()
{
  return SpectralBasisFactoryKS::make_elem(0, 1, TRIG::SIN, 0);
}

inline typename SpectralBasisFactoryKS::elem_t
SpectralBasisFactoryKS::get_energy_elem()
{
  return SpectralBasisFactoryKS::make_elem(0, 2, TRIG::COS, 0);
}

// ----------------------------------------------------------------------
inline void
SpectralBasisFactoryKS::create(basis_type &basis, int K)
{
  double beta = 2;
  bool sorted = true;
  SpectralBasisFactoryKS::create(basis, K, K, beta, sorted);
}

// ----------------------------------------------------------------------
inline void
SpectralBasisFactoryKS::create(basis_type &basis, int K, int L, double beta, bool sorted)
{
  for (int k = 0; k < K; ++k) {
    // sin
    for (int j = 1 - k % 2; j <= std::min(k / 2, L); ++j) {
      fa_type fa(TRIG::SIN, 2 * j + k % 2);
      fr_type fr(k, j, 1 / beta);
      basis.add_elem(fa, fr);
    }

    // cos
    for (int j = 0; j <= std::min(k / 2, L); ++j) {
      fa_type fa(TRIG::COS, 2 * j + k % 2);
      fr_type fr(k, j, 1 / beta);
      basis.add_elem(fa, fr);
    }
  }

  /// sort basis functions by l-index
  if (sorted) {
    basis.sort(local_::CMPKS<basis_type>());
  }

  basis.finalize();
}

// ----------------------------------------------------------------------
/**
 *  @brief Create test basis for Petrov-Galerkin Ansatz
 *
 *  @param K create basis of polynomial degree < K
 *  @param
 *  @return return type
 */
inline void
SpectralBasisFactoryKS::create_test(basis_type &basis, int K, int L, double beta, bool sorted)
{
  for (int k = 0; k < K; ++k) {
    // sin
    for (int j = 1 - k % 2; j <= std::min(k / 2, L); ++j) {
      fa_type fa(TRIG::SIN, 2 * j + k % 2);

      double w = 1 / beta;
      if (j == 0 && k == 1) w = 0.0;
      fr_type fr(k, j, w);
      basis.add_elem(fa, fr);
    }

    // cos
    for (int j = 0; j <= std::min(k / 2, L); ++j) {
      fa_type fa(TRIG::COS, 2 * j + k % 2);

      double w = 1 / beta;
      if (j == 0 && k == 1) w = 0.0;
      if (j == 0 && (k == 0 || k == 2)) w = 0.0;
      fr_type fr(k, j, w);
      basis.add_elem(fa, fr);
    }
  }

  /// sort basis functions by l-index
  if (sorted) {
    basis.sort(local_::CMPKS<basis_type>());
  }

  basis.finalize();
}

// ----------------------------------------------------------------------
inline void
SpectralBasisFactoryKS::write_basis_descriptor(const basis_type &basis, std::string fname)
{
  // typename elem_t::Acc::template get<fa_type> xir_getter;
  // typename elem_t::Acc::template get<fr_type> rr_getter;

  typename elem_t::Acc::get<fa_type> xir_getter;
  typename elem_t::Acc::get<fr_type> rr_getter;

  std::ofstream fout(fname);
  for (auto it = basis.begin(); it != basis.end(); ++it) {
    fout << xir_getter(*it).get_id() << "\t" << rr_getter(*it).get_id() << std::endl;
  }
  fout.close();
}

// ----------------------------------------------------------------------
inline void
SpectralBasisFactoryKS::create(basis_type &basis, std::string descriptor_file)
{
  std::ifstream ifile;
  ifile.open(descriptor_file);
  ASSERT(ifile.is_open());

  std::string line;

#ifdef USE_STD_REGEX
  // new descriptor
  std::regex basisf_regex_new(
      "(cos|sin)_([0-9.]+)[[:space:]]*[(]fw_([0-9.]+),["
      "[:space:]]*k_([0-9]+),[[:space:]]*j_([0-9]+)"
      "[)]");
  std::smatch basisf_match;

  while (std::getline(ifile, line)) {
    // try to match new descriptor
    std::regex_search(line, basisf_match, basisf_regex_new);
    if (basisf_match.size() > 0) {
      auto sincos_match = basisf_match[1];
      // angular frequency
      auto l_match = basisf_match[2];
      int l = atoi(l_match.str().c_str());
      // exponential weight
      auto fw_match = basisf_match[3];
      double fw = atof(fw_match.str().c_str());
      // laguerre poly index
      auto k_match = basisf_match[4];
      int k = atoi(k_match.str().c_str());
      // coupled angular index
      auto j_match = basisf_match[5];
      int j = atoi(j_match.str().c_str());

      fr_type phi(k, j, fw);
      if (sincos_match.str().compare("sin") == 0) {
        fa_type xir(TRIG::SIN, l);
        basis.add_elem(xir, phi);
      } else {
        fa_type xir(TRIG::COS, l);
        basis.add_elem(xir, phi);
      }
    }
  }

#elif defined(USE_BOOST_REGEX)
  boost::regex basisf_regex_new(
      "(cos|sin)_([0-9.]+)[[:space:]]*[(]fw_([0-9.]+)"
      ",[[:space:]]*k_([0-9]+),[[:space:]]*j_([0-9]+)"
      "[)]",
      boost::regex_constants::ECMAScript);

  boost::cmatch match;
  bool is_matched;
  while (std::getline(ifile, line)) {
    // try to match new descriptor
    is_matched = boost::regex_search(line.c_str(), match, basisf_regex_new);
    if (is_matched) {
      auto sincos_match = match[1];
      // angular frequency
      auto l_match = match[2];
      int l = atoi(l_match.str().c_str());
      // exponential weight
      auto fw_match = match[3];
      double fw = atof(fw_match.str().c_str());
      // laguerre poly index
      auto k_match = match[4];
      int k = atoi(k_match.str().c_str());

      // coupled angular index
      auto j_match = match[5];
      int j = atoi(j_match.str().c_str());

      fr_type phi(k, j, fw);
      if (sincos_match.str().compare("sin") == 0) {
        fa_type xir(TRIG::SIN, l);
        basis.add_elem(xir, phi);
      } else {
        fa_type xir(TRIG::COS, l);
        basis.add_elem(xir, phi);
      }
    }
  }
#else
  // cannot parse basis descriptor
  BOOST_STATIC_ASSERT(false);
#endif
  basis.finalize();
  ifile.close();
}

}  // end namespace boltzmann


//
// HistogramAnalysis.cpp
// Pipeline element that determines the heat intensity bins
// Using the histogram of the intensities in the thermal image
//
// Note: Only supports a greyscale thermal image
//

#include "pipeline/HistogramAnalysis.hpp"
#include "config/ConfigParser.hpp"

#include <opencv2/imgproc/imgproc.hpp>
#include <iostream>
#include <string>
#include <algorithm>
#include <boost/algorithm/string.hpp>

namespace Pipeline
{
    const float MIN_REL_FREQUENCY_REQ = 0.1;

    HistogramAnalysis::HistogramAnalysis(const std::string &name, bool saveResults) : PipelineElement(name, saveResults)
    {
        // init the frequencies for the bins to 0 (read from config file)
        std::string bins = Config::ConfigParser::GetInstance().GetValue<std::string>("config.pipeline.histogram.bins");
        std::vector<std::string> binValues;

        boost::split(binValues, bins, boost::is_space());

        for (int i = 0; i < binValues.size(); i++) {
            int binNumber = std::stoi(binValues[i]);
            m_BinFrequencies[binNumber] = 0;
        }
    }

    HistogramAnalysis::~HistogramAnalysis() = default;

    // Process thresholded thermal image and find out best number of bins of heat
    void HistogramAnalysis::Process(const cv::Mat &input, cv::Mat &output,
                                    const std::shared_ptr<void> &previousElementResult, std::shared_ptr<void> &result,
                                    const std::string &filename)
    {
        cv::Mat image = input;
        cv::Mat histogram;

        int size = 256;
        float range[] { 0, 256 };
        const float* histRange = { range };

        // calculate histogram for all 256 values
        cv::calcHist(&image, 1, 0, cv::Mat(), histogram, 1, &size, &histRange);

        // count the frequencies of the bins setup in config
        for (std::map<int, float>::const_iterator iter = m_BinFrequencies.begin(); iter != m_BinFrequencies.end(); iter++)
        {
            auto nextIter = std::next(iter, 1);
            int upperBound = (nextIter != m_BinFrequencies.end() ? nextIter->first : 256);
            for (int row = iter->first; row < upperBound; row++) {
                int frequency = histogram.at<int>(row);
                m_BinFrequencies[iter->first] += frequency;
            }
        }

        // normalize by largest value to get relative frequencies
        auto maxFrequencyIter = std::max_element(m_BinFrequencies.begin(), m_BinFrequencies.end(), [](const std::pair<int, float>& a, const std::pair<int, float>& b) {
            return a.second < b.second;
        });
        float maxFrequency = maxFrequencyIter->second > 0 ? maxFrequencyIter->second : 1.0;

        for (auto& kv : m_BinFrequencies) {
            kv.second /= maxFrequency;
        }

        // determine best value of k for k-means
        auto k = std::make_shared<int>(1);

        for (const auto& kv : m_BinFrequencies)
        {
            std::cout << "\n" << kv.first << ": " << kv.second;
            if (kv.second > MIN_REL_FREQUENCY_REQ) {
                *k += 1;
            }
        }
        std::cout << std::endl;

        // set result (k) and output is input as no processing done on image itself
        result = k;
        output = input;

        std::cout << "\nK for " << filename << ": " << *k << std::endl;
    }
}


/* Author: Tobias Leicht, 2007 */

/*    $Id: step-30.cc 27657 2012-11-21 13:19:08Z bangerth $       */
/*    Version: $Name$                                          */
/*                                                                */
/*    Copyright (C) 2007-2008, 2011-2012 by the deal.II authors */
/*                                                                */
/*    This file is subject to QPL and may not be  distributed     */
/*    without copyright and license information. Please refer     */
/*    to the file deal.II/doc/license.html for the  text  and     */
/*    further information on this license.                        */

// The deal.II include files have already been covered in previous examples
// and will thus not be further commented on.
#include <deal.II/base/quadrature_lib.h>
#include <deal.II/base/function.h>
#include <deal.II/lac/vector.h>
#include <deal.II/lac/sparse_matrix.h>
#include <deal.II/grid/tria.h>
#include <deal.II/grid/grid_generator.h>
#include <deal.II/grid/grid_out.h>
#include <deal.II/grid/grid_refinement.h>
#include <deal.II/grid/tria_accessor.h>
#include <deal.II/grid/tria_iterator.h>
#include <deal.II/fe/fe_values.h>
#include <deal.II/dofs/dof_handler.h>
#include <deal.II/dofs/dof_accessor.h>
#include <deal.II/dofs/dof_tools.h>
#include <deal.II/numerics/data_out.h>
#include <deal.II/fe/mapping_q1.h>
#include <deal.II/fe/fe_dgq.h>
#include <deal.II/lac/solver_richardson.h>
#include <deal.II/lac/precondition_block.h>
#include <deal.II/numerics/derivative_approximation.h>
#include <deal.II/base/timer.h>

// And this again is C++:
#include <iostream>
#include <fstream>

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

  // @sect3{Equation data}
  //
  // The classes describing equation data and the actual assembly of
  // individual terms are almost entirely copied from step-12. We will comment
  // on differences.
  template <int dim>
  class RHS:  public Function<dim>
  {
  public:
    virtual void value_list (const std::vector<Point<dim> > &points,
                             std::vector<double> &values,
                             const unsigned int component=0) const;
  };

  template <int dim>
  class BoundaryValues:  public Function<dim>
  {
  public:
    virtual void value_list (const std::vector<Point<dim> > &points,
                             std::vector<double> &values,
                             const unsigned int component=0) const;
  };

  template <int dim>
  class Beta
  {
  public:
    Beta () {}
    void value_list (const std::vector<Point<dim> > &points,
                     std::vector<Point<dim> > &values) const;
  };

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

    for (unsigned int i=0; i<values.size(); ++i)
      values[i]=0;
  }

  // The flow field is chosen to be a quarter circle with counterclockwise
  // flow direction and with the origin as midpoint for the right half of the
  // domain with positive $x$ values, whereas the flow simply goes to the left
  // in the left part of the domain at a velocity that matches the one coming
  // in from the right. In the circular part the magnitude of the flow
  // velocity is proportional to the distance from the origin. This is a
  // difference to step-12, where the magnitude was 1 evereywhere. the new
  // definition leads to a linear variation of $\beta$ along each given face
  // of a cell. On the other hand, the solution $u(x,y)$ is exactly the same
  // as before.
  template <int dim>
  void Beta<dim>::value_list(const std::vector<Point<dim> > &points,
                             std::vector<Point<dim> > &values) const
  {
    Assert(values.size()==points.size(),
           ExcDimensionMismatch(values.size(),points.size()));

    for (unsigned int i=0; i<points.size(); ++i)
      {
        if (points[i](0) > 0)
          {
            values[i](0) = -points[i](1);
            values[i](1) = points[i](0);
          }
        else
          {
            values[i] = Point<dim>();
            values[i](0) = -points[i](1);
          }
      }
  }

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

    for (unsigned int i=0; i<values.size(); ++i)
      {
        if (points[i](0)<0.5)
          values[i]=1.;
        else
          values[i]=0.;
      }
  }

  // @sect3{Class: DGTransportEquation}
  //
  // This declaration of this class is utterly unaffected by our current
  // changes.  The only substantial change is that we use only the second
  // assembly scheme described in step-12.
  template <int dim>
  class DGTransportEquation
  {
  public:
    DGTransportEquation();

    void assemble_cell_term(const FEValues<dim> &fe_v,
                            FullMatrix<double> &ui_vi_matrix,
                            Vector<double> &cell_vector) const;

    void assemble_boundary_term(const FEFaceValues<dim> &fe_v,
                                FullMatrix<double> &ui_vi_matrix,
                                Vector<double> &cell_vector) const;

    void assemble_face_term2(const FEFaceValuesBase<dim> &fe_v,
                             const FEFaceValuesBase<dim> &fe_v_neighbor,
                             FullMatrix<double> &ui_vi_matrix,
                             FullMatrix<double> &ue_vi_matrix,
                             FullMatrix<double> &ui_ve_matrix,
                             FullMatrix<double> &ue_ve_matrix) const;
  private:
    const Beta<dim> beta_function;
    const RHS<dim> rhs_function;
    const BoundaryValues<dim> boundary_function;
  };

  // Likewise, the constructor of the class as well as the functions
  // assembling the terms corresponding to cell interiors and boundary faces
  // are unchanged from before. The function that assembles face terms between
  // cells also did not change because all it does is operate on two objects
  // of type FEFaceValuesBase (which is the base class of both FEFaceValues
  // and FESubfaceValues). Where these objects come from, i.e. how they are
  // initialized, is of no concern to this function: it simply assumes that
  // the quadrature points on faces or subfaces represented by the two objects
  // correspond to the same points in physical space.
  template <int dim>
  DGTransportEquation<dim>::DGTransportEquation ()
    :
    beta_function (),
    rhs_function (),
    boundary_function ()
  {}

  template <int dim>
  void DGTransportEquation<dim>::assemble_cell_term(
    const FEValues<dim> &fe_v,
    FullMatrix<double> &ui_vi_matrix,
    Vector<double> &cell_vector) const
  {
    const std::vector<double> &JxW = fe_v.get_JxW_values ();

    std::vector<Point<dim> > beta (fe_v.n_quadrature_points);
    std::vector<double> rhs (fe_v.n_quadrature_points);

    beta_function.value_list (fe_v.get_quadrature_points(), beta);
    rhs_function.value_list (fe_v.get_quadrature_points(), rhs);

    for (unsigned int point=0; point<fe_v.n_quadrature_points; ++point)
      for (unsigned int i=0; i<fe_v.dofs_per_cell; ++i)
        {
          for (unsigned int j=0; j<fe_v.dofs_per_cell; ++j)
            ui_vi_matrix(i,j) -= beta[point]*fe_v.shape_grad(i,point)*
                                 fe_v.shape_value(j,point) *
                                 JxW[point];

          cell_vector(i) += rhs[point] * fe_v.shape_value(i,point) * JxW[point];
        }
  }

  template <int dim>
  void DGTransportEquation<dim>::assemble_boundary_term(
    const FEFaceValues<dim> &fe_v,
    FullMatrix<double> &ui_vi_matrix,
    Vector<double> &cell_vector) const
  {
    const std::vector<double> &JxW = fe_v.get_JxW_values ();
    const std::vector<Point<dim> > &normals = fe_v.get_normal_vectors ();

    std::vector<Point<dim> > beta (fe_v.n_quadrature_points);
    std::vector<double> g(fe_v.n_quadrature_points);

    beta_function.value_list (fe_v.get_quadrature_points(), beta);
    boundary_function.value_list (fe_v.get_quadrature_points(), g);

    for (unsigned int point=0; point<fe_v.n_quadrature_points; ++point)
      {
        const double beta_n=beta[point] * normals[point];
        if (beta_n>0)
          for (unsigned int i=0; i<fe_v.dofs_per_cell; ++i)
            for (unsigned int j=0; j<fe_v.dofs_per_cell; ++j)
              ui_vi_matrix(i,j) += beta_n *
                                   fe_v.shape_value(j,point) *
                                   fe_v.shape_value(i,point) *
                                   JxW[point];
        else
          for (unsigned int i=0; i<fe_v.dofs_per_cell; ++i)
            cell_vector(i) -= beta_n *
                              g[point] *
                              fe_v.shape_value(i,point) *
                              JxW[point];
      }
  }

  template <int dim>
  void DGTransportEquation<dim>::assemble_face_term2(
    const FEFaceValuesBase<dim> &fe_v,
    const FEFaceValuesBase<dim> &fe_v_neighbor,
    FullMatrix<double> &ui_vi_matrix,
    FullMatrix<double> &ue_vi_matrix,
    FullMatrix<double> &ui_ve_matrix,
    FullMatrix<double> &ue_ve_matrix) const
  {
    const std::vector<double> &JxW = fe_v.get_JxW_values ();
    const std::vector<Point<dim> > &normals = fe_v.get_normal_vectors ();

    std::vector<Point<dim> > beta (fe_v.n_quadrature_points);

    beta_function.value_list (fe_v.get_quadrature_points(), beta);

    for (unsigned int point=0; point<fe_v.n_quadrature_points; ++point)
      {
        const double beta_n=beta[point] * normals[point];
        if (beta_n>0)
          {
            for (unsigned int i=0; i<fe_v.dofs_per_cell; ++i)
              for (unsigned int j=0; j<fe_v.dofs_per_cell; ++j)
                ui_vi_matrix(i,j) += beta_n *
                                     fe_v.shape_value(j,point) *
                                     fe_v.shape_value(i,point) *
                                     JxW[point];

            for (unsigned int k=0; k<fe_v_neighbor.dofs_per_cell; ++k)
              for (unsigned int j=0; j<fe_v.dofs_per_cell; ++j)
                ui_ve_matrix(k,j) -= beta_n *
                                     fe_v.shape_value(j,point) *
                                     fe_v_neighbor.shape_value(k,point) *
                                     JxW[point];
          }
        else
          {
            for (unsigned int i=0; i<fe_v.dofs_per_cell; ++i)
              for (unsigned int l=0; l<fe_v_neighbor.dofs_per_cell; ++l)
                ue_vi_matrix(i,l) += beta_n *
                                     fe_v_neighbor.shape_value(l,point) *
                                     fe_v.shape_value(i,point) *
                                     JxW[point];

            for (unsigned int k=0; k<fe_v_neighbor.dofs_per_cell; ++k)
              for (unsigned int l=0; l<fe_v_neighbor.dofs_per_cell; ++l)
                ue_ve_matrix(k,l) -= beta_n *
                                     fe_v_neighbor.shape_value(l,point) *
                                     fe_v_neighbor.shape_value(k,point) *
                                     JxW[point];
          }
      }
  }

  // @sect3{Class: DGMethod}
  //
  // Even the main class of this program stays more or less the same. We omit
  // one of the assembly routines and use only the second, more effective one
  // of the two presented in step-12. However, we introduce a new routine
  // (set_anisotropic_flags) and modify another one (refine_grid).
  template <int dim>
  class DGMethod
  {
  public:
    DGMethod (const bool anisotropic);
    ~DGMethod ();

    void run ();

  private:
    void setup_system ();
    void assemble_system1 ();
    void assemble_system2 ();
    void solve (Vector<double> &solution);
    void refine_grid ();
    void set_anisotropic_flags ();
    void output_results (const unsigned int cycle) const;

    Triangulation<dim>   triangulation;
    const MappingQ1<dim> mapping;
    // Again we want to use DG elements of degree 1 (but this is only
    // specified in the constructor). If you want to use a DG method of a
    // different degree replace 1 in the constructor by the new degree.
    const unsigned int   degree;
    FE_DGQ<dim>          fe;
    DoFHandler<dim>      dof_handler;

    SparsityPattern      sparsity_pattern;
    SparseMatrix<double> system_matrix;
    // This is new, the threshold value used in the evaluation of the
    // anisotropic jump indicator explained in the introduction. Its value is
    // set to 3.0 in the constructor, but it can easily be changed to a
    // different value greater than 1.
    const double anisotropic_threshold_ratio;
    // This is a bool flag indicating whether anisotropic refinement shall be
    // used or not. It is set by the constructor, which takes an argument of
    // the same name.
    const bool anisotropic;

    const QGauss<dim>   quadrature;
    const QGauss<dim-1> face_quadrature;

    Vector<double>       solution2;
    Vector<double>       right_hand_side;

    const DGTransportEquation<dim> dg;
  };

  template <int dim>
  DGMethod<dim>::DGMethod (const bool anisotropic)
    :
    mapping (),
    // Change here for DG methods of different degrees.
    degree(1),
    fe (degree),
    dof_handler (triangulation),
    anisotropic_threshold_ratio(3.),
    anisotropic(anisotropic),
    // As beta is a linear function, we can choose the degree of the
    // quadrature for which the resulting integration is correct. Thus, we
    // choose to use degree+1 gauss points, which enables us to
    // integrate exactly polynomials of degree 2*degree+1, enough
    // for all the integrals we will perform in this program.
    quadrature (degree+1),
    face_quadrature (degree+1),
    dg ()
  {}

  template <int dim>
  DGMethod<dim>::~DGMethod ()
  {
    dof_handler.clear ();
  }

  template <int dim>
  void DGMethod<dim>::setup_system ()
  {
    dof_handler.distribute_dofs (fe);
    sparsity_pattern.reinit (dof_handler.n_dofs(),
                             dof_handler.n_dofs(),
                             (GeometryInfo<dim>::faces_per_cell
                              *GeometryInfo<dim>::max_children_per_face+1)*fe.dofs_per_cell);

    DoFTools::make_flux_sparsity_pattern (dof_handler, sparsity_pattern);

    sparsity_pattern.compress();

    system_matrix.reinit (sparsity_pattern);

    solution2.reinit (dof_handler.n_dofs());
    right_hand_side.reinit (dof_handler.n_dofs());
  }

  // @sect4{Function: assemble_system2}
  //
  // We proceed with the assemble_system2 function that
  // implements the DG discretization in its second version. This function is
  // very similar to the assemble_system2 function from step-12,
  // even the four cases considered for the neighbor-relations of a cell are
  // the same, namely a) cell is at the boundary, b) there are finer
  // neighboring cells, c) the neighbor is neither coarser nor finer and d)
  // the neighbor is coarser.  However, the way in which we decide upon which
  // case we have are modified in the way described in the introduction.
  template <int dim>
  void DGMethod<dim>::assemble_system2 ()
  {
    const unsigned int dofs_per_cell = dof_handler.get_fe().dofs_per_cell;
    std::vector<unsigned int> dofs (dofs_per_cell);
    std::vector<unsigned int> dofs_neighbor (dofs_per_cell);

    const UpdateFlags update_flags = update_values
                                     | update_gradients
                                     | update_quadrature_points
                                     | update_JxW_values;

    const UpdateFlags face_update_flags = update_values
                                          | update_quadrature_points
                                          | update_JxW_values
                                          | update_normal_vectors;

    const UpdateFlags neighbor_face_update_flags = update_values;

    FEValues<dim> fe_v (
      mapping, fe, quadrature, update_flags);
    FEFaceValues<dim> fe_v_face (
      mapping, fe, face_quadrature, face_update_flags);
    FESubfaceValues<dim> fe_v_subface (
      mapping, fe, face_quadrature, face_update_flags);
    FEFaceValues<dim> fe_v_face_neighbor (
      mapping, fe, face_quadrature, neighbor_face_update_flags);

    FullMatrix<double> ui_vi_matrix (dofs_per_cell, dofs_per_cell);
    FullMatrix<double> ue_vi_matrix (dofs_per_cell, dofs_per_cell);

    FullMatrix<double> ui_ve_matrix (dofs_per_cell, dofs_per_cell);
    FullMatrix<double> ue_ve_matrix (dofs_per_cell, dofs_per_cell);

    Vector<double>  cell_vector (dofs_per_cell);

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

        fe_v.reinit (cell);

        dg.assemble_cell_term(fe_v,
                              ui_vi_matrix,
                              cell_vector);

        cell->get_dof_indices (dofs);

        for (unsigned int face_no=0; face_no<GeometryInfo<dim>::faces_per_cell; ++face_no)
          {
            typename DoFHandler<dim>::face_iterator face=
              cell->face(face_no);

            // Case a)
            if (face->at_boundary())
              {
                fe_v_face.reinit (cell, face_no);

                dg.assemble_boundary_term(fe_v_face,
                                          ui_vi_matrix,
                                          cell_vector);
              }
            else
              {
                Assert (cell->neighbor(face_no).state() == IteratorState::valid,
                        ExcInternalError());
                typename DoFHandler<dim>::cell_iterator neighbor=
                  cell->neighbor(face_no);
                // Case b), we decide that there are finer cells as neighbors
                // by asking the face, whether it has children. if so, then
                // there must also be finer cells which are children or
                // farther offsprings of our neighbor.
                if (face->has_children())
                  {
                    // We need to know, which of the neighbors faces points in
                    // the direction of our cell. Using the @p
                    // neighbor_face_no function we get this information for
                    // both coarser and non-coarser neighbors.
                    const unsigned int neighbor2=
                      cell->neighbor_face_no(face_no);

                    // Now we loop over all subfaces, i.e. the children and
                    // possibly grandchildren of the current face.
                    for (unsigned int subface_no=0;
                         subface_no<face->number_of_children(); ++subface_no)
                      {
                        // To get the cell behind the current subface we can
                        // use the @p neighbor_child_on_subface function. it
                        // takes care of all the complicated situations of
                        // anisotropic refinement and non-standard faces.
                        typename DoFHandler<dim>::cell_iterator neighbor_child
                          = cell->neighbor_child_on_subface (face_no, subface_no);
                        Assert (!neighbor_child->has_children(), ExcInternalError());

                        // The remaining part of this case is unchanged.
                        ue_vi_matrix = 0;
                        ui_ve_matrix = 0;
                        ue_ve_matrix = 0;

                        fe_v_subface.reinit (cell, face_no, subface_no);
                        fe_v_face_neighbor.reinit (neighbor_child, neighbor2);

                        dg.assemble_face_term2(fe_v_subface,
                                               fe_v_face_neighbor,
                                               ui_vi_matrix,
                                               ue_vi_matrix,
                                               ui_ve_matrix,
                                               ue_ve_matrix);

                        neighbor_child->get_dof_indices (dofs_neighbor);

                        for (unsigned int i=0; i<dofs_per_cell; ++i)
                          for (unsigned int j=0; j<dofs_per_cell; ++j)
                            {
                              system_matrix.add(dofs[i], dofs_neighbor[j],
                                                ue_vi_matrix(i,j));
                              system_matrix.add(dofs_neighbor[i], dofs[j],
                                                ui_ve_matrix(i,j));
                              system_matrix.add(dofs_neighbor[i], dofs_neighbor[j],
                                                ue_ve_matrix(i,j));
                            }
                      }
                  }
                else
                  {
                    // Case c). We simply ask, whether the neighbor is
                    // coarser. If not, then it is neither coarser nor finer,
                    // since any finer neighbor would have been treated above
                    // with case b). Of all the cases with the same refinement
                    // situation of our cell and the neighbor we want to treat
                    // only one half, so that each face is considered only
                    // once. Thus we have the additional condition, that the
                    // cell with the lower index does the work. In the rare
                    // case that both cells have the same index, the cell with
                    // lower level is selected.
                    if (!cell->neighbor_is_coarser(face_no) &&
                        (neighbor->index() > cell->index() ||
                         (neighbor->level() < cell->level() &&
                          neighbor->index() == cell->index())))
                      {
                        // Here we know, that the neigbor is not coarser so we
                        // can use the usual @p neighbor_of_neighbor
                        // function. However, we could also use the more
                        // general @p neighbor_face_no function.
                        const unsigned int neighbor2=cell->neighbor_of_neighbor(face_no);

                        ue_vi_matrix = 0;
                        ui_ve_matrix = 0;
                        ue_ve_matrix = 0;

                        fe_v_face.reinit (cell, face_no);
                        fe_v_face_neighbor.reinit (neighbor, neighbor2);

                        dg.assemble_face_term2(fe_v_face,
                                               fe_v_face_neighbor,
                                               ui_vi_matrix,
                                               ue_vi_matrix,
                                               ui_ve_matrix,
                                               ue_ve_matrix);

                        neighbor->get_dof_indices (dofs_neighbor);

                        for (unsigned int i=0; i<dofs_per_cell; ++i)
                          for (unsigned int j=0; j<dofs_per_cell; ++j)
                            {
                              system_matrix.add(dofs[i], dofs_neighbor[j],
                                                ue_vi_matrix(i,j));
                              system_matrix.add(dofs_neighbor[i], dofs[j],
                                                ui_ve_matrix(i,j));
                              system_matrix.add(dofs_neighbor[i], dofs_neighbor[j],
                                                ue_ve_matrix(i,j));
                            }
                      }

                    // We do not need to consider case d), as those faces are
                    // treated 'from the other side within case b).
                  }
              }
          }

        for (unsigned int i=0; i<dofs_per_cell; ++i)
          for (unsigned int j=0; j<dofs_per_cell; ++j)
            system_matrix.add(dofs[i], dofs[j], ui_vi_matrix(i,j));

        for (unsigned int i=0; i<dofs_per_cell; ++i)
          right_hand_side(dofs[i]) += cell_vector(i);
      }
  }

  // @sect3{Solver}
  //
  // For this simple problem we use the simple Richardson iteration again. The
  // solver is completely unaffected by our anisotropic changes.
  template <int dim>
  void DGMethod<dim>::solve (Vector<double> &solution)
  {
    SolverControl           solver_control (1000, 1e-12, false, false);
    SolverRichardson<>      solver (solver_control);

    PreconditionBlockSSOR<SparseMatrix<double> > preconditioner;

    preconditioner.initialize(system_matrix, fe.dofs_per_cell);

    solver.solve (system_matrix, solution, right_hand_side,
                  preconditioner);
  }

  // @sect3{Refinement}
  //
  // We refine the grid according to the same simple refinement criterion used
  // in step-12, namely an approximation to the gradient of the solution.
  template <int dim>
  void DGMethod<dim>::refine_grid ()
  {
    Vector<float> gradient_indicator (triangulation.n_active_cells());

    // We approximate the gradient,
    DerivativeApproximation::approximate_gradient (mapping,
                                                   dof_handler,
                                                   solution2,
                                                   gradient_indicator);

    // and scale it to obtain an error indicator.
    typename DoFHandler<dim>::active_cell_iterator
    cell = dof_handler.begin_active(),
    endc = dof_handler.end();
    for (unsigned int cell_no=0; cell!=endc; ++cell, ++cell_no)
      gradient_indicator(cell_no)*=std::pow(cell->diameter(), 1+1.0*dim/2);
    // Then we use this indicator to flag the 30 percent of the cells with
    // highest error indicator to be refined.
    GridRefinement::refine_and_coarsen_fixed_number (triangulation,
                                                     gradient_indicator,
                                                     0.3, 0.1);
    // Now the refinement flags are set for those cells with a large error
    // indicator. If nothing is done to change this, those cells will be
    // refined isotropically. If the @p anisotropic flag given to this
    // function is set, we now call the set_anisotropic_flags() function,
    // which uses the jump indicator to reset some of the refinement flags to
    // anisotropic refinement.
    if (anisotropic)
      set_anisotropic_flags();
    // Now execute the refinement considering anisotropic as well as isotropic
    // refinement flags.
    triangulation.execute_coarsening_and_refinement ();
  }

  // Once an error indicator has been evaluated and the cells with largerst
  // error are flagged for refinement we want to loop over the flagged cells
  // again to decide whether they need isotropic refinemnt or whether
  // anisotropic refinement is more appropriate. This is the anisotropic jump
  // indicator explained in the introduction.
  template <int dim>
  void DGMethod<dim>::set_anisotropic_flags ()
  {
    // We want to evaluate the jump over faces of the flagged cells, so we
    // need some objects to evaluate values of the solution on faces.
    UpdateFlags face_update_flags
      = UpdateFlags(update_values | update_JxW_values);

    FEFaceValues<dim> fe_v_face (mapping, fe, face_quadrature, face_update_flags);
    FESubfaceValues<dim> fe_v_subface (mapping, fe, face_quadrature, face_update_flags);
    FEFaceValues<dim> fe_v_face_neighbor (mapping, fe, face_quadrature, update_values);

    // Now we need to loop over all active cells.
    typename DoFHandler<dim>::active_cell_iterator cell=dof_handler.begin_active(),
                                                   endc=dof_handler.end();

    for (; cell!=endc; ++cell)
      // We only need to consider cells which are flaged for refinement.
      if (cell->refine_flag_set())
        {
          Point<dim> jump;
          Point<dim> area;

          for (unsigned int face_no=0; face_no<GeometryInfo<dim>::faces_per_cell; ++face_no)
            {
              typename DoFHandler<dim>::face_iterator face = cell->face(face_no);

              if (!face->at_boundary())
                {
                  Assert (cell->neighbor(face_no).state() == IteratorState::valid, ExcInternalError());
                  typename DoFHandler<dim>::cell_iterator neighbor = cell->neighbor(face_no);

                  std::vector<double> u (fe_v_face.n_quadrature_points);
                  std::vector<double> u_neighbor (fe_v_face.n_quadrature_points);

                  // The four cases of different neighbor relations senn in
                  // the assembly routines are repeated much in the same way
                  // here.
                  if (face->has_children())
                    {
                      // The neighbor is refined.  First we store the
                      // information, which of the neighbor's faces points in
                      // the direction of our current cell. This property is
                      // inherited to the children.
                      unsigned int neighbor2=cell->neighbor_face_no(face_no);
                      // Now we loop over all subfaces,
                      for (unsigned int subface_no=0; subface_no<face->number_of_children(); ++subface_no)
                        {
                          // get an iterator pointing to the cell behind the
                          // present subface...
                          typename DoFHandler<dim>::cell_iterator neighbor_child = cell->neighbor_child_on_subface(face_no,subface_no);
                          Assert (!neighbor_child->has_children(), ExcInternalError());
                          // ... and reinit the respective FEFaceValues und
                          // FESubFaceValues objects.
                          fe_v_subface.reinit (cell, face_no, subface_no);
                          fe_v_face_neighbor.reinit (neighbor_child, neighbor2);
                          // We obtain the function values
                          fe_v_subface.get_function_values(solution2, u);
                          fe_v_face_neighbor.get_function_values(solution2, u_neighbor);
                          // as well as the quadrature weights, multiplied by
                          // the jacobian determinant.
                          const std::vector<double> &JxW = fe_v_subface.get_JxW_values ();
                          // Now we loop over all quadrature points
                          for (unsigned int x=0; x<fe_v_subface.n_quadrature_points; ++x)
                            {
                              // and integrate the absolute value of the jump
                              // of the solution, i.e. the absolute value of
                              // the difference between the function value
                              // seen from the current cell and the
                              // neighboring cell, respectively. We know, that
                              // the first two faces are orthogonal to the
                              // first coordinate direction on the unit cell,
                              // the second two faces are orthogonal to the
                              // second coordinate direction and so on, so we
                              // accumulate these values ito vectors with
                              // dim components.
                              jump[face_no/2]+=std::fabs(u[x]-u_neighbor[x])*JxW[x];
                              // We also sum up the scaled weights to obtain
                              // the measure of the face.
                              area[face_no/2]+=JxW[x];
                            }
                        }
                    }
                  else
                    {
                      if (!cell->neighbor_is_coarser(face_no))
                        {
                          // Our current cell and the neighbor have the same
                          // refinement along the face under
                          // consideration. Apart from that, we do much the
                          // same as with one of the subcells in the above
                          // case.
                          unsigned int neighbor2=cell->neighbor_of_neighbor(face_no);

                          fe_v_face.reinit (cell, face_no);
                          fe_v_face_neighbor.reinit (neighbor, neighbor2);

                          fe_v_face.get_function_values(solution2, u);
                          fe_v_face_neighbor.get_function_values(solution2, u_neighbor);

                          const std::vector<double> &JxW = fe_v_face.get_JxW_values ();

                          for (unsigned int x=0; x<fe_v_face.n_quadrature_points; ++x)
                            {
                              jump[face_no/2]+=std::fabs(u[x]-u_neighbor[x])*JxW[x];
                              area[face_no/2]+=JxW[x];
                            }
                        }
                      else //i.e. neighbor is coarser than cell
                        {
                          // Now the neighbor is actually coarser. This case
                          // is new, in that it did not occur in the assembly
                          // routine. Here, we have to consider it, but this
                          // is not overly complicated. We simply use the @p
                          // neighbor_of_coarser_neighbor function, which
                          // again takes care of anisotropic refinement and
                          // non-standard face orientation by itself.
                          std::pair<unsigned int,unsigned int> neighbor_face_subface
                            = cell->neighbor_of_coarser_neighbor(face_no);
                          Assert (neighbor_face_subface.first<GeometryInfo<dim>::faces_per_cell, ExcInternalError());
                          Assert (neighbor_face_subface.second<neighbor->face(neighbor_face_subface.first)->number_of_children(),
                                  ExcInternalError());
                          Assert (neighbor->neighbor_child_on_subface(neighbor_face_subface.first, neighbor_face_subface.second)
                                  == cell, ExcInternalError());

                          fe_v_face.reinit (cell, face_no);
                          fe_v_subface.reinit (neighbor, neighbor_face_subface.first,
                                               neighbor_face_subface.second);

                          fe_v_face.get_function_values(solution2, u);
                          fe_v_subface.get_function_values(solution2, u_neighbor);

                          const std::vector<double> &JxW = fe_v_face.get_JxW_values ();

                          for (unsigned int x=0; x<fe_v_face.n_quadrature_points; ++x)
                            {
                              jump[face_no/2]+=std::fabs(u[x]-u_neighbor[x])*JxW[x];
                              area[face_no/2]+=JxW[x];
                            }
                        }
                    }
                }
            }
          // Now we analyze the size of the mean jumps, which we get dividing
          // the jumps by the measure of the respective faces.
          double average_jumps[dim];
          double sum_of_average_jumps=0.;
          for (unsigned int i=0; i<dim; ++i)
            {
              average_jumps[i] = jump(i)/area(i);
              sum_of_average_jumps += average_jumps[i];
            }

          // Now we loop over the dim coordinate directions of
          // the unit cell and compare the average jump over the faces
          // orthogional to that direction with the average jumnps over faces
          // orthogonal to the remining direction(s). If the first is larger
          // than the latter by a given factor, we refine only along hat
          // axis. Otherwise we leave the refinement flag unchanged, resulting
          // in isotropic refinement.
          for (unsigned int i=0; i<dim; ++i)
            if (average_jumps[i] > anisotropic_threshold_ratio*(sum_of_average_jumps-average_jumps[i]))
              cell->set_refine_flag(RefinementCase<dim>::cut_axis(i));
        }
  }

  // @sect3{The Rest}
  //
  // The remaining part of the program is again unmodified. Only the creation
  // of the original triangulation is changed in order to reproduce the new
  // domain.
  template <int dim>
  void DGMethod<dim>::output_results (const unsigned int cycle) const
  {
    std::string refine_type;
    if (anisotropic)
      refine_type=".aniso";
    else
      refine_type=".iso";

    std::string filename = "grid-";
    filename += ('0' + cycle);
    Assert (cycle < 10, ExcInternalError());

    filename += refine_type + ".eps";
    std::cout << "Writing grid to <" << filename << ">..." << std::endl;
    std::ofstream eps_output (filename.c_str());

    GridOut grid_out;
    grid_out.write_eps (triangulation, eps_output);

    filename = "grid-";
    filename += ('0' + cycle);
    Assert (cycle < 10, ExcInternalError());

    filename += refine_type + ".gnuplot";
    std::cout << "Writing grid to <" << filename << ">..." << std::endl;
    std::ofstream gnuplot_grid_output (filename.c_str());

    grid_out.write_gnuplot (triangulation, gnuplot_grid_output);

    filename = "sol-";
    filename += ('0' + cycle);
    Assert (cycle < 10, ExcInternalError());

    filename += refine_type + ".gnuplot";
    std::cout << "Writing solution to <" << filename << ">..."
              << std::endl;
    std::ofstream gnuplot_output (filename.c_str());

    DataOut<dim> data_out;
    data_out.attach_dof_handler (dof_handler);
    data_out.add_data_vector (solution2, "u");

    data_out.build_patches (degree);

    data_out.write_gnuplot(gnuplot_output);
  }

  template <int dim>
  void DGMethod<dim>::run ()
  {
    for (unsigned int cycle=0; cycle<6; ++cycle)
      {
        std::cout << "Cycle " << cycle << ':' << std::endl;

        if (cycle == 0)
          {
            // Create the rectangular domain.
            Point<dim> p1,p2;
            p1(0)=0;
            p1(0)=-1;
            for (unsigned int i=0; i<dim; ++i)
              p2(i)=1.;
            // Adjust the number of cells in different directions to obtain
            // completely isotropic cells for the original mesh.
            std::vector<unsigned int> repetitions(dim,1);
            repetitions[0]=2;
            GridGenerator::subdivided_hyper_rectangle (triangulation,
                                                       repetitions,
                                                       p1,
                                                       p2);

            triangulation.refine_global (5-dim);
          }
        else
          refine_grid ();

        std::cout << "   Number of active cells:       "
                  << triangulation.n_active_cells()
                  << std::endl;

        setup_system ();

        std::cout << "   Number of degrees of freedom: "
                  << dof_handler.n_dofs()
                  << std::endl;

        Timer assemble_timer;
        assemble_system2 ();
        std::cout << "Time of assemble_system2: "
                  << assemble_timer()
                  << std::endl;
        solve (solution2);

        output_results (cycle);
      }
  }
}

int main ()
{
  try
    {
      using namespace dealii;
      using namespace Step30;

      // If you want to run the program in 3D, simply change the following
      // line to const unsigned int dim = 3;.
      const unsigned int dim = 2;

      {
        // First, we perform a run with isotropic refinement.
        std::cout << "Performing a " << dim << "D run with isotropic refinement..." << std::endl
                  << "------------------------------------------------" << std::endl;
        DGMethod<dim> dgmethod_iso(false);
        dgmethod_iso.run ();
      }

      {
        // Now we do a second run, this time with anisotropic refinement.
        std::cout << std::endl
                  << "Performing a " << dim << "D run with anisotropic refinement..." << std::endl
                  << "--------------------------------------------------" << std::endl;
        DGMethod<dim> dgmethod_aniso(true);
        dgmethod_aniso.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;
}


//==============================================================================
//         Copyright 2003 - 2011 LASMEA UMR 6602 CNRS/Univ. Clermont II
//         Copyright 2009 - 2011 LRI    UMR 8623 CNRS/Univ Paris Sud XI
//
//          Distributed under the Boost Software License, Version 1.0.
//                 See accompanying file LICENSE.txt or copy at
//                     http://www.boost.org/LICENSE_1_0.txt
//==============================================================================
#ifndef BOOST_SIMD_IEEE_FUNCTIONS_SIMD_COMMON_FAST_LDEXP_HPP_INCLUDED
#define BOOST_SIMD_IEEE_FUNCTIONS_SIMD_COMMON_FAST_LDEXP_HPP_INCLUDED
#include <boost/simd/ieee/functions/fast_ldexp.hpp>
#include <boost/simd/sdk/meta/size.hpp>
#include <boost/dispatch/meta/adapted_traits.hpp>
#include <boost/simd/include/constants/properties.hpp>
#include <boost/dispatch/meta/as_integer.hpp>
#include <boost/simd/include/constants/real.hpp>
#include <boost/simd/include/functions/simd/bitwise_andnot.hpp>
#include <boost/simd/include/functions/simd/bitwise_or.hpp>
#include <boost/simd/include/functions/simd/shli.hpp>
#include <boost/simd/include/functions/simd/plus.hpp>

/////////////////////////////////////////////////////////////////////////////
// Implementation when type A0 is arithmetic_
/////////////////////////////////////////////////////////////////////////////
namespace boost { namespace simd { namespace ext
{
 BOOST_SIMD_FUNCTOR_IMPLEMENTATION_IF ( boost::simd::tag::fast_ldexp_, tag::cpu_,(A0)(A1)(X)
                                , (boost::mpl::equal_to < boost::mpl::sizeof_<A0>
                                                        , boost::mpl::sizeof_<A1>
                                                        >
                                  )
                                , ((simd_<arithmetic_<A0>,X>))
                                  ((simd_<integer_<A1>,X>))
                                )
  {
    typedef A0 result_type;
    BOOST_SIMD_FUNCTOR_CALL_REPEAT(2)
    {
      return rshl(a0, a1);
    }
  };

  /////////////////////////////////////////////////////////////////////////////
  // Implementation when type A0 is arithmetic_ and A1 scalar
  /////////////////////////////////////////////////////////////////////////////
  BOOST_SIMD_FUNCTOR_IMPLEMENTATION( boost::simd::tag::fast_ldexp_, tag::cpu_
                              , (A0)(A1)(X)
                              , ((simd_<arithmetic_<A0>,X>))(scalar_< integer_<A1> >)
                              )
  {

    typedef A0 result_type;
    BOOST_SIMD_FUNCTOR_CALL(2)
    {
      return rshl(a0, a1);
    }
  };

  /////////////////////////////////////////////////////////////////////////////
  // Implementation when type A0 is floating_
  /////////////////////////////////////////////////////////////////////////////
  BOOST_SIMD_FUNCTOR_IMPLEMENTATION_IF(boost::simd::tag::fast_ldexp_, tag::cpu_,
                                (A0)(A1)(X),
                                (boost::mpl::equal_to<boost::mpl::sizeof_<A0>,
                                                        boost::mpl::sizeof_<A1>
                                                      >
                                 ),
                                ((simd_<floating_<A0>,X>))
                                ((simd_<integer_<A1>,X>))
                                )
  {
    typedef A0 result_type;
    BOOST_SIMD_FUNCTOR_CALL(2)
    {
      // No denormal provision
      typedef typename meta::scalar_of<result_type>::type             s_type;
      typedef typename dispatch::meta::as_integer<result_type, signed>::type  int_type;
      // clear exponent in x
      result_type const x = b_andnot(a0, Ldexpmask<A0>());
      // extract exponent and compute the new one
      int_type e = b_and(Ldexpmask<A0>(), a0);
      e += shli(a1, Nbmantissabits<s_type>());
      return b_or(x, e);
    }
  };

  /////////////////////////////////////////////////////////////////////////////
  // Implementation when type A0 is floating_ and A1 is scalar
  /////////////////////////////////////////////////////////////////////////////
  BOOST_SIMD_FUNCTOR_IMPLEMENTATION( boost::simd::tag::fast_ldexp_, tag::cpu_
                              , (A0)(A1)(X)
                              , ((simd_<floating_<A0>,X>))(scalar_< integer_<A1> >)
                              )
  {

    typedef A0 result_type;
    BOOST_SIMD_FUNCTOR_CALL(2)
    {
      typedef typename dispatch::meta::as_integer<A0>::type iA0;
      return fast_ldexp(a0, boost::simd::splat<iA0>(a1));
    }
  };
} } }

#endif