/*  Copyright (c) 2013, Abdullah A. Hassan <voodooattack@hotmail.com>
 *  All rights reserved.
 *
 *  Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
 *
 *  1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
 *
 *  2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following
 *     disclaimer in the documentation and/or other materials provided with the distribution.
 *
 *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
 *  INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 *  IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
 *  OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
 *  OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 *  OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 *  POSSIBILITY OF SUCH DAMAGE.
 */

#include "noiseutils.hpp"

#include "config.hpp"
#include "jsonutils.hpp"
#include "noisemodules.hpp"

#ifdef NOISE_DIR_IS_LIBNOISE
#include <libnoise/noise.h>
#else
#include <noise/noise.h>
#endif

#include <boost/algorithm/string.hpp>

namespace ADWIF
{
  std::shared_ptr<noise::module::Module> buildNoiseGraph(const Json::Value & value,
    std::vector<std::shared_ptr<noise::module::Module>> & modules,
    std::map<std::string, std::shared_ptr<noise::module::Module>> & defs,
    const std::shared_ptr<HeightMapModule> & heightMap, int seed)
  {
    std::shared_ptr<noise::module::Module> m;

    if (value["module"].empty())
    {
      if (!value["ref"].empty() && defs.find(value["ref"].asString()) != defs.end())
        return defs[value["ref"].asString()];
      else
        throw ParsingException("undefined module missing reference or reference not found:\n" +
                                value.toStyledString());
    }

    std::string module = value["module"].asString();
    boost::to_lower(module);

    if (module == "add")
    {
      std::shared_ptr<noise::module::Add> add(new noise::module::Add);
      std::shared_ptr<noise::module::Module> m1 = buildNoiseGraph(value["sources"][0], modules, defs, heightMap, seed);
      std::shared_ptr<noise::module::Module> m2 = buildNoiseGraph(value["sources"][1], modules, defs, heightMap, seed);
      add->SetSourceModule(0, *m1);
      add->SetSourceModule(1, *m2);
      m = add;
    }
    else if (module == "mul" || module == "multiply")
    {
      std::shared_ptr<noise::module::Multiply> mul(new noise::module::Multiply);
      std::shared_ptr<noise::module::Module> m1 = buildNoiseGraph(value["sources"][0], modules, defs, heightMap, seed);
      std::shared_ptr<noise::module::Module> m2 = buildNoiseGraph(value["sources"][1], modules, defs, heightMap, seed);
      mul->SetSourceModule(0, *m1);
      mul->SetSourceModule(1, *m2);
      m = mul;
    }
    else if (module == "pow" || module == "power")
    {
      std::shared_ptr<noise::module::Power> pow(new noise::module::Power);
      std::shared_ptr<noise::module::Module> m1 = buildNoiseGraph(value["sources"][0], modules, defs, heightMap, seed);
      std::shared_ptr<noise::module::Module> m2 = buildNoiseGraph(value["sources"][1], modules, defs, heightMap, seed);
      pow->SetSourceModule(0, *m1);
      pow->SetSourceModule(1, *m2);
      m = pow;
    }
    else if (module == "min")
    {
      std::shared_ptr<noise::module::Min> min(new noise::module::Min);
      std::shared_ptr<noise::module::Module> m1 = buildNoiseGraph(value["sources"][0], modules, defs, heightMap, seed);
      std::shared_ptr<noise::module::Module> m2 = buildNoiseGraph(value["sources"][1], modules, defs, heightMap, seed);
      min->SetSourceModule(0, *m1);
      min->SetSourceModule(1, *m2);
      m = min;
    }
    else if (module == "max")
    {
      std::shared_ptr<noise::module::Max> max(new noise::module::Max);
      std::shared_ptr<noise::module::Module> m1 = buildNoiseGraph(value["sources"][0], modules, defs, heightMap, seed);
      std::shared_ptr<noise::module::Module> m2 = buildNoiseGraph(value["sources"][1], modules, defs, heightMap, seed);
      max->SetSourceModule(0, *m1);
      max->SetSourceModule(1, *m2);
      m = max;
    }
    else if (module == "sel" || module == "select")
    {
      std::shared_ptr<noise::module::Select> sel(new noise::module::Select);
      std::shared_ptr<noise::module::Module> m1 = buildNoiseGraph(value["sources"][0], modules, defs, heightMap, seed);
      std::shared_ptr<noise::module::Module> m2 = buildNoiseGraph(value["sources"][1], modules, defs, heightMap, seed);
      std::shared_ptr<noise::module::Module> ctl;
      if (!value["controller"].empty())
        ctl = buildNoiseGraph(value["controller"], modules, defs, heightMap, seed);
      else
        ctl = buildNoiseGraph(value["sources"][2], modules, defs, heightMap, seed);
      sel->SetSourceModule(0, *m1);
      sel->SetSourceModule(1, *m2);
      sel->SetControlModule(*ctl);
      if (!value["falloff"].empty())
        sel->SetEdgeFalloff(value["falloff"].asDouble());
      if (value["bounds"].isArray())
      {
        if (value["bounds"][0].asDouble() < value["bounds"][1].asDouble())
          sel->SetBounds(value["bounds"][0].asDouble(), value["bounds"][1].asDouble());
        else
          throw ParsingException("Select: Upper bound must be greater than lower bound");
      }
      m = sel;
    }
    else if (module == "blend")
    {
      std::shared_ptr<noise::module::Blend> blend(new noise::module::Blend);
      std::shared_ptr<noise::module::Module> m1 = buildNoiseGraph(value["sources"][0], modules, defs, heightMap, seed);
      std::shared_ptr<noise::module::Module> m2 = buildNoiseGraph(value["sources"][1], modules, defs, heightMap, seed);
      std::shared_ptr<noise::module::Module> ctl;
      if (!value["controller"].empty())
        ctl = buildNoiseGraph(value["controller"], modules, defs, heightMap, seed);
      else
        ctl = buildNoiseGraph(value["sources"][2], modules, defs, heightMap, seed);
      blend->SetSourceModule(0, *m1);
      blend->SetSourceModule(1, *m2);
      blend->SetControlModule(*ctl);
      m = blend;
    }
    else if (module == "displace")
    {
      std::shared_ptr<noise::module::Blend> displace(new noise::module::Blend);
      std::shared_ptr<noise::module::Module> m1 = buildNoiseGraph(value["sources"][0], modules, defs, heightMap, seed);
      std::shared_ptr<noise::module::Module> m2 = buildNoiseGraph(value["sources"][1], modules, defs, heightMap, seed);
      std::shared_ptr<noise::module::Module> m3 = buildNoiseGraph(value["sources"][2], modules, defs, heightMap, seed);
      std::shared_ptr<noise::module::Module> m4 = buildNoiseGraph(value["sources"][3], modules, defs, heightMap, seed);
      displace->SetSourceModule(0, *m1);
      displace->SetSourceModule(1, *m2);
      displace->SetSourceModule(2, *m2);
      displace->SetSourceModule(3, *m3);
      m = displace;
    }
    else if (module == "curve")
    {
      std::shared_ptr<noise::module::Curve> curve(new noise::module::Curve);
      std::shared_ptr<noise::module::Module> src = buildNoiseGraph(value["sources"][0], modules, defs, heightMap, seed);
      curve->SetSourceModule(0, *src);
      for(const Json::Value & c : value["curve"])
        curve->AddControlPoint(c[0].asDouble(), c[1].asDouble()); // FIXME: No two control points can have the same input value.
      m = curve;
    }
    else if (module == "terrace")
    {
      std::shared_ptr<noise::module::Terrace> terrace(new noise::module::Terrace);
      std::shared_ptr<noise::module::Module> src = buildNoiseGraph(value["sources"][0], modules, defs, heightMap, seed);
      terrace->SetSourceModule(0, *src);
      for(const Json::Value & c : value["curve"])
        terrace->AddControlPoint(c.asDouble()); // FIXME: No two control points can have the same input value.
      m = terrace;
    }
    else if (module == "clamp")
    {
      std::shared_ptr<noise::module::Clamp> clamp(new noise::module::Clamp);
      std::shared_ptr<noise::module::Module> src = buildNoiseGraph(value["sources"][0], modules, defs, heightMap, seed);
      clamp->SetSourceModule(0, *src);
      clamp->SetBounds(value["min"].asDouble(), value["max"].asDouble());
      m = clamp;
    }
    else if (module == "exp" || module == "exponent")
    {
      std::shared_ptr<noise::module::Exponent> exp(new noise::module::Exponent);
      std::shared_ptr<noise::module::Module> src = buildNoiseGraph(value["sources"][0], modules, defs, heightMap, seed);
      exp->SetSourceModule(0, *src);
      if (!value["exponent"].empty())
        exp->SetExponent(value["exponent"].asDouble());
      m = exp;
    }
    else if (module == "abs")
    {
      std::shared_ptr<noise::module::Abs> abs(new noise::module::Abs);
      std::shared_ptr<noise::module::Module> src = buildNoiseGraph(value["sources"][0], modules, defs, heightMap, seed);
      abs->SetSourceModule(0, *src);
      m = abs;
    }
    else if (module == "invert")
    {
      std::shared_ptr<noise::module::Invert> invert(new noise::module::Invert);
      std::shared_ptr<noise::module::Module> src = buildNoiseGraph(value["sources"][0], modules, defs, heightMap, seed);
      invert->SetSourceModule(0, *src);
      m = invert;
    }
    else if (module == "cache")
    {
      std::shared_ptr<noise::module::Cache> cache(new noise::module::Cache);
      std::shared_ptr<noise::module::Module> src = buildNoiseGraph(value["sources"][0], modules, defs, heightMap, seed);
      cache->SetSourceModule(0, *src);
      m = cache;
    }
    else if (module == "scale" || module == "scalebias")
    {
      std::shared_ptr<noise::module::ScaleBias> scale(new noise::module::ScaleBias);
      std::shared_ptr<noise::module::Module> src = buildNoiseGraph(value["sources"][0], modules, defs, heightMap, seed);
      scale->SetSourceModule(0, *src);
      if (!value["scale"].empty())
        scale->SetScale(value["scale"].asDouble());
      if (!value["bias"].empty())
        scale->SetScale(value["bias"].asDouble());
      m = scale;
    }
    else if (module == "scalep" || module == "scalepoint")
    {
      std::shared_ptr<noise::module::ScalePoint> scale(new noise::module::ScalePoint);
      std::shared_ptr<noise::module::Module> src = buildNoiseGraph(value["sources"][0], modules, defs, heightMap, seed);
      scale->SetSourceModule(0, *src);
      if (!value["scale"].empty())
      {
        scale->SetScale(value["scale"].asDouble());
        if (!value["scalex"].empty())
          scale->SetXScale(value["scalex"].asDouble());
        if (!value["scaley"].empty())
          scale->SetYScale(value["scaley"].asDouble());
        if (!value["scalez"].empty())
          scale->SetZScale(value["scalez"].asDouble());
      }
      else if (value["scale"].isArray())
        scale->SetScale(value["scale"][0].asDouble(), value["scale"][1].asDouble(), value["scale"][2].asDouble());
      m = scale;
    }
    else if (module == "trans" || module == "translate")
    {
      std::shared_ptr<noise::module::TranslatePoint> trans(new noise::module::TranslatePoint);
      std::shared_ptr<noise::module::Module> src = buildNoiseGraph(value["sources"][0], modules, defs, heightMap, seed);
      trans->SetSourceModule(0, *src);
      if (!value["translation"].empty())
      {
        trans->SetTranslation(value["translation"].asDouble());
        if (!value["x"].empty())
          trans->SetXTranslation(value["x"].asDouble());
        if (!value["y"].empty())
          trans->SetYTranslation(value["y"].asDouble());
        if (!value["z"].empty())
          trans->SetZTranslation(value["z"].asDouble());
      }
      else if (value["translation"].isArray())
        trans->SetTranslation(value["translation"][0].asDouble(), value["translation"][1].asDouble(), value["translation"][2].asDouble());
      m = trans;
    }
    else if (module == "rot" || module == "rotate")
    {
      std::shared_ptr<noise::module::RotatePoint> rot(new noise::module::RotatePoint);
      std::shared_ptr<noise::module::Module> src = buildNoiseGraph(value["sources"][0], modules, defs, heightMap, seed);
      rot->SetSourceModule(0, *src);
      if (value["rotation"].isArray())
        rot->SetAngles(value["rotation"][0].asDouble(), value["rotation"][1].asDouble(), value["rotation"][2].asDouble());
      m = rot;
    }
    else if (module == "const" || module == "constant")
    {
      std::shared_ptr<noise::module::Const> con(new noise::module::Const);
      con->SetConstValue(value["value"].asDouble());
      m = con;
    }
    else if (module == "checkerboard")
    {
      std::shared_ptr<noise::module::Checkerboard> cb(new noise::module::Checkerboard);
      m = cb;
    }
    else if (module == "billow")
    {
      std::shared_ptr<noise::module::Billow> billow(new noise::module::Billow);
      if (!value["frequency"].empty())
        billow->SetFrequency(value["frequency"].asDouble());
      if (!value["lacunarity"].empty())
        billow->SetLacunarity(value["lacunarity"].asDouble());
      if (!value["octaves"].empty())
        billow->SetOctaveCount(value["octaves"].asInt());
      if (!value["persistence"].empty())
        billow->SetPersistence(value["persistence"].asDouble());
      if (value["quality"].isString())
      {
        std::string quality = value["quality"].asString();
        boost::to_lower(quality);
        if (quality == "fast")
          billow->SetNoiseQuality(noise::NoiseQuality::QUALITY_FAST);
        else if (quality == "standard")
          billow->SetNoiseQuality(noise::NoiseQuality::QUALITY_STD);
        else if (quality == "best")
          billow->SetNoiseQuality(noise::NoiseQuality::QUALITY_BEST);
        else
          throw ParsingException("unsupported quality specifier:\n" + value.toStyledString());
      }
      int s = seed;
      if (!value["seed"].empty())
        s ^= value["seed"].asInt();
      billow->SetSeed(seed);
      m = billow;
    }
    else if (module == "perlin")
    {
      std::shared_ptr<noise::module::Perlin> perlin(new noise::module::Perlin);
      if (!value["frequency"].empty())
        perlin->SetFrequency(value["frequency"].asDouble());
      if (!value["lacunarity"].empty())
        perlin->SetLacunarity(value["lacunarity"].asDouble());
      if (!value["octaves"].empty())
        perlin->SetOctaveCount(value["octaves"].asInt());
      if (!value["persistence"].empty())
        perlin->SetPersistence(value["persistence"].asDouble());
      if (value["quality"].isString())
      {
        std::string quality = value["quality"].asString();
        boost::to_lower(quality);
        if (quality == "fast")
          perlin->SetNoiseQuality(noise::NoiseQuality::QUALITY_FAST);
        else if (quality == "standard")
          perlin->SetNoiseQuality(noise::NoiseQuality::QUALITY_STD);
        else if (quality == "best")
          perlin->SetNoiseQuality(noise::NoiseQuality::QUALITY_BEST);
        else
          throw ParsingException("unsupported quality specifier:\n" + value.toStyledString());
      }
      int s = seed;
      if (!value["seed"].empty())
        s ^= value["seed"].asInt();
      perlin->SetSeed(seed);
      m = perlin;
    }
    else if (module == "ridged" || module == "ridgedmulti")
    {
      std::shared_ptr<noise::module::RidgedMulti> ridged(new noise::module::RidgedMulti);
      if (!value["frequency"].empty())
        ridged->SetFrequency(value["frequency"].asDouble());
      if (!value["lacunarity"].empty())
        ridged->SetLacunarity(value["lacunarity"].asDouble());
      if (!value["octaves"].empty())
        ridged->SetOctaveCount(value["octaves"].asInt());
      if (value["quality"].isString())
      {
        std::string quality = value["quality"].asString();
        boost::to_lower(quality);
        if (quality == "fast")
          ridged->SetNoiseQuality(noise::NoiseQuality::QUALITY_FAST);
        else if (quality == "standard")
          ridged->SetNoiseQuality(noise::NoiseQuality::QUALITY_STD);
        else if (quality == "best")
          ridged->SetNoiseQuality(noise::NoiseQuality::QUALITY_BEST);
        else
          throw ParsingException("unsupported quality specifier:\n" + value.toStyledString());
      }
      int s = seed;
      if (!value["seed"].empty())
        s ^= value["seed"].asInt();
      ridged->SetSeed(seed);
      m = ridged;
    }
    else if (module == "voronoi")
    {
      std::shared_ptr<noise::module::Voronoi> voronoi(new noise::module::Voronoi);
      if (!value["frequency"].empty())
        voronoi->SetFrequency(value["frequency"].asDouble());
      if (!value["displacement"].empty())
        voronoi->SetDisplacement(value["displacement"].asDouble());
      int s = seed;
      if (!value["seed"].empty())
        s ^= value["seed"].asInt();
      voronoi->SetSeed(seed);
      m = voronoi;
    }
    else if (module == "cylinders")
    {
      std::shared_ptr<noise::module::Cylinders> cylinders(new noise::module::Cylinders);
      if (!value["frequency"].empty())
        cylinders->SetFrequency(value["frequency"].asDouble());
      m = cylinders;
    }
    else if (module == "spheres")
    {
      std::shared_ptr<noise::module::Spheres> spheres(new noise::module::Spheres);
      if (!value["frequency"].empty())
        spheres->SetFrequency(value["frequency"].asDouble());
      m = spheres;
    }
    else if (module == "turbulence")
    {
      std::shared_ptr<noise::module::Turbulence> turbulence(new noise::module::Turbulence);
      std::shared_ptr<noise::module::Module> src = buildNoiseGraph(value["sources"][0], modules, defs, heightMap, seed);
      turbulence->SetSourceModule(0, *src);
      if (!value["frequency"].empty())
        turbulence->SetFrequency(value["frequency"].asDouble());
      if (!value["power"].empty())
        turbulence->SetPower(value["power"].asDouble());
      if (!value["roughness"].empty())
        turbulence->SetRoughness(value["roughness"].asDouble());
      int s = seed;
      if (!value["seed"].empty())
        s ^= value["seed"].asInt();
      turbulence->SetSeed(seed);
      m = turbulence;
    }
    else if (module == "heightmap")
    {
      m = heightMap;
    }
    else
      throw ParsingException("unknown noise module:\n" + value.toStyledString());

    modules.push_back(m);

    if (value["name"].isString())
      defs.insert({value["name"].asString(), m});

    return m;
  }
}


