/*
 *  Copyright 2011-2015 Maxim Milakov
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */

#include "convolution_layer.h"

#include "layer_factory.h"
#include "neural_network_exception.h"
#include "nn_types.h"
#include "proto/nnforge.pb.h"

#include <algorithm>
#include <numeric>
#include <boost/lambda/lambda.hpp>
#include <boost/format.hpp>
#include <opencv2/core/core.hpp>

namespace nnforge
{
	// {5957B44F-699E-4DDB-836E-3FB3EEB54965}
	const boost::uuids::uuid convolution_layer::layer_guid =
		{ 0x59, 0x57, 0xb4, 0x4f
		, 0x69, 0x9e
		, 0x4d, 0xdb
		, 0x83, 0x6e
		, 0x3f, 0xb3, 0xee, 0xb5, 0x49, 0x65 };

	// {8AD07635-DDFE-43B4-B26A-15CA19155A65}
	const boost::uuids::uuid convolution_layer::layer_guid_v1 =
		{ 0x8a, 0xd0, 0x76, 0x35
		, 0xdd, 0xfe
		, 0x43, 0xb4
		, 0xb2, 0x6a
		, 0x15, 0xca, 0x19, 0x15, 0x5a, 0x65 };

	const std::string convolution_layer::layer_type_name = "Convolution";

	convolution_layer::convolution_layer(
		const std::vector<unsigned int>& window_sizes,
		unsigned int input_feature_map_count,
		unsigned int output_feature_map_count,
		const std::vector<unsigned int>& left_zero_padding,
		const std::vector<unsigned int>& right_zero_padding)
		: window_sizes(window_sizes),
		input_feature_map_count(input_feature_map_count),
		output_feature_map_count(output_feature_map_count)
	{
		if ((left_zero_padding.size() != 0) && (left_zero_padding.size() != window_sizes.size()))
			throw std::runtime_error((boost::format("Invalid dimension count %1% for left zero padding") % left_zero_padding.size()).str());
		if ((right_zero_padding.size() != 0) && (right_zero_padding.size() != window_sizes.size()))
			throw std::runtime_error((boost::format("Invalid dimension count %1% for right zero padding") % right_zero_padding.size()).str());

		if (left_zero_padding.empty())
			this->left_zero_padding.resize(window_sizes.size(), 0);
		else
			this->left_zero_padding = left_zero_padding;

		if (right_zero_padding.empty())
			this->right_zero_padding.resize(window_sizes.size(), 0);
		else
			this->right_zero_padding = right_zero_padding;

		check();
	}

	void convolution_layer::check()
	{
		if (window_sizes.size() == 0)
			throw neural_network_exception("window sizes for convolution layer may not be empty");

		for(unsigned int i = 0; i < window_sizes.size(); i++)
			if (window_sizes[i] == 0)
				throw neural_network_exception("window dimension for convolution layer may not be zero");

		for(unsigned int i = 0; i < window_sizes.size(); i++)
			if (left_zero_padding[i] >= window_sizes[i])
				throw neural_network_exception((boost::format("left zero padding %1% of dimension (%2%) is greater or equal than layer window size (%3%)") % left_zero_padding[i] % i % window_sizes[i]).str());

		for(unsigned int i = 0; i < window_sizes.size(); i++)
			if (right_zero_padding[i] >= window_sizes[i])
				throw neural_network_exception((boost::format("right zero padding %1% of dimension (%2%) is greater or equal than layer window size (%3%)") % right_zero_padding[i] % i % window_sizes[i]).str());
	}

	const boost::uuids::uuid& convolution_layer::get_uuid() const
	{
		return layer_guid;
	}

	const std::string& convolution_layer::get_type_name() const
	{
		return layer_type_name;
	}

	layer_smart_ptr convolution_layer::clone() const
	{
		return layer_smart_ptr(new convolution_layer(*this));
	}

	layer_configuration convolution_layer::get_layer_configuration(const layer_configuration& input_configuration) const
	{
		if ((input_configuration.feature_map_count >= 0) && (input_configuration.feature_map_count != static_cast<int>(input_feature_map_count)))
			throw neural_network_exception((boost::format("Feature map count in layer (%1%) and input configuration (%2%) don't match") % input_feature_map_count % input_configuration.feature_map_count).str());

		if ((input_configuration.dimension_count >= 0) && (input_configuration.dimension_count != static_cast<int>(window_sizes.size())))
			throw neural_network_exception((boost::format("Dimension count in layer (%1%) and input configuration (%2%) don't match") % window_sizes.size() % input_configuration.dimension_count).str());

		return layer_configuration(output_feature_map_count, static_cast<int>(window_sizes.size()));
	}

	layer_configuration_specific convolution_layer::get_output_layer_configuration_specific(const layer_configuration_specific& input_configuration_specific) const
	{
		if (input_configuration_specific.feature_map_count != input_feature_map_count)
			throw neural_network_exception((boost::format("Feature map count in layer (%1%) and input configuration (%2%) don't match") % input_feature_map_count % input_configuration_specific.feature_map_count).str());

		if (input_configuration_specific.get_dimension_count() != window_sizes.size())
			throw neural_network_exception((boost::format("Dimension count in layer (%1%) and input configuration (%2%) don't match") % window_sizes.size() % input_configuration_specific.get_dimension_count()).str());

		layer_configuration_specific res(output_feature_map_count);

		for(unsigned int i = 0; i < window_sizes.size(); ++i)
		{
			unsigned int total_input_dimension_size = input_configuration_specific.dimension_sizes[i] + left_zero_padding[i] + right_zero_padding[i];
			if (total_input_dimension_size < window_sizes[i])
				throw neural_network_exception((boost::format("Too small total dimension size (with padding) %1% of dimension (%2%) is smaller than layer window size (%3%)") % total_input_dimension_size % i % window_sizes[i]).str());

			res.dimension_sizes.push_back(total_input_dimension_size + 1 - window_sizes[i]);
		}

		return res;
	}

	layer_configuration_specific convolution_layer::get_input_layer_configuration_specific(const layer_configuration_specific& output_configuration_specific) const
	{
		if (output_configuration_specific.feature_map_count != output_feature_map_count)
			throw neural_network_exception((boost::format("Feature map count in layer (%1%) and output configuration (%2%) don't match") % output_feature_map_count % output_configuration_specific.feature_map_count).str());

		if (output_configuration_specific.get_dimension_count() != window_sizes.size())
			throw neural_network_exception((boost::format("Dimension count in layer (%1%) and output configuration (%2%) don't match") % window_sizes.size() % output_configuration_specific.get_dimension_count()).str());

		layer_configuration_specific res(input_feature_map_count);

		for(unsigned int i = 0; i < window_sizes.size(); ++i)
			res.dimension_sizes.push_back(output_configuration_specific.dimension_sizes[i] + window_sizes[i] - 1 - left_zero_padding[i] - right_zero_padding[i]);

		return res;
	}

	std::vector<std::pair<unsigned int, unsigned int> > convolution_layer::get_input_rectangle_borders(const std::vector<std::pair<unsigned int, unsigned int> >& output_rectangle_borders) const
	{
		if (output_rectangle_borders.size() != window_sizes.size())
			throw neural_network_exception((boost::format("Dimension count in layer (%1%) and output borders (%2%) don't match") % window_sizes.size() % output_rectangle_borders.size()).str());

		std::vector<std::pair<unsigned int, unsigned int> > res;

		for(unsigned int i = 0; i < window_sizes.size(); ++i)
			res.push_back(
				std::make_pair(
					static_cast<unsigned int>(std::max(0, static_cast<int>(output_rectangle_borders[i].first) - static_cast<int>(left_zero_padding[i]))),
					(output_rectangle_borders[i].second + window_sizes[i] - 1) - left_zero_padding[i]
				)
			);

		return res;
	}

	void convolution_layer::write(std::ostream& binary_stream_to_write_to) const
	{
		binary_stream_to_write_to.write(reinterpret_cast<const char*>(&input_feature_map_count), sizeof(input_feature_map_count));
		binary_stream_to_write_to.write(reinterpret_cast<const char*>(&output_feature_map_count), sizeof(output_feature_map_count));

		unsigned int dimension_count = static_cast<unsigned int>(window_sizes.size());
		binary_stream_to_write_to.write(reinterpret_cast<const char*>(&dimension_count), sizeof(dimension_count));
		binary_stream_to_write_to.write(reinterpret_cast<const char*>(&(*window_sizes.begin())), sizeof(unsigned int) * dimension_count);
		binary_stream_to_write_to.write(reinterpret_cast<const char*>(&(*left_zero_padding.begin())), sizeof(unsigned int) * dimension_count);
		binary_stream_to_write_to.write(reinterpret_cast<const char*>(&(*right_zero_padding.begin())), sizeof(unsigned int) * dimension_count);
	}

	void convolution_layer::write_proto(void * layer_proto) const
	{
		protobuf::Layer * layer_proto_typed = reinterpret_cast<protobuf::Layer *>(layer_proto);
		protobuf::ConvolutionalParam * param = layer_proto_typed->mutable_convolution_param();

		param->set_output_feature_map_count(output_feature_map_count);
		param->set_input_feature_map_count(input_feature_map_count);

		for(int i = 0; i < window_sizes.size(); ++i)
		{
			protobuf::ConvolutionalParam_ConvolutionalDimensionParam * dim_param = param->add_dimension_param();
			dim_param->set_kernel_size(window_sizes[i]);
			if (left_zero_padding[i] > 0)
				dim_param->set_left_padding(left_zero_padding[i]);
			if (right_zero_padding[i] > 0)
				dim_param->set_right_padding(right_zero_padding[i]);
		}
	}

	void convolution_layer::read(
		std::istream& binary_stream_to_read_from,
		const boost::uuids::uuid& layer_read_guid)
	{
		binary_stream_to_read_from.read(reinterpret_cast<char*>(&input_feature_map_count), sizeof(input_feature_map_count));
		binary_stream_to_read_from.read(reinterpret_cast<char*>(&output_feature_map_count), sizeof(output_feature_map_count));

		unsigned int dimension_count;
		binary_stream_to_read_from.read(reinterpret_cast<char*>(&dimension_count), sizeof(dimension_count));
		window_sizes.resize(dimension_count);
		binary_stream_to_read_from.read(reinterpret_cast<char*>(&(*window_sizes.begin())), sizeof(unsigned int) * dimension_count);

		left_zero_padding.resize(dimension_count, 0);
		right_zero_padding.resize(dimension_count, 0);
		if (layer_read_guid != layer_guid_v1)
		{
			binary_stream_to_read_from.read(reinterpret_cast<char*>(&(*left_zero_padding.begin())), sizeof(unsigned int) * dimension_count);
			binary_stream_to_read_from.read(reinterpret_cast<char*>(&(*right_zero_padding.begin())), sizeof(unsigned int) * dimension_count);
		}
	}

	void convolution_layer::read_proto(const void * layer_proto)
	{
		const protobuf::Layer * layer_proto_typed = reinterpret_cast<const protobuf::Layer *>(layer_proto);
		if (!layer_proto_typed->has_convolution_param())
			throw neural_network_exception((boost::format("No convolution_param specified for layer %1% of type %2%") % instance_name % layer_proto_typed->type()).str());

		input_feature_map_count = layer_proto_typed->convolution_param().input_feature_map_count();
		output_feature_map_count = layer_proto_typed->convolution_param().output_feature_map_count();

		window_sizes.resize(layer_proto_typed->convolution_param().dimension_param_size());
		left_zero_padding.resize(layer_proto_typed->convolution_param().dimension_param_size());
		right_zero_padding.resize(layer_proto_typed->convolution_param().dimension_param_size());

		for(int i = 0; i < layer_proto_typed->convolution_param().dimension_param_size(); ++i)
		{
			window_sizes[i] = layer_proto_typed->convolution_param().dimension_param(i).kernel_size();
			left_zero_padding[i] = layer_proto_typed->convolution_param().dimension_param(i).left_padding();
			right_zero_padding[i] = layer_proto_typed->convolution_param().dimension_param(i).right_padding();
		}

		check();
	}

	data_config convolution_layer::get_data_config() const
	{
		data_config res;

		unsigned int weight_count = input_feature_map_count * output_feature_map_count;
		std::for_each(window_sizes.begin(), window_sizes.end(), weight_count *= boost::lambda::_1);

		res.push_back(weight_count);

		res.push_back(output_feature_map_count);

		return res;
	}

