151        {
"output", 
"test_multimesh"},
 
  152        {
"pass_through", std::vector<int64_t>()},
 
  153        {
"attributes", attributes}};
 
  160    const std::string& path,
 
  161    const std::string& name,
 
  162    std::function<std::shared_ptr<Operation>(
 
  164        std::shared_ptr<Rounding>&,
 
  167        std::vector<attribute::MeshAttributeHandle>&,
 
  174    const double target_max_amips = 10;
 
  175    const double min_edge_length = 0.001;
 
  176    const double target_edge_length = 1.04; 
 
  179    std::ifstream file(
data_dir / path);
 
  181    std::vector<Vector4l> tets;
 
  182    std::string x, y, z, token;
 
  184    while (file.good()) {
 
  186        std::getline(file, line);
 
  187        std::istringstream iss(line);
 
  189        if (line[0] == 
'v') {
 
  190            int sx = line.find(
' ', 2);
 
  191            x = line.substr(2, sx - 2);
 
  193            int sy = line.find(
' ', sx + 1);
 
  194            y = line.substr(sx + 1, sy - (sx + 1));
 
  196            int sz = line.find(
' ', sy + 1);
 
  197            z = line.substr(sy + 1, sz - (sy + 1));
 
  200        } 
else if (line[0] == 
't') {
 
  202            iss >> f[0] >> f[1] >> f[3] >> f[2];
 
  208    for (int64_t i = 0; i < 
vertices.size(); ++i) {
 
  213    for (int64_t i = 0; i < tets.size(); ++i) {
 
  224        v.col(0).cast<
double>().maxCoeff(),
 
  225        v.col(1).cast<
double>().maxCoeff(),
 
  226        v.col(2).cast<
double>().maxCoeff());
 
  228        v.col(0).cast<
double>().minCoeff(),
 
  229        v.col(1).cast<
double>().minCoeff(),
 
  230        v.col(2).cast<
double>().minCoeff());
 
  235    double diag = (bbox_max - bbox_min).norm();
 
  236    std::cout << diag << std::endl;
 
  237    std::cout << 0.05 * diag << std::endl;
 
  240    auto mesh = std::make_shared<TetMesh>();
 
  243    logger().info(
"Mesh has {} vertices {} tests", 
vertices.size(), tets.size());
 
  247    auto pt_attribute = mesh->get_attribute_handle<
Rational>(
"vertices", PrimitiveType::Vertex);
 
  248    auto pt_accessor = mesh->create_accessor(pt_attribute.as<
Rational>());
 
  250    auto amips_attribute =
 
  251        mesh->register_attribute<
double>(
"wildmeshing_amips", mesh->top_simplex_type(), 1);
 
  252    auto amips_accessor = mesh->create_accessor(amips_attribute.as<
double>());
 
  254    auto compute_amips = [](
const Eigen::MatrixX<Rational>& P) -> Eigen::VectorXd {
 
  255        assert(P.rows() == 2 || P.rows() == 3); 
 
  256        assert(P.cols() == P.rows() + 1);
 
  259            assert(P.rows() == 2);
 
  260            std::array<double, 6> pts;
 
  261            for (
size_t i = 0; i < 3; ++i) {
 
  262                for (
size_t j = 0; j < 2; ++j) {
 
  263                    pts[2 * i + j] = P(j, i).to_double();
 
  267            return Eigen::VectorXd::Constant(1, a);
 
  270            assert(P.rows() == 3);
 
  271            std::array<double, 12> pts;
 
  272            for (
size_t i = 0; i < 4; ++i) {
 
  273                for (
size_t j = 0; j < 3; ++j) {
 
  274                    pts[3 * i + j] = P(j, i).to_double();
 
  278            return Eigen::VectorXd::Constant(1, a);
 
  282        std::make_shared<wmtk::operations::SingleAttributeTransferStrategy<double, Rational>>(
 
  286    amips_update->run_on_all();
 
  290    auto edge_length_attribute =
 
  291        mesh->register_attribute<
double>(
"edge_length", PrimitiveType::Edge, 1);
 
  292    auto edge_length_accessor = mesh->create_accessor(edge_length_attribute.as<
double>());
 
  294    auto compute_edge_length = [](
const Eigen::MatrixX<Rational>& P) -> Eigen::VectorXd {
 
  295        assert(P.cols() == 2);
 
  296        assert(P.rows() == 2 || P.rows() == 3);
 
  297        return Eigen::VectorXd::Constant(1, sqrt((P.col(0) - P.col(1)).squaredNorm().to_double()));
 
  299    auto edge_length_update =
 
  300        std::make_shared<wmtk::operations::SingleAttributeTransferStrategy<double, Rational>>(
 
  301            edge_length_attribute,
 
  303            compute_edge_length);
 
  304    edge_length_update->run_on_all();
 
  306    auto visited_edge_flag =
 
  307        mesh->register_attribute<
char>(
"visited_edge", PrimitiveType::Edge, 1, 
false, char(1));
 
  350    auto update_flag_func = [](
const Eigen::MatrixX<Rational>& P) -> Eigen::VectorX<char> {
 
  351        assert(P.cols() == 2);
 
  352        assert(P.rows() == 2 || P.rows() == 3);
 
  353        return Eigen::VectorX<char>::Constant(1, 
char(1));
 
  356        std::make_shared<wmtk::operations::SingleAttributeTransferStrategy<char, Rational>>(
 
  362    std::vector<attribute::MeshAttributeHandle> pass_through_attributes;
 
  363    pass_through_attributes.push_back(edge_length_attribute);
 
  364    pass_through_attributes.push_back(amips_attribute);
 
  369    auto inversion_invariant =
 
  370        std::make_shared<SimplexInversionInvariant<Rational>>(*mesh, pt_attribute.as<
Rational>());
 
  374    auto rounding_pt_attribute =
 
  375        mesh->get_attribute_handle_typed<
Rational>(
"vertices", PrimitiveType::Vertex);
 
  376    auto rounding = std::make_shared<Rounding>(*mesh, rounding_pt_attribute);
 
  377    rounding->add_invariant(
 
  378        std::make_shared<RoundedInvariant>(*mesh, pt_attribute.as<
Rational>(), 
true));
 
  379    rounding->add_invariant(inversion_invariant);
 
  385    while (success > 0) {
 
  388            "Rounding, {} ops (S/F) {}/{}. Time: collecting: {}, sorting: {}, " 
  389            "executing: {}. Total {}",
 
  390            stats.number_of_performed_operations(),
 
  391            stats.number_of_successful_operations(),
 
  392            stats.number_of_failed_operations(),
 
  393            stats.collecting_time,
 
  395            stats.executing_time,
 
  400        int64_t unrounded = 0;
 
  402            const auto p = pt_accessor.vector_attribute(v);
 
  403            for (int64_t d = 0; d < 3; ++d) {
 
  404                if (!p[d].is_rounded()) {
 
  411        logger().info(
"Mesh has {} unrounded vertices", unrounded);
 
  419        pass_through_attributes,
 
  426        "{}, {} ops (S/F) {}/{}. Passes: {}. Time collecting: {}, sorting: {}, " 
  427        "executing: {}; total {}. Passes (avg) collecting: {}, sorting: {}, executing: {}",
 
  429        stats.number_of_performed_operations(),
 
  430        stats.number_of_successful_operations(),
 
  431        stats.number_of_failed_operations(),
 
  432        stats.sub_stats.size(),
 
  433        stats.collecting_time,
 
  435        stats.executing_time,
 
  437        stats.avg_sub_collecting_time(),
 
  438        stats.avg_sub_sorting_time(),
 
  439        stats.avg_sub_executing_time());
 
  441    int64_t unrounded = 0;
 
  443        const auto p = pt_accessor.vector_attribute(v);
 
  444        for (int64_t d = 0; d < 3; ++d) {
 
  445            if (!p[d].is_rounded()) {
 
  452    logger().info(
"Mesh has {} unrounded vertices", unrounded);
 
  454    double max_energy = std::numeric_limits<double>::lowest();
 
  455    double min_energy = std::numeric_limits<double>::max();
 
  456    for (
const auto& t : mesh->get_all(mesh->top_simplex_type())) {
 
  458        double e = amips_accessor.scalar_attribute(t);
 
  459        max_energy = std::max(max_energy, e);
 
  460        min_energy = std::min(min_energy, e);
 
  463    logger().info(
"Max AMIPS Energy: {}, Min AMIPS Energy: {}", max_energy, min_energy);
 
 
  467        mesh->get_all(PrimitiveType::Edge).size(),
 
  468        mesh->get_all(PrimitiveType::Tetrahedron).size());
 
  471        "Reference performance of original TetWild: split: 0.08s, collapse: 1.81525s, swap: 0.24s");
 
  475TEST_CASE(
"tetwild-split", 
"[components][wildmeshing][.]")
 
  477    logger().set_level(spdlog::level::debug);
 
  480        "split_initial_state_test.obj",
 
  483           std::shared_ptr<Rounding>& rounding,
 
  486           std::vector<attribute::MeshAttributeHandle>& pass_through_attributes,
 
  493            const double target_edge_length = 0.59019;
 
  494            const double target_max_amips = 10;
 
  495            const double min_edge_length = 0.001;
 
  498                "wildmeshing_target_edge_length",
 
  505            auto compute_target_edge_length =
 
  509                 target_edge_length_attribute,
 
  511                    const Eigen::MatrixXd& P,
 
  512                    const std::vector<Tuple>& neighs) -> Eigen::VectorXd {
 
  513                auto target_edge_length_accessor =
 
  516                assert(P.rows() == 1); 
 
  517                assert(!neighs.empty());
 
  518                assert(P.cols() == neighs.size());
 
  519                const double current_target_edge_length =
 
  520                    target_edge_length_accessor.const_scalar_attribute(neighs[0]);
 
  521                const double max_amips = P.maxCoeff();
 
  523                double new_target_edge_length = current_target_edge_length;
 
  524                if (max_amips > target_max_amips) {
 
  525                    new_target_edge_length *= 0.5;
 
  527                    new_target_edge_length *= 1.5;
 
  529                new_target_edge_length =
 
  530                    std::min(new_target_edge_length, target_edge_length); 
 
  531                new_target_edge_length =
 
  532                    std::max(new_target_edge_length, min_edge_length); 
 
  534                return Eigen::VectorXd::Constant(1, new_target_edge_length);
 
  537            pass_through_attributes.push_back(target_edge_length_attribute);
 
  539            auto update_flag_func = [](
const Eigen::MatrixX<Rational>& P) -> Eigen::VectorX<char> {
 
  540                assert(P.cols() == 2);
 
  541                assert(P.rows() == 2 || P.rows() == 3);
 
  542                return Eigen::VectorX<char>::Constant(1, 
char(1));
 
  545            auto edge_length_attribute =
 
  548            auto edge_length_accessor = mesh.
create_accessor(edge_length_attribute.as<
double>());
 
  551                assert(s.primitive_type() == PrimitiveType::Edge);
 
  552                return -edge_length_accessor.scalar_attribute(s);
 
  555            auto todo_larger = std::make_shared<TodoLargerInvariant>(
 
  557                edge_length_attribute.as<
double>(),
 
  558                target_edge_length_attribute.as<
double>(),
 
  561            auto split = std::make_shared<EdgeSplit>(mesh);
 
  562            split->set_priority(long_edges_first);
 
  564            split->add_invariant(todo_larger);
 
  566            split->set_new_attribute_strategy(pt_attribute);
 
  567            split->set_new_attribute_strategy(
 
  571            for (
const auto& attr : pass_through_attributes) {
 
  572                split->set_new_attribute_strategy(attr);
 
  575            split->add_transfer_strategy(amips_update);
 
  576            split->add_transfer_strategy(edge_length_update);
 
  577            split->add_transfer_strategy(tag_update);
 
 
  683            auto collapse_then_round = std::make_shared<AndOperationSequence>(mesh);
 
  684            collapse_then_round->add_operation(collapse);
 
  685            collapse_then_round->add_operation(rounding);
 
  687            return collapse_then_round;
 
  691TEST_CASE(
"tetwild-collapse-twoway", 
"[components][wildmeshing][.]")
 
  693    logger().set_level(spdlog::level::debug);
 
  695        "collapse_initial_state_104985.obj",
 
  698           std::shared_ptr<Rounding>& rounding,
 
  701           std::vector<attribute::MeshAttributeHandle>& pass_through_attributes,
 
  708            const double target_edge_length = 1.04;
 
  709            const double target_max_amips = 10;
 
  710            const double min_edge_length = 0.001;
 
  713                "wildmeshing_target_edge_length",
 
  719            pass_through_attributes.push_back(target_edge_length_attribute);
 
  721            auto edge_length_attribute =
 
  724            auto edge_length_accessor = mesh.
create_accessor(edge_length_attribute.as<
double>());
 
  728                std::make_shared<CollapseNewAttributeStrategy<Rational>>(pt_attribute);
 
  729            clps_strat->set_simplex_predicate(BasicSimplexPredicate::IsInterior);
 
  731            clps_strat->set_strategy(CollapseBasicStrategy::CopyOther);
 
  734            auto inversion_invariant = std::make_shared<SimplexInversionInvariant<Rational>>(
 
  737            auto link_condition = std::make_shared<MultiMeshLinkConditionInvariant>(mesh);
 
  738            auto invariant_separate_substructures =
 
  739                std::make_shared<invariants::SeparateSubstructuresInvariant>(mesh);
 
  742                assert(s.primitive_type() == PrimitiveType::Edge);
 
  743                return edge_length_accessor.scalar_attribute(s);
 
  746            auto todo_smaller = std::make_shared<TodoSmallerInvariant>(
 
  748                edge_length_attribute.as<
double>(),
 
  749                target_edge_length_attribute.as<
double>(),
 
  752            std::shared_ptr<function::PerSimplexFunction> 
amips =
 
  753                std::make_shared<AMIPS>(mesh, pt_attribute);
 
  755            auto function_invariant = std::make_shared<MaxFunctionInvariant>(
 
  764                std::make_shared<CollapseNewAttributeStrategy<Rational>>(pt_attribute);
 
  765            clps_strat1->set_simplex_predicate(BasicSimplexPredicate::IsInterior);
 
  767            clps_strat1->set_strategy(CollapseBasicStrategy::CopyOther);
 
  770                std::make_shared<CollapseNewAttributeStrategy<Rational>>(pt_attribute);
 
  771            clps_strat2->set_simplex_predicate(BasicSimplexPredicate::IsInterior);
 
  773            clps_strat2->set_strategy(CollapseBasicStrategy::CopyTuple);
 
  775            auto setup_collapse = [&](std::shared_ptr<EdgeCollapse>& collapse) {
 
  776                collapse->add_invariant(invariant_separate_substructures);
 
  778                collapse->add_invariant(inversion_invariant);
 
  779                collapse->add_invariant(function_invariant);
 
  782                collapse->set_new_attribute_strategy(
 
  786                collapse->add_transfer_strategy(tag_update);
 
  787                for (
const auto& attr : pass_through_attributes) {
 
  788                    collapse->set_new_attribute_strategy(attr);
 
  791                collapse->set_priority(short_edges_first);
 
  795                collapse->add_transfer_strategy(amips_update);
 
  796                collapse->add_transfer_strategy(edge_length_update);
 
  800            auto collapse1 = std::make_shared<EdgeCollapse>(mesh);
 
  801            collapse1->set_new_attribute_strategy(pt_attribute, clps_strat1);
 
  802            setup_collapse(collapse1);
 
  804            auto collapse2 = std::make_shared<EdgeCollapse>(mesh);
 
  805            collapse2->set_new_attribute_strategy(pt_attribute, clps_strat2);
 
  806            setup_collapse(collapse2);
 
  808            auto collapse = std::make_shared<OrOperationSequence>(mesh);
 
  809            collapse->add_operation(collapse1);
 
  810            collapse->add_operation(collapse2);
 
  811            collapse->add_invariant(todo_smaller);
 
 
  813            auto collapse_then_round = std::make_shared<AndOperationSequence>(mesh);
 
  814            collapse_then_round->add_operation(collapse);
 
  815            collapse_then_round->add_operation(rounding);
 
  817            return collapse_then_round;
 
  821TEST_CASE(
"tetwild-collapse-simulated", 
"[components][wildmeshing][.]")
 
  823    logger().set_level(spdlog::level::debug);
 
  825        "collapse_initial_state_104985.obj",
 
  828           std::shared_ptr<Rounding>& rounding,
 
  831           std::vector<attribute::MeshAttributeHandle>& pass_through_attributes,
 
  838            const double target_edge_length = 1.04;
 
  839            const double target_max_amips = 10;
 
  840            const double min_edge_length = 0.001;
 
  843                "wildmeshing_target_edge_length",
 
  849            pass_through_attributes.push_back(target_edge_length_attribute);
 
  851            auto edge_length_attribute =
 
  854            auto edge_length_accessor = mesh.
create_accessor(edge_length_attribute.as<
double>());
 
  858                std::make_shared<CollapseNewAttributeStrategy<Rational>>(pt_attribute);
 
  859            clps_strat->set_simplex_predicate(BasicSimplexPredicate::IsInterior);
 
  861            clps_strat->set_strategy(CollapseBasicStrategy::CopyOther);
 
  863            auto inversion_invariant = std::make_shared<SimplexInversionInvariant<Rational>>(
 
  866            auto link_condition = std::make_shared<MultiMeshLinkConditionInvariant>(mesh);
 
  867            auto invariant_separate_substructures =
 
  868                std::make_shared<invariants::SeparateSubstructuresInvariant>(mesh);
 
  871                assert(s.primitive_type() == PrimitiveType::Edge);
 
  872                return edge_length_accessor.scalar_attribute(s);
 
  875            auto todo_smaller = std::make_shared<TodoSmallerInvariant>(
 
  877                edge_length_attribute.as<
double>(),
 
  878                target_edge_length_attribute.as<
double>(),
 
  881            std::shared_ptr<function::PerSimplexFunction> 
amips =
 
  882                std::make_shared<AMIPS>(mesh, pt_attribute);
 
  884            auto function_invariant = std::make_shared<MaxFunctionInvariant>(
 
  893                std::make_shared<CollapseNewAttributeStrategy<Rational>>(pt_attribute);
 
  894            clps_strat1->set_simplex_predicate(BasicSimplexPredicate::IsInterior);
 
  896            clps_strat1->set_strategy(CollapseBasicStrategy::CopyOther);
 
  899                std::make_shared<CollapseNewAttributeStrategy<Rational>>(pt_attribute);
 
  900            clps_strat2->set_simplex_predicate(BasicSimplexPredicate::IsInterior);
 
  902            clps_strat2->set_strategy(CollapseBasicStrategy::CopyTuple);
 
  904            auto setup_collapse = [&](std::shared_ptr<EdgeCollapse>& collapse) {
 
  905                collapse->add_invariant(invariant_separate_substructures);
 
  907                collapse->add_invariant(inversion_invariant);
 
  911                collapse->set_new_attribute_strategy(
 
  915                collapse->add_transfer_strategy(tag_update);
 
  916                for (
const auto& attr : pass_through_attributes) {
 
  917                    collapse->set_new_attribute_strategy(attr);
 
  920                collapse->set_priority(short_edges_first);
 
  924                collapse->add_transfer_strategy(amips_update);
 
  925                collapse->add_transfer_strategy(edge_length_update);
 
  929            auto amips_attribute =
 
  932            auto collapse1 = std::make_shared<EdgeCollapse>(mesh);
 
  933            collapse1->add_invariant(std::make_shared<CollapseEnergyBeforeInvariant>(
 
  936                amips_attribute.as<
double>(),
 
  938            collapse1->set_new_attribute_strategy(pt_attribute, clps_strat1);
 
  939            setup_collapse(collapse1);
 
  941            auto collapse2 = std::make_shared<EdgeCollapse>(mesh);
 
  942            collapse2->add_invariant(std::make_shared<CollapseEnergyBeforeInvariant>(
 
  945                amips_attribute.as<
double>(),
 
  947            collapse2->set_new_attribute_strategy(pt_attribute, clps_strat2);
 
  948            setup_collapse(collapse2);
 
  950            auto collapse = std::make_shared<OrOperationSequence>(mesh);
 
  951            collapse->add_operation(collapse1);
 
  952            collapse->add_operation(collapse2);
 
  953            collapse->add_invariant(todo_smaller);
 
 
  956            collapse_then_round->add_operation(collapse);
 
  957            collapse_then_round->add_operation(rounding);
 
  959            return collapse_then_round;
 
  964TEST_CASE(
"tetwild-swap", 
"[components][wildmeshing][.]")
 
  966    logger().set_level(spdlog::level::debug);
 
  968        "swap_initial_state.obj",
 
  971           std::shared_ptr<Rounding>& rounding,
 
  974           std::vector<attribute::MeshAttributeHandle>& pass_through_attributes,
 
  981            const double target_edge_length = 0.59019;
 
  982            const double target_max_amips = 10;
 
  983            const double min_edge_length = 0.001;
 
  986                "wildmeshing_target_edge_length",
 
  992            pass_through_attributes.push_back(target_edge_length_attribute);
 
  994            auto edge_length_attribute =
 
  997            auto edge_length_accessor = mesh.
create_accessor(edge_length_attribute.as<
double>());
 
 1001                std::make_shared<CollapseNewAttributeStrategy<Rational>>(pt_attribute);
 
 1002            clps_strat->set_simplex_predicate(BasicSimplexPredicate::IsInterior);
 
 1004            clps_strat->set_strategy(CollapseBasicStrategy::CopyOther);
 
 1007            auto inversion_invariant = std::make_shared<SimplexInversionInvariant<Rational>>(
 
 1010            auto link_condition = std::make_shared<MultiMeshLinkConditionInvariant>(mesh);
 
 1011            auto invariant_separate_substructures =
 
 1012                std::make_shared<invariants::SeparateSubstructuresInvariant>(mesh);
 
 1015                assert(s.primitive_type() == PrimitiveType::Edge);
 
 1016                return -edge_length_accessor.scalar_attribute(s);
 
 1019            std::shared_ptr<function::PerSimplexFunction> 
amips =
 
 1020                std::make_shared<AMIPS>(mesh, pt_attribute);
 
 1028            auto swap56 = std::make_shared<MinOperationSequence>(mesh);
 
 1029            for (
int i = 0; i < 5; ++i) {
 
 1030                auto swap = std::make_shared<TetEdgeSwap>(mesh, i);
 
 1031                swap->collapse().add_invariant(invariant_separate_substructures);
 
 1033                swap->collapse().set_new_attribute_strategy(
 
 1035                    CollapseBasicStrategy::CopyOther);
 
 1036                swap->split().set_new_attribute_strategy(pt_attribute);
 
 1037                swap->split().set_new_attribute_strategy(
 
 1041                swap->collapse().set_new_attribute_strategy(
 
 1045                swap->add_invariant(std::make_shared<Swap56EnergyBeforeInvariant>(
 
 1050                for (
const auto& attr : pass_through_attributes) {
 
 1051                    swap->split().set_new_attribute_strategy(attr);
 
 1052                    swap->collapse().set_new_attribute_strategy(attr);
 
 1055                swap56->add_operation(swap);
 
 1058            auto swap56_energy_check = [&](int64_t idx, 
const simplex::Simplex& t) -> 
double {
 
 1064                auto accessor = mesh.create_const_accessor(pt_attribute.
as<
Rational>());
 
 1066                const Tuple e0 = t.tuple();
 
 1067                const Tuple e1 = mesh.switch_tuple(e0, 
PV);
 
 1069                std::array<Tuple, 5> v;
 
 1070                auto iter_tuple = e0;
 
 1071                for (int64_t i = 0; i < 5; ++i) {
 
 1072                    v[i] = mesh.switch_tuples(iter_tuple, {
PE, 
PV});
 
 1073                    iter_tuple = mesh.switch_tuples(iter_tuple, {
PF, 
PT});
 
 1075                if (iter_tuple != e0) 
return 0;
 
 1076                assert(iter_tuple == e0);
 
 1080                std::array<Eigen::Vector3<Rational>, 7> positions = {
 
 1081                    {accessor.const_vector_attribute(v[(idx + 0) % 5]),
 
 1082                     accessor.const_vector_attribute(v[(idx + 1) % 5]),
 
 1083                     accessor.const_vector_attribute(v[(idx + 2) % 5]),
 
 1084                     accessor.const_vector_attribute(v[(idx + 3) % 5]),
 
 1085                     accessor.const_vector_attribute(v[(idx + 4) % 5]),
 
 1086                     accessor.const_vector_attribute(e0),
 
 1087                     accessor.const_vector_attribute(e1)}};
 
 1089                std::array<Eigen::Vector3d, 7> positions_double = {
 
 1090                    {positions[0].cast<
double>(),
 
 1091                     positions[1].cast<double>(),
 
 1092                     positions[2].cast<
double>(),
 
 1093                     positions[3].cast<double>(),
 
 1094                     positions[4].cast<
double>(),
 
 1095                     positions[5].cast<double>(),
 
 1096                     positions[6].cast<
double>()}};
 
 1098                std::array<std::array<int, 4>, 6> new_tets = {
 
 1106                double new_energy_max = std::numeric_limits<double>::lowest();
 
 1108                for (
int i = 0; i < 6; ++i) {
 
 1110                            positions[new_tets[i][0]],
 
 1111                            positions[new_tets[i][1]],
 
 1112                            positions[new_tets[i][2]],
 
 1113                            positions[new_tets[i][3]]) > 0) {
 
 1115                            positions_double[new_tets[i][0]][0],
 
 1116                            positions_double[new_tets[i][0]][1],
 
 1117                            positions_double[new_tets[i][0]][2],
 
 1118                            positions_double[new_tets[i][1]][0],
 
 1119                            positions_double[new_tets[i][1]][1],
 
 1120                            positions_double[new_tets[i][1]][2],
 
 1121                            positions_double[new_tets[i][2]][0],
 
 1122                            positions_double[new_tets[i][2]][1],
 
 1123                            positions_double[new_tets[i][2]][2],
 
 1124                            positions_double[new_tets[i][3]][0],
 
 1125                            positions_double[new_tets[i][3]][1],
 
 1126                            positions_double[new_tets[i][3]][2],
 
 1130                        if (energy > new_energy_max) new_energy_max = energy;
 
 1133                            positions_double[new_tets[i][1]][0],
 
 1134                            positions_double[new_tets[i][1]][1],
 
 1135                            positions_double[new_tets[i][1]][2],
 
 1136                            positions_double[new_tets[i][0]][0],
 
 1137                            positions_double[new_tets[i][0]][1],
 
 1138                            positions_double[new_tets[i][0]][2],
 
 1139                            positions_double[new_tets[i][2]][0],
 
 1140                            positions_double[new_tets[i][2]][1],
 
 1141                            positions_double[new_tets[i][2]][2],
 
 1142                            positions_double[new_tets[i][3]][0],
 
 1143                            positions_double[new_tets[i][3]][1],
 
 1144                            positions_double[new_tets[i][3]][2],
 
 1148                        if (energy > new_energy_max) new_energy_max = energy;
 
 1152                return new_energy_max;
 
 1155            swap56->set_value_function(swap56_energy_check);
 
 1156            swap56->add_invariant(std::make_shared<EdgeValenceInvariant>(mesh, 5));
 
 1160            auto swap44 = std::make_shared<MinOperationSequence>(mesh);
 
 1161            for (
int i = 0; i < 2; ++i) {
 
 1162                auto swap = std::make_shared<TetEdgeSwap>(mesh, i);
 
 1163                swap->collapse().add_invariant(invariant_separate_substructures);
 
 1165                swap->collapse().set_new_attribute_strategy(
 
 1167                    CollapseBasicStrategy::CopyOther);
 
 1168                swap->split().set_new_attribute_strategy(pt_attribute);
 
 1169                swap->split().set_new_attribute_strategy(
 
 1173                swap->collapse().set_new_attribute_strategy(
 
 1177                swap->add_invariant(std::make_shared<Swap44EnergyBeforeInvariant>(
 
 1182                for (
const auto& attr : pass_through_attributes) {
 
 1183                    swap->split().set_new_attribute_strategy(attr);
 
 1184                    swap->collapse().set_new_attribute_strategy(attr);
 
 1187                swap44->add_operation(swap);
 
 1190            auto swap44_energy_check = [&](int64_t idx, 
const simplex::Simplex& t) -> 
double {
 
 1196                auto accessor = mesh.create_const_accessor(pt_attribute.
as<
Rational>());
 
 1200                const Tuple e0 = t.tuple();
 
 1201                const Tuple e1 = mesh.switch_tuple(e0, 
PV);
 
 1203                std::array<Tuple, 4> v;
 
 1204                auto iter_tuple = e0;
 
 1205                for (int64_t i = 0; i < 4; ++i) {
 
 1206                    v[i] = mesh.switch_tuples(iter_tuple, {
PE, 
PV});
 
 1207                    iter_tuple = mesh.switch_tuples(iter_tuple, {
PF, 
PT});
 
 1210                if (iter_tuple != e0) 
return 0;
 
 1211                assert(iter_tuple == e0);
 
 1213                std::array<Eigen::Vector3<Rational>, 6> positions = {
 
 1214                    {accessor.const_vector_attribute(v[(idx + 0) % 4]),
 
 1215                     accessor.const_vector_attribute(v[(idx + 1) % 4]),
 
 1216                     accessor.const_vector_attribute(v[(idx + 2) % 4]),
 
 1217                     accessor.const_vector_attribute(v[(idx + 3) % 4]),
 
 1218                     accessor.const_vector_attribute(e0),
 
 1219                     accessor.const_vector_attribute(e1)}};
 
 1220                std::array<Eigen::Vector3d, 6> positions_double = {
 
 1221                    {positions[0].cast<
double>(),
 
 1222                     positions[1].cast<double>(),
 
 1223                     positions[2].cast<
double>(),
 
 1224                     positions[3].cast<double>(),
 
 1225                     positions[4].cast<
double>(),
 
 1226                     positions[5].cast<double>()}};
 
 1228                std::array<std::array<int, 4>, 4> new_tets = {
 
 1229                    {{{0, 1, 2, 4}}, {{0, 2, 3, 4}}, {{0, 1, 2, 5}}, {{0, 2, 3, 5}}}};
 
 1231                double new_energy_max = std::numeric_limits<double>::lowest();
 
 1233                for (
int i = 0; i < 4; ++i) {
 
 1235                            positions[new_tets[i][0]],
 
 1236                            positions[new_tets[i][1]],
 
 1237                            positions[new_tets[i][2]],
 
 1238                            positions[new_tets[i][3]]) > 0) {
 
 1240                            positions_double[new_tets[i][0]][0],
 
 1241                            positions_double[new_tets[i][0]][1],
 
 1242                            positions_double[new_tets[i][0]][2],
 
 1243                            positions_double[new_tets[i][1]][0],
 
 1244                            positions_double[new_tets[i][1]][1],
 
 1245                            positions_double[new_tets[i][1]][2],
 
 1246                            positions_double[new_tets[i][2]][0],
 
 1247                            positions_double[new_tets[i][2]][1],
 
 1248                            positions_double[new_tets[i][2]][2],
 
 1249                            positions_double[new_tets[i][3]][0],
 
 1250                            positions_double[new_tets[i][3]][1],
 
 1251                            positions_double[new_tets[i][3]][2],
 
 1254                        if (energy > new_energy_max) new_energy_max = energy;
 
 1257                            positions_double[new_tets[i][1]][0],
 
 1258                            positions_double[new_tets[i][1]][1],
 
 1259                            positions_double[new_tets[i][1]][2],
 
 1260                            positions_double[new_tets[i][0]][0],
 
 1261                            positions_double[new_tets[i][0]][1],
 
 1262                            positions_double[new_tets[i][0]][2],
 
 1263                            positions_double[new_tets[i][2]][0],
 
 1264                            positions_double[new_tets[i][2]][1],
 
 1265                            positions_double[new_tets[i][2]][2],
 
 1266                            positions_double[new_tets[i][3]][0],
 
 1267                            positions_double[new_tets[i][3]][1],
 
 1268                            positions_double[new_tets[i][3]][2],
 
 1271                        if (energy > new_energy_max) new_energy_max = energy;
 
 1275                return new_energy_max;
 
 1278            swap44->set_value_function(swap44_energy_check);
 
 1279            swap44->add_invariant(std::make_shared<EdgeValenceInvariant>(mesh, 4));
 
 1282            auto swap32 = std::make_shared<TetEdgeSwap>(mesh, 0);
 
 1283            swap32->add_invariant(std::make_shared<EdgeValenceInvariant>(mesh, 3));
 
 1284            swap32->add_invariant(
 
 1285                std::make_shared<Swap32EnergyBeforeInvariant>(mesh, pt_attribute.
as<
Rational>()));
 
 1287            swap32->collapse().add_invariant(invariant_separate_substructures);
 
 1289            swap32->collapse().set_new_attribute_strategy(
 
 1291                CollapseBasicStrategy::CopyOther);
 
 1292            swap32->split().set_new_attribute_strategy(pt_attribute);
 
 1293            swap32->split().set_new_attribute_strategy(
 
 1297            swap32->collapse().set_new_attribute_strategy(
 
 1301            for (
const auto& attr : pass_through_attributes) {
 
 1302                swap32->split().set_new_attribute_strategy(attr);
 
 1303                swap32->collapse().set_new_attribute_strategy(attr);
 
 1308            auto swap_all = std::make_shared<OrOperationSequence>(mesh);
 
 1310            swap_all->add_operation(swap32);
 
 1311            swap_all->add_operation(swap44);
 
 1312            swap_all->add_operation(swap56);
 
 1313            swap_all->add_transfer_strategy(tag_update);
 
 1315            auto swap_then_round = std::make_shared<AndOperationSequence>(mesh);
 
 1316            swap_then_round->add_operation(swap_all);