// Boost.Units - A C++ library for zero-overhead dimensional analysis and
// unit/quantity manipulation and conversion
//
// Copyright (C) 2003-2008 Matthias Christian Schabel
// Copyright (C) 2008 Steven Watanabe
//
// 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)

// $Id: test_lambda.cpp 27 2008-06-16 14:50:58Z maehne $

////////////////////////////////////////////////////////////////////////
///
/// \file test_lambda.hpp
///
/// \brief Unit test for checking the usage of Boost.Units' quantity,
///        unit, and absolute types in functors created with the
///        Boost.Lambda library.
///
/// \author Torsten Maehne
/// \date   2008-06-16
///
/// This unit test contains a check for each operator action, for
/// which a specialization of Boost.Lambda's return type deduction
/// system is made in lambda.hpp, i.e., for the operators defined for
/// Boost.Units' quantity, unit, and absolute types.
///
////////////////////////////////////////////////////////////////////////

#include <boost/function.hpp>
#include <boost/units/lambda.hpp>
#include <boost/units/absolute.hpp>
#include <boost/units/systems/si/temperature.hpp>
#include "test_header.hpp"

namespace bl = boost::lambda;
namespace bu = boost::units;
namespace si = boost::units::si;

int test_main(int, char *[])
{

    ////////////////////////////////////////////////////////////////////////
    // Test for Boost.Lambda working with overloaded operators defined
    // in <boost/units/quantity.hpp>
    ////////////////////////////////////////////////////////////////////////

    bu::quantity<bu::length> lvar = 0.0 * bu::meter;

    bu::quantity<bu::dimensionless> dlvar = 3.0;

    // quantity<Unit, Y> += quantity<Unit2, YY>
    boost::function<bu::quantity<bu::length> (bu::quantity<bu::length>)>
        f = (bl::var(lvar) += bl::_1);

    lvar = 1.0 * bu::meter;
    BOOST_CHECK((f(2.0 * bu::meter) == 3.0 * bu::meter));
    BOOST_CHECK((f(6.0 * bu::meter) == 9.0 * bu::meter));

    // quantity<BOOST_UNITS_DIMENSIONLESS_UNIT(System), Y> += quantity<BOOST_UNITS_DIMENSIONLESS_UNIT(System), Y>
    dlvar = 4.0;
    BOOST_CHECK(((bl::var(dlvar) += bl::_1)(3.0) == 7.0));

    // quantity<Unit, Y> -= quantity<Unit2, YY>
    lvar = 3.0 * bu::meter;
    BOOST_CHECK((f(-2.0 * bu::meter) == 1.0 * bu::meter));
    BOOST_CHECK((f(6.0 * bu::meter) == 7.0 * bu::meter));

    // quantity<BOOST_UNITS_DIMENSIONLESS_UNIT(System), Y> -= quantity<BOOST_UNITS_DIMENSIONLESS_UNIT(System), Y>
    dlvar = 4.0;
    BOOST_CHECK(((bl::var(dlvar) -= bl::_1)(3.0) == 1.0));

    // quantity<Unit, Y> *= quantity<Unit2, YY>
    dlvar = 2.0;
    BOOST_CHECK(((bl::var(dlvar) *= bl::_1)(3.0) == 6.0));

    // quantity<Unit, Y> /= quantity<Unit2, YY>
    dlvar = 6.0;
    BOOST_CHECK(((bl::var(dlvar) /= bl::_1)(3.0) == 2.0));

    // quantity<Unit, Y> *= Y
    lvar = 3.0 * bu::meter;
    BOOST_CHECK(((bl::var(lvar) *= bl::_1)(2.0) == 6.0 * bu::meter));

    // quantity<Unit, Y> /= Y
    lvar = 6.0 * bu::meter;
    BOOST_CHECK(((bl::var(lvar) /= bl::_1)(3.0) == 2.0 * bu::meter));

    // unit<Dim, System> * Y
    BOOST_CHECK(((bl::_1 * bl::_2)(bu::meter, 2.0) == 2.0 * bu::meter));
    BOOST_CHECK(((bu::meter * bl::_1)(2.0) == 2.0 * bu::meter));

    // unit<Dim, System> / Y
    BOOST_CHECK(((bl::_1 / bl::_2)(bu::meter, 0.5) == 2.0 * bu::meter));
    BOOST_CHECK(((bu::meter / bl::_1)(0.5 * bu::second) == 2.0 * bu::meter_per_second));

    // Y * unit<Dim, System>
    BOOST_CHECK(((bl::_1 * bl::_2)(2.0, bu::meter) == 2.0 * bu::meter));
    BOOST_CHECK(((bl::_1 * bu::meter)(2.0 / bu::second) == 2.0 * bu::meter_per_second));

    // Y / unit<Dim, System>
    BOOST_CHECK(((bl::_1 / bl::_2)(3.5, bu::second) == 3.5 / bu::second));
    BOOST_CHECK(((bl::_1 / bu::second)(3.5 * bu::meter) == 3.5 * bu::meter_per_second));

    // quantity<Unit, X> * X
    BOOST_CHECK(((bl::_1 * bl::_2)(2.0, 3.0 * bu::meter) == 6.0 * bu::meter));

    // X * quantity<Unit, X>
    BOOST_CHECK(((bl::_1 * bl::_2)(4.0 * bu::joule, 2.0) == 8.0 * bu::joule));

    // quantity<Unit, X> / X
    BOOST_CHECK(((bl::_1 / bl::_2)(4.0 * bu::joule, 2.0) == 2.0 * bu::joule));

    // X / quantity<Unit, X>
    BOOST_CHECK(((3.0 / bl::_1)(2.0 * bu::second) == 1.5 / bu::second));

    // unit<Dim1, System1> * quantity<Unit2, Y>
    BOOST_CHECK(((bl::_1 * bl::_2)(bu::meter, 12.0 / bu::second) == 12.0 * bu::meter_per_second));
    BOOST_CHECK(((bu::meter * bl::_1)(12.0 / bu::second) == 12.0 * bu::meter_per_second));

    // unit<Dim1, System1> / quantity<Unit2, Y>
    BOOST_CHECK(((bl::_1 / bl::_2)(bu::meter, 0.5 * bu::second) == 2.0 * bu::meter_per_second));
    BOOST_CHECK(((bu::meter / bl::_1)(0.25 * bu::second) == 4.0 * bu::meter_per_second));

    // quantity<Unit1, Y> * unit<Dim2, System2>
    BOOST_CHECK(((bl::_1 * bl::_2)(2.0 / bu::second, bu::meter) == 2.0 * bu::meter_per_second));
    BOOST_CHECK(((bl::_1 * bu::meter)(12.0 / bu::second) == 12.0 * bu::meter_per_second));

    // quantity<Unit1, Y> / unit<Dim2, System2>
    BOOST_CHECK(((bl::_1 / bl::_2)(3.5 * bu::meter, bu::second) == 3.5 * bu::meter_per_second));
    BOOST_CHECK(((bl::_1 / bu::second)(5.0 * bu::second) == 5.0));

    // +quantity<Unit, Y>
    BOOST_CHECK(((+bl::_1)(5.0 * bu::second) == 5.0 * bu::second));

    // -quantity<Unit, Y>
    BOOST_CHECK(((-bl::_1)(5.0 * bu::second) == -5.0 * bu::second));

    // quantity<Unit1, X> + quantity<Unit2, Y>
    BOOST_CHECK(((bl::_1 + bl::_2)(2.0 * bu::meter, 4.0 * bu::meter) == 6.0 * bu::meter));

    // quantity<Unit1, X> - quantity<Unit2, Y>
    BOOST_CHECK(((bl::_1 - bl::_2)(2.0 * bu::meter, 4.0 * bu::meter) == -2.0 * bu::meter));

    // quantity<Unit1, X> * quantity<Unit2, Y>
    BOOST_CHECK(((bl::_1 * bl::_2)(2.0 * bu::kilogram, 4.0 * bu::meter_per_second) == 8.0 * bu::kilogram * bu::meter_per_second));

    // quantity<Unit1, X> / quantity<Unit2, Y>
    BOOST_CHECK(((bl::_1 / bl::_2)(2.0 * bu::meter_per_second, 4.0 * bu::meter_per_second) == 0.5));

    // quantity<Unit, X> == quantity<Unit, Y>
    BOOST_CHECK(((bl::_1 == bl::_2)(2.0 * bu::meter, 2.0 * bu::meter) == true));
    BOOST_CHECK(((bl::_1 == bl::_2)(2.0 * bu::meter, 3.0 * bu::meter) == false));

    // quantity<Unit, X> != quantity<Unit, Y>
    BOOST_CHECK(((bl::_1 != bl::_2)(2.0 * bu::meter, 2.0 * bu::meter) == false));
    BOOST_CHECK(((bl::_1 != bl::_2)(2.0 * bu::meter, 3.0 * bu::meter) == true));

    // quantity<Unit, X> < quantity<Unit, Y>
    BOOST_CHECK(((bl::_1 < bl::_2)(2.0 * bu::meter, 2.0 * bu::meter) == false));
    BOOST_CHECK(((bl::_1 < bl::_2)(2.0 * bu::meter, 3.0 * bu::meter) == true));

    // quantity<Unit, X> <= quantity<Unit, Y>
    BOOST_CHECK(((bl::_1 <= bl::_2)(2.0 * bu::meter, 2.0 * bu::meter) == true));
    BOOST_CHECK(((bl::_1 <= bl::_2)(2.0 * bu::meter, 3.0 * bu::meter) == true));
    BOOST_CHECK(((bl::_1 <= bl::_2)(4.0 * bu::meter, 3.0 * bu::meter) == false));

    // quantity<Unit, X> > quantity<Unit, Y>
    BOOST_CHECK(((bl::_1 > bl::_2)(2.0 * bu::meter, 2.0 * bu::meter) == false));
    BOOST_CHECK(((bl::_1 > bl::_2)(2.0 * bu::meter, 3.0 * bu::meter) == false));
    BOOST_CHECK(((bl::_1 > bl::_2)(4.0 * bu::meter, 3.0 * bu::meter) == true));

    // quantity<Unit, X> >= quantity<Unit, Y>
    BOOST_CHECK(((bl::_1 >= bl::_2)(2.0 * bu::meter, 2.0 * bu::meter) == true));
    BOOST_CHECK(((bl::_1 >= bl::_2)(2.0 * bu::meter, 3.0 * bu::meter) == false));
    BOOST_CHECK(((bl::_1 >= bl::_2)(4.0 * bu::meter, 3.0 * bu::meter) == true));

    ////////////////////////////////////////////////////////////////////////
    // Test for Boost.Lambda working with overloaded operators defined
    // in <boost/units/unit.hpp>
    ////////////////////////////////////////////////////////////////////////

    // +unit<Dim, System>
    BOOST_CHECK(((+bl::_1)(bu::meter) == bu::meter));

    // -unit<Dim, System>
    BOOST_CHECK(((-bl::_1)(bu::meter) == bu::meter));

    // unit<Dim1, System1> + unit<Dim2, System2>
    BOOST_CHECK(((bl::_1 + bu::meter)(bu::meter) == bu::meter));
    BOOST_CHECK(((bu::meter + bl::_1)(bu::meter) == bu::meter));
    BOOST_CHECK(((bl::_1 + bl::_2)(bu::meter, bu::meter) == bu::meter));

    // unit<Dim1, System1> - unit<Dim2, System2>
    BOOST_CHECK(((bl::_1 - bl::_2)(bu::meter, bu::meter) == bu::meter));
    BOOST_CHECK(((bl::_1 - bu::meter)(bu::meter) == bu::meter));
    BOOST_CHECK(((bu::meter - bl::_1)(bu::meter) == bu::meter));

    // unit<Dim1, System1> * unit<Dim2, System2>
    BOOST_CHECK(((bl::_1 * bl::_2)(bu::meter, bu::meter) == bu::meter * bu::meter));
    BOOST_CHECK(((bl::_1 * bu::meter)(bu::meter) == bu::meter * bu::meter));

    // unit<Dim1, System1> / unit<Dim2, System2>
    BOOST_CHECK(((bl::_1 / bl::_2)(bu::meter, bu::second) == bu::meter_per_second));
    BOOST_CHECK(((bl::_1 / bu::second)(bu::meter) == bu::meter_per_second));

    // unit<Dim1, System1> == unit<Dim2, System2>
    BOOST_CHECK(((bl::_1 == bu::meter)(bu::meter) == true));
    BOOST_CHECK(((bl::_1 == bu::meter)(bu::second) == false));

    // unit<Dim1, System1> != unit<Dim2, System2>
    BOOST_CHECK(((bl::_1 != bu::meter)(bu::meter) == false));
    BOOST_CHECK(((bl::_1 != bu::meter)(bu::second) == true));

    ////////////////////////////////////////////////////////////////////////
    // Test for Boost.Lambda working with overloaded operators defined
    // in <boost/units/absolute.hpp>
    ////////////////////////////////////////////////////////////////////////

    // absolute<Y> += Y
    bu::quantity<bu::absolute<si::temperature> > Ta = 270.0 * bu::absolute<si::temperature>();
    (Ta += bl::_1)(30.0 * si::kelvin);
    BOOST_CHECK(( Ta == 300.0 * bu::absolute<si::temperature>()));

    // absolute<Y> -= Y
    Ta = 270 * bu::absolute<si::temperature>();
    (Ta -= bl::_1)(-30.0 * si::kelvin);
    BOOST_CHECK(( Ta == 300.0 * bu::absolute<si::temperature>()));

    // absolute<Y> + Y
    BOOST_CHECK(((270.0 * bu::absolute<si::temperature>() + bl::_1)(30.0 * si::kelvin) == 300.0 * bu::absolute<si::temperature>()));

    // Y + absolute<Y>
    BOOST_CHECK(((bl::_1 + 270.0 * bu::absolute<si::temperature>())(30.0 * si::kelvin) == 300.0 * bu::absolute<si::temperature>()));

    // absolute<Y> - Y
    BOOST_CHECK(((270.0 * bu::absolute<si::temperature>() - bl::_1)(30.0 * si::kelvin) == 240.0 * bu::absolute<si::temperature>()));

    // absolute<Y> - absolute<Y>
    BOOST_CHECK(((bl::_1 - 270.0 * bu::absolute<si::temperature>())(300.0 * bu::absolute<si::temperature>()) == 30.0 * si::kelvin));

    // T * absolute<unit<D, S> >
    BOOST_CHECK(((bl::_1 * bu::absolute<si::temperature>())(300.0) == 300.0 * bu::absolute<si::temperature>()));
    BOOST_CHECK(((bl::_1 * bl::_2)(300.0, bu::absolute<si::temperature>()) == 300.0 * bu::absolute<si::temperature>()));

    // absolute<unit<D, S> > * T
    BOOST_CHECK(((bu::absolute<si::temperature>() * bl::_1)(300.0) == 300.0 * bu::absolute<si::temperature>()));
    BOOST_CHECK(((bl::_1 * bl::_2)(bu::absolute<si::temperature>(), 300.0) == 300.0 * bu::absolute<si::temperature>()));

    return 0;
}


