// Copyright (c) 2014 The Bitcoin Core developers
// Copyright (c) 2014-2015 The Dash developers
// Copyright (c) 2015-2017 The PIVX developers
// Copyright (c) 2015-2017 The Keyco developers
// Distributed under the MIT/X11 software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.

#include "primitives/transaction.h"
#include "main.h"

#include <boost/test/unit_test.hpp>

BOOST_AUTO_TEST_SUITE(main_tests)

CAmount nMoneySupplyPoWEnd = 18000000 * COIN;

BOOST_AUTO_TEST_CASE(subsidy_limit_test)
{
    CAmount nSum = 0;
    for (int nHeight = 0; nHeight < 1; nHeight += 1) {
        /* premine in block 1 */
        CAmount nSubsidy = GetBlockValue(nHeight);
        BOOST_CHECK(nSubsidy <= 360001 * COIN);
        nSum += nSubsidy;
    }

    for (int nHeight = 1; nHeight < 302; nHeight += 1) {
        CAmount nSubsidy = GetBlockValue(nHeight);
        BOOST_CHECK(nSubsidy <= 1 * COIN);
        nSum += nSubsidy;
    }
    for (int nHeight = 102; nHeight < 21703; nHeight += 1) {
        CAmount nSubsidy = GetBlockValue(nHeight);
        BOOST_CHECK(nSubsidy <= 12 * COIN);
        nSum += nSubsidy;
    }
    for (int nHeight = 21073; nHeight < 50504; nHeight += 1) {
        CAmount nSubsidy = GetBlockValue(nHeight);
        BOOST_CHECK(nSubsidy <= 10 * COIN);
        nSum += nSubsidy;
    }
    for (int nHeight = 50504; nHeight < 86505; nHeight += 1) {
        CAmount nSubsidy = GetBlockValue(nHeight);
        BOOST_CHECK(nSubsidy <= 8 * COIN);
        nSum += nSubsidy;
    }
    for (int nHeight = 86505; nHeight < 129706; nHeight += 1) {
        CAmount nSubsidy = GetBlockValue(nHeight);
        BOOST_CHECK(nSubsidy <= 7 * COIN);
        nSum += nSubsidy;
    }
    for (int nHeight = 129706; nHeight < 180107; nHeight += 1) {
        CAmount nSubsidy = GetBlockValue(nHeight);
        BOOST_CHECK(nSubsidy <= 6 * COIN);
        nSum += nSubsidy;
    }

	BOOST_CHECK(nSum > 0 && nSum <= nMoneySupplyPoWEnd);
}

BOOST_AUTO_TEST_SUITE_END()


/*
 * MIT License
 *
 * Copyright (c) 2021 Dima Dorezyuk
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */
#include <dpose_core/dpose_costmap.hpp>

#include <boost/thread/lock_types.hpp>

#include <algorithm>
#include <array>
#include <cassert>
#include <iterator>
#include <map>
#include <memory>
#include <numeric>
#include <stdexcept>
#include <vector>

namespace dpose_core {

polygon
make_footprint(costmap_2d::LayeredCostmap &_cm) {
  const auto res = _cm.getCostmap()->getResolution();
  // resolution cannot be zero otherwise we get device by zero issues
  if (res <= 0)
    throw std::runtime_error("resolution must be positive");

  // convert the message to a eigen-polygon
  const auto fp = _cm.getFootprint();
  polygon cells(2, fp.size());
  for (int cc = 0; cc != cells.cols(); ++cc) {
    cells(0, cc) = std::round(fp.at(cc).x / res);
    cells(1, cc) = std::round(fp.at(cc).y / res);
  }
  return cells;
}

cell_vector
raytrace(const cell &_begin, const cell &_end) noexcept {
  // original code under raytraceLine in
  // https://github.com/ros-planning/navigation/blob/noetic-devel/costmap_2d/include/costmap_2d/costmap_2d.h
  const cell delta_raw = _end - _begin;
  const cell delta = delta_raw.array().abs();

  cell::Index row_major, row_minor;
  const auto den = delta.maxCoeff(&row_major);
  const auto add = delta.minCoeff(&row_minor);

  // this can happen if both are equal. then we can pick whats minor and whats
  // major.
  if (row_major == row_minor)
    row_minor = 1 - row_major;
  const auto size = den;

  assert(size >= 0 && "negative size detected");

  int num = den / 2;

  // setup the increments. note Eigen's signum returns zero if the argument is
  // zero.
  cell inc_minor = delta_raw.array().sign();
  cell inc_major = inc_minor;

  // erase the "other" row
  inc_minor(row_major) = 0;
  inc_major(row_minor) = 0;

  // allocate space beforehand
  cell_vector ray(size);
  cell curr = _begin;

  // bresenham's iteration
  for (int ii = 0; ii < size; ++ii) {
    ray.at(ii) = curr;

    num += add;
    if (num >= den) {
      num -= den;
      curr += inc_minor;
    }
    curr += inc_major;
  }

  return ray;
}

cell_rays
to_rays(const rectangle<int> &_rect) noexcept {
  cell_rays rays;
  for (int cc = 0; cc != 4; ++cc) {
    const auto ray = raytrace(_rect.col(cc), _rect.col(cc + 1));
    for (const auto &cell : ray)
      rays[cell.y()].extend(cell.x());
  }
  return rays;
}

cell_vector
lethal_cells_within(costmap_2d::Costmap2D &_map,
                    const rectangle<int> &_bounds) {
  // enforce that bounds is within costmap
  rectangle<int> search_box = _bounds.array().max(0).matrix();

  // check if size is zero to prevent underflow
  if (_map.getSizeInCellsX() == 0 || _map.getSizeInCellsY() == 0)
    return {};

  // clang-format off
  search_box.row(0) = search_box.row(0).array().min(_map.getSizeInCellsX() - 1).matrix();
  search_box.row(1) = search_box.row(1).array().min(_map.getSizeInCellsY() - 1).matrix();
  // clang-format on

  // first swipe to count the number of elements
  const auto rays = to_rays(search_box);

  size_t count = 0;

  // lock the costmap
  boost::unique_lock<boost::recursive_mutex> lock(*_map.getMutex());
  const auto char_map = _map.getCharMap();

  // counting first the lethal cells and then writing them into the cell_vector
  // is for the given perf-measurement the fastest way to go.
  for (const auto &ray : rays) {
    const auto &y = ray.first;
    // debug-asserts on the indices
    assert(y >= 0 && "y index cannot be negative");
    assert(y < _map.getSizeInCellsY() && "y index out of bounds");
    assert(ray.second.min >= 0 && "x index cannot be negative");
    assert(ray.second.max < _map.getSizeInCellsX() && "x index out of bounds");

    const auto ii_end = _map.getIndex(ray.second.max + 1, y);
    // branchless formulation of "if(char_map[ii] == _value) {++count;}"
    for (auto ii = _map.getIndex(ray.second.min, y); ii != ii_end; ++ii)
      count += (char_map[ii] == costmap_2d::LETHAL_OBSTACLE);
  }

  if (!count)
    return {};

  // resize the final vector
  cell_vector cells(count);

  // dd is the index of the "destination" (where to write to)
  auto dd_iter = cells.begin();

  // write the cells
  for (const auto &ray : rays) {
    const auto &y = ray.first;
    // the conversion to x-value from index is x = index - (y * x_size). the
    // y_offset is here the second part of the equation.
    const auto y_offset = y * _map.getSizeInCellsX();
    const auto ii_end = _map.getIndex(ray.second.max + 1, y);

    // as in the first swipe, but now we convert the index to cells
    for (auto ii = _map.getIndex(ray.second.min, y); ii != ii_end; ++ii) {
      if (char_map[ii] == costmap_2d::LETHAL_OBSTACLE)
        *dd_iter++ = {ii - y_offset, y};
    }
  }

  assert(dd_iter == cells.end() && "bad index: dd_iter");

  return cells;
}

bool
is_inside(const costmap_2d::Costmap2D &_map,
          const polygon &_footprint) noexcept {
  // get the size of the costmap
  const Eigen::Array2i size(_map.getSizeInCellsX(), _map.getSizeInCellsY());

  const int cols = _footprint.cols();
  for (int cc = 0; cc != cols; ++cc) {
    const Eigen::Array2i point = _footprint.col(cc).array();
    if ((point < 0).any() || (point >= size).any())
      return false;
  }
  return true;
}

namespace bresenham {

base_iterator::base_iterator(const cell &_begin, const cell &_end) noexcept {
  const Eigen::Array2i diff = _end - _begin;

  // init the common members. the naming follows the naming of raytrace.
  x_sign = diff.sign().x();
  den = diff.abs().maxCoeff();
  add = diff.abs().minCoeff();

  // some checks
  assert(den >= 0);
  assert(add >= 0);
  assert(den >= add);

  num = den / 2;
  dx = static_cast<double>(diff.x()) / diff.y();
}

x_minor_ascending::x_minor_ascending(const cell &_begin, const cell &_end) :
    base_iterator(_begin, _end) {
  x_curr = _begin.x();
}

x_minor_ascending &
x_minor_ascending::operator++() noexcept {
  num += add;
  if (num >= den) {
    num -= den;
    x_curr += x_sign;
  }

  assert(num < den && "bresenham failed");
  return *this;
}

x_minor_descending::x_minor_descending(const cell &begin, const cell &end) :
    base_iterator(end, begin) {
  x_curr = begin.x();
}

x_minor_descending &
x_minor_descending::operator++() noexcept {
  num -= add;
  if (num < 0) {
    num += den;
    x_curr -= x_sign;
  }

  assert(num >= 0 && "bresenham failed");
  return *this;
}

x_major_ascending::x_major_ascending(const cell &_begin, const cell &_end) :
    base_iterator(_begin, _end) {
  x_curr = _begin.x();
}

x_major_ascending &
x_major_ascending::operator++() noexcept {
  const int diff = den - num;
  const int step = diff / add;
  num += (step * add);
  x_curr += (step * x_sign);
  // check if we need to add another step to reach den
  if (num < den) {
    num += add;
    x_curr += x_sign;
  }

  assert(num >= den && "logic error");
  num -= den;
  assert(num < den && "logic error");
  return *this;
}

x_major_descending::x_major_descending(const cell &begin, const cell &end) :
    base_iterator(end, begin) {
  x_curr = begin.x();
}

x_major_descending &
x_major_descending::operator++() noexcept {
  assert(den >= add && "den cannot be smaller then add");

  const int step = num / add;
  num -= (step * add);
  x_curr -= (step * x_sign);
  // check if we need to add another step to reach negative numbers
  if (num >= 0) {
    num -= add;
    x_curr -= x_sign;
  }

  assert(num < 0 && "logic error");
  num += den;
  assert(num > 0 && "logic error");
  return *this;
}

}  // namespace bresenham

x_bresenham::x_bresenham(const cell &_c0, const cell &_c1) noexcept {
  const cell diff = (_c1 - _c0).array().abs();
  if (diff.x() > diff.y()) {
    if (_c1.y() > _c0.y())
      impl_ = std::make_unique<bresenham::x_major_ascending>(_c0, _c1);
    else
      impl_ = std::make_unique<bresenham::x_major_descending>(_c1, _c0);
  }
  else {
    if (_c1.y() > _c0.y())
      impl_ = std::make_unique<bresenham::x_minor_ascending>(_c0, _c1);
    else
      impl_ = std::make_unique<bresenham::x_minor_descending>(_c1, _c0);
  }
}

struct line : public x_bresenham {
  line(const cell &_begin, const cell &_end) :
      lower(_begin), upper(_end), x_bresenham(_begin, _end) {}
  cell lower, upper;
  bool active = false;
};

struct lines_compare {
  inline bool
  operator()(const line *_l1, const line *_l2) const noexcept {
    if (_l1->get_x() != _l2->get_x())
      return _l1->get_x() < _l2->get_x();

    return _l1->get_dx() < _l2->get_dx();
  }
};

using lines_set = std::vector<line *>;

/**
 * @brief helper function for the scan-line algorithm.
 *
 * Function iterates over the _maps cells within a scan-line defined by the
 * index yy. It returns true, if no cell has the value _cost.
 *
 * @param _map The costmap.
 * @param _lines Currently active lines. The size must be even.
 * @param yy The index of the scan-line
 * @param _cost The searched cost.
 */
bool
check_line(const costmap_2d::Costmap2D &_map, const lines_set &_lines, int yy,
           uint8_t _cost) {
  // check the input quality - we need an even size because we swipe between the
  // left and right lines.
  assert(_lines.size() % 2 == 0 && "lines.size() must be even");

  // iterate pair-wise within the active-lines. the active lines are sorted
  // by the x-value.
  auto raw_map = _map.getCharMap();
  for (auto l_line = _lines.begin(); l_line != _lines.end();
       std::advance(l_line, 2)) {
    const auto r_line = std::next(l_line);
    // get the x-values
    const int l_x = (*l_line)->get_x() + 1;
    const int r_x = (*r_line)->get_x();

    if (l_x >= r_x)
      continue;

    // swipe through the raw costmap.
    const auto r_index = raw_map + _map.getIndex(r_x, yy);
    auto l_index = raw_map + _map.getIndex(l_x, yy);
    for (; l_index != r_index; ++l_index) {
      if (*l_index == _cost)
        return false;
    }
  }
  return true;
}

/**
 * @brief implements a check if a line or point footprint covers a cell with the
 * value _cost.
 *
 * The _footprint must have less then three vertices: it may be empty, a point
 * or a line. The function will check if any cell covered by the footprint has
 * the value _cost. If such cell exists, it returns false.
 *
 * @param _map The costmap.
 * @param _footprint The footprint (defined by vertices in cell coordinates).
 * @param _cost The interesting cost.
 */
bool
check_without_area(const costmap_2d::Costmap2D &_map, const polygon &_footprint,
                   uint8_t _cost) noexcept {
  // get the number of vertices
  const auto cols = _footprint.cols();

  // setup the check lambda
  auto is_good = [&](const cell __cell) {
    return _map.getCost(__cell.x(), __cell.y()) != _cost;
  };

  switch (cols) {
    case 0: {
      // an empty footprint
      return true;
    }
    case 1: {
      // a point-footprint
      return is_good(_footprint.col(0));
    }
    case 2: {
      // a line-footprint
      const auto cells = raytrace(_footprint.col(0), _footprint.col(1));
      return std::all_of(cells.begin(), cells.end(), is_good);
    }
    default: {
      assert(false && "footprint defines an area");
      return false;
    }
  }
}

/**
 * @brief checks the outline of the _footprint
 *
 * @param _map The costmap.
 * @param _footprint The footprint (closed or opened).
 * @param _cost The cost we are looking after.
 * @return true, if the outline does not contain a cell with _cost.
 */
bool
check_outline(const costmap_2d::Costmap2D &_map, const polygon &_footprint,
              uint8_t _cost) {
  // get the number of vertices
  const auto cols = _footprint.cols();
  assert(cols > 2 && "no outline defined");

  // setup the check lambda
  auto is_good = [&](const cell __cell) {
    return _map.getCost(__cell.x(), __cell.y()) != _cost;
  };

  for (int ii = 1; ii < cols; ++ii) {
    const auto ray = raytrace(_footprint.col(ii - 1), _footprint.col(ii));
    if (!std::all_of(ray.begin(), ray.end(), is_good))
      return false;
  }

  // the footprint might be closed. in this case we can skip the last vertex,
  // since it does not add any information.
  const auto is_closed = _footprint.col(cols - 1) == _footprint.col(0);
  if (!is_closed) {
    const auto ray = raytrace(_footprint.col(cols - 1), _footprint.col(0));
    return std::all_of(ray.begin(), ray.end(), is_good);
  }
  return true;
}

bool
check_footprint(const costmap_2d::Costmap2D &_map, const polygon &_footprint,
                uint8_t _cost) {
  // check if all vertices are within the costmap
  if (!is_inside(_map, _footprint))
    return false;

  const auto cols = _footprint.cols();
  // if we have less then tree vertices we don't have an area defined.
  if (cols < 3)
    return check_without_area(_map, _footprint, _cost);

  // we check the outline seperated, since we have to pay attention to the
  // discretization issues.
  if (!check_outline(_map, _footprint, _cost))
    return false;

  // we are now certain that we have a polygon and will
  // implement the scan-line algorithm below.

  // the footprint might be closed. in this case we can skip the last vertex,
  // since it does not add any information.
  const auto is_closed = _footprint.col(cols - 1) == _footprint.col(0);

  // convert the footprint into lines. a line is defined by two adjacent
  // vertices. assume we have the vertices [a, b, c] (or the closed variant [a,
  // b, c, a]). our edges will be [[a, b], [b, c], [c, a]].
  std::vector<line> lines;
  lines.reserve(cols - is_closed);
  for (int cc = 1; cc != cols; ++cc) {
    // the scan-line algorithm is allergic to horizontal lines; so we skip them.
    if (_footprint(1, cc - 1) != _footprint(1, cc))
      lines.emplace_back(_footprint.col(cc - 1), _footprint.col(cc));
  }
  if (!is_closed && _footprint(1, cols - 1) != _footprint(1, 0))
    lines.emplace_back(_footprint.col(cols - 1), _footprint.col(0));

  // create a map of the vertices mapping the y-value of vertex to its two
  // adjacent edges. assume we have the vertices [a, b, c] and have generated
  // the edges [[a, b], [b, c], [c, a]] which we call [A, B, C]. our map then
  // contains {a.y: [C, A], b.y: [A, B], c.y: [B, C]}.
  using vertex = std::array<line *, 2>;
  std::multimap<int, vertex> vertex_set;
  for (auto l_line = lines.begin(), r_line = std::next(l_line);
       r_line != lines.end(); ++l_line, ++r_line) {
    vertex_set.emplace(r_line->lower.y(), vertex{&(*l_line), &(*r_line)});
  }
  vertex_set.emplace(lines.front().lower.y(),
                     vertex{&lines.back(), &lines.front()});

  // as typical in scan-line algorithm we maintian a set of active lines: the
  // lines are intersected by the current y-value. we sort them by their lowest
  // x-value.
  lines_set active_lines;

  // init the scan-line below the lowerst y-value.
  auto yy = _footprint.row(1).minCoeff() - 1;

  // assume our vertice are [a, b, c]. as above we denote the edges [[a, b], [b,
  // c], [c, a]] as [A, B, C]. the vertices shall have the following coordinates
  // - a: (0, 0)
  // - b: (5, 2)
  // - c: (-1, 3)
  // our yy denotes the current y-value of the scan line and  will range from
  // [-1 ,3]. the vertex_set is  {a.y: [C, A], b.y: [A, B], c.y: [B, C]}. v_next
  // will be the next vertex we can reach.
  // yy = -1:
  //    active_lines: []
  //    v_next: a (with a.y = 0)
  // yy = 0:
  //    active_lines [] -> [A, C]
  //    v_next: b (with b.y = 2)
  // yy = 1:
  //    active_lines [A, C]
  //    v_next: b (with b.y = 2)
  // yy = 2
  //    active_lines [A, C] -> [C, B]
  //    v_next: c (with c.y = 3)
  // yy = 3
  //    active_lines [A, C] -> []
  //    v_next: end

  // iterate over all vertices
  for (auto v_next = vertex_set.begin(); v_next != vertex_set.end();) {
    // our active line set changes only on vertices. we can hence use the
    // current line set until we reach the next vertex.
    for (; yy < v_next->first; ++yy) {
      if (!check_line(_map, active_lines, yy, _cost))
        return false;

      // advance the lines
      for (auto &l : active_lines)
        l->advance_x();
    }

    // we have reached the v_next. we remove ending lines form and add new
    // lines to the active_lines. we iterate until we reach a vertex with a
    // bigger y value, since multiple vertices may be located at the current
    // scan line.
    for (; v_next != vertex_set.end() && v_next->first == yy; ++v_next) {
      // check the two lines of the current vertex
      for (const auto &line : v_next->second) {
        // a line starts, if both if its vertices are equal or above the current
        // scan-line.
        if (line->active) {
          auto iter = std::find(active_lines.begin(), active_lines.end(), line);
          assert(iter != active_lines.end() && "line not found");
          // swap and pop - we don't care much for the order since we will sort
          // the vertices again.
          std::iter_swap(iter, std::prev(active_lines.end()));
          active_lines.pop_back();
        }
        else {
          line->active = true;
          active_lines.emplace_back(line);
        }
      }
    }
    // reorder the active lines after we have changed them
    std::sort(active_lines.begin(), active_lines.end(), lines_compare{});
  }
  return true;
}

}  // namespace dpose_core