	void convolution_layer::randomize_data(
		layer_data& data,
		layer_data_custom& data_custom,
		random_generator& generator) const
	{
		unsigned int weight_count = 1;
		std::for_each(window_sizes.begin(), window_sizes.end(), weight_count *= boost::lambda::_1);

		float average_feature_map_count = sqrtf(static_cast<float>(input_feature_map_count) * static_cast<float>(output_feature_map_count));

		float standard_deviation = sqrtf(1.0F / (average_feature_map_count * static_cast<float>(weight_count)));
		float max_abs_value = 100.0F * standard_deviation;

		nnforge_normal_distribution<float> nd(0.0F, standard_deviation);

		for(unsigned int i = 0; i < data[0].size(); ++i)
		{
			float val = nd(generator);
			while (fabs(val) > max_abs_value)
				val = nd(generator);

			data[0][i] = val;
		}

		std::fill(data[1].begin(), data[1].end(), 0.0F);
	}

	void convolution_layer::randomize_orthogonal_data(
		layer_data& data,
		layer_data_custom& data_custom,
		random_generator& generator) const
	{
		unsigned int weight_count = 1;
		std::for_each(window_sizes.begin(), window_sizes.end(), weight_count *= boost::lambda::_1);
		unsigned int weight_col_count = weight_count * input_feature_map_count;
		unsigned int weight_row_count = output_feature_map_count;

		nnforge_normal_distribution<float> nd(0.0F, 1.0F);
		for(unsigned int i = 0; i < data[0].size(); ++i)
		{
			float val = nd(generator);
			data[0][i] = val;
		}
		cv::Mat1f weights(weight_row_count, weight_col_count, &(data[0][0]));
		cv::Mat1f w;
		cv::Mat1f u;
		cv::Mat1f vt;
		cv::SVD::compute(weights, w, u, vt, cv::SVD::MODIFY_A);

		cv::Mat1f orth;
		if ((u.rows == weights.rows) && (u.cols == weights.cols))
			orth = u;
		else if ((vt.rows == weights.rows) && (vt.cols == weights.cols))
			orth = vt;
		else
			throw neural_network_exception("Internal error when doing SVD");

		std::copy(orth.begin(), orth.end(), weights.begin());

		std::fill(data[1].begin(), data[1].end(), 0.0F);
	}

	float convolution_layer::get_forward_flops(const layer_configuration_specific& input_configuration_specific) const
	{
		unsigned int neuron_count = get_output_layer_configuration_specific(input_configuration_specific).get_neuron_count();
		unsigned int per_item_flops = input_feature_map_count * 2;
		std::for_each(window_sizes.begin(), window_sizes.end(), per_item_flops *= boost::lambda::_1);
		per_item_flops -= 1;

		return static_cast<float>(neuron_count) * static_cast<float>(per_item_flops);
	}

	float convolution_layer::get_backward_flops(const layer_configuration_specific& input_configuration_specific) const
	{
		unsigned int neuron_count = get_output_layer_configuration_specific(input_configuration_specific).get_neuron_count();
		unsigned int per_item_flops = input_feature_map_count * 2;
		std::for_each(window_sizes.begin(), window_sizes.end(), per_item_flops *= boost::lambda::_1);

		return static_cast<float>(neuron_count) * static_cast<float>(per_item_flops);
	}

	float convolution_layer::get_weights_update_flops(const layer_configuration_specific& input_configuration_specific) const
	{
		unsigned int neuron_count = get_output_layer_configuration_specific(input_configuration_specific).get_neuron_count();
		unsigned int per_item_flops = input_feature_map_count * 2;
		std::for_each(window_sizes.begin(), window_sizes.end(), per_item_flops *= boost::lambda::_1);

		return static_cast<float>(neuron_count) * static_cast<float>(per_item_flops);
	}

	layer_data_configuration_list convolution_layer::get_layer_data_configuration_list() const
	{
		layer_data_configuration_list res;
		res.push_back(layer_data_configuration(input_feature_map_count, output_feature_map_count, window_sizes));
		res.push_back(layer_data_configuration(1, output_feature_map_count, std::vector<unsigned int>()));

		return res;
	}

	std::set<unsigned int> convolution_layer::get_weight_decay_part_id_set() const
	{
		std::set<unsigned int> res;
		res.insert(0);
		return res;
	}
}


////////////////////////////////////////////////////////////////////////////////
//  Copyright (c) 2021 Andreas Wagner.
//
//  Distributed under 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 TUMORMODELS_PETSC_MAT_HPP
#define TUMORMODELS_PETSC_MAT_HPP

#include <numeric>
#include <vector>

#include "gmm.h"
#include "petsc.h"
#include <Eigen/Dense>

namespace macrocirculation {

class PetscMat {
public:
  PetscMat(const std::string &name, const std::vector<std::shared_ptr<DofMap>> &dof_map)
      : PetscMat(name,
                 std::accumulate(dof_map.begin(), dof_map.end(), 0, [](auto v, auto dm) { return v + dm->num_owned_dofs(); }),
                 std::accumulate(dof_map.begin(), dof_map.end(), 0, [](auto v, auto dm) { return v + dm->num_dof(); })) {}

  PetscMat(const std::string &name, const DofMap &dof_map)
      : PetscMat(name, dof_map.num_owned_dofs(), dof_map.num_dof()) {}

  PetscMat(const std::string &name, std::size_t num_owned_dofs, std::size_t num_dofs) {
    CHKERRABORT(PETSC_COMM_WORLD, MatCreate(PETSC_COMM_WORLD, &d_mat));
    CHKERRABORT(PETSC_COMM_WORLD, MatSetType(d_mat, MATMPIAIJ));
    CHKERRABORT(PETSC_COMM_WORLD, MatSetSizes(d_mat, num_owned_dofs, num_owned_dofs, num_dofs, num_dofs));
    // we overestimate the number non-zero entries, otherwise matrix assembly is incredibly slow :
    CHKERRABORT(PETSC_COMM_WORLD, MatMPIAIJSetPreallocation(d_mat, 1000, nullptr, 1000, nullptr));
    CHKERRABORT(PETSC_COMM_WORLD, PetscObjectSetName((PetscObject) d_mat, name.c_str()));
  }

  PetscMat(const PetscMat &) = delete;
  PetscMat(PetscMat &&) = delete;
  PetscMat &operator=(const PetscMat &) = delete;
  PetscMat &operator=(PetscMat &&) = delete;

  ~PetscMat() {
    CHKERRABORT(PETSC_COMM_WORLD, MatDestroy(&d_mat));
  }

  void add(const std::vector<size_t> &row_dofs, const std::vector<size_t> &col_dofs, gmm::row_matrix<gmm::wsvector<double>> &mat_loc) {
    assert(row_dofs.size() == mat_loc.nrows());
    assert(col_dofs.size() == mat_loc.ncols());

    // we have no guarantees that gmm's memory is continuous
    // thus we copy it over
    std::vector<double> mat_memory(row_dofs.size() * col_dofs.size(), 0);
    for (int r = 0; r < row_dofs.size(); r += 1)
      for (int c = 0; c < col_dofs.size(); c += 1)
        mat_memory[c + r * col_dofs.size()] = mat_loc[r][c];

    std::vector<PetscInt> row_dofs_(row_dofs.size(), 0);
    for (int r = 0; r < row_dofs.size(); r += 1)
      row_dofs_[r] = static_cast<PetscInt>(row_dofs[r]);

    std::vector<PetscInt> col_dofs_(col_dofs.size(), 0);
    for (int c = 0; c < col_dofs.size(); c += 1)
      col_dofs_[c] = static_cast<PetscInt>(col_dofs[c]);

    // pass it to petsc
    CHKERRABORT(PETSC_COMM_WORLD,
                MatSetValues(d_mat,
                             static_cast<PetscInt>(row_dofs.size()),
                             row_dofs_.data(),
                             static_cast<PetscInt>(col_dofs.size()),
                             col_dofs_.data(),
                             mat_memory.data(),
                             ADD_VALUES));
  }

  void add(const std::vector<size_t> &row_dofs, const std::vector<size_t> &col_dofs, Eigen::MatrixXd &mat_loc) {
    assert(row_dofs.size() == mat_loc.rows());
    assert(col_dofs.size() == mat_loc.cols());

    // we have no guarantees that gmm's memory is continuous
    // thus we copy it over
    std::vector<double> mat_memory(row_dofs.size() * col_dofs.size(), 0);
    for (int r = 0; r < row_dofs.size(); r += 1)
      for (int c = 0; c < col_dofs.size(); c += 1)
        mat_memory[c + r * col_dofs.size()] = mat_loc(r, c);

    std::vector<PetscInt> row_dofs_(row_dofs.size(), 0);
    for (int r = 0; r < row_dofs.size(); r += 1)
      row_dofs_[r] = static_cast<PetscInt>(row_dofs[r]);

    std::vector<PetscInt> col_dofs_(col_dofs.size(), 0);
    for (int c = 0; c < col_dofs.size(); c += 1)
      col_dofs_[c] = static_cast<PetscInt>(col_dofs[c]);

    // pass it to petsc
    CHKERRABORT(PETSC_COMM_WORLD,
                MatSetValues(d_mat,
                             static_cast<PetscInt>(row_dofs.size()),
                             row_dofs_.data(),
                             static_cast<PetscInt>(col_dofs.size()),
                             col_dofs_.data(),
                             mat_memory.data(),
                             ADD_VALUES));
  }

  double norm1() const {
    double value;
    CHKERRABORT(PETSC_COMM_WORLD, MatNorm(d_mat, NORM_1, &value));
    return value;
  }

  void zero() {
    MatZeroEntries(d_mat);
  }

  void assemble() {
    CHKERRABORT(PETSC_COMM_WORLD, MatAssemblyBegin(d_mat, MAT_FINAL_ASSEMBLY));
    CHKERRABORT(PETSC_COMM_WORLD, MatAssemblyEnd(d_mat, MAT_FINAL_ASSEMBLY));
  }

  Mat &get_mat() { return d_mat; }

  size_t first_dof() const {
    PetscInt start = 0;
    PetscInt end = 0;
    CHKERRABORT(PETSC_COMM_WORLD, MatGetOwnershipRange(d_mat, &start, &end));
    return start;
  }

  size_t last_dof() const {
    PetscInt start = 0;
    PetscInt end = 0;
    CHKERRABORT(PETSC_COMM_WORLD, MatGetOwnershipRange(d_mat, &start, &end));
    return end;
  }

  double get(size_t i, size_t j) const {
    double value;
    PetscInt ii = i;
    PetscInt jj = j;
    CHKERRABORT(PETSC_COMM_WORLD, MatGetValues(d_mat, 1, &ii, 1, &jj, &value));
    return value;
  }

  void print() const {
    CHKERRABORT(PETSC_COMM_WORLD, MatView(d_mat, PETSC_VIEWER_STDOUT_WORLD));
    }

private:
  Mat d_mat{};
};

template<typename Stream>
inline Stream &operator<<(Stream &out, const PetscMat &m) {
  auto ldof = m.last_dof();
  for (size_t k = m.first_dof(); k < ldof; k += 1) {
    for (size_t j = m.first_dof(); j < ldof; j += 1)
      out << m.get(k, j) << " ";
    out << "\n";
  }
  return out;
}

} // namespace macrocirculation

#endif //TUMORMODELS_PETSC_MAT_HPP


/*
 * fold_pairs.hpp
 *
 *  Created on: Aug 28, 2009
 *      Author: stewie
 */

#ifndef FOLD_PAIRS_HPP_
#define FOLD_PAIRS_HPP_

#include <functional>

#include <boost/next_prior.hpp>
#include <boost/range/begin.hpp>
#include <boost/range/end.hpp>

namespace bn {

namespace util {

/**
 * Applies binary functions @a s and @a p on all the 2-combinations without
 * repetition of elements in the given ranges.
 *
 * See fold_cartesian_product() for a formal description of the algorithm
 * replacing the cartesian product with the sequence of 2-combinations.
 * @param first forward iterator to the initial position of the range
 * @param last forward iterator to the final position of the range
 * @param init initial value for the accumulator
 * @param s binary function that implements the sum
 * @param p binary function that implements the product
 * @return the accumulated value
 *
 * @see for_each_pair() for the iteration version.
 */
template<class ForwardIterator, class T, class Sum, class Product> T fold_pairs(
		ForwardIterator first, ForwardIterator last, T init, Sum s = std::plus<
				T>(), Product p = std::multiplies<T>()) {
	for (; first != last; ++first)
		for (ForwardIterator it = boost::next(first); it != last; ++it)
			init = s(init, p(*first, *it));
	return init;
}

template<class ForwardRange, class T, class Sum, class Product> T fold_pairs(
		ForwardRange& r, T init, Sum s = std::plus<T>(), Product p =
				std::multiplies<T>()) {
	return fold_pairs(boost::begin(r), boost::end(r), init, s, p);
}

template<class ForwardRange, class T, class Sum, class Product> T fold_pairs(
		const ForwardRange& r, T init, Sum s = std::plus<T>(), Product p =
				std::multiplies<T>()) {
	return fold_pairs(boost::begin(r), boost::end(r), init, s, p);
}

}

}