#include "../Headers/spc.h"
#include "../Headers/vedge.h"
#include "../headers/claim.h"
#include <boost/property_map/transform_value_property_map.hpp>
#include <boost/config.hpp>
#include <boost/graph/adjacency_list.hpp>
#include <boost/graph/dijkstra_shortest_paths.hpp>
#include <boost/graph/graph_traits.hpp>
#include <boost/property_map/property_map.hpp>
#include <boost/range/algorithm.hpp>
#include <boost/range/adaptors.hpp>
#include <fstream>
#include <iostream>
#include "../Headers/controller.h"

using namespace FlowAlgorithms;
using namespace boost;
using namespace Flow;
using namespace boost::adaptors;

SPC::SPC(){}

SPC::SPC(std::set<VEdge*> edges) {
    int x = 0;
    for(VEdge* e : edges) {
        kInputEdges.push_back({ e->kId, e->kStart->get_id(), e->kEnd->get_id(), (double)e->kCost});
        // Save the nodes in a hashmaph for later use!
        kHashMap.insert({e->kStart->get_id(), e->kStart});
        kHashMap.insert({e->kEnd->get_id(), e->kEnd});
    }
}

SPC::~SPC(){}

/**
* Start running the SPC paper
*/
void SPC::start() {
    claim("S/start: starting SPC", kDebug);

    int num_steps = 5;
    vector<VEdgeWrapper> subset;

    for(int step = ONE; step != SIX; step++) {
        claim("Step " + to_string(step) + " of " + to_string(SIX), kDebug);
        switch(step) {
            // generate Dijkstra from subset of InputEdges (G1)
            case ONE:
                run_dijkstra(kInputEdges);
                break;
                // generate Kruskal from Dijkstra outputs (G2)
            case TWO:
                run_kruskal();
                break;
                // noop
            default:
            case ZERO:

                break;
        }
    }
}

/**
* Run the boost version of kruskal.  This utilizes the output from the dijkstra's method as input
*/
void SPC::run_kruskal() {
    claim("S/run_kruskal: starting boost/kruskal", kDebug);

    /**
    * build the typdefs required for boost documentation
    */
    typedef adjacency_list<vecS, vecS, undirectedS, no_property,
            property<edge_weight_t, int> > GAGraph;

    typedef graph_traits<GAGraph>::edge_descriptor GAEdge;
    typedef graph_traits<GAGraph>::vertex_descriptor GAVertex;

    // generate a graph, allocating the adjancency_list of inputedges sizes.
    GAGraph g(kDijkstraEdges.size());

    // build the spanning_tree vector in which will hold the answer
    boost::property_map<GAGraph, boost::edge_weight_t>::type weightmap = boost::get(boost::edge_weight, g);
    std::vector<GAEdge> spanning_tree;

    // convert the kDijkstraEdges to the input required for the kruskal input
    int x = 0;
    for(VEdgeWrapper vw : kDijkstraEdges) {
        GAEdge gae; bool s;
        tie(gae, s) = add_edge(vw.source, vw.target, g);
        weightmap[gae] = vw.weight;
    }

    // run the algorithm based on the input
    kruskal_minimum_spanning_tree(g, back_inserter(spanning_tree));

    // start building the output for the MST
    std::string d_p = "S/run_kruskal:\nPrint the edges in the MST:";
    for (std::vector < GAEdge >::iterator ei = spanning_tree.begin();
         ei != spanning_tree.end(); ++ei) {
        d_p += to_string(source(*ei, g)) + " <--> " + to_string(target(*ei, g)) +
                + " with weight of " + to_string(weightmap[*ei]) + "\n";
    }
    //claim(d_p, kDebug);

    // Generate the graphviz output
    std::ofstream fout("figs/kruskal-eg.dot");
    string output = "S/run_kruskal:Kruskal:\ngraph A {\n";
    output += " rankdir=LR\n";
    output += " size=\"3,3\"\n";
    output += " ratio=\"filled\"\n";
    output += " edge[style=\"bold\"]\n node[shape=\"circle\"]\n";

    fout << "graph A {\n"
            << " rankdir=LR\n"
            << " size=\"3,3\"\n"
            << " ratio=\"filled\"\n"
            << " edge[style=\"bold\"]\n" << " node[shape=\"circle\"]\n";
    graph_traits<GAGraph>::edge_iterator eiter, eiter_end;
    for (boost::tie(eiter, eiter_end) = edges(g); eiter != eiter_end; ++eiter) {
        fout << source(*eiter, g) << " -- " << target(*eiter, g);

        output += kHashMap.at(source(*eiter, g))->vnode_to_dot() + " -- "
                + kHashMap.at(target(*eiter, g))->vnode_to_dot();

        if (std::find(spanning_tree.begin(), spanning_tree.end(), *eiter)
                != spanning_tree.end()) {
            output += "[color=\"black\", label=\"" + to_string(get(edge_weight, g, *eiter)) + "\"];\n";
            fout << "[color=\"black\", label=\"" << get(edge_weight, g, *eiter)
                    << "\"];\n";
        } else {
            output += "[color=\"gray\", label=\"" + to_string(get(edge_weight, g, *eiter)) + "\"];\n";
            fout << "[color=\"gray\", label=\"" << get(edge_weight, g, *eiter)
                    << "\"];\n";
        }
    }
    fout << "}\n";
    output += "}\n";
    claim(output, kDebug);
    claim("S/run_kruskal: =========================================", kDebug);
}

