#pragma once
#include <boost/pending/disjoint_sets.hpp>
#include "xtensor/xtensor.hpp"
#include <queue>
#include <functional>
#include "affogato/segmentation/mutex_watershed.hxx"
#include "affogato/util.hxx"

namespace affogato {
namespace segmentation {

    //
    // semantic helper functions:
    // check_semantic_constraint: check if two representatives have different semantic labels
    // assign_semantic_label: assign the semantic label of a representative if not yet existent
    // merge_semantic_labels: assign two representatives to the same semantic label
    //

    template<class SEMANTIC_LABELING>
    inline bool check_semantic_constraint(const uint64_t ru, const uint64_t rv,
                                          const SEMANTIC_LABELING & semantic_labeling) {
        int64_t slu = semantic_labeling[ru];
        int64_t slv = semantic_labeling[rv];

        if (slu >= 0 && slv >= 0)
            return slu != slv;
        return false;
    }

    // set semantic label of 'ru' to 'sl'
    template<class SEMANTIC_LABELING>
    inline void assign_semantic_label(const uint64_t ru, const int64_t sl,
                                      SEMANTIC_LABELING & semantic_labeling) {
        if(semantic_labeling[ru] < 0){
            semantic_labeling[ru] = sl;
        }
    }

    // assign same semantic label to 'ru' and 'rv', assume they don't have different labels
    template<class SEMANTIC_LABELING>
    inline void merge_semantic_labels(const uint64_t ru, const uint64_t rv,
                                      SEMANTIC_LABELING & semantic_labeling) {
        int64_t slu = semantic_labeling[ru];
        int64_t slv = semantic_labeling[rv];

        if (slu >= 0 && slv < 0)
            semantic_labeling[rv] = slu;
        if (slu < 0 && slv >= 0)
            semantic_labeling[ru] = slv;
    }

    // compute mutex clustering for a graph with attrative and mutex edges
    template<class EDGE_ARRAY, class WEIGHT_ARRAY, class NODE_ARRAY, class SEMANTIC_ARRAY>
    void compute_semantic_mws_clustering(const size_t number_of_labels,
                                         const xt::xexpression<EDGE_ARRAY> & uvs_exp,
                                         const xt::xexpression<EDGE_ARRAY> & mutex_uvs_exp,
                                         const xt::xexpression<EDGE_ARRAY> & semantic_uts_exp,
                                         const xt::xexpression<WEIGHT_ARRAY> & weights_exp,
                                         const xt::xexpression<WEIGHT_ARRAY> & mutex_weights_exp,
                                         const xt::xexpression<WEIGHT_ARRAY> & semantic_weights_exp,
                                         xt::xexpression<NODE_ARRAY> & node_labeling_exp,
                                         xt::xexpression<SEMANTIC_ARRAY> & semantic_labeling_exp) {

        // casts
        const auto & uvs = uvs_exp.derived_cast();
        const auto & mutex_uvs = mutex_uvs_exp.derived_cast();
        const auto & semantic_uts = semantic_uts_exp.derived_cast();
        const auto & weights = weights_exp.derived_cast();
        const auto & mutex_weights = mutex_weights_exp.derived_cast();
        const auto & semantic_weights = semantic_weights_exp.derived_cast();
        auto & node_labeling = node_labeling_exp.derived_cast();
        auto & semantic_labeling = semantic_labeling_exp.derived_cast();

        // make ufd
        std::vector<uint64_t> ranks(number_of_labels);
        std::vector<uint64_t> parents(number_of_labels);
        boost::disjoint_sets<uint64_t*, uint64_t*> ufd(&ranks[0], &parents[0]);
        for(uint64_t label = 0; label < number_of_labels; ++label) {
            ufd.make_set(label);
        }

        // determine number of edge types
        const size_t num_edges = uvs.shape()[0];
        const size_t num_mutex = mutex_uvs.shape()[0];
        const size_t num_semantic = semantic_uts.shape()[0];
        const size_t num_internal = num_edges + num_mutex;

        // argsort ALL edges
        // we sort in ascending order
        std::vector<size_t> indices(num_edges + num_mutex + num_semantic);
        std::iota(indices.begin(), indices.end(), 0);
        std::sort(indices.begin(), indices.end(), [&](const size_t a, const size_t b){
            const double val_a = (a >= num_internal) ? semantic_weights(a - num_internal) :
                                 (a >= num_edges)    ? mutex_weights(a - num_edges)
                                                     : weights(a);
            const double val_b = (b >= num_internal) ? semantic_weights(b - num_internal) :
                                 (b >= num_edges)    ? mutex_weights(b - num_edges)
                                                     : weights(b);
            return val_a > val_b;
        });

        // data-structure storing mutex edges
        typedef std::vector<std::vector<uint64_t>> MutexStorage;
        MutexStorage mutexes(number_of_labels);

        // iterate over all edges
        for(const size_t edge_id : indices) {

            // check wether this edge is semantic
            const bool is_semantic_edge = edge_id >= num_internal;
            // check whether this edge is mutex via the edge offset
            const bool is_mutex_edge = edge_id >= num_edges;

            const size_t id = is_semantic_edge ? edge_id - num_internal :
                              is_mutex_edge ? edge_id - num_edges : edge_id;

            // get first incidental node and its current representative of edge_id
            const uint64_t u = is_semantic_edge ? semantic_uts(id, 0) :
                               is_mutex_edge ? mutex_uvs(id, 0) : uvs(id, 0);
            uint64_t ru = ufd.find_set(u);

            if(!is_semantic_edge){

                // find the edge-id or mutex id and the connected nodes
                const uint64_t v = is_mutex_edge ? mutex_uvs(id, 1) : uvs(id, 1);

                // find the current representatives
                uint64_t rv = ufd.find_set(v);

                // if the nodes are already connected, do nothing
                if(ru == rv) {
                    continue;
                }

                if(check_semantic_constraint(ru, rv, semantic_labeling)){
                    continue;
                }

                // if we already have a mutex, we do not need to do anything
                // (if this is a regular edge, we do not link, if it is a mutex edge
                //  we do not need to insert the redundant mutex constraint)
                if(check_mutex(ru, rv, mutexes)) {
                    continue;
                }

                if(is_mutex_edge) {

                    // insert mutex constraint
                    insert_mutex(ru, rv, id, mutexes);

                } else {

                    // link the nodes and merge their mutex constraints
                    ufd.link(u, v);
                    // check  if we have to swap the roots
                    if(ufd.find_set(ru) == rv) {
                        std::swap(ru, rv);
                    }
                    // merge mutexes from rv -> ru
                    merge_mutexes(rv, ru, mutexes);
                }
            } else{
                // find semantic label associated to edge
                int64_t t = semantic_uts(id, 1);
                assign_semantic_label(ru, t, semantic_labeling);
            }
        }

        // get node labeling into output
        util::export_consecutive_labels(ufd, number_of_labels, node_labeling);

        for(size_t label = 0; label < number_of_labels; ++label) {
            uint64_t root = ufd.find_set(label);
            semantic_labeling[label] = semantic_labeling[root];
        }
    }

    // compute mutex segmentation via kruskal
    template<class WEIGHT_ARRAY, class NODE_ARRAY, class SEMANTIC_ARRAY, class INDICATOR_ARRAY>
    void compute_semantic_mws_segmentation(const xt::xexpression<WEIGHT_ARRAY> & sorted_flat_indices_exp,
                                           const xt::xexpression<INDICATOR_ARRAY> & valid_edges_exp,
                                           const std::vector<std::vector<int>> & offsets,
                                           const size_t number_of_attractive_channels,
                                           const std::vector<int> & image_shape,
                                           xt::xexpression<NODE_ARRAY> & node_labeling_exp,
                                           xt::xexpression<SEMANTIC_ARRAY> & semantic_labeling_exp) {

        // casts
        const auto & sorted_flat_indices = sorted_flat_indices_exp.derived_cast();
        const auto & valid_edges = valid_edges_exp.derived_cast();
        auto & node_labeling = node_labeling_exp.derived_cast();
        auto & semantic_labeling = semantic_labeling_exp.derived_cast();

        // determine number of nodes and attractive edges
        const size_t number_of_nodes = node_labeling.size();
        const size_t number_of_attractive_edges = number_of_nodes * number_of_attractive_channels;
        const size_t number_of_offsets = offsets.size();
        const size_t ndims = offsets[0].size();
        const size_t number_of_internal_edges = number_of_nodes * number_of_offsets;

        std::vector<int64_t> array_stride(ndims);
        int64_t current_stride = 1;
        for (int i = ndims-1; i >= 0; --i){
            array_stride[i] = current_stride;
            current_stride *= image_shape[i];
        }

        std::vector<int64_t> offset_strides;
        for (const auto & offset: offsets){
            int64_t stride = 0;
            for (int i = 0; i < offset.size(); ++i){
                stride += offset[i] * array_stride[i];
            }
            offset_strides.push_back(stride);
        }

        // make ufd
        std::vector<uint64_t> ranks(number_of_nodes);
        std::vector<uint64_t> parents(number_of_nodes);
        boost::disjoint_sets<uint64_t*, uint64_t*> ufd(&ranks[0], &parents[0]);
        for(uint64_t label = 0; label < number_of_nodes; ++label) {
            ufd.make_set(label);
        }

        // data-structure for storing semantic labels, unassigned are set to 0
        // std::vector<uint64_t> semantic_labeling(number_of_nodes, 0);

        // data-structure storing mutex edges
        typedef std::vector<std::vector<uint64_t>> MutexStorage;
        MutexStorage mutexes(number_of_nodes);

        // iterate over all edges
        for(const size_t edge_id : sorted_flat_indices) {

            if(!valid_edges(edge_id)){
                continue;
            }

            // check wether this edge is semantic
            const bool is_semantic_edge = edge_id >= number_of_internal_edges;
            // check whether this edge is mutex via the edge offset
            const bool is_mutex_edge = edge_id >= number_of_attractive_edges;

            // get first incidental node and its current representative of edge_id
            const uint64_t u = edge_id % number_of_nodes;
            uint64_t ru = ufd.find_set(u);

            if(!is_semantic_edge){
                // const auto affCoord_ = xt::unravel_from_strides(edge_id, strides, layout);

                // get second incidental node
                const uint64_t v = u + offset_strides[edge_id / number_of_nodes];
                // find the current representative
                uint64_t rv = ufd.find_set(v);

                // if the nodes are already connected, do nothing
                if(ru == rv) {
                    continue;
                }

                if(check_semantic_constraint(ru, rv, semantic_labeling)){
                    continue;
                }

                // if we already have a mutex, we do not need to do anything
                // (if this is a regular edge, we do not link, if it is a mutex edge
                //  we do not need to insert the redundant mutex constraint)
                if(check_mutex(ru, rv, mutexes)) {
                    continue;
                }

                if(is_mutex_edge) {

                    // insert the mutex edge into both mutex edge storages
                    insert_mutex(ru, rv, edge_id, mutexes);

                } else {

                    ufd.link(u, v);
                    // check  if we have to swap the roots
                    if(ufd.find_set(ru) == rv) {
                        std::swap(ru, rv);
                    }
                    // merge mutexes from rv -> ru
                    merge_mutexes(rv, ru, mutexes);
                    merge_semantic_labels(ru, rv, semantic_labeling);
                }
            }else{
                // find semantic label associated to edge
                int64_t sl = edge_id / number_of_nodes - number_of_offsets;

                // add semantic class to cluster
                assign_semantic_label(ru, sl, semantic_labeling);
            }
        }

        // get node labeling into output
        util::export_consecutive_labels(ufd, number_of_nodes, node_labeling);

        for(size_t label = 0; label < number_of_nodes; ++label) {
            uint64_t root = ufd.find_set(label);
            semantic_labeling[label] = semantic_labeling[root];
        }
    }
}
}


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