#endif /* FOLD_PAIRS_HPP_ */


/*
 *@description - this file implements the the parallel Johnson's algorithm for
                 single source shortest path on sparse graphs using MPI. It
                 assumes the input graph file is in the format like the graph
                 http://snap.stanford.edu/data/web-Google.html
                 The node numbers are continuous intergers starting from 0.
                 The implementation depends on the Boost library.
 *@author: Yao Zhu (yzhucs@gmail.com).
 */

#include <algorithm>
#include <boost/heap/fibonacci_heap.hpp>
#include <climits>
#include <cmath>
#include <cstdlib>
#include <ctime>
#include <fstream>
#include <iostream>
#include <mpi.h>
#include <sstream>
#include <string>
#include <sys/time.h>
#include <vector>

using namespace std;
using namespace boost::heap;

// some constants definition.
#define INT_INF 			100000//INT_MAX	// indicate an inf value.

// output a vector in column format to a stream.
void output_vector(ostream &out, const int* const z, const int len) {
	if (z == NULL) {
		cerr << "z cannot be NULL in output_vector()." << endl;
		exit(-1);
	}

	out << "the vector is:" << endl;
	for (int i = 0; i < len; i++) {
		if (z[i] != INT_INF) {
			out << z[i] << endl;
		} else {
			out << "inf" << endl;
		}
	}
}

// let process 0 print out some message for showing the progress.
void print_msg(string msg, int rank) {
	if (rank == 0) {
		cerr << msg << endl;
	}
}

/*
 *@description - read in the webgraph data. (src[m], dest[m]) is a directed edge
 	 	 	 	 in the webgraph. This function also returns the maximum node
 	 	 	 	 number, which is number of nodes - 1 (we assume the node
 	 	 	 	 number starts from 0).
 *@param - webgraph_file, name of the webgraph file.
 *@param[out] - src, the array of source nodes.
 *@param[out] - dest, the array of the destination nodes.
 *@param[out] - nnz, number of edges.
 *@return - the maximum node number.
 */
int read_webgraph(const char *webgraph_file, int **src, int **dest, int *nnz) {
	std::ifstream wbfile(webgraph_file);
	string line;
	*nnz = 0;
	// count the number of edges.
	while (std::getline(wbfile, line)) {
		// check the 1st character of this line.
		if (line[0] >= '0' && line[0] <= '9') {
			(*nnz)++;
		}
	}
	// allocate storage in coordinate format.
	*src = new int[*nnz];
	*dest = new int[*nnz];
	if (*src == NULL || *dest == NULL) {
		cerr << "cannot allocate src or dest in read_webgraph()." << endl;
		exit(-1);
	}
	// return to the beginning of the input file.
	wbfile.clear();
	wbfile.seekg(0, ios::beg);
	int i = 0;
	int max_nn = -1;	// maximum node number.
	while (std::getline(wbfile, line)) {
		// check the 1st character of this line.
		if (line[0] >= '0' && line[0] <= '9') {
			std::istringstream iss(line);
			iss >> (*src)[i] >> (*dest)[i];
			if ((*src)[i] > max_nn) {
				max_nn = (*src)[i];
			}
			if ((*dest)[i] > max_nn) {
				max_nn = (*dest)[i];
			}
			i++;
		}
	}
	wbfile.close();
	return max_nn;
}

/*
 *@description - construct the CSR format of the sparse digraph matrix
 	 	 	 	 (src, dest) from the coordinate format given by (src, dest).
 *@param - (src, dest) defines the web graph edges.
 *@param - nnz, number of nonzero elements. it's the length of
 	 	   src, dest, val, and col_ind.
 *@param - N, number of nodes, it's the length of row_ptr.
 *@param - (val, col_ind, row_ptr) represents the CSR format of the sparse
 	 	   digraph matrix (src, dest).
 */
void coord2csr(const int* const src, const int* const dest,
			   const int nnz, const int N,
			   int *val, int *col_ind, int *row_ptr) {
	if (src == NULL || dest == NULL) {
		cerr << "none of src and dest can be NULL in coord2csr()." << endl;
		exit(-1);
	}
	if (val == NULL || col_ind == NULL || row_ptr == NULL) {
		cerr << "none of val, col_ind, and row_ptr can be NULL in "
				"coord2csr()." << endl;
		exit(-1);
	}
	// we need the out_degree to construct the CSR format.
	int *out_degree = new int[N];
	std::fill(out_degree, out_degree + N, 0);
	for (int l = 0; l < nnz; l++) {
		int i = src[l];
		out_degree[i]++;
	}
	// compute row_ptr as the cumsum of out_degree.
	// note row_ptr[N] = nnz. the node numbers are in [0..N-1].
	row_ptr[0] = 0;
	for (int i = 1; i < N+1; i++) {
		row_ptr[i] = row_ptr[i-1] + out_degree[i-1];
	}
	// construct val and col_ind according to row_ptr.
	for (int l = 0; l < nnz; l++) {
		int i = src[l];
		int j = dest[l];
		col_ind[row_ptr[i]] = j;
		val[row_ptr[i]] = 1;
		row_ptr[i]++;
	}
	// recompute row_ptr as the cumsum of out_degree.
	row_ptr[0] = 0;
	for (int i = 1; i < N+1; i++) {
		row_ptr[i] = row_ptr[i-1] + out_degree[i-1];
	}
	// deallocate out_degree.
	if (out_degree != NULL) {
		delete [] out_degree;
	}
}

/*
 *@description - get the rank of the process that has the node with the given
 	 	 	 	 node number. This partition assumes all the remnant elements
 	 	 	 	 are given to the last process.
 *@param - nn, node number.
 *@param - N, total number of nodes.
 *@param - nproc, number of processes.
 */
int nn2rank(const int nn, const int N, const int nproc) {
	int quota = int(N / nproc);
	if (nn >= quota * nproc) {
		return (nproc - 1);
	} else {
		return int(nn / quota);
	}
}

/*
 *@description - get the number of rows a process p has from the row partition
 				 scheme.
 *@param - N, total number of rows (one row correspond to one node).
 *@param - nproc, total number of processes.
 *@param - rank, of the process.
 */
int get_Nlocal(const int N, const int nproc, const int rank) {
	int quota = int(N / nproc);
	if (rank == nproc - 1) {	// the last process.
		return (N - (nproc - 1) * quota);
	} else {
		return quota;
	}
}

/*
 *@description - get the first node number that belongs to a process p.
 */
int get_start_nn(const int N, const int nproc, const int rank) {
	int quota = int(N / nproc);
	return (quota * rank);
}

/*
 *@description - definition of a data item stored in the priority queue.
 */
struct pq_data
{
    int node_number;		//the global node number.
    int dist;				//current value of distance from the source node.

    pq_data(int nn, int d): node_number(nn), dist(d) {}

    bool operator< (pq_data const & data) const
    {
    	// we define this way because we want our priority queue to be min-heap.
        return dist > data.dist;
    }
};

typedef fibonacci_heap<pq_data>::handle_type handle_t;

/*
 *@description - this function is to encapsulate the operation to the local
 	 	 	 	 priority queue.
 *@param - total_out_num will index into node_buffer and dist_buffer.
 */
void extract_local_pq(int *sp, fibonacci_heap<pq_data> &local_pq,
					  handle_t *handle_local_pq, const int N, const int nproc,
					  const int rank, const int start_nn,
					  const int* const local_row_ptr,
					  const int* const local_col_ind,
					  const int* const local_val, int *sendcnts,
					  int *node_buffer, int *dist_buffer, int &total_out_num) {
	if (!local_pq.empty()) {
		// get the top data item.
		pq_data data_item = local_pq.top();
		if (data_item.dist != INT_INF) {
			// extract it from the queue.
			local_pq.pop();
			int u = data_item.node_number;
			sp[u] = data_item.dist;
			// scan the adjacency list of this node.
			for (int l = local_row_ptr[u-start_nn];
				 l < local_row_ptr[u-start_nn+1]; l++) {
				// v is the neighbor node number (in global value).
				int v = local_col_ind[l];
				int edge_weight = local_val[l];
				// check whether v is a node belong itself or not.
				if (nn2rank(v, N, nproc) == rank) {
					if (sp[v] == INT_INF) { // v still in the queue.
						if ((*handle_local_pq[v-start_nn]).dist >
				 	 	 	sp[u] + edge_weight) {
							// decrease dist value to v.
							pq_data temp_data_item(v, sp[u] + edge_weight);
							local_pq.decrease(handle_local_pq[v-start_nn],
											  temp_data_item);
						}
						} else if (sp[v] > sp[u] + edge_weight) {
							// v is already extracted.
						// insert v back to queue.
						pq_data temp_data_item(v, sp[u] + edge_weight);
						handle_local_pq[v-start_nn] =
								local_pq.push(temp_data_item);
						sp[v] = INT_INF;	// mark it in the queue again.
					}
				} else { // v belong to another process.
					// record v and distance to it.
					node_buffer[total_out_num] = v;
					dist_buffer[total_out_num] = sp[u] + edge_weight;
					total_out_num++;
					// get the process that has v.
					int proc_rank = nn2rank(v, N, nproc);
					// we send the node number and its distance.
					sendcnts[proc_rank] += 2;
				}
			}
		}
	} // if (!local_pq.empty())
}

/*
 *@descriptioni - it assumes the node number starts from 0.
 *@param - argv[1] - name of the file storing web graph.
 *@param - argv[2] - the node number (starts from 0) of the source node.
 *@param - argv[3] - file to store the solution vector.
 */