/**
* run the boost implementation of dijkstra's algorithm
*/
void SPC::run_dijkstra(std::vector<VEdgeWrapper> edges) {
    claim("S/run_dijkstra: starting boost/dijkstra", kDebug);

    GRAPH_ALGO a = DIJKSTRA;

    std::vector<VEdgeWrapper> result;

    using namespace boost::adaptors;

    size_t max_node;

    // partially sort the list of our input edges, and get them ready for djikstra
    boost::partial_sort_copy(
            edges | transformed([](VEdgeWrapper const &e) -> size_t { return std::max(e.source, e.target); }),
            boost::make_iterator_range(&max_node, &max_node + 1),
            std::greater<size_t>());

    // I'm not going to lie, I don't know exactly what this line does
    auto e = edges | transformed([](VEdgeWrapper const &ve) { return std::make_pair(ve.source, ve.target); });
    // instantiate the graph object for dijkstra
    kGraph = graph_t(e.begin(), e.end(), edges.begin(), max_node + 1);

    weight_map_t kWeightMap = boost::get(&VEdgeWrapper::weight, kGraph);

    // get the itereators and answer containers
    vertex_descriptor kS    = vertex(0, kGraph);
    kVD                     = std::vector<vertex_descriptor>(num_vertices(kGraph));
    kD                      = std::vector<int>(num_vertices(kGraph));

    // run the actual dijkstra's algorithm on our input
    dijkstra_shortest_paths(
            kGraph, kS,
            predecessor_map(boost::make_iterator_property_map(kVD.begin(), get(boost::vertex_index, kGraph)))
                    .distance_map(boost::make_iterator_property_map(kD.begin(), get(boost::vertex_index, kGraph)))
                    .weight_map(kWeightMap));

    boost::graph_traits<graph_t>::edge_iterator ei, ei_end;

    /*for (boost::tie(ei, ei_end) = boost::edges(kGraph); ei != ei_end; ++ei) {
        boost::graph_traits<graph_t>::edge_descriptor e = *ei;
        boost::graph_traits<graph_t>::vertex_descriptor u = source(e, kGraph), v = target(e, kGraph);

        // Add the edges of the graph to the output, to prepare for Kruskal

    }*/

    // build the output for the our answer (graphviz is included in this)
    std::string output_file_name;
    output_file_name = kGraphAlgo[a] + "-eg.dot";
    weight_map_t weight_map = boost::get(&VEdgeWrapper::weight, kGraph);

    std::ofstream dot_file(output_file_name,std::ofstream::out);

    string output = kGraphAlgo[a] + ":\ndigraph D {\nrankdir=LR\n";
    output += "  size=\"4,3\"\nratio=\"fill\"\n";
    output += "  edge[style=\"bold\"]\nnode[shape=\"circle\"]\n";

    dot_file << "digraph D {\n"
            << "  rankdir=LR\n"
            << "  size=\"4,3\"\n"
            << "  ratio=\"fill\"\n"
            << "  edge[style=\"bold\"]\n"
            << "  node[shape=\"circle\"]\n";

    //boost::graph_traits<graph_t>::edge_iterator ei, ei_end;
    for (boost::tie(ei, ei_end) = boost::edges(kGraph); ei != ei_end; ++ei) {
        boost::graph_traits<graph_t>::edge_descriptor e = *ei;
        boost::graph_traits<graph_t>::vertex_descriptor u = source(e, kGraph), v = target(e, kGraph);

        dot_file << u << " -> " << v << "[label=\"" << get(weight_map, e) << "\"";
        output += kHashMap.at(u)->vnode_to_dot() + " -> " + kHashMap.at(v)->vnode_to_dot()
                + "[label=\"" + to_string(get(weight_map, e)) + "\"";

        // we only care about the "important" edges, the secondary routes are irrelevant to us
        if (kVD[v] == u) {
            dot_file << ", color=\"black\"";
            output += ", color=\"black\"";

            VEdgeWrapper vw;
            vw.id = VEdge::get_next_vedge_id();
            vw.source = u;
            vw.target = v;
            vw.weight = Flow::Controller::calculate_manhattan_distance(kHashMap.at((int) u), kHashMap.at((int) v));
            kDijkstraEdges.push_back(vw);

        } else {
            dot_file << ", color=\"grey\"";
            output += ", color=\"grey\"";

        }
        dot_file << "]\n";
        output += "]\n";
    }
    dot_file << "}";
    output += "}";

    claim(output, kDebug);
    dot_file.close();

    claim("S/run_dijkstra: =========================================", kDebug);
}


/*************************************************************************
 * SceneML, Copyright (C) 2007, 2008  J.D. Yamokoski
 * All rights reserved.
 * Email: yamokosk at gmail dot com
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public License as
 * published by the Free Software Foundation; either version 2.1 of the License,
 * or (at your option) any later version. The text of the GNU Lesser General
 * Public License is included with this library in the file LICENSE.TXT.
 *
 * This library is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE. See the file LICENSE.TXT for
 * more details.
 *
 *************************************************************************/
/*
 * Plane.cpp
 *
 *  Created on: Dec 1, 2008
 *      Author: yamokosk
 */

#include "Plane.h"
#include <plugin_framework/Plugin.h>
#include <boost/foreach.hpp>

const std::string Plane::Type("ODEPlane");

void* Plane::create(PF_ObjectParams* params)
{
	Plane* ptr = new Plane();
	ptr->services = params->platformServices;
	return ptr;
}

int Plane::destroy(void *p)
{
	if (!p) return -1;

	delete static_cast<Plane*>(p);

	return 0;
}

Plane::Plane() :
	Geometry()
{

}

Plane::~Plane()
{
}

void Plane::getInfo( ObjectInfo& info ) const
{
	info.type = Type;
	info.addProperty( getProperty("normal") );
	info.addProperty( getProperty("point") );

	// Have parent class deal with generic ODE properties
	Geometry::getInfo(info);
}

void Plane::notifyMoved( const Real* translation, const Real* rotation )
{
#ifdef BUG_dGeomSetPosition
	std::cout << "In Plane::notifyMoved(). Doing nothing." << std::endl;
#endif
}

void Plane::initImpl( const ObjectInfo& info )
{
	// Set object name
	name = info.name;

	odeobj = dCreatePlane (NULL, 0.0, 0.0, 1.0, 1.0);
	dGeomSetData (odeobj, this);
	// If there are any applicable parameters, set them now
	BOOST_FOREACH(Property p, info.parameters)
	{
		setProperty(p);
	}
}

Property Plane::getPropertyImpl(const std::string& name) const
{
	// To get here, it must be a geom specific property
	if ( name == "params" ) {
		return Property(name, getParams());
	}

	if ( name == "normal" ) {
		return Property(name, getNormal());
	}

	if ( name == "point" ) {
		return Property(name, getPoint());
	}

	// Have parent class deal with generic ODE properties
	return Geometry::getPropertyImpl(name);
}

void Plane::setPropertyImpl(const Property& p)
{
	// To get here, it must be a geom specific property
	if ( p.name_str() == "params" ) {
		setParams( boost::any_cast<Vector4>(p.const_value()) );
		return;
	}

	if ( p.name_str() == "normal" ) {
		setNormal( boost::any_cast<Vector3>(p.const_value()) );
		return;
	}

	if ( p.name_str() == "point" ) {
		setPoint( boost::any_cast<Vector3>(p.const_value()) );
		return;
	}

	// Have parent class deal with generic ODE properties
	Geometry::setPropertyImpl(p);
}

void Plane::setParams(const Vector4& v)
{
	dGeomPlaneSetParams(odeobj, (dReal)v[0], (dReal)v[1], (dReal)v[2], (dReal)v[3]);
}

Vector4 Plane::getParams() const
{
	dVector4 params;
	dGeomPlaneGetParams(odeobj, params);
	return Vector4((Real)params[0], (Real)params[1], (Real)params[2], (Real)params[3]);
}

void Plane::setNormal(const Vector3& n)
{
	Vector4 params = getParams();
	params[0] = n[0];
	params[1] = n[1];
	params[2] = n[2];
	setParams(params);
}

Vector3 Plane::getNormal() const
{
	Vector4 params = getParams();
	return Vector3(params[0], params[1], params[2]);
}

void Plane::setPoint(const Vector3& p)
{
	Real d = getNormal().dotProduct(p);
	Vector4 params = getParams();
	params[3] = d;
	setParams(params);
}
Vector3 Plane::getPoint() const
{
	Vector4 params = getParams();
	Vector3 normal = getNormal();
	return Vector3( params[3] * normal.normalisedCopy() );
}


// 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 <boost/numeric/mtl/mtl.hpp>

int main()
{
    using namespace mtl;
    size_t  N= 18;

    dense_vector<bool> v1( make_tag_vector(N, mtl::srange(0, imax, 2)) );
    std::cout << "Strided vector v1 is " << v1 << '\n';

    MTL_THROW_IF(size(v1) != N, runtime_error("Wrong size"));
    for (size_t i= 0; i < N; i++)
	MTL_THROW_IF(v1[i] != (i % 2 == 0), runtime_error("Wrong value for strided tags"));

    dense_vector<bool> v2( make_tag_vector(N, mtl::irange(7, 11)) );
    std::cout << "Strided vector v2 is " << v2 << '\n';

    MTL_THROW_IF(size(v2) != N, runtime_error("Wrong size"));
    for (size_t i= 0; i < N; i++)
	MTL_THROW_IF(v2[i] != (i >= 7 && i < 11), runtime_error("Wrong value for ranged tags"));

    dense_vector<bool> v3( make_tag_vector(N, mtl::irange(7, imax)) );
    std::cout << "Strided vector v3 is " << v3 << '\n';

    MTL_THROW_IF(size(v3) != N, runtime_error("Wrong size"));
    for (size_t i= 0; i < N; i++)
	MTL_THROW_IF(v3[i] != (i >= 7), runtime_error("Wrong value for ranged tags"));

    return 0;
}


#include <mpi.h>
#include <sys/time.h>
#include <iostream>
#include <functional>
#include <algorithm>
#include <vector>
#include <sstream>
#ifdef NOTR1
        #include <boost/tr1/memory.hpp>
#else
        #include <tr1/memory>
#endif
#include "../SpParVec.h"
#include "../SpTuples.h"
#include "../SpDCCols.h"
#include "../SpParMat.h"
#include "../DenseParMat.h"
#include "../DenseParVec.h"
#include "../FullyDistVec.h"
#include "../FullyDistSpVec.h"
#include "../ParFriends.h"

using namespace std;
#define NOGEMM

// Simple helper class for declarations: Just the numerical type is templated
// The index type and the sequential matrix type stays the same for the whole code
// In this case, they are "int" and "SpDCCols"
template <class NT>
class PSpMat
{
public:
	typedef SpDCCols < int64_t, NT > DCCols;
	typedef SpParMat < int64_t, NT, DCCols > MPI_DCCols;
};