#include <sensor_msgs/PointCloud2.h>
#include <sensor_msgs/point_cloud2_iterator.h>
#include <tf2/LinearMath/Transform.h>
#include <tf2/convert.h>
#include <tf2/transform_datatypes.h>
#include <tf2_sensor_msgs/tf2_sensor_msgs.h>
#include <chrono>
#include <object_association_merger/node.hpp>
#define EIGEN_MPL2_ONLY
#include <Eigen/Core>
#include <Eigen/Geometry>

namespace object_association
{
ObjectAssociationMergerNode::ObjectAssociationMergerNode()
: nh_(""),
  pnh_("~"),
  tf_listener_(tf_buffer_),
  object0_sub_(pnh_, "input/object0", 1),
  object1_sub_(pnh_, "input/object1", 1),
  sync_(SyncPolicy(10), object0_sub_, object1_sub_)
{
  sync_.registerCallback(boost::bind(&ObjectAssociationMergerNode::objectsCallback, this, _1, _2));

  merged_object_pub_ =
    pnh_.advertise<autoware_perception_msgs::DynamicObjectWithFeatureArray>("output/object", 10);
}

void ObjectAssociationMergerNode::objectsCallback(
  const autoware_perception_msgs::DynamicObjectWithFeatureArray::ConstPtr & input_object0_msg,
  const autoware_perception_msgs::DynamicObjectWithFeatureArray::ConstPtr & input_object1_msg)
{
  // Guard
  if (merged_object_pub_.getNumSubscribers() < 1) return;

  // build output msg
  autoware_perception_msgs::DynamicObjectWithFeatureArray output_msg;
  output_msg.header = input_object0_msg->header;

  /* global nearest neighbor */
  std::unordered_map<int, int> direct_assignment;
  std::unordered_map<int, int> reverse_assignment;
  Eigen::MatrixXd score_matrix =
    data_association_.calcScoreMatrix(*input_object1_msg, *input_object0_msg);
  data_association_.assign(score_matrix, direct_assignment, reverse_assignment);
  for (size_t object0_idx = 0; object0_idx < input_object0_msg->feature_objects.size();
       ++object0_idx) {
    if (direct_assignment.find(object0_idx) != direct_assignment.end())  // found
    {
      // The one with the higher score will be hired.
      if (
        input_object1_msg->feature_objects.at(direct_assignment.at(object0_idx))
          .object.semantic.confidence <
        input_object0_msg->feature_objects.at(object0_idx).object.semantic.confidence) {
        output_msg.feature_objects.push_back(input_object0_msg->feature_objects.at(object0_idx));
      } else {
        output_msg.feature_objects.push_back(
          input_object1_msg->feature_objects.at(direct_assignment.at(object0_idx)));
      }
    } else  // not found
    {
      output_msg.feature_objects.push_back(input_object0_msg->feature_objects.at(object0_idx));
    }
  }
  for (size_t object1_idx = 0; object1_idx < input_object1_msg->feature_objects.size();
       ++object1_idx) {
    if (reverse_assignment.find(object1_idx) != reverse_assignment.end())  // found
    {
    } else  // not found
    {
      output_msg.feature_objects.push_back(input_object1_msg->feature_objects.at(object1_idx));
    }
  }

  // publish output msg
  merged_object_pub_.publish(output_msg);
}
}  // namespace object_association


#pragma once

#include <Eigen/Dense>
#include <dart/dart.hpp>
#include <dart/gui/GLFuncs.hpp>
#include <dart/gui/osg/osg.hpp>
#include <osgShadow/LightSpacePerspectiveShadowMap>

#include <Utils/General/Clock.hpp>

class EnvInterface;
class DracoSensorData;
class DracoCommand;
class DracoLedPosAnnouncer;

class DracoWorldNode : public dart::gui::osg::WorldNode {
   private:
    EnvInterface* mInterface;
    DracoSensorData* mSensorData;
    DracoCommand* mCommand;

    void _get_imu_data(Eigen::VectorXd& ang_vel, Eigen::VectorXd& acc,
                       Eigen::VectorXd& mag);
    void _check_foot_contact(bool& rfoot_contact, bool& lfoot_contact);
    void _check_collision();
    void _hold_xy();
    void _hold_rot();
    void UpdateLedData_();
    void SetParameters_();

    dart::dynamics::SkeletonPtr mSkel;
    dart::dynamics::SkeletonPtr mGround;
    dart::dynamics::SkeletonPtr mStar;
    dart::dynamics::SkeletonPtr mTorus;
    Eigen::VectorXd mTorqueCommand;
    int mDof;
    double mReleaseTime;

    int count_;
    double waiting_time_;
    double t_;
    bool b_check_collision_;
    bool b_print_computation_time;
    double servo_rate_;

   public:
    DracoWorldNode(const dart::simulation::WorldPtr& world);

    virtual ~DracoWorldNode();

    void customPreStep() override;
    Eigen::VectorXd mKp;
    Eigen::VectorXd mKd;
    Eigen::VectorXd q_sim_;

    dart::simulation::WorldPtr world_;

    DracoLedPosAnnouncer* led_pos_announcer_;
    Clock clock_;
};


#include <iostream>
#include <fstream>
#include <iomanip>
#include <string>
#include <boost/program_options.hpp>

#include "msa.hpp"
#include "model.hpp"
#include "MCseq.hpp"
#include "OptParam.hpp"
using namespace std;

namespace po = boost::program_options;

int main(int argc, char *argv[])
{
  po::options_description desc("Allowed options");
  desc.add_options()
    ("help,h","produce help message")
    ("in,i", po::value<string>(), "set input file (multiple FASTA)")
    ("ipair", po::value<string>(), "interacting pairs of reference sequence")
    ("rmap", po::value<string>(), "HMMer3 alignment of reference sequence")
    ("param,p", po::value<string>(), "LGM parameter file (for restart)")
    ("logbase,l", po::value<string>(), "basename for log files")
    ("alphaH", po::value<double>(), "alphaH")
    ("alphaJ", po::value<double>(), "alphaJ")
    ("alphaK", po::value<double>(), "alphaK")
    ("betaH", po::value<double>(), "betaH")
    ("betaJ", po::value<double>(), "betaJ")
    ("betaK", po::value<double>(), "betaK")
    ("nloop", po::value<int>(), "number of optimization loops")
    ;

  po::positional_options_description p;
  po::variables_map vm;
  po::store(po::command_line_parser(argc,argv).
	    options(desc).positional(p).run(), vm);
  po::notify(vm);

  if(vm.count("help")) {
    cerr << desc << endl;
    return 1;
  }
  string logbase = "train";
  if(vm.count("logbase")) {
    logbase = vm["logbase"].as<string>();
  }

  if(!vm.count("in")) {
    cerr << "Specify the input MSA!" << endl;
    exit(1);
  }

  cerr << "Input file was set to " << vm["in"].as<string>() << endl;
  string infile = vm["in"].as<string>();
  Msa msa(infile);
  Model model(msa);

  if(vm.count("ipair")) {
    cerr << "#reading ipair " << (vm["ipair"].as<string>()) << endl;
    model.read_ipairs(vm["ipair"].as<string>());
    if(vm.count("rmap")) {
      cerr << "#reading rmap " << (vm["rmap"].as<string>()) << endl;
      model.read_reference_3Dalignment(vm["rmap"].as<string>());
    } else {
      cerr << "Give --rmap for 3D contact mapping!" << endl;
      exit(2);
    }
  } else {
    cerr << "Setting default ipairs." << endl;
    model.set_default_ipairs();
  }

  if(vm.count("param")) {
    model.read_parameters(vm["param"].as<string>());
    cerr << "Parameters read from " << vm["param"].as<string>() << endl;
  } else {
    cerr << "Parameter file (with optimized J) must be specified!" << endl;
    exit(1);
  }

  OptParam optpar(model);
  int nloop = 20;
  if(vm.count("nloop")) {
    nloop = vm["nloop"].as<int>();
  }
  cerr << "# optimizing for " << nloop << " loops." << endl;

  if(vm.count("alphaH")) {
    optpar.alpha_H = vm["alphaH"].as<double>();
  }
  if(vm.count("alphaJ")) {
    optpar.alpha_J = vm["alphaJ"].as<double>();
  }
  if(vm.count("alphaK")) {
    optpar.alpha_K = vm["alphaK"].as<double>();
  }
  cerr << "# alpha_{H,J,K}= "
       << optpar.alpha_H << "\t"
       << optpar.alpha_J << "\t"
       << optpar.alpha_K << endl;

  if(vm.count("betaH")) {
    optpar.beta_H = vm["betaH"].as<double>();
  }
  if(vm.count("betaJ")) {
    optpar.beta_J = vm["betaJ"].as<double>();
  }
  if(vm.count("betaK")) {
    optpar.beta_K = vm["betaK"].as<double>();
  }
  cerr << "# beta_{H,J,K}= "
       << optpar.beta_H << "\t"
       << optpar.beta_J << "\t"
       << optpar.beta_K << endl;

  cerr << "energy(obs)= " << model.get_energy() << endl;

  const string param_out = logbase + ".param";
  const clock_t begin_time = clock();
  optpar.optimize_plm(model,nloop, param_out);
  const clock_t end_time = clock();
  cerr << "Time " << float(end_time - begin_time)/CLOCKS_PER_SEC << endl;

  //  optpar.write_momentum(model, logbase + ".mmt");

  cerr << "energy(obs)= " << model.get_energy() << endl;
  cerr << "energy(exp)= " << model.get_energy_exp() << endl;

  cerr << "max adel (site) " << model.check_site(false) << endl;
  cerr << "max adel (bonded) " << model.check_bonded(false) << endl;
  cerr << "max adel (nonbonded) " << model.check_nonbonded(false) << endl;

  return 0;
}