/**************************************************************************\
|
|    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 "main.hpp"
#include <sstream>
#include <boost/lexical_cast.hpp>
#include <locale>

#define MAX_LOOP_SIZE 22

template<class _Ty>
string array_to_cppstring(const _Ty vals[], unsigned cnt)
{
	if (cnt == 0) return string("{}");
	string o = "{ " + boost::lexical_cast<string>(vals[0]);
	for (unsigned i = 1; i < cnt; ++i)
		o += ", " + boost::lexical_cast<string>(vals[i]);
	o += "}";
	return o;
}
template<class _Ty>
string vector_to_cppstring(const vector<_Ty>& vals)
{	return array_to_cppstring(&vals[0], vals.size()); }

#define LCSTRING(s) boost::lexical_cast<string>(s)
string mask2bit_to_string(const string& variable, const uint32 mask, const unsigned bit) {
	if (hw(mask)==0) return "0";
	if (hw(mask)==1) {
		for (unsigned b = 0; b < 32; ++b)
			if (mask & (1<<b)) {
				if (b == bit) return "(" + variable + "&" + LCSTRING(1<<b) + ")";
				if (b < bit) return "((" + variable + "&" + LCSTRING(1<<b) + ")<<" + LCSTRING(bit-b) + ")";
				if (b > bit) return "((" + variable + "&" + LCSTRING(1<<b) + ")>>" + LCSTRING(b-bit) + ")";
			}
	}
	if (hw(mask)<=5) {
		string res;
		for (unsigned b = 0; b < 32; ++b)
			if (mask & (1<<b)) {
				if (res.size())
					res += "+";
				res += "(" + variable + ">>" + LCSTRING(b) + ")";
			}
		return "(((" + res + ")&1)<<" + LCSTRING(bit) + ")";
	}
	return "((hw("+variable+"&"+LCSTRING(mask)+")&1)<<"+LCSTRING(bit)+")";
}

uint32 rotationmask(const vector<uint32>& rels, unsigned rot)
{
	uint32 mask = 0;
	for (unsigned b = 0; b < 32; ++b)
		if (rotate_left(rels[b], rot)&(1<<b))
			mask |= 1<<b;
	return mask;
}
uint32 fixedmask(const vector<uint32>& rels, unsigned bit)
{
	uint32 mask = 0;
	for (unsigned b = 0; b < 32; ++b)
		if (rels[b] & (1<<bit))
			mask |= 1<<b;
	return mask;
}
// rels contains for each bit 0,...,31 a mask that specifies which bits of 'variable' have to be summed
string optimized_mecond_to_string(const string& variable, const vector<uint32>& _rels)
{
	vector<uint32> rels = _rels;
	rels.resize(32,0); // sanitize
	vector< pair<uint32, unsigned> > fixed_mask_bit;
	vector< pair<uint32, unsigned> > rotation_mask_rot;
	while (rels != vector<uint32>(32,0)) {
		int bestgain = -1;
		int bestoption = -1;
		for (unsigned bit = 0; bit < 32; ++bit) {
			uint32 mask = fixedmask(rels, bit);
			int gain = int(hw(mask))*3 - 5;              //^((0-((var>>bit)&1))&mask)
			if (0 == (mask & ~uint32(0-uint32(1<<bit))))
				gain += 1; // don't need to shift 'var' by 'bit' bits
			if (gain > bestgain) {
				bestgain = gain;
				bestoption = bit;
			}
		}
		for (unsigned rot = 0; rot < 32; ++rot) {
			uint32 mask = rotationmask(rels, rot);
			int gain = int(hw(mask))*3 - 3;
			if (gain > bestgain) {
				bestgain = gain;
				bestoption = rot+32;
			}
		}
		if (bestoption == -1) {
			cerr << "don't know what to do anymore ?!" << endl;
			throw;
		}
		if (bestoption < 32) {
			unsigned bit = bestoption;
			uint32 mask = fixedmask(rels, bit);
			fixed_mask_bit.push_back( pair<uint32,unsigned>(mask, bit) );
			for (unsigned b = 0; b < 32; ++b)
				if (mask & (1<<b))
					rels[b] &= ~uint32(1<<bit);
		} else {
			unsigned rot = bestoption-32;
			uint32 mask = rotationmask(rels, rot);
			rotation_mask_rot.push_back( pair<uint32,unsigned>(mask, rot) );
			for (unsigned b = 0; b < 32; ++b)
				if (mask & (1<<b))
					rels[b] &= rotate_right(~uint32(1<<b), rot);
		}
	}
	string res = "0";

	for (unsigned i = 0; i < fixed_mask_bit.size(); ++i) {
		uint32 mask = fixed_mask_bit[i].first;
		unsigned bit = fixed_mask_bit[i].second;
		if (0 == (mask & ~uint32(0-uint32(1<<bit)))) {
			//^((0-(var&(1<<bit)))&mask)
			res += "\n\t\t\t\t^((0-(" + variable + "&(1<<" + boost::lexical_cast<string>(bit) + ")))&" + boost::lexical_cast<string>(mask) + ")";
			res += " //" + boost::lexical_cast<string>(mask) + "=[!";
			bool firstone = true;
			for (unsigned b = 0; b < 32; ++b)
				if (mask & (1<<b)) {
					if (firstone) firstone = false; else res += ",";
					res += boost::lexical_cast<string>(b);
				}
			res += "!]";
		} else {
			//^((0-((var>>bit)&1))&mask)
			res += "\n\t\t\t\t^((0-((" + variable + ">>" + boost::lexical_cast<string>(bit) + ")&1))&" + boost::lexical_cast<string>(mask) + ")";
			res += " //" + boost::lexical_cast<string>(mask) + "=[!";
			bool firstone = true;
			for (unsigned b = 0; b < 32; ++b)
				if (mask & (1<<b)) {
					if (firstone) firstone = false; else res += ",";
					res += boost::lexical_cast<string>(b);
				}
			res += "!]";
		}
	}
	for (unsigned i = 0; i < rotation_mask_rot.size(); ++i) {
		uint32 mask = rotation_mask_rot[i].first;
		unsigned rot = rotation_mask_rot[i].second;
		// ^(rotate_left(var,rot)&mask)
		res += "\n\t\t\t\t^(rotate_left(" + variable + "," + boost::lexical_cast<string>(rot) + ")&" + boost::lexical_cast<string>(mask) + ")";
		res += " //" + boost::lexical_cast<string>(mask) + "=[!";
		bool firstone = true;
		for (unsigned b = 0; b < 32; ++b)
			if (mask & (1<<b)) {
				if (firstone) firstone = false; else res += ",";
				res += boost::lexical_cast<string>(b);
			}
		res += "!]";
		mask = rotate_right(mask, rot);
		res += ", " + boost::lexical_cast<string>(mask) + "=[!";
		firstone = true;
		for (unsigned b = 0; b < 32; ++b)
			if (mask & (1<<b)) {
				if (firstone) firstone = false; else res += ",";
				res += boost::lexical_cast<string>(b);
			}
		res += "!]";
	}
	res += "\n\t\t\t\t";
	return res;
}

// tunnel_analysis.cpp
void fill_tables(const sha1differentialpath& diffpath);
extern uint32 Q[85];
extern uint32 Q2[85];
extern uint32 dQ[85];
extern uint32 dT[85];
extern uint32 dR[85];
extern uint32 dF[80];

extern uint32 Qvaluemask[85];
extern uint32 Qvalue[85];
extern uint32 Qprev[85];
extern uint32 Qprev2[85];
uint32 Qvaluemask_adj[85];
vector< vector< vector<uint32> > > mainpathbitrelationsmatrix;
vector<uint32> mainmask_nextstepcorrection(16,0), mainmask_delayfreedom(16,0), mainmask_medependencies(16,0);

extern const char program_header[];
extern const char program_statistics[];
string generate_program_step(unsigned t)
{
	uint32 metmask = 0, metset1 = 0;
	vector<uint32> met_dependency_metm1(32,0);
	for (unsigned b = 0; b < 32; ++b)
		if (mainpathbitrelationsmatrix[t][b].size()) {
			/***** WARNING: DON'T FORGET TO ADD EXTRA CHECKS FOR inside m[t] bitrelations *****/
			metmask |= 1<<b; // or use mainpathbitrelationsmatrix[t][b][t] if you are lazy!!: costs freedom
			metset1 |= mainpathbitrelationsmatrix[t][b][16]&(1<<b);
			if (t>0)
				met_dependency_metm1[b] = mainpathbitrelationsmatrix[t][b][t-1];
		}

	std::ostringstream of;
	of <<
		"\nbool step_" << boost::lexical_cast<string>(t) << "()"
		"\n{"
		"\n	" << (t>=16?"//":"") << "static bool firsttime = true;"
		"\n	" << (t>=16?"//":"") << "if (firsttime) { cout << \"t" << t << " \" << endl; firsttime = false; }"
		"\n	const unsigned t = " << t << ";"
		"\n	" << (t>=16?"//":"") << "UPDATE(t);"
		"\n	" << (t>=16?"//":"") << "show_performance_data(t);"
		"\n	bool step" << boost::lexical_cast<string>(t) << "ok = false;"
		"\n	Qr30[offset+t-2] = rotate_left(Q[offset+t-2],30);"
		"\n	const uint32 mpartial2 = 0 - sha1_f1(Q[offset+t-1], Qr30[offset+t-2], Qr30[offset+t-3]) - sha1_ac[0] - Qr30[offset+t-4];"
		"\n	const uint32 metmask = " << metmask << ";"
		;
	if (t == 0) {
		// alternative beginning for t = 0
		of <<
		"\n	{"
		"\n		const uint32 metset1 = " << metset1 << ";"
		;
	} else {
		of <<
		"\n	uint32* Qtcheckptr = Qtp1valsvec[20];"
		"\n	for (uint32* Qtvalsptr = Qtp1valsvecptr[t-1]-1; Qtvalsptr >= Qtp1valsvec[t-1]; --Qtvalsptr) {"
		"\n		Q[offset+t] = *Qtvalsptr;"
		"\n		m[t-1] = *Qtvalsptr + metpartialvec[t-1];"
		"\n		const uint32 metset1 = metset1precomp[" << t-1 << "][" << t << "] ^ " << optimized_mecond_to_string("m["+boost::lexical_cast<string>(t-1)+"]", met_dependency_metm1) << ";"
		;
	}
	if (hw(Qvaluemask_adj[offset+t+1])>=hw(metmask)) {
		uint32 Qtp1mask = ~Qvaluemask_adj[offset+t+1];
		uint32 metmaskfix = metmask & Qtp1mask;
		uint32 metmasknofix = metmask ^ metmaskfix;
		Qtp1mask ^= metmaskfix;
		if (hw(Qtp1mask & (~metmask)) <= MAX_LOOP_SIZE || (metmask&~Qtp1mask)==0) {
			uint32 metmasknofixrange = 0;
			for (unsigned rr = 0; rr < 6; ++rr)
				metmasknofixrange |= metmasknofix>>rr;
			uint32 curmask = Qtp1mask & metmasknofixrange;
			of <<
				"\n		const uint32 Qtp1val = Qvalue[offset+t+1] ^ (Qprev[offset+t+1]&Q[offset+t]) ^ ((~metset1) & " << metmaskfix << ") ^ (xrng64()&" << Qtp1mask << ");"
				"\n		const uint32 mpartial = mpartial2 - rotate_left(Q[offset+t],5);"
				"\n		metpartialvec[t] = mpartial;"
				"\n		uint32* Qtp1valsptr = Qtp1valsvec[t];"
				"\n		uint32 cur = 0; const uint32 curmask = " << curmask << ";"
				"\n		do { cur -= 1; cur &= curmask;"
				"\n		//uint32* ptrbu = Qtp1valsptr;"
				"\n		uint32 Qtp1cur = 0;"
				"\n		do {"
				"\n			Qtp1cur -= 1; Qtp1cur &= " << Qtp1mask << "^curmask;"
				"\n			Q[offset+t+1] = Qtp1cur ^ cur ^ Qtp1val;"
				"\n			m[t] = Q[offset+t+1] + mpartial;"
				"\n			if (0 != ((m[t]^metset1)&" << metmasknofix << ")) break;"
				"\n			uint32 metxorfix = (m[t]^metset1) & " << metmaskfix << ";"
				"\n			*(Qtp1valsptr++) = Q[offset+t+1]^metxorfix;"
				"\n		} while (Qtp1cur != 0);"
				"\n		// loop_data_vec[40+t].add(Qtp1valsptr-ptrbu, 1<<hw(uint32(" << Qtp1mask << "^curmask)));"
				"\n		} while (cur != 0);"
				"\n		time_avg[t] += Qtp1valsptr-Qtp1valsvec[t];"
				"\n		loop_data_vec[t].add(Qtp1valsptr-Qtp1valsvec[t],1<<" << hw(Qtp1mask) << ");"
				"\n		if (Qtp1valsptr-Qtp1valsvec[t]>0) loop_data_vec[t+20].add(Qtp1valsptr-Qtp1valsvec[t],1<<" << hw(Qtp1mask) << ");"
				"\n		const uint32 checkmask_mask = " << Qtp1mask << ";"
				"\n		const uint32 checkmask_add = 0;"
				;
		} else {
			of <<
				"\n		const uint32 Qtp1val = Qvalue[offset+t+1] ^ (Qprev[offset+t+1]&Q[offset+t]) ^ ((~metset1) & " << metmaskfix << ");"
				"\n		const uint32 mpartial = mpartial2 - rotate_left(Q[offset+t],5);"
				"\n		metpartialvec[t] = mpartial;"
				"\n		uint32* Qtp1valsptr = Qtp1valsvec[t];"
				"\n		for (unsigned j = 0; j < (1<<MAX_LOOP_SIZE); ++j) {"
				"\n			Q[offset+t+1] = (xrng64()&" << Qtp1mask << ") ^ Qtp1val;"
				"\n			m[t] = Q[offset+t+1] + mpartial;"
				"\n			if (0 != ((m[t]^metset1)&" << metmasknofix << ")) " << (t>=13||t==4||t==6||t==7?"break;":"continue;") <<
				"\n			uint32 metxorfix = (m[t]^metset1) & " << metmaskfix << ";"
				"\n			*(Qtp1valsptr++) = Q[offset+t+1]^metxorfix;"
				"\n		}"
				"\n		time_avg[t] += Qtp1valsptr-Qtp1valsvec[t];"
				"\n		loop_data_vec[t].add(Qtp1valsptr-Qtp1valsvec[t],1<<MAX_LOOP_SIZE);"
				"\n		if (Qtp1valsptr-Qtp1valsvec[t]>0) loop_data_vec[20+t].add(Qtp1valsptr-Qtp1valsvec[t],1<<MAX_LOOP_SIZE);"
				"\n		const uint32 checkmask_mask = " << Qtp1mask << ";"
				"\n		const uint32 checkmask_add = 0;"
				;
		}
	} else {
		uint32 metmaskinv = ~metmask;
		uint32 Qtp1maskinv = Qvaluemask_adj[offset+t+1];
		uint32 Qtp1maskfix = Qtp1maskinv & metmaskinv;
		uint32 Qtp1masknofix = Qtp1maskinv ^ Qtp1maskfix;
		metmaskinv ^= Qtp1maskfix;

		if (hw(metmaskinv & (~Qtp1maskinv)) <= MAX_LOOP_SIZE || (Qtp1maskinv&~metmaskinv)==0) {
			uint32 Qtp1masknofixrange = 0;
			for (unsigned rr = 0; rr < 6; ++rr)
				Qtp1masknofixrange |= Qtp1masknofix>>rr;
			uint32 curmask = metmaskinv & Qtp1masknofixrange;
			of <<
				"\n		const uint32 Qtp1val = Qvalue[offset+t+1] ^ (Qprev[offset+t+1]&Q[offset+t]);"
				"\n		const uint32 metset1adj = metset1 ^ ((~Qtp1val) & " << Qtp1maskfix << ") ^ (xrng64()&" << metmaskinv << ");"
				"\n		const uint32 mpartial = mpartial2 - rotate_left(Q[offset+t],5);"
				"\n		metpartialvec[t] = mpartial;"
				"\n		uint32* Qtp1valsptr = Qtp1valsvec[t];"
				"\n		uint32 cur = 0; const uint32 curmask = " << curmask << ";"
				"\n		do { cur -= 1; cur &= curmask;"
				"\n		uint32 metcur = 0;"
				"\n		//uint32* ptrbu = Qtp1valsptr;"
				"\n		do {"
				"\n			metcur -= 1; metcur &= " << metmaskinv << "^curmask;"
				"\n			m[t] = metcur ^ cur ^ metset1adj;"
				"\n			Q[offset+t+1] = m[t] - mpartial;"
				"\n			if (0 != ((Q[offset+t+1]^Qtp1val)&" << Qtp1masknofix << ")) break;"
				"\n			uint32 Qtp1xorfix = (Q[offset+t+1]^Qtp1val) & " << Qtp1maskfix << ";"
				"\n			*(Qtp1valsptr++) = Q[offset+t+1]^Qtp1xorfix;"
				"\n		} while (metcur != 0);"
				"\n		// loop_data_vec[40+t].add(Qtp1valsptr-ptrbu, 1<<hw(uint32(" << metmaskinv << "^curmask)));"
				"\n		} while (cur != 0);"
				"\n		time_avg[t] += Qtp1valsptr-Qtp1valsvec[t];"
				"\n		loop_data_vec[t].add(Qtp1valsptr-Qtp1valsvec[t],1<<" << hw(metmaskinv) << ");"
				"\n		if (Qtp1valsptr-Qtp1valsvec[t]>0) loop_data_vec[20+t].add(Qtp1valsptr-Qtp1valsvec[t],1<<" << hw(metmaskinv) << ");"
				"\n		const uint32 checkmask_mask = " << metmaskinv << ";"
				"\n		const uint32 checkmask_add = mpartial;"
				;
		} else {
			of <<
				"\n		const uint32 Qtp1val = Qvalue[offset+t+1] ^ (Qprev[offset+t+1]&Q[offset+t]);"
				"\n		const uint32 metset1adj = metset1 ^ ((~Qtp1val) & " << Qtp1maskfix << ");"
				"\n		const uint32 mpartial = mpartial2 - rotate_left(Q[offset+t],5);"
				"\n		metpartialvec[t] = mpartial;"
				"\n		uint32* Qtp1valsptr = Qtp1valsvec[t];"
				"\n		for (unsigned j = 0; j < (1<<MAX_LOOP_SIZE); ++j) {"
				"\n			m[t] = (xrng64() & " << metmaskinv << ") ^ metset1adj;"
				"\n			Q[offset+t+1] = m[t] - mpartial;"
				"\n			if (0 != ((Q[offset+t+1]^Qtp1val)&" << Qtp1masknofix << ")) " << (t>=13||t==4||t==6||t==7?"break;":"continue;") <<
				"\n			uint32 Qtp1xorfix = (Q[offset+t+1]^Qtp1val) & " << Qtp1maskfix << ";"
				"\n			*(Qtp1valsptr++) = Q[offset+t+1]^Qtp1xorfix;"
				"\n		}"
				"\n		time_avg[t] += Qtp1valsptr-Qtp1valsvec[t];"
				"\n		loop_data_vec[t].add(Qtp1valsptr-Qtp1valsvec[t],1<<MAX_LOOP_SIZE);"
				"\n		if (Qtp1valsptr-Qtp1valsvec[t]>0) loop_data_vec[20+t].add(Qtp1valsptr-Qtp1valsvec[t],1<<MAX_LOOP_SIZE);"
				"\n		const uint32 checkmask_mask = " << metmaskinv << ";"
				"\n		const uint32 checkmask_add = mpartial;"
				;
		}
	}
	of <<
		"\n		if (Qtp1valsptr == Qtp1valsvec[t]) " << (t==0?"return step0ok;":"continue;") <<
		"\n		step" << boost::lexical_cast<string>(t) << "ok = true;"
		"\n		//process_checkmask(Qtp1valsvec[t], Qtp1valsptr, checkmask_mask, checkmask_add);"
		"\n		//*(Qtcheckptr++) = *Qtvalsptr;"
		"\n		//continue;"
		;
	if (t>0) {
		for (unsigned i = t+1; i < 16; ++i) {
			vector<uint32> dependency_metm1(32,0);
			for (unsigned b = 0; b < 32; ++b)
				if (mainpathbitrelationsmatrix[i][b].size())
					dependency_metm1[b] = mainpathbitrelationsmatrix[i][b][t-1];
			of << "\n		metset1precomp[" << t << "][" << i << "] = metset1precomp[" << t-1 << "][" << i << "] ^ " << optimized_mecond_to_string("m["+boost::lexical_cast<string>(t-1)+"]", dependency_metm1) << ";";
		}
	}
	of <<
		"\n		Qtp1valsvecptr[t] = Qtp1valsptr;"
		"\n		step_" << t+1 << "();"
		"\n	}"
		;
	if (t>0) {
		of <<
			"\n	if (Qtcheckptr-Qtp1valsvec[20] > 0) {"
			"\n		loop_data_vec[16+t].add(Qtcheckptr-Qtp1valsvec[20], Qtp1valsvecptr[t-1]-Qtp1valsvec[t-1]);"
			"\n		process_checkmask(Qtp1valsvec[20], Qtcheckptr);"
			"\n	}"
			;
	}
	of <<
		"\n	return step" << boost::lexical_cast<string>(t) << "ok;"
		"\n}"
		;
	return of.str();
}