int main(int argc, char* argv[])
{
	MPI::Init(argc, argv);
	int nprocs = MPI::COMM_WORLD.Get_size();
	int myrank = MPI::COMM_WORLD.Get_rank();

	if(argc < 6)
	{
		if(myrank == 0)
		{
			cout << "Usage: ./MultTest <MatrixA> <MatrixB> <MatrixC> <vecX> <vecY>" << endl;
			cout << "<MatrixA>,<MatrixB>,<MatrixC> are absolute addresses, and files should be in triples format" << endl;
		}
		MPI::Finalize();
		return -1;
	}
	{
		string Aname(argv[1]);
		string Bname(argv[2]);
		string Cname(argv[3]);
		string V1name(argv[4]);
		string V2name(argv[5]);

		ifstream inputA(Aname.c_str());
		ifstream inputB(Bname.c_str());
		ifstream inputC(Cname.c_str());
		ifstream vecinpx(V1name.c_str());
		ifstream vecinpy(V2name.c_str());

		MPI::COMM_WORLD.Barrier();

		typedef PlusTimesSRing<double, double> PTDOUBLEDOUBLE;
		typedef SelectMaxSRing<bool, int64_t> SR;

		PSpMat<double>::MPI_DCCols A, B, C, CControl;	// construct objects
		FullyDistVec<int64_t, double> ycontrol, x;
		FullyDistSpVec<int64_t, double> spycontrol, spx;

		A.ReadDistribute(inputA, 0);
#ifndef NOGEMM
		B.ReadDistribute(inputB, 0);
		CControl.ReadDistribute(inputC, 0);
#endif
		x.ReadDistribute(vecinpx, 0);
		spx.ReadDistribute(vecinpx, 0);
		ycontrol.ReadDistribute(vecinpy,0);
		spycontrol.ReadDistribute(vecinpy,0);

		FullyDistVec<int64_t, double> y = SpMV<PTDOUBLEDOUBLE>(A, x);
		if (ycontrol == y)
		{
			SpParHelper::Print("Dense SpMV (fully dist) working correctly\n");
		}
		else
		{
			SpParHelper::Print("ERROR in Dense SpMV, go fix it!\n");
		}

		FullyDistSpVec<int64_t, double> spy = SpMV<PTDOUBLEDOUBLE>(A, spx);

		if (spycontrol == spy)
		{
			SpParHelper::Print("Sparse SpMV (fully dist) working correctly\n");
		}
		else
		{
			SpParHelper::Print("ERROR in Sparse SpMV, go fix it!\n");
		}
#ifndef NOGEMM
		C = Mult_AnXBn_Synch<PTDOUBLEDOUBLE>(A,B);
		if (CControl == C)
		{
			SpParHelper::Print("Synchronous Multiplication working correctly\n");
		}
		else
		{
			SpParHelper::Print("ERROR in Synchronous Multiplication, go fix it!\n");
		}

		C = Mult_AnXBn_DoubleBuff<PTDOUBLEDOUBLE>(A,B);
		if (CControl == C)
		{
			SpParHelper::Print("Double buffered multiplication working correctly\n");
		}
		else
		{
			SpParHelper::Print("ERROR in double buffered multiplication, go fix it!\n");
		}
#endif
		OptBuf<int64_t, int64_t> optbuf;
		PSpMat<bool>::MPI_DCCols ABool(A);

		spx.Apply(bind1st (multiplies<double>(), 100));
		FullyDistSpVec<int64_t, int64_t> spxint64 (spx);
		FullyDistSpVec<int64_t, int64_t> spyint64 = SpMV<SR>(ABool, spxint64, false);

		ABool.OptimizeForGraph500(optbuf);
		FullyDistSpVec<int64_t, int64_t> spyint64buf = SpMV<SR>(ABool, spxint64, false, optbuf);

		if (spyint64 == spyint64buf)
		{
			SpParHelper::Print("Graph500 Optimizations are correct\n");
		}
		else
		{
			SpParHelper::Print("ERROR in graph500 optimizations, go fix it!\n");
		}
		ABool.ActivateThreading(4);
		FullyDistSpVec<int64_t, int64_t> spyint64_threaded = SpMV<SR>(ABool, spxint64, false);

		if (spyint64 == spyint64_threaded)
		{
			SpParHelper::Print("Multithreaded Sparse SpMV works\n");
		}
		else
		{
			SpParHelper::Print("ERROR in multithreaded sparse SpMV, go fix it!\n");
		}

		inputA.clear();
		inputA.close();
		inputB.clear();
		inputB.close();
		inputC.clear();
		inputC.close();
		vecinpx.clear();
		vecinpx.close();
		vecinpy.clear();
		vecinpy.close();
	}
	MPI::Finalize();
	return 0;
}


#pragma once

#include "ir/ir.hpp"
#include <unordered_map>
#include <boost/optional.hpp>

struct ColoringResult
{
    bool valid;
    std::unordered_map<Ir::Argument, int> colors;
    std::set<Ir::Argument> potentialSpills;
};

ColoringResult linear_color(const Ir::Block &block, size_t N);
Ir::Block applyColors(const std::unordered_map<Ir::Argument, int> &c, const Ir::Block &block);


// Boost.Units - A C++ library for zero-overhead dimensional analysis and
// unit/quantity manipulation and conversion
//
// Copyright (C) 2003-2008 Matthias Christian Schabel
// Copyright (C) 2008 Steven Watanabe
//
// 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)

/**
\file

\brief test_predicates.cpp

\details
Test metafunctions is_unit, is_quantity, is_dimension_list ....

Output:
@verbatim
@endverbatim
**/

#include <boost/mpl/assert.hpp>
#include <boost/mpl/list/list0.hpp>

#include <boost/units/base_dimension.hpp>
#include <boost/units/base_unit.hpp>
#include <boost/units/dimension.hpp>
#include <boost/units/is_dimension_list.hpp>
#include <boost/units/is_quantity.hpp>
#include <boost/units/is_quantity_of_dimension.hpp>
#include <boost/units/is_quantity_of_system.hpp>
#include <boost/units/is_unit.hpp>
#include <boost/units/is_unit_of_dimension.hpp>
#include <boost/units/is_unit_of_system.hpp>
#include <boost/units/make_system.hpp>
#include <boost/units/quantity.hpp>
#include <boost/units/unit.hpp>

namespace bu = boost::units;

struct dimension_tag : boost::units::base_dimension<dimension_tag,0> { };

typedef dimension_tag::dimension_type dimension;

struct base_unit1 : bu::base_unit<base_unit1, dimension, 100> {};
struct base_unit2 : bu::base_unit<base_unit2, dimension, 101> {};

typedef bu::make_system<base_unit1>::type system1;
typedef bu::make_system<base_unit2>::type system2;

int main(int,char *[])
{
    BOOST_MPL_ASSERT((bu::is_dimension_list<bu::dimensionless_type>));
    BOOST_MPL_ASSERT((bu::is_dimension_list<dimension>));
    BOOST_MPL_ASSERT_NOT((bu::is_dimension_list<boost::mpl::list0<> >));
    BOOST_MPL_ASSERT_NOT((bu::is_dimension_list<int>));

    BOOST_MPL_ASSERT((bu::is_unit<bu::unit<bu::dimensionless_type, system1> >));
    BOOST_MPL_ASSERT((bu::is_unit<bu::unit<dimension, system1> >));
    BOOST_MPL_ASSERT_NOT((bu::is_unit<int>));

    BOOST_MPL_ASSERT((bu::is_unit_of_system<bu::unit<bu::dimensionless_type, system1>, system1>));
    BOOST_MPL_ASSERT((bu::is_unit_of_system<bu::unit<dimension, system1>, system1>));
    BOOST_MPL_ASSERT_NOT((bu::is_unit_of_system<bu::unit<bu::dimensionless_type, system1>, system2>));
    BOOST_MPL_ASSERT_NOT((bu::is_unit_of_system<bu::unit<dimension, system1>, system2>));
    BOOST_MPL_ASSERT_NOT((bu::is_unit_of_system<int, system1>));
    BOOST_MPL_ASSERT_NOT((bu::is_unit_of_system<int, system2>));

    BOOST_MPL_ASSERT((bu::is_unit_of_dimension<bu::unit<bu::dimensionless_type, system1>, bu::dimensionless_type>));
    BOOST_MPL_ASSERT((bu::is_unit_of_dimension<bu::unit<dimension, system1>, dimension>));
    BOOST_MPL_ASSERT_NOT((bu::is_unit_of_dimension<bu::unit<bu::dimensionless_type, system1>, dimension>));
    BOOST_MPL_ASSERT_NOT((bu::is_unit_of_dimension<bu::unit<dimension, system1>, bu::dimensionless_type>));
    BOOST_MPL_ASSERT_NOT((bu::is_unit_of_dimension<int, bu::dimensionless_type>));
    BOOST_MPL_ASSERT_NOT((bu::is_unit_of_dimension<int, dimension>));

    BOOST_MPL_ASSERT((bu::is_quantity<bu::quantity<bu::unit<bu::dimensionless_type, system1> > >));
    BOOST_MPL_ASSERT((bu::is_quantity<bu::quantity<bu::unit<dimension, system1> > >));
    BOOST_MPL_ASSERT_NOT((bu::is_quantity<int>));

    BOOST_MPL_ASSERT((bu::is_quantity<bu::quantity<bu::unit<bu::dimensionless_type, system1>, int> >));
    BOOST_MPL_ASSERT((bu::is_quantity<bu::quantity<bu::unit<dimension, system1>, int> >));

    BOOST_MPL_ASSERT((bu::is_quantity_of_system<bu::quantity<bu::unit<bu::dimensionless_type, system1> >, system1>));
    BOOST_MPL_ASSERT((bu::is_quantity_of_system<bu::quantity<bu::unit<dimension, system1> >, system1>));
    BOOST_MPL_ASSERT_NOT((bu::is_quantity_of_system<bu::quantity<bu::unit<bu::dimensionless_type, system1> >, system2>));
    BOOST_MPL_ASSERT_NOT((bu::is_quantity_of_system<bu::quantity<bu::unit<dimension, system1> >, system2>));
    BOOST_MPL_ASSERT_NOT((bu::is_quantity_of_system<int, system1>));
    BOOST_MPL_ASSERT_NOT((bu::is_quantity_of_system<int, system2>));

    BOOST_MPL_ASSERT((bu::is_quantity_of_dimension<bu::quantity<bu::unit<bu::dimensionless_type, system1> >, bu::dimensionless_type>));
    BOOST_MPL_ASSERT((bu::is_quantity_of_dimension<bu::quantity<bu::unit<dimension, system1> >, dimension>));
    BOOST_MPL_ASSERT_NOT((bu::is_quantity_of_dimension<bu::quantity<bu::unit<bu::dimensionless_type, system1> >, dimension>));
    BOOST_MPL_ASSERT_NOT((bu::is_quantity_of_dimension<bu::quantity<bu::unit<dimension, system1> >, bu::dimensionless_type>));
    BOOST_MPL_ASSERT_NOT((bu::is_quantity_of_dimension<int, bu::dimensionless_type>));
    BOOST_MPL_ASSERT_NOT((bu::is_quantity_of_dimension<int, dimension>));

    return 0;
}


// Copyright 2015 The ALMA Project Developers
//
// 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.

/// @file
/// Writes phonon properties associated with HDF5 file to a text file.

#include <iostream>
#include <fstream>
#include <boost/filesystem.hpp>
#include <boost/mpi.hpp>
#include <Eigen/Dense>
#include <Eigen/Sparse>
#include <cmakevars.hpp>
#include <constants.hpp>
#include <utilities.hpp>
#include <structures.hpp>
#include <bulk_hdf5.hpp>
#include <isotopic_scattering.hpp>
#include <io_utils.hpp>
#include <bulk_properties.hpp>

