#include <iostream>
#include <cmath>
#include <boost/graph/adjacency_list.hpp>
#include <boost/graph/push_relabel_max_flow.hpp>
typedef boost::adjacency_list_traits<boost::vecS, boost::vecS, boost::directedS> traits;
typedef boost::adjacency_list<boost::vecS, boost::vecS, boost::directedS, boost::no_property,
                              boost::property<boost::edge_capacity_t, long,
                                              boost::property<boost::edge_residual_capacity_t, long,
                                                              boost::property<boost::edge_reverse_t, traits::edge_descriptor>>>>
    graph;

typedef traits::vertex_descriptor vertex_desc;
typedef traits::edge_descriptor edge_desc;

class edge_adder
{
    graph &G;

public:
    explicit edge_adder(graph &G) : G(G) {}

    void add_edge(int from, int to, long capacity)
    {
        auto c_map = boost::get(boost::edge_capacity, G);
        auto r_map = boost::get(boost::edge_reverse, G);
        const auto e = boost::add_edge(from, to, G).first;
        const auto rev_e = boost::add_edge(to, from, G).first;
        c_map[e] = capacity;
        c_map[rev_e] = 0; // reverse edge has no capacity!
        r_map[e] = rev_e;
        r_map[rev_e] = e;
    }

    void add_edge_if_exists(int i1, int j1, int i2, int j2, int n, std::vector<bool>& present) {
        int from = i1 * n + j1;
        int to = i2 * n + j2;
        if (i1 < 0 || i2 < 0 || j1 < 0 || j2 < 0
            || i1 >= n || i2 >= n || j1 >= n || j2 >= n
            || !present[from] || !present[to]) {
            return;
        }
        add_edge(from, to, 1);
    }
};

using namespace std;

void solve() {
    int n; cin >> n;

    graph G(n * n);
    edge_adder adder(G);

    auto source = boost::add_vertex(G);
    auto target = boost::add_vertex(G);

    bool p;
    vector<bool> present(n * n, false);
    int count = 0;
    for (int i = 0; i < n; ++i) {
        for (int j = 0; j < n; ++j) {
            cin >> p;
            present[i * n + j] = p;

            if (!p) {
                continue;
            }

            ++count;

            int parity = (i + j) % 2;
            if (parity == 0) {
                adder.add_edge(source, i * n + j, 1);
            }
            else {
                adder.add_edge(i * n + j, target, 1);
            }
        }
    }

    // Build conflict graph
    for (int i = 0; i < n; ++i) {
        for (int j = 0; j < n; ++j) {
            int parity = (i + j) % 2;
            if (parity == 1) {
                continue;
            }
            adder.add_edge_if_exists(i, j, (i - 1), (j - 2), n, present);
            adder.add_edge_if_exists(i, j, (i - 1), (j + 2), n, present);
            adder.add_edge_if_exists(i, j, (i + 1), (j - 2), n, present);
            adder.add_edge_if_exists(i, j, (i + 1), (j + 2), n, present);
            adder.add_edge_if_exists(i, j, (i - 2), (j - 1), n, present);
            adder.add_edge_if_exists(i, j, (i - 2), (j + 1), n, present);
            adder.add_edge_if_exists(i, j, (i + 2), (j - 1), n, present);
            adder.add_edge_if_exists(i, j, (i + 2), (j + 1), n, present);
        }
    }

    long maxFlow = boost::push_relabel_max_flow(G, source, target);
    long maxMatching = maxFlow;
    long minVC = maxMatching;
    long maxIS = count - minVC;

    cout << maxIS << endl;
}

int main() {
    int t; cin >> t;
    for (int i = 0; i < t; ++i) {
        solve();
    }
}


// Boost.Geometry

// Copyright (c) 2018-2019 Barend Gehrels, Amsterdam, the Netherlands.

// Use, modification and distribution is subject to the Boost Software License,
// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)

#ifndef BOOST_GEOMETRY_ARITHMETIC_LINE_FUNCTIONS_HPP
#define BOOST_GEOMETRY_ARITHMETIC_LINE_FUNCTIONS_HPP

#include <boost/geometry/arithmetic/determinant.hpp>
#include <boost/geometry/core/access.hpp>
#include <boost/geometry/core/assert.hpp>
#include <boost/geometry/core/config.hpp>
#include <boost/geometry/geometries/infinite_line.hpp>
#include <boost/geometry/util/math.hpp>
#include <boost/geometry/util/select_most_precise.hpp>

namespace boost { namespace geometry
{

namespace arithmetic
{

template <typename Line, typename Line::type Line::* member1, typename Line::type Line::* member2>
inline auto determinant(Line const& p, Line const& q)
{
    return geometry::detail::determinant<typename Line::type>(p.*member1, p.*member2,
                                                              q.*member1, q.*member2);
}

template <typename Point, typename Line, typename Type>
inline Point assign_intersection_point(Line const& p, Line const& q, Type const& denominator)
{
    BOOST_ASSERT(denominator != Type(0));

    // x = | pb pc | / d  and y = | pc pa | / d
    //     | qb qc |              | qc qa |

    Point result;
    geometry::set<0>(result, determinant<Line, &Line::b, &Line::c>(p, q) / denominator);
    geometry::set<1>(result, determinant<Line, &Line::c, &Line::a>(p, q) / denominator);
    return result;
}

// Calculates intersection point of two infinite lines.
// Returns true if the lines intersect.
// Returns false if lines are parallel (or collinear, possibly opposite)
template <typename Line, typename Point>
inline bool intersection_point(Line const& p, Line const& q, Point& ip)
{
    auto const denominator = determinant<Line, &Line::a, &Line::b>(p, q);
    constexpr decltype(denominator) const zero = 0;

    if (math::equals(denominator, zero))
    {
        // Lines are parallel
        return false;
    }

    ip = assign_intersection_point<Point>(p, q, denominator);

    return true;
}

//! Return a distance-side-measure for a point to a line
//! Point is located left of the line if value is positive,
//! right of the line is value is negative, and on the line if the value
//! is exactly zero
template <typename Type, typename CoordinateType>
inline
typename select_most_precise<Type, CoordinateType>::type
side_value(model::infinite_line<Type> const& line,
    CoordinateType const& x, CoordinateType const& y)
{
    // https://en.wikipedia.org/wiki/Distance_from_a_point_to_a_line#Line_defined_by_an_equation
    // Distance from point to line in general form is given as:
    // (a * x + b * y + c) / sqrt(a * a + b * b);
    // In most use cases comparisons are enough, saving the sqrt
    // and often even the division.
    // Also, this gives positive values for points left to the line,
    // and negative values for points right to the line.
    return line.a * x + line.b * y + line.c;
}

template <typename Type, typename Point>
inline
typename select_most_precise
<
    Type,
    typename geometry::coordinate_type<Point>::type
>::type
side_value(model::infinite_line<Type> const& line, Point const& p)
{
    return side_value(line, geometry::get<0>(p), geometry::get<1>(p));
}

template <typename Type>
inline bool is_degenerate(const model::infinite_line<Type>& line)
{
    static Type const zero = 0;
    return math::equals(line.a, zero) && math::equals(line.b, zero);
}

} // namespace arithmetic

}} // namespace boost::geometry

#endif // BOOST_GEOMETRY_ARITHMETIC_LINE_FUNCTIONS_HPP


/*
    Authors: Darya Filippova, Geet Duggal, Rob Patro
    dfilippo | geet | robp @cs.cmu.edu
    See LICENSE.txt included with this distribution.
*/

#include <limits>
#include <boost/heap/binomial_heap.hpp>
#include <algorithm>
#include "ArmatusDAG.hpp"

/*

OPT(l) = max{  max_{ k<l } OPTD(k-1),  // OPT(l) ends in a non-domain
               OPTD(l) }               // OPT(l) ends in a domain

OPTD(l) = max_{ k<l } OPT(k-1) + q(k,l)

q(k,l) = {  s(k,l)   if s(k,l) > 0,
            -inf     otherwise  }

OPT(0) = OPT(1) = OPTD(0) = OPTD(1) = 0

*/

ArmatusDAG::ArmatusDAG(ArmatusParams& p) :
    OPT(SubProbMatrix(p.n+1)),
    OPTD(SubProbMatrix(p.n+1)) {
	params = &p;
    for (size_t l=0; l <= p.n; l++) {
       OPT[l].resize(p.K);
       OPTD[l].resize(p.K);
    }
}

double ArmatusDAG::s(size_t k, size_t l) {
    size_t d_i = d(k,l);
    return params->sums(k-1, l-1)/ std::pow(static_cast<double>(d_i),params->gamma);
}

/*
q(k,l) = {  s(k,l)   if s(k,l) > 0,
            -inf     otherwise }
*/
double ArmatusDAG::q(size_t k, size_t l) {
    size_t d_i = d(k,l);
    double score = (s(k, l) - params->mu[d_i]);
    if (score > 0) {
        return score;
    }
	return -std::numeric_limits<double>::infinity();
}

void ArmatusDAG::build() {
    // OPTD(0) = OPTD(1) = 0 for all K near-optimal solutions
    OPT[0][0] = OPT[1][0] = {0, 1, 0};
    OPTD[0][0] = OPTD[1][0] = {0, 1, 0};
    for (size_t i=1; i<params->K; i++) {
        OPT[0][i] = OPT[1][i] = {-std::numeric_limits<double>::infinity(), 1, 0};
        OPTD[0][i] = OPTD[1][i] = {-std::numeric_limits<double>::infinity(), 1, 0};
    }

    // Build optimal solutions for l=2 to n
    for (size_t l=2; l<=params->n; l++) {

        // Initialize best scores and backpointers
        double scoreDomain, scoreNonDomain;
        size_t backPointerDomain, backPointerNonDomain;

        scoreDomain = scoreNonDomain = -std::numeric_limits<double>::infinity();
        backPointerDomain = backPointerNonDomain = 1;

        // max_{ k<l } OPTD( k-1 )
        for (size_t k=1; k<l; k++) {
            if (OPTD[k-1][0].score > scoreNonDomain) {
                scoreNonDomain = OPTD[k-1][0].score;
                backPointerNonDomain = k;
            }
        }

        // OPTD(l) = max_{ k<l } OPT(k-1) + q(k,l)
        for (size_t k=1; k<l; k++) {
            double candidateScore = OPT[k-1][0].score + q(k,l);
            if (candidateScore > scoreDomain) {
                scoreDomain = candidateScore;
                backPointerDomain = k;
            }
        }
        OPTD[l][0] = {scoreDomain, backPointerDomain, 0};

        /*  OPT(l) = max{  max_{ k<l } OPTD(k-1),  // OPT(l) ends in a non-domain
                           OPTD(l) }               // OPT(l) ends in a domain     */
        if (scoreNonDomain > scoreDomain) {
            OPT[l][0] = {scoreNonDomain, backPointerNonDomain, 0};
        } else {
            OPT[l][0] = OPTD[l][0];
        }
    }
    cout << "OPTIMAL SCORE: " << OPT[params->n][0].score << endl;
}