#include <stdexcept>
#include <sstream>
#include <vector>
#include <cmath>
#include <iostream>
#include <fstream>
#include <string>
#include <typeinfo>

#include <math.h>
#if defined( STK_HAS_MPI )
#include <mpi.h>
#endif

#include <gtest/gtest.h>

#include <stk_util/environment/WallTime.hpp>
#include <stk_util/diag/PrintTable.hpp>

#include <Teuchos_ScalarTraits.hpp>

#include <gtest/gtest.h>
#include <boost/lexical_cast.hpp>
#include <stk_io/IossBridge.hpp>

#include <percept/Percept.hpp>
#include <percept/Util.hpp>
#include <percept/ExceptionWatch.hpp>

#include <percept/function/StringFunction.hpp>
#include <percept/function/FieldFunction.hpp>
#include <percept/function/ConstantFunction.hpp>
#include <percept/PerceptMesh.hpp>

#include <adapt/UniformRefinerPattern.hpp>
#include <adapt/UniformRefiner.hpp>
#include <adapt/RefinerUtil.hpp>
#include <adapt/UniformRefinerPattern_Tri3_Quad4_3.hpp>
#include <adapt/UniformRefinerPattern_Tet4_Hex8_4.hpp>
#include <adapt/UniformRefinerPattern_Wedge6_Hex8_6.hpp>
#include <unit_tests/TestLocalRefiner.hpp>
#include <adapt/sierra_element/StdMeshObjTopologies.hpp>
#include <adapt/sierra_element/RefinementTopology.hpp>
#include <percept/RunEnvironment.hpp>

#include <percept/fixtures/Fixture.hpp>
#include <percept/fixtures/BeamFixture.hpp>
#include <percept/fixtures/SingleTetFixture.hpp>
#include <percept/fixtures/TetWedgeFixture.hpp>
#include <percept/fixtures/HeterogeneousFixture.hpp>
#include <percept/fixtures/PyramidFixture.hpp>
#include <percept/fixtures/QuadFixture.hpp>
#include <percept/fixtures/WedgeFixture.hpp>

// smoothing tests
#include <percept/mesh/mod/smoother/MeshSmoother.hpp>
#include <percept/mesh/mod/smoother/ReferenceMeshSmootherConjugateGradient.hpp>