int main(int argc, char** argv) {
    boost::mpi::environment env;
    boost::mpi::communicator world;

    // Reference temperature at which to compute
    // heat capacities and scattering rates.
    // Optional 2nd argument, defaults to 300K
    double Tambient = 300.0;

    // Path to the HDF5 file.
    std::string h5filename;

    if (argc < 2) {
        std::cout << "USAGE: phononinfo <inputfile.hdf5> <OPTIONAL: Tambient "
                     "(default 300K)>"
                  << std::endl;
        world.abort(1);
    }

    if (argc >= 2) {
        h5filename = std::string(argv[1]);
    }

    if (argc >= 3) {
        Tambient = atof(argv[2]);
        if (Tambient <= 0.0) {
            std::cout << "ERROR: Tambient must be positive (" << Tambient
                      << " provided)." << std::endl;
            world.abort(1);
        }
    }

    std::cout << "***********************************" << std::endl;
    std::cout << "This is ALMA/phononinfo version " << ALMA_VERSION_MAJOR << "."
              << ALMA_VERSION_MINOR << std::endl;
    std::cout << "***********************************" << std::endl;

    // Create name of outputfile
    boost::filesystem::path hdf5_path{h5filename};
    std::stringstream output_file_builder;
    output_file_builder << hdf5_path.stem().string() << "_" << Tambient
                        << "K.phononinfo";
    std::string output_file = output_file_builder.str();

    // Obtain phonon data
    std::cout << "Reading " << h5filename << std::endl;

    if (!(boost::filesystem::exists(hdf5_path))) {
        std::cout << "ERROR:" << std::endl;
        std::cout << "HDF5 file " << hdf5_path << " does not exist."
                  << std::endl;
        world.abort(1);
    }

    auto hdf5_data = alma::load_bulk_hdf5(hdf5_path.string().c_str(), world);
    auto poscar = std::move(std::get<1>(hdf5_data));
    auto syms = std::move(std::get<2>(hdf5_data));
    auto grid = std::move(std::get<3>(hdf5_data));
    auto processes = std::move(std::get<4>(hdf5_data));

    // RTA scattering rates at the specified temperature.
    std::cout << "Calculating scattering rates" << std::endl;
    Eigen::ArrayXXd w3(
        alma::calc_w0_threeph(*grid, *processes, Tambient, world));
    auto twoph_processes = alma::find_allowed_twoph(*grid, world);
    Eigen::ArrayXXd w2(
        alma::calc_w0_twoph(*poscar, *grid, twoph_processes, world));
    Eigen::ArrayXXd w0(w3 + w2);

    // Create output writer
    std::cout << "Writing data to " << output_file << std::endl;
    std::ofstream filewriter;
    filewriter.open(output_file);
    filewriter << "nq,nbranch,qa[-],qb[-],qc[-],omega[rad/s],C[J/"
                  "m^3-K],tau[s],vx[m/s],vy[m/s],vz[m/s]"
               << std::endl;

    int Nq = grid->nqpoints;
    int Nbranches = grid->get_spectrum_at_q(0).omega.size();

    const double prefactor =
        1e27 * alma::constants::kB / grid->nqpoints / poscar->V;

    Eigen::Matrix3d kappa_RTA = alma::calc_kappa(*poscar, *grid, w0, Tambient);
    Eigen::Matrix3d invkappa_RTA;
    invkappa_RTA.setZero();
    for (auto axis : {0,1,2}) {
        invkappa_RTA(axis,axis) = 1./kappa_RTA(axis,axis);
    }

    Eigen::Matrix3d lnum, lden;
    lnum.setZero();
    lden.setZero();
    double lxnum = 0., lynum = 0., lznum = 0.;
    double lxden = 0., lyden = 0., lzden = 0.;

    for (int nq = 0; nq < Nq; ++nq) {

        auto coords_ = poscar->map_to_firstbz(
            grid->get_q(nq));

        double hqx = 1.0e+9 * alma::constants::hbar * coords_.row(0).mean();
        double hqy = 1.0e+9 * alma::constants::hbar * coords_.row(1).mean();
        double hqz = 1.0e+9 * alma::constants::hbar * coords_.row(2).mean();

        Eigen::Matrix3d moment;

        moment.setZero();

        moment(0,0) = hqx;
        moment(1,1) = hqy;
        moment(2,2) = hqz;

        auto sp = grid->get_spectrum_at_q(nq);

        std::array<int, 3> qcoords = grid->one_to_three(nq);
        double qa =
            static_cast<double>(qcoords[0]) / static_cast<double>(grid->na);
        double qb =
            static_cast<double>(qcoords[1]) / static_cast<double>(grid->nb);
        double qc =
            static_cast<double>(qcoords[2]) / static_cast<double>(grid->nc);

        for (int nbranch = 0; nbranch < Nbranches; nbranch++) {
            // scattering rate
            double my_w0 = w0(nbranch, nq);

            // relaxation time [seconds]
            double tau0 = (my_w0 == 0.) ? 0. : (1e-12 / my_w0);

            // angular frequency [rad/s]
            double omega = 1e12 * sp.omega[nbranch];

            // volumetric heat capacity [J/m^3-K]
            double C = prefactor *
                       alma::bose_einstein_kernel(sp.omega[nbranch], Tambient);

            // group velocity vector [m/s]
            Eigen::Vector3d vg_vector = 1e3 * sp.vg.col(nbranch);

            Eigen::Matrix3d vmat;
            vmat.setZero();
            for (auto axis : {0,1,2}){
                vmat(axis,axis) = vg_vector(axis);
            }

            Eigen::Matrix3d g1;
            g1.setZero();
            g1 = - tau0 * tau0 *  alma::constants::kB *
                   alma::bose_einstein_kernel(sp.omega[nbranch], Tambient) *
                   invkappa_RTA * vmat * vmat;

            double g1_x = - tau0 * tau0 * alma::constants::kB *
                   alma::bose_einstein_kernel(sp.omega[nbranch], Tambient) *
                   vg_vector(0) * vg_vector(0)/kappa_RTA(0,0);
            double g1_y = - tau0 * tau0 * alma::constants::kB *
                   alma::bose_einstein_kernel(sp.omega[nbranch], Tambient) *
                   vg_vector(1) * vg_vector(1)/kappa_RTA(1,1);
            double g1_z = - tau0 * tau0 * alma::constants::kB *
                   alma::bose_einstein_kernel(sp.omega[nbranch], Tambient) *
                   vg_vector(2) * vg_vector(2)/kappa_RTA(2,2);

            lxnum += hqx * vg_vector(0) * g1_x;
            lynum += hqy * vg_vector(1) * g1_y;
            lznum += hqz * vg_vector(2) * g1_z;

            lxden += hqx * vg_vector(0) * alma::constants::kB * alma::bose_einstein_kernel(sp.omega[nbranch], Tambient);
            lyden += hqx * vg_vector(0) * alma::constants::kB * alma::bose_einstein_kernel(sp.omega[nbranch], Tambient);
            lzden += hqx * vg_vector(0) * alma::constants::kB * alma::bose_einstein_kernel(sp.omega[nbranch], Tambient);

            lnum += moment * vmat * g1;
            lden += moment * vmat * alma::constants::kB *
                   alma::bose_einstein_kernel(sp.omega[nbranch], Tambient);

            // write output
            filewriter << nq << "," << nbranch << "," << qa << "," << qb << ","
                       << qc << ",";
            filewriter << omega << "," << C << "," << tau0 << ",";
            filewriter << vg_vector(0) << "," << vg_vector(1) << ","
                       << vg_vector(2) << std::endl;
        }
    }

    filewriter.close();
    if (world.rank() == 0.) {
        std::cout << "#Small-grain-limit kappa [W/(m**2 * K)]\n";
        Eigen::Matrix3d ksg = alma::calc_kappa_sg(*poscar,*grid,Tambient);
        std::cout << ksg << std::endl;
        std::cout << "#l**2  assuming isotropic bands [m**2]" <<std::endl;
        std::cout << "#x " << -1./5 * kappa_RTA(0,0) * lxnum / lxden << std::endl;
        std::cout << "#y " << -1./5 * kappa_RTA(1,1) * lynum / lyden << std::endl;
        std::cout << "#z " << -1./5 * kappa_RTA(2,2) * lznum / lzden << std::endl;
    }

    std::cout << std::endl << "[DONE.]" << std::endl;

    return 0;
}


#include "mm_alignment.h"
#include "io.h"
#include "ereal.h"

#include <boost/tuple/tuple.hpp>

mm_alignment::mm_alignment( int i ) : nmodels(i), interface(1), rescale_scores(false) {
	mp.resize(i);
	p_sw.resize(i);
	set_default_parameters(blitz::TinyVector<double,4>(0.25,0.25,0.25,0.25));
}

void mm_alignment::set_initial_background( blitz::TinyVector<double,4> q ) {
	set_default_parameters(q);
	rp.set( 500, 500, q );
}

mm_alignment::model_parameters::model_parameters() {
	p.resize(dna_alpha::SIZE,dna_alpha::SIZE);
}

mm_alignment::random_model_parameters::random_model_parameters() {
	set( 500, 500,blitz::TinyVector<double,4>(0.25,0.25,0.25,0.25) );
}

void mm_alignment::random_model_parameters::set( int a, int b, blitz::TinyVector<double,4> nq ) {
	p_a = 1.0/(1.0 + 1.0/(double)a);
	p_1ma = 1.0/(1.0+(double)a);
	p_b = 1.0/(1.0 + 1.0/(double)b);
	p_1mb = 1.0/(1.0 + (double)b);
	for( int i = 0; i < 4; ++i) q(i) = nq(i);
}

mm_alignment::model_parameters::model_parameters( mm_alignment::model_parameters const &other) {
	p.resize(dna_alpha::SIZE,dna_alpha::SIZE);
	p = other.p;
	q = other.q;
	p_at = other.p_at;
	p_1m2at = other.p_1m2at;
	p_a = other.p_a;
	p_1m2at = other.p_1m2at;
	p_bt = other.p_bt;
	p_1mbt = other.p_1mbt;
	p_1mt = other.p_1mt;
	nwmp = other.nwmp;
}

void mm_alignment::model_parameters::set( nw_model_parameters const &nwp, double t ) {
	// set open
   p_at = nwp.pr_open*t;
	p_1m2at = (1.0-2.0*nwp.pr_open)*t;
	p_a = nwp.pr_open;
	p_1m2a = 1.0 - 2.0*nwp.pr_open;

	// Since a gap is length 1 by the virtue of opening it, we adjust n down by 1.
   // The remaining gap extension probability corresponds to the mean of a
   // geometric distribution allowing zero extensions.
   double pr_b = 1.0-1.0/nwp.mean_gap_length;
   p_bt = pr_b*t;
   p_1mbt = (1.0 - pr_b)*t;

	p_1mt = 1.0 - t;

	// set background
	for( int i = 0; i < 4; i++ ) {
      q(i) = nwp.q(i);
   }

	// set p
	for( int i = 0; i < 4; i++ ) {
		for( int j = 0; j < 4; j++ ) {
			p(i,j) = nwp.p(i,j);
		}
   }

	// store nw_model_parameters for information purposes
	nwmp = nwp;
}

int mm_alignment::num_models() {
	return nmodels;
}

nw_model_parameters const &mm_alignment::get_parameters( int k ) {
	return mp[k].nwmp;
}

void mm_alignment::set_parameters( int k, nw_model_parameters const &p, int t ) {
	mp[k].set(p,1.0-1.0/(double)t);
}

// disable the random model
void mm_alignment::disable_random_model() {
	// we redistribution the probability assigned to the random model to the remaining
	// models evenly
	ereal prslice = (double)p_rand/(double)nmodels;
	for( int k = 0; k < nmodels; ++k ) p_sw[k] += prslice;
	p_rand = 0.0;
}

double mm_alignment::get_random_pr() {
	return (double)p_rand;
}

double mm_alignment::get_random_length_a() {
	return 1.0/(double)rp.p_1ma;
}

double mm_alignment::get_random_length_b() {
	return 1.0/(double)rp.p_1mb;
}

void mm_alignment::set_random_param( int i, int j, blitz::TinyVector<double,4> q ) {
	rp.set(i,j,q);
}

blitz::TinyVector<ereal,4> mm_alignment::get_random_distribution() {
	return rp.q;
}

double mm_alignment::get_region_pr( int k ) {
	return (double)p_sw[k];
}

double mm_alignment::get_region_length( int k ) {
	return 1.0/(double)mp[k].p_1mt;
}