void ArmatusDAG::computeTopK() {
    std::cerr << "begin computeTopK()\n";
    for (size_t l=2; l<=params->n; l++) {
        /*

        OPT(l) = max{  max_{ k<l } OPTD(k-1),  // OPT(l) ends in a non-domain
                       OPTD(l) }               // OPT(l) ends in a domain

        OPTD(l) = max_{ k<l } OPT(k-1) + q(k,l)

        */
        using heapT = boost::heap::binomial_heap<SubProblem>;
        heapT heapOPTNonDomain;
        heapT heapOPTDomain;

        for (size_t k=1; k<l; k++) {
            auto nonDomainCandidate = OPTD[k-1][0];
            nonDomainCandidate.backPointer = k;
            heapOPTNonDomain.push(nonDomainCandidate);

            auto domainCandidate = OPT[k-1][0];
            domainCandidate.score += q(k,l);
            domainCandidate.backPointer = k;
            heapOPTDomain.push(domainCandidate);
        }

        auto pushSubProblem = [&](heapT & heap, SubProblem & subProb, bool isDomain) {
            heap.pop();
            auto nextOptimalIndex = subProb.backOptimalIndex + 1;
            auto k = subProb.backPointer;

            if (nextOptimalIndex < params->K) {
                SubProblem newCandidate;
                if (isDomain) {
                    newCandidate = OPT[k-1][nextOptimalIndex];
                    newCandidate.score += q(k,l);
                } else {
                    newCandidate = OPTD[k-1][nextOptimalIndex];
                }
                newCandidate.backPointer = k;
                newCandidate.backOptimalIndex = nextOptimalIndex;
                heap.push(newCandidate);
            }
        };

        size_t i = 0;
        size_t j = 0;
        while (i < params->K) {
            OPTD[l][i] = heapOPTDomain.top();
            pushSubProblem(heapOPTDomain, OPTD[l][i], true);
            auto nonDomainCandidate = heapOPTNonDomain.top();

            if (nonDomainCandidate.score > OPTD[l][j].score) {
                OPT[l][i] = nonDomainCandidate;
                pushSubProblem(heapOPTNonDomain, nonDomainCandidate, false);
            } else {
                OPT[l][i] = OPTD[l][j];
                j++;
            }

            i++;
        }
    }

    std::cerr << "In topK()\n";
    for (size_t i = 0; i < params->K; ++i) {
        std::cerr << "The " << i << "th-best solution had score " << OPT[params->n][i].score << "\n";
    }
}

DomainSet ArmatusDAG::extractDomains(size_t i) {
    size_t k,l;
    DomainSet dset;

    l = params->n;
    do {
        k = OPT[l][i].backPointer;
        if (q(k,l) > 0) {
            dset.push_back(Domain(k-1,l-1));
        }
        l = k-1;
    } while(l > 1);

    return dset;
}

WeightedDomainEnsemble ArmatusDAG::extractTopK() {
    WeightedDomainEnsemble ensemble;

    for (size_t i = 0; i < params->K; i++) {
        ensemble.domainSets.push_back( extractDomains(i) );
        ensemble.weights.push_back( OPT[params->n][i].score/OPT[params->n][0].score );
    }

    return ensemble;
}


#ifndef MULTI_LAYER_PERCEPTRON_CLASSIFIER_HPP
#define MULTI_LAYER_PERCEPTRON_CLASSIFIER_HPP

#include <Eigen/Core>
#include <Eigen/SVD>

namespace mlt {
namespace models {
namespace classifiers {

    // Implementation of a Multi Layer Perceptron Classifier
    // Categorization:
    // - Application: Classifier
    // - Parametrization: Parametrized
    // - Method of Training: Gradient-Based
    // - Supervision: Supervised
	// Parameters:
	// - size_t[] hidden_layers_neurons: array with number of neurons per layer.
	// - double regularization: amount of L2 regularization to apply. Set to 0 or less if don't want to use.
	template <typename Params>
    class MultiLayerPerceptronClassifier {
    public:
		MultiLayerPerceptronClassifier() : _init(false) {}

		MultiLayerPerceptronClassifier(size_t input, size_t classes) : _init(true) {
			size_t previous_layer = input;

			for (unsigned int i = 0; i < params_t::hidden_layers_neurons_size(); i++) {
				assert(params_t::hidden_layers_neurons(i) > 0);
				this->_theta.push_back(Eigen::MatrixXd::Random(params_t::hidden_layers_neurons(i), previous_layer + 1) * params_t::epsilon_init());

				previous_layer = params_t::hidden_layers_neurons(i);
			}

			this->_theta.push_back(Eigen::MatrixXd::Random(output, previous_layer + 1) * params_t::epsilon_init());
		}

        // Disable copy constructors
		MultiLayerPerceptronClassifier(const MultiLayerPerceptronClassifier& other) = delete;
		MultiLayerPerceptronClassifier& operator=(const MultiLayerPerceptronClassifier& other) = delete;

        inline size_t input() const {
            assert(_init);
            return this->_theta[0].rows() - 1;
        }

        inline size_t output() const {
            assert(_init);
            return this->_theta.back().cols();
        }

        inline bool add_intercept() const {
            return true;
        }

        inline bool is_initialized() const {
            return _init;
        }

        inline void init(size_t input, size_t classes) {
			size_t previous_layer = input;

			this->_theta.clear();

			for (unsigned int i = 0; i < params_t::hidden_layers_neurons_size(); i++) {
				assert(params_t::hidden_layers_neurons(i) > 0);
				this->_theta.push_back(Eigen::MatrixXd::Random(params_t::hidden_layers_neurons(i), previous_layer + 1) * params_t::epsilon_init());

				previous_layer = params_t::hidden_layers_neurons(i);
			}

			this->_theta.push_back(Eigen::MatrixXd::Random(output, previous_layer + 1) * params_t::epsilon_init());

            _init = true;
        }

        inline void reset() {
            assert(_init);
			for (auto w : this->_theta) {
				w.setRandom() * params_t::epsilon_init();
			}
        }

        inline Eigen::VectorXd score_single(const Eigen::VectorXd& input) const {
			assert(_init);
			return this->_feed_forward(this->_theta(), input.transpose()).transpose();
		}

        inline Eigen::MatrixXd score_multi(const Eigen::MatrixXd& input) const {
			assert(_init);
			return this->_feed_forward(this->_theta(), input);
        }

        inline size_t params_size() const {
            assert(_init);
			size_t counter = 0;
			for (size_t i = 0; i < this->_theta.size(); i++) {
				counter += this->_theta[i].size();
			}

			return counter;
        }

        inline Eigen::VectorXd params() const {
            assert(_init);

			size_t counter = 0;
			for (size_t i = 0; i < this->_theta.size(); i++) {
				counter += this->_theta[i].size();
			}

			Eigen::VectorXd theta_plain(counter);
			counter = 0;
			for (size_t i = 0; i < this->_theta.size(); i++) {
				theta_plain.block(counter, 0, this->_theta[i].size(), 1) = VectorXd::Map(this->_theta[i].data(), this->_theta[i].size());
				counter += this->_theta[i].size();
			}

			return theta_plain;
		}

        inline void set_params(const Eigen::VectorXd& beta) {
            assert(_init);

			size_t counter = 0;
			for (size_t i = 0; i < this->_theta.size(); i++) {
				this->_theta[i] = Eigen::MatrixXd::Map(parameters.data() + counter, this->_theta[i].rows(), this->_theta[i].cols());
				counter += this->_theta[i].size();
			}
			assert(parameters.size() == counter);
        }

        inline double cost(const Eigen::MatrixXd& input, const Eigen::MatrixXd& result) const {
            assert(_init);
            return _cost_internal(this->_theta(), input, result);
        }

        inline double cost(const Eigen::VectorXd& thetas, const Eigen::MatrixXd& input, const Eigen::MatrixXd& result) const {
			assert(_init);
			std::vector<MatrixXd> theta(this->_theta.size());

			size_t counter = 0;
			for (size_t i = 0; i < this->_theta.size(); i++) {
				theta[i] = Eigen::MatrixXd::Map(parameters.data() + counter, this->_theta[i].rows(), this->_theta[i].cols());
				counter += this->_theta[i].size();
			}

            return _cost_internal(theta, input, result);
        }

        inline std::tuple<double, Eigen::VectorXd> cost_and_gradient(const Eigen::MatrixXd& input, const Eigen::MatrixXd& result) const {
            assert(_init);
            auto c_a_g = _cost_and_gradient_internal(this->_theta, input, result);
			auto d_theta = std::get<1>(c_a_g);

			Eigen::VectorXd d_theta_plain(counter);
			counter = 0;
			for (size_t i = 0; i < d_theta.size(); i++) {
				d_theta_plain.block(counter, 0, d_theta[i].size(), 1) = VectorXd::Map(d_theta[i].data(), d_theta[i].size());
				counter += d_theta[i].size();
			}

			return std::make_tuple(std::get<0>(c_a_g), d_theta_plain);
        }

		inline std::tuple<double, Eigen::VectorXd> cost_and_gradient(const Eigen::VectorXd& thetas, const Eigen::MatrixXd& input, const Eigen::MatrixXd& result) const {
            assert(_init);
			std::vector<MatrixXd> theta(this->_theta.size());

			size_t counter = 0;
			for (size_t i = 0; i < this->_theta.size(); i++) {
				theta[i] = Eigen::MatrixXd::Map(parameters.data() + counter, this->_theta[i].rows(), this->_theta[i].cols());
				counter += this->_theta[i].size();
			}

            auto c_a_g = _cost_and_gradient_internal(theta, input, result);
			auto d_theta = std::get<1>(c_a_g);

			Eigen::VectorXd d_theta_plain(counter);
			counter = 0;
			for (size_t i = 0; i < d_theta.size(); i++) {
				d_theta_plain.block(counter, 0, d_theta[i].size(), 1) = VectorXd::Map(d_theta[i].data(), d_theta[i].size());
				counter += d_theta[i].size();
			}

			return std::make_tuple(std::get<0>(c_a_g), d_theta_plain);
        }

    protected:
		typedef Params::MultiLayerPerceptronClassifier params_t;

		inline MatrixXd _feed_forward(const std::vector<MatrixXd>& theta, const MatrixXd& input) const {
			MatrixXd previous = input.transpose();

			for (unsigned int i = 0; i < theta.size() - 1; i++) {
				MatrixXd temp = MatrixXd::Ones(theta[i].rows() + 1, input.rows());
				temp.bottomRows(theta[i].rows()) = (theta[i] * previous).unaryExpr(std::ptr_fun(sigmoid));
				previous = temp;
			}

			return (theta.back() * previous).unaryExpr(std::ptr_fun(sigmoid));
		}

		inline double _cost_internal(const std::vector<Eigen::MatrixXd>& theta, const Eigen::MatrixXd& input, const Eigen::MatrixXd& result) const {
			Eigen::MatrixXd output = this->_feed_forward(theta, x);

			Eigen::MatrixXd ones = MatrixXd::Ones(theta.back().rows(), x.rows());
			double loss = ((-y).array() * output.array().log() - (ones - y).array() * (ones - output).array().log()).sum() / x.rows();
			double reg = 0;

			for (unsigned int i = 0; i < theta.size(); i++) {
				reg += theta[i].rightCols(theta[i].cols() - 1).array().pow(2).sum();
			}

			reg *= (params_t::regularization() / (double)(2 * x.rows()));

			loss += reg;

			return loss;
		}