void generate_program()
{
	uint32 mxordiff[80];
	for (unsigned t = 0; t < 80; ++t) {
		mxordiff[t] = maindiffpath.getme(t).mask;
		if (t >= 16 && mxordiff[t] != rotate_left(mxordiff[t-3] ^ mxordiff[t-8] ^ mxordiff[t-14] ^ mxordiff[t-16], 1)) {
			cerr << "mxordiff inconsistent @ " << t << endl;
			throw;
		}
	}
	fill_tables(maindiffpath);
	vector< vector< vector<uint32> > > pathbitrelationsmatrix;
	mespace_to_pathbitrelationsmatrix(mainmespace, pathbitrelationsmatrix);
	mainpathbitrelationsmatrix = pathbitrelationsmatrix;
	fill_tables(maindiffpath);
	sha1differentialpath maindiffpath_adj = maindiffpath;

	vector<uint32> mask_nextstepcorrection(16,0), mask_delayfreedom(16,0), mask_medependencies(16,0);
	for (unsigned t = 0; t < 16; ++t)
		for (unsigned b = 0; b < 32; ++b)
			if (t+1 < 16 && pathbitrelationsmatrix[t+1][b].size())
				mask_medependencies[t] |= pathbitrelationsmatrix[t+1][b][t];
	mainmask_nextstepcorrection = mask_nextstepcorrection;
	mainmask_delayfreedom = mask_delayfreedom;
	mainmask_medependencies = mask_medependencies;

	{
		// fill tables with adjusted bitconditions
		// however! keep the adjusted Qvaluemask in a seperate variable: Qvaluemask_adj
		uint32 Qtmp[85];
		memcpy(Qtmp, Qvaluemask, sizeof(uint32)*85);
		fill_tables(maindiffpath_adj);
		memcpy(Qvaluemask_adj, Qvaluemask, sizeof(uint32)*85);
		memcpy(Qvaluemask, Qtmp, sizeof(uint32)*85);
	}

	std::string pathbitrelationsmatrixstring;
	{
		std::stringstream tmp;
		{
			boost::archive::text_oarchive oa(tmp);
			oa << boost::serialization::make_nvp("pathbitrelationsmatrix", pathbitrelationsmatrix);
		} // oa destructor
		pathbitrelationsmatrixstring = tmp.str();
		string::size_type pos = 0;
		while (pos < pathbitrelationsmatrixstring.size()) {
			if (pathbitrelationsmatrixstring[pos] == '\n' || pathbitrelationsmatrixstring[pos] == '\r')
				pathbitrelationsmatrixstring.erase(pos,1);
			else
				++pos;
		}
	}

	ofstream of("collfind.cpp");
	of
		<< program_header <<
		"\n#include <hashclash/saveload.hpp>"
		"\n"
		"\n#define MAX_LOOP_SIZE " << MAX_LOOP_SIZE <<
		"\n"
		"\nuint64 tendcount = 0;"
		"\nuint64 testcounts[1<<16];"
		"\nuint32 m_diff[80] = " << array_to_cppstring(mxordiff, 80) << ";"
		"\nconst uint32 dQ[85] = " << array_to_cppstring(dQ, 85) << ";"
		"\nconst uint32 Qvaluemask[85] = " << array_to_cppstring(Qvaluemask, 85) << ";"
		"\nconst uint32 Qvaluemask_adj[85] = " << array_to_cppstring(Qvaluemask_adj, 85) << ";"
		"\nconst uint32 Qvalue[85] = " << array_to_cppstring(Qvalue, 85) << ";"
		"\nconst uint32 Qprev[85] = " << array_to_cppstring(Qprev, 85) << ";"
		"\nconst uint32 Qprev2[85] = " << array_to_cppstring(Qprev2, 85) << ";"
		"\nconst uint32 dF[80] = " << array_to_cppstring(dF, 80) << ";"
		"\nvector< vector< vector<uint32> > > pathbitrelationsmatrix;"
		"\nchar pathbitrelationsmatrixstring[] = \"" << pathbitrelationsmatrixstring << "\";"
		"\nuint32 Qtp1valsvec[30][1<<22];"
		"\nuint32* Qtp1valsvecptr[30];"
		"\nuint32 metpartialvec[30];"
		"\n"
		<< program_statistics <<
		"\nvoid step0();"
		"\nint main(int argc, char** argv) {"
		"\n	{ stringstream tmp; tmp.str(string(pathbitrelationsmatrixstring)); boost::archive::text_iarchive ia(tmp); ia >> boost::serialization::make_nvp(\"pathbitrelationsmatrix\", pathbitrelationsmatrix); }"
		"\n	for (unsigned t = 0; t < 16; ++t)"
		"\n		for (unsigned b = 0; b < 32; ++b)"
		"\n			if (pathbitrelationsmatrix[t][b].size()!=0 && pathbitrelationsmatrix[t][b][t] != 1<<b) {"
		"\n				cout << \"Inside message relation @ t=\" << t << \" b=\" << b << \": \";"
		"\n				for (unsigned b2 = 0; b2 < 32; ++b2)"
		"\n					if (pathbitrelationsmatrix[t][b][t]&(1<<b2))"
		"\n						cout << \"(\" << t << \",\" << b2 << \")\";"
		"\n				cout << endl;"
		"\n			}"
		"\n	cout << \"Starting collision search...\" << endl;"
		"\n	for (unsigned i = 0; i < (1<<16); ++i) testcounts[i] = 0;"
		"\n	while (true) {"
		"\n		try {"
		"\n			step0();"
		"\n		} catch (std::exception&e) { cerr << e.what() << endl; } catch (...) {}"
		"\n	}"
		"\n}"
		"\nbool test_first_block(const uint32 block[16]) {"
		"\n	static uint32 ihv[5];"
		"\n	static uint32 me[80];"
		"\n	memcpy(ihv, sha1_iv, 5*4);"
		"\n	memcpy(me, block, 4*16);"
		"\n	for (unsigned i = 16; i < 80; ++i)"
		"\n		me[i]=rotate_left(me[i-3] ^ me[i-8] ^ me[i-14] ^ me[i-16], 1);"
		"\n	sha1compress_me(ihv, me);"
		"\n	Q2[0] = Q[0] = rotate_right(ihv[4], 30);"
		"\n	Q2[1] = Q[1] = rotate_right(ihv[3], 30);"
		"\n	Q2[2] = Q[2] = rotate_right(ihv[2], 30);"
		"\n	Q2[3] = Q[3] = ihv[1];"
		"\n	Q2[4] = Q[4] = ihv[0];"
		"\n	if ((Q[0]&Qvaluemask_adj[0])!=Qvalue[0]) return false;"
		"\n	if ((Q[1]&Qvaluemask_adj[1])!=(Qvalue[1]^(Q[0]&Qprev[1]))) return false;"
		"\n	if ((Q[2]&Qvaluemask_adj[2])!=(Qvalue[2]^(Q[1]&Qprev[2])^(Q[0]&Qprev2[2]))) return false;"
		"\n	if ((Q[3]&Qvaluemask_adj[3])!=(Qvalue[3]^(Q[2]&Qprev[3])^(Q[1]&Qprev2[3]))) return false;"
		"\n	if ((Q[4]&Qvaluemask_adj[4])!=(Qvalue[4]^(Q[3]&Qprev[4])^(Q[2]&Qprev2[4]))) return false;"
		"\n	return true;"
		"\n}"
		"\n"
		"\nbool step_0();"
		"\nvoid step0() {"
		"\n	{UPDATE(79);"
		"\n	static uint64 fbcnt = 0;"
		"\n	while (true) {"
		"\n		for (unsigned i = 0; i < 16; ++i)"
		"\n			firstmsg[i] = xrng128();"
		"\n		if (hw(++fbcnt)==1) cout << \"(fb:\" << fbcnt << \")\" << flush;"
		"\n		if (test_first_block(firstmsg))"
		"\n			break;"
		"\n	}"
		"\n	for (unsigned i = 0; i < 5; ++i) {"
		"\n		Qr30[i] = rotate_left(Q[i], 30);"
		"\n		Q2r30[i] = rotate_left(Q2[i], 30);"
		"\n	}"
		"\n	}//UPDATE(79);"
		"\n	//cout << \".\" << flush;"
		"\n"
		;
	for (unsigned j = 0; j < 16; ++j) {
		uint32 metset1j = 0;
	        for (unsigned b = 0; b < 32; ++b)
        	        if (pathbitrelationsmatrix[j][b].size()) {
                	        metset1j |= pathbitrelationsmatrix[j][b][16]&(1<<b);
              		}
		of << "\n	metset1precomp[0]["<<j<<"]=" << metset1j << ";";
	}
	of <<
		"\n 	step_0();"
		"\n}" << flush;
	;
	of <<
		"\nvoid step_16()"
		"\n{"
		"\n	UPDATE(16);"
		"\n	for (int t = 16-5; t < 16; ++t) {"
		"\n		Q2[offset+t] = Q[offset+t] + dQ[offset+t];"
		"\n		//m2[t] = m[t] ^ m_diff[t];"
		"\n	}"
		"\n	for (uint32* Qtvalsptr = Qtp1valsvecptr[15]-1; Qtvalsptr >= Qtp1valsvec[15]; --Qtvalsptr) "
		"\n	{"
		"\n		avg_data_vec[40].add_cnt(1);"
		"\n		Q[offset+16] = *Qtvalsptr;"
		"\n		Q2[offset+16] = Q[offset+16] + dQ[offset+16];"
		"\n		m[15] = *Qtvalsptr + metpartialvec[15];"
		"\n		m2[15] = m[15] ^ m_diff[15];"
		"\n		int t = 16;"
		"\n		for (; t < 20; ++t) {"
		"\n			m[t]=rotate_left(m[t-3] ^ m[t-8] ^ m[t-14] ^ m[t-16], 1);"
		"\n			m2[t] = m[t] ^ m_diff[t];"
		"\n			Q[offset+t+1] = m[t] + sha1_f1(Q[offset+t-1], rotate_left(Q[offset+t-2],30), rotate_left(Q[offset+t-3],30))"
		"\n				+ sha1_ac[0] + rotate_left(Q[offset+t],5) + rotate_left(Q[offset+t-4],30);"
		"\n			Q2[offset+t+1] = m2[t] + sha1_f1(Q2[offset+t-1], rotate_left(Q2[offset+t-2],30), rotate_left(Q2[offset+t-3],30))"
		"\n				+ sha1_ac[0] + rotate_left(Q2[offset+t],5) + rotate_left(Q2[offset+t-4],30);"
		"\n			if (naf(Q2[offset+t+1]-Q[offset+t+1]).mask != naf(dQ[offset+t+1]).mask) break;"
		"\n		}"
		"\n		if (t < 20) continue;"
		"\n		for (; t < 33; ++t) {"
		"\n			m[t]=rotate_left(m[t-3] ^ m[t-8] ^ m[t-14] ^ m[t-16], 1);"
		"\n			m2[t] = m[t] ^ m_diff[t];"
		"\n			Q[offset+t+1] = m[t] + sha1_f2(Q[offset+t-1], rotate_left(Q[offset+t-2],30), rotate_left(Q[offset+t-3],30))"
		"\n				+ sha1_ac[1] + rotate_left(Q[offset+t],5) + rotate_left(Q[offset+t-4],30);"
		"\n			Q2[offset+t+1] = m2[t] + sha1_f2(Q2[offset+t-1], rotate_left(Q2[offset+t-2],30), rotate_left(Q2[offset+t-3],30))"
		"\n				+ sha1_ac[1] + rotate_left(Q2[offset+t],5) + rotate_left(Q2[offset+t-4],30);"
		"\n		}"
		"\n		for (int t2 = 1; t2 <= t+1; ++t2) {"
		"\n			avg_data_vec[t2].add_cnt(1);"
		"\n			if (Q2[offset+t2]-Q[offset+t2]==dQ[offset+t2])"
		"\n				avg_data_vec[t2].add_sum(1);"
		"\n		}"
		"\n		if (t==33 && Q2[offset+33]==Q[offset+33] && Q2[offset+32]==Q[offset+32] && Q2[offset+31]==Q[offset+31] && Q2[offset+30]==Q[offset+30] && Q2[offset+29]==Q[offset+29]) {"
		"\n			cout << \"!\" << endl;"
		"\n			avg_data_vec[40].add_sum(1);"
		"\n			++time_avg[40];"
		"\n			for (int j = 15; j <= 33; ++j)"
		"\n				cout << \"dQ\" << j << \"=\" << sdr(Q[offset+j],Q2[offset+j]) << \"\\tdM\" << j << \"=\" << sdr(m[j],m2[j]) << endl;"
		"\n//			throw std::runtime_error(\".\");"
		"\n		}"
		"\n	}"
		"\n	static uint64 throwcnt = 0;"
		"\n//	if ((++throwcnt & 0xFFFFFF)==0) throw std::runtime_error(\".\");"

		"\n}" << endl;
	for (int t = 15; t >= 0; --t)
		of << generate_program_step(t) << flush;
	of.close();
	system("./sha1_collfind.sh collfind.cpp");
	exit(0);
}