void mm_alignment::set_default_parameters( blitz::TinyVector<double,4> q ) {
	using namespace std;
	using namespace boost;
	assert(nmodels < 30);

	typedef boost::tuple<double,double,double> double3;
	vector<double3> custom_pts;
	custom_pts.push_back( double3(0.4,0.0625,2.0) );
	custom_pts.push_back( double3(0.2,0.03125,1.333) );
	custom_pts.push_back( double3(0.6,0.0625,3.41424) );
	custom_pts.push_back( double3(0.8,0.03125,2.0) );

	/*
	//static unsigned int const greedy_opt[] = {106, 88, 150, 62, 59, 77, 103, 145, 44, 121, 132, 31, 84, 93, 72,
	//131, 146, 75, 28, 54, 61, 78, 134, 138, 142, 46, 58, 63, 115, 119 };
	static unsigned int const greedy_opt[] = {16, 85, 40, 75, 59, 77, 103, 145, 44, 121, 132, 31, 84, 93, 72,
	 131, 146, 75, 28, 54, 61, 78, 134, 138, 142, 46, 58, 63, 115, 119 };

   vector<double> gap_lengths, gap_opens, distances;
   gap_opens.push_back( 0.03125 ); // -5
   gap_opens.push_back( 0.0625  ); // -4
   gap_opens.push_back( 0.125   ); // -3
   gap_opens.push_back( 0.25    ); // -2

   gap_lengths.push_back( 1.333    ); // -2
   gap_lengths.push_back( 2.0      ); // -1
   gap_lengths.push_back( 3.414214 ); // -0.5
   gap_lengths.push_back( 6.285214 ); // -0.25

	distances.push_back( 0.1 );
   distances.push_back( 0.2 );
   distances.push_back( 0.3 );
   distances.push_back( 0.4 );
   distances.push_back( 0.5 );
   distances.push_back( 0.6 );
   distances.push_back( 0.7 );
   distances.push_back( 0.8 );
   distances.push_back( 0.9 );
   distances.push_back( 1.0 );

	std::vector<nw_model_parameters> all;
	for( vector<double>::const_iterator d = distances.begin(); d != distances.end(); ++d ) {
		for( vector<double>::const_iterator a = gap_opens.begin(); a != gap_opens.end(); ++a ) {
			for( vector<double>::const_iterator L = gap_lengths.begin(); L != gap_lengths.end(); ++L ) {
				all.push_back( nw_model_parameters() );
				all.back().set_p_hky( *d, 1.0 );
				all.back().pr_open = *a;
				all.back().mean_gap_length = *L;

			}
		}
   }
	*/

	double pr_rand =  1.0/10000.0;
	p_rand = pr_rand;
	rp.set( 500, 500,q );

	assert( (unsigned int)nmodels <= custom_pts.size() );
	for( int k = 0; k < nmodels; ++k ) {
		nw_model_parameters newp;
		newp.q = q;
		newp.set_p_hky( custom_pts[k].get<0>(), 1.0 );
		newp.pr_open = custom_pts[k].get<1>();
		newp.mean_gap_length = custom_pts[k].get<2>();
		mp[k].set( newp, 1.0 - 1.0/200 );
		p_sw[k] = (1.0-pr_rand)/nmodels;
	}
}

std::pair<pairwise_dna_alignment,annotation_ptr> mm_alignment::align_and_annotate( dna_sequence_region &a, dna_sequence_region &b ) {
	int n = (int)a.data.size(), m = (int)b.data.size();

	// allocate dp matricies if needed
	if( dp.m.extent(blitz::secondDim) < (n+1) || dp.m.extent(blitz::thirdDim) < (m+1) ) dp.m.resize(nmodels,n+1,m+1);
	if( dp.s.extent(blitz::firstDim) < (n+1) || dp.s.extent(blitz::secondDim) < (m+1) ) dp.s.resize(n+1,m+1);
	if( dp.r.extent(blitz::firstDim) < (n+1) || dp.r.extent(blitz::secondDim) < (m+1) ) dp.r.resize(n+1,m+1);

	model_data null;
	viterbi_init_borders( dp, null, a.data, b.data );
	viterbi_filldp( a.data, b.data );
	alignment_chain_ptr r = viterbi_traceback(-1,state_switch,n,m,a.data,b.data);

	// compute odds
	ereal odds = 1.0;
	for( annotation_iter i = r->ann->begin(); i != r->ann->end(); ++i ) {
		for( int c = i->a; c <= i->b; ++c ) {
			if( r->align.a->data[c] != dna_alignment_alpha::GAP ) odds *= mp[i->t].q((int)dna_alpha::symbol(r->align.a->data[c]));
			if( r->align.b->data[c] != dna_alignment_alpha::GAP ) odds *= mp[i->t].q((int)dna_alpha::symbol(r->align.b->data[c]));
		}
	}
	r->align.score = (dp.s(n,m).s/odds).as_base();

	return make_pair(r->align,r->ann);
}

pairwise_dna_alignment mm_alignment::align( dna_sequence_region &a, dna_sequence_region &b ) {
	std::pair<pairwise_dna_alignment,annotation_ptr> r = align_and_annotate(a,b);
	return r.first;
}

void mm_alignment::viterbi_init_borders( model_data &mydp, model_data &init, dna_sequence_region_data &a, dna_sequence_region_data &b ) {
	int n = (int)a.size(), m = (int)b.size();

	// init size will always be a multiple of two
	int init_size = init.m.extent(blitz::secondDim);
	assert( init_size % 2 == 0 );

	if( init_size > 0 ) {
		for( int i = 0; i < std::min(init_size,n); ++i ) {
			mydp.s(i,0) = init.s(i,0);
			mydp.r(i,0) = init.r(i,0);
		}
	} else {
		mydp.s(0,0).s = 1.0;
		mydp.r(1,0).Ia.s = mydp.s(0,0).s*p_rand*q_rand(1,a);
		mydp.r(1,0).Ia.from = state_switch;
	}

	// set random model init
	for( int i = (init_size > 0) ? init_size : 2; i <= n; ++i ) {
		mydp.r(i,0).Ia.s = mydp.r(i-1,0).Ia.s*rp.p_a*q_rand(i,a);
		mydp.r(i,0).Ia.from = state_RIa;
	}

	for( int k = 0; k < nmodels; ++k ) {
		if( init_size > 0 )  {
			for( int i = 0; i < std::min(init_size,n); ++i ) mydp.m(k,i,0) = init.m(k,i,0);
		} else {
			mydp.m(k,0,0).S.s = mydp.s(0,0).s*p_sw[k];
			mydp.m(k,0,0).S.from = state_switch;
			mydp.m(k,1,0).Ia.s = mydp.m(k,0,0).S.s*mp[k].p_a*q(k,1,a);
			mydp.m(k,1,0).Ia.from = state_S;
			mydp.m(k,1,0).E.s = mydp.m(k,1,0).Ia.s*mp[k].p_1mt;
			mydp.m(k,1,0).E.from = state_Ia;
			if( mydp.s(1,0).s < mydp.m(k,1,0).E.s ) {
				mydp.s(1,0).s = mydp.m(k,1,0).E.s;
				mydp.s(1,0).from = state_E + k;
			}
		}

		for( int i = (init_size > 0) ? init_size : 2; i <= n; ++i ) {
			mydp.m(k,i,0).Ia.s = mydp.m(k,i-1,0).Ia.s*mp[k].p_bt*q(k,i,a);
			mydp.m(k,i,0).Ia.from = state_Ia;
			mydp.m(k,i,0).E.s = mydp.m(k,i,0).Ia.s*mp[k].p_1mt;
			mydp.m(k,i,0).E.from = state_Ia;
			if( mydp.s(i,0).s < mydp.m(k,i,0).E.s ) {
				mydp.s(i,0).s = mydp.m(k,i,0).E.s;
				mydp.s(i,0).from = state_E + k;
			}
		}

		if( m > 0 ) {
			mydp.m(k,0,1).Ib.s = mydp.m(k,0,0).S.s*mp[k].p_a*q(k,1,b);
			mydp.m(k,0,1).Ib.from = state_S;
			// if we have initialization data we must consider coming from an open gap
			if( init_size > 0 ) {
				ereal sc = mydp.m(k,0,0).Ib.s*mp[k].p_bt*q(k,1,b);
				if( sc > mydp.m(k,0,1).Ib.s ) {
					mydp.m(k,0,1).Ib.s = sc;
					mydp.m(k,0,1).Ib.from = state_Ib;
				}
			}
			mydp.m(k,0,1).E.s = mydp.m(k,0,1).Ib.s*mp[k].p_1mt;
			mydp.m(k,0,1).E.from = state_Ib;
			if( mydp.s(0,1).s < mydp.m(k,0,1).E.s ) {
				mydp.s(0,1).s = mydp.m(k,0,1).E.s;
				mydp.s(0,1).from = state_E + k;
			}
		}

		for( int j = 2; j <= m; ++j ) {
			mydp.m(k,0,j).Ib.s = mydp.m(k,0,j-1).Ib.s*mp[k].p_bt*q(k,j,b);
			mydp.m(k,0,j).Ib.from = state_Ib;
			mydp.m(k,0,j).E.s = mydp.m(k,0,j-1).Ib.s*mp[k].p_1mt;
			mydp.m(k,0,j).E.from = state_Ib;
			if( mydp.s(0,j).s < mydp.m(k,0,j).E.s ) {
				mydp.s(0,j).s = mydp.m(k,0,j).E.s;
				mydp.s(0,j).from = state_E + k;
			}
		}
	}
}