int parallel_johnson(int argc, char *argv[]) {
	int nproc;							// total number of processes.
	int N;								// total number of nodes.
	int rank;							// rank of the process.

	MPI_Comm_size(MPI_COMM_WORLD, &nproc);
	MPI_Comm_rank(MPI_COMM_WORLD, &rank);

	if (argc != 4) {
		if (rank == 0) {
			cerr << "to run this program must supply the following command "
					"line arguments (in order)" << endl;
			cerr << "argv[1]---web graph file." << endl;
			cerr << "argv[2]---source node number." << endl;
			cerr << "argv[3]---file to save the solution." << endl;
		}
		exit(-1);
	}

	//--------process 0 reads in the web graph, change it to CSR format
	//--------and then partition the nodes and distributes them to each process.

	// data only used by process 0 for reading in the webgraph.
	int *val = NULL;
	int *col_ind = NULL;
	int *row_ptr = NULL;

	if (rank == 0) {
		print_msg("process 0 reads in the web graph data......", rank);
		// process 0 reads in the web graph from file in coordinate format.
		int *src = NULL;
		int *dest = NULL;
		int nnz = 0;
		N = read_webgraph(argv[1], &src, &dest, &nnz) + 1;
		cerr << "N = " << N << endl;
		// construct the CSR format of the sparse matrix P from the
		// coordinate format.
		val = new int[nnz];
		col_ind = new int[nnz];
		row_ptr = new int[N+1];
		coord2csr(src, dest, nnz, N, val, col_ind, row_ptr);

		// we no longer need src and dest.
		if (src != NULL) {
			delete [] src;
			src = NULL;
		}
		if (dest != NULL) {
			delete [] dest;
			dest = NULL;
		}
		nnz = 0;
	}

	print_msg("read in the webgraph is done.", rank);

	/***collective communications for scattering necessary information.***/
	// broadcast the total number of nodes.
	MPI_Bcast(&N, 1, MPI_INT, 0, MPI_COMM_WORLD);
	// process 0 prepare the information for scattering P.
	// nnz of the row block local to each process. it's also the sendcounts
	// buffer used to scatter val and col_ind.
	int *local_nnz = NULL;
	// the data sent to process p start at location displs_csr[p] when
	// scattering val and col_ind.
	int *displs_csr = NULL;
	// the sendcounts buffer used to scatter row_ptr.
	int *sendcounts_node = NULL;
	// the data sent to process p start at location displs_node[p] when
	// scattering row_ptr.
	int *displs_node = NULL;
	if (rank == 0) {
		int quota = int(N / nproc);
		local_nnz = new int[nproc];
		displs_csr = new int[nproc];
		sendcounts_node = new int[nproc];
		displs_node = new int[nproc];
		for (int p = 0; p < nproc; p++) {
			int sp = p * quota;
			int tp;
			if (p != nproc - 1) {
				tp = (p + 1) * quota - 1;
			} else {
				tp = N - 1;
			}
			local_nnz[p] = row_ptr[tp+1] - row_ptr[sp];
			displs_csr[p] = row_ptr[sp];
			sendcounts_node[p] = get_Nlocal(N, nproc, p);
			displs_node[p] = sp;
		}
	}
	// (local_val, local_col_ind, local_row_ptr) represents the CSR format
	// of the row block local to this process.
	int *local_val = NULL;
	int *local_col_ind = NULL;
	int *local_row_ptr = NULL;
	// allocate space for local_row_ptr according to the row parition scheme.
	int start_nn = get_start_nn(N, nproc, rank);	// first node belong to the
													// process.
	// this process will possess node numbers [start_nn..start_nn+Nlocal]
	// assuming the contiguous row partition.
	int Nlocal = get_Nlocal(N, nproc, rank);
	local_row_ptr = new int[Nlocal + 1];	// local_row_ptr[Nlocal] stores nnz
											// local to this process from
											// row partition.
	// process 0 scatter local_nnz to each process.
	MPI_Scatter(local_nnz, 1, MPI_INT, local_row_ptr+Nlocal, 1, MPI_INT,
				0, MPI_COMM_WORLD);
	// allocate space for local_val and local_col_ind with the size given by
	// local_row_ptr[Nlocal].
	local_val = new int[local_row_ptr[Nlocal]];
	local_col_ind = new int[local_row_ptr[Nlocal]];
	/***process 0 scatterv the sparse matrix P in CSR format.***/
	// process 0 scatterv val and col_ind to each process.
	MPI_Scatterv(val, local_nnz, displs_csr, MPI_INT, local_val,
				 local_row_ptr[Nlocal], MPI_INT, 0, MPI_COMM_WORLD);
	MPI_Scatterv(col_ind, local_nnz, displs_csr, MPI_INT, local_col_ind,
				 local_row_ptr[Nlocal], MPI_INT, 0, MPI_COMM_WORLD);
	// process 0 scatterv row_ptr to each process.
	MPI_Scatterv(row_ptr, sendcounts_node, displs_node, MPI_INT,
				 local_row_ptr, Nlocal, MPI_INT, 0, MPI_COMM_WORLD);
	// adjust local_row_ptr to make it start from 0.
	for (int i = Nlocal - 1; i >= 0; i--) {
		local_row_ptr[i] -= local_row_ptr[0];
	}

	print_msg("distribute sparse matrix is done.", rank);

	// process 0 cleans up some space no longer needed.
	if (rank == 0) {
		if (val != NULL) {
			delete [] val;
			val = NULL;
		}
		if (col_ind != NULL) {
			delete [] col_ind;
			col_ind = NULL;
		}
		if (row_ptr != NULL) {
			delete [] row_ptr;
			row_ptr = NULL;
		}
		if (local_nnz != NULL) {
			delete [] local_nnz;
			local_nnz = NULL;
		}
		if (displs_csr != NULL) {
			delete [] displs_csr;
			displs_csr = NULL;
		}
		// sendcounts_node and displs_node will be reused when gathering
		// the solution back.
	}

	// we store the sp vector in full. but each process only needs to
	// operate on the elements it need. sp stores the snapshot shortest path
	// distance from s to each node.
	int *sp = new int[N];
	// initialize sp to be INT_INF.
	std::fill(sp + start_nn, sp + start_nn + Nlocal, INT_INF);

	// get the source node number from command line.
	int source_node = atoi(argv[2]);
	if (rank == 0) {
		cerr << "compute shortest paths from source node: "
			 << source_node << endl;
		cerr << "parallel Johnson's algorithm starts......" << endl;
	}

	// time variables for profiling. we need to include the time for
	// constructing the dependency among processes.
	struct timeval start_tv, end_tv;
	// barrier for time profiling.
	MPI_Barrier(MPI_COMM_WORLD);
	if (rank == 0) {
		gettimeofday(&start_tv, NULL);
	}

	//--------parallel Johnson's algorithm through MPI.-----------

	fibonacci_heap<pq_data> local_pq;	// local priority queue.
	//typedef fibonacci_heap<pq_data>::handle_type handle_t;
	// handle to local_pq. it provides the random access to queue elements,
	// as well as for changing the key directly.
	handle_t *handle_local_pq = NULL;
	int local_pq_len = 0;				// length of local_pq.
	int total_pq_len = 0;				// total length of all local_pq across
										// processes.

	// initialize the local priority queue and its handle.
	handle_local_pq = new handle_t[Nlocal];
	for (int i = 0; i < Nlocal; i++) {
		int dist;
		if ((i + start_nn) == source_node) {
			dist = 0;
		} else {
			dist = INT_INF;
		}
		pq_data data_item(start_nn + i, dist);
		handle_local_pq[i] = local_pq.push(data_item);
	}

	// space to communicate possible shortest distances to nodes across
	// processes.
	int senddisp[nproc];
	int sendcnts[nproc];
	int recvdisp[nproc];
	int recvcnts[nproc];
	// we will send and recv node number and its distances simultaneously.
	int *sendbuf = new int[2 * N];
	int *recvbuf = new int[2 * N];

	// buffer to store the node number and distance to them, for communicating
	// to other processes.
	int *node_buffer = new int[N];
	int *dist_buffer = new int[N];
	int total_out_num;	// total number of nodes to send out. it will be
						// the effective length of node_buffer and dist_buffer.
	int total_in_num;	// total number of nodes to recv. the effective length
						// of recvbuf is 2*total_in_num.

	do {
		// initialize space for communication.
		std::fill(sendcnts, sendcnts + nproc, 0);
		std::fill(recvcnts, recvcnts + nproc, 0);
		total_out_num = 0;

		// check the local priority queue.
		const int async_iter = 30;
		for (int l = 0; l < async_iter; l++) {
			extract_local_pq(sp, local_pq, handle_local_pq, N, nproc, rank,
							 start_nn, local_row_ptr, local_col_ind, local_val,
							 sendcnts, node_buffer, dist_buffer, total_out_num);
		}
		// use MPI_Alltoall() to let each process aware of the number of
		// (node, distance) it will receive from each other process.
		MPI_Alltoall(sendcnts, 1, MPI_INT, recvcnts, 1, MPI_INT,
					 MPI_COMM_WORLD);
		// prepare senddisp and recvdisp.
		total_in_num = 0;
		for (int i = 0; i < nproc; i++) {
			total_in_num += recvcnts[i]/2;
			if (i > 0) {
				senddisp[i] = senddisp[i-1] + sendcnts[i-1];
				recvdisp[i] = recvdisp[i-1] + recvcnts[i-1];
			} else {
				senddisp[i] = 0;
				recvdisp[i] = 0;
			}
		}
		// prepared the content of the sendbuf using senddisp.
		for(int j = 0; j < total_out_num; j++) {
			int i = nn2rank(node_buffer[j], N, nproc);
			// put the key and value in sendbuf.
			sendbuf[senddisp[i]++] = node_buffer[j];
			sendbuf[senddisp[i]++] = dist_buffer[j];
		}

		// reconstruct senddisp.
		for (int i = 0; i < nproc; i++) {
			if (i > 0) {
				senddisp[i] = senddisp[i-1] + sendcnts[i-1];
			} else {
				senddisp[i] = 0;
			}
		}
		// use MPI_Alltoallv() to communicate the nodes and distances.
		MPI_Alltoallv(sendbuf, sendcnts, senddisp, MPI_INT, recvbuf,
					  recvcnts, recvdisp, MPI_INT, MPI_COMM_WORLD);

		// scan the recvbuf for distances found by other processes.
		for (int j = 0; j < total_in_num; j++) {
			int v = recvbuf[2*j];
			int v_dist = recvbuf[2*j + 1];
			if (sp[v] == INT_INF) { // v still in the queue.
				if ((*handle_local_pq[v-start_nn]).dist > v_dist) {
					// decrease dist value to v.
					pq_data temp_data_item(v, v_dist);
					local_pq.decrease(handle_local_pq[v-start_nn],
									  temp_data_item);
				}
			} else if (sp[v] > v_dist) { // v is already extracted.
				// insert v back to queue.
				pq_data temp_data_item(v, v_dist);
				handle_local_pq[v-start_nn] = local_pq.push(temp_data_item);
				sp[v] = INT_INF;	// mark it in the queue again.
			}
		}

		// MPI_Allreduce to get the total length of local priority queues
		// to detect termination.
		// we need to check the effective size of local_pq, i.e., #elements
		// with finite values in local_pq. We only need to check the top.
		if (!local_pq.empty()) {
			pq_data check_data_item = local_pq.top();
			if (check_data_item.dist != INT_INF) {
				local_pq_len = 1;
			} else {
				local_pq_len = 0;
			}
		} else {
			local_pq_len = 0;
		}
		MPI_Allreduce(&local_pq_len, &total_pq_len, 1,
					  MPI_INT, MPI_SUM, MPI_COMM_WORLD);
		if (total_pq_len == 0) { // when every process is done.
			break;
		}
	} while (true);

	// barrier for time profiling.
	MPI_Barrier(MPI_COMM_WORLD);
	if (rank == 0) {
		gettimeofday(&end_tv, NULL);
		double tElapsed = (end_tv.tv_sec + end_tv.tv_usec/1000000.0) -
						  (start_tv.tv_sec + start_tv.tv_usec/1000000.0);
		cerr << "parallel Johnson's algorithm completes." << endl;
		cout << "Time: " << tElapsed << " seconds when using "
			 << nproc << " processes." << endl;
	}

	// process 0 gather the sp vector and stores it.
	int *sp_sol = NULL;			// the final vector of shortest path distance.
	if (rank == 0) {
		sp_sol = new int[N];
	}
	MPI_Gatherv(sp + start_nn, Nlocal, MPI_INT, sp_sol,
			    sendcounts_node, displs_node, MPI_INT, 0,
			    MPI_COMM_WORLD);
	if (rank == 0) {
		// save the sp_sol vector.
		ofstream out(argv[3], ios::trunc);
		output_vector(out, sp_sol, N);
		cerr << "the shortest path distance vector has been saved in file "
			 << argv[3] << endl;
		// we no longer need sendcounts_node and displs_node.
		if (sendcounts_node != NULL) {
			delete [] sendcounts_node;
			sendcounts_node = NULL;
		}
		if (displs_node != NULL) {
			delete [] displs_node;
			displs_node = NULL;
		}
		// we no longer need sp_sol.
		if (sp_sol != NULL) {
			delete [] sp_sol;
			sp_sol = NULL;
		}
	}

	// clean up all the space.
	if (local_val != NULL) {
		delete [] local_val;
		local_val = NULL;
	}
	if (local_col_ind != NULL) {
		delete [] local_col_ind;
		local_col_ind = NULL;
	}
	if (local_row_ptr != NULL) {
		delete [] local_row_ptr;
		local_row_ptr = NULL;
	}
	if (sp != NULL) {
		delete [] sp;
		sp = NULL;
	}
	if (handle_local_pq != NULL) {
		delete [] handle_local_pq;
		handle_local_pq = NULL;
	}
	if (sendbuf != NULL) {
		delete [] sendbuf;
		sendbuf = NULL;
	}
	if (recvbuf != NULL) {
		delete [] recvbuf;
		recvbuf = NULL;
	}
	if (node_buffer != NULL) {
		delete [] node_buffer;
		node_buffer = NULL;
	}
	if (dist_buffer != NULL) {
		delete [] dist_buffer;
		dist_buffer = NULL;
	}

	return 0;
}

int main(int argc, char *argv[]) {
	MPI_Init(&argc, &argv);

	parallel_johnson(argc, argv);

	MPI_Finalize();
}


//  Copyright John Maddock 2007.
//  Copyright Paul A. Bristow 2007, 2009
//  Use, modification and distribution are 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_STATS_PARETO_HPP
#define BOOST_STATS_PARETO_HPP

// http://en.wikipedia.org/wiki/Pareto_distribution
// http://www.itl.nist.gov/div898/handbook/eda/section3/eda3661.htm
// Also:
// Weisstein, Eric W. "Pareto Distribution."
// From MathWorld--A Wolfram Web Resource.
// http://mathworld.wolfram.com/ParetoDistribution.html
// Handbook of Statistical Distributions with Applications, K Krishnamoorthy, ISBN 1-58488-635-8, Chapter 23, pp 257 - 267.
// Caution KK's a and b are the reverse of Mathworld!