// (C) Copyright Jeremy Siek    2004.
// Distributed under the Boost Software License, Version 1.0. (See
// accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
#ifndef BOOST_FIBONACCI_HEAP_HPP
#define BOOST_FIBONACCI_HEAP_HPP

#if defined(__sgi) && !defined(__GNUC__)
# include <math.h>
#else
# include <boost/config/no_tr1/cmath.hpp>
#endif
#include <iosfwd>
#include <vector>
#include <functional>
#include <boost/config.hpp>
#include <boost/property_map/property_map.hpp>

//
// An adaptation of Knuth's Fibonacci heap implementation
// in "The Stanford Graph Base", pages 475-482.
//

namespace boost {

template <class T,
          class Compare = std::less<T>,
          class ID = identity_property_map>
class fibonacci_heap
{
  typedef typename boost::property_traits<ID>::value_type size_type;
  typedef T value_type;
protected:
  typedef fibonacci_heap self;
  typedef std::vector<size_type> LinkVec;
  typedef typename LinkVec::iterator LinkIter;
public:

  fibonacci_heap(size_type n,
                 const Compare& cmp,
                 const ID& id = identity_property_map())
    : _key(n), _left(n), _right(n), _p(n), _mark(n), _degree(n),
      _n(0), _root(n), _id(id), _compare(cmp), _child(n),
#if defined(BOOST_MSVC) || defined(__ICL) // need a new macro?
      new_roots(size_type(log(float(n))) + 5) { }
#else
      new_roots(size_type(std::log(float(n))) + 5) { }
#endif

  // 33
  void push(const T& d) {
    ++_n;
    size_type v = get(_id, d);
    _key[v] = d;
    _p[v] = nil();
    _degree[v] = 0;
    _mark[v] = false;
    _child[v] = nil();
    if (_root == nil()) {
      _root = _left[v] = _right[v] = v;
      //std::cout << "root added" << std::endl;
    } else {
      size_type u = _left[_root];
      _left[v] = u;
      _right[v] = _root;
      _left[_root] = _right[u] = v;
      if (_compare(d, _key[_root]))
        _root = v;
      //std::cout << "non-root node added" << std::endl;
    }
  }
  T& top() { return _key[_root]; }
  const T& top() const { return _key[_root]; }

  // 38
  void pop() {
    --_n;
    int h = -1;
    size_type v, w;
    if (_root != nil()) {
      if (_degree[_root] == 0) {
        v = _right[_root];
      } else {
        w = _child[_root];
        v = _right[w];
        _right[w] = _right[_root];
        for (w = v; w != _right[_root]; w = _right[w])
          _p[w] = nil();
      }
      while (v != _root) {
        w = _right[v];
        add_tree_to_new_roots(v, new_roots.begin(), h);
        v = w;
      }
      rebuild_root_list(new_roots.begin(), h);
    }
  }
  // 39
  inline void add_tree_to_new_roots(size_type v,
                                    LinkIter new_roots,
                                    int& h)
  {
    int r;
    size_type u;
    r = _degree[v];
    while (1) {
      if (h < r) {
        do {
          ++h;
          new_roots[h] = (h == r ? v : nil());
        } while (h < r);
        break;
      }
      if (new_roots[r] == nil()) {
        new_roots[r] = v;
        break;
      }
      u = new_roots[r];
      new_roots[r] = nil();
      if (_compare(_key[u], _key[v])) {
        _degree[v] = r;
        _mark[v] = false;
        std::swap(u, v);
      }
      make_child(u, v, r);
      ++r;
    }
    _degree[v] = r;
    _mark[v] = false;
  }
  // 40
  void make_child(size_type u, size_type v, size_type r) {
    if (r == 0) {
      _child[v] = u;
      _left[u] = u;
      _right[u] = u;
    } else {
      size_type t = _child[v];
      _right[u] = _right[t];
      _left[u] = t;
      _right[t] = u;
      _left[_right[u]] = u;
    }
    _p[u] = v;
  }
  // 41
  inline void rebuild_root_list(LinkIter new_roots, int& h)
  {
    size_type u, v, w;
    if (h < 0)
      _root = nil();
    else {
      T d;
      u = v = new_roots[h];
      d = _key[u];
      _root = u;
      for (h--; h >= 0; --h)
        if (new_roots[h] != nil()) {
          w = new_roots[h];
          _left[w] = v;
          _right[v] = w;
          if (_compare(_key[w], d)) {
            _root = w;
            d = _key[w];
          }
          v = w;
        }
      _right[v] = u;
      _left[u] = v;
    }
  }

  // 34
  void update(const T& d) {
    size_type v = get(_id, d);
    assert(!_compare(_key[v], d));
    _key[v] = d;
    size_type p = _p[v];
    if (p == nil()) {
      if (_compare(d, _key[_root]))
        _root = v;
    } else if (_compare(d, _key[p]))
      while (1) {
        size_type r = _degree[p];
        if (r >= 2)
          remove_from_family(v, p);
        insert_into_forest(v, d);
        size_type pp = _p[p];
        if (pp == nil()) {
          --_degree[p];
          break;
        }
        if (_mark[p] == false) {
          _mark[p] = true;
          --_degree[p];
          break;
        } else
          --_degree[p];
        v = p;
        p = pp;
      }
  }

  inline size_type size() const { return _n; }
  inline bool empty() const { return _n == 0; }

  void print(std::ostream& os) {
    if (_root != nil()) {
      size_type i = _root;
      do {
        print_recur(i, os);
        os << std::endl;
        i = _right[i];
      } while (i != _root);
    }
  }

protected:
  // 35
  inline void remove_from_family(size_type v, size_type p) {
    size_type u = _left[v];
    size_type w = _right[v];
    _right[u] = w;
    _left[w] = u;
    if (_child[p] == v)
      _child[p] = w;
  }
  // 36
  inline void insert_into_forest(size_type v, const T& d) {
    _p[v] = nil();
    size_type u = _left[_root];
    _left[v] = u;
    _right[v] = _root;
    _left[_root] = _right[u] = v;
    if (_compare(d, _key[_root]))
      _root = v;
  }

  void print_recur(size_type x, std::ostream& os) {
    if (x != nil()) {
      os << x;
      if (_degree[x] > 0) {
        os << "(";
        size_type i = _child[x];
        do {
          print_recur(i, os); os << " ";
          i = _right[i];
        } while (i != _child[x]);
        os << ")";
      }
    }
  }

