/* Copyright (c) 2016 - 2019, the adamantine authors.
 *
 * This file is subject to the Modified BSD License and may not be distributed
 * without copyright and license information. Please refer to the file LICENSE
 * for the text and further information on this license.
 */

#ifndef POST_PROCESSOR_HH
#define POST_PROCESSOR_HH

#include "MaterialProperty.hh"
#include "types.hh"

#include <deal.II/dofs/dof_handler.h>
#include <deal.II/lac/la_parallel_vector.h>
#include <deal.II/numerics/data_out.h>

#include <boost/property_tree/ptree.hpp>

namespace adamantine
{
/**
 * This class outputs the results using the vtu format.
 */
template <int dim>
class PostProcessor
{
public:
  /**
   * Constructor.
   * \param database requires the following entries:
   *   - <B>file_name</B>: string
   */
  PostProcessor(MPI_Comm &communicator,
                boost::property_tree::ptree const &database,
                dealii::DoFHandler<dim> &dof_handler,
                std::shared_ptr<MaterialProperty<dim>> material_properties);

  /**
   * Output the different vtu and pvtu files.
   */
  void output_pvtu(unsigned int cycle, unsigned int n_time_step, double time,
                   dealii::LA::distributed::Vector<double> const &solution);

  /**
   * Output the pvd file for Paraview.
   */
  void output_pvd();

private:
  /**
   * Compute the temperature given the enthalpy.
   */
  dealii::LA::distributed::Vector<double>
  compute_temperature(dealii::LA::distributed::Vector<double> const &enthalpy);

  /**
   * MPI communicator.
   */
  MPI_Comm _communicator;
  /**
   * Root of the different output files.
   */
  std::string _filename;
  /**
   * Vector of pair of time and pvtu file.
   */
  std::vector<std::pair<double, std::string>> _times_filenames;
  /**
   * DataOut associated with the post-processing.
   */
  dealii::DataOut<dim> _data_out;
  /**
   * DoFHandler associated with the simulation.
   */
  dealii::DoFHandler<dim> &_dof_handler;
  /**
   * Material properties associated with the simulation.
   */
  std::shared_ptr<MaterialProperty<dim>> _material_properties;
};
} // namespace adamantine
#endif


//=======================================================================
// Copyright (c) 2013 Piotr Wygocki
//
// 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 local_search_obj_function.hpp
 * @brief
 * @author Piotr Wygocki
 * @version 1.0
 * @date 2013-02-11
 */
#ifndef PAAL_LOCAL_SEARCH_OBJ_FUNCTION_HPP
#define PAAL_LOCAL_SEARCH_OBJ_FUNCTION_HPP

#include "local_search.hpp"
#include "trivial_solution_commit.hpp"
#include "search_obj_func_components.hpp"

#include "paal/data_structures/components/component_traits.hpp"

#include <boost/range/adaptor/transformed.hpp>

namespace paal {
namespace local_search {

/**
 * @brief traits class for search_componentsObjFun
 *
 * @tparam search_componentsObjFun
 */
template <typename SearchComponentsObjFun>
struct search_obj_function_components_traits {
    using get_moves_t = typename data_structures::component_traits<
        SearchComponentsObjFun>::template type<GetMoves>::type;
    using obj_function_t = typename data_structures::component_traits<
        SearchComponentsObjFun>::template type<ObjFunction>::type;
    using commit_t = typename data_structures::component_traits<
        SearchComponentsObjFun>::template type<Commit>::type;
};

namespace detail {

template <typename F, typename GetMoves, typename Commit>
class obj_fun_get_moves {
    F m_f;
    GetMoves const m_get_moves;
    Commit const m_commit;

public:

    obj_fun_get_moves(F f, GetMoves get_moves, Commit commit)
        : m_f(std::move(f)),
          m_get_moves(std::move(get_moves)),
          m_commit(std::move(commit)) {}

    template <typename Solution>
    auto operator()(Solution const &solution) {
        using move = typename move_type_from_get_moves<GetMoves, Solution>::reference;
        return m_get_moves(solution) | boost::adaptors::transformed(
                [&solution, this](move m) {
                        Solution new_solution(solution);
                        m_commit(new_solution, m);
                        return std::make_pair(m, m_f(new_solution));
                    });
    }
};

template <typename Fitness>
class obj_fun_gain {
    Fitness & m_current_res;

public:

    obj_fun_gain(Fitness & current_res)
        : m_current_res(current_res) {}

    template <typename Solution, typename Move>
    auto operator()(Solution const &solution, Move const &move) {
        return move.second - m_current_res;
    }
};

template <typename Commit, typename Fitness>
class obj_fun_commit {
    Commit const m_commit;
    Fitness & m_current_res;

public:

    obj_fun_commit(Commit commit, Fitness & current_res)
        : m_commit(std::move(commit)), m_current_res(current_res) {}

    template <typename Solution, typename Move>
    auto operator()(Solution &solution, Move const &move) {
        if (!m_commit(solution, move.first)) {
            return false;
        }
        m_current_res = move.second;
        return true;
    }
};

template <typename Solution, typename SearchObjFunctionComponents,
          typename Traits = search_obj_function_components_traits<SearchObjFunctionComponents>,
          typename F = typename Traits::obj_function_t,
          typename Fitness = pure_result_of_t<F(Solution &)>>
auto convert_comps(Solution & sol, SearchObjFunctionComponents components, Fitness & current_res) {
    using commit_t = typename Traits::commit_t;
    using get_moves_t = typename Traits::get_moves_t;

    using obj_fun_get_moves = detail::obj_fun_get_moves<F, get_moves_t, commit_t>;
    using obj_fun_gain = detail::obj_fun_gain<Fitness>;
    using obj_fun_commit = detail::obj_fun_commit<commit_t, Fitness>;

    auto get_moves = std::move(components.template get<GetMoves>());
    auto commit = std::move(components.template get<Commit>());
    auto obj_fun = std::move(components.template get<ObjFunction>());

    return make_search_components(
        obj_fun_get_moves(obj_fun, get_moves, commit),
        obj_fun_gain(current_res),
        obj_fun_commit(commit, current_res));
}

} // !detail

///local search function for objective function case.
template <typename SearchStrategy, typename ContinueOnSuccess,
          typename ContinueOnFail, typename Solution,
          typename SearchObjFunctionComponent,
          typename... SearchObjFunctionComponents>
bool local_search_obj_fun(Solution &solution, SearchStrategy searchStrategy,
                          ContinueOnSuccess on_success, ContinueOnFail on_fail,
                          SearchObjFunctionComponent component,
                          SearchObjFunctionComponents ... components) {
    //TODO make it work for many different objective functions
    auto cur_res = component.template call<ObjFunction>(solution);

    return local_search(solution, searchStrategy, std::move(on_success),
                        std::move(on_fail), detail::convert_comps(solution, std::move(component ), cur_res),
                                            detail::convert_comps(solution, std::move(components), cur_res)...);
}

///simple version of local_search_obj_fun - first improving strategy
template <typename Solution, typename... Components>
bool obj_fun_first_improving(Solution &solution, Components... comps) {
    return local_search_obj_fun(solution, first_improving_strategy{},
                                utils::always_true{}, utils::always_false{},
                                std::move(comps)...);
}

///simple version of local_search_obj_fun - best improving strategy
template <typename Solution, typename... Components>
bool obj_fun_best_improving(Solution &solution, Components... comps) {
    return local_search_obj_fun(solution, best_improving_strategy{},
                                utils::always_true{}, utils::always_false{},
                                std::move(comps)...);
}

} // local_search
} // paal

#endif // PAAL_LOCAL_SEARCH_OBJ_FUNCTION_HPP


// graph-tool -- a general graph modification and manipulation thingy
//
// Copyright (C) 2006-2015 Tiago de Paula Peixoto <tiago@skewed.de>
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 3
// of the License, or (at your option) any later version.
//
// This program 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
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.

#include "graph_filtering.hh"

#include <boost/python.hpp>
#include <boost/bind.hpp>

#include "graph.hh"
#include "graph_selectors.hh"
#include "graph_pagerank.hh"

using namespace std;
using namespace boost;
using namespace graph_tool;

size_t pagerank(GraphInterface& g, boost::any rank, boost::any pers,
                boost::any weight, double d, double epsilon, size_t max_iter)
{
    if (!belongs<vertex_floating_properties>()(rank))
        throw ValueException("rank vertex property must have a floating-point value type");

    if (!pers.empty() && !belongs<vertex_scalar_properties>()(pers))
        throw ValueException("personalization vertex property must have a scalar value type");

    typedef ConstantPropertyMap<double, GraphInterface::vertex_t> pers_map_t;
    typedef boost::mpl::push_back<vertex_scalar_properties, pers_map_t>::type
        pers_props_t;

    if(pers.empty())
        pers = pers_map_t(1.0 / g.GetNumberOfVertices());

    typedef ConstantPropertyMap<double, GraphInterface::edge_t> weight_map_t;
    typedef boost::mpl::push_back<edge_scalar_properties, weight_map_t>::type
        weight_props_t;

    if (!weight.empty() && !belongs<edge_scalar_properties>()(weight))
        throw ValueException("weight edge property must have a scalar value type");

    if(weight.empty())
        weight = weight_map_t(1.0);

    size_t iter;
    run_action<>()
        (g, std::bind(get_pagerank(),
                      placeholders::_1, g.GetVertexIndex(), placeholders::_2,
                      placeholders::_3, placeholders::_4, d,
                      epsilon, max_iter, std::ref(iter)),
         vertex_floating_properties(),
         pers_props_t(), weight_props_t())(rank, pers, weight);
    return iter;
}

void export_pagerank()
{
    using namespace boost::python;
    def("get_pagerank", &pagerank);
}


#include "ValueWithError.hpp"
#include "precompiled.hpp"
#include "common.hpp"

#ifdef __clang__
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wglobal-constructors"
#endif // __clang__

#if defined __GNUC__ \
            && ( __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 7) ) \
            && !defined __clang__
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wunused-local-typedefs"
#endif

#include <boost/test/unit_test.hpp>

using namespace error_propagation;

namespace std
{
  template<typename charT, typename traits, typename T>
  basic_ostream<charT,traits>&
  operator<<(basic_ostream<charT,traits>& out, const valarray<T>& arr)
  {
    for(unsigned int i = 0; i != arr.size(); i++)
      out << arr[i] << " ";

    out << endl;

    return out;
  }
}

namespace {

#define BOOST_CHECK_EQUAL_PV_VAL(A, B, C) \
  BOOST_CHECK_PREDICATE( valarray_comp, (A.GetValue())(B) ); \
  BOOST_CHECK_PREDICATE( valarray_comp, (A.GetError())(C) );

  /// Compares two valarray objects and determines if they are equal or not
  ///
  /// Required as operator== of std::valarray returns std::valarray<bool>
  bool valarray_comp( std::valarray<double> const& v1, std::valarray<double> const& v2)
  {
    std::valarray<bool> cmp = ( v1 == v2 );
    bool equal = true;
    for(unsigned int i = 0; i < cmp.size() && equal; i++)
      equal &= cmp[i];

    return equal;
  }

  typedef std::valarray<double> VAD;
  typedef ValueWithError<VAD> VD;

  struct Fixture
  {
   VD pvd;
  };

} // anonymous namespace

namespace boost { namespace math { namespace tools {
  template <> struct promote_args_2<std::valarray<double>, double> {  typedef std::valarray<double> type; };
  template <> struct promote_args_2<double, std::valarray<double> > {  typedef std::valarray<double> type; };
}}}

BOOST_FIXTURE_TEST_SUITE(Test_ValueWithError_Valarray,Fixture)

  BOOST_AUTO_TEST_CASE(default_ctor)
  {
    const VAD val;
    const VAD vd1(1.0,1);
    const VAD vd2(2.0,1);
    BOOST_CHECK_EQUAL_PV_VAL(VD(),val,val);
    BOOST_CHECK_EQUAL_PV_VAL(VD(vd1),vd1,val);
    BOOST_CHECK_EQUAL_PV_VAL(VD(vd1,vd2),vd1,vd2);
  }

  BOOST_AUTO_TEST_CASE(assign)
  {
    VD a, b;
    a = b;
    BOOST_CHECK_EQUAL_PV_VAL(a,b.GetValue(),b.GetError());
  }

TEST_OP(times,*)
TEST_OP(plus,+)
TEST_OP(minus,-)
TEST_OP(div,/)

// Comparison operators don't return bool so they are not usable for our purpose
// TEST_OP_COMPARE(equal,==)
// TEST_OP_COMPARE(not_equal,!=)
// TEST_OP_COMPARE(greater,>)
// TEST_OP_COMPARE(greater_than,>=)
// TEST_OP_COMPARE(smaller,<)
// TEST_OP_COMPARE(smaller_than,<=)

TEST_MATH_ONE_ARG(abs)
TEST_MATH_ONE_ARG(acos)
TEST_MATH_ONE_ARG(asin)
TEST_MATH_ONE_ARG(atan)
TEST_MATH_ONE_ARG(cos)
TEST_MATH_ONE_ARG(cosh)
TEST_MATH_ONE_ARG(exp)
TEST_MATH_ONE_ARG(log)
TEST_MATH_TWO_ARGS(pow)
TEST_MATH_ONE_ARG(sin)
TEST_MATH_ONE_ARG(sinh)
TEST_MATH_ONE_ARG(sqrt)
TEST_MATH_ONE_ARG(tan)
TEST_MATH_ONE_ARG(tanh)

BOOST_AUTO_TEST_SUITE_END() // Test_ValueWithError_Valarray

#ifdef __clang__
#pragma clang diagnostic pop
#endif // __clang__

#if defined __GNUC__ \
            && ( __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 7) ) \
            && !defined __clang__
#pragma GCC diagnostic pop
#endif


//=======================================================================
// Copyright (c)
//
// 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 steiner_network_long_test.cpp
 * @brief
 * @author Piotr Godlewski
 * @version 1.0
 * @date 2013-07-10
 */

#include "test_utils/logger.hpp"
#include "test_utils/read_bounded_deg_mst.hpp"

#include "paal/iterative_rounding/steiner_network/steiner_network.hpp"
#include "paal/utils/irange.hpp"
#include "paal/utils/parse_file.hpp"

#include <boost/graph/adjacency_list.hpp>
#include <boost/graph/connected_components.hpp>
#include <boost/range/join.hpp>
#include <boost/test/unit_test.hpp>

#include <cstdlib>
#include <fstream>
#include <iomanip>
#include <iostream>
#include <iterator>
#include <map>
#include <utility>
#include <vector>

using Graph = boost::adjacency_list<boost::vecS, boost::vecS, boost::undirectedS,
                        boost::property<boost::vertex_index_t, int>,
                        boost::property<boost::edge_weight_t, double>>;
using Traits = boost::adjacency_list_traits<boost::vecS, boost::vecS, boost::undirectedS>;
using Edge = boost::graph_traits<Graph>::edge_descriptor;
using Vertex = boost::graph_traits<Graph>::vertex_descriptor;