#include <boost/math/distributions/fwd.hpp>
#include <boost/math/distributions/complement.hpp>
#include <boost/math/distributions/detail/common_error_handling.hpp>
#include <boost/math/special_functions/powm1.hpp>

#include <utility> // for BOOST_CURRENT_VALUE?

namespace boost
{
  namespace math
  {
    namespace detail
    { // Parameter checking.
      template <class RealType, class Policy>
      inline bool check_pareto_scale(
        const char* function,
        RealType scale,
        RealType* result, const Policy& pol)
      {
        if((boost::math::isfinite)(scale))
        { // any > 0 finite value is OK.
          if (scale > 0)
          {
            return true;
          }
          else
          {
            *result = policies::raise_domain_error<RealType>(
              function,
              "Scale parameter is %1%, but must be > 0!", scale, pol);
            return false;
          }
        }
        else
        { // Not finite.
          *result = policies::raise_domain_error<RealType>(
            function,
            "Scale parameter is %1%, but must be finite!", scale, pol);
          return false;
        }
      } // bool check_pareto_scale

      template <class RealType, class Policy>
      inline bool check_pareto_shape(
        const char* function,
        RealType shape,
        RealType* result, const Policy& pol)
      {
        if((boost::math::isfinite)(shape))
        { // Any finite value > 0 is OK.
          if (shape > 0)
          {
            return true;
          }
          else
          {
            *result = policies::raise_domain_error<RealType>(
              function,
              "Shape parameter is %1%, but must be > 0!", shape, pol);
            return false;
          }
        }
        else
        { // Not finite.
          *result = policies::raise_domain_error<RealType>(
            function,
            "Shape parameter is %1%, but must be finite!", shape, pol);
          return false;
        }
      } // bool check_pareto_shape(

      template <class RealType, class Policy>
      inline bool check_pareto_x(
        const char* function,
        RealType const& x,
        RealType* result, const Policy& pol)
      {
        if((boost::math::isfinite)(x))
        { //
          if (x > 0)
          {
            return true;
          }
          else
          {
            *result = policies::raise_domain_error<RealType>(
              function,
              "x parameter is %1%, but must be > 0 !", x, pol);
            return false;
          }
        }
        else
        { // Not finite..
          *result = policies::raise_domain_error<RealType>(
            function,
            "x parameter is %1%, but must be finite!", x, pol);
          return false;
        }
      } // bool check_pareto_x

      template <class RealType, class Policy>
      inline bool check_pareto( // distribution parameters.
        const char* function,
        RealType scale,
        RealType shape,
        RealType* result, const Policy& pol)
      {
        return check_pareto_scale(function, scale, result, pol)
           && check_pareto_shape(function, shape, result, pol);
      } // bool check_pareto(

    } // namespace detail

    template <class RealType = double, class Policy = policies::policy<> >
    class pareto_distribution
    {
    public:
      typedef RealType value_type;
      typedef Policy policy_type;

      pareto_distribution(RealType l_scale = 1, RealType l_shape = 1)
        : m_scale(l_scale), m_shape(l_shape)
      { // Constructor.
        RealType result = 0;
        detail::check_pareto("boost::math::pareto_distribution<%1%>::pareto_distribution", l_scale, l_shape, &result, Policy());
      }

      RealType scale()const
      { // AKA Xm and Wolfram b and beta
        return m_scale;
      }

      RealType shape()const
      { // AKA k and Wolfram a and alpha
        return m_shape;
      }
    private:
      // Data members:
      RealType m_scale;  // distribution scale (xm) or beta
      RealType m_shape;  // distribution shape (k) or alpha
    };

    typedef pareto_distribution<double> pareto; // Convenience to allow pareto(2., 3.);

    template <class RealType, class Policy>
    inline const std::pair<RealType, RealType> range(const pareto_distribution<RealType, Policy>& /*dist*/)
    { // Range of permissible values for random variable x.
      using boost::math::tools::max_value;
      return std::pair<RealType, RealType>(static_cast<RealType>(0), max_value<RealType>()); // scale zero to + infinity.
    } // range

    template <class RealType, class Policy>
    inline const std::pair<RealType, RealType> support(const pareto_distribution<RealType, Policy>& dist)
    { // Range of supported values for random variable x.
      // This is range where cdf rises from 0 to 1, and outside it, the pdf is zero.
      using boost::math::tools::max_value;
      return std::pair<RealType, RealType>(dist.scale(), max_value<RealType>() ); // scale to + infinity.
    } // support

    template <class RealType, class Policy>
    inline RealType pdf(const pareto_distribution<RealType, Policy>& dist, const RealType& x)
    {
      BOOST_MATH_STD_USING  // for ADL of std function pow.
      static const char* function = "boost::math::pdf(const pareto_distribution<%1%>&, %1%)";
      RealType scale = dist.scale();
      RealType shape = dist.shape();
      RealType result = 0;
      if(false == (detail::check_pareto_x(function, x, &result, Policy())
         && detail::check_pareto(function, scale, shape, &result, Policy())))
         return result;
      if (x < scale)
      { // regardless of shape, pdf is zero (or should be disallow x < scale and throw an exception?).
        return 0;
      }
      result = shape * pow(scale, shape) / pow(x, shape+1);
      return result;
    } // pdf

    template <class RealType, class Policy>
    inline RealType cdf(const pareto_distribution<RealType, Policy>& dist, const RealType& x)
    {
      BOOST_MATH_STD_USING  // for ADL of std function pow.
      static const char* function = "boost::math::cdf(const pareto_distribution<%1%>&, %1%)";
      RealType scale = dist.scale();
      RealType shape = dist.shape();
      RealType result = 0;

      if(false == (detail::check_pareto_x(function, x, &result, Policy())
         && detail::check_pareto(function, scale, shape, &result, Policy())))
         return result;

      if (x <= scale)
      { // regardless of shape, cdf is zero.
        return 0;
      }

      // result = RealType(1) - pow((scale / x), shape);
      result = -boost::math::powm1(scale/x, shape, Policy()); // should be more accurate.
      return result;
    } // cdf

    template <class RealType, class Policy>
    inline RealType quantile(const pareto_distribution<RealType, Policy>& dist, const RealType& p)
    {
      BOOST_MATH_STD_USING  // for ADL of std function pow.
      static const char* function = "boost::math::quantile(const pareto_distribution<%1%>&, %1%)";
      RealType result = 0;
      RealType scale = dist.scale();
      RealType shape = dist.shape();
      if(false == (detail::check_probability(function, p, &result, Policy())
           && detail::check_pareto(function, scale, shape, &result, Policy())))
      {
        return result;
      }
      if (p == 0)
      {
        return scale; // x must be scale (or less).
      }
      if (p == 1)
      {
        return tools::max_value<RealType>(); // x = + infinity.
      }
      result = scale /
        (pow((1 - p), 1 / shape));
      // K. Krishnamoorthy,  ISBN 1-58488-635-8 eq 23.1.3
      return result;
    } // quantile

    template <class RealType, class Policy>
    inline RealType cdf(const complemented2_type<pareto_distribution<RealType, Policy>, RealType>& c)
    {
       BOOST_MATH_STD_USING  // for ADL of std function pow.
       static const char* function = "boost::math::cdf(const pareto_distribution<%1%>&, %1%)";
       RealType result = 0;
       RealType x = c.param;
       RealType scale = c.dist.scale();
       RealType shape = c.dist.shape();
       if(false == (detail::check_pareto_x(function, x, &result, Policy())
           && detail::check_pareto(function, scale, shape, &result, Policy())))
         return result;

       if (x <= scale)
       { // regardless of shape, cdf is zero, and complement is unity.
         return 1;
       }
       result = pow((scale/x), shape);

       return result;
    } // cdf complement

    template <class RealType, class Policy>
    inline RealType quantile(const complemented2_type<pareto_distribution<RealType, Policy>, RealType>& c)
    {
      BOOST_MATH_STD_USING  // for ADL of std function pow.
      static const char* function = "boost::math::quantile(const pareto_distribution<%1%>&, %1%)";
      RealType result = 0;
      RealType q = c.param;
      RealType scale = c.dist.scale();
      RealType shape = c.dist.shape();
      if(false == (detail::check_probability(function, q, &result, Policy())
           && detail::check_pareto(function, scale, shape, &result, Policy())))
      {
        return result;
      }
      if (q == 1)
      {
        return scale; // x must be scale (or less).
      }
      if (q == 0)
      {
        return tools::max_value<RealType>(); // x = + infinity.
      }
      result = scale / (pow(q, 1 / shape));
      // K. Krishnamoorthy,  ISBN 1-58488-635-8 eq 23.1.3
      return result;
    } // quantile complement

    template <class RealType, class Policy>
    inline RealType mean(const pareto_distribution<RealType, Policy>& dist)
    {
      RealType result = 0;
      static const char* function = "boost::math::mean(const pareto_distribution<%1%>&, %1%)";
      if(false == detail::check_pareto(function, dist.scale(), dist.shape(), &result, Policy()))
      {
        return result;
      }
      if (dist.shape() > RealType(1))
      {
        return dist.shape() * dist.scale() / (dist.shape() - 1);
      }
      else
      {
        using boost::math::tools::max_value;
        return max_value<RealType>(); // +infinity.
      }
    } // mean

    template <class RealType, class Policy>
    inline RealType mode(const pareto_distribution<RealType, Policy>& dist)
    {
      return dist.scale();
    } // mode

    template <class RealType, class Policy>
    inline RealType median(const pareto_distribution<RealType, Policy>& dist)
    {
      RealType result = 0;
      static const char* function = "boost::math::median(const pareto_distribution<%1%>&, %1%)";
      if(false == detail::check_pareto(function, dist.scale(), dist.shape(), &result, Policy()))
      {
        return result;
      }
      BOOST_MATH_STD_USING
      return dist.scale() * pow(RealType(2), (1/dist.shape()));
    } // median

    template <class RealType, class Policy>
    inline RealType variance(const pareto_distribution<RealType, Policy>& dist)
    {
      RealType result = 0;
      RealType scale = dist.scale();
      RealType shape = dist.shape();
      static const char* function = "boost::math::variance(const pareto_distribution<%1%>&, %1%)";
      if(false == detail::check_pareto(function, scale, shape, &result, Policy()))
      {
        return result;
      }
      if (shape > 2)
      {
        result = (scale * scale * shape) /
         ((shape - 1) *  (shape - 1) * (shape - 2));
      }
      else
      {
        result = policies::raise_domain_error<RealType>(
          function,
          "variance is undefined for shape <= 2, but got %1%.", dist.shape(), Policy());
      }
      return result;
    } // variance

    template <class RealType, class Policy>
    inline RealType skewness(const pareto_distribution<RealType, Policy>& dist)
    {
      BOOST_MATH_STD_USING
      RealType result = 0;
      RealType shape = dist.shape();
      static const char* function = "boost::math::pdf(const pareto_distribution<%1%>&, %1%)";
      if(false == detail::check_pareto(function, dist.scale(), shape, &result, Policy()))
      {
        return result;
      }
      if (shape > 3)
      {
        result = sqrt((shape - 2) / shape) *
          2 * (shape + 1) /
          (shape - 3);
      }
      else
      {
        result = policies::raise_domain_error<RealType>(
          function,
          "skewness is undefined for shape <= 3, but got %1%.", dist.shape(), Policy());
      }
      return result;
    } // skewness

    template <class RealType, class Policy>
    inline RealType kurtosis(const pareto_distribution<RealType, Policy>& dist)
    {
      RealType result = 0;
      RealType shape = dist.shape();
      static const char* function = "boost::math::pdf(const pareto_distribution<%1%>&, %1%)";
      if(false == detail::check_pareto(function, dist.scale(), shape, &result, Policy()))
      {
        return result;
      }
      if (shape > 4)
      {
        result = 3 * ((shape - 2) * (3 * shape * shape + shape + 2)) /
          (shape * (shape - 3) * (shape - 4));
      }
      else
      {
        result = policies::raise_domain_error<RealType>(
          function,
          "kurtosis_excess is undefined for shape <= 4, but got %1%.", shape, Policy());
      }
      return result;
    } // kurtosis