void mm_alignment::viterbi_filldp( dna_sequence_region_data &a, dna_sequence_region_data &b ) {
	int n = (int)a.size(), m = (int)b.size();
	using namespace std;

	ereal sc;
	for( int i = 1; i <= n; ++i ) {
	for( int j = 1; j <= m; ++j ) {
		dp.s(i,j).s = 0.0;

		// do random model
		dp.r(i,j).Ia.s = dp.r(i-1,j).Ia.s*rp.p_a*q_rand(i,a);
		dp.r(i,j).Ia.from = state_RIa;
		sc = dp.s(i-1,j).s*p_rand*q_rand(i,a);
		if( sc > dp.r(i,j).Ia.s ) { dp.r(i,j).Ia.s = sc; dp.r(i,j).Ia.from = state_switch; }

		dp.r(i,j).Ib.s = dp.r(i,j-1).Ib.s*rp.p_b*q_rand(j,b);
		dp.r(i,j).Ib.from = state_RIb;
		sc = dp.r(i,j-1).Ia.s*rp.p_1ma*q_rand(j,b);
		if( sc > dp.r(i,j).Ib.s ) { dp.r(i,j).Ib.s = sc; dp.r(i,j).Ib.from = state_RIa; }

		dp.s(i,j).s = dp.r(i,j).Ib.s*rp.p_1mb;
		dp.s(i,j).from = state_RIb;

		for( int k = 0; k < nmodels; ++k ) {
			// state M
			dp.m(k,i,j).M.s = dp.m(k,i-1,j-1).S.s*mp[k].p_1m2at*p(k,i,j,a,b);
			dp.m(k,i,j).M.from = state_S;

			sc = dp.m(k,i-1,j-1).M.s*mp[k].p_1m2at*p(k,i,j,a,b);
			if( sc > dp.m(k,i,j).M.s ) { dp.m(k,i,j).M.s = sc; dp.m(k,i,j).M.from = state_M; }

			sc = dp.m(k,i-1,j-1).Ia.s*mp[k].p_1mbt*p(k,i,j,a,b);
			if( sc > dp.m(k,i,j).M.s ) { dp.m(k,i,j).M.s = sc; dp.m(k,i,j).M.from = state_Ia; }

			sc = dp.m(k,i-1,j-1).Ib.s*mp[k].p_1mbt*p(k,i,j,a,b);
			if( sc > dp.m(k,i,j).M.s ) { dp.m(k,i,j).M.s = sc; dp.m(k,i,j).M.from = state_Ib; }

			// state Ia
			dp.m(k,i,j).Ia.s = dp.m(k,i-1,j).S.s*mp[k].p_a*q(k,i,a);
			dp.m(k,i,j).Ia.from = state_S;

			sc = dp.m(k,i-1,j).M.s*mp[k].p_at*q(k,i,a);
			if( sc > dp.m(k,i,j).Ia.s ) { dp.m(k,i,j).Ia.s = sc; dp.m(k,i,j).Ia.from = state_M; }

			sc = dp.m(k,i-1,j).Ia.s*mp[k].p_bt*q(k,i,a);
			if( sc > dp.m(k,i,j).Ia.s ) { dp.m(k,i,j).Ia.s = sc; dp.m(k,i,j).Ia.from = state_Ia; }

			// state Ib
			dp.m(k,i,j).Ib.s = dp.m(k,i,j-1).S.s*mp[k].p_a*q(k,j,b);
			dp.m(k,i,j).Ib.from = state_S;

			sc = dp.m(k,i,j-1).M.s*mp[k].p_at*q(k,j,b);
			if( sc > dp.m(k,i,j).Ib.s ) { dp.m(k,i,j).Ib.s = sc; dp.m(k,i,j).Ib.from = state_M; }

			sc = dp.m(k,i,j-1).Ib.s*mp[k].p_bt*q(k,j,b);
			if( sc > dp.m(k,i,j).Ib.s ) { dp.m(k,i,j).Ib.s = sc; dp.m(k,i,j).Ib.from = state_Ib; }

			// end state
			dp.m(k,i,j).E.s = dp.m(k,i,j).Ia.s*mp[k].p_1mt;
			dp.m(k,i,j).E.from = state_Ia;

			sc = dp.m(k,i,j).M.s*mp[k].p_1mt;
			if( sc > dp.m(k,i,j).E.s ) { dp.m(k,i,j).E.s = sc; dp.m(k,i,j).E.from = state_M; }

			sc = dp.m(k,i,j).Ib.s*mp[k].p_1mt;
			if( sc > dp.m(k,i,j).E.s ) { dp.m(k,i,j).E.s = sc; dp.m(k,i,j).E.from = state_Ib; }

			// switch state
			if( dp.m(k,i,j).E.s > dp.s(i,j).s ) {
				dp.s(i,j).s = dp.m(k,i,j).E.s;
				dp.s(i,j).from = state_E + k;
			}
		} // k

		for( int k = 0; k < nmodels; ++k ) {
			// start state
			dp.m(k,i,j).S.s = dp.s(i,j).s*p_sw[k];
			dp.m(k,i,j).S.from = state_switch;
		} // k

	} // j
	} // i
}

mm_alignment::alignment_chain_ptr mm_alignment::viterbi_traceback( int k, int state, int i, int j,  dna_sequence_region_data &a, dna_sequence_region_data &b, uint16_t pos, bool chain_required, int sid ) {
	using namespace std;
	assert( pos <= 16384 );
  	/*
	cerr << "trace " << k << " ";
	switch( state ) {
		case state_M:
			cerr << "M " << dp.m(k,i,j).M.s.as_base();
			break;
		case state_Ia:
			cerr << "Ia " << dp.m(k,i,j).Ia.s.as_base();
			break;
		case state_Ib:
			cerr << "Ib " << dp.m(k,i,j).Ib.s.as_base();
			break;
	}
	cerr << endl;*/

	alignment_chain_ptr mychain(new alignment_chain());
	annotation_ptr empty_annotation( new annotation() );
	dna_alignment_sequence_ptr emptya( new dna_alignment_sequence()), emptyb( new dna_alignment_sequence() );
	mychain->ann = empty_annotation;
	mychain->align = pairwise_dna_alignment( emptya, emptyb, 0.0 );
	mychain->sid = sid;
	mychain->i_end = i;
	mychain->j_end = j;
	mychain->prev_i = 0;
	switch( state ) {
		case state_M:
			if( dp.m(k,i,j).M.s == 0.0 ) return mychain;
			break;
		case state_Ib:
			if( dp.m(k,i,j).Ib.s == 0.0 ) return mychain;
			break;
		case state_Ia:
			if( dp.m(k,i,j).Ia.s == 0.0 ) return mychain;
			break;
		case state_S:
			if( dp.m(k,i,j).S.s == 0.0 ) return mychain;
			break;
		case state_E:
			if( dp.m(k,i,j).E.s == 0.0 ) return mychain;
			break;
		case state_RIa:
			if( dp.r(i,j).Ia.s == 0.0 ) return mychain;
			break;
		case state_RIb:
			if( dp.r(i,j).Ib.s == 0.0 ) return mychain;
			break;
		case state_switch:
			if( dp.s(i,j).s == 0.0 ) return mychain;
	}

   // alignment sequences
   dna_alignment_sequence_ptr newa( new dna_alignment_sequence()), newb( new dna_alignment_sequence() );

	// annotation
	annotation_ptr myann( new annotation() ), final_ann( new annotation() );
	int rlast_col = -1, rlast_type = k;

   int next_i = i, next_j = j, next_k = k, next_state = state;
	int column = -1;
	bool chain = false, unannotated_columns = false;
	while( i > 0 || j > 0 || (!chain_required && (state != state_switch)) || (chain_required && !chain) ) {
		assert( i >= 0 );
		assert( j >= 0 );
      switch( state ) {
         case state_M:
            next_i = i - 1;
            next_j = j - 1;
				next_state = dp.m(k,i,j).M.from;
				if( (next_state >> 4) > 0 ) { chain = true; break; }
				assert( dp.m(k,i,j).M.from <= 16 );
				dp.m(k,i,j).M.from |= pos << 4;
            newa->data.push_front( a[(uint)i-1] );
            newb->data.push_front( b[(uint)j-1] );
				++column;
				unannotated_columns = true;
            break;
         case state_Ib:
            next_j = j - 1;
            next_state = dp.m(k,i,j).Ib.from;
				if( (next_state >> 4) > 0 ) { chain = true; break; }
				assert( dp.m(k,i,j).Ib.from <= 16 );
				dp.m(k,i,j).Ib.from |= pos << 4;
            newa->data.push_front( dna_alignment_alpha::GAP );
            newb->data.push_front( b[(uint)j-1] );
				++column;
				unannotated_columns = true;
            break;
         case state_Ia:
            next_i = i - 1;
            next_state = dp.m(k,i,j).Ia.from;
				if( (next_state >> 4) > 0 ) { chain = true; break; }
				assert( dp.m(k,i,j).Ia.from <= 16 );
				dp.m(k,i,j).Ia.from |= pos << 4;
            newa->data.push_front( a[(uint)i-1] );
            newb->data.push_front( dna_alignment_alpha::GAP );
				++column;
				unannotated_columns = true;
				break;
			case state_S:
				next_state = dp.m(k,i,j).S.from;
				break;
			case state_E:
				next_state = dp.m(k,i,j).E.from;
				break;
			case state_RIa:
				next_i = i - 1;
            next_state = dp.r(i,j).Ia.from;
				if( (next_state >> 4) > 0 ) { chain = true; break; }
				assert( dp.r(i,j).Ia.from <= 16 );
				dp.r(i,j).Ia.from |= pos << 4;
            newa->data.push_front( a[(uint)i-1] );
            newb->data.push_front( dna_alignment_alpha::GAP );
				++column;
				unannotated_columns = true;
				break;
			case state_RIb:
				next_j = j - 1;
            next_state = dp.r(i,j).Ib.from;
				if( (next_state >> 4) > 0 ) { chain = true; break; }
				assert( dp.r(i,j).Ib.from <= 16 );
				dp.r(i,j).Ib.from |= pos << 4;
            newa->data.push_front( dna_alignment_alpha::GAP );
            newb->data.push_front( b[(uint)j-1] );
				++column;
				unannotated_columns = true;
            break;
			case state_switch:
				if( dp.s(i,j).from == state_RIb ) {
					next_state = state_RIb;
					next_k = -1;
				} else {
					next_state = state_E;
					next_k = dp.s(i,j).from - state_E;
				}
				assert( unannotated_columns );

				// store annotated region
				if( rlast_type >= 0 ) {
					myann->push_back(annotation_interval(rlast_col+1,column,rlast_type+USER_MODEL));
				} else myann->push_back(annotation_interval(rlast_col+1,column,RANDOM_MODEL));
				unannotated_columns = false;

				//cerr << rlast_type << " " << next_k << " " << i << endl;
				rlast_col = column;
				rlast_type = next_k;
				break;

				// we encountered a previous chain
			default:
				cerr << state << "\t" << i << "\t" << j << endl;
				assert( 0 && "error in traceback" );
		}
		// preserve  values if we need to chain
		if( chain ) break;

      i = next_i;
      j = next_j;
		k = next_k;
		state = next_state;
   }
	if( chain_required && !chain ) cerr << "Did not chain: " << i << "\t" << j << "\t" << state << "\t" << k << endl;

	//cerr << " " << i << " " << j << " " << state << " " << k << endl;

	// store final region if needed
	if( unannotated_columns ) {
		if( rlast_type >= 0 ) {
			myann->push_back(annotation_interval(rlast_col+1,column,rlast_type+USER_MODEL));
		} else myann->push_back(annotation_interval(rlast_col+1,column,RANDOM_MODEL));
	}

	// now we need to invert the regions
	for( annotation_riter r = myann->rbegin(); r != myann->rend(); ++r ) {
		final_ann->push_back(annotation_interval(column - r->b, column - r->a, r->t));
	}

	mychain->align = pairwise_dna_alignment( newa, newb, 0.0 );
	mychain->ann = final_ann;
	mychain->i = i;
	mychain->j = j;
	mychain->prev_k = k;
	mychain->prev_i = (next_state >> 4);
	mychain->prev_state = state;

	return mychain;
}