const char program_header[] =
	"\n#include <cmath>"
	"\n#include <iomanip>"
	"\n#include <algorithm>"
	"\n#include <map>"
	"\n#include <fstream>"
	"\n#include <stdexcept>"
	"\n#define SHA1DETAIL_INLINE_IMPL"
	"\n#include <hashclash/sha1detail.hpp>"
	"\n#include <hashclash/sha1differentialpath.hpp>"
	"\n#include <hashclash/booleanfunction.hpp>"
	"\n#include <hashclash/rng.hpp>"
	"\n#include <hashclash/timer.hpp>"
	"\n#include <hashclash/bestof.hpp>"
	"\n#include <hashclash/progress_display.hpp>"
	"\n#include <hashclash/sha1messagespace.hpp>"
	"\n#include <boost/lexical_cast.hpp>"
	"\nusing namespace std;"
	"\nusing namespace hashclash;"
	"\n#define CPUPERFORMANCE"
	"\n#ifdef CPUPERFORMANCE"
	"\n#include <hashclash/cpuperformance.hpp>"
	"\nuint64 cpu_step_t[80];"
	"\n#define UPDATE(s) update_performance_counter __update(cpu_step_t[s]);"
	"\n#ifdef __GNUC__"
	"\n#include <sched.h>"
	"\n#endif"
	"\n#else"
	"\n#define UPDATE(s)"
	"\n#endif"
	"\nconst int offset = 4;"
	"\nuint32 firstmsg[16];"
	"\nuint32 m[80];"
	"\nuint32 m2[80];"
	"\nuint32 Q[85];"
	"\nuint32 Q2[85];"
	"\nuint32 Qr30[85];"
	"\nuint32 Q2r30[85];"
	"\nuint32 metset1precomp[16][16];"
	""
	"\ntemplate<int tstart, int tend>"
	"\ninline void compute_mt() {"
	"\n        for (int t = tstart; t < tend; ++t)"
	"\n                m[t] = rotate_left(m[t-3] ^ m[t-8] ^ m[t-14] ^ m[t-16], 1);"
	"\n}"
	"\ntemplate<int r> inline uint32 sha1_f(uint32 b, uint32 c, uint32 d);"
	"\ntemplate<> inline uint32 sha1_f<0>(uint32 b, uint32 c, uint32 d) { return sha1_f1(b,c,d) + 0x5A827999; }"
	"\ntemplate<> inline uint32 sha1_f<1>(uint32 b, uint32 c, uint32 d) { return sha1_f2(b,c,d) + 0x6ED9EBA1; }"
	"\ntemplate<> inline uint32 sha1_f<2>(uint32 b, uint32 c, uint32 d) { return sha1_f3(b,c,d) + 0x8F1BBCDC; }"
	"\ntemplate<> inline uint32 sha1_f<3>(uint32 b, uint32 c, uint32 d) { return sha1_f4(b,c,d) + 0xCA62C1D6; }"
	"\n"
	"\ntemplate<int tstart, int tend>"
	"\ninline void compute_qt() {"
	"\n        if (tstart >= tend) return;"
	"\n        uint32 a = Q[offset+tstart], b = Q[offset+tstart-1], c = Qr30[offset+tstart-2], d = Qr30[offset+tstart-3], e = Qr30[offset+tstart-4];"
	"\n        e += rotate_left(a, 5) + sha1_f<(tstart+0)/20>(b,c,d) + m[tstart+0]; Q[offset+tstart+1] = e; Qr30[offset+tstart-1] = b = rotate_left(b, 30);"
	"\n        if (tstart+1 == tend) return;"
	"\n        d += rotate_left(e, 5) + sha1_f<(tstart+1)/20>(a,b,c) + m[tstart+1]; Q[offset+tstart+2] = d; Qr30[offset+tstart+0] = a = rotate_left(a, 30);"
	"\n        if (tstart+2 == tend) return;"
	"\n        c += rotate_left(d, 5) + sha1_f<(tstart+2)/20>(e,a,b) + m[tstart+2]; Q[offset+tstart+3] = c; Qr30[offset+tstart+1] = e = rotate_left(e, 30);"
	"\n        if (tstart+3 == tend) return;"
	"\n        b += rotate_left(c, 5) + sha1_f<(tstart+3)/20>(d,e,a) + m[tstart+3]; Q[offset+tstart+4] = b; Qr30[offset+tstart+2] = d = rotate_left(d, 30);"
	"\n        if (tstart+4 == tend) return;"
	"\n        a += rotate_left(b, 5) + sha1_f<(tstart+4)/20>(c,d,e) + m[tstart+4]; Q[offset+tstart+5] = a; Qr30[offset+tstart+3] = c = rotate_left(c, 30);"
	"\n        if (tstart+5 == tend) return;"
	"\n        e += rotate_left(a, 5) + sha1_f<(tstart+5)/20>(b,c,d) + m[tstart+5]; Q[offset+tstart+6] = e; Qr30[offset+tstart+4] = b = rotate_left(b, 30);"
	"\n        if (tstart+6 == tend) return;"
	"\n        d += rotate_left(e, 5) + sha1_f<(tstart+6)/20>(a,b,c) + m[tstart+6]; Q[offset+tstart+7] = d; Qr30[offset+tstart+5] = a = rotate_left(a, 30);"
	"\n        if (tstart+7 == tend) return;"
	"\n        c += rotate_left(d, 5) + sha1_f<(tstart+7)/20>(e,a,b) + m[tstart+7]; Q[offset+tstart+8] = c; Qr30[offset+tstart+6] = e = rotate_left(e, 30);"
	"\n        if (tstart+8 == tend) return;"
	"\n        b += rotate_left(c, 5) + sha1_f<(tstart+8)/20>(d,e,a) + m[tstart+8]; Q[offset+tstart+9] = b; Qr30[offset+tstart+7] = d = rotate_left(d, 30);"
	"\n        if (tstart+9 == tend) return;"
	"\n        a += rotate_left(b, 5) + sha1_f<(tstart+9)/20>(c,d,e) + m[tstart+9]; Q[offset+tstart+10] = a; Qr30[offset+tstart+8] = c = rotate_left(c, 30);"
	"\n        if (tstart+10 == tend) return;"
	"\n        e += rotate_left(a, 5) + sha1_f<(tstart+10)/20>(b,c,d) + m[tstart+10]; Q[offset+tstart+11] = e; Qr30[offset+tstart+9] = b = rotate_left(b, 30);"
	"\n        if (tstart+11 == tend) return;"
	"\n        d += rotate_left(e, 5) + sha1_f<(tstart+11)/20>(a,b,c) + m[tstart+11]; Q[offset+tstart+12] = d; Qr30[offset+tstart+10] = a = rotate_left(a, 30);"
	"\n        if (tstart+12 == tend) return;"
	"\n        c += rotate_left(d, 5) + sha1_f<(tstart+12)/20>(e,a,b) + m[tstart+12]; Q[offset+tstart+13] = c; Qr30[offset+tstart+11] = e = rotate_left(e, 30);"
	"\n        if (tstart+13 == tend) return;"
	"\n        b += rotate_left(c, 5) + sha1_f<(tstart+13)/20>(d,e,a) + m[tstart+13]; Q[offset+tstart+14] = b; Qr30[offset+tstart+12] = d = rotate_left(d, 30);"
	"\n        if (tstart+14 == tend) return;"
	"\n        a += rotate_left(b, 5) + sha1_f<(tstart+14)/20>(c,d,e) + m[tstart+14]; Q[offset+tstart+15] = a; Qr30[offset+tstart+13] = c = rotate_left(c, 30);"
	"\n        if (tstart+15 < tend) throw; // prevent stupid mistake"
	"\n}"

	;