using Cost = boost::property_map<Graph, boost::edge_weight_t>::type;

using RestrictionVec = std::vector<std::vector<int>>;
using Edges = std::map<std::pair<int, int>, int>;

namespace {

int rand(int min, int max) { return ::rand() % (max - min + 1) + min; }

int rand_cost() {
    static const int MAX_COST = 100;
    return rand(1, MAX_COST);
}

void generate_restrictions(RestrictionVec & res, int max_res) {
    for (int i : paal::irange(res.size())) {
        for (int j : paal::irange(i+1, int(res.size()))) {
            res[i][j] = res[j][i] = rand(2, max_res);
        }
    }
}

bool non_zero(const std::vector<int> &vec) {
    for (auto v : vec)
        if (v != 0) return true;

    return false;
}

bool non_zero(const RestrictionVec &res) {
    for (auto row : res)
        if (non_zero(row)) return true;

    return false;
}

void dec(std::vector<int> &vec) {
    for (auto &v : vec) v = std::max(v - 1, 0);
}

void add_edge_to_graph(Edges & edges, int u, int v, int & edge_num) {
    auto edge = std::make_pair(std::min(u,v), std::max(u,v));
    if (edges.find(edge) == edges.end()) {
        edges.insert(std::make_pair(edge, 1));
        ++edge_num;
    }
    else {
        ++edges[edge];
        edge_num += 2;
    }
}

void random_tree(Edges & edges, RestrictionVec & restrictions,
        int special_vertices, int vertices_num, int & edge_num) {
    int first = rand(special_vertices, vertices_num - 2);
    std::vector<int> vertices(1, first);

    // Generates edge between current vertex and already processed (tree) vertices.
    auto generate_tree_edge = [&](int v){
        int index = rand(0, vertices.size() - 1);
        add_edge_to_graph(edges, v, vertices[index], edge_num);
        vertices.push_back(v);
    };

    // Generate a random tree on non-special vertices.
    for (auto v : boost::join(paal::irange(first + 1, vertices_num),
                              paal::irange(special_vertices, first))) {
        generate_tree_edge(v);
    }

    // Join special vertices with non-zero restrictions to the random tree.
    // There is be a path in the tree between every pair of vertices,
    // so we decrement special vertices restrictions.
    for (int v : paal::irange(special_vertices)) {
        if (non_zero(restrictions[v])) {
            generate_tree_edge(v);
            dec(restrictions[v]);
        }
    }
}

void fill_graph(Graph & g, const Edges & edges, int & vertices_num) {
    g.clear();
    auto cost = get(boost::edge_weight, g);
    for (auto e : edges) {
        // add regular edge
        paal::add_edge_to_graph(g, cost, e.first.first, e.first.second,
                                rand_cost());
        // add new vertices on multi-edges, so that we get a graph not a
        // multigraph
        for (int i = 1; i < e.second; ++i) {
            paal::add_edge_to_graph(g, cost, e.first.first, vertices_num, rand_cost());
            paal::add_edge_to_graph(g, cost, e.first.second, vertices_num, rand_cost());
            ++vertices_num;
        }
    }
}

struct clique_tag {};
struct sparse_graph_tag {};

int generate_instance(Graph & g, RestrictionVec & res, int vertices_num,
        int min_edge_num, int special_vertices, int max_res, unsigned int seed,
        sparse_graph_tag) {
    srand(seed);
    assert(special_vertices + 1 < vertices_num);
    assert(max_res >= 2);

    int edge_num(0);
    Edges edges;
    res = RestrictionVec(special_vertices, std::vector<int>(special_vertices, 0));

    generate_restrictions(res, max_res);
    RestrictionVec temp_res = res;

    while (non_zero(temp_res)) {
        random_tree(edges, temp_res, special_vertices, vertices_num, edge_num);
    }

    while (edge_num < min_edge_num) {
        int u = rand(0, vertices_num - 1);
        int v = rand(u + 1, vertices_num);
        add_edge_to_graph(edges, u, v, edge_num);
    }

    fill_graph(g, edges, vertices_num);

    LOGLN("edges: " << edge_num);

    return vertices_num;
}

int generate_instance(Graph & g, RestrictionVec & res, int vertices_num,
        int min_edge_num, int restricted_vertices, int max_res, unsigned int seed,
        clique_tag) {
    srand(seed);
    assert(restricted_vertices <= vertices_num);
    assert(max_res >= 2);

    res = RestrictionVec(restricted_vertices, std::vector<int>(restricted_vertices, 0));
    generate_restrictions(res, max_res);

    auto cost = get(boost::edge_weight, g);
    for (int i : paal::irange(vertices_num)) {
        for (int j : paal::irange(i+1, vertices_num)) {
            paal::add_edge_to_graph(g, cost, i, j, rand_cost());
        }
    }

    return vertices_num;
}

template <typename Oracle, typename Restrictions>
void run_single_test(const Graph &g, const Cost &costs,
                     const Restrictions &restrictions) {
    namespace ir = paal::ir;
    namespace lp = paal::lp;

    using ResultNetwork = std::vector<Edge>;
    ResultNetwork result_network;
    auto steiner_network(ir::make_steiner_network<Oracle>(g, restrictions,
                            std::back_inserter(result_network)));
    auto invalid = steiner_network.check_input_validity();
    BOOST_CHECK(!invalid);

    auto result = ir::solve_iterative_rounding(steiner_network,
                        ir::steiner_network_ir_components<>());
    BOOST_CHECK(result.first == lp::OPTIMAL);
}

template <typename Restrictions>
void run_test(const Graph & g, const Cost & costs, const Restrictions & restrictions,
        int vertices_num) {
    // default heuristics
    for (int i : paal::irange(5)) {
        LOGLN("random violated, seed " << i);
        srand(i);
        run_single_test<paal::lp::random_violated_separation_oracle>(
            g, costs, restrictions);
    }

    // non-default heuristics
    if (vertices_num <= 80) {
        LOGLN("most violated");
        run_single_test<paal::lp::max_violated_separation_oracle>(
            g, costs, restrictions);
    }

    // non-default heuristics
    if (vertices_num <= 50) {
        LOGLN("first violated");
        run_single_test<paal::lp::first_violated_separation_oracle>(
            g, costs, restrictions);
    }
}

template <typename GraphTypeTag>
void run_test_case(const std::vector<int> & vertices_num, const std::vector<int> & min_edge_num,
        const std::vector<int> & special_vertices, const std::vector<int> & max_res,
        GraphTypeTag graph_type_tag) {
    Graph g;
    RestrictionVec res;
    auto seed = [](int x) { return 12345 * x; };

    for (int i : paal::irange(vertices_num.size())) {
        int vertices = generate_instance(g, res, vertices_num[i], min_edge_num[i],
                special_vertices[i], max_res[i], seed(i), graph_type_tag);
        LOGLN("vertices: " << vertices);
        LOGLN("special vertices: " << special_vertices[i]);
        LOGLN("max restriction: " << max_res[i]);

        auto restrictions = [&](int i, int j) {
            if (std::size_t(i) < res.size() && std::size_t(j) < res.size())
                return res[i][j];
            else
                return 1;
        };

        run_test(g, get(boost::edge_weight, g), restrictions, vertices);
    }
}

} // namespace

BOOST_AUTO_TEST_SUITE(steiner_network_long)

BOOST_AUTO_TEST_CASE(steiner_network_long_sparse) {
    std::vector<int> vertices_num     = { 6, 10, 10,  10,  15,  20,  40,  50,  75};
    std::vector<int> min_edge_num     = {30, 50, 80, 200, 200, 250, 400, 350, 500};
    std::vector<int> special_vertices = { 4,  4,  6,   5,   5,  10,  15,  15,  20};
    std::vector<int> max_res          = { 3,  3,  5,   6,   7,   5,   8,   6,   6};

    run_test_case(vertices_num, min_edge_num, special_vertices, max_res, sparse_graph_tag());
}

BOOST_AUTO_TEST_CASE(steiner_network_long_clique) {
    std::vector<int> vertices_num     = {10, 20, 40, 60, 80};
    std::vector<int> min_edge_num     = { 0,  0,  0,  0,  0};
    std::vector<int> special_vertices = { 5,  5, 20, 40, 60};
    std::vector<int> max_res          = { 5,  8, 15, 25, 30};

    run_test_case(vertices_num, min_edge_num, special_vertices, max_res, clique_tag());
}

BOOST_AUTO_TEST_SUITE_END()


// Copyright 2013 Christian Henning
// 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)

/// \brief Unit test for subchroma_image type.

#include <boost/test/unit_test.hpp>

#include <boost/gil/gil_all.hpp>

#include <boost/gil/extension/toolbox/color_spaces/ycbcr.hpp>
#include <boost/gil/extension/toolbox/image_types/subchroma_image.hpp>

using namespace std;
using namespace boost;
using namespace gil;

BOOST_AUTO_TEST_SUITE( toolbox_tests )

BOOST_AUTO_TEST_CASE( subchroma_image_test )
{
    {
        ycbcr_601_8_pixel_t a( 10, 20, 30 );
        rgb8_pixel_t b;
        bgr8_pixel_t c;

        color_convert( a, b );
        color_convert( a, c );
        BOOST_ASSERT( static_equal( b, c ));

        color_convert( b, a );
    }

    {
        ycbcr_709_8_pixel_t a( 10, 20, 30 );
        rgb8_pixel_t b;
        bgr8_pixel_t c;

        color_convert( a, b );
        color_convert( a, c );
        BOOST_ASSERT( static_equal( b, c ));

        color_convert( b, a );
    }

    {
        typedef rgb8_pixel_t pixel_t;
        typedef subchroma_image< pixel_t > image_t;

        image_t img( 640, 480 );

        fill_pixels( view( img )
                   , pixel_t( 10, 20, 30 )
                   );
    }

    {
        typedef rgb8_pixel_t pixel_t;

        subchroma_image< pixel_t, mpl::vector_c< int, 4, 4, 4 > > a( 640, 480 ); BOOST_STATIC_ASSERT(( a.ss_X == 1 && a.ss_Y == 1 ));
        subchroma_image< pixel_t, mpl::vector_c< int, 4, 4, 0 > > b( 640, 480 ); BOOST_STATIC_ASSERT(( b.ss_X == 1 && b.ss_Y == 2 ));
        subchroma_image< pixel_t, mpl::vector_c< int, 4, 2, 2 > > c( 640, 480 ); BOOST_STATIC_ASSERT(( c.ss_X == 2 && c.ss_Y == 1 ));
        subchroma_image< pixel_t, mpl::vector_c< int, 4, 2, 0 > > d( 640, 480 ); BOOST_STATIC_ASSERT(( d.ss_X == 2 && d.ss_Y == 2 ));
        subchroma_image< pixel_t, mpl::vector_c< int, 4, 1, 1 > > e( 640, 480 ); BOOST_STATIC_ASSERT(( e.ss_X == 4 && e.ss_Y == 1 ));
        subchroma_image< pixel_t, mpl::vector_c< int, 4, 1, 0 > > f( 640, 480 ); BOOST_STATIC_ASSERT(( f.ss_X == 4 && f.ss_Y == 2 ));

        fill_pixels( view( a ), pixel_t( 10, 20, 30 ) );
        fill_pixels( view( b ), pixel_t( 10, 20, 30 ) );
        fill_pixels( view( c ), pixel_t( 10, 20, 30 ) );
        fill_pixels( view( d ), pixel_t( 10, 20, 30 ) );
        fill_pixels( view( e ), pixel_t( 10, 20, 30 ) );
        fill_pixels( view( f ), pixel_t( 10, 20, 30 ) );

    }

    {
        typedef ycbcr_601_8_pixel_t pixel_t;
        typedef mpl::vector_c< int, 4, 2, 2 > factors_t;
        typedef subchroma_image< pixel_t, factors_t > image_t;

        std::size_t y_width     = 640;
        std::size_t y_height    = 480;

        std::size_t image_size = ( y_width * y_height )
                               + ( y_width / image_t::ss_X ) * ( y_height / image_t::ss_Y )
                               + ( y_width / image_t::ss_X ) * ( y_height / image_t::ss_Y );

        vector< unsigned char > data( image_size );

        image_t::view_t v = subchroma_view< pixel_t, factors_t >( y_width
                                                                , y_height
                                                                , &data.front()
                                                                );
        rgb8_pixel_t p = *v.xy_at( 0, 0 );
    }
}

BOOST_AUTO_TEST_SUITE_END()


// 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.

#ifndef MTL_OPERATION_EXTENDED_COMPLEX_INCLUDE
#define MTL_OPERATION_EXTENDED_COMPLEX_INCLUDE

#include <complex>
#include <boost/utility/enable_if.hpp>
#include <boost/type_traits/is_unsigned.hpp>
#include <boost/numeric/mtl/utility/different_non_complex.hpp>
#include <boost/numeric/mtl/utility/extended_complex.hpp>

// Now we dare writing in the standard namespace
namespace std {

// ========
// Addition
// ========

template <typename T, typename U>
typename mtl::traits::extended_complex<T, U>::type // implicit enable_if
inline operator+(const T& x, const complex<U>& y)
{
    typedef typename mtl::traits::extended_complex<T, U>::type type;
    return type(x + real(y), imag(y));
}

template <typename T, typename U>
typename mtl::traits::extended_complex<T, U>::type // implicit enable_if
inline operator+(const complex<T>& x, const U& y)
{
    typedef typename mtl::traits::extended_complex<T, U>::type type;
    return type(real(x) + y, imag(x));
}

template <typename T, typename U>
typename mtl::traits::extended_complex<T, U>::type // implicit enable_if
inline operator+(const complex<T>& x, const complex<U>& y)
{
    typedef typename mtl::traits::extended_complex<T, U>::type type;
    return type(real(x) + real(y), imag(x) + imag(y));
}

// ===========
// Subtraction
// ===========

namespace detail {

    // To avoid stupid warnings on unary - for unsigned int specialize it
    template <typename T>
    typename boost::disable_if<boost::is_unsigned<T>, T>::type
    inline negate_helper(const T& x)
    { return -x; }