  size_type nil() const { return _left.size(); }

  std::vector<T> _key;
  LinkVec _left, _right, _p;
  std::vector<bool> _mark;
  LinkVec _degree;
  size_type _n, _root;
  ID _id;
  Compare _compare;
  LinkVec _child;
  LinkVec new_roots;
};

} // namespace boost

#endif // BOOST_FIBONACCI_HEAP_HPP


// Copyright Louis Dionne 2013-2017
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt)

#include <boost/hana/assert.hpp>
#include <boost/hana/config.hpp>
#include <boost/hana/equal.hpp>
#include <boost/hana/functional/infix.hpp>
#include <boost/hana/pair.hpp>
namespace hana = boost::hana;

BOOST_HANA_CONSTEXPR_LAMBDA auto divmod = hana::infix([](auto x, auto y) {
    // this could be a more efficient implementation
    return hana::make_pair(x / y, x % y);
});

int main() {
    BOOST_HANA_CONSTEXPR_CHECK((42 ^divmod^ 23) == hana::make_pair(1, 19));
}


#include "num_to_str.hpp"
#include <stdio.h>
#include <assert.h>
//#include <boost/type_traits.hpp>

namespace terark {

int num_to_str(char* buf, bool x) {
	buf[0] = "01"[x?1:0];
	buf[1] = 0;
	return 1;
}

int num_to_str(char* buf, float x) {
	return sprintf(buf, "%f", x);
}
int num_to_str(char* buf, double x) {
	return sprintf(buf, "%f", x);
}
int num_to_str(char* buf, long double x) {
	return sprintf(buf, "%Lf", x);
}

template<class Int>
inline
int signed_int_to_str(char* buf, Int x) {
	const static char digits[] = "9876543210123456789";
	const char* base = digits + 9;
	const Int radix = 10;
	int i = 0;
	bool sign = x < 0;
	do {
		Int mod = x % radix;
		x /= radix;
		buf[i++] = base[mod];
	} while (x);
	if (sign)
		buf[i++] = '-';
	assert(i >= 1);
	assert(i < 16);
	buf[i] = 0;
	int n = i;
	for (int j = 0; j < i; ++j) {
		--i;
		char tmp = buf[j];
		buf[j] = buf[i];
		buf[i] = tmp;
	}
	return n;
}
template<class Int>
inline
int unsigned_int_to_str(char* buf, Int x) {
	const static char digits[] = "0123456789";
	const Int radix = 10;
	int i = 0;
	do {
		Int mod = x % radix;
		x /= radix;
		buf[i++] = digits[mod];
	} while (x);
	assert(i >= 1);
	assert(i < 16);
	buf[i] = 0;
	int n = i;
	for (int j = 0; j < i; ++j) {
		--i;
		char tmp = buf[j];
		buf[j] = buf[i];
		buf[i] = tmp;
	}
	return n;
}

#define GEN_num_to_str(Sign, Int) \
	int num_to_str(char* buf, Sign Int x) { return Sign##_int_to_str(buf, x); }

//GEN_num_to_str(char)
//GEN_num_to_str(signed char)
//GEN_num_to_str(unsigned char)
GEN_num_to_str(  signed, short)
GEN_num_to_str(  signed, int)
GEN_num_to_str(  signed, long)
GEN_num_to_str(  signed, long long)
GEN_num_to_str(unsigned, short)
GEN_num_to_str(unsigned, int)
GEN_num_to_str(unsigned, long)
GEN_num_to_str(unsigned, long long)

} // namespace terark


#include <boost/numeric/odeint/stepper/generation/generation_controlled_runge_kutta.hpp>


// Copyright Vladimir Prus 2002-2004.
// 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)

/* The simplest usage of the library.
 */

#include <boost/program_options.hpp>
#include <boost/random.hpp>

namespace po = boost::program_options;

#include <iostream>
#include <iterator>
#include <vector>

using namespace std;

#include <wali/nwa/NWA.hpp>

using namespace wali;
using namespace wali::nwa;

boost::mt19937 rng;

class incompatible_options_exception : public std::exception {
public:
    virtual const char* what() const throw() {
        return "Incompatibile options";
    }
};

int get_int(int low, int hi)
{
    boost::random::uniform_int_distribution<> num(low, hi);
    return num(rng);
}

int get_bernoulli(int n, double p)
{
    boost::binomial_distribution<> bin(n, p);

    boost::variate_generator<boost::mt19937&, boost::binomial_distribution<> > die(rng, bin);
    return die();
}

int get_num_initial_states(po::variables_map const & options)
{
    if (options.count("number-of-initial-states") > 0) {
        if (options.count("initial-states-density") > 0) {
            throw incompatible_options_exception();
        }
        return options["number-of-initial-states"].as<int>();
    }
    else if (options.count("initial-states-density") > 0) {
        double density = options["initial-states-density"].as<int>() / 100.0;
        int states = options["number-of-states"].as<int>();
        return get_bernoulli(states, density);
    }
    else {
        return 1;
    }
}

int get_num_accepting_states(po::variables_map const & options)
{
    if (options.count("number-of-accepting-states") > 0) {
        if (options.count("accepting-states-density") > 0) {
            throw incompatible_options_exception();
        }
        return options["number-of-accepting-states"].as<int>();
    }
    else if (options.count("accepting-states-density") > 0) {
        double density = options["accepting-states-density"].as<int>() / 100.0;
        int states = options["number-of-states"].as<int>();
        return get_bernoulli(states, density);
    }
    else {
        return 1;
    }
}

int get_num_transitions_helper(po::variables_map const & options,
                               const char * absolute_density_name,
                               const char * deterministic_density_name)
{
    int states = options["number-of-states"].as<int>();
    int symbols = options["number-of-symbols"].as<int>();

    if (options.count(absolute_density_name) > 0) {
        if (options.count(deterministic_density_name) > 0) {
            throw incompatible_options_exception();
        }
        double density = options[absolute_density_name].as<int>() / 100.0;
        return get_bernoulli(states * states * symbols, density);
    }
    else if (options.count(deterministic_density_name) > 0) {
        double density = options[deterministic_density_name].as<int>() / 100.0;
        return get_bernoulli(states * symbols, density);
    }
    else {
        return get_bernoulli(states * states * symbols, 0.1);
    }
}

int get_num_call_transitions(po::variables_map const & options)
{
    return get_num_transitions_helper(options,
                                      "absolute-call-transition-density",
                                      "deterministic-call-transition-density");
}

int get_num_internal_transitions(po::variables_map const & options)
{
    return get_num_transitions_helper(options,
                                      "absolute-internal-transition-density",
                                      "deterministic-internal-transition-density");
}

int get_num_jump_transitions(po::variables_map const & options)
{
    return get_num_transitions_helper(options,
                                      "absolute-jump-transition-density",
                                      "deterministic-jump-transition-density");
}

int get_num_return_transitions(po::variables_map const & options)
{
    int states = options["number-of-states"].as<int>();
    int symbols = options["number-of-symbols"].as<int>();

    const char * absolute = "absolute-return-transition-density";
    const char * deterministic = "deterministic-return-transition-density";
    const char * single_src = "single-source-single-predecessor-return-transition-density";

    if (options.count(absolute) + options.count(deterministic)
        + options.count(single_src) > 1)
    {
        throw incompatible_options_exception();
    }

    if (options.count(absolute) > 0) {
        double density = options[absolute].as<int>() / 100.0;
        return get_bernoulli(states * states * states * symbols, density);
    }
    else if (options.count(deterministic) > 0) {
        double density = options[deterministic].as<int>() / 100.0;
        return get_bernoulli(states * states * symbols, density);
    }
    else if (options.count(single_src) > 0) {
        double density = options[single_src].as<int>() / 100.0;
        return get_bernoulli(states * symbols, density);
    }
    else {
        return get_bernoulli(states * states * symbols, 0.1);
    }
}

class NwaGenerator
{
    int num_states;
    int num_symbols;
    int num_initial_states;
    int num_accepting_states;

    int num_call;
    int num_internal; // no epsilon
    int num_epsilon;
    int num_return;

    std::vector<State> states;
    std::vector<Symbol> symbols;

public:
    NwaGenerator(po::variables_map const & options)
        : num_states(options["number-of-states"].as<int>())
        , num_symbols(options["number-of-symbols"].as<int>())
        , num_initial_states(get_num_initial_states(options))
        , num_accepting_states(get_num_accepting_states(options))
        , num_call(get_num_call_transitions(options))
        , num_internal(get_num_internal_transitions(options))
        , num_epsilon(get_num_jump_transitions(options))
        , num_return(get_num_return_transitions(options))
    {}

    wali::nwa::NWARefPtr generate() {
        NWARefPtr nwa = new NWA();

        // Add states
        states.resize(num_states);
        for (int i=0; i<num_states; ++i) {
            states.at(i) = getKey(i);
            nwa->addState(states.at(i));
        }

        // Add symbols
        symbols.resize(num_symbols);
        for (int i=0; i<num_symbols; ++i) {
            symbols[i] = getKey(i);
            nwa->addSymbol(symbols.at(i));
        }

        // Add initial states; WLOG we use the first n
        for (int i=0; i<num_initial_states; ++i) {
            nwa->addInitialState(states.at(i));
        }

        // Add final states; we can't do the same trick
        // since initials and finals can overlap
        for (int i=0; i<num_accepting_states; ++i) {
            bool added = nwa->addFinalState(randomState());
            if (!added) --i;
        }

        // Add call transitions
        for (int i=0; i<num_call; ++i) {
            bool added = nwa->addCallTrans(randomState(), randomSymbol(), randomState());
            if (!added) --i;
        }

        // Add internal transitions
        for (int i=0; i<num_internal; ++i) {
            bool added = nwa->addInternalTrans(randomState(), randomSymbol(), randomState());
            if (!added) --i;
        }

        // Add jump transitions
        for (int i=0; i<num_epsilon; ++i) {
            bool added = nwa->addInternalTrans(randomState(), WALI_EPSILON, randomState());
            if (!added) --i;
        }

        // Add call transitions
        for (int i=0; i<num_return; ++i) {
            bool added = nwa->addReturnTrans(randomState(), randomState(), randomSymbol(), randomState());
            if (!added) --i;
        }

        return nwa;
    }

private:
    State randomState() {
        return states.at(get_int(0, num_states-1));
    }

    Symbol randomSymbol() {
        return symbols.at(get_int(0, num_symbols-1));
    }
};

int main(int ac, char* av[])
{
    try {

        po::options_description desc("Allowed options");
        desc.add_options()
            ("help", "produce help message")

            ("rng-seed", po::value<unsigned int>()->default_value(static_cast<unsigned int>(std::time(0))), "")

            ("absolute-internal-transition-density", po::value<int>(), "")
            ("deterministic-internal-transition-density", po::value<int>(), "")
            ("absolute-jump-density", po::value<int>(), "")
            ("deterministic-jump-density", po::value<int>(), "")

            ("absolute-call-transition-density", po::value<int>(), "")
            ("deterministic-call-transition-density", po::value<int>(), "")

            ("absolute-return-transition-density", po::value<int>(), "")
            ("deterministic-return-transition-density", po::value<int>(), "")
            ("single-source-single-predecessor-return-transition-density", po::value<int>(), "")

            ("number-of-states", po::value<int>()->default_value(50), "")
            ("number-of-symbols", po::value<int>()->default_value(5), "")

            ("number-of-initial-states", po::value<int>(), "")
            ("number-of-accepting-states", po::value<int>(), "")
            ("initial-states-density", po::value<int>(), "")
            ("accepting-states-density", po::value<int>(), "")
        ;

        po::variables_map vm;
        po::store(po::parse_command_line(ac, av, desc), vm);
        po::notify(vm);

        if (vm.count("help")) {
            cout << desc << "\n";
            return 1;
        }

        rng.seed(vm["rng-seed"].as<unsigned int>());

        NwaGenerator gen(vm);

        gen.generate()->print(std::cout);
    }
    catch(exception& e) {
        cerr << "error: " << e.what() << "\n";
        return 1;
    }
    catch(...) {
        cerr << "Exception of unknown type!\n";
    }

    return 0;
}


////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

/// @project        Open Space Toolkit ▸ Mathematics
/// @file           OpenSpaceToolkit/Mathematics/Geometry/2D/Objects/MultiPolygon.cpp
/// @author         Lucas Brémond <lucas@loftorbital.com>
/// @license        Apache License 2.0

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

#include <OpenSpaceToolkit/Mathematics/Geometry/2D/Transformation.hpp>
#include <OpenSpaceToolkit/Mathematics/Geometry/2D/Objects/MultiPolygon.hpp>

#include <OpenSpaceToolkit/Core/Types/String.hpp>
#include <OpenSpaceToolkit/Core/Types/Size.hpp>
#include <OpenSpaceToolkit/Core/Types/Index.hpp>
#include <OpenSpaceToolkit/Core/Error.hpp>
#include <OpenSpaceToolkit/Core/Utilities.hpp>

#include <boost/geometry/io/wkt/wkt.hpp>
#include <boost/geometry/strategies/transform/matrix_transformers.hpp>
#include <boost/geometry/strategies/transform.hpp>
#include <boost/geometry/geometries/polygon.hpp>
#include <boost/geometry/geometries/point_xy.hpp>
#include <boost/geometry.hpp>
#include <boost/numeric/ublas/matrix.hpp>

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

namespace ostk
{
namespace math
{
namespace geom
{
namespace d2
{
namespace objects
{

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

using boost::geometry::cs::cartesian ;
using boost::geometry::model::point ;
using boost::geometry::model::ring ;
using boost::geometry::model::polygon ;
using boost::geometry::model::multi_polygon ;

using ostk::core::types::Index ;
using ostk::core::types::Size ;
using ostk::core::types::String ;

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

class MultiPolygon::Impl
{