void mm_alignment::do_statistics( std::pair<pairwise_dna_alignment,annotation_ptr> const &r, all_counts &cnt ) {
	assert( r.first.a->data.size() == r.first.b->data.size() );

	for( annotation_iter i = r.second->begin(); i != r.second->end(); ++i ) {

		// record region, here random is type RANDOM_MODEL and the models start at USER_MODEL
		cnt.region_uses[i->t] += 1;
		cnt.region_lengths[i->t] += i->b - i->a + 1;

		int gap_a = -1, gap_b = -1;
		for( int c = i->a; c <= i->b; ++c ) {
			assert( c < (int)r.first.a->data.size() );
			assert( c < (int)r.first.b->data.size() );

			dna_alignment_alpha::symbol c_a = r.first.a->data[c], c_b = r.first.b->data[c];

			// found a pair!
			if( c_a != dna_alignment_alpha::GAP && c_b != dna_alignment_alpha::GAP ) {
				int base_a = (int)dna_alpha::symbol(c_a);
				int base_b = (int)dna_alpha::symbol(c_b);
				cnt.pairs(i->t, base_a, base_b) += 1;
				cnt.bases_a(i->t,base_a) += 1;
				cnt.bases_b(i->t,base_b) += 1;

				// closing a gap in A
				if( gap_a >= 0 ) {
					cnt.gap_lengths_a[i->t] += c - gap_a + 1;
					gap_a = -1;
				}

				// closing a gap in B
				if( gap_b >= 0 ) {
					cnt.gap_lengths_b[i->t] += c - gap_b + 1;
					gap_b = -1;
				}

			// found a gap in A
			} else if( c_a == dna_alignment_alpha::GAP && c_b != dna_alignment_alpha::GAP ) {
				cnt.bases_b(i->t,(int)dna_alpha::symbol(c_b)) += 1;

				// gap is not open
				if( gap_a == -1 ) {
					gap_a = c;
					cnt.gaps_a[i->t] += 1;
				} // otherwise nothing to do

				// closing a gap in B
				if( gap_b >= 0 ) {
					cnt.gap_lengths_b[i->t] += c - gap_b + 1;
					gap_b = -1;
				}

			// found a gap in B
			} else if( c_a != dna_alignment_alpha::GAP && c_b == dna_alignment_alpha::GAP ) {
				cnt.bases_a(i->t,(int)dna_alpha::symbol(c_a)) += 1;

				// closing a gap in A
				if( gap_a >= 0 ) {
					cnt.gap_lengths_a[i->t] += c - gap_a + 1;
					gap_a = -1;
				}

				// gap is not open
				if( gap_b == -1 ) {
					gap_b = c;
				cnt.gaps_b[i->t] += 1;
				} // otherwise nothing to do
			} else {
				using namespace std;
				cerr << c << "\t" << c_a << "\t" << c_b << endl;
				assert( 0 && "corrupt alignment" );
			}

		} // for c

		// finished region, count any last gaps
		// closing a gap in A
		if( gap_a >= 0 ) {
			cnt.gap_lengths_a[i->t] += i->b - gap_a + 1;
			gap_a = -1;
		}

		// closing a gap in B
		if( gap_b >= 0 ) {
			cnt.gap_lengths_b[i->t] += i->b - gap_b + 1;
			gap_b = -1;
		}

	} // for i
}

void mm_alignment::perform_chain( mm_alignment::alignment_chain &chain, mm_alignment::interface_data &past, mm_alignment::interface_data &current ) {
	using namespace std;
	// must be starting region, nothing to chain
	if( chain.prev_i == 0 ) return;

	// if in interface, chain to previous
	if( chain.j == 0 && chain.i < interface*2 && past.alignments[chain.prev_i-1].get() != 0 ) chain.prev = past.alignments[chain.prev_i-1];
	else chain.prev = current.alignments[chain.prev_i-1];

}

std::pair<pairwise_dna_alignment,annotation_ptr> mm_alignment::collapse_chain( alignment_chain &c ) {
	int prev_i = c.prev_i, i = c.i, j = c.j, state = c.prev_state;
	alignment_chain_ptr prev = c.prev;

	using namespace std;
	/*std::cerr << "collapse_chain " << i << "\t" << j << std::endl;
	for( int id = 0; id < c.align.a->data.size(); ++id ) cerr << c.align.a->data[id];
	cerr << endl;
	for( int id = 0; id < c.align.b->data.size(); ++id ) cerr << c.align.b->data[id];
	cerr << endl;*/

	int current_segment = c.sid;
	while( prev_i > 0 ) {
		int col = prev->align.a->data.size()-1, ba = prev->i_end, bb = prev->j_end;

		/*cerr << ba << "\t" << bb << '\t' << i << '\t' << j << endl;
		for( int id = 0; id < prev->align.a->data.size(); ++id ) cerr << prev->align.a->data[id];
		cerr << endl;
		for( int id = 0; id < prev->align.b->data.size(); ++id ) cerr << prev->align.b->data[id];
		cerr << endl;*/

		// first find column to start at if we need to
		//if( !(j == 0 && i < interface*2) ) {
		if( current_segment == prev->sid ) {
			for( ; ba > i || bb > j; --col ) {
				assert( col >= 0 );
				if( prev->align.a->data[col] != dna_alignment_alpha::GAP ) --ba;
				if( prev->align.b->data[col] != dna_alignment_alpha::GAP ) --bb;
			}
			assert( ba == i );
			assert( bb == j );
		} // otherwise we just start at the end
//		cerr << "adjusted " << ba << '\t' << bb << endl;

		// add annotation
		for( annotation_riter k = prev->ann->rbegin(); k != prev->ann->rend(); ++k ) {
			// just add the annotation if not near the end column
			if( k->b <= col ) c.ann->push_front( *k );

			// check if we need to join the annotation intervals
			if( k->a <= col && k->b > col ) c.ann->push_front( annotation_interval(k->a,col,k->t) );

		}

		// add the required columns
		for( ; col >= 0; --col ) {
			c.align.a->data.push_front(prev->align.a->data[col]);
			c.align.b->data.push_front(prev->align.b->data[col]);
			if( prev->align.a->data[col] != dna_alignment_alpha::GAP ) --i;
			if( prev->align.b->data[col] != dna_alignment_alpha::GAP ) --j;
			//cerr << i << '\t' << prev->i << '\t' << j << '\t' << prev->j << endl;
		}

		prev_i = prev->prev_i;
		i = prev->i;
		j = prev->j;
		state = prev->prev_state;
		current_segment = prev->sid;
		prev = prev->prev;
	}

	// now fix annotation
	int c_last = -1;
	for( annotation_iter k = c.ann->begin(); k != c.ann->end(); ++k ) {
		assert( k->b >= 0 );
		assert( k->a >= 0 );
		assert( k->b >= k->a );
		k->b = c_last + 1 + k->b - k->a;
		k->a = c_last + 1;
		c_last = k->b;
	}

	// now merge regions that don't jump
	for( annotation_iter pk = c.ann->begin(), k = ++(c.ann->begin()); k != c.ann->end(); ) {
		if( pk->t == k-> t ) {
			pk->b = k->b;
			k = c.ann->erase(k);
		} else {
			++pk;
			++k;
		}
	}

	return make_pair(c.align,c.ann);
}

void mm_alignment::update_probabilities( mm_alignment::all_counts &counts ) {
	using namespace blitz;

	int total_regions = 0;
	// we have nmodels plus the random region
	for( int k = 0; k < (USER_MODEL+nmodels); ++k ) total_regions += counts.region_uses[k];

	// get combined base count for both sequences
	base_count all_bases(USER_MODEL+nmodels,dna_alpha::SIZE);
	all_bases = counts.bases_a + counts.bases_b;

	// compute probablity dist on regions
	for( int k = 0; k < nmodels; ++k ) {
		using namespace std;
		// region 0 is the random region
		p_sw[k] = (double)counts.region_uses[USER_MODEL+k]/(double)total_regions;

		// set model parameters if we ever use this region and have enough training data
		int total_bases = sum( all_bases(USER_MODEL+k,Range::all()) );

		if( counts.region_uses[USER_MODEL+k] > 0 && total_bases > 1000 ) {
			nw_model_parameters newp;

			double t = 1.0 - 1.0/(double)(counts.region_lengths[USER_MODEL+k]/counts.region_uses[USER_MODEL+k]);

			// compute p for region k
			int total_pairs = sum( counts.pairs(USER_MODEL+k,Range::all(),Range::all()) );
			for( int i = 0; i < dna_alpha::SIZE; ++i ) {
				for( int j = 0; j < dna_alpha::SIZE; ++j ) {
					newp.p(i,j) = (double) counts.pairs(USER_MODEL+k,i,j)/(double)total_pairs;
				} // j
			} // i

			for( int i = 0; i < dna_alpha::SIZE; ++i ) {
				newp.q(i) = (double) all_bases(USER_MODEL+k,i)/(double)total_bases;
			}

			// gap open
			int total_gaps = counts.gaps_a[USER_MODEL+k] + counts.gaps_b[USER_MODEL+k];
			newp.pr_open = (double)total_gaps/(double)total_bases;

			// mean gap length
			newp.mean_gap_length = (double)(counts.gap_lengths_a[USER_MODEL+k]+counts.gap_lengths_b[USER_MODEL+k])/(double)total_gaps;

			// set parameters
			mp[k].set(newp,t);
		}
	}

	// compute probability dist for random region
	p_rand = (double)counts.region_uses[RANDOM_MODEL]/(double)total_regions;

	// gaps in A are insertions in B
	if( counts.gaps_a[RANDOM_MODEL] > 5 ) {
		double mean_len_b = (double)counts.gap_lengths_a[RANDOM_MODEL]/(double)counts.gaps_a[RANDOM_MODEL];
		rp.p_b = 1.0 - 1.0/mean_len_b;
		rp.p_1mb = 1.0/mean_len_b;
	}

	// gaps in B are insertions in A
	if( counts.gaps_b[RANDOM_MODEL] > 5 ) {
		double mean_len_a = (double)counts.gap_lengths_b[RANDOM_MODEL]/(double)counts.gaps_b[RANDOM_MODEL];

		rp.p_a = 1.0 - 1.0/mean_len_a;
		rp.p_1ma = 1.0/mean_len_a;
	}

	// set model parameters if we ever use this region and have enough training data
	int total_bases = sum( all_bases(RANDOM_MODEL,Range::all()) );
	if( total_bases > 1000 ) {
		for( int i = 0; i < dna_alpha::SIZE; ++i ) {
			rp.q(i) = (double) all_bases((int)RANDOM_MODEL,i)/(double)total_bases;
		}
	}

}

void mm_alignment::set_interface_size( int i ) {
	assert( i > 0 );
	interface = i;
}

void mm_alignment::read_parameters( std::istream &in ) {
	using namespace std;
	vector<double> mdist;
	mdist.push_back( read<double>(in) );
	int rand_a = (int)read<double>(in);
	int rand_b = (int)read<double>(in);
	blitz::TinyVector<double,4> q;
	q = read<double>(in), read<double>(in), read<double>(in), read<double>(in);
	set_random_param(rand_a, rand_b, q);

	int nmodels = read<int>(in);
	for( int j = 0; j < nmodels; ++j ) {
		mdist.push_back( read<double>(in) );
		int length = (int)read<double>(in);
		nw_model_parameters param;
		in >> param;
		set_parameters( j, param, length );
	}
	set_region_dist( mdist );
}

void mm_alignment::start_training() {
	my_counts.reset( nmodels );
	my_pr = 0.0;
}

double mm_alignment::end_training() {
	update_probabilities( my_counts );
	return my_pr;
}