#include <adapt/UniformRefinerPattern_def.hpp>
#include <stk_mesh/base/MeshUtils.hpp>
#include <stk_mesh/base/FEMHelpers.hpp>
#include "stk_unit_test_utils/ReadWriteSidesetTester.hpp"
#include "stk_unit_test_utils/FaceTestingUtils.hpp"

  namespace percept
  {
    namespace regression_tests
    {
    using namespace stk::unit_test_util::sideset;
    typedef stk::mesh::EntityVector PseudoEntity;

#include "RegressionTestFileLoc.hpp"

    const percept::Elem::RefinementTopology* get_refinement_topology(const CellTopologyData * const cellTopoData)
    {
        shards::CellTopology cellTopo(cellTopoData);
        percept::Elem::CellTopology elem_celltopo = percept::Elem::getBasicCellTopology(cellTopo.getName());
        const percept::Elem::RefinementTopology* ref_topo_p = Elem::getRefinementTopology(elem_celltopo);
        return ref_topo_p;
    }

    const percept::Elem::RefinementTopology* get_refinement_topology(stk::topology topology)
    {
        return get_refinement_topology(stk::mesh::get_cell_topology(topology).getCellTopologyData());
    }

    const percept::Elem::RefinementTopology* get_refinement_topology(percept::PerceptMesh &eMesh, stk::mesh::Entity element)
    {
        return get_refinement_topology(eMesh.get_cell_topology(element));
    }

    class MeshWithSideset {
    public:
        MeshWithSideset(const std::string &inputBaseName) :
            m_baseName(inputBaseName),
            m_meta(3, get_entity_rank_names()),
            m_bulk(m_meta, MPI_COMM_WORLD),
            m_eMesh(&m_meta, &m_bulk, false),
            m_isMeshLoaded(false)
        {

        }

        ~MeshWithSideset()
        {
            delete m_breakPattern;
        }

    private:
        std::vector<std::string> get_entity_rank_names()
        {
            std::vector<std::string> entityRankNames = stk::mesh::entity_rank_names();
            entityRankNames.push_back("FAMILY_TREE");
            return entityRankNames;
        }

        MeshWithSideset();
        MeshWithSideset(const MeshWithSideset&);

    private:
        std::string m_baseName;
        stk::mesh::MetaData m_meta;
        stk::unit_test_util::sideset::BulkDataTester m_bulk;
        percept::PerceptMesh m_eMesh;
        URP_Heterogeneous_3D *m_breakPattern = nullptr;
        bool m_isMeshLoaded;

    public:
        stk::unit_test_util::sideset::BulkDataTester &get_bulk() { return m_bulk; }
        stk::mesh::MetaData &get_meta() { return m_meta; }
        percept::PerceptMesh &get_mesh() { return m_eMesh; }
        UniformRefinerPatternBase &get_breaker() { return *m_breakPattern; }

        std::string get_input_filename()
        {
            std::string inputFilename(input_files_loc + m_baseName + ".e");
            return inputFilename;
        }

        std::string get_output_filename()
        {
            std::string outputFilename(output_files_loc + m_baseName + "_1.e");
            return outputFilename;
        }

        void load_mesh_and_fill_sideset_data()
        {
            if(m_isMeshLoaded) return;

            stk::unit_test_util::sideset::StkMeshIoBrokerTester inputIoBroker;

            inputIoBroker.property_add(Ioss::Property("DECOMPOSITION_METHOD", "RCB"));
            inputIoBroker.set_rank_name_vector(get_entity_rank_names());
            inputIoBroker.set_bulk_data(m_bulk);

            inputIoBroker.add_mesh_database(get_input_filename(), stk::io::READ_MESH);
            inputIoBroker.create_input_mesh();
            inputIoBroker.add_all_mesh_fields_as_input_fields();

            m_breakPattern = new URP_Heterogeneous_3D(m_eMesh);

            m_eMesh.add_field("proc_rank", stk::topology::ELEMENT_RANK, 0);
            inputIoBroker.populate_bulk_data();

            m_eMesh.setCoordinatesField();

            m_isMeshLoaded = true;
        }

        void write_exodus_file()
        {
            ThrowRequire(m_isMeshLoaded);
            stk::unit_test_util::sideset::StkMeshIoBrokerTester outputIoBroker;
            outputIoBroker.set_bulk_data(m_bulk);
            size_t resultFileIndex = outputIoBroker.create_output_mesh(get_output_filename(), stk::io::WRITE_RESULTS);
            outputIoBroker.write_output_mesh(resultFileIndex);
        }

        void do_uniform_refinement(bool removeOldElements=true)
        {
            ThrowRequire(m_isMeshLoaded);
            UniformRefiner breaker(m_eMesh, *m_breakPattern, m_eMesh.get_field(stk::topology::ELEMENT_RANK, "proc_rank"));
            breaker.setRemoveOldElements(removeOldElements);
            //breaker.setIgnoreSideSets(true);
            breaker.doBreak();
        }
    };

      void refine_mesh(const std::string &baseName,
                       const SideSetIdAndElemIdSidesVector& expectedInputSideset,
                       const SideSetIdAndElemIdSidesVector& expectedRefinedSideset)
      {
        MeshWithSideset inputMesh(baseName);
        inputMesh.load_mesh_and_fill_sideset_data();

        stk::unit_test_util::sideset::BulkDataTester &bulk = inputMesh.get_bulk();
        stk::mesh::MetaData &meta = inputMesh.get_meta();

        compare_sidesets(inputMesh.get_input_filename(), bulk, expectedInputSideset);

        inputMesh.do_uniform_refinement();

        for(const SideSetIdAndElemIdSides& sset : expectedRefinedSideset)
	{
	    stk::mesh::SideSet sideSet = get_stk_side_set(bulk, sset.sideSet);

	    stk::mesh::Part *surface_part = stk::unit_test_util::get_surface_part_with_id(meta, sset.id);

	    ThrowRequire(nullptr != surface_part);
            bulk.create_sideset(stk::io::get_sideset_parent(*surface_part)) = sideSet;
	}

        stk::mesh::EntityVector faces;
        stk::mesh::get_selected_entities(meta.locally_owned_part(), bulk.buckets(meta.side_rank()), faces);

        for(stk::mesh::Entity face : faces)
            ASSERT_EQ(2u, bulk.num_elements(face));

        inputMesh.write_exodus_file();

        stk::mesh::MetaData meta2;
        stk::unit_test_util::sideset::BulkDataTester bulk2(meta2, MPI_COMM_WORLD);
        stk::unit_test_util::sideset::read_exo_file(bulk2, inputMesh.get_output_filename(), stk::unit_test_util::sideset::READ_SERIAL_AND_DECOMPOSE);
        EXPECT_NO_THROW(compare_sidesets(inputMesh.get_output_filename(), bulk2, expectedRefinedSideset));
      }

      struct TestCase
      {
          std::string filename;
          SideSetIdAndElemIdSidesVector originalSideset;
          SideSetIdAndElemIdSidesVector refinedSideset;
      };

      void add_test_case(const std::string &baseName,
                         const SideSetIdAndElemIdSidesVector &original,
                         const SideSetIdAndElemIdSidesVector &refined,
                         std::vector<TestCase> &testCases)
      {
          // This function is necessary to get around intel compiler issues with seagull brace initialization
          testCases.push_back({baseName, original, refined});
      }

      TEST(regr_stk_sideset_refine, DISABLED_internal_sidesets)
      {
        stk::ParallelMachine pm = MPI_COMM_WORLD;

        const unsigned p_size = stk::parallel_machine_size(pm);

        std::vector<TestCase> testCases;
        add_test_case("ADA",
                      {{1, {{ 1, 5}, { 2, 4}                                                      }}},
                      {{1, {{23, 5}, {24, 5}, {25, 5}, {26, 5}, {27, 4}, {28, 4}, {29, 4}, {30, 4}}}},
                      testCases);
        add_test_case("ARA",
                      {{1, {{ 2, 4}                           }}},
                      {{1, {{27, 4}, {28, 4}, {29, 4}, {30, 4}}}},
                      testCases);
        add_test_case("ALA",
                      {{1, {{ 1, 5}                           }}},
                      {{1, {{23, 5}, {24, 5}, {25, 5}, {26, 5}}}},
                      testCases);
        add_test_case("ADe",
                      {{1, {{ 1, 5}, { 2, 1}                                                      }}},
                      {{1, {{23, 5}, {24, 5}, {25, 5}, {26, 5}, {85, 1}, {86, 1}, {87, 1}, {88, 1}}}},
                      testCases);
        add_test_case("ARe",
                      {{1, {{ 2, 1}                           }}},
                      {{1, {{85, 1}, {86, 1}, {87, 1}, {88, 1}}}},
                      testCases);
        add_test_case("ALe",
                      {{1, {{ 1, 5}                           }}},
                      {{1, {{23, 5}, {24, 5}, {25, 5}, {26, 5}}}},
                      testCases);

        std::vector<TestCase> testCasesP0;
        add_test_case("ADA",
                      {{1, {{ 1, 5}                           }}},
                      {{1, {{23, 5}, {24, 5}, {25, 5}, {26, 5}}}},
                      testCasesP0);
        add_test_case("ARA",
                      {{1, { }}},
                      {{1, { }}},
                      testCasesP0);
        add_test_case("ALA",
                      {{1, {{ 1, 5}                           }}},
                      {{1, {{23, 5}, {24, 5}, {25, 5}, {26, 5}}}},
                      testCasesP0);
        add_test_case("ADe",
                      {{1, {{ 1, 5}                           }}},
                      {{1, {{23, 5}, {24, 5}, {25, 5}, {26, 5}}}},
                      testCasesP0);
        add_test_case("ARe",
                      {{1, { }}},
                      {{1, { }}},
                      testCasesP0);
        add_test_case("ALe",
                      {{1, {{ 1, 5}                           }}},
                      {{1, {{23, 5}, {24, 5}, {25, 5}, {26, 5}}}},
                      testCasesP0);

        std::vector<TestCase> testCasesP1;
        add_test_case("ADA",
                      {{1, {{ 2, 4}                               }}},
                      {{1, {{27,  4}, {28,  4}, {29,  4}, {30,  4}}}},
                      testCasesP1);
        add_test_case("ARA",
                      {{1, {{ 2, 4}                               }}},
                      {{1, {{27,  4}, {28,  4}, {29,  4}, {30,  4}}}},
                      testCasesP1);
        add_test_case("ALA",
                      {{1, { }}},
                      {{1, { }}},
                      testCasesP1);
        add_test_case("ADe",
                      {{1, {{  2, 1}                              }}},
                      {{1, {{139, 1}, {140, 1}, {141, 1}, {142, 1}}}},
                      testCasesP1);
        add_test_case("ARe",
                      {{1, {{  2, 1}                              }}},
                      {{1, {{139, 1}, {140, 1}, {141, 1}, {142, 1}}}},
                      testCasesP1);
        add_test_case("ALe",
                      {{1, { }}},
                      {{1, { }}},
                      testCasesP1);

        std::vector<TestCase> runTheseCases;
        if(p_size == 1)
            runTheseCases = testCases;
        else if(stk::parallel_machine_rank(pm) == 0)
            runTheseCases = testCasesP0;
        else
            runTheseCases = testCasesP1;

        if (p_size <= 2) {
            for(const auto& test : runTheseCases)
                refine_mesh(test.filename, test. originalSideset, test.refinedSideset);
        }
      }

      void print_parent_child_sideset_connectivity(MeshWithSideset &inputMesh,
                                                   const ElemIdSideVector &parentSideSet,
                                                   const ElemIdSideVector &childSideSet)
      {
          std::ostringstream os;
          bool checkForFamilyTree = true;

          os << "On mesh(" << inputMesh.get_bulk().parallel_rank() << "): " << inputMesh.get_input_filename() << std::endl;
          percept::PerceptMesh &eMesh = inputMesh.get_mesh();

          for(const auto &entry : parentSideSet)
          {
              stk::mesh::Entity parent = eMesh.get_bulk_data()->get_entity(stk::topology::ELEMENT_RANK, entry.elem_id);

              if(eMesh.get_bulk_data()->is_valid(parent))
              {
                  os << "  For element: " << eMesh.get_bulk_data()->identifier(parent) << std::endl;

                  for(unsigned i=0; i<childSideSet.size(); ++i)
                  {
                      stk::mesh::Entity child = eMesh.get_bulk_data()->get_entity(stk::topology::ELEMENT_RANK, childSideSet[i].elem_id);
                      stk::mesh::Entity parentOfChild = eMesh.getParent(child, checkForFamilyTree);

                      if(parent == parentOfChild)
                          os << "    (child,ordinal) : (" << childSideSet[i].elem_id << "," << childSideSet[i].side_ordinal << ")" << std::endl;
                  }
              }
          }

          std::cerr << os.str() << std::endl;
      }

      ElemIdSideVector get_refined_child_sideset(MeshWithSideset &inputMesh,
                                                 const ElemIdSideVector &parentSideSet)
      {
          percept::PerceptMesh &eMesh = inputMesh.get_mesh();

          ElemIdSideVector childSideSet;
          const bool checkForFamilyTree = true;
          const bool onlyIfElementIsParentLeaf = false;

          for(const auto &entry : parentSideSet)
          {
              stk::mesh::Entity parent = eMesh.get_bulk_data()->get_entity(stk::topology::ELEMENT_RANK, entry.elem_id);

              EXPECT_TRUE(eMesh.get_bulk_data()->is_valid(parent));

              const percept::Elem::RefinementTopology* refTopo = get_refinement_topology(eMesh, parent);

              std::vector< std::pair<UInt,UInt> > refinedSideset = refTopo->get_children_on_ordinal(static_cast<UInt>(entry.side_ordinal));

              std::vector<stk::mesh::Entity> children;
              bool hasChildren = eMesh.getChildren( parent, children, checkForFamilyTree, onlyIfElementIsParentLeaf);

              EXPECT_EQ(hasChildren, (refinedSideset.size() > 0));

              if(hasChildren)
              {
                  EXPECT_GE(children.size(), refinedSideset.size());

                  for(unsigned i=0; i<refinedSideset.size(); ++i)
                  {
                      UInt childIndex = refinedSideset[i].first;
                      UInt faceIndex  = refinedSideset[i].second;

                      childSideSet.push_back({static_cast<int>(eMesh.get_bulk_data()->identifier(children[childIndex])), static_cast<int>(faceIndex)});
                  }
              }
          }

          std::sort(childSideSet.begin(), childSideSet.end(), ElemIdSideLess());

          return childSideSet;
      }

      SideSetIdAndElemIdSidesVector
      get_refined_sideset_from_parent(MeshWithSideset &inputMesh,
                                      const SideSetIdAndElemIdSidesVector& parentSidesetData)
      {
          SideSetIdAndElemIdSidesVector refinedSideset;

          refinedSideset.resize(parentSidesetData.size());

          for(unsigned i=0; i<parentSidesetData.size(); ++i)
          {
              refinedSideset[i].id = parentSidesetData[i].id;
              refinedSideset[i].sideSet = get_refined_child_sideset(inputMesh, parentSidesetData[i].sideSet);
              print_parent_child_sideset_connectivity(inputMesh, parentSidesetData[i].sideSet, refinedSideset[i].sideSet);
          }

          return refinedSideset;
      }

      void test_output_sideset(MeshWithSideset &inputMesh,const SideSetIdAndElemIdSidesVector &refinedSideset)
      {
        inputMesh.write_exodus_file();

        stk::mesh::MetaData meta2;
        stk::unit_test_util::sideset::BulkDataTester bulk2(meta2, MPI_COMM_WORLD);
        stk::unit_test_util::sideset::read_exo_file(bulk2, inputMesh.get_output_filename(), stk::unit_test_util::sideset::READ_ALREADY_DECOMPOSED);
        EXPECT_NO_THROW(compare_sidesets(inputMesh.get_output_filename(), bulk2, refinedSideset));
      }

      void test_refine_sideset(MeshWithSideset &inputMesh,
                               stk::mesh::PartVector &parts,
                               const SideSetIdAndElemIdSidesVector& originalSideset,
                               const SideSetIdAndElemIdSidesVector& expectedRefinedSideset)
      {
        stk::unit_test_util::sideset::BulkDataTester &bulk = inputMesh.get_bulk();
        stk::mesh::MetaData &meta = inputMesh.get_meta();

        bool removeOldElements = false;
        inputMesh.do_uniform_refinement(removeOldElements);

        bulk.initialize_face_adjacent_element_graph();

        stk::mesh::EntityVector faces;
        stk::mesh::get_selected_entities(meta.locally_owned_part(), bulk.buckets(meta.side_rank()), faces);

        ASSERT_EQ(0u, faces.size());
        EXPECT_TRUE(bulk.get_number_of_sidesets() == 0);

        SideSetIdAndElemIdSidesVector refinedSideset = get_refined_sideset_from_parent(inputMesh, originalSideset);

        for(const SideSetIdAndElemIdSides& sset : refinedSideset)
        {
            stk::mesh::SideSet sideSet = get_stk_side_set(bulk, sset.sideSet);

            stk::mesh::Part *surface_part = stk::unit_test_util::get_surface_part_with_id(meta, sset.id);

            ThrowRequire(nullptr != surface_part);
            bulk.create_sideset(stk::io::get_sideset_parent(*surface_part)) =  sideSet;
            bulk.create_side_entities(sideSet, parts);
        }

        EXPECT_NO_THROW(compare_sidesets(inputMesh.get_input_filename(), bulk, expectedRefinedSideset));

        stk::mesh::get_selected_entities(meta.locally_owned_part(), bulk.buckets(meta.side_rank()), faces);

        for(stk::mesh::Entity face : faces)
            ASSERT_EQ(2u, bulk.num_elements(face));

        test_output_sideset(inputMesh, refinedSideset);
      }

      void refine_sideset(const std::string &base_name,
                          const SideSetIdAndElemIdSidesVector& originalSideset,
                          const SideSetIdAndElemIdSidesVector& expectedRefinedSideset)
      {
        MeshWithSideset inputMesh(base_name);

        stk::mesh::MetaData &meta = inputMesh.get_meta();

        stk::mesh::PartVector parts;
        for(const SideSetIdAndElemIdSides &sideSet : originalSideset)
        {
            std::ostringstream os;
            os << "surface_" << sideSet.id;

            stk::mesh::Part &part = meta.declare_part(os.str(), meta.side_rank());
            meta.set_part_id(part, sideSet.id);
            stk::io::put_io_part_attribute(part);
            parts.push_back(&part);
        }

        inputMesh.load_mesh_and_fill_sideset_data();

        EXPECT_TRUE(inputMesh.get_bulk().get_number_of_sidesets() == 0);
        test_refine_sideset(inputMesh, parts, originalSideset, expectedRefinedSideset);
      }

      TEST(regr_stk_sideset_refine, get_refined_sideset)
      {
        stk::ParallelMachine pm = MPI_COMM_WORLD;

        const unsigned p_size = stk::parallel_machine_size(pm);

        std::vector<TestCase> testCases;
        add_test_case("AA",
                      {{1, {{ 1, 5}, { 2, 4}                                                      }}},
                      {{1, {{23, 5}, {24, 5}, {25, 5}, {26, 5}, {27, 4}, {28, 4}, {29, 4}, {30, 4}}}},
                      testCases);
        add_test_case("Ae",
                      {{1, {{ 1, 5}, { 2, 1}                                                      }}},
                      {{1, {{23, 5}, {24, 5}, {25, 5}, {26, 5}, {85, 1}, {86, 1}, {87, 1}, {88, 1}}}},
                      testCases);

        std::vector<TestCase> testCasesP0;
        add_test_case("AA",
                      {{1, {{ 1, 5}                           }}},
                      {{1, {{23, 5}, {24, 5}, {25, 5}, {26, 5}}}},
                      testCasesP0);
        add_test_case("Ae",
                      {{1, {{ 1, 5}                           }}},
                      {{1, {{23, 5}, {24, 5}, {25, 5}, {26, 5}}}},
                      testCasesP0);

        std::vector<TestCase> testCasesP1;
        add_test_case("AA",
                      {{1, {{ 2, 4}                               }}},
                      {{1, {{27,  4}, {28,  4}, {29,  4}, {30,  4}}}},
                      testCasesP1);
        add_test_case("Ae",
                      {{1, {{  2, 1}                              }}},
                      {{1, {{139, 1}, {140, 1}, {141, 1}, {142, 1}}}},
                      testCasesP1);

        std::vector<TestCase> runTheseCases;
        if(p_size == 1)
            runTheseCases = testCases;
        else if(stk::parallel_machine_rank(pm) == 0)
            runTheseCases = testCasesP0;
        else
            runTheseCases = testCasesP1;

        if (p_size <= 2) {
            for(const auto& test : runTheseCases)
                refine_sideset(test.filename, test.originalSideset, test.refinedSideset);
        }
      }

      stk::mesh::EntityVector get_entities(const stk::mesh::BulkData &bulk, stk::topology::rank_t rank, const stk::mesh::EntityIdVector &ids)
      {
          stk::mesh::EntityVector entities;
          for(stk::mesh::EntityId id : ids)
          {
              stk::mesh::Entity entity = bulk.get_entity(rank, id);
              EXPECT_TRUE(bulk.is_valid(entity));
              entities.push_back(entity);
          }
          return entities;
      }

      std::vector< PseudoEntity > get_pseudo_side_entities(const stk::mesh::BulkData &bulk, const stk::mesh::EntityVector &sides)
      {
          std::vector< PseudoEntity > pseudoSides;
          for(const stk::mesh::Entity side : sides)
          {
              ThrowRequire(bulk.entity_rank(side) == bulk.mesh_meta_data().side_rank());

              const stk::mesh::Entity *nodes = bulk.begin_nodes(side);
              pseudoSides.push_back( stk::mesh::EntityVector(nodes, nodes + bulk.num_nodes(side)) );
          }
          return pseudoSides;
      }

      void fill_sideset_from_elements_and_pseudo_side(const stk::mesh::BulkData &bulk,
                                                      const stk::mesh::EntityVector &elements,
                                                      const PseudoEntity &nodes,
                                                      stk::mesh::SideSet &sideset)
      {
          for(const stk::mesh::Entity element : elements)
          {
              stk::mesh::OrdinalAndPermutation ordinalAndPermutation = stk::mesh::get_ordinal_and_permutation(bulk,
                                                                                element,
                                                                                bulk.mesh_meta_data().side_rank(),
                                                                                nodes);

              if(stk::mesh::INVALID_CONNECTIVITY_ORDINAL != ordinalAndPermutation.first)
              {
                  sideset.add({element, ordinalAndPermutation.first});
                  break;
              }
          }
      }

      stk::mesh::SideSet get_sideset_from_elements_and_pseudo_sides(const stk::mesh::BulkData &bulk,
                                                                    const stk::mesh::EntityVector &elements,
                                                                    const std::vector< PseudoEntity > &pseudoSides)

      {
          stk::mesh::SideSet sideset;
          for(const PseudoEntity &pseudoSide : pseudoSides)
              fill_sideset_from_elements_and_pseudo_side(bulk, elements, pseudoSide, sideset);

          return sideset;
      }

      void test_parent_info(const stk::mesh::BulkData &bulk,
                            const stk::mesh::Entity &parentElement,
                            const stk::mesh::Entity &parentSide,
                            const stk::mesh::ConnectivityOrdinal parentSideOrdinal)
      {
          unsigned ordinalIndex = bulk.find_ordinal(parentElement, bulk.mesh_meta_data().side_rank(), parentSideOrdinal);
          unsigned numConnectivity = bulk.num_connectivity(parentElement, bulk.mesh_meta_data().side_rank());
          EXPECT_TRUE(ordinalIndex < numConnectivity);
          EXPECT_TRUE(parentSide == bulk.begin(parentElement, bulk.mesh_meta_data().side_rank())[ordinalIndex]);
      }

      stk::mesh::SideSet get_refined_sideset_from_parent_child_info(const stk::mesh::BulkData &bulk,
                                                                    const stk::mesh::Entity &parentElement,
                                                                    const stk::mesh::EntityVector &childElements,
                                                                    const stk::mesh::Entity & parentSide,
                                                                    const stk::mesh::EntityVector &childSides,
                                                                    const stk::mesh::ConnectivityOrdinal parentSideOrdinal)
      {
          test_parent_info(bulk, parentElement, parentSide, parentSideOrdinal);
          return get_sideset_from_elements_and_pseudo_sides(bulk, childElements, get_pseudo_side_entities(bulk, childSides));
      }

      void test_refined_sideset_from_parent_child_info(const std::string &inputFileName,
                                                       const stk::mesh::ConnectivityOrdinal &parentSideOrdinal,
                                                       const stk::mesh::EntityId &parentElementId,
                                                       const stk::mesh::EntityIdVector &childElementIds,
                                                       const stk::mesh::EntityId &parentSideId,
                                                       const stk::mesh::EntityIdVector &childSideIds,
                                                       const ElemIdSideVector &expectedElemIdSides)
      {
          MeshWithSideset inputMesh(inputFileName);
          inputMesh.load_mesh_and_fill_sideset_data();

          stk::unit_test_util::sideset::BulkDataTester &bulk = inputMesh.get_bulk();
          stk::mesh::MetaData &meta = inputMesh.get_meta();

          inputMesh.do_uniform_refinement(false);

          stk::mesh::Entity parentElement = bulk.get_entity(stk::topology::ELEMENT_RANK, parentElementId);
          stk::mesh::EntityVector childElements = get_entities(bulk, stk::topology::ELEMENT_RANK, childElementIds);

          stk::mesh::Entity parentSide = bulk.get_entity(meta.side_rank(), parentSideId);
          stk::mesh::EntityVector childSides = get_entities(bulk, meta.side_rank(), childSideIds);

          stk::mesh::SideSet sideSet = get_refined_sideset_from_parent_child_info(bulk, parentElement, childElements, parentSide, childSides, parentSideOrdinal);

          ASSERT_EQ(expectedElemIdSides.size(), sideSet.size()) << "for file: " << inputFileName;

          for(size_t i=0;i<sideSet.size();++i)
          {
              EXPECT_EQ(static_cast<stk::mesh::EntityId>(expectedElemIdSides[i].elem_id), bulk.identifier(sideSet[i].element)) << "for file: " << inputFileName;
              EXPECT_EQ(expectedElemIdSides[i].side_ordinal, sideSet[i].side) << "for file: " << inputFileName;
          }
      }

      TEST(regr_stk_sideset_refine, refined_child_ordinal)
      {
        stk::ParallelMachine pm = MPI_COMM_WORLD;

        const unsigned p_size = stk::parallel_machine_size(pm);

        if(1 != p_size) return;

        test_refined_sideset_from_parent_child_info("ARA",
                                                    static_cast<stk::mesh::ConnectivityOrdinal>(4),
                                                    2u,
                                                    {27, 28, 29, 30, 31, 32, 33, 34},
                                                    16u,
                                                    {17, 18, 19, 20},
                                                    {{27, 4}, {28, 4}, {29, 4}, {30, 4}});

        test_refined_sideset_from_parent_child_info("ALA",
                                                    static_cast<stk::mesh::ConnectivityOrdinal>(5),
                                                    1u,
                                                    {19, 20, 21, 22, 23, 24, 25, 26},
                                                    16u,
                                                    {17, 18, 19, 20},
                                                    {{23, 5}, {24, 5}, {25, 5}, {26, 5}});

        test_refined_sideset_from_parent_child_info("ADA",
                                                    static_cast<stk::mesh::ConnectivityOrdinal>(4),
                                                    2u,
                                                    {27, 28, 29, 30, 31, 32, 33, 34},
                                                    16u,
                                                    {17, 18, 19, 20},
                                                    {{27, 4}, {28, 4}, {29, 4}, {30, 4}});

        test_refined_sideset_from_parent_child_info("ADA",
                                                    static_cast<stk::mesh::ConnectivityOrdinal>(5),
                                                    1u,
                                                    {19, 20, 21, 22, 23, 24, 25, 26},
                                                    16u,
                                                    {17, 18, 19, 20},
                                                    {{23, 5}, {24, 5}, {25, 5}, {26, 5}});

//        ARe and ALe still fail due to percept flipping the connected ordinal on the elements (including parent)
//        test_refined_sideset_from_parent_child_info("ARe",
//                                                    static_cast<stk::mesh::ConnectivityOrdinal>(1),
//                                                    2u,
//                                                    {85, 86, 87, 88},
//                                                    16u,
//                                                    {17, 18, 19, 20},
//                                                    {{85, 1}, {86, 1}, {87, 1}, {88, 1}});
//        test_refined_sideset_from_parent_child_info("ALe",
//                                                    static_cast<stk::mesh::ConnectivityOrdinal>(5),
//                                                    1u,
//                                                    {19, 20, 21, 22, 23, 24, 25, 26},
//                                                    16u,
//                                                    {17, 18, 19, 20},
//                                                    {{23, 5}, {24, 5}, {25, 5}, {26, 5}});
      }

    }//    namespace regression_test
  }//  namespace percept