    public:

                                Impl                                        ( ) ;

                                Impl                                        (   const   Array<Polygon2d>&           aPolygonArray                               ) ;

                                ~Impl                                       ( ) = default ;

        bool                    operator ==                                 (   const   MultiPolygon::Impl&         aMultiPolygon                               ) const ;

        bool                    isDefined                                   ( ) const ;

        bool                    contains                                    (   const   Point&                      aPoint                                      ) const ;

        bool                    contains                                    (   const   PointSet&                   aPointSet                                   ) const ;

        Size                    getPolygonCount                             ( ) const ;

        Array<Polygon2d>        getPolygons                                 ( ) const ;

        Polygon2d               getConvexHull                               ( ) const ;

        MultiPolygon::Impl      unionWith                                   (   const   MultiPolygon::Impl&         aMultiPolygon                               ) const ;

        String                  toString                                    (   const   Object::Format&             aFormat,
                                                                                const   Integer&                    aPrecision                                  ) const ;

        void                    applyTransformation                         (   const   Transformation&             aTransformation                             ) ;

    private:

        typedef                 point<double, 2, cartesian>                     BoostPoint ;
        typedef                 ring<Impl::BoostPoint>                          BoostRing ;
        typedef                 polygon<Impl::BoostPoint>                       BoostPolygon ;
        typedef                 multi_polygon<Impl::BoostPolygon>               BoostMultiPolygon ;

        Impl::BoostMultiPolygon multiPolygon_ ;

        static Impl::BoostMultiPolygon BoostMultiPolygonFromPolygons        (   const   Array<Polygon2d>&           aPolygonArray                               ) ;

        static Polygon2d        PolygonFromBoostPolygon                     (   const   MultiPolygon::Impl::BoostPolygon& aPolygon                              ) ;

} ;

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

                                MultiPolygon::Impl::Impl                    ( )
                                :   multiPolygon_()
{

}

                                MultiPolygon::Impl::Impl                    (   const   Array<Polygon2d>&           aPolygonArray                               )
                                :   multiPolygon_(MultiPolygon::Impl::BoostMultiPolygonFromPolygons(aPolygonArray))
{

}

bool                            MultiPolygon::Impl::operator ==             (   const   MultiPolygon::Impl&         aMultiPolygon                               ) const
{
    return boost::geometry::equals(multiPolygon_, aMultiPolygon.multiPolygon_) ;
}

bool                            MultiPolygon::Impl::isDefined               ( ) const
{
    return !multiPolygon_.empty() ;
}

bool                            MultiPolygon::Impl::contains                (   const   Point&                      aPoint                                      ) const
{

    try
    {
        return boost::geometry::covered_by(MultiPolygon::Impl::BoostPoint(aPoint.x(), aPoint.y()), multiPolygon_) ;
    }
    catch (const std::exception& anException)
    {
        throw ostk::core::error::RuntimeError("Error when checking if multi-polygon contains point: [{}]", anException.what()) ;
    }

    return false ;

}

bool                            MultiPolygon::Impl::contains                (   const   PointSet&                   aPointSet                                   ) const
{

    (void) aPointSet ;

    throw ostk::core::error::runtime::ToBeImplemented("MultiPolygon::contains (PointSet&)") ;

    return false ;

}

Size                            MultiPolygon::Impl::getPolygonCount         ( ) const
{
    return multiPolygon_.size() ;
}

Array<Polygon2d>                MultiPolygon::Impl::getPolygons             ( ) const
{

    Array<Polygon2d> polygons = Array<Polygon2d>::Empty() ;

    for (const auto& polygon : multiPolygon_)
    {

        Array<Point> outerRing = Array<Point>::Empty() ;

        for (size_t vertexIdx = 0; vertexIdx < (polygon.outer().size() - 1); ++vertexIdx)
        {
            outerRing.add(Point(boost::geometry::get<0>(polygon.outer().at(vertexIdx)), boost::geometry::get<1>(polygon.outer().at(vertexIdx)))) ;
        }

        Array<Array<Point>> innerRings = Array<Array<Point>>::Empty() ;

        for (const auto& innerRing : polygon.inners())
        {

            Array<Point> innerRingVertices = Array<Point>::Empty() ;

            for (size_t vertexIdx = 0; vertexIdx < (innerRing.size() - 1); ++vertexIdx)
            {
                innerRingVertices.add(Point(boost::geometry::get<0>(innerRing.at(vertexIdx)), boost::geometry::get<1>(innerRing.at(vertexIdx)))) ;
            }

            innerRings.add(innerRingVertices) ;

        }

        polygons.add(Polygon2d { outerRing, innerRings }) ;

    }

    return polygons ;

}

Polygon2d                       MultiPolygon::Impl::getConvexHull           ( ) const
{

    MultiPolygon::Impl::BoostPolygon convexHull ;

    try
    {
        boost::geometry::convex_hull(multiPolygon_, convexHull) ;
    }
    catch (const std::exception& anException)
    {
        throw ostk::core::error::RuntimeError("Error caught while computing the convex hull: [{}]", anException.what()) ;
    }

    return MultiPolygon::Impl::PolygonFromBoostPolygon(convexHull) ;

}

MultiPolygon::Impl              MultiPolygon::Impl::unionWith               (   const   MultiPolygon::Impl&         aMultiPolygon                               ) const
{

    try
    {

        MultiPolygon::Impl multiPolygonUnion ;

        boost::geometry::union_(multiPolygon_, aMultiPolygon.multiPolygon_, multiPolygonUnion.multiPolygon_) ;

        return multiPolygonUnion ;

    }
    catch (const std::exception& anException)
    {
        throw ostk::core::error::RuntimeError("Cannot compute union of multi-polyons: [{}].", anException.what()) ;
    }

}

String                          MultiPolygon::Impl::toString                (   const   Object::Format&             aFormat,
                                                                                const   Integer&                    aPrecision                                  ) const
{

    switch (aFormat)
    {

        case Object::Format::Standard:
        case Object::Format::WKT:
        {

            std::stringstream stringStream ;

            if (aPrecision.isDefined())
            {
                stringStream << std::fixed << std::setprecision(aPrecision) << boost::geometry::wkt(multiPolygon_) ;
            }
            else
            {
                stringStream << boost::geometry::wkt(multiPolygon_) ;
            }

            return stringStream.str() ;

        }

        default:
            throw ostk::core::error::runtime::Wrong("Format") ;
            break ;

    }

    return String::Empty() ;

}

void                            MultiPolygon::Impl::applyTransformation     (   const   Transformation&             aTransformation                             )
{

    using ostk::math::obj::Matrix3d ;

    MultiPolygon::Impl::BoostMultiPolygon transformedMultiPolygon ;

    const Matrix3d transformationMatrix = aTransformation.getMatrix() ;

    const boost::geometry::strategy::transform::matrix_transformer<double, 2, 2> transform = { transformationMatrix(0, 0), transformationMatrix(0, 1), transformationMatrix(0, 2),
                                                                                transformationMatrix(1, 0), transformationMatrix(1, 1), transformationMatrix(1, 2),
                                                                                transformationMatrix(2, 0), transformationMatrix(2, 1), transformationMatrix(2, 2) } ;

    boost::geometry::transform(multiPolygon_, transformedMultiPolygon, transform) ;

    multiPolygon_ = transformedMultiPolygon ;

}

MultiPolygon::Impl::BoostMultiPolygon MultiPolygon::Impl::BoostMultiPolygonFromPolygons ( const Array<Polygon2d>&   aPolygonArray                               )
{

    MultiPolygon::Impl::BoostMultiPolygon boostMultiPolygon ;

    for (const auto& polygon : aPolygonArray)
    {

        MultiPolygon::Impl::BoostPolygon boostPolygon ;

        for (const auto& point : polygon.getOuterRing())
        {
            boost::geometry::append(boostPolygon, MultiPolygon::Impl::BoostPoint(point.x(), point.y())) ;
        }

        for (Index innerRingIndex = 0; innerRingIndex < polygon.getInnerRingCount(); ++innerRingIndex)
        {

            const auto innerRing = polygon.getInnerRingAt(innerRingIndex) ;

            MultiPolygon::Impl::BoostRing boostRing ;

            for (const auto& innerRingPoint : innerRing)
            {
                boost::geometry::append(boostRing, MultiPolygon::Impl::BoostPoint(innerRingPoint.x(), innerRingPoint.y())) ;
            }

            boost::geometry::correct(boostRing) ;

            boostPolygon.inners().push_back(boostRing) ;

        }

        boostMultiPolygon.push_back(boostPolygon) ;

    }

    boost::geometry::correct(boostMultiPolygon) ;

    return boostMultiPolygon ;

}

Polygon2d                       MultiPolygon::Impl::PolygonFromBoostPolygon (   const   MultiPolygon::Impl::BoostPolygon& aPolygon                              )
{

    Array<Point> outerRing = Array<Point>::Empty() ;

    for (size_t vertexIdx = 0; vertexIdx < (aPolygon.outer().size() - 1); ++vertexIdx)
    {
        outerRing.add(Point(boost::geometry::get<0>(aPolygon.outer().at(vertexIdx)), boost::geometry::get<1>(aPolygon.outer().at(vertexIdx)))) ;
    }

    Array<Array<Point>> innerRings = Array<Array<Point>>::Empty() ;

    for (const auto& innerRing : aPolygon.inners())
    {

        Array<Point> innerRingVertices = Array<Point>::Empty() ;

        for (size_t vertexIdx = 0; vertexIdx < (innerRing.size() - 1); ++vertexIdx)
        {
            innerRingVertices.add(Point(boost::geometry::get<0>(innerRing.at(vertexIdx)), boost::geometry::get<1>(innerRing.at(vertexIdx)))) ;
        }

        innerRings.add(innerRingVertices) ;

    }

    return Polygon2d { outerRing, innerRings } ;

}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

                                MultiPolygon::MultiPolygon                  (   const   Array<Polygon2d>&           aPolygonArray                               )
                                :   Object(),
                                    implUPtr_(std::make_unique<MultiPolygon::Impl>(aPolygonArray))
{

}

                                MultiPolygon::MultiPolygon                  (   const   MultiPolygon&               aMultiPolygon                               )
                                :   Object(aMultiPolygon),
                                    implUPtr_((aMultiPolygon.implUPtr_ != nullptr) ? new MultiPolygon::Impl(*aMultiPolygon.implUPtr_) : nullptr)
{

}