    template <typename T>
    typename boost::enable_if<boost::is_unsigned<T>, T>::type
    inline negate_helper(const T& x)
    { return T(0) - x; }
}

template <typename T, typename U>
typename mtl::traits::extended_complex<T, U>::type // implicit enable_if
inline operator-(const T& x, const complex<U>& y)
{
    typedef typename mtl::traits::extended_complex<T, U>::type type;
    return type(x - real(y), detail::negate_helper(imag(y))); // see above for helper
}

template <typename T, typename U>
typename mtl::traits::extended_complex<T, U>::type // implicit enable_if
inline operator-(const complex<T>& x, const U& y)
{
    typedef typename mtl::traits::extended_complex<T, U>::type type;
    return type(real(x) - y, imag(x));
}

template <typename T, typename U>
typename mtl::traits::extended_complex<T, U>::type // implicit enable_if
inline operator-(const complex<T>& x, const complex<U>& y)
{
    typedef typename mtl::traits::extended_complex<T, U>::type type;
    return type(real(x) - real(y), imag(x) - imag(y));
}

// ==============
// Multiplication
// ==============

template <typename T, typename U>
typename mtl::traits::extended_complex<T, U>::type // implicit enable_if
inline operator*(const T& x, const complex<U>& y)
{
    typedef typename mtl::traits::extended_complex<T, U>::type type;
    return type(x * real(y), x * imag(y));
}

template <typename T, typename U>
typename mtl::traits::extended_complex<T, U>::type // implicit enable_if
inline operator*(const complex<T>& x, const U& y)
{
    typedef typename mtl::traits::extended_complex<T, U>::type type;
    return type(real(x) * y, imag(x) * y);
}

template <typename T, typename U>
typename mtl::traits::extended_complex<T, U>::type // implicit enable_if
inline operator*(const complex<T>& x, const complex<U>& y)
{
    typedef typename mtl::traits::extended_complex<T, U>::type type;
    return type(real(x) * real(y) - imag(x) * imag(y),
		real(x) * imag(y) + imag(x) * real(y));
}

// ========
// Division
// ========
// Not necessarily the most efficient implementations
// Dealing primarily with types

template <typename T, typename U>
typename mtl::traits::extended_complex<T, U>::type // implicit enable_if
inline operator/(const T& x, const complex<U>& y)
{
    typedef typename mtl::traits::extended_complex<T, U>::type type;
    type r(x);
    return r/= type(real(y), imag(y));
}

template <typename T, typename U>
typename mtl::traits::extended_complex<T, U>::type // implicit enable_if
inline operator/(const complex<T>& x, const U& y)
{
    typedef typename mtl::traits::extended_complex<T, U>::type type;
    return type(real(x) / y, imag(x) / y);
}

template <typename T, typename U>
typename mtl::traits::extended_complex<T, U>::type // implicit enable_if
inline operator/(const complex<T>& x, const complex<U>& y)
{
    typedef typename mtl::traits::extended_complex<T, U>::type type;
    type r(real(x), imag(x));
    return r/= type(real(y), imag(y));
}

// ==========
// Comparison
// ==========

template <typename T, typename U>
typename boost::enable_if<mtl::traits::different_non_complex<T, U>, bool>::type
inline operator==(const complex<T>& x, const U& y)
{
    return real(x) == y && imag(x) == T(0);
}

template <typename T, typename U>
typename boost::enable_if<mtl::traits::different_non_complex<T, U>, bool>::type
inline operator==(const T& x, const complex<U>& y)
{
    return x == real(y) && imag(y) == T(0);
}

template <typename T, typename U>
typename boost::enable_if<mtl::traits::different_non_complex<T, U>, bool>::type
inline operator==(const complex<T>& x, const complex<U>& y)
{
    return real(x) == real(y) && imag(x) == imag(y);
}

template <typename T, typename U>
typename boost::enable_if<mtl::traits::different_non_complex<T, U>, bool>::type
inline operator!=(const complex<T>& x, const U& y)
{
    return real(x) != y || imag(x) != T(0);
}

template <typename T, typename U>
typename boost::enable_if<mtl::traits::different_non_complex<T, U>, bool>::type
inline operator!=(const T& x, const complex<U>& y)
{
    return x != real(y) || imag(y) != T(0);
}

template <typename T, typename U>
typename boost::enable_if<mtl::traits::different_non_complex<T, U>, bool>::type
inline operator!=(const complex<T>& x, const complex<U>& y)
{
    return real(x) != real(y) || imag(x) != imag(y);
}

} // namespace std

#endif // MTL_OPERATION_EXTENDED_COMPLEX_INCLUDE


/* Siconos is a program dedicated to modeling, simulation and control
 * of non smooth dynamical systems.
 *
 * Copyright 2016 INRIA.
 *
 * 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 "AlgebraTools.hpp"
#include "SiconosMatrix.hpp"
#include <boost/numeric/ublas/io.hpp>
#include "expm.hpp"

namespace Siconos {
  namespace algebra {
    namespace tools {

      void expm(SiconosMatrix& A, SiconosMatrix& Exp, bool computeAndAdd)
      {
        // Implemented only for dense matrices.
        // Note FP : Maybe it works for others but it has not been
        // tested here --> to be done
        // Do not work with sparse.
        A.resetLU();
        Exp.resetLU();
        assert(Exp.num() == 1 || A.num() == 1);
        if(computeAndAdd)
          *Exp.dense() += expm_pad(*A.dense());
        else
          *Exp.dense() = expm_pad(*A.dense());
      }
    } // namespace tools
  } // namespace algebra
} // namespace Siconos


//          Copyright Rein Halbersma 2010-2021.
// 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)

#include <core/board/group.hpp>                         // action::is_realized, make
#include <core/board/sequence.hpp>                      // nano, micro, checkers, Roman, spantsiretti, international, frisian, ktar<10, 11>,
                                                        // ktar<10, 12>, Compact_10_12, Compact_12_10, basic_board<12, 10>, canadian, srilankan, dumm
#include <dctl/core/board/angle.hpp>                    // _deg, inverse, rotate
#include <dctl/core/board/detail/bit_layout.hpp>        // dimensions
#include <boost/test/unit_test.hpp>                     // BOOST_AUTO_TEST_SUITE, BOOST_AUTO_TEST_SUITE_END, BOOST_AUTO_TEST_CASE_TEMPLATE, BOOST_CHECK
#include <algorithm>                                    // all_of
#include <type_traits>                                  // common_type
#include <vector>                                       // vector

using namespace dctl::core;
using namespace literals;

BOOST_AUTO_TEST_SUITE(GridDimensions)

BOOST_AUTO_TEST_CASE_TEMPLATE(GroupActionIsRealizedForAllCyclicGroupsOnAllDimensions, T, BoardSequence)
{
        auto const op = [](auto i, auto j) { return rotate(i, j); };
        auto const inv = [](auto i) { return inverse(i); };

        auto const C1 = make_group(
                { 0_deg },
                op, inv
        );

        auto const C2 = make_group(
                { 0_deg, 180_deg },
                op, inv
        );

        auto const C4 = make_group(
                { 0_deg,  90_deg, 180_deg, 270_deg },
                op, inv
        );

        using CyclicGroup = std::common_type_t<decltype(C1), decltype(C2), decltype(C4)>;

        auto const C_N = std::vector<CyclicGroup>
        {
                C1, C2, C4
        };

        auto const dim = detail::dimensions{ T::width, T::height, T::coloring };

        BOOST_CHECK(
                std::all_of(C_N.begin(), C_N.begin(), [&](auto const& g) {
                        return group::action::is_realized(dim, g);
                })
        );
}

BOOST_AUTO_TEST_SUITE_END()


#pragma once

#include <Eigen/Dense>

#include "pnc/whole_body_controllers/task.hpp"

/// class JointTask
class JointTask : public Task {
public:
  /// \{ \name Constructor and Destructor
  JointTask(RobotSystem *_robot);

  virtual ~JointTask(){};
  /// \}

private:
  void update_cmd();
  void update_jacobian();
};

/// class SelectedJointTask
class SelectedJointTask : public Task {
public:
  /// \{ \name Constructor and Destructor
  SelectedJointTask(RobotSystem *_robot, std::vector<std::string> _target_ids);

  virtual ~SelectedJointTask(){};
  /// \}

private:
  void update_cmd();
  void update_jacobian();
};

/// class LinkPosTask
class LinkPosTask : public Task {
public:
  /// \{ \name Constructor and Destructor
  LinkPosTask(RobotSystem *_robot, std::vector<std::string> _target_ids);

  virtual ~LinkPosTask(){};
  /// \}

private:
  void update_cmd();
  void update_jacobian();
};

/// class LinkOriTask
class LinkOriTask : public Task {
public:
  /// \{ \name Constructor and Destructor
  LinkOriTask(RobotSystem *_robot, std::vector<std::string> _target_ids);

  virtual ~LinkOriTask(){};
  /// \}

private:
  void update_cmd();
  void update_jacobian();
};

/// class CenterOfMassTask
class CenterOfMassTask : public Task {
public:
  /// \{ \name Constructor and Destructor
  CenterOfMassTask(RobotSystem *_robot);

  virtual ~CenterOfMassTask(){};
  /// \}

private:
  void update_cmd();
  void update_jacobian();
};


/**************************************************************************\
|
|    Copyright (C) 2009 Marc Stevens
|
|    This program is free software: you can redistribute it and/or modify
|    it under the terms of the GNU General Public License as published by
|    the Free Software Foundation, either version 3 of the License, or
|    (at your option) any later version.
|
|    This program 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
|    GNU General Public License for more details.
|
|    You should have received a copy of the GNU General Public License
|    along with this program.  If not, see <http://www.gnu.org/licenses/>.
|
\**************************************************************************/

#include <vector>
#include <string>
#include <map>

#include <boost/lexical_cast.hpp>

#include "main.hpp"

#include <hashclash/timer.hpp>
#include <hashclash/saveload_bz2.hpp>
#include <hashclash/sha1detail.hpp>
#include <hashclash/sha1differentialpath.hpp>
#include <hashclash/booleanfunction.hpp>

using namespace hashclash;
using namespace std;

booleanfunction* sha1bf(unsigned t)
{
	if (t < 20) return &SHA1_F1_data;
	else if (t < 40) return &SHA1_F2_data;
	else if (t < 60) return &SHA1_F3_data;
	return &SHA1_F4_data;
}

#define CHECK_TUNNEL_BITCONDITION(tc,pc) \
	if (tc != bc_constant) { \
		if (tc == bc_one && (pc == bc_zero || pc == bc_plus)) continue; \
		if (tc == bc_zero && (pc == bc_one || pc == bc_minus)) continue; \
	}

inline void connect_helper(unsigned val, unsigned diff, bitcondition& cond, uint32& out, uint32 in, unsigned b)
{
	if (val == 0) {
		if (diff) {
			cond = bc_minus;
			out = in + (1<<b);
		} else {
			cond = bc_constant;
			out = in;
		}
	} else {
		cond = bc_plus;
		out = in - (1<<b);
	}
}
inline void connect_helper(unsigned val, unsigned diff, uint32& out, uint32 in, unsigned b)
{
	if (val == 0) {
		if (diff)
			out = in + (1<<b);
		else
			out = in;
	} else
		out = in - (1<<b);
}
inline bitcondition connect_helper(unsigned val, unsigned diff)
{
	if (val == 0) {
		if (diff)
			return bc_minus;
		else
			return bc_constant;
	} else
		return bc_plus;
}