    template <class RealType, class Policy>
    inline RealType kurtosis_excess(const pareto_distribution<RealType, Policy>& dist)
    {
      RealType result = 0;
      RealType shape = dist.shape();
      static const char* function = "boost::math::pdf(const pareto_distribution<%1%>&, %1%)";
      if(false == detail::check_pareto(function, dist.scale(), shape, &result, Policy()))
      {
        return result;
      }
      if (shape > 4)
      {
        result = 6 * ((shape * shape * shape) + (shape * shape) - 6 * shape - 2) /
          (shape * (shape - 3) * (shape - 4));
      }
      else
      {
        result = policies::raise_domain_error<RealType>(
          function,
          "kurtosis_excess is undefined for shape <= 4, but got %1%.", dist.shape(), Policy());
      }
      return result;
    } // kurtosis_excess

    } // namespace math
  } // namespace boost

  // This include must be at the end, *after* the accessors
  // for this distribution have been defined, in order to
  // keep compilers that support two-phase lookup happy.
#include <boost/math/distributions/detail/derived_accessors.hpp>

#endif // BOOST_STATS_PARETO_HPP


#include <cstdio>
#include <iostream>
#include <algorithm>
#include <string>
#include <vector>
#include <cmath>
#include <stack>
#include <set>
#include <map>
#include <boost/algorithm/string.hpp>
#define Reg register
#define gmax(_a, _b) ((_a) > (_b) ? (_a) : (_b))
#define gmin(_a, _b) ((_a) < (_b) ? (_a) : (_b))
#define cmax(_a, _b) (_a < (_b) ? _a = (_b) : 0)
#define cmin(_a, _b) (_a > (_b) ? _a = (_b) : 0)
#define FOR(_i,_a,_b) for (Reg int _i = (_a) ; _i <= (_b) ; _i++)
#define REP(_i,_a,_b) for (Reg int _i = (_a) ; _i >= (_b) ; _i--)
#define FOR_LETTER(_i) for (Reg int _i = 'A'; _i <= 'z'; _i != 'Z' ? (_i++) : (_i = 'a'))
#define ll long long
using namespace std;
const int maxn = 1000;
string BeginSym;
set <string> termin, non_termin;
map <string, set<string> > first, follow;
set <string> created_first, created_follow;
vector<string> boostsplit(const string & input);
struct Proce;
vector<Proce> proce;
map <string, vector<Proce> > proce_index;
map <string, map<string, Proce> > Table;
stack<string> Stack;
struct Proce
{
    string ori;
    string left;
    vector<string> right;
    Proce() {};
    Proce(const string & _p)
    {
        ori = _p;
        vector<string> syms = boostsplit(_p);
        left = syms[0];
        int l = syms.size();
        FOR(i, 2, l - 1)
            right.push_back(syms[i]);
        proce_index[left].push_back(*this);
    }
};
void init()
{
    proce.push_back(Proce("E -> T E'"));
    proce.push_back(Proce("E' -> + T E'"));
    proce.push_back(Proce("E' -> - T E'"));
    proce.push_back(Proce("E' -> epsilon"));
    proce.push_back(Proce("T -> F T'"));
    proce.push_back(Proce("T' -> * F T'"));
    proce.push_back(Proce("T' -> / F T'"));
    proce.push_back(Proce("T' -> epsilon"));
    proce.push_back(Proce("F -> ( E )"));
    proce.push_back(Proce("F -> num"));

    termin.insert("num");
    termin.insert("+");
    termin.insert("-");
    termin.insert("*");
    termin.insert("/");
    termin.insert("(");
    termin.insert(")");
    termin.insert("epsilon");
    termin.insert("$");

    non_termin.insert("E");
    non_termin.insert("E'");
    non_termin.insert("T");
    non_termin.insert("T'");
    non_termin.insert("F");

    BeginSym = "E";

}

vector<string> boostsplit(const std::string& input)
{
    std::vector <std::string> fields;
    boost::split(fields, input, boost::is_any_of(" "));
    return fields;
}

void CreateFirst(const string & sym)
{
    vector<Proce> & Pv = proce_index[sym];

    for (vector<Proce>::iterator it = Pv.begin(); it != Pv.end(); ++it)
    {
        Proce &P = (*it);
        string left = P.left;
        bool all_epsilon = true;
        for (vector<string>::iterator j = P.right.begin(); j != P.right.end(); j++)
        {

            if (termin.find(*j) != termin.end())
            {
                first[left].insert(*j);
                break;
            }

            if (created_first.find(*j) == created_first.end())
            {
                CreateFirst(*j);
            }

            first[left].insert(first[*j].begin(), first[*j].end());

            if (first[*j].find("epsilon") == first[*j].end())
            {
                all_epsilon = false;
                break;
            }
            else
                first[left].erase("epsilon");

            if (all_epsilon)
                first[left].insert("epsilon");
        }
    }
    created_first.insert(sym);
}

void CreateFollow()
{
    follow[BeginSym].insert("$");
    bool changed = true;
    while (changed)
    {
        changed = false;
        for (vector<Proce>::iterator it1 = proce.begin(); it1 != proce.end(); it1++)      //遍历所有产生式
        {
            Proce& P = *it1;
            for (vector<string>::iterator it2 = P.right.begin(); it2 != P.right.end(); it2++)
            {
                string X = *it2;
                int old_size = follow[X].size();
                bool all_epsilon = true;
                for (vector<string>::iterator it3 = it2 + 1; it3 != P.right.end() && all_epsilon; it3++)
                {
                    string b = *it3;
                    bool show_epsilon = false;
                    for (set<string>::iterator it4 = first[b].begin(); it4 != first[b].end(); it4++)
                    {
                        string a = *it4;
                        if (a != "epsilon")
                            follow[X].insert(a);
                        else
                            show_epsilon = true;
                    }
                    all_epsilon &= show_epsilon;
                }
                if (all_epsilon)
                    for (set<string>::iterator it5 = follow[P.left].begin(); it5 != follow[P.left].end(); it5++)
                        follow[X].insert(*it5);
                if (follow[X].size() > old_size)
                    changed = true;
            }
        }
    }
}

void CreateTable()
{
    for (vector<Proce>::iterator it1 = proce.begin(); it1 != proce.end(); it1++)
    {
        Proce& P = *it1;
        bool show_epsilon = false;
        for (set<string>::iterator it2 = first[P.right[0]].begin(); it2 != first[P.right[0]].end(); it2++)
        {
            string a = *it2;
            if (a != "epsilon")
                Table[P.left][a] = P;
            else
                show_epsilon = true;
        }
        if (show_epsilon)
            for (set<string>::iterator it3 = follow[P.left].begin(); it3 != follow[P.left].end(); it3++)
            {
                string b = *it3;
                Table[P.left][b] = P;
            }
    }
}

void error(string w, int pointer)
{
    cout << "出现错误！错误出现在字符串'" << w.substr(0, pointer) << "'结尾处" << endl;
}

void Prase(string w)
{
    w += "$";
    int pointer = 0;
    while (!Stack.empty())
        Stack.pop();
    Stack.push("$");
    Stack.push(BeginSym);
    while (!Stack.empty())
    {
        string X = Stack.top();
        string a = w.substr(pointer, 1);
        if (w[pointer] >= '0' && w[pointer] <= '9')
        {
            a = "num";
            while (w[pointer + 1] >= '0' && w[pointer + 1] <= '9') pointer++;
        }
        if (termin.find(X) != termin.end())
        {
            if (X == a)
            {
                Stack.pop();
                pointer++;
            }
            else
            {
                error(w, pointer);
                return;
            }

        }
        else
        {
            if (Table.find(X) != Table.end() && Table[X].find(a) != Table[X].end())
            {
                Stack.pop();
                Proce P = Table[X][a];
                int length = P.right.size();
                for (int i = length - 1; i >= 0; i--)
                {
                    string Y = P.right[i];
                    if (Y != "epsilon")
                        Stack.push(Y);
                }
                cout << P.ori << endl;
            }
            else
            {
                error(w, pointer);
                return;
            }
        }
    }
    cout << "分析完成！" << endl;
}

int main()
{
    init();
    cout << "First:" << endl;
    for (set<string>::iterator it = non_termin.begin(); it != non_termin.end(); it++)
    {
        CreateFirst(*it);
        cout << *it << ": ";
        for (set<string>::iterator j = first[*it].begin(); j != first[*it].end(); j++)
            cout << *j << " ";
        cout << endl;
    }
    for (set<string>::iterator it = termin.begin(); it != termin.end(); it++)
    {
        first[*it].insert(*it);
    }

    CreateFollow();
    cout << "Follow:" << endl;
    for (set<string>::iterator it = non_termin.begin(); it != non_termin.end(); it++)
    {
        cout << *it << ": ";
        for (set<string>::iterator j = follow[*it].begin(); j != follow[*it].end(); j++)
            cout << *j << " ";
        cout << endl;
    }

    CreateTable();

    for (set<string>::iterator it1 = non_termin.begin(); it1 != non_termin.end(); it1++)
    {
        string A = *it1;
        if (Table.find(A) != Table.end())
        {
            for (set<string>::iterator it2 = termin.begin(); it2 != termin.end(); it2++)
            {
                string a = *it2;
                if (Table[A].find(a) != Table[A].end())
                {
                    cout << "M[" << A << " , " << a << "] = " << Table[A][a].ori << endl;
                }
            }
        }

    }
    string input_string;
    while (1)
    {
        cin >> input_string;
        Prase(input_string);
    }

}


// fp_traits.hpp

#ifndef BOOST_SPIRIT_MATH_FP_TRAITS_HPP
#define BOOST_SPIRIT_MATH_FP_TRAITS_HPP

// Copyright (c) 2006 Johan Rade

// Distributed under 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)

#if defined(__vms) && defined(__DECCXX) && !__IEEE_FLOAT
#   error The VAX floating point mode on VMS is not supported.
#endif

#if defined(_MSC_VER)
#pragma once
#endif

#include <cstring>

#include <boost/assert.hpp>
#include <boost/cstdint.hpp>
#include <boost/detail/endian.hpp>
#include <boost/static_assert.hpp>
#include <boost/type_traits/is_floating_point.hpp>

//------------------------------------------------------------------------------