                                MultiPolygon::~MultiPolygon                 ( )
{

}

MultiPolygon&                   MultiPolygon::operator =                    (   const   MultiPolygon&               aMultiPolygon                               )
{

    if (this != &aMultiPolygon)
    {

        Object::operator = (aMultiPolygon) ;

        implUPtr_.reset((aMultiPolygon.implUPtr_ != nullptr) ? new MultiPolygon::Impl(*aMultiPolygon.implUPtr_) : nullptr) ;

    }

    return *this ;

}

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

bool                            MultiPolygon::operator ==                   (   const   MultiPolygon&               aMultiPolygon                               ) const
{

    if ((!this->isDefined()) || (!aMultiPolygon.isDefined()))
    {
        return false ;
    }

    return (*implUPtr_) == (*aMultiPolygon.implUPtr_) ;

}

bool                            MultiPolygon::operator !=                   (   const   MultiPolygon&               aMultiPolygon                               ) const
{
    return !((*this) == aMultiPolygon) ;
}

bool                            MultiPolygon::isDefined                     ( ) const
{
    return (implUPtr_ != nullptr) && implUPtr_->isDefined() ;
}

bool                            MultiPolygon::contains                      (   const   Point&                      aPoint                                      ) const
{

    if (!aPoint.isDefined())
    {
        throw ostk::core::error::runtime::Undefined("Point") ;
    }

    if (!this->isDefined())
    {
        throw ostk::core::error::runtime::Undefined("Multi-polygon") ;
    }

    return implUPtr_->contains(aPoint) ;

}

bool                            MultiPolygon::contains                      (   const   PointSet&                   aPointSet                                   ) const
{

    if (!aPointSet.isDefined())
    {
        throw ostk::core::error::runtime::Undefined("Point set") ;
    }

    if (!this->isDefined())
    {
        throw ostk::core::error::runtime::Undefined("Multi-polygon") ;
    }

    return implUPtr_->contains(aPointSet) ;

}

Size                            MultiPolygon::getPolygonCount               ( ) const
{

    if (!this->isDefined())
    {
        throw ostk::core::error::runtime::Undefined("Multi-polygon") ;
    }

    return implUPtr_->getPolygonCount() ;

}

Array<Polygon2d>                MultiPolygon::getPolygons                   ( ) const
{

    if (!this->isDefined())
    {
        throw ostk::core::error::runtime::Undefined("Multi-polygon") ;
    }

    return implUPtr_->getPolygons() ;

}

Polygon2d                       MultiPolygon::getConvexHull                 ( ) const
{

    if (!this->isDefined())
    {
        throw ostk::core::error::runtime::Undefined("Multi-polygon") ;
    }

    return implUPtr_->getConvexHull() ;

}

MultiPolygon                    MultiPolygon::unionWith                     (   const   MultiPolygon&               aMultiPolygon                               ) const
{

    if (!aMultiPolygon.isDefined())
    {
        throw ostk::core::error::runtime::Undefined("Multi-polygon") ;
    }

    if (!this->isDefined())
    {
        throw ostk::core::error::runtime::Undefined("Multi-polygon") ;
    }

    // [TBM] Could be improved to avoid unnecessary copies

    MultiPolygon unionMultiPolygon = MultiPolygon::Undefined() ;

    unionMultiPolygon.implUPtr_ = std::make_unique<MultiPolygon::Impl>(implUPtr_->unionWith(*aMultiPolygon.implUPtr_)) ;

    return unionMultiPolygon ;

}

String                          MultiPolygon::toString                      (   const   Object::Format&             aFormat,
                                                                                const   Integer&                    aPrecision                                  ) const
{

    if (!this->isDefined())
    {
        throw ostk::core::error::runtime::Undefined("Multi-polygon") ;
    }

    return implUPtr_->toString(aFormat, aPrecision) ;

}

void                            MultiPolygon::print                         (           std::ostream&               anOutputStream,
                                                                                bool                        displayDecorators                           ) const
{

    displayDecorators ? ostk::core::utils::Print::Header(anOutputStream, "Multi-polygon") : void () ;

    if (implUPtr_ != nullptr)
    {

        for (const auto& polygon : this->getPolygons())
        {
            polygon.print(anOutputStream, false) ;
        }

    }
    else
    {
        ostk::core::utils::Print::Line(anOutputStream) << "Undefined" ;
    }

    displayDecorators ? ostk::core::utils::Print::Footer(anOutputStream) : void () ;

}

// MultiPolygon::ConstIterator     MultiPolygon::begin                         ( ) const
// {

// }

// MultiPolygon::ConstIterator     MultiPolygon::end                           ( ) const
// {

// }

void                            MultiPolygon::applyTransformation           (   const   Transformation&             aTransformation                             )
{

    if (!aTransformation.isDefined())
    {
        throw ostk::core::error::runtime::Undefined("Transformation") ;
    }

    if (!this->isDefined())
    {
        throw ostk::core::error::runtime::Undefined("Multi-polygon") ;
    }

    implUPtr_->applyTransformation(aTransformation) ;

}

MultiPolygon                    MultiPolygon::Undefined                     ( )
{
    return { Array<Polygon2d>::Empty() } ;
}

MultiPolygon                    MultiPolygon::Polygon                       (   const   Polygon2d&                  aPolygon                                    )
{
    return { Array<Polygon2d> { aPolygon } } ;
}

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

}
}
}
}
}

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////


//
// Copyright © 2017 Arm Ltd. All rights reserved.
// SPDX-License-Identifier: MIT
//
#include <boost/test/unit_test.hpp>
#include "ParserFlatbuffersFixture.hpp"
#include "../TfLiteParser.hpp"

using armnnTfLiteParser::TfLiteParser;
using ModelPtr = TfLiteParser::ModelPtr;

BOOST_AUTO_TEST_SUITE(TensorflowLiteParser)

struct GetInputsOutputsMainFixture : public ParserFlatbuffersFixture
{
    explicit GetInputsOutputsMainFixture(const std::string& inputs, const std::string& outputs)
    {
        m_JsonString = R"(
        {
            "version": 3,
            "operator_codes": [ { "builtin_code": "AVERAGE_POOL_2D" }, { "builtin_code": "CONV_2D" } ],
            "subgraphs": [
            {
                "tensors": [
                {
                    "shape": [ 1, 1, 1, 1 ] ,
                    "type": "UINT8",
                            "buffer": 0,
                            "name": "OutputTensor",
                            "quantization": {
                                "min": [ 0.0 ],
                                "max": [ 255.0 ],
                                "scale": [ 1.0 ],
                                "zero_point": [ 0 ]
                            }
                },
                {
                    "shape": [ 1, 2, 2, 1 ] ,
                    "type": "UINT8",
                            "buffer": 1,
                            "name": "InputTensor",
                            "quantization": {
                                "min": [ -1.2 ],
                                "max": [ 25.5 ],
                                "scale": [ 0.25 ],
                                "zero_point": [ 10 ]
                            }
                }
                ],
                "inputs": [ 1 ],
                "outputs": [ 0 ],
                "operators": [ {
                        "opcode_index": 0,
                        "inputs":  )"
                            + inputs
                            + R"(,
                        "outputs": )"
                            + outputs
                            + R"(,
                        "builtin_options_type": "Pool2DOptions",
                        "builtin_options":
                        {
                            "padding": "VALID",
                            "stride_w": 2,
                            "stride_h": 2,
                            "filter_width": 2,
                            "filter_height": 2,
                            "fused_activation_function": "NONE"
                        },
                        "custom_options_format": "FLEXBUFFERS"
                    } ]
                },
                {
                    "tensors": [
                        {
                            "shape": [ 1, 3, 3, 1 ],
                            "type": "UINT8",
                            "buffer": 0,
                            "name": "ConvInputTensor",
                            "quantization": {
                                "scale": [ 1.0 ],
                                "zero_point": [ 0 ],
                            }
                        },
                        {
                            "shape": [ 1, 1, 1, 1 ],
                            "type": "UINT8",
                            "buffer": 1,
                            "name": "ConvOutputTensor",
                            "quantization": {
                                "min": [ 0.0 ],
                                "max": [ 511.0 ],
                                "scale": [ 2.0 ],
                                "zero_point": [ 0 ],
                            }
                        },
                        {
                            "shape": [ 1, 3, 3, 1 ],
                            "type": "UINT8",
                            "buffer": 2,
                            "name": "filterTensor",
                            "quantization": {
                                "min": [ 0.0 ],
                                "max": [ 255.0 ],
                                "scale": [ 1.0 ],
                                "zero_point": [ 0 ],
                            }
                        }
                    ],
                    "inputs": [ 0 ],
                    "outputs": [ 1 ],
                    "operators": [
                        {
                            "opcode_index": 0,
                            "inputs": [ 0, 2 ],
                            "outputs": [ 1 ],
                            "builtin_options_type": "Conv2DOptions",
                            "builtin_options": {
                                "padding": "VALID",
                                "stride_w": 1,
                                "stride_h": 1,
                                "fused_activation_function": "NONE"
                            },
                            "custom_options_format": "FLEXBUFFERS"
                        }
                    ],
                }
            ],
            "description": "Test Subgraph Inputs Outputs",
            "buffers" : [
                    { },
                    { },
                    { "data": [ 2,1,0, 6,2,1, 4,1,2 ], },
                    { },
                ]
        })";

        ReadStringToBinary();
    }

};

struct GetEmptyInputsOutputsFixture : GetInputsOutputsMainFixture
{
    GetEmptyInputsOutputsFixture() : GetInputsOutputsMainFixture("[ ]", "[ ]") {}
};

struct GetInputsOutputsFixture : GetInputsOutputsMainFixture
{
    GetInputsOutputsFixture() : GetInputsOutputsMainFixture("[ 1 ]", "[ 0 ]") {}
};

BOOST_FIXTURE_TEST_CASE(GetEmptyInputs, GetEmptyInputsOutputsFixture)
{
    TfLiteParser::ModelPtr model = TfLiteParser::LoadModelFromBinary(m_GraphBinary.data(), m_GraphBinary.size());
    TfLiteParser::TensorRawPtrVector tensors = TfLiteParser::GetInputs(model, 0, 0);
    BOOST_CHECK_EQUAL(0, tensors.size());
}

BOOST_FIXTURE_TEST_CASE(GetEmptyOutputs, GetEmptyInputsOutputsFixture)
{
    TfLiteParser::ModelPtr model = TfLiteParser::LoadModelFromBinary(m_GraphBinary.data(), m_GraphBinary.size());
    TfLiteParser::TensorRawPtrVector tensors = TfLiteParser::GetOutputs(model, 0, 0);
    BOOST_CHECK_EQUAL(0, tensors.size());
}

BOOST_FIXTURE_TEST_CASE(GetInputs, GetInputsOutputsFixture)
{
    TfLiteParser::ModelPtr model = TfLiteParser::LoadModelFromBinary(m_GraphBinary.data(), m_GraphBinary.size());
    TfLiteParser::TensorRawPtrVector tensors = TfLiteParser::GetInputs(model, 0, 0);
    BOOST_CHECK_EQUAL(1, tensors.size());
    CheckTensors(tensors[0], 4, { 1, 2, 2, 1 }, tflite::TensorType::TensorType_UINT8, 1,
                      "InputTensor", { -1.2f }, { 25.5f }, { 0.25f }, { 10 });
}

BOOST_FIXTURE_TEST_CASE(GetOutputs, GetInputsOutputsFixture)
{
    TfLiteParser::ModelPtr model = TfLiteParser::LoadModelFromBinary(m_GraphBinary.data(), m_GraphBinary.size());
    TfLiteParser::TensorRawPtrVector tensors = TfLiteParser::GetOutputs(model, 0, 0);
    BOOST_CHECK_EQUAL(1, tensors.size());
    CheckTensors(tensors[0], 4, { 1, 1, 1, 1 }, tflite::TensorType::TensorType_UINT8, 0,
                      "OutputTensor", { 0.0f }, { 255.0f }, { 1.0f }, { 0 });
}

BOOST_FIXTURE_TEST_CASE(GetInputsMultipleInputs, GetInputsOutputsFixture)
{
    TfLiteParser::ModelPtr model = TfLiteParser::LoadModelFromBinary(m_GraphBinary.data(), m_GraphBinary.size());
    TfLiteParser::TensorRawPtrVector tensors = TfLiteParser::GetInputs(model, 1, 0);
    BOOST_CHECK_EQUAL(2, tensors.size());
    CheckTensors(tensors[0], 4, { 1, 3, 3, 1 }, tflite::TensorType::TensorType_UINT8, 0,
                      "ConvInputTensor", { }, { }, { 1.0f }, { 0 });
    CheckTensors(tensors[1], 4, { 1, 3, 3, 1 }, tflite::TensorType::TensorType_UINT8, 2,
                      "filterTensor", { 0.0f }, { 255.0f }, { 1.0f }, { 0 });
}