template<int steps, bool storefdata, bool storenewconds, bool compmincond>
void sha1_connect_thread::connectbits_01234(const pair<connect_bitdata,unsigned>& in2, map<connect_bitdata,unsigned>& out,
				 unsigned b, const sha1differentialpath& lower, const sha1differentialpath& upper,
				 vector< pair<byteconditions,byteconditions> >& newconds, vector<connect_bitdata>& outdata, vector<unsigned>& outmincond)
{
	const connect_bitdata& in = in2.first;
	const unsigned b0 = b;
	const unsigned b1 = (b+30)&31;
	const unsigned b2 = (b+28)&31;
	const unsigned b3 = (b+26)&31;
	const unsigned b4 = (b+24)&31;
/*	 connect_bitdata result;
	 bf_outcome bfo0, bfo1, bfo2, bfo3, bfo4;
	 bf_conditions bfc0, bfc1, bfc2, bfc3, bfc4;
	 bitcondition Qtm3b, Qtm2b, Qtm1b, Qtb, Qtp1b, Qtp2b, Qtp3b;
	 uint32 dft, dftp1, dftp2, dftp3, dftp4;
	 pair<byteconditions,byteconditions> newcond;
	 bitcondition Ttm3b, Ttm2b, Ttm1b, Ttb, Ttp1b, Ttp2b, Ttp3b;
*/	Ttm3b = tbc(t-3,(b0+2)&31);
	Ttm2b = tbc(t-2,(b0+2)&31);
	Ttm1b = tbc(t-1,b0);
	Ttb = tbc(t, b1);
	Ttp1b = tbc(t+1, b2);
	Ttp2b = tbc(t+2, b3);
	Ttp3b = tbc(t+3, b4);

	//update the running bitconditions
	result = in;
	result.rqtm2[1] = result.rqtm2[0];
	result.rqtm1[1] = result.rqtm1[0];
	result.rqt[1] = result.rqt[0];
	result.rqtp1[1] = result.rqtp1[0];

	Qtm3b = lower(t-3,(b0+2)&31);
	Qtm2b = lower(t-2,(b0+2)&31);
	Qtp2b = upper(t+2,b3); if (Qtp2b == bc_prev) { Qtp2b = bc_constant; } //if (storenewconds) throw; }
	Qtp3b = upper(t+3,b4); if (Qtp3b == bc_prev) { Qtp3b = bc_constant; } //if (storenewconds) throw; }
	unsigned dQtm1bit = (in.dQtm1>>b0)&1;
	unsigned dQtbit = (in.dQt>>b1)&1;
	unsigned dQtp1bit = (in.dQtp1>>b2)&1;
	unsigned mmtbit = (mmaskt>>b0)&1;
	unsigned mmtp1bit = (mmasktp1>>b1)&1;
	unsigned mmtp2bit = (mmasktp2>>b2)&1;
	unsigned mmtp3bit = (mmasktp3>>b3)&1;
	unsigned mmtp4bit = (mmasktp4>>b4)&1;
	for (unsigned dqtm1 = 0; dqtm1 <= dQtm1bit; ++dqtm1) {
		connect_helper(dqtm1, dQtm1bit, Qtm1b, result.dQtm1, in.dQtm1, b0);
		if (b0 == 1 && result.dQtm1 != dQtm1b2b31) continue;
		if (b0 == 26 && result.dQtm1 != dQtm1b27b31) continue;
		if (b0 == 31 && (Qtm1b == Qtm1b31not || Qtm1b == Qtm1b31not2)) continue;
		if (b0 == 31)
			bfo0 = msb_bf_outcome(*Ft, Qtm1b, Qtm2b, Qtm3b);
		else
			bfo0 = Ft->outcome(Qtm1b, Qtm2b, Qtm3b);
		for (unsigned bfo0index = 0; bfo0index < bfo0.size(); ++bfo0index) {
			dft = in.dFt - bfo0(bfo0index, b0);
			for (unsigned mmt = 0; mmt <= mmtbit; ++mmt) {
				if (mmt == 1 && b0 == 31) continue;
				connect_helper(mmt, mmtbit, result.dFt, dft, b0);
				if (result.dFt & (1<<b0)) continue;
				if (b0 == 31)
					bfc0 = msb_bf_forwardconditions(*Ft, Qtm1b, Qtm2b, Qtm3b, bfo0[bfo0index]);
				else
					bfc0 = Ft->forwardconditions(Qtm1b, Qtm2b, Qtm3b, bfo0[bfo0index]);
				if ((Qtm1freemask & (1<<b0)) && bfc0.first != bc_constant) continue;
				if ((Qtm2freemask & (1<<((b0+2)&31))) && bfc0.second != bc_constant) continue;
				if ((Qtm3freemask & (1<<((b0+2)&31))) && bfc0.third != bc_constant) continue;
				CHECK_TUNNEL_BITCONDITION(Ttm3b,bfc0.third);
				CHECK_TUNNEL_BITCONDITION(Ttm2b,bfc0.second);
				CHECK_TUNNEL_BITCONDITION(Ttm1b,bfc0.first);
				result.rqtm2[0] = bfc0.second;
				if (steps == 1) {
					if (storefdata) result.fqtm1[b0] = bfc0.first;
					if (storenewconds) {
						newcond.first.set(bc_constant, bc_constant, bc_constant, bc_constant, bc_constant, bc_constant, bfc0.third);
						newcond.second.set(bc_constant, bc_constant, bc_constant, bc_constant, connect_helper(mmt, mmtbit) );
						newconds.push_back(newcond);
						outdata.push_back(result);
						unsigned curcond = in2.second + (bfc0.third==bc_constant?0:1);
						outmincond.push_back(curcond);
					}
					else
					{
						if (compmincond)
						{
							unsigned curcond = in2.second + (bfc0.third==bc_constant?0:1);
							auto itb = out.insert(make_pair(result,curcond));
							if (!itb.second && curcond < itb.first->second)
								itb.first->second = curcond;
						}
						else
							out[result];
					}
					continue;
				}

				usedFtp1 = true;
				for (unsigned dqt = 0; dqt <= dQtbit; ++dqt) {
					connect_helper(dqt, dQtbit, Qtb, result.dQt, in.dQt, b1);
					if (b1 == 1 && result.dQt != dQtb2b31) continue;
					if (b1 == 26 && result.dQt != dQtb27b31) continue;
					if (b1 == 31 && (Qtb == Qtb31not || Qtb == Qtb31not2)) continue;
					if (b1 == 31)
						bfo1 = msb_bf_outcome(*Ftp1, Qtb, bfc0.first, in.rqtm2[1]);
					else
						bfo1 = Ftp1->outcome(Qtb, bfc0.first, in.rqtm2[1]);
					for (unsigned bfo1index = 0; bfo1index < bfo1.size(); ++bfo1index) {
						dftp1 = in.dFtp1 - bfo1(bfo1index, b1);
						for (unsigned mmtp1 = 0; mmtp1 <= mmtp1bit; ++mmtp1) {
							connect_helper(mmtp1, mmtp1bit, result.dFtp1, dftp1, b1);
							if (mmtp1 == 1 && b1 == 31) continue;
							if (result.dFtp1 & (1<<b1)) continue;
							if (b1 == 31)
								bfc1 = msb_bf_forwardconditions(*Ftp1, Qtb, bfc0.first, in.rqtm2[1], bfo1[bfo1index]);
							else
								bfc1 = Ftp1->forwardconditions(Qtb, bfc0.first, in.rqtm2[1], bfo1[bfo1index]);
							if ((Qtfreemask & (1<<b1)) && bfc1.first != bc_constant) continue;
							if ((Qtm1freemask & (1<<b0)) && bfc1.second != bc_constant) continue;
							if ((Qtm2freemask & (1<<b0)) && bfc1.third != bc_constant) continue;
							CHECK_TUNNEL_BITCONDITION(Ttm1b,bfc1.second);
							CHECK_TUNNEL_BITCONDITION(Ttb,bfc1.first);
							result.rqtm1[0] = bfc1.second;
							if (steps == 2) {
								if (storefdata) result.fqt[b1] = bfc1.first;

								if (storenewconds)
								{
									newcond.first.set(bc_constant, bc_constant, bc_constant, bc_constant, bc_constant, bfc1.third, bfc0.third);
									newcond.second.set(bc_constant, bc_constant, bc_constant, connect_helper(mmtp1, mmtp1bit), connect_helper(mmt, mmtbit) );
									newconds.push_back(newcond);
									outdata.push_back(result);
									unsigned curcond = in2.second + (bfc0.third==bc_constant?0:1) + (bfc1.third==bc_constant?0:1);
									outmincond.push_back(curcond);
								}
								else
								{
									if (compmincond)
									{
										unsigned curcond = in2.second + (bfc0.third==bc_constant?0:1) + (bfc1.third==bc_constant?0:1);
										auto itb = out.insert(make_pair(result,curcond));
										if (!itb.second && curcond < itb.first->second)
											itb.first->second = curcond;
									}
									else
										out[result];
								}
								continue;
							}

							usedFtp2 = true;
							for (unsigned dqtp1 = 0; dqtp1 <= dQtp1bit; ++dqtp1) {
								connect_helper(dqtp1, dQtp1bit, Qtp1b, result.dQtp1, in.dQtp1, b2);
								if (b2 == 1 && result.dQtp1 != dQtp1b2b31) continue;
								if (b2 == 26 && result.dQtp1 != dQtp1b27b31) continue;
								if (b2 == 31 && (Qtp1b == Qtp1b31not || Qtp1b == Qtp1b31not2)) continue;
								if (b2 == 31)
									bfo2 = msb_bf_outcome(*Ftp2, Qtp1b, bfc1.first, in.rqtm1[1]);
								else
									bfo2 = Ftp2->outcome(Qtp1b, bfc1.first, in.rqtm1[1]);
								for (unsigned bfo2index = 0; bfo2index < bfo2.size(); ++bfo2index) {
									dftp2 = in.dFtp2 - bfo2(bfo2index, b2);
									for (unsigned mmtp2 = 0; mmtp2 <= mmtp2bit; ++mmtp2) {
										if (mmtp2 == 1 && b2 == 31) continue;
										connect_helper(mmtp2, mmtp2bit, result.dFtp2, dftp2, b2);
										if (result.dFtp2 & (1<<b2)) continue;
										if (b2 == 31)
											bfc2 = msb_bf_forwardconditions(*Ftp2, Qtp1b, bfc1.first, in.rqtm1[1], bfo2[bfo2index]);
										else
											bfc2 = Ftp2->forwardconditions(Qtp1b, bfc1.first, in.rqtm1[1], bfo2[bfo2index]);
										if ((Qtp1freemask & (1<<b2)) && bfc2.first != bc_constant) continue;
										if ((Qtfreemask & (1<<b1)) && bfc2.second != bc_constant) continue;
										if ((Qtm1freemask & (1<<b1)) && bfc2.third != bc_constant) continue;
										CHECK_TUNNEL_BITCONDITION(Ttb,bfc2.second);
										CHECK_TUNNEL_BITCONDITION(Ttp1b,bfc2.first);
										result.rqt[0] = bfc2.second;
										if (steps == 3) {
											if (storefdata) result.fqtp1[b2] = bfc2.first;
											if (storenewconds)
											{
												newcond.first.set(bc_constant, bc_constant, bc_constant, bc_constant, bfc2.third, bfc1.third, bfc0.third);
												newcond.second.set(bc_constant, bc_constant, connect_helper(mmtp2, mmtp2bit),
													connect_helper(mmtp1, mmtp1bit), connect_helper(mmt, mmtbit) );
												newconds.push_back(newcond);
												outdata.push_back(result);
												unsigned curcond = in2.second + (bfc0.third==bc_constant?0:1) + (bfc1.third==bc_constant?0:1) + (bfc2.third==bc_constant?0:1);
												outmincond.push_back(curcond);
											}
											else
											{
												if (compmincond)
												{
													unsigned curcond = in2.second + (bfc0.third==bc_constant?0:1) + (bfc1.third==bc_constant?0:1) + (bfc2.third==bc_constant?0:1);
													auto itb = out.insert(make_pair(result,curcond));
													if (!itb.second && curcond < itb.first->second)
														itb.first->second = curcond;
												}
												else
													out[result];
											}
											continue;
										}

										usedFtp3 = true;
										if (b3 == 31)
											bfo3 = msb_bf_outcome(*Ftp3, Qtp2b, bfc2.first, in.rqt[1]);
										else
											bfo3 = Ftp3->outcome(Qtp2b, bfc2.first, in.rqt[1]);
										for (unsigned bfo3index = 0; bfo3index < bfo3.size(); ++bfo3index) {
											dftp3 = in.dFtp3 - bfo3(bfo3index, b3);
											for (unsigned mmtp3 = 0; mmtp3 <= mmtp3bit; ++mmtp3) {
												if (mmtp3 == 1 && b3 == 31) continue;
												connect_helper(mmtp3, mmtp3bit, result.dFtp3, dftp3, b3);
												if (result.dFtp3 & (1<<b3)) continue;
												if (b3 == 31)
													bfc3 = msb_bf_forwardconditions(*Ftp3, Qtp2b, bfc2.first, in.rqt[1], bfo3[bfo3index]);
												else
													bfc3 = Ftp3->forwardconditions(Qtp2b, bfc2.first, in.rqt[1], bfo3[bfo3index]);
												if ((Qtp2freemask & (1<<b3)) && bfc3.first != bc_constant) continue;
												if ((Qtp1freemask & (1<<b2)) && bfc3.second != bc_constant) continue;
												if ((Qtfreemask & (1<<b2)) && bfc3.third != bc_constant) continue;
												CHECK_TUNNEL_BITCONDITION(Ttp1b,bfc3.second);
												CHECK_TUNNEL_BITCONDITION(Ttp2b,bfc3.first);
												result.rqtp1[0] = bfc3.second;
												if (steps == 4) {
													if (storefdata) result.fqtp2[b3] = bfc3.first;
													if (storenewconds)
													{
														newcond.first.set(bc_constant, bc_constant, bc_constant, bfc3.third, bfc2.third, bfc1.third, bfc0.third);
														newcond.second.set(bc_constant, connect_helper(mmtp3, mmtp3bit),
															connect_helper(mmtp2, mmtp2bit), connect_helper(mmtp1, mmtp1bit), connect_helper(mmt, mmtbit) );
														newconds.push_back(newcond);
														outdata.push_back(result);
														unsigned curcond = in2.second + (bfc0.third==bc_constant?0:1) + (bfc1.third==bc_constant?0:1) + (bfc2.third==bc_constant?0:1)  + (bfc3.third==bc_constant?0:1);
														outmincond.push_back(curcond);
													}
													else
													{
														if (compmincond)
														{
															unsigned curcond = in2.second + (bfc0.third==bc_constant?0:1) + (bfc1.third==bc_constant?0:1) + (bfc2.third==bc_constant?0:1)  + (bfc3.third==bc_constant?0:1);
															auto itb = out.insert(make_pair(result,curcond));
															if (!itb.second && curcond < itb.first->second)
																itb.first->second = curcond;
														}
														else
															out[result];
													}
													continue;
												}

												usedFtp4 = true;
												if (b4 == 31)
													bfo4 = msb_bf_outcome(*Ftp4, Qtp3b, bfc3.first, in.rqtp1[1]);
												else
													bfo4 = Ftp4->outcome(Qtp3b, bfc3.first, in.rqtp1[1]);
												for (unsigned bfo4index = 0; bfo4index < bfo4.size(); ++bfo4index) {
													dftp4 = in.dFtp4 - bfo4(bfo4index, b4);
													for (unsigned mmtp4 = 0; mmtp4 <= mmtp4bit; ++mmtp4) {
														if (mmtp4 == 1 && b4 == 31) continue;
														connect_helper(mmtp4, mmtp4bit, result.dFtp4, dftp4, b4);
														if (result.dFtp4 & (1<<b4)) continue;
															if (b4 == 31)
																bfc4 = msb_bf_forwardconditions(*Ftp4, Qtp3b, bfc3.first, in.rqtp1[1], bfo4[bfo4index]);
															else
																bfc4 = Ftp4->forwardconditions(Qtp3b, bfc3.first, in.rqtp1[1], bfo4[bfo4index]);
															if ((Qtp3freemask & (1<<b4)) && bfc4.first != bc_constant) continue;
															if ((Qtp2freemask & (1<<b3)) && bfc4.second != bc_constant) continue;
															if ((Qtp1freemask & (1<<b3)) && bfc4.third != bc_constant) continue;
															CHECK_TUNNEL_BITCONDITION(Ttp2b,bfc4.second);
															CHECK_TUNNEL_BITCONDITION(Ttp3b,bfc4.first);
														if (storenewconds)
														{
															newcond.first.set(bfc4.first, bfc4.second, bfc4.third, bfc3.third, bfc2.third, bfc1.third, bfc0.third);
															newcond.second.set( connect_helper(mmtp4, mmtp4bit), connect_helper(mmtp3, mmtp3bit),
																connect_helper(mmtp2, mmtp2bit), connect_helper(mmtp1, mmtp1bit), connect_helper(mmt, mmtbit) );
															newconds.push_back(newcond);
															outdata.push_back(result);
															unsigned curcond = in2.second + (bfc0.third==bc_constant?0:1) + (bfc1.third==bc_constant?0:1) + (bfc2.third==bc_constant?0:1) + (bfc3.third==bc_constant?0:1)
																	 + (bfc4.first==bc_constant?0:1) + (bfc4.second==bc_constant?0:1) + (bfc4.third==bc_constant?0:1);
															outmincond.push_back(curcond);
														}
														else
														{
															if (compmincond)
															{
																unsigned curcond = in2.second + (bfc0.third==bc_constant?0:1) + (bfc1.third==bc_constant?0:1) + (bfc2.third==bc_constant?0:1) + (bfc3.third==bc_constant?0:1)
																		 + (bfc4.first==bc_constant?0:1) + (bfc4.second==bc_constant?0:1) + (bfc4.third==bc_constant?0:1);
																auto itb = out.insert(make_pair(result,curcond));
																if (!itb.second && curcond < itb.first->second)
																	itb.first->second = curcond;
															}
															else
																out[result];
														}
													}
												}
											}
										}
									}
								}
							}
						}
					}
				}
			}
		}
	}
}