const char program_statistics[] =
	"\nstruct avg_data {"
	"\n	uint64 avgcnt, avgsum;"
	"\n	avg_data(): avgcnt(0), avgsum(0) {}"
	"\n	void add_ok() {"
	"\n		++avgcnt;"
	"\n		++avgsum;"
	"\n	}"
	"\n	void add_bad() {"
	"\n		++avgcnt;"
	"\n	}"
	"\n	void add_cnt(uint64 toadd = 1) {"
	"\n		avgcnt += toadd;"
	"\n	}"
	"\n	void add_sum(uint64 toadd = 1) {"
	"\n		avgsum += toadd;"
	"\n	}"
	"\n	void show(const std::string& name) {"
	"\n		double avg = double(avgsum)/double(avgcnt);"
	"\n		double logavg = log(avg)/log(2.0);"
	"\n		if (logavg >= 20 || logavg < -2.0) "
	"\n	                cout << name << \":\t2^(\" << logavg << \")\";"
	"\n		else"
	"\n			cout << name << \":\t\" << avg;"
	"\n		cout << \"\t(#=\" << avgcnt << \")\" << endl;"
	"\n	}"
	"\n};"
	"\nvector<avg_data> avg_data_vec(80);"
	"\n"
	"\nstruct loop_data {"
	"\n        uint64 proc_cnt[101];"
	"\n        uint64 totcnt;"
	"\n        bool fulldetail;"
	"\n        loop_data() { "
	"\n        	fulldetail = false;"
	"\n                for (unsigned i = 0; i < 101; ++i)"
	"\n                        proc_cnt[i] = 0;"
	"\n                totcnt = 0;"
	"\n        }"
	"\n        void add(uint32 ok, uint32 cnt) {"
	"\n                unsigned proc = round(float(ok*100)/float(cnt));"
	"\n                if (proc > 100) throw;"
	"\n                ++proc_cnt[proc];"
	"\n                ++totcnt;"
	"\n        }"
	"\n        void add(unsigned proc) {"
	"\n                if (proc > 100) throw;"
	"\n                ++proc_cnt[proc];"
	"\n                ++totcnt;"
	"\n        }"
	"\n        void show(const std::string& name) {"
	"\n                cout << name << \": \";"
	"\n                uint64 mincnt = totcnt >> 3;"
	"\n                uint64 remcnt = totcnt;"
	"\n                if (mincnt == 0) mincnt = 1;"
	"\n                for (unsigned i = 0; i < 101; ++i)"
	"\n                        if (proc_cnt[i] >= mincnt || i == 0 || i == 100 || (proc_cnt[i]>0 && fulldetail)) {"
	"\n                                cout << \"(\" << i << \"% occurs with p=\" << double(proc_cnt[i])/double(totcnt) << \") \";"
	"\n                                remcnt -= proc_cnt[i];"
	"\n                        }"
	"\n                cout << \"(other p=\" << double(remcnt)/double(totcnt) << \")\" << endl;"
	"\n        }"
	"\n};"
	"\nvector<loop_data> loop_data_vec(80);"
	"\n"
	"\nstruct checkmaskcnt_type {"
	"\n        void process(uint32* Qtbegin, uint32* Qtend, uint32 mask = 0xFFFFFFFF) {"
	"\n                if (Qtend-Qtbegin < 2) return;"
	"\n                bits.resize(32);"
	"\n                for (unsigned b = 0; b < 32; ++b) {"
	"\n			   if ((mask & (1<<b))==0) continue;"
	"\n                        uint32 cnt = 0;"
	"\n                        for (uint32* p = Qtbegin; p != Qtend; ++p)"
	"\n                                if (*p & (1<<b))"
	"\n                                        ++cnt;"
	"\n//                      if (cnt > ((Qtend-Qtbegin+1)>>1))"
	"\n//                              cnt = (Qtend-Qtbegin)-cnt;"
	"\n                        bits[b].add(cnt, Qtend-Qtbegin);"
	"\n                }"
	"\n        }"
	"\n        void show(const std::string& name) {"
	"\n                if (bits.empty()) return;"
	"\n                cout << name << \":\" << endl;"
	"\n                for (unsigned b = 0; b < 32; ++b) {"
	"\n			   if (bits[b].totcnt == 0) continue;"
	"\n                        bits[b].show(\"checkmaskbit \" + boost::lexical_cast<string>(b));"
	"\n                }"
	"\n        }"
	"\n        vector<loop_data> bits;"
	"\n};"
	"\ncheckmaskcnt_type checkmaskcnt;"
	"\nvoid process_checkmask(uint32* Qtbegin, uint32* Qtend, uint32 mask = 0xFFFFFFFF, uint32 add = 0)"
	"\n{"
	"\n	   if (add) { for (uint32* ptr = Qtbegin; ptr != Qtend; ++ptr) *ptr += add; }"
	"\n        checkmaskcnt.process(Qtbegin, Qtend,mask);"
	"\n	   if (add) { for (uint32* ptr = Qtbegin; ptr != Qtend; ++ptr) *ptr -= add; }"
	"\n}"
	"\n"
	"\nvector<uint64> time_avg(1024);"
	"\nvector<uint64> step_cnt(20,0);"
	"\ntimer spd_sw(true), runtime_sw(true), restart_sw(true);"
	"\n#ifdef CPUPERFORMANCE"
	"\nuint64 cpu_timestamp_begin = cpu_timestamp();"
	"\n#endif"
	"\nvoid show_performance_data(const int correct_cpu_step_t = -1)"
	"\n{"
	"\n	if (spd_sw.time() < 15) return;"
	"\n	if (time_avg[40] == 0 && restart_sw.time() > 60) {"
	"\n		restart_sw.start();"
	"\n		throw std::runtime_error(\"restart\");"
	"\n	}"
	"\n	spd_sw.start();"
	"\n	cout << endl;"
	"\n#ifdef CPUPERFORMANCE"
	"\n	uint64 cputime = cpu_timestamp();"
	"\n	uint64 cpuspend = cputime-cpu_timestamp_begin;"
	"\n	if (correct_cpu_step_t != -1) {"
	"\n		for (int t = 0; t <= correct_cpu_step_t; ++t)"
	"\n			cpu_step_t[t] += cputime;"
	"\n	}"
	"\n	for (unsigned i = 0; i < 16; ++i)"
	"\n		if (cpu_step_t[i]-cpu_step_t[i+1])"
	"\n			cout << \"t=\" << i << \"\t\" << round(double(cpu_step_t[i]-cpu_step_t[i+1])*1000.0/double(cpuspend)) << \"\t\" << cpu_step_t[i]-cpu_step_t[i+1] << endl;"
	"\n     for (unsigned i = 16; i < 80; ++i) {"
	"\n             if (cpu_step_t[i] > (cpuspend<<1))"
	"\n                     cout << \"i=\" << i << \"\t\" << round(double(cpu_step_t[i]+cputime)*1000.0/double(cpuspend)) << \"\t\" << (cpu_step_t[i]+cputime) << endl;"
	"\n             else if (cpu_step_t[i])"
	"\n                     cout << \"i=\" << i << \"\t\" << round(double(cpu_step_t[i])*1000.0/double(cpuspend)) << \"\t\" << (cpu_step_t[i]) << endl;"
	"\n     }"
	"\n	if (correct_cpu_step_t != -1) {"
	"\n		for (int t = 0; t <= correct_cpu_step_t; ++t)"
	"\n			cpu_step_t[t] -= cputime;"
	"\n	}"
	"\n#endif"
	"\n	for (unsigned i = 0; i < 80; ++i)"
	"\n		if (loop_data_vec[i].totcnt)"
	"\n			loop_data_vec[i].show(\"loop \" + boost::lexical_cast<string>(i) + \" stats\");"
	"\n	for (unsigned i = 0; i < 80; ++i)"
	"\n		if (avg_data_vec[i].avgcnt)"
	"\n			avg_data_vec[i].show(\"avg \" + boost::lexical_cast<string>(i) + \" stats\");"
	"\n	for (unsigned i = 0; i < time_avg.size(); ++i)"
	"\n		if (time_avg[i])"
	"\n			cout << \"timeavg \" << i << \": \t2^\" << log(double(time_avg[i])/double(runtime_sw.time()))/log(2.0) << \"#/s\" << endl;"
	"\n	for (unsigned i = 0; i < 20; ++i)"
	"\n		if (step_cnt[i])"
	"\n			cout << \"(\" << i << \":\" << step_cnt[i] << \")\t\";"
	"\n	cout << endl;"
	"\n	checkmaskcnt.show(\"checkmask\");"
	"\n}"
	"\n"
	"\nvoid verify_block(unsigned tend = 16)"
	"\n{"
	"\n	static uint32 cnt = 0;"
	"\n	if (hw(++cnt)!=1) return;"
	"\n	UPDATE(76);"
	"\n	for (unsigned t = 16; t < tend; ++t)"
	"\n		m[t]=rotate_left(m[t-3] ^ m[t-8] ^ m[t-14] ^ m[t-16], 1);"
	"\n	for (unsigned t = 0; t < tend && t < 20; ++t)"
	"\n		if (rotate_left(Q[offset+t-2],30)!=Qr30[offset+t-2]) {"
	"\n			cout << \"Precomp Qr30 error @ t=\" << t << endl;"
	"\n			exit(0);"
	"\n		}"
	"\n	for (unsigned t = 0; t < tend && t < 20; ++t) {"
	"\n		bool terror = false;"
	"\n		uint32 Qtp1 = m[t] + sha1_f1(Q[offset+t-1], rotate_left(Q[offset+t-2],30), rotate_left(Q[offset+t-3],30)) + sha1_ac[0] + rotate_left(Q[offset+t],5) + rotate_left(Q[offset+t-4],30);"
	"\n		if (Qtp1 != Q[offset+t+1]) {"
	"\n			cout << \"Hash error @ t=\" << t << \": \" << hex << (Qtp1^Q[offset+t+1]) << dec << endl;"
	"\n			terror = true;"
	"\n		}   "
	"\n		if ((Q[offset+t+1]^Qvalue[offset+t+1]^(Qprev[offset+t+1]&Q[offset+t])) & Qvaluemask[offset+t+1]) {"
	"\n			cout << \"Verify error @ t=\" << t << \": \";"
	"\n			cout << hex << ((Q[offset+t+1]^Qvalue[offset+t+1]^(Qprev[offset+t+1]&Q[offset+t])) & Qvaluemask[offset+t+1]) << dec << endl;"
	"\n			terror = true;"
	"\n		}"
	"\n		if (t < 16) {"
	"\n			for (unsigned b = 0; b < 32; ++b)"
	"\n				if (pathbitrelationsmatrix[t][b].size()) {"
	"\n					uint32 r = pathbitrelationsmatrix[t][b][16]&1;"
	"\n					for (unsigned j = 0; j <= t; ++j)"
	"\n						r ^= m[j]&pathbitrelationsmatrix[t][b][j];"
	"\n					if (hw(r)&1) {"
	"\n 						cout << \"Message relation error @ t=\" << t << \" b=\" << b << \": \";"
	"\n						for (unsigned j = 0; j <= t; ++j)"
	"\n							for (unsigned b2 = 0; b2 < 32; ++b2)"
	"\n								if (pathbitrelationsmatrix[t][b][j]&(1<<b2))"
	"\n									cout << \"(\" << j << \",\" << b2 << \")\";"
	"\n						cout << endl;"
	"\n						terror = true;"
	"\n					}"
	"\n				}"
	"\n		}"
	"\n		if (terror) exit(1);"
	"\n	}"
	"\n}"
	;