		inline std::tuple<double, std::vector<Eigen::MatrixXd>> _cost_and_gradient_internal(const std::vector<Eigen::MatrixXd>& theta, const Eigen::MatrixXd& input, const Eigen::MatrixXd& result) const {
			std::vector<MatrixXd> z, a;
			a.push_back(input.transpose());

			for (unsigned int i = 0; i < theta.size() - 1; i++) {
				Eigen::MatrixXd temp = Eigen::MatrixXd::Ones(theta[i].rows() + 1, input.rows());
				z.push_back(theta[i] * a.back());
				temp.bottomRows(theta[i].rows()) = (z.back()).unaryExpr(std::ptr_fun(sigmoid));
				a.push_back(temp);
			}

			a.push_back((theta.back() * a.back()).unaryExpr(std::ptr_fun(sigmoid)));

			Eigen::MatrixXd ones = MatrixXd::Ones(theta.back().rows(), x.rows());
			double loss = ((-y).array() * a.back().array().log() - (ones - y).array() * (ones - a.back()).array().log()).sum() / x.rows();
			double reg = 0;

			for (unsigned int i = 0; i < theta.size(); i++) {
				reg += theta[i].rightCols(theta[i].cols() - 1).array().pow(2).sum();
			}

			reg *= (params_t::regularization() / (double)(2 * x.rows()));
			loss += reg;

			std::vector<Eigen::MatrixXd> d_theta;

			Eigen::MatrixXd previous_delta = a.back() - y;
			d_theta.push_back(previous_delta * a[a.size() - 2].transpose() / (double)y.cols());
			d_theta.back().rightCols(d_theta.back().cols() - 1) += (params_t::regularization() / (double)(x.rows())) * theta.back().rightCols(theta.back().cols() - 1);

			for (size_t i = theta.size() - 1; i > 0; i--) {
				Eigen::MatrixXd temp = (theta[i].rightCols(theta[i].cols() - 1).transpose() * previous_delta).array() *
					z[i - 1].unaryExpr(std::ptr_fun(sigmoidGradient)).array();
				d_theta.push_back(temp * a[i - 1].transpose() / (double)y.cols());
				d_theta.back().rightCols(d_theta.back().cols() - 1) += (params_t::regularization() / (double)(x.rows())) * theta[i].rightCols(theta[i].cols() - 1);
				previous_delta = temp;
			}

			std::reverse(d_theta.begin(), d_theta.end());

			return std::make_tuple(loss, d_theta);
        }

        bool _init;
		std::vector<Eigen::MatrixXd> _theta;
    };
}
}
}
#endif


// This file is part of KWIVER, and is distributed under the
// OSI-approved BSD 3-Clause License. See top-level LICENSE file or
// https://github.com/Kitware/kwiver/blob/master/LICENSE for details.

/**
 * \file
 * \brief core essential matrix template implementations
 */

#include "essential_matrix.h"

#include <cmath>

#include <vital/exceptions/math.h>

#include <Eigen/SVD>

namespace kwiver {
namespace vital {

/// Compute the twisted pair rotation from the rotation and translation
rotation_d
essential_matrix
::twisted_rotation() const
{
  // The quaternion representation of a 180 degree rotation about
  // unit vector [X,Y,Z] is simply [X, Y, Z, 0]
  vector_3d t = this->translation();
  return rotation_d(vector_4d(t.x(), t.y(), t.z(), 0.0)) * this->rotation();
}

/// Construct from a provided matrix
template <typename T>
essential_matrix_<T>
::essential_matrix_( Eigen::Matrix<T,3,3> const &mat )
{
  const matrix_t W = (matrix_t() << T(0), T(-1), T(0),
                                    T(1), T( 0), T(0),
                                    T(0), T( 0), T(1)).finished();
  Eigen::JacobiSVD<matrix_t> svd(mat, Eigen::ComputeFullU |
                                      Eigen::ComputeFullV);
  const matrix_t& U = svd.matrixU();
  const matrix_t& V = svd.matrixV();
  trans_ = U.col(2);
  matrix_t R = U*W*V.transpose();
  if( R.determinant() < T(0) )
  {
    R *= T(-1);
  }
  rot_ = rotation_<T>(R);
}

/// Construct from a rotation and translation
template <typename T>
essential_matrix_<T>
::essential_matrix_( rotation_<T> const &rot,
                     vector_t const &trans )
  : rot_( rot ),
    trans_( trans.normalized() )
{
}

/// Conversion Copy constructor -- float specialization
template <>
template <>
essential_matrix_<float>
::essential_matrix_( essential_matrix_<float> const &other )
  : rot_( other.rot_ ),
    trans_( other.trans_ )
{
}

/// Conversion Copy constructor -- double specialization
template <>
template <>
essential_matrix_<double>
::essential_matrix_( essential_matrix_<double> const &other )
  : rot_( other.rot_ ),
    trans_( other.trans_ )
{
}

/// Construct from a generic essential_matrix
template <typename T>
essential_matrix_<T>
::essential_matrix_( essential_matrix const &base )
  : rot_( static_cast<rotation_<T> >(base.rotation()) ),
    trans_( base.translation().template cast<T>() )
{
}

/// Construct from a generic essential_matrix -- double specialization
template <>
essential_matrix_<double>
::essential_matrix_( essential_matrix const &base )
  : rot_( base.rotation() ),
    trans_( base.translation() )
{
}

/// Create a clone of outself as a shared pointer
template <typename T>
essential_matrix_sptr
essential_matrix_<T>
::clone() const
{
  return essential_matrix_sptr( new essential_matrix_<T>( *this ) );
}

/// Get a double-typed copy of the underlying matrix
template <typename T>
Eigen::Matrix<double,3,3>
essential_matrix_<T>
::matrix() const
{
  return this->compute_matrix().template cast<double>();
}

/// Specialization for matrices with native double type
template <>
Eigen::Matrix<double,3,3>
essential_matrix_<double>
::matrix() const
{
  return this->compute_matrix();
}

/// Return the one of two possible 3D rotations that can parameterize E
template <typename T>
rotation_d
essential_matrix_<T>
::rotation() const
{
  return static_cast<rotation_d>(this->rot_);
}

/// Return the second possible rotation that can parameterize E
template <typename T>
rotation_d
essential_matrix_<T>
::twisted_rotation() const
{
  return static_cast<rotation_d>(this->compute_twisted_rotation());
}

/// Return a unit translation vector (up to a sign) that parameterizes E
template <typename T>
vector_3d
essential_matrix_<T>
::translation() const
{
  return this->trans_.template cast<double>();
}

/// Get the underlying matrix
template <typename T>
typename essential_matrix_<T>::matrix_t
essential_matrix_<T>
::compute_matrix() const
{
  matrix_t t_cross;
  t_cross << T(0), -trans_[2], trans_[1],
             trans_[2], T(0), -trans_[0],
            -trans_[1], trans_[0], T(0);
  return t_cross * matrix_t(rot_.matrix());
}

/// Compute the twisted pair rotation from the rotation and translation
template <typename T>
rotation_<T>
essential_matrix_<T>
::compute_twisted_rotation() const
{
  typedef Eigen::Matrix<T,4,1> vector_4;
  // The quaternion representation of a 180 degree rotation about
  // unit vector [X,Y,Z] is simply [X, Y, Z, 0]
  const vector_t& t = trans_;
  return rotation_<T>(vector_4(t.x(), t.y(), t.z(), T(0))) * rot_;
}

/// Get a const reference to the underlying rotation
template <typename T>
rotation_<T> const&
essential_matrix_<T>
::get_rotation() const
{
  return rot_;
}

/// Get a const reference to the underlying translation
template <typename T>
typename essential_matrix_<T>::vector_t const&
essential_matrix_<T>
::get_translation() const
{
  return trans_;
}

// ===========================================================================
// Other Functions
// ---------------------------------------------------------------------------

/// essential_matrix_<T> output stream operator
template <typename T>
std::ostream&
operator<<( std::ostream &s, essential_matrix_<T> const &e )
{
  s << e.compute_matrix();
  return s;
}

/// Output stream operator for \p essential_matrix instances
std::ostream&
operator<<( std::ostream &s, essential_matrix const &e )
{
  s << e.matrix();
  return s;
}

// ===========================================================================
// Template class instantiation
// ---------------------------------------------------------------------------
/// \cond DoxygenSuppress
#define INSTANTIATE_ESSENTIAL_MATRIX(T)                                 \
  template class essential_matrix_<T>;                                  \
  template VITAL_EXPORT std::ostream& operator<<( std::ostream &,       \
                        essential_matrix_<T> const & )

INSTANTIATE_ESSENTIAL_MATRIX(float);
INSTANTIATE_ESSENTIAL_MATRIX(double);
#undef INSTANTIATE_ESSENTIAL_MATRIX
/// \endcond

} } // end vital namespace


/* ---------------------------------------------------------------------
 *
 * Copyright (C) 2018 - 2021 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.md at
 * the top level directory of deal.II.
 *
 * ---------------------------------------------------------------------
 *      Author: Zhuoran Wang, Colorado State University, 2018
 */

// @sect3{Include files}  这个程序是基于 step-7  、 step-20  和  step-51  ，所以下面的头文件大部分是熟悉的。我们需要以下文件，其中只有导入FE_DGRaviartThomas类的文件（即`deal.II/fe/fe_dg_vector.h`）是真正的新文件；FE_DGRaviartThomas实现了介绍中讨论的 "破碎 "Raviart-Thomas空间。

#include <deal.II/base/quadrature.h>
#include <deal.II/base/quadrature_lib.h>
#include <deal.II/base/tensor_function.h>
#include <deal.II/base/logstream.h>
#include <deal.II/base/function.h>
#include <deal.II/base/point.h>
#include <deal.II/lac/block_vector.h>
#include <deal.II/lac/vector.h>
#include <deal.II/lac/full_matrix.h>
#include <deal.II/lac/block_sparse_matrix.h>
#include <deal.II/lac/sparse_matrix.h>
#include <deal.II/lac/solver_cg.h>
#include <deal.II/lac/precondition.h>
#include <deal.II/lac/affine_constraints.h>
#include <deal.II/grid/tria.h>
#include <deal.II/grid/grid_generator.h>
#include <deal.II/dofs/dof_handler.h>
#include <deal.II/dofs/dof_tools.h>
#include <deal.II/fe/fe_dgq.h>
#include <deal.II/fe/fe_raviart_thomas.h>
#include <deal.II/fe/fe_dg_vector.h>
#include <deal.II/fe/fe_system.h>
#include <deal.II/fe/fe_values.h>
#include <deal.II/fe/fe_face.h>
#include <deal.II/fe/component_mask.h>
#include <deal.II/numerics/vector_tools.h>
#include <deal.II/numerics/data_out.h>
#include <deal.II/numerics/data_out_faces.h>

#include <fstream>
#include <iostream>

// 我们的第一步，像往常一样，是把所有与本教程程序有关的东西放到自己的命名空间中。