template<bool storenewconds>
void sha1_connect_thread::connectbits_1234(const pair<connect_bitdata,unsigned>& in2, map<connect_bitdata,unsigned>& out,
				 unsigned b, const sha1differentialpath& lower, const sha1differentialpath& upper,
				 vector< pair<byteconditions,byteconditions> >& newconds, vector<connect_bitdata>& outdata, vector<unsigned>& outmincond)
{
	const connect_bitdata& in = in2.first;
	const unsigned b0 = b&31;
	const unsigned b1 = (b+30)&31;
	const unsigned b2 = (b+28)&31;
	const unsigned b3 = (b+26)&31;
	const unsigned b4 = (b+24)&31;
/*	 connect_bitdata result;
	 bf_outcome bfo1, bfo2, bfo3, bfo4;
	 bf_conditions bfc1, bfc2, bfc3, bfc4;
	 bitcondition Qtm2b, Qtb, Qtp1b, Qtp2b, Qtp3b;
	 uint32 dftp1, dftp2, dftp3, dftp4;
	 pair<byteconditions,byteconditions> newcond;
	 bitcondition Ttm1b, Ttb, Ttp1b, Ttp2b, Ttp3b;
*/
	Ttm1b = tbc(t-1,b0);
	Ttb = tbc(t, b1);
	Ttp1b = tbc(t+1, b2);
	Ttp2b = tbc(t+2, b3);
	Ttp3b = tbc(t+3, b4);

	//update the running bitconditions
	result = in;
	result.rqtm2[1] = result.rqtm2[0];
	result.rqtm1[1] = result.rqtm1[0];
	result.rqt[1] = result.rqt[0];
	result.rqtp1[1] = result.rqtp1[0];

	Qtm2b = lower(t-2,(b0+2)&31);
	Qtp2b = upper(t+2,b3); if (Qtp2b == bc_prev) { Qtp2b = bc_constant; } // if (storenewconds) throw; }
	Qtp3b = upper(t+3,b4); if (Qtp3b == bc_prev) { Qtp3b = bc_constant; } // if (storenewconds) throw; }
	unsigned dQtbit = (in.dQt>>b1)&1;
	unsigned dQtp1bit = (in.dQtp1>>b2)&1;
	unsigned mmtp1bit = (mmasktp1>>b1)&1;
	unsigned mmtp2bit = (mmasktp2>>b2)&1;
	unsigned mmtp3bit = (mmasktp3>>b3)&1;
	unsigned mmtp4bit = (mmasktp4>>b4)&1;

	result.rqtm2[0] = bc_constant;

	usedFtp1 = true;
	if (b0 != 0 && b0 != 1) throw;
	for (unsigned dqt = 0; dqt <= dQtbit; ++dqt) {
		connect_helper(dqt, dQtbit, Qtb, result.dQt, in.dQt, b1);
		if (b1 == 1 && result.dQt != dQtb2b31) continue;
		if (b1 == 26 && result.dQt != dQtb27b31) continue;
		if (b1 == 31 && (Qtb == Qtb31not || Qtb == Qtb31not2)) continue;
		if (b1 == 31)
			bfo1 = msb_bf_outcome(*Ftp1, Qtb, in.fqtm1[b0], in.rqtm2[1]);
		else
			bfo1 = Ftp1->outcome(Qtb, in.fqtm1[b0], in.rqtm2[1]);
		for (unsigned bfo1index = 0; bfo1index < bfo1.size(); ++bfo1index) {
			dftp1 = in.dFtp1 - bfo1(bfo1index, b1);
			for (unsigned mmtp1 = 0; mmtp1 <= mmtp1bit; ++mmtp1) {
				if (mmtp1 == 1 && b1 == 31) continue;
				connect_helper(mmtp1, mmtp1bit, result.dFtp1, dftp1, b1);
				if (result.dFtp1 & (1<<b1)) continue;
				if (b1 == 31)
					bfc1 = msb_bf_forwardconditions(*Ftp1, Qtb, in.fqtm1[b0], in.rqtm2[1], bfo1[bfo1index]);
				else
					bfc1 = Ftp1->forwardconditions(Qtb, in.fqtm1[b0], in.rqtm2[1], bfo1[bfo1index]);
				if ((Qtfreemask & (1<<b1)) && bfc1.first != bc_constant) continue;
				if ((Qtm1freemask & (1<<b0)) && bfc1.second != bc_constant) continue;
				if ((Qtm2freemask & (1<<b0)) && bfc1.third != bc_constant) continue;
				CHECK_TUNNEL_BITCONDITION(Ttm1b,bfc1.second);
				CHECK_TUNNEL_BITCONDITION(Ttb,bfc1.first);
				result.rqtm1[0] = bfc1.second;

				usedFtp2 = true;
				for (unsigned dqtp1 = 0; dqtp1 <= dQtp1bit; ++dqtp1) {
					connect_helper(dqtp1, dQtp1bit, Qtp1b, result.dQtp1, in.dQtp1, b2);
					if (b2 == 1 && result.dQtp1 != dQtp1b2b31) continue;
					if (b2 == 26 && result.dQtp1 != dQtp1b27b31) continue;
					if (b2 == 31 && (Qtp1b == Qtp1b31not || Qtp1b == Qtp1b31not2)) continue;
					if (b2 == 31)
						bfo2 = msb_bf_outcome(*Ftp2, Qtp1b, bfc1.first, in.rqtm1[1]);
					else
						bfo2 = Ftp2->outcome(Qtp1b, bfc1.first, in.rqtm1[1]);
					for (unsigned bfo2index = 0; bfo2index < bfo2.size(); ++bfo2index) {
						dftp2 = in.dFtp2 - bfo2(bfo2index, b2);
						for (unsigned mmtp2 = 0; mmtp2 <= mmtp2bit; ++mmtp2) {
							if (mmtp2 == 1 && b2 == 31) continue;
							connect_helper(mmtp2, mmtp2bit, result.dFtp2, dftp2, b2);
							if (result.dFtp2 & (1<<b2)) continue;
							if (b2 == 31)
								bfc2 = msb_bf_forwardconditions(*Ftp2, Qtp1b, bfc1.first, in.rqtm1[1], bfo2[bfo2index]);
							else
								bfc2 = Ftp2->forwardconditions(Qtp1b, bfc1.first, in.rqtm1[1], bfo2[bfo2index]);
							if ((Qtp1freemask & (1<<b2)) && bfc2.first != bc_constant) continue;
							if ((Qtfreemask & (1<<b1)) && bfc2.second != bc_constant) continue;
							if ((Qtm1freemask & (1<<b1)) && bfc2.third != bc_constant) continue;
							CHECK_TUNNEL_BITCONDITION(Ttb,bfc2.second);
							CHECK_TUNNEL_BITCONDITION(Ttp1b,bfc2.first);
							result.rqt[0] = bfc2.second;

							usedFtp3 = true;
							if (b3 == 31)
								bfo3 = msb_bf_outcome(*Ftp3, Qtp2b, bfc2.first, in.rqt[1]);
							else
								bfo3 = Ftp3->outcome(Qtp2b, bfc2.first, in.rqt[1]);
							for (unsigned bfo3index = 0; bfo3index < bfo3.size(); ++bfo3index) {
								dftp3 = in.dFtp3 - bfo3(bfo3index, b3);
								for (unsigned mmtp3 = 0; mmtp3 <= mmtp3bit; ++mmtp3) {
									if (mmtp3 == 1 && b3 == 31) continue;
									connect_helper(mmtp3, mmtp3bit, result.dFtp3, dftp3, b3);
									if (result.dFtp3 & (1<<b3)) continue;
									if (b3 == 31)
										bfc3 = msb_bf_forwardconditions(*Ftp3, Qtp2b, bfc2.first, in.rqt[1], bfo3[bfo3index]);
									else
										bfc3 = Ftp3->forwardconditions(Qtp2b, bfc2.first, in.rqt[1], bfo3[bfo3index]);
									if ((Qtp2freemask & (1<<b3)) && bfc3.first != bc_constant) continue;
									if ((Qtp1freemask & (1<<b2)) && bfc3.second != bc_constant) continue;
									if ((Qtfreemask & (1<<b2)) && bfc3.third != bc_constant) continue;
									CHECK_TUNNEL_BITCONDITION(Ttp1b,bfc3.second);
									CHECK_TUNNEL_BITCONDITION(Ttp2b,bfc3.first);
									result.rqtp1[0] = bfc3.second;

									usedFtp4 = true;
									if (b4 == 31)
										bfo4 = msb_bf_outcome(*Ftp4, Qtp3b, bfc3.first, in.rqtp1[1]);
									else
										bfo4 = Ftp4->outcome(Qtp3b, bfc3.first, in.rqtp1[1]);
									for (unsigned bfo4index = 0; bfo4index < bfo4.size(); ++bfo4index) {
										dftp4 = in.dFtp4 - bfo4(bfo4index, b4);
										for (unsigned mmtp4 = 0; mmtp4 <= mmtp4bit; ++mmtp4) {
											if (mmtp4 == 1 && b4 == 31) continue;
											connect_helper(mmtp4, mmtp4bit, result.dFtp4, dftp4, b4);
											if (result.dFtp4 & (1<<b4)) continue;
												if (b4 == 31)
													bfc4 = msb_bf_forwardconditions(*Ftp4, Qtp3b, bfc3.first, in.rqtp1[1], bfo4[bfo4index]);
												else
													bfc4 = Ftp4->forwardconditions(Qtp3b, bfc3.first, in.rqtp1[1], bfo4[bfo4index]);
												if ((Qtp3freemask & (1<<b4)) && bfc4.first != bc_constant) continue;
												if ((Qtp2freemask & (1<<b3)) && bfc4.second != bc_constant) continue;
												if ((Qtp1freemask & (1<<b3)) && bfc4.third != bc_constant) continue;
												CHECK_TUNNEL_BITCONDITION(Ttp2b,bfc4.second);
												CHECK_TUNNEL_BITCONDITION(Ttp3b,bfc4.first);
											unsigned curcond = in2.second + (bfc1.third==bc_constant?0:1) + (bfc2.third==bc_constant?0:1) + (bfc3.third==bc_constant?0:1)
													 + (bfc4.first==bc_constant?0:1) + (bfc4.second==bc_constant?0:1) + (bfc4.third==bc_constant?0:1);
											if (storenewconds)
											{
												newcond.first.set(bfc4.first, bfc4.second, bfc4.third, bfc3.third, bfc2.third, bfc1.third);
												newcond.second.set( connect_helper(mmtp4, mmtp4bit), connect_helper(mmtp3, mmtp3bit),
													connect_helper(mmtp2, mmtp2bit), connect_helper(mmtp1, mmtp1bit));
												newconds.push_back(newcond);
												outdata.push_back(result);
												outmincond.push_back(curcond);
											}
											else
											{
												auto itb = out.insert(make_pair(result,curcond));
												if (!itb.second && curcond < itb.first->second)
													itb.first->second = curcond;
											}
										}
									}
								}
							}
						}
					}
				}
			}
		}
	}
}

template<bool storenewconds>
void sha1_connect_thread::connectbits_234(const pair<connect_bitdata,unsigned>& in2, map<connect_bitdata,unsigned>& out,
				 unsigned b, const sha1differentialpath& lower, const sha1differentialpath& upper,
				 vector< pair<byteconditions,byteconditions> >& newconds, vector<connect_bitdata>& outdata, vector<unsigned>& outmincond)
{
	const connect_bitdata& in = in2.first;
	const unsigned b0 = b&31;
	const unsigned b1 = (b+30)&31;
	const unsigned b2 = (b+28)&31;
	const unsigned b3 = (b+26)&31;
	const unsigned b4 = (b+24)&31;
/*	 connect_bitdata result;
	 bf_outcome bfo2, bfo3, bfo4;
	 bf_conditions bfc2, bfc3, bfc4;
	 bitcondition Qtp1b, Qtp2b, Qtp3b;
	 uint32 dftp2, dftp3, dftp4;
	 pair<byteconditions,byteconditions> newcond;
	 bitcondition Ttb, Ttp1b, Ttp2b, Ttp3b;
*/
	Ttb = tbc(t, b1);
	Ttp1b = tbc(t+1, b2);
	Ttp2b = tbc(t+2, b3);
	Ttp3b = tbc(t+3, b4);
	//update the running bitconditions
	result = in;
	result.rqtm2[1] = result.rqtm2[0];
	result.rqtm1[1] = result.rqtm1[0];
	result.rqt[1] = result.rqt[0];
	result.rqtp1[1] = result.rqtp1[0];

	Qtp2b = upper(t+2,b3); if (Qtp2b == bc_prev) { Qtp2b = bc_constant; } // if (storenewconds) throw; }
	Qtp3b = upper(t+3,b4); if (Qtp3b == bc_prev) { Qtp3b = bc_constant; } // if (storenewconds) throw; }
	unsigned dQtp1bit = (in.dQtp1>>b2)&1;
	unsigned mmtp2bit = (mmasktp2>>b2)&1;
	unsigned mmtp3bit = (mmasktp3>>b3)&1;
	unsigned mmtp4bit = (mmasktp4>>b4)&1;

	result.rqtm2[0] = bc_constant;
	result.rqtm1[0] = bc_constant;

	usedFtp2 = true;
	if (b1 != 0 && b1 != 1) throw;
	for (unsigned dqtp1 = 0; dqtp1 <= dQtp1bit; ++dqtp1) {
		connect_helper(dqtp1, dQtp1bit, Qtp1b, result.dQtp1, in.dQtp1, b2);
		if (b2 == 1 && result.dQtp1 != dQtp1b2b31) continue;
		if (b2 == 26 && result.dQtp1 != dQtp1b27b31) continue;
		if (b2 == 31 && (Qtp1b == Qtp1b31not || Qtp1b == Qtp1b31not2)) continue;
		if (b2 == 31)
			bfo2 = msb_bf_outcome(*Ftp2, Qtp1b, in.fqt[b1], in.rqtm1[1]);
		else
			bfo2 = Ftp2->outcome(Qtp1b, in.fqt[b1], in.rqtm1[1]);
		for (unsigned bfo2index = 0; bfo2index < bfo2.size(); ++bfo2index) {
			dftp2 = in.dFtp2 - bfo2(bfo2index, b2);
			for (unsigned mmtp2 = 0; mmtp2 <= mmtp2bit; ++mmtp2) {
				if (mmtp2 == 1 && b2 == 31) continue;
				connect_helper(mmtp2, mmtp2bit, result.dFtp2, dftp2, b2);
				if (result.dFtp2 & (1<<b2)) continue;
				if (b2 == 31)
					bfc2 = msb_bf_forwardconditions(*Ftp2, Qtp1b, in.fqt[b1], in.rqtm1[1], bfo2[bfo2index]);
				else
					bfc2 = Ftp2->forwardconditions(Qtp1b, in.fqt[b1], in.rqtm1[1], bfo2[bfo2index]);
				if ((Qtp1freemask & (1<<b2)) && bfc2.first != bc_constant) continue;
				if ((Qtfreemask & (1<<b1)) && bfc2.second != bc_constant) continue;
				if ((Qtm1freemask & (1<<b1)) && bfc2.third != bc_constant) continue;
				CHECK_TUNNEL_BITCONDITION(Ttb,bfc2.second);
				CHECK_TUNNEL_BITCONDITION(Ttp1b,bfc2.first);
				result.rqt[0] = bfc2.second;

				usedFtp3 = true;
				if (b3 == 31)
					bfo3 = msb_bf_outcome(*Ftp3, Qtp2b, bfc2.first, in.rqt[1]);
				else
					bfo3 = Ftp3->outcome(Qtp2b, bfc2.first, in.rqt[1]);
				for (unsigned bfo3index = 0; bfo3index < bfo3.size(); ++bfo3index) {
					dftp3 = in.dFtp3 - bfo3(bfo3index, b3);
					for (unsigned mmtp3 = 0; mmtp3 <= mmtp3bit; ++mmtp3) {
						if (mmtp3 == 1 && b3 == 31) continue;
						connect_helper(mmtp3, mmtp3bit, result.dFtp3, dftp3, b3);
						if (result.dFtp3 & (1<<b3)) continue;
						if (b3 == 31)
							bfc3 = msb_bf_forwardconditions(*Ftp3, Qtp2b, bfc2.first, in.rqt[1], bfo3[bfo3index]);
						else
							bfc3 = Ftp3->forwardconditions(Qtp2b, bfc2.first, in.rqt[1], bfo3[bfo3index]);
						if ((Qtp2freemask & (1<<b3)) && bfc3.first != bc_constant) continue;
						if ((Qtp1freemask & (1<<b2)) && bfc3.second != bc_constant) continue;
						if ((Qtfreemask & (1<<b2)) && bfc3.third != bc_constant) continue;
						CHECK_TUNNEL_BITCONDITION(Ttp1b,bfc3.second);
						CHECK_TUNNEL_BITCONDITION(Ttp2b,bfc3.first);
						result.rqtp1[0] = bfc3.second;

						usedFtp4 = true;
						if (b4 == 31)
							bfo4 = msb_bf_outcome(*Ftp4, Qtp3b, bfc3.first, in.rqtp1[1]);
						else
							bfo4 = Ftp4->outcome(Qtp3b, bfc3.first, in.rqtp1[1]);
						for (unsigned bfo4index = 0; bfo4index < bfo4.size(); ++bfo4index) {
							dftp4 = in.dFtp4 - bfo4(bfo4index, b4);
							for (unsigned mmtp4 = 0; mmtp4 <= mmtp4bit; ++mmtp4) {
								if (mmtp4 == 1 && b4 == 31) continue;
								connect_helper(mmtp4, mmtp4bit, result.dFtp4, dftp4, b4);
								if (result.dFtp4 & (1<<b4)) continue;
									if (b4 == 31)
										bfc4 = msb_bf_forwardconditions(*Ftp4, Qtp3b, bfc3.first, in.rqtp1[1], bfo4[bfo4index]);
									else
										bfc4 = Ftp4->forwardconditions(Qtp3b, bfc3.first, in.rqtp1[1], bfo4[bfo4index]);
									if ((Qtp3freemask & (1<<b4)) && bfc4.first != bc_constant) continue;
									if ((Qtp2freemask & (1<<b3)) && bfc4.second != bc_constant) continue;
									if ((Qtp1freemask & (1<<b3)) && bfc4.third != bc_constant) continue;
									CHECK_TUNNEL_BITCONDITION(Ttp2b,bfc4.second);
									CHECK_TUNNEL_BITCONDITION(Ttp3b,bfc4.first);
								unsigned curcond = in2.second + (bfc2.third==bc_constant?0:1) + (bfc3.third==bc_constant?0:1)
										 + (bfc4.first==bc_constant?0:1) + (bfc4.second==bc_constant?0:1) + (bfc4.third==bc_constant?0:1);
								if (storenewconds)
								{
									newcond.first.set(bfc4.first, bfc4.second, bfc4.third, bfc3.third, bfc2.third);
									newcond.second.set( connect_helper(mmtp4, mmtp4bit), connect_helper(mmtp3, mmtp3bit),
										connect_helper(mmtp2, mmtp2bit));
									newconds.push_back(newcond);
									outdata.push_back(result);
									outmincond.push_back(curcond);
								}
								else
								{
									auto itb = out.insert(make_pair(result,curcond));
									if (!itb.second && curcond < itb.first->second)
										itb.first->second = curcond;
								}
							}
						}
					}
				}
			}
		}
	}
}