#include <stdint.h>
#include <iostream>
#include <Eigen/Dense>

// CUDA runtime
#include <cuda_runtime.h>
// Utilities and system includes
//#include <helper_functions.h>
#include <nvidia/helper_cuda.h>

#include <timer.hpp>

using namespace Eigen;
using namespace std;

extern void reduction(float *h_mu, float *d_mu,
    float *h_dbg, float *d_dbg, int selection);

extern void reduction(double *h_mu, double *d_mu,
    double *h_dbg, double *d_dbg, int selection);

int main(int argc, char ** argv)
{
  Matrix<float,1,256> dbg;
  Matrix<float,6,4> mu;
  float *d_mu,*d_dbg;
  checkCudaErrors(cudaMalloc((void **)&d_mu, 6*4*sizeof(float)));
  checkCudaErrors(cudaMalloc((void **)&d_dbg, 256*sizeof(float)));

  Timer t0;
  for(uint32_t i=0; i< 1000; ++i)
    reduction(mu.data(),d_mu,dbg.data(),d_dbg,0);
  t0.toctic("100 times old");
  for(uint32_t i=0; i< 1000; ++i)
    reduction(mu.data(),d_mu,dbg.data(),d_dbg,1);
  t0.toctic("100 times new");
  for(uint32_t i=0; i< 1000; ++i)
    reduction(mu.data(),d_mu,dbg.data(),d_dbg,2);
  t0.toctic("100 times newNew");
  for(uint32_t i=0; i< 1000; ++i)
    reduction(mu.data(),d_mu,dbg.data(),d_dbg,4);
  t0.toctic("100 times oldOptimizedMemLayout");
  for(uint32_t i=0; i< 1000; ++i)
    reduction(mu.data(),d_mu,dbg.data(),d_dbg,3);
  t0.toctic("100 times oldOptimized");
  cout<<mu<<endl<<dbg<<endl;

  checkCudaErrors(cudaFree(d_mu));
  checkCudaErrors(cudaFree(d_dbg));

  Matrix<double,1,256> dbgd;
  Matrix<double,6,4> mud;
  double *d_mud,*d_dbgd;
  checkCudaErrors(cudaMalloc((void **)&d_mud, 6*4*sizeof(double)));
  checkCudaErrors(cudaMalloc((void **)&d_dbgd, 256*sizeof(double)));

  t0.tic();
  for(uint32_t i=0; i< 1000; ++i)
    reduction(mud.data(),d_mud,dbgd.data(),d_dbgd,0);
  t0.toctic("100 times old");
  for(uint32_t i=0; i< 1000; ++i)
    reduction(mud.data(),d_mud,dbgd.data(),d_dbgd,1);
  t0.toctic("100 times new");
  for(uint32_t i=0; i< 1000; ++i)
    reduction(mud.data(),d_mud,dbgd.data(),d_dbgd,2);
  t0.toctic("100 times newNew");
  for(uint32_t i=0; i< 1000; ++i)
    reduction(mud.data(),d_mud,dbgd.data(),d_dbgd,4);
  t0.toctic("100 times oldOptimizedMemLayout");
  for(uint32_t i=0; i< 1000; ++i)
    reduction(mud.data(),d_mud,dbgd.data(),d_dbgd,3);
  t0.toctic("100 times oldOptimized");
  cout<<mud<<endl<<dbgd<<endl;

  checkCudaErrors(cudaFree(d_mud));
  checkCudaErrors(cudaFree(d_dbgd));

  return 0;
}