namespace Step61
{
  using namespace dealii;
// @sect3{The WGDarcyEquation class template}

// 这是本程序的主类。我们将使用弱加勒金（WG）方法求解内部和面上的数值压力，并计算出压力的 $L_2$ 误差。在后处理步骤中，我们还将计算速度和通量的 $L_2$  误差。

// 该类的结构与以前的教程程序没有根本的不同，所以除了一个例外，没有必要对细节进行评论。该类有一个成员变量`fe_dgrt`，对应于介绍中提到的 "破碎 "的Raviart-Thomas空间。还有一个与之匹配的`dof_handler_dgrt`，表示从这个元素创建的有限元场的全局枚举，还有一个向量`darcy_velocity`，用于保持这个场的节点值。在求解压力后，我们将使用这三个变量来计算一个后处理的速度场，然后我们可以对其进行误差评估，并将其输出用于可视化。

  template <int dim>
  class WGDarcyEquation
  {
  public:
    WGDarcyEquation(const unsigned int degree);
    void run();

  private:
    void make_grid();
    void setup_system();
    void assemble_system();
    void solve();
    void compute_postprocessed_velocity();
    void compute_velocity_errors();
    void compute_pressure_error();
    void output_results() const;

    Triangulation<dim> triangulation;

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

    AffineConstraints<double> constraints;

    SparsityPattern      sparsity_pattern;
    SparseMatrix<double> system_matrix;

    Vector<double> solution;
    Vector<double> system_rhs;

    FE_DGRaviartThomas<dim> fe_dgrt;
    DoFHandler<dim>         dof_handler_dgrt;
    Vector<double>          darcy_velocity;
  };

//  @sect3{Right hand side, boundary values, and exact solution}

// 接下来，我们定义系数矩阵 $\mathbf{K}$ （这里是身份矩阵），迪里希特边界条件，右手边 $f = 2\pi^2 \sin(\pi x) \sin(\pi y)$  ，以及与这些选择相对应的 $K$ 和 $f$ 的精确解，即 $p = \sin(\pi x) \sin(\pi y)$  。

  template <int dim>
  class Coefficient : public TensorFunction<2, dim>
  {
  public:
    Coefficient()
      : TensorFunction<2, dim>()
    {}

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