template<bool storenewconds>
void sha1_connect_thread::connectbits_34(const pair<connect_bitdata,unsigned>& in2, map<connect_bitdata,unsigned>& out,
				 unsigned b, const sha1differentialpath& lower, const sha1differentialpath& upper,
				 vector< pair<byteconditions,byteconditions> >& newconds, vector<connect_bitdata>& outdata, vector<unsigned>& outmincond)
{
	const connect_bitdata& in = in2.first;
	const unsigned b0 = b&31;
	const unsigned b1 = (b+30)&31;
	const unsigned b2 = (b+28)&31;
	const unsigned b3 = (b+26)&31;
	const unsigned b4 = (b+24)&31;
/*	 connect_bitdata result;
	 bf_outcome bfo3, bfo4;
	 bf_conditions bfc3, bfc4;
	 bitcondition Qtp2b, Qtp3b;
	 uint32 dftp3, dftp4;
	 pair<byteconditions,byteconditions> newcond;
	 bitcondition Ttp1b, Ttp2b, Ttp3b;
*/
	Ttp1b = tbc(t+1, b2);
	Ttp2b = tbc(t+2, b3);
	Ttp3b = tbc(t+3, b4);
	//update the running bitconditions
	result = in;
	result.rqtm2[1] = result.rqtm2[0];
	result.rqtm1[1] = result.rqtm1[0];
	result.rqt[1] = result.rqt[0];
	result.rqtp1[1] = result.rqtp1[0];

	Qtp2b = upper(t+2,b3); if (Qtp2b == bc_prev) { Qtp2b = bc_constant; } // if (storenewconds) throw; }
	Qtp3b = upper(t+3,b4); if (Qtp3b == bc_prev) { Qtp3b = bc_constant; } // if (storenewconds) throw; }
	unsigned mmtp3bit = (mmasktp3>>b3)&1;
	unsigned mmtp4bit = (mmasktp4>>b4)&1;

	result.rqtm2[0] = bc_constant;
	result.rqtm1[0] = bc_constant;
	result.rqt[0] = bc_constant;

	usedFtp3 = true;
	if (b2 != 0 && b2 != 1) throw;
	if (b3 == 31)
		bfo3 = msb_bf_outcome(*Ftp3, Qtp2b, in.fqtp1[b2], in.rqt[1]);
	else
		bfo3 = Ftp3->outcome(Qtp2b, in.fqtp1[b2], in.rqt[1]);
	for (unsigned bfo3index = 0; bfo3index < bfo3.size(); ++bfo3index) {
		dftp3 = in.dFtp3 - bfo3(bfo3index, b3);
		for (unsigned mmtp3 = 0; mmtp3 <= mmtp3bit; ++mmtp3) {
			if (mmtp3 == 1 && b3 == 31) continue;
			connect_helper(mmtp3, mmtp3bit, result.dFtp3, dftp3, b3);
			if (result.dFtp3 & (1<<b3)) continue;
			if (b3 == 31)
				bfc3 = msb_bf_forwardconditions(*Ftp3, Qtp2b, in.fqtp1[b2], in.rqt[1], bfo3[bfo3index]);
			else
				bfc3 = Ftp3->forwardconditions(Qtp2b, in.fqtp1[b2], in.rqt[1], bfo3[bfo3index]);
			if ((Qtp2freemask & (1<<b3)) && bfc3.first != bc_constant) continue;
			if ((Qtp1freemask & (1<<b2)) && bfc3.second != bc_constant) continue;
			if ((Qtfreemask & (1<<b2)) && bfc3.third != bc_constant) continue;
			CHECK_TUNNEL_BITCONDITION(Ttp1b,bfc3.second);
			CHECK_TUNNEL_BITCONDITION(Ttp2b,bfc3.first);
			result.rqtp1[0] = bfc3.second;

			usedFtp4 = true;
			if (b4 == 31)
				bfo4 = msb_bf_outcome(*Ftp4, Qtp3b, bfc3.first, in.rqtp1[1]);
			else
				bfo4 = Ftp4->outcome(Qtp3b, bfc3.first, in.rqtp1[1]);
			for (unsigned bfo4index = 0; bfo4index < bfo4.size(); ++bfo4index) {
				dftp4 = in.dFtp4 - bfo4(bfo4index, b4);
				for (unsigned mmtp4 = 0; mmtp4 <= mmtp4bit; ++mmtp4) {
					if (mmtp4 == 1 && b4 == 31) continue;
					connect_helper(mmtp4, mmtp4bit, result.dFtp4, dftp4, b4);
					if (result.dFtp4 & (1<<b4)) continue;
						if (b4 == 31)
							bfc4 = msb_bf_forwardconditions(*Ftp4, Qtp3b, bfc3.first, in.rqtp1[1], bfo4[bfo4index]);
						else
							bfc4 = Ftp4->forwardconditions(Qtp3b, bfc3.first, in.rqtp1[1], bfo4[bfo4index]);
						if ((Qtp3freemask & (1<<b4)) && bfc4.first != bc_constant) continue;
						if ((Qtp2freemask & (1<<b3)) && bfc4.second != bc_constant) continue;
						if ((Qtp1freemask & (1<<b3)) && bfc4.third != bc_constant) continue;
						CHECK_TUNNEL_BITCONDITION(Ttp2b,bfc4.second);
						CHECK_TUNNEL_BITCONDITION(Ttp3b,bfc4.first);
					unsigned curcond = in2.second + (bfc3.third==bc_constant?0:1)
							 + (bfc4.first==bc_constant?0:1) + (bfc4.second==bc_constant?0:1) + (bfc4.third==bc_constant?0:1);
					if (storenewconds)
					{
						newcond.first.set(bfc4.first, bfc4.second, bfc4.third, bfc3.third);
						newcond.second.set( connect_helper(mmtp4, mmtp4bit), connect_helper(mmtp3, mmtp3bit));
						newconds.push_back(newcond);
						outdata.push_back(result);
						outmincond.push_back(curcond);
					}
					else
					{
						auto itb = out.insert(make_pair(result,curcond));
						if (!itb.second && curcond < itb.first->second)
							itb.first->second = curcond;
					}
				}
			}
		}
	}
}

template<bool storenewconds>
void sha1_connect_thread::connectbits_4(const pair<connect_bitdata,unsigned>& in2, map<connect_bitdata,unsigned>& out,
				 unsigned b, const sha1differentialpath& lower, const sha1differentialpath& upper,
				 vector< pair<byteconditions,byteconditions> >& newconds, vector<connect_bitdata>& outdata, vector<unsigned>& outmincond)
{
	const connect_bitdata& in = in2.first;
	const unsigned b0 = b&31;
	const unsigned b1 = (b+30)&31;
	const unsigned b2 = (b+28)&31;
	const unsigned b3 = (b+26)&31;
	const unsigned b4 = (b+24)&31;
/*	connect_bitdata result;
	bf_outcome bfo4;
	bf_conditions bfc4;
	bitcondition Qtp2b, Qtp3b;
	uint32 dftp4;
	pair<byteconditions,byteconditions> newcond;
	bitcondition Ttp2b, Ttp3b;
*/
	Ttp2b = tbc(t+2, b3);
	Ttp3b = tbc(t+3, b4);
	//update the running bitconditions
	result = in;
	result.rqtm2[1] = result.rqtm2[0];
	result.rqtm1[1] = result.rqtm1[0];
	result.rqt[1] = result.rqt[0];
	result.rqtp1[1] = result.rqtp1[0];

	Qtp2b = upper(t+2,b3); if (Qtp2b == bc_prev) { Qtp2b = bc_constant; } // if (storenewconds) throw; }
	Qtp3b = upper(t+3,b4); if (Qtp3b == bc_prev) { Qtp3b = bc_constant; } // if (storenewconds) throw; }
	unsigned mmtp4bit = (mmasktp4>>b4)&1;

	result.rqtm2[0] = bc_constant;
	result.rqtm1[0] = bc_constant;
	result.rqt[0] = bc_constant;
	result.rqtp1[0] = bc_constant;

	usedFtp4 = true;
	if (b3 != 0 && b3 != 1) throw;
	if (b4 == 31)
		bfo4 = msb_bf_outcome(*Ftp4, Qtp3b, in.fqtp2[b3], in.rqtp1[1]);
	else
		bfo4 = Ftp4->outcome(Qtp3b, in.fqtp2[b3], in.rqtp1[1]);
	for (unsigned bfo4index = 0; bfo4index < bfo4.size(); ++bfo4index) {
		dftp4 = in.dFtp4 - bfo4(bfo4index, b4);
		for (unsigned mmtp4 = 0; mmtp4 <= mmtp4bit; ++mmtp4) {
			if (mmtp4 == 1 && b4 == 31) continue;
			connect_helper(mmtp4, mmtp4bit, result.dFtp4, dftp4, b4);
			if (result.dFtp4 & (1<<b4)) continue;
				if (b4 == 31)
					bfc4 = msb_bf_forwardconditions(*Ftp4, Qtp3b, in.fqtp2[b3], in.rqtp1[1], bfo4[bfo4index]);
				else
					bfc4 = Ftp4->forwardconditions(Qtp3b, in.fqtp2[b3], in.rqtp1[1], bfo4[bfo4index]);
				if ((Qtp3freemask & (1<<b4)) && bfc4.first != bc_constant) continue;
				if ((Qtp2freemask & (1<<b3)) && bfc4.second != bc_constant) continue;
				if ((Qtp1freemask & (1<<b3)) && bfc4.third != bc_constant) continue;
				CHECK_TUNNEL_BITCONDITION(Ttp2b,bfc4.second);
				CHECK_TUNNEL_BITCONDITION(Ttp3b,bfc4.first);
			unsigned curcond = in2.second
					 + (bfc4.first==bc_constant?0:1) + (bfc4.second==bc_constant?0:1) + (bfc4.third==bc_constant?0:1);
			if (storenewconds)
			{
				newcond.first.set(bfc4.first, bfc4.second, bfc4.third);
				newcond.second.set( connect_helper(mmtp4, mmtp4bit) );
				newconds.push_back(newcond);
				outdata.push_back(result);
				outmincond.push_back(curcond);
			}
			else
			{
				auto itb = out.insert(make_pair(result,curcond));
				if (!itb.second && curcond < itb.first->second)
					itb.first->second = curcond;
			}
		}
	}
}

void set_sdr_bit(sdr& bsdr, unsigned b, bitcondition bc) {
	switch (bc) {
		case bc_constant:
			bsdr.mask &= ~uint32(1<<b);
			bsdr.sign &= bsdr.mask;
			break;
		case bc_plus:
			bsdr.mask |= 1<<b;
			bsdr.sign |= 1<<b;
			break;
		case bc_minus:
			bsdr.mask |= 1<<b;
			bsdr.sign &= ~uint32(1<<b);
			break;
		default:
			throw;
	}
}