/*
    Lightmetrica - A modern, research-oriented renderer

    Copyright (c) 2015 Hisanari Otsu

    Permission is hereby granted, free of charge, to any person obtaining a copy
    of this software and associated documentation files (the "Software"), to deal
    in the Software without restriction, including without limitation the rights
    to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
    copies of the Software, and to permit persons to whom the Software is
    furnished to do so, subject to the following conditions:

    The above copyright notice and this permission notice shall be included in
    all copies or substantial portions of the Software.

    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
    AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
    OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
    THE SOFTWARE.
*/

#include <lightmetrica/lightmetrica.h>
#include "radiosityutils.h"
#include <sstream>
#include <boost/format.hpp>
#if LM_COMPILER_MSVC
#pragma warning(disable:4714)
#pragma warning(disable:4701)
#pragma warning(disable:4456)
#include <Eigen/Sparse>
#else
#include <eigen3/Eigen/Sparse>
#endif

#define LM_RADIOSITY_DEBUG 0

namespace Eigen
{
    template <>
    struct NumTraits<lightmetrica_v2::Vec3>
    {
        using T          = lightmetrica_v2::Vec3;
        using VT         = lightmetrica_v2::Float;
        using Real       = T;
        using NonInteger = T;
        using Nested     = T;
        using Literal    = T;
        enum
        {
            IsComplex = 0,
            IsInteger = 0,
            IsSigned = 1,
            RequireInitialization = 1,
            ReadCost = 3,
            AddCost = 3,
            MulCost = 3
        };
        static inline T epsilon()         { return T(NumTraits<VT>::epsilon()); }
        static inline T dummy_precision() { return T(NumTraits<VT>::dummy_precision()); }
        static inline T highest()         { return T(std::numeric_limits<VT>::max()); }
        static inline T lowest()          { return T(std::numeric_limits<VT>::min()); }
    };

    namespace internal
    {
        template<>
        struct significant_decimals_impl<lightmetrica_v2::Vec3>
        {
            static inline int run()
            {
                return significant_decimals_impl<lightmetrica_v2::Float>::run();
            }
        };
    }
}

LM_NAMESPACE_BEGIN

// For Eigen
// https://eigen.tuxfamily.org/dox/TopicCustomizingEigen.html
auto abs(const Vec3& v) -> Vec3 { return Vec3(std::abs(v.x), std::abs(v.y), std::abs(v.z)); }
auto sqrt(const Vec3& v) -> Vec3 { return Vec3(std::sqrt(v.x), std::sqrt(v.y), std::sqrt(v.z)); }
auto log(const Vec3& v) -> Vec3 { return Vec3(std::log(v.x), std::log(v.y), std::log(v.z)); }
auto ceil(const Vec3& v) -> Vec3 { return Vec3(std::ceil(v.x), std::ceil(v.y), std::ceil(v.z)); }
auto operator<<(std::ostream& os, const Vec3& v) -> std::ostream&
{
    os << "(" << v.x << "," << v.y << "," << v.z << ")";
    return os;
}

/*!
    \brief Radiosity renderer.

    Implements the radiosity algorithm by directly solving linear system.
    This implementation currently only supports the diffues BSDF (`bsdf::diffuse`)
    and the area light (`light::area`).

    References:
      - [Cohen & Wallace 1995] Radiosity and realistic image synthesis
      - [Willmott & Heckbert 1997] An empirical comparison of radiosity algorithms
      - [Schroder & Hanrahan 1993] On the form factor between two polygons
*/
class Renderer_Radiosity final : public Renderer
{
public:

    LM_IMPL_CLASS(Renderer_Radiosity, Renderer);

public:

    LM_IMPL_F(Initialize) = [this](const PropertyNode* prop) -> bool
    {
        subdivLimitArea_ = prop->ChildAs<Float>("subdivlimitarea", 0.1_f);
        wireframe_ = prop->ChildAs<int>("wireframe", 0);
        return true;
    };

    LM_IMPL_F(Render) = [this](const Scene* scene_, Random* initRng, const std::string& outputPath) -> void
    {
        const auto* scene = static_cast<const Scene3*>(scene_);
        auto* film = static_cast<const Sensor*>(scene->GetSensor()->emitter)->GetFilm();

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

        // Create patches
        Patches patches;
        patches.Create(scene, subdivLimitArea_);

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

        #pragma region Setup matrices

        LM_LOG_INFO("Setup matrix");

        const int N = patches.Size();
        //using Matrix = Eigen::SparseMatrix<Vec3>;
        using Matrix = Eigen::Matrix<Vec3, Eigen::Dynamic, Eigen::Dynamic>;
        using Vector = Eigen::Matrix<Vec3, Eigen::Dynamic, 1>;

        // Setup emission term
        Vector E(N);
        for (int i = 0; i < N; i++)
        {
            const auto& patch = patches.At(i);
            const auto* light = patch.primitive->light;
            if (!light)
            {
                continue;
            }
            E(i) = light->Emittance().ToRGB();
        }

        // Setup matrix of interactions
        Matrix K(N, N);
        K.setIdentity();
        for (int i = 0; i < N; i++)
        {
            for (int j = 0; j < N; j++)
            {
                const auto Fij = RadiosityUtils::EstimateFormFactor(scene, patches.At(i), patches.At(j));
                if (Fij > 0_f)
                {
                    K.coeffRef(i, j) -= patches.At(i).primitive->bsdf->Reflectance().ToRGB() * Fij;
                }
            }

            const double progress = 100.0 * i / N;
            LM_LOG_INPLACE(boost::str(boost::format("Progress: %.1f%%") % progress));
        }

        LM_LOG_INFO("Progress: 100.0%");

        #pragma endregion

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

        #pragma region Solve radiosity equation

        LM_LOG_INFO("Solving linear system");

        Eigen::BiCGSTAB<Matrix> solver;
        solver.compute(K);
        Vector B = solver.solve(E);

        #if LM_RADIOSITY_DEBUG
        std::stringstream ss;
        ss << B << std::endl;
        LM_LOG_INFO(ss.str());
        #endif

        #pragma endregion

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

        #pragma region Rendering (ray casting)

        LM_LOG_INFO("Visualizing result");

        const int width  = film->Width();
        const int height = film->Height();
        for (int y = 0; y < height; y++)
        {
            for (int x = 0; x < width; x++)
            {
                // Raster position
                Vec2 rasterPos((Float(x) + 0.5_f) / Float(width), (Float(y) + 0.5_f) / Float(height));

                // Position and direction of a ray
                SurfaceGeometry geomE;
                Vec3 wo;
                scene->GetSensor()->emitter->SamplePositionAndDirection(rasterPos, Vec2(), geomE, wo);

                // Setup a ray
                Ray ray = { geomE.p, wo };

                // Intersection query
                Intersection isect;
                if (!scene->Intersect(ray, isect))
                {
                    // No intersection -> black
                    film->SetPixel(x, y, SPD());
                    continue;
                }

                // Compute patch index & visualize the radiosity
                patches.IteratePatches(isect, subdivLimitArea_, [&](size_t patchindex, const Vec2& uv) -> void
                {
                    if (wireframe_)
                    {
                        // Visualize wire frame
                        // Compute minimum distance from each edges
                        const auto mind = Math::Min(uv.x, Math::Min(uv.y, 1_f - uv.x - uv.y));
                        if (mind < 0.05f)
                        {
                            film->SetPixel(x, y, SPD(Math::Abs(Math::Dot(isect.geom.sn, -ray.d))));
                        }
                    }
                    else
                    {
                        film->SetPixel(x, y, SPD(B(patchindex)));
                    }
                });
            }

            if (y % 10 == 0)
            {
                const double progress = 100.0 * y / film->Height();
                LM_LOG_INPLACE(boost::str(boost::format("Progress: %.1f%%") % progress));
            }
        }

        LM_LOG_INFO("Progress: 100.0%");

        #pragma endregion

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

        #pragma region Save image
        {
            LM_LOG_INFO("Saving image");
            LM_LOG_INDENTER();
            film->Save(outputPath);
        }
        #pragma endregion
    };

private:

    Float subdivLimitArea_;
    int wireframe_;

};

LM_COMPONENT_REGISTER_IMPL(Renderer_Radiosity, "renderer::radiosity");

LM_NAMESPACE_END


#pragma once
#include <boost/serialization/access.hpp>
#include <boost/serialization/base_object.hpp>
#include "BaseNeuron.hpp"
#include "RecurrentNeuron.hpp"

namespace snn::internal
{
    class GatedRecurrentUnit final
    {
    private:
        friend class boost::serialization::access;
        template <class Archive>
        void serialize(Archive& ar, unsigned version);

        friend class RecurrentNeuron;

        std::vector<float> errors;

        int numberOfInputs{};

        float previousOutput = 0;
        float recurrentError = 0;
        float updateGateOutput = 0;
        float outputGateOutput = 0;

        RecurrentNeuron resetGate;
        RecurrentNeuron updateGate;
        RecurrentNeuron outputGate;

        void reset();

    public:
        GatedRecurrentUnit() = default; // use restricted to Boost library only
        GatedRecurrentUnit(NeuronModel model, std::shared_ptr<NeuralNetworkOptimizer> optimizer);
        GatedRecurrentUnit(const GatedRecurrentUnit& recurrentNeuron) = default;
        ~GatedRecurrentUnit() = default;

        [[nodiscard]] float output(const std::vector<float>& inputs, bool reset);
        [[nodiscard]] std::vector<float>& backOutput(float error);
        void train(float error);

        [[nodiscard]] std::vector<float> getWeights() const;
        [[nodiscard]] int getNumberOfParameters() const;
        [[nodiscard]] int getNumberOfInputs() const;

        [[nodiscard]] int isValid() const;

        NeuralNetworkOptimizer* getOptimizer() const;
        void setOptimizer(std::shared_ptr<NeuralNetworkOptimizer> newOptimizer);

        bool operator==(const GatedRecurrentUnit& neuron) const;
        bool operator!=(const GatedRecurrentUnit& neuron) const;
    };

    template <class Archive>
    void GatedRecurrentUnit::serialize(Archive& ar, [[maybe_unused]] const unsigned version)
    {
        ar & this->numberOfInputs;
        ar & this->previousOutput;
        ar & this->recurrentError;
        ar & this->updateGateOutput;
        ar & this->outputGateOutput;
        ar & this->resetGate;
        ar & this->updateGate;
        ar & this->outputGate;
    }
}


#ifndef STATE_HPP
#define STATE_HPP

#include <polyfem/ElementBases.hpp>
#include <polyfem/ElementAssemblyValues.hpp>
#include <polyfem/AssemblyValsCache.hpp>
#include <polyfem/RhsAssembler.hpp>
#include <polyfem/Problem.hpp>
#include <polyfem/Mesh.hpp>
#include <polyfem/Problem.hpp>
#include <polyfem/LocalBoundary.hpp>
#include <polyfem/InterfaceData.hpp>
#include <polyfem/AssemblerUtils.hpp>
#include <polyfem/ElasticityUtils.hpp>
#include <polyfem/Common.hpp>
#include <polyfem/Logger.hpp>

#include <polyfem/Mesh2D.hpp>
#include <polyfem/Mesh3D.hpp>

#include <Eigen/Dense>
#include <Eigen/Sparse>

// #ifdef POLYFEM_WITH_TBB
// #include <thread>
// #include <tbb/task_scheduler_init.h>
// #endif

#include <memory>
#include <string>

namespace polyfem
{
	//class used to save the solution of time dependent problems in code instead of saving it to the disc
	class SolutionFrame
	{
	public:
		std::string name;
		Eigen::MatrixXd points;
		Eigen::MatrixXi connectivity;
		Eigen::MatrixXd solution;
		Eigen::MatrixXd pressure;
		Eigen::MatrixXd exact;
		Eigen::MatrixXd error;
		Eigen::MatrixXd scalar_value;
		Eigen::MatrixXd scalar_value_avg;
	};

	//main class that contains the polyfem solver
	class State
	{
	public:
		~State() = default;

		State();

		//initalizing the logger
		//log_file is to write it to a file (use log_file="") to output to consolle
		//log_level 0 all message, 6 no message. 2 is info, 1 is debug
		//quiet the log
		void init_logger(const std::string &log_file, int log_level, const bool is_quiet);

		//initalizing the logger
		//writes to an outputstream
		void init_logger(std::ostream &os, int log_level);

		//initalizing the logger meant for internal usage
		void init_logger(std::vector<spdlog::sink_ptr> &sinks, int log_level);

		//initialize the polyfem solver with a json settings
		void init(const json &args, const std::string &output_dir = "");
		// void init(const std::string &json);

		//change log level, log_level 0 all message, 6 no message. 2 is info, 1 is debug
		void set_log_level(int log_level)
		{
			log_level = std::max(0, std::min(6, log_level));
			spdlog::set_level(static_cast<spdlog::level::level_enum>(log_level));
		}

		//get the output log as json
		//this is *not* what gets printed but more informative
		//information, eg it contains runtimes, errors, etc.
		std::string get_log()
		{
			std::stringstream ss;
			save_json(ss);
			return ss.str();
		}

		//solver settings
		json args;

		// Directory for output files
		std::string output_dir;

		//assembler, it dispatches call to the differnt assembers based on the formulation
		AssemblerUtils assembler;
		//current problem, it contains rhs and bc
		std::shared_ptr<Problem> problem;

		//density of the input, default=1.
		Density density;

		//FE bases, the size is #elements
		std::vector<ElementBases> bases;
		//FE pressure bases for mixed elements, the size is #elements
		std::vector<ElementBases> pressure_bases;

		//Geometric mapping bases, if the elements are isoparametric, this list is empty
		std::vector<ElementBases> geom_bases;

		//used to store assembly values for small problems
		AssemblyValsCache ass_vals_cache;
		AssemblyValsCache pressure_ass_vals_cache;

		//list of boundary nodes
		std::vector<int> boundary_nodes;
		//list of neumann boundary nodes
		std::vector<int> pressure_boundary_nodes;
		//mapping from elements to nodes for dirichlet boundary conditions
		std::vector<LocalBoundary> local_boundary;
		//mapping from elements to nodes for neumann boundary conditions
		std::vector<LocalBoundary> local_neumann_boundary;
		//nodes on the boundary of polygonal elements, used for harmonic bases
		std::map<int, InterfaceData> poly_edge_to_data;

		//current mesh, it can be a Mesh2D or Mesh3D
		std::unique_ptr<Mesh> mesh;

		//polygons, used since poly have no geom mapping
		std::map<int, Eigen::MatrixXd> polys;
		//polyhedra, used since poly have no geom mapping
		std::map<int, std::pair<Eigen::MatrixXd, Eigen::MatrixXi>> polys_3d;

		//parent element used to track refinements
		std::vector<int> parent_elements;

		//average system mass, used for contact with IPC
		double avg_mass;

		//stiffness and mass matrix.
		//Stiffness is not compute for non linea problems
		//Mass is computed only for time dependent problems
		StiffnessMatrix stiffness, mass;
		//System righ-hand side.
		//rhs_in is an input that, if not empty, gets copied to rhs
		Eigen::MatrixXd rhs, rhs_in;

		//solution and pressure solution
		//if the problem is not mixed, pressure is empty
		Eigen::MatrixXd sol, pressure;

		//boundary mesh used for collision
		//boundary_nodes_pos contains the total number of nodes, the internal ones are zero
		//for high-order fem the faces are triangulated
		//this is currently supported only for tri and tet meshes
		Eigen::MatrixXd boundary_nodes_pos;
		Eigen::MatrixXd boundary_nodes_pos_pressure;
		Eigen::MatrixXi boundary_edges;
		Eigen::MatrixXi boundary_edges_pressure;
		Eigen::MatrixXi boundary_triangles;
		Eigen::MatrixXi boundary_triangles_pressure;
		Eigen::MatrixXi boundary_faces_to_edges;

		//boundary visualization mesh
		Eigen::MatrixXd boundary_vis_vertices;
		Eigen::MatrixXd boundary_vis_local_vertices;
		Eigen::MatrixXi boundary_vis_elements;
		Eigen::MatrixXi boundary_vis_elements_ids;
		Eigen::MatrixXd boundary_vis_normals;

		//grid mesh
		Eigen::MatrixXd grid_points;
		Eigen::MatrixXi grid_points_to_elements;
		Eigen::MatrixXd grid_points_bc;

		//spectrum of the stiffness matrix, enable only if POLYSOLVE_WITH_SPECTRA is ON (off by default)
		Eigen::Vector4d spectrum;

		//information of the solver, eg num iteration, time, errors, etc
		//the informations varies depending on the solver
		json solver_info;

		//use average pressure for stokes problem to fix the additional dofs, true by default
		//if false, it will fix one pressure node to zero
		bool use_avg_pressure;

		//number of bases and pressure bases
		int n_bases, n_pressure_bases;
		//vector of discretization oders, used when not all elements have the same degree
		//one per element
		Eigen::VectorXi disc_orders;

		//max edge lenght
		double mesh_size;
		//min edge lenght
		double min_edge_length;
		//avg edge lenght
		double average_edge_length;

		//errors, lp_err is in fact an L8 error
		double l2_err, linf_err, lp_err, h1_err, h1_semi_err, grad_max_err;

		//non zeros and sytem matrix size
		//num dof is the totdal dof in the system
		long long nn_zero, mat_size, num_dofs;

		//timings
		//construct the basis
		double building_basis_time;
		//load the mesh
		double loading_mesh_time;
		//build the polygonal/polyhedral bases
		double computing_poly_basis_time;
		//assembly
		double assembling_stiffness_mat_time;
		//computing the rhs
		double assigning_rhs_time;
		//solve
		double solving_time;
		//compute error
		double computing_errors_time;

		//statiscs on angle, compute only when using p_ref (false by default)
		double max_angle;
		double sigma_max, sigma_min, sigma_avg;

		//number of flipped elements, compute only when using count_flipped_els (false by default)
		int n_flipped;

		//statiscs on the mesh, see Polyspline paper for desciption
		int simplex_count;
		int regular_count;
		int regular_boundary_count;
		int simple_singular_count;
		int multi_singular_count;
		int boundary_count;
		int non_regular_boundary_count;
		int non_regular_count;
		int undefined_count;
		int multi_singular_boundary_count;

		//flag to decide if exporting the time dependent solution to files
		//or save it in the solution_frames array
		bool solve_export_to_file = true;
		std::vector<SolutionFrame> solution_frames;

		//utility function that gets the problem params (eg material)
		//it adds the problem dimension from the problem and PDE
		json build_json_params();

		//computes the mesh size, it samples every edges n_samples times
		//uses curved_mesh_size (false by default) to compute the size of
		//the linear mesh
		void compute_mesh_size(const Mesh &mesh, const std::vector<ElementBases> &bases, const int n_samples);

		//loads the mesh from the json arguments
		void load_mesh();
		//loads a febio file
		void load_febio(const std::string &path);
		//loads the mesh from a geogram mesh, skip_boundary_sideset = false it uses the lambda boundary_marker to assigm the sideset
		//the input of the lambda is the face barycenter, the output is the sideset id
		void load_mesh(GEO::Mesh &meshin, const std::function<int(const RowVectorNd &)> &boundary_marker, bool skip_boundary_sideset = false);
		//loads a mesh from a path
		void load_mesh(const std::string &path)
		{
			args["mesh"] = path;
			load_mesh();
		}
		//loads a mesh from a path and uses the bc_tag to assign sideset ids
		//the bc_tag file should contain a list of integers, one per face
		void load_mesh(const std::string &path, const std::string &bc_tag)
		{
			args["mesh"] = path;
			args["bc_tag"] = bc_tag;
			load_mesh();
		}
		//load the mesh from V and F, V is #vertices x dim, F is #elements x size (size = 3 for triangle mesh, size=4 for a quaud mesh if dim is 2)
		void load_mesh(const Eigen::MatrixXd &V, const Eigen::MatrixXi &F)
		{
			if (V.cols() == 2)
				mesh = std::make_unique<polyfem::Mesh2D>();
			else
				mesh = std::make_unique<polyfem::Mesh3D>();
			mesh->build_from_matrices(V, F);

			load_mesh();
		}