  template <int dim>
  void Coefficient<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] = unit_symmetric_tensor<dim>();
  }

  template <int dim>
  class BoundaryValues : public Function<dim>
  {
  public:
    BoundaryValues()
      : Function<dim>(2)
    {}

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

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

  template <int dim>
  class RightHandSide : public Function<dim>
  {
  public:
    virtual double value(const Point<dim> & p,
                         const unsigned int component = 0) const override;
  };

  template <int dim>
  double RightHandSide<dim>::value(const Point<dim> &p,
                                   const unsigned int /*component*/) const
  {
    return (2 * numbers::PI * numbers::PI * std::sin(numbers::PI * p[0]) *
            std::sin(numbers::PI * p[1]));
  }

// 实现精确压力解决方案的类有一个奇怪的地方，我们把它作为一个有两个分量的向量值来实现。(我们在构造函数中说它有两个分量，在这里我们调用基函数类的构造函数)。在`value()`函数中，我们不测试`component`参数的值，这意味着我们为向量值函数的两个分量返回相同的值。我们这样做是因为我们将本程序中使用的有限元描述为一个包含内部和界面压力的矢量值系统，当我们计算误差时，我们希望使用相同的压力解来测试这两个分量。

  template <int dim>
  class ExactPressure : public Function<dim>
  {
  public:
    ExactPressure()
      : Function<dim>(2)
    {}

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

  template <int dim>
  double ExactPressure<dim>::value(const Point<dim> &p,
                                   const unsigned int /*component*/) const
  {
    return std::sin(numbers::PI * p[0]) * std::sin(numbers::PI * p[1]);
  }

  template <int dim>
  class ExactVelocity : public TensorFunction<1, dim>
  {
  public:
    ExactVelocity()
      : TensorFunction<1, dim>()
    {}

    virtual Tensor<1, dim> value(const Point<dim> &p) const override;
  };

  template <int dim>
  Tensor<1, dim> ExactVelocity<dim>::value(const Point<dim> &p) const
  {
    Tensor<1, dim> return_value;
    return_value[0] = -numbers::PI * std::cos(numbers::PI * p[0]) *
                      std::sin(numbers::PI * p[1]);
    return_value[1] = -numbers::PI * std::sin(numbers::PI * p[0]) *
                      std::cos(numbers::PI * p[1]);
    return return_value;
  }

//  @sect3{WGDarcyEquation class implementation}
// @sect4{WGDarcyEquation::WGDarcyEquation}

// 在这个构造函数中，我们创建了一个矢量值函数的有限元空间，这里将包括用于内部和界面压力的函数， $p^\circ$  和  $p^\partial$  。

  template <int dim>
  WGDarcyEquation<dim>::WGDarcyEquation(const unsigned int degree)
    : fe(FE_DGQ<dim>(degree), 1, FE_FaceQ<dim>(degree), 1)
    , dof_handler(triangulation)
    , fe_dgrt(degree)
    , dof_handler_dgrt(triangulation)
  {}

//  @sect4{WGDarcyEquation::make_grid}

// 我们在单位平方域上生成一个网格并对其进行细化。

  template <int dim>
  void WGDarcyEquation<dim>::make_grid()
  {
    GridGenerator::hyper_cube(triangulation, 0, 1);
    triangulation.refine_global(5);

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

//  @sect4{WGDarcyEquation::setup_system}

// 在我们创建了上面的网格后，我们分配自由度并调整矩阵和向量的大小。这个函数中唯一值得关注的部分是我们如何插值压力的边界值。由于压力由内部和界面分量组成，我们需要确保我们只插值到矢量值解空间中与界面压力相对应的分量上（因为这些分量是唯一定义在域的边界上的）。我们通过一个只针对界面压力的分量屏蔽对象来做到这一点。

  template <int dim>
  void WGDarcyEquation<dim>::setup_system()
  {
    dof_handler.distribute_dofs(fe);
    dof_handler_dgrt.distribute_dofs(fe_dgrt);

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

    solution.reinit(dof_handler.n_dofs());
    system_rhs.reinit(dof_handler.n_dofs());

    {
      constraints.clear();
      const FEValuesExtractors::Scalar interface_pressure(1);
      const ComponentMask              interface_pressure_mask =
        fe.component_mask(interface_pressure);
      VectorTools::interpolate_boundary_values(dof_handler,
                                               0,
                                               BoundaryValues<dim>(),
                                               constraints,
                                               interface_pressure_mask);
      constraints.close();
    }

// 在双线性形式中，在两个相邻单元之间的面上没有积分项，所以我们可以直接使用 DoFTools::make_sparsity_pattern 来计算稀疏矩阵。

    DynamicSparsityPattern dsp(dof_handler.n_dofs());
    DoFTools::make_sparsity_pattern(dof_handler, dsp, constraints);
    sparsity_pattern.copy_from(dsp);

    system_matrix.reinit(sparsity_pattern);
  }

//  @sect4{WGDarcyEquation::assemble_system}

// 这个函数比较有趣。正如介绍中所详述的，线性系统的装配要求我们评估形状函数的弱梯度，这是Raviart-Thomas空间的一个元素。因此，我们需要定义一个Raviart-Thomas有限元对象，并有FEValues对象在正交点评估它。然后我们需要计算每个单元 $K$ 上的矩阵 $C^K$ ，为此我们需要介绍中提到的矩阵 $M^K$ 和 $G^K$ 。

// 有一点可能不是很明显，在之前所有的教程程序中，我们总是用DoFHandler的单元格迭代器来调用 FEValues::reinit() 。这样就可以调用诸如 FEValuesBase::get_function_values() 这样的函数，在单元格的正交点上提取有限元函数的值（用DoF值的矢量表示）。为了使这种操作发挥作用，人们需要知道哪些向量元素对应于给定单元上的自由度--也就是说，正是DoFHandler类所提供的那种信息和操作。

// 我们可以为 "破碎的 "Raviart-Thomas空间创建一个DoFHandler对象（使用FE_DGRT类），但是我们在这里真的不想这样做。至少在当前函数中，我们不需要任何与这个破碎空间相关的全局定义的自由度，而只需要引用当前单元上的这种空间的形状函数。因此，我们利用这样一个事实，即人们也可以用单元格迭代器来调用 FEValues::reinit() 的Triangulation对象（而不是DoFHandler对象）。在这种情况下，FEValues当然只能为我们提供只引用单元格的信息，而不是这些单元格上列举的自由度。所以我们不能使用 FEValuesBase::get_function_values(), ，但我们可以使用 FEValues::shape_value() 来获取当前单元上正交点的形状函数值。下面我们要利用的就是这种功能。下面给我们提供Raviart-Thomas函数信息的变量是`fe_values_rt`（和相应的`fe_face_values_rt`）对象。

// 鉴于上述介绍，下面的声明应该是非常明显的。

  template <int dim>
  void WGDarcyEquation<dim>::assemble_system()
  {
    const QGauss<dim>     quadrature_formula(fe_dgrt.degree + 1);
    const QGauss<dim - 1> face_quadrature_formula(fe_dgrt.degree + 1);

    FEValues<dim>     fe_values(fe,
                            quadrature_formula,
                            update_values | 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);

    FEValues<dim>     fe_values_dgrt(fe_dgrt,
                                 quadrature_formula,
                                 update_values | update_gradients |
                                   update_quadrature_points |
                                   update_JxW_values);
    FEFaceValues<dim> fe_face_values_dgrt(fe_dgrt,
                                          face_quadrature_formula,
                                          update_values |
                                            update_normal_vectors |
                                            update_quadrature_points |
                                            update_JxW_values);

    const unsigned int dofs_per_cell      = fe.n_dofs_per_cell();
    const unsigned int dofs_per_cell_dgrt = fe_dgrt.n_dofs_per_cell();

    const unsigned int n_q_points      = fe_values.get_quadrature().size();
    const unsigned int n_q_points_dgrt = fe_values_dgrt.get_quadrature().size();

    const unsigned int n_face_q_points = fe_face_values.get_quadrature().size();

    RightHandSide<dim>  right_hand_side;
    std::vector<double> right_hand_side_values(n_q_points);

    const Coefficient<dim>      coefficient;
    std::vector<Tensor<2, dim>> coefficient_values(n_q_points);

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

// 接下来，让我们声明介绍中讨论的各种单元格矩阵。

    FullMatrix<double> cell_matrix_M(dofs_per_cell_dgrt, dofs_per_cell_dgrt);
    FullMatrix<double> cell_matrix_G(dofs_per_cell_dgrt, dofs_per_cell);
    FullMatrix<double> cell_matrix_C(dofs_per_cell, dofs_per_cell_dgrt);
    FullMatrix<double> local_matrix(dofs_per_cell, dofs_per_cell);
    Vector<double>     cell_rhs(dofs_per_cell);
    Vector<double>     cell_solution(dofs_per_cell);

// 我们需要  FEValuesExtractors  来访问形状函数的  @p interior  和  @p face  部分。

    const FEValuesExtractors::Vector velocities(0);
    const FEValuesExtractors::Scalar pressure_interior(0);
    const FEValuesExtractors::Scalar pressure_face(1);

// 这最终让我们在所有单元格上进行循环。在每个单元中，我们将首先计算用于构建局部矩阵的各种单元矩阵--因为它们取决于相关的单元，所以它们需要在每个单元中重新计算。我们还需要Raviart-Thomas空间的形状函数，为此我们需要首先创建一个通往三角化单元的迭代器，我们可以通过从指向DoFHandler的单元中的赋值来获得。

    for (const auto &cell : dof_handler.active_cell_iterators())
      {
        fe_values.reinit(cell);

        const typename Triangulation<dim>::active_cell_iterator cell_dgrt =
          cell;
        fe_values_dgrt.reinit(cell_dgrt);

        right_hand_side.value_list(fe_values.get_quadrature_points(),
                                   right_hand_side_values);
        coefficient.value_list(fe_values.get_quadrature_points(),
                               coefficient_values);

// 我们要计算的第一个单元矩阵是拉维-托马斯空间的质量矩阵。 因此，我们需要循环计算速度FEValues对象的所有正交点。

        cell_matrix_M = 0;
        for (unsigned int q = 0; q < n_q_points_dgrt; ++q)
          for (unsigned int i = 0; i < dofs_per_cell_dgrt; ++i)
            {
              const Tensor<1, dim> v_i = fe_values_dgrt[velocities].value(i, q);
              for (unsigned int k = 0; k < dofs_per_cell_dgrt; ++k)
                {
                  const Tensor<1, dim> v_k =
                    fe_values_dgrt[velocities].value(k, q);
                  cell_matrix_M(i, k) += (v_i * v_k * fe_values_dgrt.JxW(q));
                }
            }

// 接下来我们通过使用 FullMatrix::gauss_jordan(). 对这个矩阵进行求逆 它将被用来计算后面的系数矩阵 $C^K$ 。值得一提的是，后面的 "cell_matrix_M "实际上包含了*的逆*。
//在这个调用之后的 $M^K$ 的*逆。

        cell_matrix_M.gauss_jordan();

// 从介绍中，我们知道定义 $C^K$ 的方程的右边 $G^K$ 是面积分和单元积分的区别。在这里，我们对内部的贡献的负值进行了近似。这个矩阵的每个分量都是多项式空间的一个基函数与拉维-托马斯空间的一个基函数的发散之间的乘积的积分。这些基函数是在内部定义的。

        cell_matrix_G = 0;
        for (unsigned int q = 0; q < n_q_points; ++q)
          for (unsigned int i = 0; i < dofs_per_cell_dgrt; ++i)
            {
              const double div_v_i =
                fe_values_dgrt[velocities].divergence(i, q);
              for (unsigned int j = 0; j < dofs_per_cell; ++j)
                {
                  const double phi_j_interior =
                    fe_values[pressure_interior].value(j, q);

                  cell_matrix_G(i, j) -=
                    (div_v_i * phi_j_interior * fe_values.JxW(q));
                }
            }

// 接下来，我们用正交法对面的积分进行近似。每个分量都是多项式空间的基函数与Raviart-Thomas空间的基函数与法向量的点积的积分。所以我们在元素的所有面上循环，得到法向量。

        for (const auto &face : cell->face_iterators())
          {
            fe_face_values.reinit(cell, face);
            fe_face_values_dgrt.reinit(cell_dgrt, face);

            for (unsigned int q = 0; q < n_face_q_points; ++q)
              {
                const Tensor<1, dim> &normal = fe_face_values.normal_vector(q);

                for (unsigned int i = 0; i < dofs_per_cell_dgrt; ++i)
                  {
                    const Tensor<1, dim> v_i =
                      fe_face_values_dgrt[velocities].value(i, q);
                    for (unsigned int j = 0; j < dofs_per_cell; ++j)
                      {
                        const double phi_j_face =
                          fe_face_values[pressure_face].value(j, q);

                        cell_matrix_G(i, j) +=
                          ((v_i * normal) * phi_j_face * fe_face_values.JxW(q));
                      }
                  }
              }
          }
// @p cell_matrix_C 是 $G^K$ 的转置与质量矩阵的逆之间的矩阵乘积（该逆存储在 @p cell_matrix_M): 中）。
        cell_matrix_G.Tmmult(cell_matrix_C, cell_matrix_M);

// 最后我们可以计算出本地矩阵  $A^K$  。 元素  $A^K_{ij}$  由  $\int_{E} \sum_{k,l} C_{ik} C_{jl} (\mathbf{K} \mathbf{v}_k) \cdot \mathbf{v}_l \mathrm{d}x$  得到。我们在上一步已经计算了系数 $C$ ，因此在适当地重新排列循环后得到以下结果。

        local_matrix = 0;
        for (unsigned int q = 0; q < n_q_points_dgrt; ++q)
          {
            for (unsigned int k = 0; k < dofs_per_cell_dgrt; ++k)
              {
                const Tensor<1, dim> v_k =
                  fe_values_dgrt[velocities].value(k, q);
                for (unsigned int l = 0; l < dofs_per_cell_dgrt; ++l)
                  {
                    const Tensor<1, dim> v_l =
                      fe_values_dgrt[velocities].value(l, q);

                    for (unsigned int i = 0; i < dofs_per_cell; ++i)
                      for (unsigned int j = 0; j < dofs_per_cell; ++j)
                        local_matrix(i, j) +=
                          (coefficient_values[q] * cell_matrix_C[i][k] * v_k) *
                          cell_matrix_C[j][l] * v_l * fe_values_dgrt.JxW(q);
                  }
              }
          }

// 接下来，我们计算右手边， $\int_{K} f q \mathrm{d}x$  。

        cell_rhs = 0;
        for (unsigned int q = 0; q < n_q_points; ++q)
          for (unsigned int i = 0; i < dofs_per_cell; ++i)
            {
              cell_rhs(i) += (fe_values[pressure_interior].value(i, q) *
                              right_hand_side_values[q] * fe_values.JxW(q));
            }

// 最后一步是将本地矩阵的组件分配到系统矩阵中，并将单元格右侧的组件转移到系统右侧。

        cell->get_dof_indices(local_dof_indices);
        constraints.distribute_local_to_global(
          local_matrix, cell_rhs, local_dof_indices, system_matrix, system_rhs);
      }
  }

//  @sect4{WGDarcyEquation<dim>::solve}

// 这一步相当琐碎，与之前的许多教程程序相同。

  template <int dim>
  void WGDarcyEquation<dim>::solve()
  {
    SolverControl            solver_control(1000, 1e-8 * system_rhs.l2_norm());
    SolverCG<Vector<double>> solver(solver_control);
    solver.solve(system_matrix, solution, system_rhs, PreconditionIdentity());
    constraints.distribute(solution);
  }
// @sect4{WGDarcyEquation<dim>::compute_postprocessed_velocity}

// 在这个函数中，根据之前计算的压力解计算出速度场。速度被定义为 $\mathbf{u}_h = \mathbf{Q}_h \left(-\mathbf{K}\nabla_{w,d}p_h \right)$ ，这需要我们计算许多与系统矩阵组装相同的项。还有一些矩阵 $E^K,D^K$ 我们也需要组装（见介绍），但它们实际上只是遵循相同的模式。

// 在这里计算与我们在`assemble_system()`函数中已经完成的相同的矩阵，当然是浪费CPU时间的。同样地，我们把那里的一些代码复制到这个函数中，这通常也是一个糟糕的主意。一个更好的实现可能会提供一个函数来封装这些重复的代码。我们也可以考虑使用计算效率和内存效率之间的经典权衡，在装配过程中每个单元只计算一次 $C^K$ 矩阵，把它们存储在边上的某个地方，然后在这里重新使用它们。例如， step-51 就是这样做的，`assemble_system()`函数需要一个参数来决定是否重新计算本地矩阵，类似的方法--也许是将本地矩阵存储在其他地方--可以适用于当前的程序）。

  template <int dim>
  void WGDarcyEquation<dim>::compute_postprocessed_velocity()
  {
    darcy_velocity.reinit(dof_handler_dgrt.n_dofs());

    const QGauss<dim>     quadrature_formula(fe_dgrt.degree + 1);
    const QGauss<dim - 1> face_quadrature_formula(fe_dgrt.degree + 1);

    FEValues<dim> fe_values(fe,
                            quadrature_formula,
                            update_values | 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);

    FEValues<dim> fe_values_dgrt(fe_dgrt,
                                 quadrature_formula,
                                 update_values | update_gradients |
                                   update_quadrature_points |
                                   update_JxW_values);

    FEFaceValues<dim> fe_face_values_dgrt(fe_dgrt,
                                          face_quadrature_formula,
                                          update_values |
                                            update_normal_vectors |
                                            update_quadrature_points |
                                            update_JxW_values);

    const unsigned int dofs_per_cell      = fe.n_dofs_per_cell();
    const unsigned int dofs_per_cell_dgrt = fe_dgrt.n_dofs_per_cell();

    const unsigned int n_q_points      = fe_values.get_quadrature().size();
    const unsigned int n_q_points_dgrt = fe_values_dgrt.get_quadrature().size();

    const unsigned int n_face_q_points = fe_face_values.get_quadrature().size();

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

    FullMatrix<double> cell_matrix_M(dofs_per_cell_dgrt, dofs_per_cell_dgrt);
    FullMatrix<double> cell_matrix_G(dofs_per_cell_dgrt, dofs_per_cell);
    FullMatrix<double> cell_matrix_C(dofs_per_cell, dofs_per_cell_dgrt);
    FullMatrix<double> cell_matrix_D(dofs_per_cell_dgrt, dofs_per_cell_dgrt);
    FullMatrix<double> cell_matrix_E(dofs_per_cell_dgrt, dofs_per_cell_dgrt);

    Vector<double> cell_solution(dofs_per_cell);
    Vector<double> cell_velocity(dofs_per_cell_dgrt);

    const Coefficient<dim>      coefficient;
    std::vector<Tensor<2, dim>> coefficient_values(n_q_points_dgrt);

    const FEValuesExtractors::Vector velocities(0);
    const FEValuesExtractors::Scalar pressure_interior(0);
    const FEValuesExtractors::Scalar pressure_face(1);

// 在介绍中，我们解释了如何计算单元上的数值速度。我们需要每个单元上的压力解值、格拉姆矩阵的系数和 $L_2$ 投影的系数。我们已经计算了全局解，所以我们将从全局解中提取单元解。格拉姆矩阵的系数在我们计算压力的系统矩阵时已经计算过了。我们在这里也要这样做。对于投影的系数，我们做矩阵乘法，即用格拉姆矩阵的倒数乘以 $(\mathbf{K} \mathbf{w}, \mathbf{w})$ 的矩阵作为组成部分。然后，我们将所有这些系数相乘，称之为β。数值速度是贝塔和拉维尔特-托马斯空间的基础函数的乘积。

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

        coefficient.value_list(fe_values_dgrt.get_quadrature_points(),
                               coefficient_values);

// 这个 cell_matrix_E 的分量是 $(\mathbf{K} \mathbf{w}, \mathbf{w})$ 的积分。  cell_matrix_M 是格拉姆矩阵。

        cell_matrix_M = 0;
        cell_matrix_E = 0;
        for (unsigned int q = 0; q < n_q_points_dgrt; ++q)
          for (unsigned int i = 0; i < dofs_per_cell_dgrt; ++i)
            {
              const Tensor<1, dim> v_i = fe_values_dgrt[velocities].value(i, q);
              for (unsigned int k = 0; k < dofs_per_cell_dgrt; ++k)
                {
                  const Tensor<1, dim> v_k =
                    fe_values_dgrt[velocities].value(k, q);

                  cell_matrix_E(i, k) +=
                    (coefficient_values[q] * v_i * v_k * fe_values_dgrt.JxW(q));

                  cell_matrix_M(i, k) += (v_i * v_k * fe_values_dgrt.JxW(q));
                }
            }

// 为了计算介绍中提到的矩阵 $D$ ，我们就需要按照介绍中的解释来评估 $D=M^{-1}E$ 。

        cell_matrix_M.gauss_jordan();
        cell_matrix_M.mmult(cell_matrix_D, cell_matrix_E);

// 然后，我们还需要再次计算矩阵 $C$ ，用于评估弱离散梯度。这与组装系统矩阵时使用的代码完全相同，所以我们只需从那里复制它。

        cell_matrix_G = 0;
        for (unsigned int q = 0; q < n_q_points; ++q)
          for (unsigned int i = 0; i < dofs_per_cell_dgrt; ++i)
            {
              const double div_v_i =
                fe_values_dgrt[velocities].divergence(i, q);
              for (unsigned int j = 0; j < dofs_per_cell; ++j)
                {
                  const double phi_j_interior =
                    fe_values[pressure_interior].value(j, q);

                  cell_matrix_G(i, j) -=
                    (div_v_i * phi_j_interior * fe_values.JxW(q));
                }
            }

        for (const auto &face : cell->face_iterators())
          {
            fe_face_values.reinit(cell, face);
            fe_face_values_dgrt.reinit(cell_dgrt, face);

            for (unsigned int q = 0; q < n_face_q_points; ++q)
              {
                const Tensor<1, dim> &normal = fe_face_values.normal_vector(q);

                for (unsigned int i = 0; i < dofs_per_cell_dgrt; ++i)
                  {
                    const Tensor<1, dim> v_i =
                      fe_face_values_dgrt[velocities].value(i, q);
                    for (unsigned int j = 0; j < dofs_per_cell; ++j)
                      {
                        const double phi_j_face =
                          fe_face_values[pressure_face].value(j, q);

                        cell_matrix_G(i, j) +=
                          ((v_i * normal) * phi_j_face * fe_face_values.JxW(q));
                      }
                  }
              }
          }
        cell_matrix_G.Tmmult(cell_matrix_C, cell_matrix_M);

// 最后，我们需要提取对应于当前单元的压力未知数。

        cell->get_dof_values(solution, cell_solution);

// 我们现在可以计算当地的速度未知数（相对于我们将 $-\mathbf K \nabla_{w,d} p_h$ 项投影到的Raviart-Thomas空间而言）。

        cell_velocity = 0;
        for (unsigned int k = 0; k < dofs_per_cell_dgrt; ++k)
          for (unsigned int j = 0; j < dofs_per_cell_dgrt; ++j)
            for (unsigned int i = 0; i < dofs_per_cell; ++i)
              cell_velocity(k) +=
                -(cell_solution(i) * cell_matrix_C(i, j) * cell_matrix_D(k, j));

// 我们计算达西速度。这与cell_velocity相同，但用于绘制Darcy速度图。

        cell_dgrt->get_dof_indices(local_dof_indices_dgrt);
        for (unsigned int k = 0; k < dofs_per_cell_dgrt; ++k)
          for (unsigned int j = 0; j < dofs_per_cell_dgrt; ++j)
            for (unsigned int i = 0; i < dofs_per_cell; ++i)
              darcy_velocity(local_dof_indices_dgrt[k]) +=
                -(cell_solution(i) * cell_matrix_C(i, j) * cell_matrix_D(k, j));
      }
  }

//  @sect4{WGDarcyEquation<dim>::compute_pressure_error}

// 这一部分是为了计算压力的 $L_2$ 误差。 我们定义一个向量，用来保存每个单元上的误差规范。接下来，我们使用 VectorTool::integrate_difference() 来计算每个单元上的 $L_2$ 准则的误差。然而，我们实际上只关心解向量的内部分量的误差（我们甚至不能评估正交点的界面压力，因为这些都位于单元格的内部），因此必须使用一个权重函数，确保解变量的界面分量被忽略。这是通过使用ComponentSelectFunction来实现的，其参数表明我们要选择哪个分量（零分量，即内部压力）以及总共有多少分量（两个）。

  template <int dim>
  void WGDarcyEquation<dim>::compute_pressure_error()
  {
    Vector<float> difference_per_cell(triangulation.n_active_cells());
    const ComponentSelectFunction<dim> select_interior_pressure(0, 2);
    VectorTools::integrate_difference(dof_handler,
                                      solution,
                                      ExactPressure<dim>(),
                                      difference_per_cell,
                                      QGauss<dim>(fe.degree + 2),
                                      VectorTools::L2_norm,
                                      &select_interior_pressure);

    const double L2_error = difference_per_cell.l2_norm();
    std::cout << "L2_error_pressure " << L2_error << std::endl;
  }

//  @sect4{WGDarcyEquation<dim>::compute_velocity_error}

// 在这个函数中，我们评估每个单元的速度的 $L_2$ 误差，以及面的流量的 $L_2$ 误差。该函数依赖于之前计算过的`compute_postprocessed_velocity()`函数，该函数根据之前计算过的压力解来计算速度场。

// 我们将评估每个单元的速度，并计算数值速度和精确速度之间的差异。

  template <int dim>
  void WGDarcyEquation<dim>::compute_velocity_errors()
  {
    const QGauss<dim>     quadrature_formula(fe_dgrt.degree + 1);
    const QGauss<dim - 1> face_quadrature_formula(fe_dgrt.degree + 1);

    FEValues<dim> fe_values_dgrt(fe_dgrt,
                                 quadrature_formula,
                                 update_values | update_gradients |
                                   update_quadrature_points |
                                   update_JxW_values);

    FEFaceValues<dim> fe_face_values_dgrt(fe_dgrt,
                                          face_quadrature_formula,
                                          update_values |
                                            update_normal_vectors |
                                            update_quadrature_points |
                                            update_JxW_values);

    const unsigned int n_q_points_dgrt = fe_values_dgrt.get_quadrature().size();
    const unsigned int n_face_q_points_dgrt =
      fe_face_values_dgrt.get_quadrature().size();

    std::vector<Tensor<1, dim>> velocity_values(n_q_points_dgrt);
    std::vector<Tensor<1, dim>> velocity_face_values(n_face_q_points_dgrt);

    const FEValuesExtractors::Vector velocities(0);

    const ExactVelocity<dim> exact_velocity;

    double L2_err_velocity_cell_sqr_global = 0;
    double L2_err_flux_sqr                 = 0;

// 在之前计算了后处理的速度之后，我们在这里只需要提取每个单元和面的相应数值，并与精确的数值进行比较。

    for (const auto &cell_dgrt : dof_handler_dgrt.active_cell_iterators())
      {
        fe_values_dgrt.reinit(cell_dgrt);

// 首先计算后处理的速度场与精确速度场之间的 $L_2$ 误差。

        fe_values_dgrt[velocities].get_function_values(darcy_velocity,
                                                       velocity_values);
        double L2_err_velocity_cell_sqr_local = 0;
        for (unsigned int q = 0; q < n_q_points_dgrt; ++q)
          {
            const Tensor<1, dim> velocity = velocity_values[q];
            const Tensor<1, dim> true_velocity =
              exact_velocity.value(fe_values_dgrt.quadrature_point(q));

            L2_err_velocity_cell_sqr_local +=
              ((velocity - true_velocity) * (velocity - true_velocity) *
               fe_values_dgrt.JxW(q));
          }
        L2_err_velocity_cell_sqr_global += L2_err_velocity_cell_sqr_local;

// 为了重建通量，我们需要单元格和面的大小。由于通量是按面计算的，我们必须在每个单元的所有四个面上进行循环。为了计算面的速度，我们从之前计算的`darcy_velocity`中提取正交点的值。然后，我们计算法线方向的速度平方误差。最后，我们通过对面和单元面积的适当缩放来计算单元上的 $L_2$ 通量误差，并将其加入全局误差。

        const double cell_area = cell_dgrt->measure();
        for (const auto &face_dgrt : cell_dgrt->face_iterators())
          {
            const double face_length = face_dgrt->measure();
            fe_face_values_dgrt.reinit(cell_dgrt, face_dgrt);
            fe_face_values_dgrt[velocities].get_function_values(
              darcy_velocity, velocity_face_values);

            double L2_err_flux_face_sqr_local = 0;
            for (unsigned int q = 0; q < n_face_q_points_dgrt; ++q)
              {
                const Tensor<1, dim> velocity = velocity_face_values[q];
                const Tensor<1, dim> true_velocity =
                  exact_velocity.value(fe_face_values_dgrt.quadrature_point(q));

                const Tensor<1, dim> &normal =
                  fe_face_values_dgrt.normal_vector(q);

                  ((velocity * normal - true_velocity * normal) *
                   (velocity * normal - true_velocity * normal) *
                   fe_face_values_dgrt.JxW(q));
              }
            const double err_flux_each_face =
              L2_err_flux_face_sqr_local / face_length * cell_area;
            L2_err_flux_sqr += err_flux_each_face;
          }
      }

// 将所有单元和面的误差相加后，我们进行平方根计算，得到速度和流量的 $L_2$ 误差。我们将这些数据输出到屏幕上。

    const double L2_err_velocity_cell =
      std::sqrt(L2_err_velocity_cell_sqr_global);
    const double L2_err_flux_face = std::sqrt(L2_err_flux_sqr);

    std::cout << "L2_error_vel:  " << L2_err_velocity_cell << std::endl
              << "L2_error_flux: " << L2_err_flux_face << std::endl;
  }
// @sect4{WGDarcyEquation::output_results}

// 我们有两组结果要输出：内部解和骨架解。我们使用 DataOut 来显示内部结果。骨架结果的图形输出是通过使用DataOutFaces类完成的。

// 在这两个输出文件中，内部和面的变量都被存储。对于界面输出，输出文件只是包含了内部压力对面的插值，但是因为没有确定从两个相邻的单元中得到的是哪一个内部压力变量，所以在界面输出文件中最好是忽略内部压力。相反，对于单元格内部输出文件，当然不可能显示任何界面压力 $p^\partial$ ，因为这些压力只适用于界面，而不是单元格内部。因此，你会看到它们被显示为一个无效的值（比如一个无穷大）。

// 对于单元内部的输出，我们还想输出速度变量。这有点棘手，因为它生活在同一个网格上，但使用不同的DoFHandler对象（压力变量生活在`dof_handler`对象上，达西速度生活在`dof_handler_dgrt`对象上）。幸运的是， DataOut::add_data_vector() 函数有一些变化，允许指定一个矢量对应的DoFHandler，因此我们可以在同一个文件中对两个DoFHandler对象的数据进行可视化。

  template <int dim>
  void WGDarcyEquation<dim>::output_results() const
  {
    {
      DataOut<dim> data_out;

// 首先将压力解决方案附加到DataOut对象上。

      const std::vector<std::string> solution_names = {"interior_pressure",
                                                       "interface_pressure"};
      data_out.add_data_vector(dof_handler, solution, solution_names);

// 然后对达西速度场做同样的处理，并继续将所有内容写进文件。

      const std::vector<std::string> velocity_names(dim, "velocity");
      const std::vector<
        DataComponentInterpretation::DataComponentInterpretation>
        velocity_component_interpretation(
          dim, DataComponentInterpretation::component_is_part_of_vector);
      data_out.add_data_vector(dof_handler_dgrt,
                               darcy_velocity,
                               velocity_names,
                               velocity_component_interpretation);

      data_out.build_patches(fe.degree);
      std::ofstream output("solution_interior.vtu");
      data_out.write_vtu(output);
    }

    {
      DataOutFaces<dim> data_out_faces(false);
      data_out_faces.attach_dof_handler(dof_handler);
      data_out_faces.add_data_vector(solution, "Pressure_Face");
      data_out_faces.build_patches(fe.degree);
      std::ofstream face_output("solution_interface.vtu");
      data_out_faces.write_vtu(face_output);
    }
  }
// @sect4{WGDarcyEquation::run}

// 这是主类的最后一个函数。它调用我们类的其他函数。

  template <int dim>
  void WGDarcyEquation<dim>::run()
  {
    std::cout << "Solving problem in " << dim << " space dimensions."
              << std::endl;
    make_grid();
    setup_system();
    assemble_system();
    solve();
    compute_postprocessed_velocity();
    compute_pressure_error();
    compute_velocity_errors();
    output_results();
  }

} // namespace Step61
// @sect3{The main function}

// 这是主函数。我们可以在这里改变维度以在3D中运行。

int main()
{
  try
    {
      Step61::WGDarcyEquation<2> wg_darcy(0);
      wg_darcy.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 (c) 2018 Silvio Mayolo
//// See LICENSE.txt for licensing details

#ifndef NUMBER_HPP
#define NUMBER_HPP

#include <ios>
#include <string>
#include <type_traits>
#include <complex>
#include <boost/variant.hpp>
#include <boost/mpl/vector.hpp>
#include <boost/mpl/distance.hpp>
#include <boost/mpl/begin_end.hpp>
#include <boost/mpl/find.hpp>
#include <boost/mpl/size.hpp>
#include <boost/multiprecision/cpp_int.hpp>
#include <boost/operators.hpp>
#include <boost/optional.hpp>

/// \file
///
/// \brief The Number class and its helpers.

/// A Latitude number can be stored in one of five ways, depending on
/// the precision needed. They are listed here, in increasing order of
/// "wideness". That is, if an operation is performed that involves
/// two numbers, the resulting number will be at least as wide as the
/// wider of the two operands.
///
/// -# `smallint` A fixed-precision integer value
/// -# `bigint` An arbitrary precision integer value
/// -# `ratio` A fraction consisting of arbitrary precision integers
/// -# `floating` A floating-point real value
/// -# `complex` A floating-point complex value
class Number :
    private boost::integer_arithmetic<Number>,
    private boost::bitwise<Number>,
    private boost::unit_steppable<Number> {
public:

    /// A fixed-precision integer is a C++ long integer.
    typedef long smallint;

    /// An arbitrary precision integer.
    typedef boost::multiprecision::cpp_int bigint;

    /// A rational number.
    typedef boost::multiprecision::cpp_rational ratio;

    /// A floating-point number, using the appropriate C++ type.
    typedef double floating;

    /// A C++ complex type, consisting of floating-point values.
    typedef std::complex<double> complex;

    /// \brief This is the internal type of the Number instance which
    /// contains the actual value.
    typedef boost::variant<smallint, bigint, ratio, floating, complex> magic_t;

    /// An enumeration representing the levels of the Latitude
    /// numerical hierarchy.
    enum hierarchy_t {
        SMALLINT = 0,
        BIGINT   = 1,
        RATIO    = 2,
        FLOATING = 3,
        COMPLEX  = 4
    };

private:
    std::unique_ptr<magic_t> value;
public:

    /// \details Constructs a zero number of the narrowest type.
    Number();

    /// Constructs a number.
    ///
    /// \param arg a small integer
    Number(smallint arg);

    /// Constructs a number.
    ///
    /// \param arg a large integer
    Number(bigint arg);

    /// Constructs a number.
    ///
    /// \param arg a rational number
    Number(ratio arg);

    /// Constructs a number.
    ///
    /// \param arg a floating-point number
    Number(floating arg);

    /// Constructs a number.
    ///
    /// \param arg a complex number
    Number(complex arg);

    /// Copy-constructs a number.
    ///
    /// \param num the other number
    Number(const Number& num);

    /// Copy-assigns a number.
    ///
    /// \param other the other number
    Number& operator=(Number other);

    /// Adds the number in-place.
    ///
    /// \param other the number to add
    /// \return the current number
    Number& operator +=(const Number& other);

    /// Subtracts the number in-place.
    ///
    /// \param other the number to subtract
    /// \return the current number
    Number& operator -=(const Number& other);

    /// Multiplies the number in-place.
    ///
    /// \param other the number to multiply
    /// \return the current number
    Number& operator *=(const Number& other);

    /// Divides the number in-place.
    ///
    /// \param other the divisor
    /// \return the current number
    Number& operator /=(const Number& other);

    /// Replaces the number with its value modulo some divisor.
    ///
    /// \param other the divisor
    /// \return the current number
    Number& operator %=(const Number& other);

    /// Returns the current number to the power of the argument. If
    /// the answer does not exist, then the appropriate floating-point
    /// exceptional value, such as NaN, is returned. If floating-point
    /// special values are unsupported on the current system, the
    /// behavior is undefined.
    ///
    /// \param other the exponent
    /// \return the resulting value
    Number pow(const Number& other) const;

    /// Negates the current number.
    ///
    /// \return the additive inverse
    Number operator -() const;

    /// Reciprocates the value. If the value is zero, then the
    /// appropriate floating-point exceptional value is returned, with
    /// an architecture-defined fallback if that fails.
    ///
    /// \return the multiplicative inverse
    Number recip() const;

    /// Performs bitwise AND on the number in-place.
    ///
    /// \param other the other number
    /// \return the current number
    Number& operator &=(const Number& other);

    /// Performs bitwise OR on the number in-place.
    ///
    /// \param other the other number
    /// \return the current number
    Number& operator |=(const Number& other);

    /// Performs bitwise XOR on the number in-place.
    ///
    /// \param other the other number
    /// \return the current number
    Number& operator ^=(const Number& other);

    /// Performs bitwise NOT on the number in-place.
    ///
    /// \return the result
    Number operator ~() const;

    /// Returns the sine of the number.
    ///
    /// \return the result of the computation
    Number sin() const;

    /// Returns the cosine of the number.
    ///
    /// \return the result of the computation
    Number cos() const;

    /// Returns the tangent of the number.
    ///
    /// \return the result of the computation
    Number tan() const;

    /// Returns the hyperbolic sine of the number.
    ///
    /// \return the result of the computation
    Number sinh() const;

    /// Returns the hyperbolic cosine of the number.
    ///
    /// \return the result of the computation
    Number cosh() const;

    /// Returns the hyperbolic tangent of the number.
    ///
    /// \return the result of the computation
    Number tanh() const;

    /// Returns e to the power of the number.
    ///
    /// \return the result of the computation
    Number exp() const;

    /// Returns the inverse sine of the number.
    ///
    /// \return the result of the computation
    Number asin() const;

    /// Returns the inverse cosine of the number.
    ///
    /// \return the result of the computation
    Number acos() const;

    /// Returns the inverse tangent of the number.
    ///
    /// \return the result of the computation
    Number atan() const;

    /// Returns the inverse hyperbolic sine of the number.
    ///
    /// \return the result of the computation
    Number asinh() const;

    /// Returns the inverse hyperbolic cosine of the number.
    ///
    /// \return the result of the computation
    Number acosh() const;

    /// Returns the inverse hyperbolic tangent of the number.
    ///
    /// \return the result of the computation
    Number atanh() const;

    /// Returns the natural logarithm of the number.
    ///
    /// \return the result of the computation
    Number log() const;

    /// Returns the floor of the number, truncated toward negative
    /// infinity. If the caller is a complex number, the resulting
    /// value is simply zero, as the greatest integer function is not
    /// well-defined on complex numbers.
    ///
    /// \return the result of the computation
    Number floor() const;

    /// Returns the real part of the number, or the number itself if
    /// it is real.
    ///
    /// \return the real part
    Number realPart() const;

    /// Returns the imaginary part of the number, or zero if it is
    /// real.
    ///
    /// \return the imaginary part
    Number imagPart() const;

    /// Returns a string representation of the number. Where possible,
    /// the string is be a valid Latitude string that evaluates to the
    /// number.
    ///
    /// \return the string representation
    std::string asString() const;

    /// Forcibly casts the number to the C++ integral type
    /// corresponding to Number::smallint. If the value was wider, it
    /// will be truncated. This is useful in VM functions that require
    /// an enumeration value, as it is convenient to simply assume
    /// that the enumeration value is a small integer.
    ///
    /// \return the casted value
    smallint asSmallInt() const;

    /// Returns a numerical value corresponding to the level of the
    /// hierarchy that the value belongs to, with 0 being the
    /// narrowest type (small integer) and 4 being the largest
    /// (complex number).
    ///
    /// \return the hierarchy level
    hierarchy_t hierarchyLevel() const;

    friend Number complexNumber(const Number& real, const Number& imag);
    friend bool operator ==(const Number& self, const Number& other);
    friend bool operator <(const Number& self, const Number& other);

};

/// Constructs a complex number from its real and imaginary parts. If
/// either of the real or the imaginary parts is itself a non-real
/// number, then the complex number is constructed using the formula
/// `a + b i` where `a` is the first argument and `b` is the second,
/// even if they are complex.
///
/// \param real the real part
/// \param imag the imaginary part
/// \return the complex number
Number complexNumber(const Number& real, const Number& imag);

/// Returns a floating point number representing NaN, if it exists.
///
/// \return the value NaN
boost::optional<Number> constantNan();

/// Returns a floating point number representing positive infinity, if
/// it exists.
///
/// \return the value infinity
boost::optional<Number> constantInf();

/// Returns a floating point number representing negative infinity, if
/// it exists.
///
/// \return the value negative infinity
boost::optional<Number> constantNegInf();

/// Returns the smallest floating point number strictly greater than
/// zero representable on the current system.
///
/// \return the epsilon value
Number constantEps();

/// Reads an integer. This function expects a string in the format
/// used internally by the Latitude VM for radix literals. That is,
/// the first character should be D, X, O, or B (case sensitive),
/// respectively representing base 10, base 16, base 8, or base 2.
/// This is optionally followed by a sign, either + or -, and then one
/// or more digits.
///
/// \param integer the text to parse
/// \return the number (an integer) or none
boost::optional<Number> parseInteger(const char* integer);

/// Returns whether the two numbers are the same. The comparison
/// is done by coercing to the wider type.
///
/// \param self the first number
/// \param other the other number
/// \return whether the values are equal
bool operator ==(const Number& self, const Number& other);

/// Returns whether the number is less than another number. The
/// comparison is done by coercing to the wider type. Comparing
/// complex numbers with this operator always returns false.
///
/// \param self the first number
/// \param other the other number
/// \return whether the value is less than the argument
bool operator <(const Number& self, const Number& other);

bool operator >(const Number& self, const Number& other);
bool operator <=(const Number& self, const Number& other);
bool operator >=(const Number& self, const Number& other);
bool operator !=(const Number& self, const Number& other);

std::ostream& operator <<(std::ostream& out, const Number& number);

#endif // NUMBER_HPP


#include "ammannbeenker.h"
#include <Eigen/Dense>
#include <Eigen/Geometry>
namespace quacry{
using Vec2 = glm::vec2;
using namespace kipod::Shapes;
using Vec3 = glm::vec3;
using Vec4 = glm::vec4;
using Mat4 = glm::mat4;

auto GetOrthogonalLine(const Vec2&a, const Vec2& b) -> Vec2
{
    Vec2 dir = b-a;
    return {-dir.y, dir.x};
}

auto FindIntersection(const Vec2& a,const Vec2& b, const Vec2& x, const Vec2& y) -> Vec2
{
    using Line2 = Eigen::Hyperplane<float,2>;
    auto ToEigen = [](Vec2 x){ return Eigen::Vector2f{x.x,x.y}; };
    Line2 A = Line2::Through(ToEigen(a), ToEigen(b));
    Line2 B = Line2::Through(ToEigen(x), ToEigen(y));
    Eigen::Vector2f p = A.intersection(B);
    LOG_INFO("Intersection found: {}", p);
    return {p[0],p[1]};
}

auto AvgPoint(const std::vector<Vec2>& vs) -> Vec2
{
    return std::accumulate(begin(vs),end(vs),Vec2(0))/float(size(vs));
}

auto Pattern1(float s) ->std::vector<Vec2>
{   //      ____a   0
    //          \
    //           \b   7
    //         i/ |
    //          \ |c  6
    //           /
    //     _____/d   5
    auto o = Octagon(s);
    auto a = o.GetVertex(0);
    auto b = o.GetVertex(7);
    auto c = o.GetVertex(6);
    auto d = o.GetVertex(5);
    auto i = FindIntersection(b,
                              b+GetOrthogonalLine(b, a),
                              c,
                              c+GetOrthogonalLine(d,c));
    return {c,b,i};
}

auto Pattern2(float s) ->std::vector<Vec2>
{
    auto o = Octagon(s);
    auto a = o.GetVertex(0);
    auto b = o.GetVertex(7);
    auto c = o.GetVertex(6);
    auto d = o.GetVertex(5);
    auto e = o.GetVertex(4);
    auto f = o.GetVertex(3);
    auto g = o.GetVertex(2);
    auto h = o.GetVertex(1);
    auto i = FindIntersection(a,d,b,g);
    auto j = FindIntersection(a,d,c,h);
    auto k = FindIntersection(c,h,b,e);
    return {b,i,j,k};
}

auto Pattern3(float s) -> std::vector<Vec2>
{
    auto o = Octagon(s);

    auto i = FindIntersection(o.GetVertex(6), o.GetVertex(1), o.GetVertex(7), o.GetVertex(4));
    auto j = FindIntersection(o.GetVertex(0), o.GetVertex(5), o.GetVertex(6), o.GetVertex(1));
    auto k = FindIntersection(o.GetVertex(0), o.GetVertex(5), o.GetVertex(7), o.GetVertex(4));
    auto toflip = i-j; toflip.x *= -1;
    auto jk = j+toflip;
    return {i,j,jk,k};
}

auto Pattern4(float s) -> std::vector<Vec2>
{
    auto o = Octagon(s);

    auto i = FindIntersection(o.GetVertex(6), o.GetVertex(1), o.GetVertex(7), o.GetVertex(4));
    auto j = FindIntersection(o.GetVertex(0), o.GetVertex(5), o.GetVertex(6), o.GetVertex(1));
    auto k = FindIntersection(o.GetVertex(0), o.GetVertex(5), o.GetVertex(7), o.GetVertex(4));
    auto toflip = i-j; toflip.x *= -1;
    auto jk = j+toflip;

    auto c = FindIntersection(j,j+Vec2(-1,0), o.GetVertex(7)+Vec2(-s,0), o.GetVertex(6)+Vec2(-s,0));
    auto d = FindIntersection(jk,k, o.GetVertex(7)+Vec2(-s,0), o.GetVertex(6)+Vec2(-s,0));
    return {jk, j, c, d};
}

auto Pattern5(float s) -> std::vector<Vec2>
{
    auto prev = Pattern4(s);
    auto a = prev[0];
    auto b = prev[3];
    auto toflip = b-a; toflip.y *= -1;
    auto c = a + toflip;
    return {a,b,c};
}

auto Pattern6(float s) -> std::vector<Vec2>
{
    double lambda = -1.+std::sqrt(2.);
    auto o = Octagon(s);
    auto vs = o.transformed_vertices_;
    for(auto& v : vs) v*= lambda*lambda;
    return vs;
}

auto Rotate8(const Vec2& in) -> std::vector<Vec2>
{
    std::vector<Vec2> out;
    auto theta = [](int i){ return 	3.1415926535897*2./i; };
    for(int i = 0; i<8; ++i)
        out.emplace_back(Mat2(cos(theta(i)),sin(theta(i)),-sin(theta(i)),cos(theta(i)))*in);
    return out;
}

auto AmmannBeenker() -> Quasicrystal22
{
    const float s = std::sqrt(2);

    Basis4 basis = {1,0,s,0,
                   1,2,0,s,
                   1,0,-s,0,
                   1,2,0,-s}; basis = glm::transpose(basis);

    SampleSize sample_range = {-20,20,-20,20,-10,10,-10,10};
    Quasicrystal22 AmmBee = {basis, Shape(Octagon(s)),
                           "Ammann Beenker", sample_range};
    auto vs = Pattern1(s); auto polygon1 = Polygon{vs,AvgPoint(vs)};
    AmmBee.patterns_.emplace_back( std::make_unique<Window2>(Shape(polygon1)));

    auto vs2 = Pattern2(s); auto polygon2 = Polygon{vs2,AvgPoint(vs2)};
    AmmBee.patterns_.emplace_back( std::make_unique<Window2>(Shape(polygon2)));

    auto vs3 = Pattern3(s); auto polygon3 = Polygon{vs3,AvgPoint(vs3)};
    AmmBee.patterns_.emplace_back( std::make_unique<Window2>(Shape(polygon3)));

    auto vs4 = Pattern4(s); auto polygon4 = Polygon{vs4,AvgPoint(vs4)};
    AmmBee.patterns_.emplace_back( std::make_unique<Window2>(Shape(polygon4)));

    auto vs5 = Pattern5(s); auto polygon5 = Polygon{vs5,AvgPoint(vs5)};
    AmmBee.patterns_.emplace_back( std::make_unique<Window2>(Shape(polygon5)));

    auto vs6 = Pattern6(s); auto polygon6 = Polygon{vs6,AvgPoint(vs6)};
    AmmBee.patterns_.emplace_back( std::make_unique<Window2>(Shape(polygon6)));

    for(auto& p : AmmBee.patterns_)
        p->Init();
    return AmmBee;
}
}


/* Copyright (c) 2017, Julian Straub <jstraub@csail.mit.edu>, Randi Cabezas <rcabezas@csail.mit.edu>
 * Licensed under the MIT license. See the license file LICENSE.
 */

#pragma once

#include <Eigen/Dense>

#include <dpMM/basemeasure.hpp>
#include <dpMM/vmfPriorAOne.hpp>

/*
 * vmf base measure; uses closed form for marginal data density
 * (J. Straub, "Nonparamatric Directional Perception", 2017)
 */
template<typename T>
class vMFbase3D : public BaseMeasure<T>
{
public:
  vMFbase3D(const vMFprior<T>& vmfPrior);
  vMFbase3D(const vMFbase3D<T>& vmf);
  ~vMFbase3D();

  virtual BaseMeasure<T>* copy();
  virtual vMFbase3D<T>* copyNative();

  T logLikelihood(const Matrix<T,Dynamic,1>& x) const;
  T logLikelihood(const Matrix<T,Dynamic,Dynamic>& x, uint32_t i) const
    {return logLikelihood(x.col(i));};
  void posterior(const Matrix<T,Dynamic,Dynamic>& x, const VectorXu& z,
    uint32_t k);
  void posterior(const vector<Matrix<T,Dynamic,Dynamic> >&x, const VectorXu& z,
    uint32_t k);
  void sample();

  T logPdfUnderPrior() const;
  virtual T logPdfUnderPriorMarginalized() const;

  virtual T logPdfUnderPriorMarginalized(const Matrix<T,Dynamic,1>& x);

//  virtual NiwSampled<T>* merge(const NiwSampled<T>& other);
//  void fromMerge(const NiwSampled<T>& niwA, const NiwSampled<T>& niwB);

  void print() const;
  virtual uint32_t getDim() const {return(uint32_t(vmf_.D_));};

//  const Matrix<T,Dynamic,Dynamic>& scatter() const {return niw0_.scatter();};
//  const Matrix<T,Dynamic,1>& mean() const {return niw0_.mean();};
//  T count() const {return niw0_.count();};
////  T& count() {return niw0_.count_;};
  const Matrix<T,Dynamic,1>& getMean() const {return vmf_.mu_;};
  const T tau() const {return vmf_.tau();};

  vMFprior<T> vmfPrior_;
  vMF<T> vmf_;
private:

};

// ------------------------- impl -------------------------------------------

template<typename T>
vMFbase3D<T>::vMFbase3D(const vMFprior<T>& vmfPrior)
  : vmfPrior_(vmfPrior), vmf_(vmfPrior_.sample())
{};

template<typename T>
vMFbase3D<T>::vMFbase3D(const vMFbase3D<T>& base)
  :  vmfPrior_(base.vmfPrior_), vmf_(base.vmf_)
{};

template<typename T>
vMFbase3D<T>::~vMFbase3D()
{};

template<typename T>
BaseMeasure<T>* vMFbase3D<T>::copy()
{
  return new vMFbase3D<T>(*this);
};

template<typename T>
vMFbase3D<T>* vMFbase3D<T>::copyNative()
{
  return new vMFbase3D<T>(*this);
};

template<typename T>
T vMFbase3D<T>::logLikelihood(const Matrix<T,Dynamic,1>& x) const
{
//  cout<<vmf_.logPdf(x)<<endl;
  return vmf_.logPdf(x);
};

template<typename T>
void vMFbase3D<T>::posterior(const Matrix<T,Dynamic,Dynamic>& x, const VectorXu& z,
    uint32_t k)
{
  vmfPrior_.getSufficientStatistics(x,z,k);
  std::cout << vmfPrior_.xSum_.transpose() << " " << vmfPrior_.count_ << std::endl;
  vmf_ = vmfPrior_.sampleFromPosterior();
};

template<typename T>
void vMFbase3D<T>::posterior(const vector<Matrix<T,Dynamic,Dynamic> >&x, const VectorXu& z,
    uint32_t k)
{
};

template<typename T>
void vMFbase3D<T>::sample()
{
  vmf_ = vmfPrior_.sample();
};

template<typename T>
void vMFbase3D<T>::print() const
{
  vmf_.print();
};

template<typename T>
T vMFbase3D<T>::logPdfUnderPrior() const
{
  return 0.;
};

template<typename T>
T vMFbase3D<T>::logPdfUnderPriorMarginalized() const
{
  return vmfPrior_.logPdfMarginalized();
};

template<typename T>
T vMFbase3D<T>::logPdfUnderPriorMarginalized(const Matrix<T,Dynamic,1>& x)
{
  return vmfPrior_.logMarginal(x);
};