unsigned sha1_connect_thread::connect_paths(const sha1differentialpath& lowerpath, const sha1differentialpath& upperpath, connect_bitdata& startdata, path_container& container, bool returnb40)
{
/*	static vector<connect_bitdata> bitdataresults[41];
	static vector<connect_bitdata> bitdatastart[40];
	static vector<connect_bitdata> bitdataend[40];
	static vector< unsigned > bitdatamincond[40];
	static vector< pair<byteconditions,byteconditions> >  bitdatanewcond[40];
	static vector< unsigned > bitdatanewcondhw[40];
	static vector< unsigned > mincond[41];
*/

	bitdataresults[0].clear();
	bitdataresults[0][startdata];

	unsigned b = 0;
	while (b < 32) {
		bitdataresults[b+1].clear();
		usedFtp1 = usedFtp2 = usedFtp3 = usedFtp4 = false;
		if (b < 2)
			for (auto it = bitdataresults[b].begin(); it != bitdataresults[b].end(); ++it)
				connectbits_01234<1,false,false,false>(*it, bitdataresults[b+1], b, lowerpath, upperpath, bitdatanewcond[b], bitdataend[b], bitdatamincond[b]);
		else if (b < 4)
			for (auto it = bitdataresults[b].begin(); it != bitdataresults[b].end(); ++it)
				connectbits_01234<2,false,false,false>(*it, bitdataresults[b+1], b, lowerpath, upperpath, bitdatanewcond[b], bitdataend[b], bitdatamincond[b]);
		else if (b < 6)
			for (auto it = bitdataresults[b].begin(); it != bitdataresults[b].end(); ++it)
				connectbits_01234<3,false,false,false>(*it, bitdataresults[b+1], b, lowerpath, upperpath, bitdatanewcond[b], bitdataend[b], bitdatamincond[b]);
		else if (b < 8)
			for (auto it = bitdataresults[b].begin(); it != bitdataresults[b].end(); ++it)
				connectbits_01234<4,false,false,false>(*it, bitdataresults[b+1], b, lowerpath, upperpath, bitdatanewcond[b], bitdataend[b], bitdatamincond[b]);
		else if (b < 32)
			for (auto it = bitdataresults[b].begin(); it != bitdataresults[b].end(); ++it)
				connectbits_01234<5,false,false,false>(*it, bitdataresults[b+1], b, lowerpath, upperpath, bitdatanewcond[b], bitdataend[b], bitdatamincond[b]);
		else throw;

		if (bitdataresults[b+1].size() == 0)
			return b;
		// remove results that have more conditions than our current limit
//		for (auto it = bitdataresults[b+1].begin(); it != bitdataresults[b+1].end(); )
//			if (it->second > container.bestpathcond)
//				it = bitdataresults[b+1].erase(it);
//			else
//				++it;

		++b;
	}

	unsigned mincond0 = 0;
	for (int k = lowerpath.tbegin(); k < t-3 && k < lowerpath.tend(); ++k)
		mincond0 += lowerpath[k].hw();
	for (int k = t+4; k < upperpath.tend(); ++k)
		mincond0 += upperpath[k].hw();
	bitdataresults[0].clear();
	bitdataresults[0][startdata]=mincond0;

	b = 0;
	while (b < 32+8) {
		bitdataresults[b+1].clear();
		usedFtp1 = usedFtp2 = usedFtp3 = usedFtp4 = false;
		if (b < 2)
			for (auto it = bitdataresults[b].begin(); it != bitdataresults[b].end(); ++it)
				connectbits_01234<1,true,false,true>(*it, bitdataresults[b+1], b, lowerpath, upperpath, bitdatanewcond[b], bitdataend[b], bitdatamincond[b]);
		else if (b < 4)
			for (auto it = bitdataresults[b].begin(); it != bitdataresults[b].end(); ++it)
				connectbits_01234<2,true,false,true>(*it, bitdataresults[b+1], b, lowerpath, upperpath, bitdatanewcond[b], bitdataend[b], bitdatamincond[b]);
		else if (b < 6)
			for (auto it = bitdataresults[b].begin(); it != bitdataresults[b].end(); ++it)
				connectbits_01234<3,true,false,true>(*it, bitdataresults[b+1], b, lowerpath, upperpath, bitdatanewcond[b], bitdataend[b], bitdatamincond[b]);
		else if (b < 8)
			for (auto it = bitdataresults[b].begin(); it != bitdataresults[b].end(); ++it)
				connectbits_01234<4,true,false,true>(*it, bitdataresults[b+1], b, lowerpath, upperpath, bitdatanewcond[b], bitdataend[b], bitdatamincond[b]);
		else if (b < 32)
			for (auto it = bitdataresults[b].begin(); it != bitdataresults[b].end(); ++it)
				connectbits_01234<5,true,false,true>(*it, bitdataresults[b+1], b, lowerpath, upperpath, bitdatanewcond[b], bitdataend[b], bitdatamincond[b]);
		else if (b < 34)
			for (auto it = bitdataresults[b].begin(); it != bitdataresults[b].end(); ++it)
				connectbits_1234<false>(*it, bitdataresults[b+1], b, lowerpath, upperpath, bitdatanewcond[b], bitdataend[b], bitdatamincond[b]);
		else if (b < 36)
			for (auto it = bitdataresults[b].begin(); it != bitdataresults[b].end(); ++it)
				connectbits_234<false>(*it, bitdataresults[b+1], b, lowerpath, upperpath, bitdatanewcond[b], bitdataend[b], bitdatamincond[b]);
		else if (b < 38)
			for (auto it = bitdataresults[b].begin(); it != bitdataresults[b].end(); ++it)
				connectbits_34<false>(*it, bitdataresults[b+1], b, lowerpath, upperpath, bitdatanewcond[b], bitdataend[b], bitdatamincond[b]);
		else if (b < 40)
			for (auto it = bitdataresults[b].begin(); it != bitdataresults[b].end(); ++it)
				connectbits_4<false>(*it, bitdataresults[b+1], b, lowerpath, upperpath, bitdatanewcond[b], bitdataend[b], bitdatamincond[b]);
		else throw;
		if (bitdataresults[b+1].size() == 0)
			break;

		// remove results that have more conditions than our current limit
		for (auto it = bitdataresults[b+1].begin(); it != bitdataresults[b+1].end(); )
			if (it->second > container.bestpathcond)
				it = bitdataresults[b+1].erase(it);
			else
				++it;

		++b;
	}
	if (b < 32+8) return b;
	if (returnb40) return 40;

	unsigned overalmincond = bitdataresults[40].begin()->second;

//	mincond[0].clear();
//	mincond[0].push_back(mincond0);

	for (b = 0; b < 40; ++b)
	{
		bitdatastart[b].clear();
		bitdataend[b].clear();
		bitdatamincond[b].clear();
		bitdatanewcond[b].clear();
		bitdatanewcondhw[b].clear();
		map<connect_bitdata,unsigned> tmpmap;
//		mincond[b+1].clear();
//		mincond[b+1].resize(bitdataresults[b+1].size(),262144);
		if (b < 2)
			for (auto it = bitdataresults[b].begin(); it != bitdataresults[b].end(); ++it)
			{
				tmpmap.clear();
				connectbits_01234<1,true,true>(*it, tmpmap, b, lowerpath, upperpath, bitdatanewcond[b], bitdataend[b], bitdatamincond[b]);
				if (bitdatanewcond[b].size() != bitdataend[b].size()) { cerr << "#bitdatanewcond!=#bitdataend @ b=" << b << endl; throw; }
				bitdatastart[b].resize(bitdataend[b].size(), it->first);
			}
		else if (b < 4)
			for (auto it = bitdataresults[b].begin(); it != bitdataresults[b].end(); ++it)
			{
				tmpmap.clear();
				connectbits_01234<2,true,true>(*it, tmpmap, b, lowerpath, upperpath, bitdatanewcond[b], bitdataend[b], bitdatamincond[b]);
				if (bitdatanewcond[b].size() != bitdataend[b].size()) { cerr << "#bitdatanewcond!=#bitdataend @ b=" << b << endl; throw; }
				bitdatastart[b].resize(bitdataend[b].size(), it->first);
			}
		else if (b < 6)
			for (auto it = bitdataresults[b].begin(); it != bitdataresults[b].end(); ++it)
			{
				tmpmap.clear();
				connectbits_01234<3,true,true>(*it, tmpmap, b, lowerpath, upperpath, bitdatanewcond[b], bitdataend[b], bitdatamincond[b]);
				if (bitdatanewcond[b].size() != bitdataend[b].size()) { cerr << "#bitdatanewcond!=#bitdataend @ b=" << b << endl; throw; }
				bitdatastart[b].resize(bitdataend[b].size(), it->first);
			}
		else if (b < 8)
			for (auto it = bitdataresults[b].begin(); it != bitdataresults[b].end(); ++it) {
				tmpmap.clear();
				connectbits_01234<4,true,true>(*it, tmpmap, b, lowerpath, upperpath, bitdatanewcond[b], bitdataend[b], bitdatamincond[b]);
				if (bitdatanewcond[b].size() != bitdataend[b].size()) { cerr << "#bitdatanewcond!=#bitdataend @ b=" << b << endl; throw; }
				bitdatastart[b].resize(bitdataend[b].size(), it->first);
			}
		else if (b < 32)
			for (auto it = bitdataresults[b].begin(); it != bitdataresults[b].end(); ++it) {
				tmpmap.clear();
				connectbits_01234<5,true,true>(*it, tmpmap, b, lowerpath, upperpath, bitdatanewcond[b], bitdataend[b], bitdatamincond[b]);
				if (bitdatanewcond[b].size() != bitdataend[b].size()) { cerr << "#bitdatanewcond!=#bitdataend @ b=" << b << endl; throw; }
				bitdatastart[b].resize(bitdataend[b].size(), it->first);
			}
		else if (b < 34)
			for (auto it = bitdataresults[b].begin(); it != bitdataresults[b].end(); ++it) {
				tmpmap.clear();
				connectbits_1234<true>(*it, tmpmap, b, lowerpath, upperpath, bitdatanewcond[b], bitdataend[b], bitdatamincond[b]);
				if (bitdatanewcond[b].size() != bitdataend[b].size()) { cerr << "#bitdatanewcond!=#bitdataend @ b=" << b << endl; throw; }
				bitdatastart[b].resize(bitdataend[b].size(), it->first);
			}
		else if (b < 36)
			for (auto it = bitdataresults[b].begin(); it != bitdataresults[b].end(); ++it) {
				tmpmap.clear();
				connectbits_234<true>(*it, tmpmap, b, lowerpath, upperpath, bitdatanewcond[b], bitdataend[b], bitdatamincond[b]);
				if (bitdatanewcond[b].size() != bitdataend[b].size()) { cerr << "#bitdatanewcond!=#bitdataend @ b=" << b << endl; throw; }
				bitdatastart[b].resize(bitdataend[b].size(), it->first);
			}
		else if (b < 38)
			for (auto it = bitdataresults[b].begin(); it != bitdataresults[b].end(); ++it) {
				tmpmap.clear();
				connectbits_34<true>(*it, tmpmap, b, lowerpath, upperpath, bitdatanewcond[b], bitdataend[b], bitdatamincond[b]);
				if (bitdatanewcond[b].size() != bitdataend[b].size()) { cerr << "#bitdatanewcond!=#bitdataend @ b=" << b << endl; throw; }
				bitdatastart[b].resize(bitdataend[b].size(), it->first);
			}
		else if (b < 40)
			for (auto it = bitdataresults[b].begin(); it != bitdataresults[b].end(); ++it) {
				tmpmap.clear();
				connectbits_4<true>(*it, tmpmap, b, lowerpath, upperpath, bitdatanewcond[b], bitdataend[b], bitdatamincond[b]);
				if (bitdatanewcond[b].size() != bitdataend[b].size()) { cerr << "#bitdatanewcond!=#bitdataend @ b=" << b << endl; throw; }
				bitdatastart[b].resize(bitdataend[b].size(), it->first);
			}
		bitdatanewcondhw[b].resize(bitdatanewcond[b].size());
		for (unsigned k = 0; k < bitdatanewcond[b].size(); ++k)
			bitdatanewcondhw[b][k] = bitdatanewcond[b][k].first.hw();
		if (bitdatastart[b].size() != bitdataend[b].size()
			|| bitdataend[b].size() != bitdatanewcond[b].size())
			throw;
	}
//	unsigned overalmincond = mincond[40][0];
	if (container.determinelowestcond) {
		if (overalmincond < container.bestpathcond) {
			container.bestpathcond = overalmincond;
			cout << "Best path: totcond=" << container.bestpathcond << " count=?" << endl;
		}
		return 40;
	}
	if (overalmincond > container.bestpathcond)
		return 40;
	unsigned bestcond2 = 262144;

	/*static*/ sha1differentialpath newpath2;
	newpath2 = lowerpath;
	for (int k = upperpath.tbegin(); k < upperpath.tend(); ++k) {
		newpath2[k] = upperpath[k];
		newpath2.getme(k) = upperpath.getme(k);
	}
	unsigned bindex[40];
	unsigned bcond[40];
	unsigned bit = 39;
	bindex[bit] = 0;
	bcond[bit] = 0;
	//static uint64 badcnt = 0, okcnt = 0;
	while (bit <= 39)
	{
		if (bindex[bit] < bitdataend[bit].size())
		{
			// check and update # conds
//			if (bcond[bit] + bitdatamincond[bit][bindex[bit]] > container.bestpathcond) {
//				--bit;
//				bindex[bit] = bitdataend[bit].size();
//				continue;
//			}
			bcond[bit-1] = bcond[bit] + bitdatanewcondhw[bit][bindex[bit]];
			// set bitconditions for committed bit
			if (bit < 32) {
				newpath2.setbitcondition(t-3,(bit+2)&31,bitdatanewcond[bit][bindex[bit]].first[6]);
				set_sdr_bit(newpath2.getme(t),(bit+0)&31,bitdatanewcond[bit][bindex[bit]].second[4]);
			}
			if (bit >= 2 && bit < 32+2) {
				newpath2.setbitcondition(t-2,(bit+0)&31,bitdatanewcond[bit][bindex[bit]].first[5]);
				set_sdr_bit(newpath2.getme(t+1),(bit+30)&31,bitdatanewcond[bit][bindex[bit]].second[3]);
			}
			if (bit >= 4 && bit < 32+4) {
				newpath2.setbitcondition(t-1,(bit+30)&31,bitdatanewcond[bit][bindex[bit]].first[4]);
				set_sdr_bit(newpath2.getme(t+2),(bit+28)&31,bitdatanewcond[bit][bindex[bit]].second[2]);
			}
			if (bit >= 6 && bit < 32+6) {
				newpath2.setbitcondition(t+0,(bit+28)&31,bitdatanewcond[bit][bindex[bit]].first[3]);
				set_sdr_bit(newpath2.getme(t+3),(bit+26)&31,bitdatanewcond[bit][bindex[bit]].second[1]);
			}
			if (bit >= 8 && bit < 32+8) {
				newpath2.setbitcondition(t+3,(bit+24)&31,bitdatanewcond[bit][bindex[bit]].first[0]);
				newpath2.setbitcondition(t+2,(bit+26)&31,bitdatanewcond[bit][bindex[bit]].first[1]);
				newpath2.setbitcondition(t+1,(bit+26)&31,bitdatanewcond[bit][bindex[bit]].first[2]);
				set_sdr_bit(newpath2.getme(t+4),(bit+24)&31,bitdatanewcond[bit][bindex[bit]].second[0]);
			}

			--bit;
			// if bit==0 then create path
			if (bit != 0) {
				bindex[bit] = bitdataend[bit].size();
				for (unsigned k = 0; k < bitdataend[bit].size(); ++k)
					if (bitdataend[bit][k] == bitdatastart[bit+1][bindex[bit+1]])
					{
						bindex[bit] = k;
						break;
					}
			} else {
				for (unsigned k = 0; k < bitdataend[0].size(); ++k)
					if (bitdataend[0][k] == bitdatastart[1][bindex[1]])
					{ // we have a full path !!!
						if (bcond[0] + bitdatanewcondhw[0][k] + mincond0 > container.bestpathcond) continue;
						bindex[0] = k;
						for (unsigned b = 0; b < 1; ++b)
						{
							if (b < 32) {
								newpath2.setbitcondition(t-3,(b+2)&31,bitdatanewcond[b][bindex[b]].first[6]);
								set_sdr_bit(newpath2.getme(t),(b+0)&31,bitdatanewcond[b][bindex[b]].second[4]);
							}
							if (b >= 2 && b < 32+2) {
								newpath2.setbitcondition(t-2,(b+0)&31,bitdatanewcond[b][bindex[b]].first[5]);
								set_sdr_bit(newpath2.getme(t+1),(b+30)&31,bitdatanewcond[b][bindex[b]].second[3]);
							}
							if (b >= 4 && b < 32+4) {
								newpath2.setbitcondition(t-1,(b+30)&31,bitdatanewcond[b][bindex[b]].first[4]);
								set_sdr_bit(newpath2.getme(t+2),(b+28)&31,bitdatanewcond[b][bindex[b]].second[2]);
							}
							if (b >= 6 && b < 32+6) {
								newpath2.setbitcondition(t+0,(b+28)&31,bitdatanewcond[b][bindex[b]].first[3]);
								set_sdr_bit(newpath2.getme(t+3),(b+26)&31,bitdatanewcond[b][bindex[b]].second[1]);
							}
							if (b >= 8 && b < 32+8) {
								newpath2.setbitcondition(t+3,(b+24)&31,bitdatanewcond[b][bindex[b]].first[0]);
								newpath2.setbitcondition(t+2,(b+26)&31,bitdatanewcond[b][bindex[b]].first[1]);
								newpath2.setbitcondition(t+1,(b+26)&31,bitdatanewcond[b][bindex[b]].first[2]);
								set_sdr_bit(newpath2.getme(t+4),(b+24)&31,bitdatanewcond[b][bindex[b]].second[0]);
							}
						}
						container.push_back(newpath2);
					}
				bindex[bit] = bitdataend[bit].size();
			}
		} else
		{
			++bit;
			if (bit <= 39)
			{
				++bindex[bit];
				if (bit < 39)
				{
					while (bindex[bit] < bitdataend[bit].size()
						&& ((bitdataend[bit][bindex[bit]]) != (bitdatastart[bit+1][bindex[bit+1]])))
						++bindex[bit];
					if (bindex[bit] < bitdataend[bit].size())
						bcond[bit] = bcond[bit+1] + bitdatanewcond[bit+1][bindex[bit+1]].first.hw();
				}
			}
		}
	}
	return 40;
}