#include <iostream>
#include <memory>

#include <Eigen/Core>

using namespace std;

struct Foo {
  Foo(int v) : value(v) {}

  int value = 0;
};

typedef Eigen::Matrix<std::unique_ptr<Foo>, Eigen::Dynamic, Eigen::Dynamic> CellMatrix;

int main() {
  CellMatrix c(3, 5);
  c(0, 0) = std::make_unique<Foo>(8);
  c(1, 0) = std::make_unique<Foo>(6);
  c(0, 2) = std::make_unique<Foo>(7);
  c(2, 4) = std::make_unique<Foo>(5);
  c(2, 1) = std::make_unique<Foo>(3);
  c(1, 3) = std::make_unique<Foo>(0);
  c(1, 2) = std::make_unique<Foo>(9);

  cout << "cells:\n" << endl;
  for (int row = 0; row < 3; row++) {
    for (int col = 0; col < 5; col++) {
      const auto& ptr = c(row, col);
      if (ptr) {
        cout << ptr->value << " ";
      } else {
        cout << "- ";
      }

    }
    cout << endl;
  }

  return 0;
}


#ifndef _LIBKAMERIS_UTILS_TYPES_
#define _LIBKAMERIS_UTILS_TYPES_

#include <cstdint>
#include <type_traits>
#include <utility>

#include <boost/mpl/arithmetic.hpp>
#include <boost/mpl/at.hpp>
#include <boost/mpl/back_inserter.hpp>
#include <boost/mpl/map.hpp>
#include <boost/mpl/pair.hpp>
#include <boost/mpl/range_c.hpp>
#include <boost/mpl/size.hpp>
#include <boost/mpl/transform.hpp>
#include <boost/mpl/vector.hpp>

namespace kameris {
	namespace types_impl {
		using extra_precision_map = boost::mpl::map<
			//signed
			boost::mpl::pair<int8_t, int16_t>, //
			boost::mpl::pair<int16_t, int32_t>, //
			boost::mpl::pair<int32_t, int64_t>, //
			boost::mpl::pair<int64_t, int64_t>, //
			//unsigned
			boost::mpl::pair<uint8_t, uint16_t>, //
			boost::mpl::pair<uint16_t, uint32_t>, //
			boost::mpl::pair<uint32_t, uint64_t>, //
			boost::mpl::pair<uint64_t, uint64_t>, //
			//float
			boost::mpl::pair<float, double>, //
			boost::mpl::pair<double, double>>;

		//ugly hack to work around a MSVC2015 bug
		struct vect_element_impl {
			template <typename T>
			auto operator()(const T &x) -> decltype(x[0]) {
				return x[0];
			}
		};

		//ugly hack to work around a MSVC2015 bug
		struct sparse_vect_element_impl {
			template <typename T>
			auto operator()(const T &x) -> decltype(x.begin()->second) {
				return x.begin()->second;
			}
		};

		template <typename Vect>
		using mpl_all_pairs_indexes = typename boost::mpl::transform<
			boost::mpl::range_c<size_t, 0, boost::mpl::size<Vect>::value * boost::mpl::size<Vect>::value>,
			typename boost::mpl::pair< //
				boost::mpl::divides<boost::mpl::_1, boost::mpl::size<Vect>>,
				boost::mpl::modulus<boost::mpl::_1, boost::mpl::size<Vect>>>,
			boost::mpl::back_inserter<boost::mpl::vector<>>>::type;
	}

	template <typename T>
	using extra_precision_t = typename boost::mpl::at<types_impl::extra_precision_map, T>::type;

	template <typename Vect>
	using vect_element_t = std::decay_t<typename std::result_of<types_impl::vect_element_impl(Vect)>::type>;

	template <typename... Vect>
	using common_vect_element_t = std::common_type_t<vect_element_t<Vect>...>;

	template <typename Vect>
	using sparse_vect_element_t =
		std::decay_t<typename std::result_of<types_impl::sparse_vect_element_impl(Vect)>::type>;

	template <typename... Vect>
	using common_sparse_vect_element_t = std::common_type_t<sparse_vect_element_t<Vect>...>;

	template <template <typename, typename> class T, typename Vect>
	using mpl_all_pairs = typename boost::mpl::transform<types_impl::mpl_all_pairs_indexes<Vect>,
		T<boost::mpl::at<Vect, boost::mpl::first<boost::mpl::_1>>,
			boost::mpl::at<Vect, boost::mpl::second<boost::mpl::_1>>>>::type;
}

#endif


#ifndef SAM_VAST_NETFLOW_GENERATORS_HPP
#define SAM_VAST_NETFLOW_GENERATORS_HPP

#include <boost/lexical_cast.hpp>
#include <cstdlib>
#include <chrono>
#include <random>

/*************************************************************************/
// Utilities for creating artificial netflow data for testing purposes.
/*************************************************************************/

namespace sam {

/**
 * Generates strings that look like ip4 addresses.
 */
std::string generateRandomIp()
{
  std::string result = "";
  result = result + boost::lexical_cast<std::string>(rand() % 255) + ".";
  result = result + boost::lexical_cast<std::string>(rand() % 255) + ".";
  result = result + boost::lexical_cast<std::string>(rand() % 255) + ".";
  result = result + boost::lexical_cast<std::string>(rand() % 255);
  return result;
}

/**
 * Generates a random port in the range 0-65355
 */
int generateRandomPort()
{
  return rand() % 65355;
}

/**
 * Provides a string that has the current seconds since epoch.  It
 * is a float in a string form.
 */
std::string secondsSinceEpoch()
{
  using namespace std::chrono;
  auto timestamp = duration_cast<milliseconds>(
    system_clock::now().time_since_epoch());
  double doubleTime = static_cast<double>(timestamp.count()) / 1000;
  return boost::lexical_cast<std::string>(doubleTime);
}

/**
 * The abstract netflow generator.
 */
class AbstractVastNetflowGenerator {
public:
  AbstractVastNetflowGenerator() {}
  virtual ~AbstractVastNetflowGenerator() {}

  /**
   * Generates a netflow formatted as a string.
   * Unless this function is overridden The time is taken from the system clock.
   * \return Returns a netflow as a string.
   */
  virtual std::string generate();

  /**
   * This function allows you to specify the time of the generated netflow.
   * \param epochTime The time in seconds since the epoch.
   */
  virtual std::string generate(double epochTime) = 0;
};

inline
std::string AbstractVastNetflowGenerator::generate()
{
  double epochTime = boost::lexical_cast<double>(secondsSinceEpoch());
  return generate(epochTime);
}

/**
 * This creates a watering hole attack signature.  There are n clients
 * that access m servers.  Generally n >> m.  When a netflow is created,
 * the client and server IPs are uniformly randomly selected from the
 * client and server IP pools.  Also, a controller IP is designated.  A
 * target machine is selected (the first IP), and it communicates with
 * the controller a specified number of times at the end of the simulation.
 */
class WateringHoleGenerator : public AbstractVastNetflowGenerator
{
private:
  size_t numClients; ///> How many client ip's.
  size_t numServers; ///> How many server ip's.
  std::uniform_int_distribution<size_t> clientDist; ///> client distribution
  std::uniform_int_distribution<size_t> serverDist; ///> Server distribution
  std::mt19937 myRand; ///> random engine

public:

  /**
   * Constructor.
   * \param numClients How many client Ip's.
   * \param numServers How many server Ip's.
   */
  WateringHoleGenerator(size_t numClients, size_t numServers);

  std::string generate() { return AbstractVastNetflowGenerator::generate(); }

  /**
   * This function allows you to specify the time of the generated netflow.
   * \param epochTime The time in seconds since the epoch.
   */
  std::string generate(double epochTime);

  /**
   * This creates message from node0 to a server, providing the first
   * message of the pattern.
   */
  std::string generateInfection(double epochTime);

  /**
   * The regular generate string creates "benign traffic."  This creates
   * messages from node0 to the controller, thus finishing the
   * watering hole pattern.
   */
  std::string generateControlMessage(double epochTime);

};

/**
 * Constructor.
 */
WateringHoleGenerator::WateringHoleGenerator(size_t numClients,
                                             size_t numServers)
{
  this->numClients = numClients;
  this->numServers = numServers;
  std::random_device rd;
  myRand = std::mt19937(rd());
  // We reserve out node 0.  Node 0 only creates traffic with the
  // generateInfection and generateControlMessage methods.
  clientDist = std::uniform_int_distribution<size_t>(1, numClients - 1);
  serverDist = std::uniform_int_distribution<size_t>(0, numServers - 1);
}

std::string WateringHoleGenerator::generate(double epochTime)
{
  std::string sourceIp = boost::lexical_cast<std::string>(clientDist(myRand));
  std::string destIp = boost::lexical_cast<std::string>(serverDist(myRand));

  std::string result;
  result = boost::lexical_cast<std::string>(epochTime) + ",";
  result = result + "parseDate,dateTimeStr,ipLayerProtocol,";
  result = result + "ipLayerProtocolCode," + sourceIp + ",";
  result = result + destIp + ",";
  result = result + boost::lexical_cast<std::string>(generateRandomPort());
  result = result + ",";
  result = result + boost::lexical_cast<std::string>(generateRandomPort());
  result = result + ",1,1,1,";
  result = result + "1,1,";
  result = result + "1,1,";
  result = result + "1,1,";
  result = result + "1";

  return result;
}

std::string WateringHoleGenerator::generateInfection(double epochTime)
{
  std::string sourceIp = "0";
  std::string destIp = "1";

  std::string result;
  result = boost::lexical_cast<std::string>(epochTime) + ",";
  result = result + "parseDate,dateTimeStr,ipLayerProtocol,";
  result = result + "ipLayerProtocolCode," + sourceIp + ",";
  result = result + destIp + ",";
  result = result + boost::lexical_cast<std::string>(generateRandomPort());
  result = result + ",";
  result = result + boost::lexical_cast<std::string>(generateRandomPort());
  result = result + ",1,1,1,";
  result = result + "1,1,";
  result = result + "1,1,";
  result = result + "1,1,";
  result = result + "1";

  return result;

}

std::string WateringHoleGenerator::generateControlMessage(double epochTime)
{
  std::string sourceIp = "0";
  std::string destIp = "controller";

  std::string result;
  result = boost::lexical_cast<std::string>(epochTime) + ",";
  result = result + "parseDate,dateTimeStr,ipLayerProtocol,";
  result = result + "ipLayerProtocolCode," + sourceIp + ",";
  result = result + destIp + ",";
  result = result + boost::lexical_cast<std::string>(generateRandomPort());
  result = result + ",";
  result = result + boost::lexical_cast<std::string>(generateRandomPort());
  result = result + ",1,1,1,";
  result = result + "1,1,";
  result = result + "1,1,";
  result = result + "1,1,";
  result = result + "1";

  return result;

}

/**
 * Evenly spreads out the traffic to one IP along n destination ports.
 * The strings generated are in VAST csv form.  There is no SamGenerateId
 * and no label.  The source ips are from randomly generated Ips.
 */
class UniformDestPort : public AbstractVastNetflowGenerator
{
private:
  std::string destIp; ///> The single destination
  int numPorts; ///> The number of destination ports.
  int iter = 0;
  int* ports; ///> An array of ports

public:
  UniformDestPort(std::string destIp, int numPorts) :
    AbstractVastNetflowGenerator()
  {
    this->destIp = destIp;
    this->numPorts = numPorts;
    ports = new int[numPorts];
    for (int i = 0; i < numPorts; i++) ports[i] = i + 1;
  }

  ~UniformDestPort() {
    delete[] ports;
  }

  std::string generate() {
    return AbstractVastNetflowGenerator::generate();
  }

  std::string generate(double epochTime)
  {
    std::string result;
    result = boost::lexical_cast<std::string>(epochTime) + ",";
    result = result + "parseDate,dateTimeStr,ipLayerProtocol,";
    result = result + "ipLayerProtocolCode," + generateRandomIp() + ",";
    result = result + destIp + ",";
    result = result + boost::lexical_cast<std::string>(generateRandomPort());
    result = result + ",";

    // Get the port number for this iteration
    result = result + boost::lexical_cast<std::string>(ports[iter]) + ",";
    result = result + "1,1,1,";
    result = result + "1,1,";
    result = result + "1,1,";
    result = result + "1,1,";
    result = result + "1";

    // We cycle through ports each time a netflow is generated.
    iter = (iter + 1) % numPorts;

    return result;
  }

private:
};

/**
 * Creates completely random source and destination ip addresses
 */
class RandomGenerator : public AbstractVastNetflowGenerator
{
private:

public:
  /**
   * Constructor.
   */
  RandomGenerator() {}

  /**
   * Destructor.
   */
  ~RandomGenerator() {}

  std::string generate() {
    return AbstractVastNetflowGenerator::generate();
  }

  std::string generate(double epochTime) {
    std::string result;
    result = boost::lexical_cast<std::string>(epochTime) + ",";
    result = result + "parseDate,dateTimeStr,ipLayerProtocol,";
    result = result + "ipLayerProtocolCode," + generateRandomIp() + ",";
    result = result + generateRandomIp() + ",";
    result = result + boost::lexical_cast<std::string>(generateRandomPort());
    result = result + ",";

    // Get the port number for this iteration
    result = result + boost::lexical_cast<std::string>(generateRandomPort());
    result = result + ",";
    result = result + "1,1,1,";
    result = result + "1,1,";
    result = result + "1,1,";
    result = result + "1,1,";
    result = result + "1";
    return result;
  }
};

/**
 * Chooses at random source and destination from a small set of n vertices.
 * The source and target Ips have the form node<x>, where <x> is from 0 to
 * n-1.  Right now the class allows edges that have target and destination
 * as the same.
 */
class RandomPoolGenerator : public AbstractVastNetflowGenerator
{
private:
  /// The number of vertices.
  size_t numVertices;

  /// Even with different random seeds, still seeing similar output.
  /// Adding a little offset based on the random seed.
  double timeOffset;

  std::random_device rd;
  std::mt19937 myRand;
  std::uniform_int_distribution<size_t> dist;

public:
  /**
   * Constructor.
   */
  RandomPoolGenerator(size_t n) {
    this->numVertices = n;
    myRand = std::mt19937(rd());
    dist = std::uniform_int_distribution<size_t>(0, n-1);
  }

  /**
   * Destructor.
   */
  ~RandomPoolGenerator()
  {
  }

  /**
   * Uses AbstractVastNetflowGenerator::generate, which calls generate(epochTime)
   * with the current clock time.
   */
  std::string generate() {
    return AbstractVastNetflowGenerator::generate();
  }

  std::string generate(double epochTime) {

    epochTime = epochTime + timeOffset;

    size_t sourceInt = dist(myRand);
    size_t targetInt = dist(myRand);

    while (targetInt == sourceInt) {
      targetInt = rand() % numVertices;
    }

    std::string sourceStr = "node" +
      boost::lexical_cast<std::string>(sourceInt);
    std::string targetStr = "node" +
      boost::lexical_cast<std::string>(targetInt);

    std::string result;
    result = boost::lexical_cast<std::string>(epochTime) + ",";
    result = result + "parseDate,dateTimeStr,ipLayerProtocol,";
    result = result + "ipLayerProtocolCode," + sourceStr + ",";
    result = result + targetStr + ",";
    result = result + boost::lexical_cast<std::string>(generateRandomPort());
    result = result + ",";

    // Get the port number for this iteration
    result = result + boost::lexical_cast<std::string>(generateRandomPort());
    result = result + ",";
    result = result + "1,1,1,";
    result = result + "1,1,";
    result = result + "1,1,";
    result = result + "1,1,";
    result = result + "1";
    return result;
  }
};

/**
 * This generates traffic between one pair of one client and one server.
 * You can specify mean and deviation for a normal distribution for the
 * payload size for both the client and the server.
 */
class OnePairSizeDist : public AbstractVastNetflowGenerator
{
private:
  std::random_device rd;

  /// This is a particular type of random generator.  Mersenne Twister with
  /// a state size of 19937 bits.
  std::mt19937 gen;

  /// The destination IP for all generated netflows.
  std::string destIp;

  /// The source IP for all generate netflows.
  std::string sourceIp;

  double meanDestFlow;
  double meanSourceFlow;
  double devDestFlow;
  double devSourceFlow;
  std::normal_distribution<double> destDist;
  std::normal_distribution<double> sourceDist;

public:

  /**
   * Constructor.
   * \param destIp The destination IP as a string.
   * \param sourceIp The source IP as a string.
   * \param meanDestFlow The mean flow size of payload from destination/server.
   * \param meanSourceFlow The mean flow size of payload from source/client.
   * \param devDestFlow The standard deviation of flow size from
   *                    destination/server.
   * \param devSource The standard deviation of flow size from
   *                  source/client.
   */
  OnePairSizeDist(std::string sourceIp, std::string destIp,
                      double meanDestFlow, double meanSourceFlow,
                      double devDestFlow, double devSourceFlow)
  {
    gen = std::mt19937(rd());
    this->destIp = destIp;
    this->sourceIp = sourceIp;
    this->meanDestFlow = meanDestFlow;
    this->meanSourceFlow = meanSourceFlow;
    destDist = std::normal_distribution<double>(meanDestFlow, devDestFlow);
    sourceDist = std::normal_distribution<double>(meanSourceFlow,
                                                  devSourceFlow);
  }

  ~OnePairSizeDist() {
  }

  std::string generate() {
    return AbstractVastNetflowGenerator::generate();
  }