BOOST_FIXTURE_TEST_CASE(GetOutputs2, GetInputsOutputsFixture)
{
    TfLiteParser::ModelPtr model = TfLiteParser::LoadModelFromBinary(m_GraphBinary.data(), m_GraphBinary.size());
    TfLiteParser::TensorRawPtrVector tensors = TfLiteParser::GetOutputs(model, 1, 0);
    BOOST_CHECK_EQUAL(1, tensors.size());
    CheckTensors(tensors[0], 4, { 1, 1, 1, 1 }, tflite::TensorType::TensorType_UINT8, 1,
                      "ConvOutputTensor", { 0.0f }, { 511.0f }, { 2.0f }, { 0 });
}

BOOST_AUTO_TEST_CASE(GetInputsNullModel)
{
    BOOST_CHECK_THROW(TfLiteParser::GetInputs(nullptr, 0, 0), armnn::ParseException);
}

BOOST_AUTO_TEST_CASE(GetOutputsNullModel)
{
    BOOST_CHECK_THROW(TfLiteParser::GetOutputs(nullptr, 0, 0), armnn::ParseException);
}

BOOST_FIXTURE_TEST_CASE(GetInputsInvalidSubgraph, GetInputsOutputsFixture)
{
    TfLiteParser::ModelPtr model = TfLiteParser::LoadModelFromBinary(m_GraphBinary.data(), m_GraphBinary.size());
    BOOST_CHECK_THROW(TfLiteParser::GetInputs(model, 2, 0), armnn::ParseException);
}

BOOST_FIXTURE_TEST_CASE(GetOutputsInvalidSubgraph, GetInputsOutputsFixture)
{
    TfLiteParser::ModelPtr model = TfLiteParser::LoadModelFromBinary(m_GraphBinary.data(), m_GraphBinary.size());
    BOOST_CHECK_THROW(TfLiteParser::GetOutputs(model, 2, 0), armnn::ParseException);
}

BOOST_FIXTURE_TEST_CASE(GetInputsInvalidOperator, GetInputsOutputsFixture)
{
    TfLiteParser::ModelPtr model = TfLiteParser::LoadModelFromBinary(m_GraphBinary.data(), m_GraphBinary.size());
    BOOST_CHECK_THROW(TfLiteParser::GetInputs(model, 0, 1), armnn::ParseException);
}

BOOST_FIXTURE_TEST_CASE(GetOutputsInvalidOperator, GetInputsOutputsFixture)
{
    TfLiteParser::ModelPtr model = TfLiteParser::LoadModelFromBinary(m_GraphBinary.data(), m_GraphBinary.size());
    BOOST_CHECK_THROW(TfLiteParser::GetOutputs(model, 0, 1), armnn::ParseException);
}

BOOST_AUTO_TEST_SUITE_END()


/*
@copyright Zach Laine 2014
Distributed under the Boost Software License, Version 1.0.
(See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt)
 */

#include <boost/fusion/algorithm/iteration/fold.hpp>
#include <boost/fusion/container/vector/vector10.hpp>
#include <boost/fusion/container/vector/vector20.hpp>

#include "benchmark.hpp"
#include <cstdlib>
namespace bf = boost::fusion;

int main () {
    using vector_type = bf::vector<%=input_size%><
        <%= types.join(', ') %>
    >;

    boost::hana::benchmark::measure([] {
        long double result = 0;
        for (int iteration = 0; iteration < 1 << 10; ++iteration) {
            vector_type values(
                <%=
                    types.map { |t| "static_cast<#{t}>(std::rand())" }.join(', ')
                %>
            );

            result += bf::fold(values, 0, [](auto state, auto t) {
                return state + t;
            });
        }
    });
}


/************************************************************************
MIT License

Copyright (c) 2021 Deqi Tang

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

/// @file src/atomsciflow/utils/structure.cpp
/// @author DeqiTang
/// Mail: deqitang@gmail.com
/// Created Time: Wed 09 Mar 2022 08:06:59 PM CST

#include "atomsciflow/utils/structure.h"

#include <armadillo>

namespace atomsciflow {

/// crystal/fractional coordinate can be convert from cartesian coordinates
/// the conversion process is like transformation of presentation in quantum
/// mechanics the convmat is bulid to do the conversion
void atoms_cart_to_frac(std::vector<Atom>& atoms_frac, const std::vector<Atom>& atoms_cart, const std::vector<std::vector<double>>& cell) {

    atoms_frac.clear();

    arma::mat latcell(3, 3);
    latcell.row(0) = arma::conv_to<arma::rowvec>::from(cell[0]);
    latcell.row(1) = arma::conv_to<arma::rowvec>::from(cell[1]);
    latcell.row(2) = arma::conv_to<arma::rowvec>::from(cell[2]);
    arma::mat convmat = arma::inv(arma::trans(latcell));

    arma::vec cart_tmp(3);
    arma::vec frac_tmp(3);

    for (int i = 0; i < atoms_cart.size(); i++) {
        cart_tmp[0] = atoms_cart[i].x;
        cart_tmp[1] = atoms_cart[i].y;
        cart_tmp[2] = atoms_cart[i].z;
        //frac_tmp = arma::dot(convmat, arma::vec(cart_tmp));
        frac_tmp = convmat * arma::vec(cart_tmp);
        Atom atom;
        atom.x = frac_tmp[0];
        atom.y = frac_tmp[1];
        atom.z = frac_tmp[2];
        atoms_frac.emplace_back(atom);
    }
    return;
}

void atoms_frac_to_cart(std::vector<Atom>& atoms_cart, const std::vector<Atom>& atoms_frac, const std::vector<std::vector<double>>& cell) {

    atoms_cart.clear();

    arma::mat latcell(3, 3);
    latcell.row(0) = arma::conv_to<arma::rowvec>::from(cell[0]);
    latcell.row(1) = arma::conv_to<arma::rowvec>::from(cell[1]);
    latcell.row(2) = arma::conv_to<arma::rowvec>::from(cell[2]);
    arma::mat convmat = arma::trans(latcell);

    arma::vec cart_tmp(3);
    arma::vec frac_tmp(3);

    for (int i = 0; i < atoms_frac.size(); i++) {
        frac_tmp[0] = atoms_frac[i].x;
        frac_tmp[1] = atoms_frac[i].y;
        frac_tmp[2] = atoms_frac[i].z;
        //cart_tmp = arma::dot(convmat, frac_tmp);
        cart_tmp = convmat * frac_tmp;
        Atom atom;
        atom.x = cart_tmp[0];
        atom.y = cart_tmp[1];
        atom.z = cart_tmp[2];
        atoms_cart.emplace_back(atom);
    }
    return;
}

} // namespace atomsciflow


/*    This file is part of the Gudhi Library - https://gudhi.inria.fr/ - which is released under MIT.
 *    See file LICENSE or go to https://gudhi.inria.fr/licensing/ for full license details.
 *    Author(s):       Pawel Dlotko
 *
 *    Copyright (C) 2016 Inria
 *
 *    Modification(s):
 *      - YYYY/MM Author: Description of the modification
 */

#define BOOST_TEST_DYN_LINK
#define BOOST_TEST_MODULE "Persistence_heat_maps_test"
#include <boost/test/unit_test.hpp>
#include <gudhi/reader_utils.h>
#include <gudhi/Persistence_heat_maps.h>
#include <gudhi/Unitary_tests_utils.h>

#include <iostream>

using namespace Gudhi;
using namespace Gudhi::Persistence_representations;

double epsilon = 0.0005;

BOOST_AUTO_TEST_CASE(check_construction_of_heat_maps) {
  std::vector<std::vector<double> > filter = create_Gaussian_filter(100, 1);
  Persistence_heat_maps<constant_scaling_function> p("data/file_with_diagram", filter, false, 1000, 0, 1);
  p.print_to_file("data/persistence_heat_map_from_file_with_diagram");

  Persistence_heat_maps<constant_scaling_function> q;
  q.load_from_file("data/persistence_heat_map_from_file_with_diagram");

  BOOST_CHECK(p == q);
}

BOOST_AUTO_TEST_CASE(check_averages_of_heat_maps) {
  std::vector<std::vector<double> > filter = create_Gaussian_filter(30, 1);
  Persistence_heat_maps<constant_scaling_function> p("data/file_with_diagram", filter, false, 1000, 0, 10);
  Persistence_heat_maps<constant_scaling_function> q("data/file_with_diagram_1", filter, false, 1000, 0, 10);
  Persistence_heat_maps<constant_scaling_function> r("data/file_with_diagram_2", filter, false, 1000, 0, 10);

  Persistence_heat_maps<constant_scaling_function> av;
  av.compute_average({&p, &q, &r});

  Persistence_heat_maps<constant_scaling_function> template_average;
  template_average.load_from_file("data/template_average_of_heat_maps");

  BOOST_CHECK(av == template_average);
}

BOOST_AUTO_TEST_CASE(check_median_of_heat_maps) {
  std::vector<std::vector<double> > filter = create_Gaussian_filter(30, 1);
  Persistence_heat_maps<constant_scaling_function> p("data/file_with_diagram", filter, false, 1000, 0, 1);
  Persistence_heat_maps<constant_scaling_function> q("data/file_with_diagram_1", filter, false, 1000, 0, 1);
  Persistence_heat_maps<constant_scaling_function> r("data/file_with_diagram_2", filter, false, 1000, 0, 1);

  std::vector<Persistence_heat_maps<constant_scaling_function>*> to_compute_median;
  to_compute_median.push_back(&p);
  to_compute_median.push_back(&q);
  to_compute_median.push_back(&r);
  Persistence_heat_maps<constant_scaling_function> median;
  median.compute_median(to_compute_median);

  Persistence_heat_maps<constant_scaling_function> template_median;
  template_median.load_from_file("data/template_median_of_heat_maps");

  BOOST_CHECK(median == template_median);
}

BOOST_AUTO_TEST_CASE(check_compute_percentage_of_active_of_heat_maps) {
  std::vector<std::vector<double> > filter = create_Gaussian_filter(30, 1);
  Persistence_heat_maps<constant_scaling_function> p("data/file_with_diagram", filter, false, 1000, 0, 1);
  Persistence_heat_maps<constant_scaling_function> q("data/file_with_diagram_1", filter, false, 1000, 0, 1);
  Persistence_heat_maps<constant_scaling_function> r("data/file_with_diagram_2", filter, false, 1000, 0, 1);

  std::vector<Persistence_heat_maps<constant_scaling_function>*> to_compute_percentage_of_active;
  to_compute_percentage_of_active.push_back(&p);
  to_compute_percentage_of_active.push_back(&q);
  to_compute_percentage_of_active.push_back(&r);
  Persistence_heat_maps<constant_scaling_function> percentage_of_active;
  percentage_of_active.compute_percentage_of_active(to_compute_percentage_of_active, 0.1);

  Persistence_heat_maps<constant_scaling_function> template_percentage_of_active;
  template_percentage_of_active.load_from_file("data/template_percentage_of_active_of_heat_maps");

  BOOST_CHECK(percentage_of_active == template_percentage_of_active);
}

BOOST_AUTO_TEST_CASE(check_vectorize_for_heat_maps) {
  std::vector<std::vector<double> > filter = create_Gaussian_filter(30, 1);
  Persistence_heat_maps<constant_scaling_function> p("data/file_with_diagram", filter, false, 5, 0, 1);

  std::vector<double> p_vect_template;

  p_vect_template.push_back(0.0606728);
  p_vect_template.push_back(0.0610023);
  p_vect_template.push_back(0.0607978);
  p_vect_template.push_back(0.0600647);
  p_vect_template.push_back(0.0588224);
  p_vect_template.push_back(0.0619829);
  p_vect_template.push_back(0.0623218);
  p_vect_template.push_back(0.0621152);
  p_vect_template.push_back(0.0613686);
  p_vect_template.push_back(0.0601016);
  p_vect_template.push_back(0.0627679);
  p_vect_template.push_back(0.0631134);
  p_vect_template.push_back(0.0629066);
  p_vect_template.push_back(0.0621528);
  p_vect_template.push_back(0.0608719);
  p_vect_template.push_back(0.0630073);
  p_vect_template.push_back(0.0633564);
  p_vect_template.push_back(0.0631511);
  p_vect_template.push_back(0.0623968);
  p_vect_template.push_back(0.0611132);
  p_vect_template.push_back(0.0626947);
  p_vect_template.push_back(0.0630445);
  p_vect_template.push_back(0.0628425);
  p_vect_template.push_back(0.0620941);
  p_vect_template.push_back(0.060819);

  std::vector<double> p_vect = p.vectorize(0);
  for (size_t i = 0; i != p_vect.size(); ++i) {
    BOOST_CHECK(almost_equal(p_vect_template[i], p_vect[i]));
  }
}