void sha1_connect_thread::sha1_connect(const sha1differentialpath& lowerpath
				 , const vector<sha1differentialpath>& upperpaths
				 , path_container& container, unsigned prev_eq_b)
{
	if (lowerpath.path.size() == 0) {
		cout << "lowerpath empty!" << endl;
		return;
	}
	if (dpFt.size() == 0) {
		t = container.t;
		mmaskt = container.m_mask[t];
		mmasktp1 = container.m_mask[t+1];
		mmasktp2 = container.m_mask[t+2];
		mmasktp3 = container.m_mask[t+3];
		mmasktp4 = container.m_mask[t+4];

		Ft = sha1bf(t);
		Ftp1 = sha1bf(t+1);
		Ftp2 = sha1bf(t+2);
		Ftp3 = sha1bf(t+3);
		Ftp4 = sha1bf(t+4);

		dpFt.resize(upperpaths.size());
		dpFtp1.resize(upperpaths.size());
		dpFtp2.resize(upperpaths.size());
		dpFtp3.resize(upperpaths.size());
		dpFtp4.resize(upperpaths.size());
		dQtp1.resize(upperpaths.size());
		for (unsigned i = 0; i < upperpaths.size(); ++i) {
			dpFt[i] = upperpaths[i][t+1].diff();
			dpFtp1[i] = upperpaths[i][t+2].diff() - upperpaths[i][t+1].getsdr().rotate_left(5).adddiff();
			dpFtp2[i] = upperpaths[i][t+3].diff() - upperpaths[i][t+2].getsdr().rotate_left(5).adddiff();
			dpFtp3[i] = upperpaths[i][t+4].diff() - upperpaths[i][t+3].getsdr().rotate_left(5).adddiff();
			dpFtp4[i] = upperpaths[i][t+5].diff() - upperpaths[i][t+4].getsdr().rotate_left(5).adddiff();
			dQtp1[i] = upperpaths[i][t+1].diff();
		}
		prevb.resize(upperpaths.size(),40);

		tbc = container.tunnelconditions;
		tbc.get(t-3); tbc.get(t+3);
		Qtm3freemask = Qtm2freemask = Qtm1freemask = Qtfreemask = Qtp1freemask = Qtp2freemask = Qtp3freemask = 0;
		for (unsigned b = 0; b < 32; ++b) {
			if (container.tunnelconditions(t-3,b) == bc_plus || container.tunnelconditions(t-3,b) == bc_minus) Qtm3freemask |= 1<<b;
			if (container.tunnelconditions(t-2,b) == bc_plus || container.tunnelconditions(t-2,b) == bc_minus) Qtm2freemask |= 1<<b;
			if (container.tunnelconditions(t-1,b) == bc_plus || container.tunnelconditions(t-1,b) == bc_minus) Qtm1freemask |= 1<<b;
			if (container.tunnelconditions(t,b) == bc_plus || container.tunnelconditions(t,b) == bc_minus) Qtfreemask |= 1<<b;
			if (container.tunnelconditions(t+1,b) == bc_plus || container.tunnelconditions(t+1,b) == bc_minus) Qtp1freemask |= 1<<b;
			if (container.tunnelconditions(t+2,b) == bc_plus || container.tunnelconditions(t+2,b) == bc_minus) Qtp2freemask |= 1<<b;
			if (container.tunnelconditions(t+3,b) == bc_plus || container.tunnelconditions(t+3,b) == bc_minus) Qtp3freemask |= 1<<b;
		}
	}
	uint32 dlFt = 0 - lowerpath[t].getsdr().rotate_left(5).adddiff() - lowerpath[t-4].getsdr().rotate_left(30).adddiff();
	uint32 dlFtp1 = 0 - lowerpath[t-3].getsdr().rotate_left(30).adddiff();
	uint32 dlFtp2 = 0 - lowerpath[t-2].getsdr().rotate_left(30).adddiff();
	uint32 dlFtp3 = 0 - lowerpath[t-1].getsdr().rotate_left(30).adddiff();
	uint32 dlFtp4 = 0 - lowerpath[t].getsdr().rotate_left(30).adddiff();

	uint32 dQtm1 = lowerpath[t-1].diff();
	uint32 dQt = lowerpath[t].diff();

	vector<connect_bitdata> bitdataresults[41];
	connect_bitdata startdata;
	startdata.dQtm1 = dQtm1;
	startdata.dQt = dQt;
	for (unsigned j = 0; j < 2; ++j)
	{
		startdata.rqtm2[j] = startdata.rqtm1[j] = startdata.rqt[j] = startdata.rqtp1[j] = bc_constant;
		startdata.fqtm1[j] = startdata.fqt[j] = startdata.fqtp1[j] = startdata.fqtp2[j] = bc_constant;
	}

	Qtm1b31not = Qtb31not = bc_plus;
	Qtm1b31not2 = Qtb31not2 = bc_constant;
	for (unsigned b = 27; b <= 31; ++b) {
		if (lowerpath(t-1,b) == bc_minus) Qtm1b31not = bc_plus;
		if (lowerpath(t-1,b) == bc_plus) Qtm1b31not = bc_minus;
		if (lowerpath(t,b) == bc_minus) Qtb31not = bc_plus;
		if (lowerpath(t,b) == bc_plus) Qtb31not = bc_minus;
	}
	if (lowerpath(t-1,31) == bc_constant) Qtm1b31not2 = Qtm1b31not;
	if (lowerpath(t,31) == bc_constant) Qtb31not2 = Qtb31not;
	sdr sdrQtm1 = lowerpath[t-1].getsdr();
	sdrQtm1.mask &= uint32(0)-uint32(1<<2); sdrQtm1.sign &= sdrQtm1.mask;
	dQtm1b2b31 = sdrQtm1.adddiff();
	sdrQtm1.mask &= uint32(0)-uint32(1<<27); sdrQtm1.sign &= sdrQtm1.mask;
	dQtm1b27b31 = sdrQtm1.adddiff();
	sdr sdrQt = lowerpath[t].getsdr();
	sdrQt.mask &= uint32(0)-uint32(1<<2); sdrQt.sign &= sdrQt.mask;
	dQtb2b31 = sdrQt.adddiff();
	sdrQt.mask &= uint32(0)-uint32(1<<27); sdrQt.sign &= sdrQt.mask;
	dQtb27b31 = sdrQt.adddiff();

	bool connected = false;
	unsigned i = 0;
	unsigned testcount = 0;
	unsigned highestb = 0;
	/*static vector<uint64> bcnt(41,0);*/
	while (i < upperpaths.size()) {
		Qtp1b31not = bc_plus;
		Qtp1b31not2 = bc_constant;
		for (unsigned b = 27; b <= 31; ++b) {
			if (upperpaths[i](t+1,b) == bc_minus) Qtp1b31not = bc_plus;
			if (upperpaths[i](t+1,b) == bc_plus) Qtp1b31not = bc_minus;
		}
		if (upperpaths[i](t+1,31) == bc_constant) Qtp1b31not2 = Qtp1b31not;
		sdr sdrQtp1 = upperpaths[i][t+1].getsdr();
		sdrQtp1.mask &= uint32(0)-uint32(1<<2); sdrQtp1.sign &= sdrQtp1.mask;
		dQtp1b2b31 = sdrQtp1.adddiff();
		sdrQtp1.mask &= uint32(0)-uint32(1<<27); sdrQtp1.sign &= sdrQtp1.mask;
		dQtp1b27b31 = sdrQtp1.adddiff();

		bitdataresults[0].clear();
		startdata.dQtp1 = dQtp1[i];
		startdata.dFt = dpFt[i] + dlFt;
		startdata.dFtp1 = dpFtp1[i] + dlFtp1;
		startdata.dFtp2 = dpFtp2[i] + dlFtp2;
		startdata.dFtp3 = dpFtp3[i] + dlFtp3;
		startdata.dFtp4 = dpFtp4[i] + dlFtp4;
		unsigned b = prevb[i];
		if (b >= prev_eq_b)
			b = connect_paths(lowerpath, upperpaths[i], startdata, container, false);
		prevb[i] = b;
		if (0) { //b == 40) {
			cout << "?" << flush;
			mmaskt = container.m_mask[t];
			mmasktp1 = container.m_mask[t+1];
			mmasktp2 = container.m_mask[t+2];
			mmasktp3 = container.m_mask[t+3];
			mmasktp4 = container.m_mask[t+4];
			b = connect_paths(lowerpath, upperpaths[i], startdata, container, false);
			mmaskt = mmasktp1 = mmasktp2 = mmasktp3 = mmasktp4 = 0;
		}
		if (b < 32+8) {
			unsigned j = i+1;
			uint32 ftmask = ~uint32(0); if (b < 32) ftmask >>= (31-b);
			uint32 ftp1mask = ~uint32(0);
			if (b < 2) ftp1mask = 0; else if (b-2 < 32) { ftp1mask >>=(31-(b-2)); if (!usedFtp1) ftp1mask >>=1; }
			uint32 qtp1mask = ~uint32(0);
			if (b < 4) qtp1mask = 0; else if (b-4 < 32) { qtp1mask >>=(31-(b-4)); if (!usedFtp2) qtp1mask >>=1; }
			uint32 ftp2mask = ~uint32(0);
			if (b < 4) ftp2mask = 0; else if (b-4 < 32) { ftp2mask >>=(31-(b-4)); if (!usedFtp2) ftp2mask >>=1; }
			unsigned qtp2bits = 32;
			if (b < 6) qtp2bits = 0; else if (b-6 < 32) { qtp2bits = b-5; if (!usedFtp3) qtp2bits -= 1; }
			uint32 ftp3mask = ~uint32(0);
			if (b < 6) ftp3mask = 0; else if (b-6 < 32) { ftp3mask >>=(31-(b-6)); if (!usedFtp3) ftp3mask >>=1; }
			unsigned qtp3bits = 32;
			if (b < 8) qtp3bits = 0; else if (b-8 < 32) { qtp3bits = b-7; if (!usedFtp4) qtp3bits -= 1; }
			uint32 ftp4mask = ~uint32(0);
			if (b < 8) ftp4mask = 0; else if (b-8 < 32) { ftp4mask >>=(31-(b-8)); if (!usedFtp4) ftp4mask >>=1; }

			while (j < upperpaths.size()) {
				if ((dpFt[i]^dpFt[j])&ftmask) break;
				if ((dpFtp1[i]^dpFtp1[j])&ftp1mask) break;
				if ((dQtp1[i]^dQtp1[j])&qtp1mask) break;
				if ((dpFtp2[i]^dpFtp2[j])&ftp2mask) break;
				if ((dpFtp3[i]^dpFtp3[j])&ftp3mask) break;
				if ((dpFtp4[i]^dpFtp4[j])&ftp4mask) break;
				bool bcok = true;
				for (unsigned k = 0; bcok && k < qtp2bits; ++k)
					if (upperpaths[i](t+2,k) != upperpaths[j](t+2,k))
						bcok = false;
				for (unsigned k = 0; bcok && k < qtp3bits; ++k)
					if (upperpaths[i](t+3,k) != upperpaths[j](t+3,k))
						bcok = false;
				if (!bcok) break;
				prevb[j]=b;
				++j;
			}
			i = j;
		} else {
			++i;
			connected = true;
		}
		if (b > highestb) highestb = b;
		++bcnt[b];
	}
	if (connected)
		cout << "+" << flush;
	/*static timer sw(true);*/
	if (container.showstats && sw.time() > 3600) {
		cout << endl;
		for (unsigned i = 0; i < bcnt.size(); ++i)
			cout << i << ": " << bcnt[i] << endl;
		sw.start();
	}
	/*static timer sw2(true);*/
	if (sw2.time() > 600) {
		container.save_bestpaths();
		sw2.start();
	}
}


#include <boost/math/bindings/rr.hpp>