namespace boost {
namespace spirit {
namespace math {
namespace detail {

//------------------------------------------------------------------------------

/*
Most processors support three different floating point precisions:
single precision (32 bits), double precision (64 bits)
and extended double precision (>64 bits)

Note that the C++ type long double can be implemented
both as double precision and extended double precision.
*/

struct single_precision_tag {};
struct double_precision_tag {};
struct extended_double_precision_tag {};

//------------------------------------------------------------------------------

/*
template<class T, class U> struct fp_traits_impl;

  This is traits class that describes the binary structure of floating
  point numbers of C++ type T and precision U

Requirements:

  T = float, double or long double
  U = single_precision_tag, double_precision_tag
      or extended_double_precision_tag

Typedef members:

  bits -- the target type when copying the leading bytes of a floating
      point number. It is a typedef for uint32_t or uint64_t.

  coverage -- tells us whether all bytes are copied or not.
      It is a typedef for all_bits or not_all_bits.

Static data members:

  sign, exponent, flag, mantissa -- bit masks that give the meaning of the bits
      in the leading bytes.

Static function members:

  init() -- initializes the static data members, if needed.
            (Is a no-op in the specialized versions of the template.)

  get_bits(), set_bits() -- provide access to the leading bytes.
*/

struct all_bits {};
struct not_all_bits {};

// Generic version -------------------------------------------------------------

// The generic version uses run time initialization to determine the floating
// point format. It is capable of handling most formats,
// but not the Motorola 68K extended double precision format.

// Currently the generic version is used only for extended double precision
// on Itanium. In all other cases there are specializations of the template
// that use compile time initialization.

template<class T> struct uint32_t_coverage
{
    typedef not_all_bits type;
};

template<> struct uint32_t_coverage<single_precision_tag>
{
    typedef all_bits type;
};

template<class T, class U> struct fp_traits_impl
{
    typedef uint32_t bits;
    typedef BOOST_DEDUCED_TYPENAME uint32_t_coverage<U>::type coverage;

    BOOST_STATIC_CONSTANT(uint32_t, sign = 0x80000000);
    static uint32_t exponent;
    static uint32_t flag;
    static uint32_t mantissa;

    static void init()
    {
        if(is_init_) return;
        do_init_();
        is_init_ = true;
    }

    static void get_bits(T x, uint32_t& a)
    {
        memcpy(&a, reinterpret_cast<const unsigned char*>(&x) + offset_, 4);
    }

    static void set_bits(T& x, uint32_t a)
    {
        memcpy(reinterpret_cast<unsigned char*>(&x) + offset_, &a, 4);
    }

private:
    static size_t offset_;
    static bool is_init_;
    static void do_init_();
};

//..............................................................................

template<class T, class U> uint32_t fp_traits_impl<T,U>::exponent;
template<class T, class U> uint32_t fp_traits_impl<T,U>::flag;
template<class T, class U> uint32_t fp_traits_impl<T,U>::mantissa;
template<class T, class U> size_t   fp_traits_impl<T,U>::offset_;
template<class T, class U> bool     fp_traits_impl<T,U>::is_init_;

// In a single-threaded program, do_init will be called exactly once.
// In a multi-threaded program, do_init may be called simultaneously
// by more then one thread. That should not be a problem.

//..............................................................................

template<class T, class U> void fp_traits_impl<T,U>::do_init_()
{
    T x = static_cast<T>(3) / static_cast<T>(4);
    // sign bit = 0
    // exponent: first and last bit = 0, all other bits  = 1
    // flag bit (if present) = 1
    // mantissa: first bit = 1, all other bits = 0

    uint32_t a;

    for(size_t k = 0; k <= sizeof(T) - 4; ++k) {

        memcpy(&a, reinterpret_cast<unsigned char*>(&x) + k, 4);

        switch(a) {

        case 0x3f400000:      // IEEE single precision format

            offset_  = k;
            exponent = 0x7f800000;
            flag     = 0x00000000;
            mantissa = 0x007fffff;
            return;

        case 0x3fe80000:      // IEEE double precision format
                              // and PowerPC extended double precision format
            offset_  = k;
            exponent = 0x7ff00000;
            flag     = 0x00000000;
            mantissa = 0x000fffff;
            return;

        case 0x3ffe0000:      // Motorola extended double precision format

            // Must not get here. Must be handled by specialization.
            // To get accurate cutoff between normals and subnormals
            // we must use the flag bit that is in the 5th byte.
            // Otherwise this cutoff will be off by a factor 2.
            // If we do get here, then we have failed to detect the Motorola
            // processor at compile time.

            BOOST_ASSERT(false &&
                "Failed to detect the Motorola processor at compile time");
            return;

        case 0x3ffe8000:      // IEEE extended double precision format
                              // with 15 exponent bits
            offset_  = k;
            exponent = 0x7fff0000;
            flag     = 0x00000000;
            mantissa = 0x0000ffff;
            return;

        case 0x3ffec000:      // Intel extended double precision format

            offset_  = k;
            exponent = 0x7fff0000;
            flag     = 0x00008000;
            mantissa = 0x00007fff;
            return;

        default:
            continue;
        }
    }

    BOOST_ASSERT(false);

    // Unknown format.
}

// float (32 bits) -------------------------------------------------------------

template<> struct fp_traits_impl<float, single_precision_tag>
{
    typedef uint32_t bits;
    typedef all_bits coverage;

    BOOST_STATIC_CONSTANT(uint32_t, sign     = 0x80000000);
    BOOST_STATIC_CONSTANT(uint32_t, exponent = 0x7f800000);
    BOOST_STATIC_CONSTANT(uint32_t, flag     = 0x00000000);
    BOOST_STATIC_CONSTANT(uint32_t, mantissa = 0x007fffff);

    static void init() {}
    static void get_bits(float x, uint32_t& a) { memcpy(&a, &x, 4); }
    static void set_bits(float& x, uint32_t a) { memcpy(&x, &a, 4); }
};

// double (64 bits) ------------------------------------------------------------

#if defined(BOOST_NO_INT64_T) || defined(BOOST_NO_INCLASS_MEMBER_INITIALIZATION)

template<> struct fp_traits_impl<double, double_precision_tag>
{
    typedef uint32_t bits;
    typedef not_all_bits coverage;

    BOOST_STATIC_CONSTANT(uint32_t, sign     = 0x80000000);
    BOOST_STATIC_CONSTANT(uint32_t, exponent = 0x7ff00000);
    BOOST_STATIC_CONSTANT(uint32_t, flag     = 0);
    BOOST_STATIC_CONSTANT(uint32_t, mantissa = 0x000fffff);

    static void init() {}

    static void get_bits(double x, uint32_t& a)
    {
        memcpy(&a, reinterpret_cast<const unsigned char*>(&x) + offset_, 4);
    }

    static void set_bits(double& x, uint32_t a)
    {
        memcpy(reinterpret_cast<unsigned char*>(&x) + offset_, &a, 4);
    }

private:

#if defined(BOOST_BIG_ENDIAN)
    BOOST_STATIC_CONSTANT(int, offset_ = 0);
#elif defined(BOOST_LITTLE_ENDIAN)
    BOOST_STATIC_CONSTANT(int, offset_ = 4);
#else
    BOOST_STATIC_ASSERT(false);
#endif
};

//..............................................................................

#else

template<> struct fp_traits_impl<double, double_precision_tag>
{
    typedef uint64_t bits;
    typedef all_bits coverage;

    static const uint64_t sign     = (uint64_t)0x80000000 << 32;
    static const uint64_t exponent = (uint64_t)0x7ff00000 << 32;
    static const uint64_t flag     = 0;
    static const uint64_t mantissa
        = ((uint64_t)0x000fffff << 32) + (uint64_t)0xffffffff;

    static void init() {}
    static void get_bits(double x, uint64_t& a) { memcpy(&a, &x, 8); }
    static void set_bits(double& x, uint64_t a) { memcpy(&x, &a, 8); }
};

#endif

// long double (64 bits) -------------------------------------------------------

#if defined(BOOST_NO_INT64_T) || defined(BOOST_NO_INCLASS_MEMBER_INITIALIZATION)

template<> struct fp_traits_impl<long double, double_precision_tag>
{
    typedef uint32_t bits;
    typedef not_all_bits coverage;

    BOOST_STATIC_CONSTANT(uint32_t, sign     = 0x80000000);
    BOOST_STATIC_CONSTANT(uint32_t, exponent = 0x7ff00000);
    BOOST_STATIC_CONSTANT(uint32_t, flag     = 0);
    BOOST_STATIC_CONSTANT(uint32_t, mantissa = 0x000fffff);

    static void init() {}

    static void get_bits(long double x, uint32_t& a)
    {
        memcpy(&a, reinterpret_cast<const unsigned char*>(&x) + offset_, 4);
    }

    static void set_bits(long double& x, uint32_t a)
    {
        memcpy(reinterpret_cast<unsigned char*>(&x) + offset_, &a, 4);
    }

private:

#if defined(BOOST_BIG_ENDIAN)
    BOOST_STATIC_CONSTANT(int, offset_ = 0);
#elif defined(BOOST_LITTLE_ENDIAN)
    BOOST_STATIC_CONSTANT(int, offset_ = 4);
#else
    BOOST_STATIC_ASSERT(false);
#endif
};

//..............................................................................

#else

template<> struct fp_traits_impl<long double, double_precision_tag>
{
    typedef uint64_t bits;
    typedef all_bits coverage;

    static const uint64_t sign     = (uint64_t)0x80000000 << 32;
    static const uint64_t exponent = (uint64_t)0x7ff00000 << 32;
    static const uint64_t flag     = 0;
    static const uint64_t mantissa
        = ((uint64_t)0x000fffff << 32) + (uint64_t)0xffffffff;

    static void init() {}
    static void get_bits(long double x, uint64_t& a) { memcpy(&a, &x, 8); }
    static void set_bits(long double& x, uint64_t a) { memcpy(&x, &a, 8); }
};

#endif

// long double (>64 bits), x86 and x64 -----------------------------------------

#if defined(__i386) || defined(__i386__) || defined(_M_IX86) \
    || defined(__amd64) || defined(__amd64__)  || defined(_M_AMD64) \
    || defined(__x86_64) || defined(__x86_64__) || defined(_M_X64)

// Intel extended double precision format (80 bits)

template<> struct fp_traits_impl<long double, extended_double_precision_tag>
{
    typedef uint32_t bits;
    typedef not_all_bits coverage;

    BOOST_STATIC_CONSTANT(uint32_t, sign     = 0x80000000);
    BOOST_STATIC_CONSTANT(uint32_t, exponent = 0x7fff0000);
    BOOST_STATIC_CONSTANT(uint32_t, flag     = 0x00008000);
    BOOST_STATIC_CONSTANT(uint32_t, mantissa = 0x00007fff);

    static void init() {}

    static void get_bits(long double x, uint32_t& a)
    {
        memcpy(&a, reinterpret_cast<const unsigned char*>(&x) + 6, 4);
    }

    static void set_bits(long double& x, uint32_t a)
    {
        memcpy(reinterpret_cast<unsigned char*>(&x) + 6, &a, 4);
    }
};

// long double (>64 bits), Itanium ---------------------------------------------

#elif defined(__ia64) || defined(__ia64__) || defined(_M_IA64)

// The floating point format is unknown at compile time
// No template specialization is provided.
// The generic definition is used.

// The Itanium supports both
// the Intel extended double precision format (80 bits) and
// the IEEE extended double precision format with 15 exponent bits (128 bits).

// long double (>64 bits), PowerPC ---------------------------------------------

#elif defined(__powerpc) || defined(__powerpc__) || defined(__POWERPC__) \
    || defined(__ppc) || defined(__ppc__) || defined(__PPC__)

// PowerPC extended double precision format (128 bits)

template<> struct fp_traits_impl<long double, extended_double_precision_tag>
{
    typedef uint32_t bits;
    typedef not_all_bits coverage;

    BOOST_STATIC_CONSTANT(uint32_t, sign     = 0x80000000);
    BOOST_STATIC_CONSTANT(uint32_t, exponent = 0x7ff00000);
    BOOST_STATIC_CONSTANT(uint32_t, flag     = 0x00000000);
    BOOST_STATIC_CONSTANT(uint32_t, mantissa = 0x000fffff);

    static void init() {}

    static void get_bits(long double x, uint32_t& a)
    {
        memcpy(&a, reinterpret_cast<const unsigned char*>(&x) + offset_, 4);
    }

    static void set_bits(long double& x, uint32_t a)
    {
        memcpy(reinterpret_cast<unsigned char*>(&x) + offset_, &a, 4);
    }

private:

#if defined(BOOST_BIG_ENDIAN)
    BOOST_STATIC_CONSTANT(int, offset_ = 0);
#elif defined(BOOST_LITTLE_ENDIAN)
    BOOST_STATIC_CONSTANT(int, offset_ = 12);
#else
    BOOST_STATIC_ASSERT(false);
#endif
};

// long double (>64 bits), Motorola 68K ----------------------------------------

#elif defined(__m68k) || defined(__m68k__) \
    || defined(__mc68000) || defined(__mc68000__) \

// Motorola extended double precision format (96 bits)

// It is the same format as the Intel extended double precision format,
// except that 1) it is big-endian, 2) the 3rd and 4th byte are padding, and
// 3) the flag bit is not set for infinity

template<> struct fp_traits_impl<long double, extended_double_precision_tag>
{
    typedef uint32_t bits;
    typedef not_all_bits coverage;

    BOOST_STATIC_CONSTANT(uint32_t, sign     = 0x80000000);
    BOOST_STATIC_CONSTANT(uint32_t, exponent = 0x7fff0000);
    BOOST_STATIC_CONSTANT(uint32_t, flag     = 0x00008000);
    BOOST_STATIC_CONSTANT(uint32_t, mantissa = 0x00007fff);

    static void init() {}

    // copy 1st, 2nd, 5th and 6th byte. 3rd and 4th byte are padding.

    static void get_bits(long double x, uint32_t& a)
    {
        memcpy(&a, &x, 2);
        memcpy(reinterpret_cast<unsigned char*>(&a) + 2,
               reinterpret_cast<const unsigned char*>(&x) + 4, 2);
    }

    static void set_bits(long double& x, uint32_t a)
    {
        memcpy(&x, &a, 2);
        memcpy(reinterpret_cast<unsigned char*>(&x) + 4,
               reinterpret_cast<const unsigned char*>(&a) + 2, 2);
    }
};

// long double (>64 bits), All other processors --------------------------------

#else

// IEEE extended double precision format with 15 exponent bits (128 bits)

template<> struct fp_traits_impl<long double, extended_double_precision_tag>
{
    typedef uint32_t bits;
    typedef not_all_bits coverage;

    BOOST_STATIC_CONSTANT(uint32_t, sign     = 0x80000000);
    BOOST_STATIC_CONSTANT(uint32_t, exponent = 0x7fff0000);
    BOOST_STATIC_CONSTANT(uint32_t, flag     = 0x00000000);
    BOOST_STATIC_CONSTANT(uint32_t, mantissa = 0x0000ffff);

    static void init() {}

    static void get_bits(long double x, uint32_t& a)
    {
        memcpy(&a, reinterpret_cast<const unsigned char*>(&x) + offset_, 4);
    }

    static void set_bits(long double& x, uint32_t a)
    {
        memcpy(reinterpret_cast<unsigned char*>(&x) + offset_, &a, 4);
    }

private:

#if defined(BOOST_BIG_ENDIAN)
    BOOST_STATIC_CONSTANT(int, offset_ = 0);
#elif defined(BOOST_LITTLE_ENDIAN)
    BOOST_STATIC_CONSTANT(int, offset_ = 12);
#else
    BOOST_STATIC_ASSERT(false);
#endif
};

#endif

//------------------------------------------------------------------------------

// size_to_precision is a type switch for converting a C++ floating point type
// to the corresponding precision type.

template<int n> struct size_to_precision;

template<> struct size_to_precision<4>
{
    typedef single_precision_tag type;
};

template<> struct size_to_precision<8>
{
    typedef double_precision_tag type;
};

template<> struct size_to_precision<10>
{
    typedef extended_double_precision_tag type;
};

template<> struct size_to_precision<12>
{
    typedef extended_double_precision_tag type;
};

template<> struct size_to_precision<16>
{
    typedef extended_double_precision_tag type;
};

// fp_traits is a type switch that selects the right fp_traits_impl

template<class T> struct fp_traits
{
    BOOST_STATIC_ASSERT(boost::is_floating_point<T>::value);
    typedef BOOST_DEDUCED_TYPENAME size_to_precision<sizeof(T)>::type precision;
    typedef fp_traits_impl<T, precision> type;
};

//------------------------------------------------------------------------------

}   // namespace detail
}   // namespace math
}   // namespace spirit
}   // namespace boost