BOOST_AUTO_TEST_CASE(check_distance_for_heat_maps) {
  std::vector<std::vector<double> > filter = create_Gaussian_filter(30, 1);
  Persistence_heat_maps<constant_scaling_function> p("data/file_with_diagram", filter, false, 1000, 0, 1);
  Persistence_heat_maps<constant_scaling_function> q("data/file_with_diagram_1", filter, false, 1000, 0, 1);
  Persistence_heat_maps<constant_scaling_function> r("data/file_with_diagram_2", filter, false, 1000, 0, 1);

  GUDHI_TEST_FLOAT_EQUALITY_CHECK(p.distance(p), 0., epsilon);
  GUDHI_TEST_FLOAT_EQUALITY_CHECK(p.distance(q), 624.183, epsilon);
  GUDHI_TEST_FLOAT_EQUALITY_CHECK(p.distance(r), 415.815, epsilon);
  GUDHI_TEST_FLOAT_EQUALITY_CHECK(q.distance(p), 624.183, epsilon);
  GUDHI_TEST_FLOAT_EQUALITY_CHECK(q.distance(q), 0., epsilon);
  GUDHI_TEST_FLOAT_EQUALITY_CHECK(q.distance(r), 528.066, epsilon);
  GUDHI_TEST_FLOAT_EQUALITY_CHECK(r.distance(p), 415.815, epsilon);
  GUDHI_TEST_FLOAT_EQUALITY_CHECK(r.distance(q), 528.066, epsilon);
  GUDHI_TEST_FLOAT_EQUALITY_CHECK(r.distance(r), 0., epsilon);
}

BOOST_AUTO_TEST_CASE(check_projections_to_R_for_heat_maps) {
  std::vector<std::vector<double> > filter = create_Gaussian_filter(30, 1);
  Persistence_heat_maps<constant_scaling_function> p("data/file_with_diagram", filter, false, 1000, 0, 1);
  Persistence_heat_maps<constant_scaling_function> q("data/file_with_diagram_1", filter, false, 1000, 0, 1);
  Persistence_heat_maps<constant_scaling_function> r("data/file_with_diagram_2", filter, false, 1000, 0, 1);

  GUDHI_TEST_FLOAT_EQUALITY_CHECK(p.project_to_R(0), 44.3308, epsilon);
  GUDHI_TEST_FLOAT_EQUALITY_CHECK(q.project_to_R(0), 650.568, epsilon);
  GUDHI_TEST_FLOAT_EQUALITY_CHECK(r.project_to_R(0), 429.287, epsilon);
}

BOOST_AUTO_TEST_CASE(check_scalar_products_for_heat_maps) {
  std::vector<std::vector<double> > filter = create_Gaussian_filter(30, 1);
  Persistence_heat_maps<constant_scaling_function> p("data/file_with_diagram", filter, false, 1000, 0, 1);
  Persistence_heat_maps<constant_scaling_function> q("data/file_with_diagram_1", filter, false, 1000, 0, 1);
  Persistence_heat_maps<constant_scaling_function> r("data/file_with_diagram_2", filter, false, 1000, 0, 1);

  GUDHI_TEST_FLOAT_EQUALITY_CHECK(p.compute_scalar_product(p), 0.0345687, epsilon);
  GUDHI_TEST_FLOAT_EQUALITY_CHECK(p.compute_scalar_product(q), 0.0509357, epsilon);
  GUDHI_TEST_FLOAT_EQUALITY_CHECK(p.compute_scalar_product(r), 0.0375608, epsilon);
  GUDHI_TEST_FLOAT_EQUALITY_CHECK(q.compute_scalar_product(p), 0.0509357, epsilon);
  GUDHI_TEST_FLOAT_EQUALITY_CHECK(q.compute_scalar_product(q), 1.31293, epsilon);
  GUDHI_TEST_FLOAT_EQUALITY_CHECK(q.compute_scalar_product(r), 0.536799, epsilon);
  GUDHI_TEST_FLOAT_EQUALITY_CHECK(r.compute_scalar_product(p), 0.0375608, epsilon);
  GUDHI_TEST_FLOAT_EQUALITY_CHECK(r.compute_scalar_product(q), 0.536799, epsilon);
  GUDHI_TEST_FLOAT_EQUALITY_CHECK(r.compute_scalar_product(r), 0.672907, epsilon);
}

BOOST_AUTO_TEST_CASE(check_arythmetic_operations_for_heat_maps) {
  std::vector<std::vector<double> > filter = create_Gaussian_filter(30, 1);
  Persistence_heat_maps<constant_scaling_function> p("data/file_with_diagram", filter, false, 1000, 0, 1);
  Persistence_heat_maps<constant_scaling_function> q("data/file_with_diagram_1", filter, false, 1000, 0, 1);

  Persistence_heat_maps<constant_scaling_function> sum = p + q;
  Persistence_heat_maps<constant_scaling_function> difference = p - q;
  Persistence_heat_maps<constant_scaling_function> multiply_by_scalar = 2 * p;

  // sum.print_to_file( "sum" );
  // difference.print_to_file( "difference" );
  // multiply_by_scalar.print_to_file( "multiply_by_scalar" );

  Persistence_heat_maps<constant_scaling_function> sum_template;
  sum_template.load_from_file("data/heat_map_sum");
  Persistence_heat_maps<constant_scaling_function> difference_template;
  difference_template.load_from_file("data/heat_map_difference");
  Persistence_heat_maps<constant_scaling_function> multiply_by_scalar_template;
  multiply_by_scalar_template.load_from_file("data/heat_map_multiply_by_scalar");

  BOOST_CHECK(sum == sum_template);
}

BOOST_AUTO_TEST_CASE(check_distance_of_heat_maps_infinite_power_parameters) {
  std::vector<std::vector<double> > filter = create_Gaussian_filter(100, 1);
  Persistence_heat_maps<constant_scaling_function> p("data/file_with_diagram", filter, false, 1000, 0, 1);

  std::vector<std::vector<double> > filter_2 = create_Gaussian_filter(150, 1);
  Persistence_heat_maps<constant_scaling_function> q("data/file_with_diagram", filter_2, true, 1000, 0, 1);

  double distance_max_double_parameter = p.distance(q, std::numeric_limits<double>::max());
  double distance_inf_double_parameter = p.distance(q, std::numeric_limits<double>::infinity());

  // std::cerr << "distance_max_double_parameter: " << distance_max_double_parameter << std::endl;
  // std::cerr << "distance_inf_double_parameter: " << distance_inf_double_parameter << std::endl;

  BOOST_CHECK(distance_max_double_parameter == distance_inf_double_parameter);
}

// Below I am storing the code used to generate tests for that functionality.
/*
        std::vector< std::pair< double,double > > intervals;
        intervals.push_back( std::make_pair(0.5,0.5) );
        std::vector< std::vector<double> > filter = create_Gaussian_filter(5,1);
        Persistence_heat_maps p( intervals , filter ,  constant_function, false , 100 , 0 , 1 );
        p.plot( "heat_map_1" );

        std::vector< std::pair< double,double > > intervals2;
        intervals2.push_back( std::make_pair(7,12) );
        Persistence_heat_maps q( intervals2 , filter ,  constant_function, false , 100 , 0 , 10 );
        q.plot( "heat_map_2" );
*/
/*
        std::vector< std::pair< double,double > > intervals;
        intervals.push_back( std::make_pair(0.5,0.5) );
        std::vector< std::vector<double> > filter = create_Gaussian_filter(5,1);
        Persistence_heat_maps p( intervals , filter ,  constant_function, false , 10 , 0 , 1 );
        p.write_to_file( "aaa" );

        Persistence_heat_maps q;
        q.load_from_file( "aaa" );

        cerr << ( p == q ) << endl;
        */

/*
        std::vector< std::vector<double> > filter = create_Gaussian_filter(30,1);
        Persistence_heat_maps p( "file_with_diagram" , filter ,  constant_function, false , 100 , 0 , 1 );
        p.plot( "heat_map_1" );
*/

/*
   //test to construct persistence heat map:
    std::vector< std::vector<double> > filter = create_Gaussian_filter(100,1);
        Persistence_heat_maps p( "file_with_diagram" , filter ,  constant_function, false , 1000 , 0 , 1 );
        p.print_to_file( "persistence_heat_map_from_file_with_diagram" );

        Persistence_heat_maps q;
        q.load_from_file( "persistence_heat_map_from_file_with_diagram" );

        cerr << (p == q) << endl;
*/
/*
        //test of computations of a mean:
        std::vector< std::pair< double,double > > intervals;
        intervals.push_back( std::make_pair(5,5) );
        std::vector< std::vector<double> > filter = create_Gaussian_filter(5,1);
        Persistence_heat_maps p( intervals , filter ,  constant_function, false , 100 , 0 , 10 );
        p.plot( "heat_map_1" );

        std::vector< std::pair< double,double > > intervals2;
        intervals2.push_back( std::make_pair(7,7) );
        Persistence_heat_maps q( intervals2 , filter ,  constant_function, false , 100 , 0 , 10 );
        q.plot( "heat_map_2" );

        Persistence_heat_maps av;
        av.compute_average( { &P , &q } );
        av.plot( "average" );
*/

/*
        std::vector< std::vector<double> > filter = create_Gaussian_filter(30,1);
        Persistence_heat_maps p( "file_with_diagram" , filter ,  constant_function, false , 1000 , 0 , 10 );
        Persistence_heat_maps q( "file_with_diagram_1" , filter ,  constant_function, false , 1000 , 0 , 10 );
        Persistence_heat_maps r( "file_with_diagram_2" , filter ,  constant_function, false , 1000 , 0 , 10 );
        Persistence_heat_maps av;
        av.compute_average( {&p,&q,&r} );

        av.print_to_file( "template_average_of_heat_maps" );
*/

/*
        std::vector< std::pair< double,double > > intervals;
        intervals.push_back( std::make_pair(5,5) );
        std::vector< std::vector<double> > filter = create_Gaussian_filter(5,1);
        Persistence_heat_maps p( intervals , filter ,  constant_function, false , 10 , 0 , 10 );
        p.plot( "heat_map_1" );

        std::vector< std::pair< double,double > > intervals2;
        intervals2.push_back( std::make_pair(7,7) );
        Persistence_heat_maps q( intervals2 , filter ,  constant_function, false , 10 , 0 , 10 );
        q.plot( "heat_map_2" );

        Persistence_heat_maps median;
        median.compute_median( {&p,&q} );
        median.plot( "median" );
*/

/*
        std::vector< std::vector<double> > filter = create_Gaussian_filter(30,1);
        Persistence_heat_maps p( "file_with_diagram" , filter ,  constant_function, false , 1000 , 0 , 1 );
        Persistence_heat_maps q( "file_with_diagram_1" , filter ,  constant_function, false , 1000 , 0 , 1 );
        Persistence_heat_maps r( "file_with_diagram_2" , filter ,  constant_function, false , 1000 , 0 , 1 );
        Persistence_heat_maps median;
        median.compute_median( {&p,&q,&r} );
        median.print_to_file( "template_median_of_heat_maps" );
*/

/*
        std::vector< std::vector<double> > filter = create_Gaussian_filter(30,1);
        Persistence_heat_maps p( "file_with_diagram" , filter ,  constant_function, false , 1000 , 0 , 1 );
        Persistence_heat_maps q( "file_with_diagram_1" , filter ,  constant_function, false , 1000 , 0 , 1 );
        Persistence_heat_maps r( "file_with_diagram_2" , filter ,  constant_function, false , 1000 , 0 , 1 );

        Persistence_heat_maps percentage_of_active;
        percentage_of_active.compute_percentage_of_active( {&p,&q,&r} , 0.1 );

        percentage_of_active.print_to_file( "template_percentage_of_active_of_heat_maps" );
        //percentage_of_active.plot( "template_percentage_of_active_of_heat_maps" );
*/


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

    floats considered very nearly equal when they (relatively) differ by some small epsilon.
*/