  std::string generate(double epochTime)
  {
    int destPayloadBytes = static_cast<int>(destDist(gen));
    int sourcePayloadBytes = static_cast<int>(sourceDist(gen));
    int destTotalBytes = destPayloadBytes + 10;
    int sourceTotalBytes = sourcePayloadBytes + 10;
    std::string result;
    result = boost::lexical_cast<std::string>(epochTime) + ",";
    result = result + "parseDate,dateTimeStr,ipLayerProtocol,";
    result = result + "ipLayerProtocolCode," + sourceIp + ",";
    result = result + destIp + ",";
    result = result + boost::lexical_cast<std::string>(generateRandomPort());
    result = result + ",";
    result = result + "1000,"; // port
    result = result + "1,1,1,"; // MoreFragments, CountFragments,DurationSeconds
    result = result + boost::lexical_cast<std::string>(sourcePayloadBytes)+",";
    result = result + boost::lexical_cast<std::string>(destPayloadBytes)+",";
    result = result + boost::lexical_cast<std::string>(sourceTotalBytes)+",";
    result = result + boost::lexical_cast<std::string>(destTotalBytes) +",";
    result = result + "1,1,";
    result = result + "1";
    return result;
  }
};

}

#endif


/* Copyright 2017-2018 by Yifei Zheng
 *
 * 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.
 *
 * This file implements routines that actually resolve the solving order.
 */

#include <utility>
#include <algorithm>
#include <cstddef>
#include "resolver.hpp"
#include <boost/container/flat_set.hpp>

namespace std {
template<>
struct less<support::variable_designation> {
    bool operator()(const support::variable_designation& l, const support::variable_designation& r) const {
        return l.id < r.id || (l.id == r.id && l.order < r.order);
    }
};
}

namespace codegen {

resolved_sequence RuleResolver::get() {
    return std::move(sln);
}

bool RuleResolver::solve() {
    bool first = true;
    // I strongly belive this should be cached.
    std::size_t unresolved = std::count_if(pool.begin(), pool.end(),
                                           [](variable v) { return v.needs_update(); });
    while (unresolved) {
        switch (alg_consistent(first)) {
        case SUCCEEDED_AND_UPDATED:
            break;
        case SUCCEEDED_NONE_UPDATED:
            // It might be possible that we have fewer equations than variables
            // but still are able to solve the system. Here this is only for
            // programmatical safety that unresolved shall not exceed size
            // FIXME
            if (pack.size() < unresolved)
                return false;
            for (auto& attempt : powerset(pack.size(), unresolved)) {
                boost::container::flat_set<support::variable_designation> agg_unknowns;
                agg_unknowns.reserve(attempt.size());
                for (auto i : attempt) {
                    for (const auto& v : pack[i]) {
                        if (pool[v].needs_update()) {
                            agg_unknowns.insert(v);
                        }
                    }
                }
                if (agg_unknowns.size() == attempt.size()) {
                    for (const auto& var : agg_unknowns)
                        pool[var].update();
                    std::unique_ptr<std::size_t[]> rules = std::make_unique<std::size_t[]>(attempt.size());
                    std::size_t* end = rules.get() + attempt.size();
                    for (std::size_t i = 0; i != attempt.size(); ++i)
                        rules[i] = attempt[i];
                    std::unique_ptr<support::variable_designation[]> vars =
                        std::make_unique<support::variable_designation[]>(agg_unknowns.size());
                    std::copy(agg_unknowns.cbegin(), agg_unknowns.cend(), vars.get());
                    sln.push_back(step{std::move(rules), end, std::move(vars)});
                    break;
                }
            }
            break;
        case FAILED:
            return false;
        }
        first = false;
        std::size_t new_unresolved = std::count_if(pool.begin(), pool.end(),
                                   [](variable v) { return v.needs_update(); });
        if (new_unresolved != unresolved)
            unresolved = new_unresolved;
        else
            return false;
    }
    return true;
}

int RuleResolver::broadcast(const support::base_t& base) noexcept {
    bool updated = false;
    for (variable& var : pool.at(base))
        if (var.update())
            updated = true;
        else
            return FAILED;
    return updated ? SUCCEEDED_AND_UPDATED
                   : SUCCEEDED_NONE_UPDATED;
}

int RuleResolver::alg_consistent(bool use_start) {
    // save the current state, or the state might change while updating
    std::vector<std::pair<Rule*, support::variable_designation>> to_be_updated;
    for (auto& rule : pack) {
        if (rule.enabled) {
            const support::variable_designation* unknown = nullptr;
            for (const auto& var : rule) {
                const variable& state = pool[var];
                if (state.needs_update() && !(use_start && state.initialized)) {
                    if (unknown)
                        goto next_rule;
                    else
                        unknown = &var;
                }
            }
            to_be_updated.emplace_back(&rule, *unknown);
        }
    next_rule:;
    }

    if (to_be_updated.empty())
        return SUCCEEDED_NONE_UPDATED;

    for (const auto& update : to_be_updated) {
        int ret = broadcast(update.second.id);
        if (ret == FAILED)
             return FAILED;
    }

    for (const auto& rule : to_be_updated) {
        rule.first->enabled = false;
        std::unique_ptr<std::size_t[]> rules = std::make_unique<std::size_t[]>(1);
        std::unique_ptr<support::variable_designation[]> vars = std::make_unique<support::variable_designation[]>(1);
        rules[0] = rule.first - &*pack.begin();
        vars[0] = rule.second;
        std::size_t* end = rules.get() + 1;
        sln.push_back(step{ std::move(rules), end, std::move(vars) });
    }

    return SUCCEEDED_AND_UPDATED;
}

}


//Copyright (c) 2008-2016 Emil Dotchevski and Reverge Studios, Inc.

//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 UUID_8AC84A2217C411E0A7AF3A1BDFD72085
#define UUID_8AC84A2217C411E0A7AF3A1BDFD72085

#include <boost/qvm/inline.hpp>
#include <boost/qvm/quat_traits.hpp>
#include <boost/qvm/deduce_vec.hpp>
#include <boost/qvm/static_assert.hpp>
#include <boost/qvm/enable_if.hpp>

namespace
boost
    {
    namespace
    qvm
        {
        ////////////////////////////////////////////////

        namespace
        qvm_detail
            {
            template <class Q>
            struct
            quat_v_
                {
                template <class R>
                operator R() const
                    {
                    R r;
                    assign(r,*this);
                    return r;
                    }

                private:

                quat_v_( quat_v_ const & );
                quat_v_ const & operator=( quat_v_ const & );
                ~quat_v_();
                };
            }

        template <class V>
        struct vec_traits;

        template <class Q>
        struct
        vec_traits< qvm_detail::quat_v_<Q> >
            {
            typedef qvm_detail::quat_v_<Q> this_vector;
            typedef typename quat_traits<Q>::scalar_type scalar_type;
            static int const dim=3;

            template <int I>
            BOOST_QVM_INLINE_CRITICAL
            static
            scalar_type
            read_element( this_vector const & q )
                {
                BOOST_QVM_STATIC_ASSERT(I>=0);
                BOOST_QVM_STATIC_ASSERT(I<dim);
                return quat_traits<Q>::template read_element<I+1>( reinterpret_cast<Q const &>(q) );
                }

            template <int I>
            BOOST_QVM_INLINE_CRITICAL
            static
            scalar_type &
            write_element( this_vector & q )
                {
                BOOST_QVM_STATIC_ASSERT(I>=0);
                BOOST_QVM_STATIC_ASSERT(I<dim);
                return quat_traits<Q>::template write_element<I+1>( reinterpret_cast<Q &>(q) );
                }
            };

        template <class Q,int D>
        struct
        deduce_vec<qvm_detail::quat_v_<Q>,D>
            {
            typedef vec<typename quat_traits<Q>::scalar_type,D> type;
            };

        template <class Q,int D>
        struct
        deduce_vec2<qvm_detail::quat_v_<Q>,qvm_detail::quat_v_<Q>,D>
            {
            typedef vec<typename quat_traits<Q>::scalar_type,D> type;
            };

        template <class Q>
        BOOST_QVM_INLINE_TRIVIAL
        typename enable_if_c<
            is_quat<Q>::value,
            qvm_detail::quat_v_<Q> const &>::type
        V( Q const & a )
            {
            return reinterpret_cast<qvm_detail::quat_v_<Q> const &>(a);
            }

        template <class Q>
        BOOST_QVM_INLINE_TRIVIAL
        typename enable_if_c<
            is_quat<Q>::value,
            qvm_detail::quat_v_<Q> &>::type
        V( Q & a )
            {
            return reinterpret_cast<qvm_detail::quat_v_<Q> &>(a);
            }

        template <class Q> BOOST_QVM_INLINE_TRIVIAL typename enable_if_c<is_quat<Q>::value,typename quat_traits<Q>::scalar_type>::type S( Q const & a ) { return quat_traits<Q>::template read_element<0>(a); }
        template <class Q> BOOST_QVM_INLINE_TRIVIAL typename enable_if_c<is_quat<Q>::value,typename quat_traits<Q>::scalar_type>::type X( Q const & a ) { return quat_traits<Q>::template read_element<1>(a); }
        template <class Q> BOOST_QVM_INLINE_TRIVIAL typename enable_if_c<is_quat<Q>::value,typename quat_traits<Q>::scalar_type>::type Y( Q const & a ) { return quat_traits<Q>::template read_element<2>(a); }
        template <class Q> BOOST_QVM_INLINE_TRIVIAL typename enable_if_c<is_quat<Q>::value,typename quat_traits<Q>::scalar_type>::type Z( Q const & a ) { return quat_traits<Q>::template read_element<3>(a); }

        template <class Q> BOOST_QVM_INLINE_TRIVIAL typename enable_if_c<is_quat<Q>::value,typename quat_traits<Q>::scalar_type &>::type S( Q & a ) { return quat_traits<Q>::template write_element<0>(a); }
        template <class Q> BOOST_QVM_INLINE_TRIVIAL typename enable_if_c<is_quat<Q>::value,typename quat_traits<Q>::scalar_type &>::type X( Q & a ) { return quat_traits<Q>::template write_element<1>(a); }
        template <class Q> BOOST_QVM_INLINE_TRIVIAL typename enable_if_c<is_quat<Q>::value,typename quat_traits<Q>::scalar_type &>::type Y( Q & a ) { return quat_traits<Q>::template write_element<2>(a); }
        template <class Q> BOOST_QVM_INLINE_TRIVIAL typename enable_if_c<is_quat<Q>::value,typename quat_traits<Q>::scalar_type &>::type Z( Q & a ) { return quat_traits<Q>::template write_element<3>(a); }

        ////////////////////////////////////////////////
        }
    }

#endif