#endif


// Software License for MTL
//
// Copyright (c) 2007 The Trustees of Indiana University.
//               2008 Dresden University of Technology and the Trustees of Indiana University.
//               2010 SimuNova UG (haftungsbeschränkt), www.simunova.com.
// All rights reserved.
// Authors: Peter Gottschling and Andrew Lumsdaine
//
// This file is part of the Matrix Template Library
//
// See also license.mtl.txt in the distribution.

#include <iostream>
#include <cmath>
#include <string>
#include <vector>
#include <boost/timer.hpp>

#include <boost/numeric/mtl/matrix/parameter.hpp>
#include <boost/numeric/mtl/matrix/dense2D.hpp>
#include <boost/numeric/mtl/matrix/morton_dense.hpp>
#include <boost/numeric/mtl/operation/print_matrix.hpp>
#include <boost/numeric/mtl/operation/cholesky.hpp>
#include <boost/numeric/mtl/operation/matrix_mult.hpp>
#include <boost/numeric/mtl/matrix/hessian_setup.hpp>
#include <boost/numeric/mtl/operation/assign_mode.hpp>
#include <boost/numeric/mtl/utility/papi.hpp>

using namespace mtl;
using namespace mtl::recursion;
using namespace std;

// Maximum time for a single measurement
// is 5 min
const double max_time= 300;

    // Bitmasks:
    const unsigned long morton_mask= generate_mask<true, 0, row_major, 0>::value,
	morton_z_mask= generate_mask<false, 0, row_major, 0>::value,
	doppled_16_row_mask= generate_mask<true, 4, row_major, 0>::value,
	doppled_16_col_mask= generate_mask<true, 4, col_major, 0>::value,
	doppled_32_row_mask= generate_mask<true, 5, row_major, 0>::value,
	doppled_32_col_mask= generate_mask<true, 5, col_major, 0>::value,
	doppled_z_32_row_mask= generate_mask<false, 5, row_major, 0>::value,
	doppled_z_32_col_mask= generate_mask<false, 5, col_major, 0>::value,
	doppled_64_row_mask= generate_mask<true, 6, row_major, 0>::value,
	doppled_64_col_mask= generate_mask<true, 6, col_major, 0>::value,
	doppled_z_64_row_mask= generate_mask<false, 6, row_major, 0>::value,
	doppled_z_64_col_mask= generate_mask<false, 6, col_major, 0>::value,
	doppled_128_row_mask= generate_mask<true, 7, row_major, 0>::value,
	doppled_128_col_mask= generate_mask<true, 7, col_major, 0>::value,
	shark_32_row_mask= generate_mask<true, 5, row_major, 1>::value,
	shark_32_col_mask= generate_mask<true, 5, col_major, 1>::value,
	shark_z_32_row_mask= generate_mask<false, 5, row_major, 1>::value,
	shark_z_32_col_mask= generate_mask<false, 5, col_major, 1>::value,
	shark_64_row_mask= generate_mask<true, 6, row_major, 1>::value,
	shark_64_col_mask= generate_mask<true, 6, col_major, 1>::value,
	shark_z_64_row_mask= generate_mask<false, 6, row_major, 1>::value,
	shark_z_64_col_mask= generate_mask<false, 6, col_major, 1>::value;

typedef assign::plus_sum                            ama_t;

typedef recursion::bound_test_static<32>                    test32_t;
typedef recursion::bound_test_static<64>                    test64_t;

typedef gen_dense_mat_mat_mult_t<assign::plus_sum>  base_mult_t;
typedef gen_recursive_dense_mat_mat_mult_t<base_mult_t>     rec_mult_t;

typedef gen_tiling_22_dense_mat_mat_mult_t<assign::plus_sum>  tiling_22_base_mult_t;
typedef gen_tiling_44_dense_mat_mat_mult_t<assign::plus_sum>  tiling_44_base_mult_t;

utility::papi_t papi;
int l1i= papi.add_event("PAPI_L1_DCM");
int l2i= papi.add_event("PAPI_L2_DCM");
int tlbi= papi.add_event("PAPI_TLB_DM");

// ugly short cuts
typedef dense2D<double>                                       dr_t;
typedef dense2D<double, mat::parameters<col_major> >        dc_t;

#ifdef MTL_HAS_LAPACK
extern "C" {
  void dpotrf_(char* uplo, int*, double*, int*, int*);
}
#endif

struct dpotrf_t
{
    void operator()(dc_t& a)
    {
#ifdef MTL_HAS_LAPACK
	int size= a.num_rows(), info;
	dpotrf_("U", &size, &a[0][0], &size, &info);
#endif
    }
};

void print_time_and_mflops(double time, double size)
{
    // time and MFlops of single measure
    std::cout << time << ", " << 0.333333333333333333 * size * size * size / time / 1e6f << ", ";
}

// Matrices are only placeholder to provide the type
template <typename Visitor, typename Matrix>
void single_measure(Matrix&, Visitor visitor, unsigned size, std::vector<int>& enabled, int i)
{
    Matrix matrix(size, size);
    fill_matrix_for_cholesky(matrix);

    if (enabled[i]) {
	int reps= 0;
	boost::timer start;
	papi.reset();
	for (; start.elapsed() < 5; reps++)
	    recursive_cholesky(matrix, visitor);
	papi.read();
	double time= start.elapsed() / double(reps);
	print_time_and_mflops(time, size);
	std::cout << papi[l1i]/reps << ", " << papi[l2i]/reps << ", " << papi[tlbi]/reps << ", ";
	if (time > max_time)
	    enabled[i]= 0;
    } else
	std::cout << ", , , , , ";
}

// Matrices are only placeholder to provide the type
template <typename Matrix, typename Functor>
void single_measure_dpotrf(Matrix&, Functor fcholesky, unsigned size, std::vector<int>& enabled, int i)
{
    Matrix matrix(size, size);
    fill_matrix_for_cholesky(matrix);

    if (enabled[i]) {
	int reps= 0;
	boost::timer start;
	papi.reset();
	for (; start.elapsed() < 5; reps++)
	    fcholesky(matrix);
	papi.read();
	double time= start.elapsed() / double(reps);
	print_time_and_mflops(time, size);
	std::cout << papi[l1i]/reps << ", " << papi[l2i]/reps << ", " << papi[tlbi]/reps << ", ";
	if (time > max_time)
	    enabled[i]= 0;
    } else
	std::cout << ", , , , , ";
}

template <typename Visitor>
void measure(unsigned size, std::vector<int>& enabled)
{

    // The matrices in the following functions are only place holders, the real matrices are used in single_measure
    morton_dense<double,  morton_mask>             md(4, 4);
    morton_dense<double,  morton_z_mask>           mzd(4, 4);
    dense2D<double>                                dr(4, 4);
    dense2D<double, mat::parameters<col_major> > dc(4, 4);
    morton_dense<double,  doppled_32_row_mask>     d32r(4, 4);
    morton_dense<double,  doppled_64_row_mask>     d64r(4, 4);

    std::cout << size << ", ";

    Visitor visitor;
    single_measure(md, visitor, size, enabled, 0);
    single_measure(mzd, visitor, size, enabled, 1);
    single_measure(dr, visitor, size, enabled, 2);
    single_measure(dc, visitor, size, enabled, 3);
    single_measure(d32r, visitor, size, enabled, 4);
    single_measure(d64r, visitor, size, enabled, 5);

    single_measure_dpotrf(dc, dpotrf_t(), size, enabled, 6);

    std::cout << "0\n"; // to not finish with comma
    std::cout.flush();
}

template <typename Measure>
void series(unsigned steps, unsigned max_size, Measure measure, const string& comment)
{
    std::cout << "# " << comment << '\n';
    std::cout << "# Gnu-Format size, time, MFlops, ...\n";
    std::cout << "# N-order, Z-order, row, col, 32 row, 64 row\n"; std::cout.flush();

    std::vector<int> enabled(16, 1);
    for (unsigned i= steps; i <= max_size; i+= steps)
	measure(i, enabled);
}

int main(int argc, char* argv[])
{
    papi.start();

    std::vector<std::string> scenarii;
    scenarii.push_back(string("Comparing canonical implementation with different matrix types"));
    scenarii.push_back(string("Comparing iterator implementation with different matrix types"));
    scenarii.push_back(string("Comparing prev. using fast Schur update (2x2 tiling) with different matrix types"));
    scenarii.push_back(string("Comparing prev. using fast Schur update (4x4 tiling) with different matrix types"));

    using std::cout;
    if (argc < 4) {
	cerr << "usage: recursive_mult_timing <scenario> <steps> <max_size>\nScenarii:\n";
	for (unsigned i= 0; i < scenarii.size(); i++)
	    cout << i << ": " << scenarii[i] << "\n";
	exit(1);
    }
    unsigned int scenario= atoi(argv[1]), steps= atoi(argv[2]), max_size= atoi(argv[3]), size= 32;

    typedef with_bracket::recursive_cholesky_base_visitor_t     bracket_t;
    typedef with_iterator::recursive_cholesky_base_visitor_t    iterator_t;

    typedef detail::mult_schur_update_t<gen_tiling_22_dense_mat_mat_mult_t<assign::minus_sum> > schur_update_22_t;
    typedef recursive_cholesky_visitor_t<recursion::bound_test_static<64>, with_iterator::cholesky_base_t, with_iterator::tri_solve_base_t,
	                                 with_iterator::tri_schur_base_t, schur_update_22_t>
	tiling_22_t;

    typedef detail::mult_schur_update_t<gen_tiling_44_dense_mat_mat_mult_t<assign::minus_sum> > schur_update_44_t;
    typedef recursive_cholesky_visitor_t<recursion::bound_test_static<64>, with_iterator::cholesky_base_t, with_iterator::tri_solve_base_t,
	                                 with_iterator::tri_schur_base_t, schur_update_44_t>
	tiling_44_t;

    switch (scenario) {
      case 0: 	series(steps, max_size, measure<bracket_t>, scenarii[0]); break;
      case 1: 	series(steps, max_size, measure<iterator_t>, scenarii[1]); break;
      case 2: 	series(steps, max_size, measure<tiling_22_t>, scenarii[2]); break;
      case 3: 	series(steps, max_size, measure<tiling_44_t>, scenarii[3]); break;
    }

    return 0;

}

// Compile command:
// g++4 cholesky_timing.cpp -o cholesky_timing -O3 -DNDEBUG -ffast-math  -mcpu=opteron -mtune=opteron -msse2 -mfpmath=sse  -I${MTL_BOOST_ROOT} -I${BOOST_ROOT} -I/usr/local/include -L/usr/local/lib -lpapi -DMTL_HAS_PAPI -DMTL_HAS_BLAS -DMTL_HAS_LAPACK -L/u/htor/projekte/mathlibs/acml-2-6-0-gnu-64bit/gnu64/lib -lacml -L/usr/lib/gcc/x86_64-redhat-linux/3.4.3 -lg2c