#ifndef GRAEHL__SHARED__EPSILON_HPP
#define GRAEHL__SHARED__EPSILON_HPP
#pragma once

#include <boost/cstdint.hpp>
#include <graehl/shared/nan.hpp>
#include <graehl/shared/order_preserving.hpp>
#include <algorithm>
#include <cmath>
#include <cstdlib>

#ifndef GRAEHL_EPSILON
/**
   compare to 1e-5 (FLT_EPSILON) 1e-9 (DBL_EPSILON) - Difference between 1 and
   the least value greater than 1 that is representable.

   for simplicity we use the same difference for both
*/
#define GRAEHL_EPSILON 2e-5
#endif

#ifndef GRAEHL_DEFAULT_IEEE_APART
/// for within_epsilon_or_(floats|doubles|ieee)_apart(a, b, epsilon): check approx
/// equality by the number of representable IEEE float/double between two
/// numbers
#define GRAEHL_DEFAULT_IEEE_APART 8
// for floats, this is about 1 in a million
#endif

namespace graehl {

#ifndef ONE_PLUS_EPSILON
#ifndef EPSILON
static const double EPSILON = GRAEHL_EPSILON;
#endif
static const double ONE_PLUS_EPSILON = 1 + EPSILON;
#endif

//#define ONE_PLUS_EPSILON (1+EPSILON)

/*
  The simple solution like abs(f1-f2) <= e does not work for very small or very big values. This
  floating-point comparison algorithm is based on the more confident solution presented by Knuth in [1]. For a
  given floating point values u and v and a tolerance e:

  | u - v | <= e * |u| and | u - v | <= e * |v|
  defines a "very close with tolerance e" relationship between u and v
  (1)

  | u - v | <= e * |u| or | u - v | <= e * |v|
  defines a "close enough with tolerance e" relationship between u and v
  (2)

  Both relationships are commutative but are not transitive. The relationship defined by inequations (1) is
  stronger that the relationship defined by inequations (2) (i.e. (1) => (2) ). Because of the multiplication
  in the right side of inequations, that could cause an unwanted underflow condition, the implementation is
  using modified version of the inequations (1) and (2) where all underflow, overflow conditions could be
  guarded safely:

  | u - v | / |u| <= e and | u - v | / |v| <= e
  | u - v | / |u| <= e or | u - v | / |v| <= e
  (1`)
  (2`)
*/

// intent: if you want to be conservative about an assert of a<b, test a<(slightly smaller b)
// if you want a<=b to succeed when a is == b but there were rounding errors so that a+epsilon=b, test
// a<(slightly larger b)
template <class Float>
inline Float slightly_larger(Float target) {
  return target * ONE_PLUS_EPSILON;
}

template <class Float>
inline Float slightly_smaller(Float target) {
  return target * (1. / ONE_PLUS_EPSILON);
}

// note, more meaningful tests exist for values near 0, see Knuth
// (but for logs, near 0 should be absolute-compared)
inline bool same_within_abs_epsilon(double a, double b, double epsilon = EPSILON) {
  return std::fabs(a - b) < epsilon;
}

inline bool close_by_first(double a, double b, double epsilon = EPSILON) {
  return std::fabs(a - b) <= epsilon * std::fabs(a);
}

/**
   \return a==b (approximately), symmetric (strict)
*/
inline bool very_close(double a, double b, double epsilon = EPSILON) {
  using std::fabs;
  double diff = fabs(a - b);
  return diff <= epsilon * fabs(a) && diff <= epsilon * fabs(b);
}

/**
   \return a==b (approximately), symmetric (looser)
*/
inline bool close_enough(double a, double b, double epsilon = EPSILON) {
  using std::fabs;
  double diff = fabs(a - b);
  return diff <= epsilon * fabs(a) || diff <= epsilon * fabs(b);
}

/**
   \return a==b (approximately)

   epsilon is relative (if a or b is larger than min_scale) or else absolute
   (using epsilon*min_scale)

*/
inline bool close_enough_min_scale(double a, double b, double epsilon = EPSILON, double min_scale = 1.) {
  using std::fabs;
  double diff = fabs(a - b);
  double scale = std::max(min_scale, std::max(fabs(a), fabs(b)));
  return diff <= epsilon * scale;
}

inline bool sign_ieee(boost::uint32_t f) {
  return f >> 31;
}

inline bool sign_ieee(float f) {
  return sign_ieee(reinterpret_cast<boost::uint32_t const&>(f));
}

inline bool sign_ieee(boost::uint64_t f) {
  // TODO: test
  return f >> 63;
}

inline bool sign_ieee(double f) {
  // TODO: test
  return sign_ieee(reinterpret_cast<boost::uint64_t const&>(f));
}

/**
   \return absolute distance between a and b. unlike std::abs(a-b), this is correct

   (also works for signed cast to unsigned)
*/
inline boost::uint32_t unsigned_distance(boost::uint32_t a, boost::uint32_t b) {
  return a < b ? b - a : a - b;
}

/**
   \return absolute distance between a and b. unlike std::abs(a-b), this is correct

   (also works for signed cast to unsigned)
*/
inline boost::uint64_t unsigned_distance(boost::uint64_t a, boost::uint64_t b) {
  return a < b ? b - a : a - b;
}

inline double next_representible_double(double f) {
  ++reinterpret_cast<boost::uint64_t&>(f);
  return f;
}

inline float next_representible_float(float f) {
  // TODO: test
  ++reinterpret_cast<boost::uint32_t&>(f);
  return f;
}

inline double next_representible(double f) {
  return next_representible_double(f);
}

inline float next_representible(float f) {
  // TODO: test
  return next_representible_float(f);
}

/**
   \return # of ieee floats between a and b, which must have the same sign

   this works because same-signed IEEE float/double have the same < order as the
   same-sized int (except perhaps for nan)

   see http://randomascii.wordpress.com/2012/02/25/comparing-floating-point-numbers-2012-edition/
*/
inline boost::int32_t ieee_floats_apart(float a, float b) {
  return unsigned_distance(reinterpret_cast<boost::uint32_t const&>(a),
                           reinterpret_cast<boost::uint32_t const&>(b));
}

/**
   \return # of ieee doubles between a and b, which must have the same sign

   see http://randomascii.wordpress.com/2012/02/25/comparing-floating-point-numbers-2012-edition/
*/
inline boost::int64_t ieee_doubles_apart(double a, double b) {
  return unsigned_distance(reinterpret_cast<boost::uint64_t const&>(a),
                           reinterpret_cast<boost::uint64_t const&>(b));
}

inline boost::int64_t ieee_apart(float a, float b) {
  return ieee_floats_apart(a, b);
}

inline boost::int64_t ieee_apart(double a, double b) {
  return ieee_doubles_apart(a, b);
}

/**
   \return a==b or within max_floats_apart for ieee floats. a and b should have the same sign.

   (unlike regular floating point a==b, compares non-numbers nan, +inf, etc. equal)

   the absolute epsilon is useful because there are millions of representable
   floats between GRAEHL_EPSILON and 0 (the denormals)

   max_floats_apart allows for slight relative differences when the scale of the
   numbers compared grows too large for epsilon (on the order of 1e-5 relative
   per max_floats_apart)

   see http://randomascii.wordpress.com/2012/02/25/comparing-floating-point-numbers-2012-edition/
*/
inline bool few_ieee_floats_apart(float a, float b, boost::int32_t max_floats_apart = GRAEHL_DEFAULT_IEEE_APART) {
  return sign_ieee(a) == sign_ieee(b) ? ieee_floats_apart(a, b) <= max_floats_apart : a == b;
  // implementation note: we could instead check a==b first, but by checking sign
  // using integral ops, then floats_apart using integral ops, in the common case
  // where signs match, it should be slightly more efficient (memory contents
  // need not be loaded to FPU). of course, if the floats were already in FPU,
  // maybe it would be better to use a similar relative difference check using
  // floating point ops only, e.g. close_enough or close_enough_min_scale
}

/**
   note: there are many adjacent doubles per adjacent float - 52 sig binary
   digits vs 23? so half a billion times as many. therefore these methods have
   different names
*/

inline bool few_ieee_doubles_apart(double a, double b,
                                   boost::int64_t max_doubles_apart = GRAEHL_DEFAULT_IEEE_APART) {
  // TODO: test
  return sign_ieee(a) == sign_ieee(b) ? ieee_doubles_apart(a, b) <= max_doubles_apart : a == b;
}

inline bool few_ieee_apart(float a, float b, boost::int32_t max_floats_apart = GRAEHL_DEFAULT_IEEE_APART) {
  return few_ieee_floats_apart(a, b, max_floats_apart);
}

inline bool few_ieee_apart(double a, double b, boost::int64_t max_doubles_apart = GRAEHL_DEFAULT_IEEE_APART) {
  return few_ieee_doubles_apart(a, b, max_doubles_apart);
}

/**
   \return a==b (within +- epsilon, absolute).
*/
template <class Float>
inline bool within_epsilon(Float a, Float b, Float epsilon = (float)GRAEHL_EPSILON) {
  return std::fabs(a - b) <= epsilon;
}

/**
   return a==b (within +- epsilon) or if a is no more than max_floats_apart
   representable floats away from b
*/
inline bool within_epsilon_or_floats_apart(float a, float b, float epsilon = (float)GRAEHL_EPSILON,
                                           boost::int32_t max_floats_apart = GRAEHL_DEFAULT_IEEE_APART) {
  return std::fabs(a - b) <= epsilon
         || (sign_ieee(a) == sign_ieee(b) && few_ieee_floats_apart(a, b, max_floats_apart));
}

/**
   return a==b (within +- epsilon) or if a is no more than max_floats_apart
   representable floats away from b
*/
inline bool within_epsilon_or_doubles_apart(double a, double b, double epsilon = (double)GRAEHL_EPSILON,
                                            boost::int64_t max_doubles_apart = GRAEHL_DEFAULT_IEEE_APART) {
  return std::fabs(a - b) <= epsilon
         || (sign_ieee(a) == sign_ieee(b) && few_ieee_doubles_apart(a, b, max_doubles_apart));
}

/**
   return a==b (within +- epsilon) or if a is no more than GRAEHL_DEFAULT_IEEE_APART representable
   floats away from b
*/
inline bool within_epsilon_or_ieee_apart(float a, float b, float epsilon = (float)GRAEHL_EPSILON) {
  return within_epsilon_or_floats_apart(a, b, epsilon, GRAEHL_DEFAULT_IEEE_APART);
}

/**
   return a==b (within +- epsilon) or if a is no more than GRAEHL_DEFAULT_IEEE_APART representable
   doubles away from b. this would be appropriate for ignoring rounding errors
   for computations done entirely with doubles; if you use it for the results on
   computations as floats, it will likely reduce to within_epsilon.
*/
inline bool within_epsilon_or_ieee_apart(double a, double b, double epsilon = (double)GRAEHL_EPSILON) {
  return within_epsilon_or_doubles_apart(a, b, epsilon, GRAEHL_DEFAULT_IEEE_APART);
}

template <class T>
inline bool few_ieee_apart_equate_inf_nan(T a, T b, boost::int32_t max_apart) {
  return few_ieee_apart(a, b, max_apart) || nonfinite_same_sign(a, b);
}
}

#ifdef GRAEHL_TEST
#include <graehl/shared/test.hpp>
namespace graehl {
namespace unit_test {

template <class Float>
void testEpsilon(Float f, unsigned n, double max_ieee_apart_per_n = 0.2) {
  Float x = f;
  Float sum = 0;
  for (unsigned i = 0; i < n; ++i) sum += x;
  Float nx = x * n;
  unsigned max_ieee_apart = (unsigned)(max_ieee_apart_per_n * n);
  BOOST_CHECK_MESSAGE(ieee_apart(nx, sum) <= max_ieee_apart,
                      "ieee_apart larger than " << max_ieee_apart << ": x=" << x << " n=" << n
                                                << " x*n=" << nx << " (x+...+x)(n times)=" << sum
                                                << " ieee_apart=" << ieee_apart(nx, sum));
}

BOOST_AUTO_TEST_CASE(TEST_epsilon) {
  testEpsilon(.2f, 1000);
  testEpsilon(.2, 1000);
  BOOST_CHECK_EQUAL(ieee_apart(1234.05, 1234.06), 43980465111);
  BOOST_CHECK_EQUAL(ieee_apart(1234.05f, 1234.06f), 82);
}
}
}

#endif

#endif