		//set the multimaterial, this is mean for internal usage.
		void set_multimaterial(const std::function<void(const Eigen::MatrixXd &, const Eigen::MatrixXd &, const Eigen::MatrixXd &)> &setter);

		//set the boundary sideset
		//from a lambda that takes the face/edge barycenter
		void set_boundary_side_set(const std::function<int(const polyfem::RowVectorNd &)> &boundary_marker) { mesh->compute_boundary_ids(boundary_marker); }
		//from a lambda that takes the face/edge barycenter and a flag if the face/edge is boundary or not (used to set internal boundaries)
		void set_boundary_side_set(const std::function<int(const polyfem::RowVectorNd &, bool)> &boundary_marker) { mesh->compute_boundary_ids(boundary_marker); }
		//from a lambda that takes the face/edge vertices and a flag if the face/edge is boundary or not (used to set internal boundaries)
		void set_boundary_side_set(const std::function<int(const std::vector<int> &, bool)> &boundary_marker) { mesh->compute_boundary_ids(boundary_marker); }

		//solves the problem
		void solve()
		{
			compute_mesh_stats();

			build_basis();

			assemble_rhs();
			assemble_stiffness_mat();

			solve_export_to_file = false;
			solution_frames.clear();
			solve_problem();
			solve_export_to_file = true;
		}

		//internal methods, they are called from solve

		//builds the bases step 2 of solve
		void build_basis();
		//extracts the boundary mesh for collision, called in build_basis
		void extract_boundary_mesh(bool for_pressure = false);

		//extracts the boundary mesh for visualization, called in build_basis
		void extract_vis_boundary_mesh();
		//assemble matrices, step 4 of solve
		void assemble_stiffness_mat();
		//compute rhs, step 3 of solve
		void assemble_rhs();
		//solves the proble, step 5
		void solve_problem();

		//Aux solving functions, c_sol=x are necessary since they contain the pressure, while sol dosent
		void solve_transient_navier_stokes_split(const int time_steps, const double dt, const RhsAssembler &rhs_assembler);
		void solve_transient_navier_stokes(const int time_steps, const double t0, const double dt, const RhsAssembler &rhs_assembler, Eigen::VectorXd &c_sol);
		void solve_transient_scalar(const int time_steps, const double t0, const double dt, const RhsAssembler &rhs_assembler, Eigen::VectorXd &x);
		void solve_transient_tensor_linear(const int time_steps, const double t0, const double dt, const RhsAssembler &rhs_assembler);
		void solve_transient_tensor_non_linear(const int time_steps, const double t0, const double dt, const RhsAssembler &rhs_assembler);
		void solve_linear();
		void solve_navier_stokes();
		void solve_non_linear();

		//compute the errors, not part of solve
		void compute_errors();
		//saves all data on the disk according to the input params
		void export_data();

		//evaluates the function fun at the vertices on the mesh
		//actual dim is the size of the problem (e.g., 1 for Laplace, dim for elasticity)
		void compute_vertex_values(int actual_dim, const std::vector<ElementBases> &basis,
								   const MatrixXd &fun, Eigen::MatrixXd &result);
		//compute von mises stress at quadrature points for the function fun, also compute the interpolated function
		void compute_stress_at_quadrature_points(const MatrixXd &fun, Eigen::MatrixXd &result, Eigen::VectorXd &von_mises);
		//interpolate the function fun. n_points is the size of the output. boundary_only interpolates only at boundary elements
		void interpolate_function(const int n_points, const Eigen::MatrixXd &fun, Eigen::MatrixXd &result, const bool boundary_only = false);
		//interpolate the function fun. n_points is the size of the output. boundary_only interpolates only at boundary elements
		//actual dim is the size of the problem (e.g., 1 for Laplace, dim for elasticity)
		void interpolate_function(const int n_points, const int actual_dim, const std::vector<ElementBases> &basis, const MatrixXd &fun, MatrixXd &result, const bool boundary_only = false);

		//interpolate solution and gradient at in element el_index for the local_pts in the reference element (calls interpolate_at_local_vals with sol)
		void interpolate_at_local_vals(const int el_index, const MatrixXd &local_pts, MatrixXd &result, MatrixXd &result_grad);
		//interpolate the function fun and its gradient at in element el_index for the local_pts in the reference element
		void interpolate_at_local_vals(const int el_index, const MatrixXd &local_pts, const MatrixXd &fun, MatrixXd &result, MatrixXd &result_grad);
		//interpolate the function fun and its gradient at in element el_index for the local_pts in the reference element using bases bases
		//actual dim is the size of the problem (e.g., 1 for Laplace, dim for elasticity)
		void interpolate_at_local_vals(const int el_index, const int actual_dim, const std::vector<ElementBases> &bases, const MatrixXd &local_pts, const MatrixXd &fun, MatrixXd &result, MatrixXd &result_grad);

		//computes scalar quantity of funtion (ie von mises for elasticity and norm of velocity for fluid)
		void compute_scalar_value(const int n_points, const Eigen::MatrixXd &fun, Eigen::MatrixXd &result, const bool boundary_only = false);
		//computes scalar quantity of funtion (ie von mises for elasticity and norm of velocity for fluid)
		//the scalar value is averaged around every node to make it continuos
		void average_grad_based_function(const int n_points, const MatrixXd &fun, MatrixXd &result_scalar, MatrixXd &result_tensor, const bool boundary_only = false);
		//compute tensor quantity (ie stress tensor or velocy)
		void compute_tensor_value(const int n_points, const Eigen::MatrixXd &fun, Eigen::MatrixXd &result, const bool boundary_only = false);

		//computes integrated solution (fun) per surface face. pts and faces are the boundary are the boundary on the rest configuration
		void interpolate_boundary_function(const MatrixXd &pts, const MatrixXi &faces, const MatrixXd &fun, const bool compute_avg, MatrixXd &result);
		//computes integrated solution (fun) per surface face vertex. pts and faces are the boundary are the boundary on the rest configuration
		void interpolate_boundary_function_at_vertices(const MatrixXd &pts, const MatrixXi &faces, const MatrixXd &fun, MatrixXd &result);
		//computes traction foces for fun (tensor * surface normal) result, stress tensor, and von mises, per surface face. pts and faces are the boundary on the rest configuration.
		//disp is the displacement of the surface vertices
		void interpolate_boundary_tensor_function(const MatrixXd &pts, const MatrixXi &faces, const MatrixXd &fun, const MatrixXd &disp, const bool compute_avg, MatrixXd &result, MatrixXd &stresses, MatrixXd &mises);
		//same as above with disp=0
		void interpolate_boundary_tensor_function(const MatrixXd &pts, const MatrixXi &faces, const MatrixXd &fun, const bool compute_avg, MatrixXd &result, MatrixXd &stresses, MatrixXd &mises);

		//returns a triangulated representation of the sideset. sidesets contains integers mapping to faces
		void get_sidesets(Eigen::MatrixXd &pts, Eigen::MatrixXi &faces, Eigen::MatrixXd &sidesets);

		// Resolve path relative to output_dir if the path is not absolute
		std::string resolve_output_path(const std::string &path);

		//saves the output statistic to a stream
		void save_json(std::ostream &out);
		//saves the output statistic to a json object
		void save_json(nlohmann::json &j);
		//saves the output statistic to disc accoding to params
		void save_json();

		//compute stats (counts els type, mesh lenght, etc), step 1 of solve
		void compute_mesh_stats();

		//builds visualzation mesh, upsampled mesh used for visualization
		//the visualization mesh is a dense mesh per element all disconnected
		//it also retuns the mapping to element id and discretization of every elment
		//works in 2 and 3d. if the mesh is not simplicial it gets tri/tet halized
		void build_vis_mesh(Eigen::MatrixXd &points, Eigen::MatrixXi &tets, Eigen::MatrixXi &el_id, Eigen::MatrixXd &discr);

		//saves the vtu file for time t
		void save_vtu(const std::string &name, const double t);
		//saves the surface vtu file for for surface quantites, eg traction forces
		void save_surface(const std::string &name);
		//saves an obj of the wireframe
		void save_wire(const std::string &name, bool isolines = false);

		//samples to solution on the visualization mesh and return the vis mesh (points and tets) and the interpolated values (fun)
		void get_sampled_solution(Eigen::MatrixXd &points, Eigen::MatrixXi &tets, Eigen::MatrixXd &fun, bool boundary_only = false)
		{
			Eigen::MatrixXd discr;
			Eigen::MatrixXi el_id;
			const bool tmp = args["export"]["vis_boundary_only"];
			args["export"]["vis_boundary_only"] = boundary_only;

			build_vis_mesh(points, tets, el_id, discr);
			interpolate_function(points.rows(), sol, fun, boundary_only);

			args["export"]["vis_boundary_only"] = tmp;
		}

		//samples to stess tensor on the visualization mesh and return them (fun)
		void get_stresses(Eigen::MatrixXd &fun, bool boundary_only = false)
		{
			Eigen::MatrixXd points;
			Eigen::MatrixXi tets;
			Eigen::MatrixXi el_id;
			Eigen::MatrixXd discr;
			const bool tmp = args["export"]["vis_boundary_only"];
			args["export"]["vis_boundary_only"] = boundary_only;

			build_vis_mesh(points, tets, el_id, discr);
			compute_tensor_value(points.rows(), sol, fun, boundary_only);

			args["export"]["vis_boundary_only"] = tmp;
		}

		//samples to von mises stesses on the visualization mesh and return them (fun)
		void get_sampled_mises(Eigen::MatrixXd &fun, bool boundary_only = false)
		{
			Eigen::MatrixXd points;
			Eigen::MatrixXi tets;
			Eigen::MatrixXi el_id;
			Eigen::MatrixXd discr;
			const bool tmp = args["export"]["vis_boundary_only"];
			args["export"]["vis_boundary_only"] = boundary_only;

			build_vis_mesh(points, tets, el_id, discr);
			compute_scalar_value(points.rows(), sol, fun, boundary_only);

			args["export"]["vis_boundary_only"] = tmp;
		}

		//samples to averaged von mises stesses on the visualization mesh and return them (fun)
		void get_sampled_mises_avg(Eigen::MatrixXd &fun, Eigen::MatrixXd &tfun, bool boundary_only = false)
		{
			Eigen::MatrixXd points;
			Eigen::MatrixXi tets;
			Eigen::MatrixXi el_id;
			Eigen::MatrixXd discr;
			const bool tmp = args["export"]["vis_boundary_only"];
			args["export"]["vis_boundary_only"] = boundary_only;

			build_vis_mesh(points, tets, el_id, discr);
			average_grad_based_function(points.rows(), sol, fun, tfun, boundary_only);

			args["export"]["vis_boundary_only"] = tmp;
		}

		//returns the path of the input mesh (wrappers around the arguments)
		inline std::string mesh_path() const { return args["mesh"]; }

		inline bool has_mesh() const
		{
			return !mesh_path().empty() || (args.contains("meshes") && !args["meshes"].empty());
		}

		//return the formulation (checks if the problem is scalar or not)
		inline std::string formulation() const { return problem->is_scalar() ? scalar_formulation() : tensor_formulation(); }

		//check if using iso parametric bases
		inline bool iso_parametric() const
		{
			if (non_regular_count > 0 || non_regular_boundary_count > 0 || undefined_count > 0)
				return true;

			if (args["use_spline"])
				return true;

			if (mesh->is_rational())
				return false;

			if (args["use_p_ref"])
				return false;

			if (mesh->orders().size() <= 0)
			{
				if (args["discr_order"] == 1)
					return true;
				else
					return args["iso_parametric"];
			}

			if (mesh->orders().minCoeff() != mesh->orders().maxCoeff())
				return false;

			if (args["discr_order"] == mesh->orders().minCoeff())
				return true;

			if (args["discr_order"] == 1 && args["force_linear_geometry"])
				return true;

			return args["iso_parametric"];
		}

		//returns solver, preconditioner and solver parameters (wrappers around the arguments)
		inline std::string solver_type() const { return args["solver_type"]; }
		inline std::string precond_type() const { return args["precond_type"]; }
		inline const json &solver_params() const { return args["solver_params"]; }

		//returns the tensor and scalar formulation (wrappers around the arguments)
		inline std::string scalar_formulation() const { return args["scalar_formulation"]; }
		inline std::string tensor_formulation() const { return args["tensor_formulation"]; }
		// inline std::string mixed_formulation() const { return args["mixed_formulation"]; }

		//compute a priori prefinement in 2d and 3d, fills disc_orders
		void p_refinement(const Mesh2D &mesh2d);
		void p_refinement(const Mesh3D &mesh3d);

	private:
		//splits the solution in solution and pressure for mixed problems
		void sol_to_pressure();
		//builds bases for polygons, called inside build_basis
		void build_polygonal_basis();

		// #ifdef USE_TBB
		// 		tbb::task_scheduler_init scheduler;
		// #endif
	};

} // namespace polyfem
#endif //STATE_HPP