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

#include "open3d/geometry/TriangleMesh.h"

#include <Eigen/Dense>
#include <queue>
#include <tuple>

#include "open3d/utility/Console.h"

namespace open3d {
namespace geometry {

std::shared_ptr<TriangleMesh> TriangleMesh::SubdivideMidpoint(
        int number_of_iterations) const {
    if (HasTriangleUvs()) {
        utility::LogWarning(
                "[SubdivideMidpoint] This mesh contains triangle uvs that are "
                "not handled in this function");
    }
    auto mesh = std::make_shared<TriangleMesh>();
    mesh->vertices_ = vertices_;
    mesh->vertex_colors_ = vertex_colors_;
    mesh->vertex_normals_ = vertex_normals_;
    mesh->triangles_ = triangles_;

    bool has_vert_normal = HasVertexNormals();
    bool has_vert_color = HasVertexColors();

    // Compute and return midpoint.
    // Also adds edge - new vertex refrence to new_verts map.
    auto SubdivideEdge =
            [&](std::unordered_map<Eigen::Vector2i, int,
                                   utility::hash_eigen::hash<Eigen::Vector2i>>&
                        new_verts,
                int vidx0, int vidx1) {
                int min = std::min(vidx0, vidx1);
                int max = std::max(vidx0, vidx1);
                Eigen::Vector2i edge(min, max);
                if (new_verts.count(edge) == 0) {
                    mesh->vertices_.push_back(0.5 * (mesh->vertices_[min] +
                                                     mesh->vertices_[max]));
                    if (has_vert_normal) {
                        mesh->vertex_normals_.push_back(
                                0.5 * (mesh->vertex_normals_[min] +
                                       mesh->vertex_normals_[max]));
                    }
                    if (has_vert_color) {
                        mesh->vertex_colors_.push_back(
                                0.5 * (mesh->vertex_colors_[min] +
                                       mesh->vertex_colors_[max]));
                    }
                    int vidx01 = int(mesh->vertices_.size()) - 1;
                    new_verts[edge] = vidx01;
                    return vidx01;
                } else {
                    return new_verts[edge];
                }
            };
    for (int iter = 0; iter < number_of_iterations; ++iter) {
        std::unordered_map<Eigen::Vector2i, int,
                           utility::hash_eigen::hash<Eigen::Vector2i>>
                new_verts;
        std::vector<Eigen::Vector3i> new_triangles(4 * mesh->triangles_.size());
        for (size_t tidx = 0; tidx < mesh->triangles_.size(); ++tidx) {
            const auto& triangle = mesh->triangles_[tidx];
            int vidx0 = triangle(0);
            int vidx1 = triangle(1);
            int vidx2 = triangle(2);
            int vidx01 = SubdivideEdge(new_verts, vidx0, vidx1);
            int vidx12 = SubdivideEdge(new_verts, vidx1, vidx2);
            int vidx20 = SubdivideEdge(new_verts, vidx2, vidx0);
            new_triangles[tidx * 4 + 0] =
                    Eigen::Vector3i(vidx0, vidx01, vidx20);
            new_triangles[tidx * 4 + 1] =
                    Eigen::Vector3i(vidx01, vidx1, vidx12);
            new_triangles[tidx * 4 + 2] =
                    Eigen::Vector3i(vidx12, vidx2, vidx20);
            new_triangles[tidx * 4 + 3] =
                    Eigen::Vector3i(vidx01, vidx12, vidx20);
        }
        mesh->triangles_ = new_triangles;
    }

    if (HasTriangleNormals()) {
        mesh->ComputeTriangleNormals();
    }

    return mesh;
}

std::shared_ptr<TriangleMesh> TriangleMesh::SubdivideLoop(
        int number_of_iterations) const {
    if (HasTriangleUvs()) {
        utility::LogWarning(
                "[SubdivideLoop] This mesh contains triangle uvs that are not "
                "handled in this function");
    }
    typedef std::unordered_map<Eigen::Vector2i, int,
                               utility::hash_eigen::hash<Eigen::Vector2i>>
            EdgeNewVertMap;
    typedef std::unordered_map<Eigen::Vector2i, std::unordered_set<int>,
                               utility::hash_eigen::hash<Eigen::Vector2i>>
            EdgeTrianglesMap;
    typedef std::vector<std::unordered_set<int>> VertexNeighbours;

    bool has_vert_normal = HasVertexNormals();
    bool has_vert_color = HasVertexColors();

    auto UpdateVertex = [&](int vidx,
                            const std::shared_ptr<TriangleMesh>& old_mesh,
                            std::shared_ptr<TriangleMesh>& new_mesh,
                            const std::unordered_set<int>& nbs,
                            const EdgeTrianglesMap& edge_to_triangles) {
        // check if boundary edge and get nb vertices in that case
        std::unordered_set<int> boundary_nbs;
        for (int nb : nbs) {
            const Eigen::Vector2i edge = GetOrderedEdge(vidx, nb);
            if (edge_to_triangles.at(edge).size() == 1) {
                boundary_nbs.insert(nb);
            }
        }

        // in manifold meshes this should not happen
        if (boundary_nbs.size() > 2) {
            utility::LogWarning(
                    "[SubdivideLoop] boundary edge with > 2 neighbours, maybe "
                    "mesh is not manifold.");
        }

        double beta, alpha;
        if (boundary_nbs.size() >= 2) {
            beta = 1. / 8.;
            alpha = 1. - boundary_nbs.size() * beta;
        } else if (nbs.size() == 3) {
            beta = 3. / 16.;
            alpha = 1. - nbs.size() * beta;
        } else {
            beta = 3. / (8. * nbs.size());
            alpha = 1. - nbs.size() * beta;
        }

        new_mesh->vertices_[vidx] = alpha * old_mesh->vertices_[vidx];
        if (has_vert_normal) {
            new_mesh->vertex_normals_[vidx] =
                    alpha * old_mesh->vertex_normals_[vidx];
        }
        if (has_vert_color) {
            new_mesh->vertex_colors_[vidx] =
                    alpha * old_mesh->vertex_colors_[vidx];
        }

        auto Update = [&](int nb) {
            new_mesh->vertices_[vidx] += beta * old_mesh->vertices_[nb];
            if (has_vert_normal) {
                new_mesh->vertex_normals_[vidx] +=
                        beta * old_mesh->vertex_normals_[nb];
            }
            if (has_vert_color) {
                new_mesh->vertex_colors_[vidx] +=
                        beta * old_mesh->vertex_colors_[nb];
            }
        };
        if (boundary_nbs.size() >= 2) {
            for (int nb : boundary_nbs) {
                Update(nb);
            }
        } else {
            for (int nb : nbs) {
                Update(nb);
            }
        }
    };

    auto SubdivideEdge = [&](int vidx0, int vidx1,
                             const std::shared_ptr<TriangleMesh>& old_mesh,
                             std::shared_ptr<TriangleMesh>& new_mesh,
                             EdgeNewVertMap& new_verts,
                             const EdgeTrianglesMap& edge_to_triangles) {
        Eigen::Vector2i edge = GetOrderedEdge(vidx0, vidx1);
        if (new_verts.count(edge) == 0) {
            Eigen::Vector3d new_vert =
                    old_mesh->vertices_[vidx0] + old_mesh->vertices_[vidx1];
            Eigen::Vector3d new_normal;
            if (has_vert_normal) {
                new_normal = old_mesh->vertex_normals_[vidx0] +
                             old_mesh->vertex_normals_[vidx1];
            }
            Eigen::Vector3d new_color;
            if (has_vert_color) {
                new_color = old_mesh->vertex_colors_[vidx0] +
                            old_mesh->vertex_colors_[vidx1];
            }

            const auto& edge_triangles = edge_to_triangles.at(edge);
            if (edge_triangles.size() < 2) {
                new_vert *= 0.5;
                if (has_vert_normal) {
                    new_normal *= 0.5;
                }
                if (has_vert_color) {
                    new_color *= 0.5;
                }
            } else {
                new_vert *= 3. / 8.;
                if (has_vert_normal) {
                    new_normal *= 3. / 8.;
                }
                if (has_vert_color) {
                    new_color *= 3. / 8.;
                }
                size_t n_adjacent_trias = edge_triangles.size();
                double scale = 1. / (4. * n_adjacent_trias);
                for (int tidx : edge_triangles) {
                    const auto& tria = old_mesh->triangles_[tidx];
                    int vidx2 =
                            (tria(0) != vidx0 && tria(0) != vidx1)
                                    ? tria(0)
                                    : ((tria(1) != vidx0 && tria(1) != vidx1)
                                               ? tria(1)
                                               : tria(2));
                    new_vert += scale * old_mesh->vertices_[vidx2];
                    if (has_vert_normal) {
                        new_normal += scale * old_mesh->vertex_normals_[vidx2];
                    }
                    if (has_vert_color) {
                        new_color += scale * old_mesh->vertex_colors_[vidx2];
                    }
                }
            }

            int vidx01 = int(old_mesh->vertices_.size() + new_verts.size());

            new_mesh->vertices_[vidx01] = new_vert;
            if (has_vert_normal) {
                new_mesh->vertex_normals_[vidx01] = new_normal;
            }
            if (has_vert_color) {
                new_mesh->vertex_colors_[vidx01] = new_color;
            }

            new_verts[edge] = vidx01;
            return vidx01;
        } else {
            int vidx01 = new_verts[edge];
            return vidx01;
        }
    };

    auto InsertTriangle = [&](int tidx, int vidx0, int vidx1, int vidx2,
                              std::shared_ptr<TriangleMesh>& mesh,
                              EdgeTrianglesMap& edge_to_triangles,
                              VertexNeighbours& vertex_neighbours) {
        mesh->triangles_[tidx] = Eigen::Vector3i(vidx0, vidx1, vidx2);
        edge_to_triangles[GetOrderedEdge(vidx0, vidx1)].insert(tidx);
        edge_to_triangles[GetOrderedEdge(vidx1, vidx2)].insert(tidx);
        edge_to_triangles[GetOrderedEdge(vidx2, vidx0)].insert(tidx);
        vertex_neighbours[vidx0].insert(vidx1);
        vertex_neighbours[vidx0].insert(vidx2);
        vertex_neighbours[vidx1].insert(vidx0);
        vertex_neighbours[vidx1].insert(vidx2);
        vertex_neighbours[vidx2].insert(vidx0);
        vertex_neighbours[vidx2].insert(vidx1);
    };

    EdgeTrianglesMap edge_to_triangles;
    VertexNeighbours vertex_neighbours(vertices_.size());
    for (size_t tidx = 0; tidx < triangles_.size(); ++tidx) {
        const auto& tria = triangles_[tidx];
        Eigen::Vector2i e0 = GetOrderedEdge(tria(0), tria(1));
        edge_to_triangles[e0].insert(int(tidx));
        Eigen::Vector2i e1 = GetOrderedEdge(tria(1), tria(2));
        edge_to_triangles[e1].insert(int(tidx));
        Eigen::Vector2i e2 = GetOrderedEdge(tria(2), tria(0));
        edge_to_triangles[e2].insert(int(tidx));

        if (edge_to_triangles[e0].size() > 2 ||
            edge_to_triangles[e1].size() > 2 ||
            edge_to_triangles[e2].size() > 2) {
            utility::LogWarning("[SubdivideLoop] non-manifold edge.");
        }

        vertex_neighbours[tria(0)].insert(tria(1));
        vertex_neighbours[tria(0)].insert(tria(2));
        vertex_neighbours[tria(1)].insert(tria(0));
        vertex_neighbours[tria(1)].insert(tria(2));
        vertex_neighbours[tria(2)].insert(tria(0));
        vertex_neighbours[tria(2)].insert(tria(1));
    }

    auto old_mesh = std::make_shared<TriangleMesh>();
    old_mesh->vertices_ = vertices_;
    old_mesh->vertex_colors_ = vertex_colors_;
    old_mesh->vertex_normals_ = vertex_normals_;
    old_mesh->triangles_ = triangles_;

    for (int iter = 0; iter < number_of_iterations; ++iter) {
        size_t n_new_vertices =
                old_mesh->vertices_.size() + edge_to_triangles.size();
        size_t n_new_triangles = 4 * old_mesh->triangles_.size();
        auto new_mesh = std::make_shared<TriangleMesh>();
        new_mesh->vertices_.resize(n_new_vertices);
        if (has_vert_normal) {
            new_mesh->vertex_normals_.resize(n_new_vertices);
        }
        if (has_vert_color) {
            new_mesh->vertex_colors_.resize(n_new_vertices);
        }
        new_mesh->triangles_.resize(n_new_triangles);

        EdgeNewVertMap new_verts;
        EdgeTrianglesMap new_edge_to_triangles;
        VertexNeighbours new_vertex_neighbours(n_new_vertices);

        for (size_t vidx = 0; vidx < old_mesh->vertices_.size(); ++vidx) {
            UpdateVertex(int(vidx), old_mesh, new_mesh, vertex_neighbours[vidx],
                         edge_to_triangles);
        }

        for (size_t tidx = 0; tidx < old_mesh->triangles_.size(); ++tidx) {
            const auto& triangle = old_mesh->triangles_[tidx];
            int vidx0 = triangle(0);
            int vidx1 = triangle(1);
            int vidx2 = triangle(2);

            int vidx01 = SubdivideEdge(vidx0, vidx1, old_mesh, new_mesh,
                                       new_verts, edge_to_triangles);
            int vidx12 = SubdivideEdge(vidx1, vidx2, old_mesh, new_mesh,
                                       new_verts, edge_to_triangles);
            int vidx20 = SubdivideEdge(vidx2, vidx0, old_mesh, new_mesh,
                                       new_verts, edge_to_triangles);

            InsertTriangle(int(tidx) * 4 + 0, vidx0, vidx01, vidx20, new_mesh,
                           new_edge_to_triangles, new_vertex_neighbours);
            InsertTriangle(int(tidx) * 4 + 1, vidx01, vidx1, vidx12, new_mesh,
                           new_edge_to_triangles, new_vertex_neighbours);
            InsertTriangle(int(tidx) * 4 + 2, vidx12, vidx2, vidx20, new_mesh,
                           new_edge_to_triangles, new_vertex_neighbours);
            InsertTriangle(int(tidx) * 4 + 3, vidx01, vidx12, vidx20, new_mesh,
                           new_edge_to_triangles, new_vertex_neighbours);
        }

        old_mesh = std::move(new_mesh);
        edge_to_triangles = std::move(new_edge_to_triangles);
        vertex_neighbours = std::move(new_vertex_neighbours);
    }

    if (HasTriangleNormals()) {
        old_mesh->ComputeTriangleNormals();
    }

    return old_mesh;
}

}  // namespace geometry
}  // namespace open3d


#ifndef DUMP_ROW_MODEL_OUTPUT
#include <boost/test/unit_test.hpp>
#include "libs/ml_models/utilities.h"
#include <algorithm>

BOOST_AUTO_TEST_CASE(convert_classes)
{
    // note - all are unique values, we want to see if we are getting the same number of elements
    mlmodels::class_data input{
        1.1f, 2.111f, 33.1f, 444.5f, 23.5555f
    };

    auto classes = mlmodels::classes_transform(std::begin(input), std::end(input));
    BOOST_TEST_REQUIRE(classes.size() == input.size());
    BOOST_TEST_REQUIRE(classes[0] == 1);
    BOOST_TEST_REQUIRE(classes[classes.size()-1] == (int)input.size());
}

BOOST_AUTO_TEST_CASE(convert_classes_single)
{
    mlmodels::class_data input(10, 555.666f);  // 10 elements all the same value

    auto classes = mlmodels::classes_transform(std::begin(input), std::end(input));
    BOOST_TEST_REQUIRE(classes.size() == input.size());
    BOOST_TEST_REQUIRE(classes.size() == 10u);
    BOOST_TEST_REQUIRE(classes[0] != input[0]);
    BOOST_TEST_REQUIRE(classes[0] == 1);
    BOOST_TEST_REQUIRE(classes[classes.size()-1] == classes[0]);
    BOOST_TEST_REQUIRE((std::all_of(std::begin(classes), std::end(classes), [](auto val) { return val == 1; } )), "all values must be the same value 1");
}

BOOST_AUTO_TEST_CASE(order_classes_test)
{
    mlmodels::class_data input(10, 555.666f);  // 10 elements all the same value
    input[3] = 1.43;    // so that we would have a single entry that is different in a given location
    auto classes = mlmodels::classes_transform(std::begin(input), std::end(input));

    BOOST_TEST_REQUIRE(classes.size() == input.size());
    BOOST_TEST_REQUIRE(classes.size() == 10u);
    BOOST_TEST_REQUIRE(classes[0] != input[0]);
    BOOST_TEST_REQUIRE(classes[0] == 1);
    BOOST_TEST_REQUIRE(classes[classes.size()-1] == classes[0]);
    BOOST_TEST_REQUIRE(classes[3] != classes[0]);
    BOOST_TEST_REQUIRE(classes[3] == 2);
}

BOOST_AUTO_TEST_CASE(order_classes_multi_test)
{
    mlmodels::class_data input(10, 555.666f);  // 10 elements all the same value
    input[3] = 1.43;    // so that we would have a single entry that is different in a given location
    input[5] = 1.43;
    input[7] = 1.434;   // make another unique value here
    auto classes = mlmodels::classes_transform(std::begin(input), std::end(input));

    BOOST_TEST_REQUIRE(classes.size() == input.size());
    BOOST_TEST_REQUIRE(classes.size() == 10u);
    BOOST_TEST_REQUIRE(classes[0] != input[0]);
    BOOST_TEST_REQUIRE(classes[0] == 1);
    BOOST_TEST_REQUIRE(classes[classes.size()-1] == classes[0]);
    BOOST_TEST_REQUIRE(classes[3] != classes[0]);
    BOOST_TEST_REQUIRE(classes[3] == 2);
    BOOST_TEST_REQUIRE(classes[3] == classes[5]);
    BOOST_TEST_REQUIRE(classes[3] != classes[7]);
    BOOST_TEST_REQUIRE(classes[0] != classes[7]);
    BOOST_TEST_REQUIRE(3 == classes[7]);
}

BOOST_AUTO_TEST_CASE(test_make_transformer)
{
    mlmodels::class_data input{1.1f, 2.111f, 33.1f, 444.5f, 23.5555f}; // all different
    auto transofer = mlmodels::make_transformer(std::begin(input), std::end(input));
    BOOST_TEST_REQUIRE(transofer.size() == input.size());
    mlmodels::class_data input_same(40u, 234.23f);
    transofer = mlmodels::make_transformer(std::begin(input_same), std::end(input_same));
    BOOST_TEST_REQUIRE(transofer.size() == 1u);
    BOOST_TEST_REQUIRE(transofer.begin()->second == 1); // only one element and it must be one
}

#endif  // DUMP_ROW_MODEL_OUTPUT


#include <stdexcept>
#include <boost/lexical_cast.hpp>
#include <boost/variant.hpp>

#include "../../Type.h"
#include "NumberLiteral.h"

namespace Sunda::Syntax::AST {

boost::variant<ALL_NUMBER_PRIMITIVE_TYPE> NumberLiteral::to_integer_variant() {
  boost::variant<ALL_NUMBER_PRIMITIVE_TYPE> ret;
  switch (type) {
    case Type::u8:ret = to_integer<u8>();
      break;
    case Type::u16:ret = to_integer<u16>();
      break;
    case Type::u32:ret = to_integer<u32>();
      break;
    case Type::u64:ret = to_integer<u64>();
      break;
    case Type::i8:ret = to_integer<i8>();
      break;
    case Type::i16:ret = to_integer<i16>();
      break;
    case Type::i32:ret = to_integer<i32>();
      break;
    case Type::i64:ret = to_integer<i64>();
      break;
    default:throw std::runtime_error("No such integer type exists!");
  }
  return ret;
}

template<typename T, typename>
inline T NumberLiteral::to_integer() {
  return boost::lexical_cast<T>(value);
}

}


#include <boost/numeric/ublas/operation_blocked.hpp>


/*=========================================================================
 *
 *  Copyright David Doria 2012 daviddoria@gmail.com
 *
 *  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.txt
 *
 *  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.
 *
 *=========================================================================*/

#ifndef InpaintingHistogram_HPP
#define InpaintingHistogram_HPP

// Pixel descriptors
#include "PixelDescriptors/ImagePatchPixelDescriptor.h"

// Visitors
#include "Visitors/NearestNeighborsDefaultVisitor.hpp"

// Descriptor visitors
#include "Visitors/DescriptorVisitors/ImagePatchDescriptorVisitor.hpp"

// Inpainting visitors
#include "Visitors/InpaintingVisitors/InpaintingVisitor.hpp"
#include "Visitors/AcceptanceVisitors/DefaultAcceptanceVisitor.hpp"

// Nearest neighbors functions
#include "NearestNeighbor/LinearSearchBest/HistogramCorrelation.hpp"
#include "NearestNeighbor/LinearSearchBest/HistogramDifference.hpp"
#include "NearestNeighbor/LinearSearchBest/HoleHistogramDifference.hpp"
#include "NearestNeighbor/LinearSearchBest/HistogramNewColors.hpp"
#include "NearestNeighbor/LinearSearchBest/DualHistogramDifference.hpp"
#include "NearestNeighbor/LinearSearchBest/AdaptiveDualHistogramDifference.hpp"
#include "NearestNeighbor/LinearSearchBest/AdaptiveDualQuadrantHistogramDifference.hpp"
#include "NearestNeighbor/LinearSearchBest/First.hpp"
#include "NearestNeighbor/LinearSearchBest/FirstAndWrite.hpp"
#include "NearestNeighbor/LinearSearchBest/Texture.hpp"
#include "NearestNeighbor/LinearSearchBest/ColorTexture.hpp"
#include "NearestNeighbor/LinearSearchKNNProperty.hpp"
#include "NearestNeighbor/TwoStepNearestNeighbor.hpp"

// Initializers
#include "Initializers/InitializeFromMaskImage.hpp"
#include "Initializers/InitializePriority.hpp"

// Inpainters
#include "Inpainters/PatchInpainter.hpp"
#include "Inpainters/CompositePatchInpainter.hpp"

// Difference functions
#include "DifferenceFunctions/Patch/ImagePatchDifference.hpp"
#include "DifferenceFunctions/Pixel/SumAbsolutePixelDifference.hpp"
#include "DifferenceFunctions/Pixel/SumSquaredPixelDifference.hpp"

// Utilities
#include "Utilities/PatchHelpers.h"
#include "Utilities/IndirectPriorityQueue.h"

// Inpainting
#include "Algorithms/InpaintingAlgorithm.hpp"

// Priority
#include "Priority/PriorityRandom.h"
#include "Priority/PriorityCriminisi.h"

// Submodules
#include <Helpers/Helpers.h>
#include <ITKVTKHelpers/ITKVTKHelpers.h>
#include <Mask/MaskOperations.h>

// Boost
#include <boost/graph/grid_graph.hpp>
#include <boost/property_map/property_map.hpp>

// Run with: Data/trashcan.png Data/trashcan.mask 15 filled.png
template <typename TImage>
void InpaintingHistogram(TImage* const originalImage, Mask* const mask,
                         const unsigned int patchHalfWidth, const unsigned int numberOfKNN,
                         const unsigned int binsPerChannel)
{
  itk::ImageRegion<2> fullRegion = originalImage->GetLargestPossibleRegion();

  // Convert the image to HSV
  typedef itk::Image<itk::CovariantVector<float, 3>, 2> HSVImageType;
  HSVImageType::Pointer hsvImage = HSVImageType::New();
  ITKVTKHelpers::ConvertRGBtoHSV(originalImage, hsvImage.GetPointer());

  ITKHelpers::WriteImage(hsvImage.GetPointer(), "HSVImage.mha");

  // Blur the image
  typedef TImage BlurredImageType; // Usually the blurred image is the same type as the original image.
  typename BlurredImageType::Pointer blurredImage = BlurredImageType::New();
  float blurVariance = 2.0f;
  MaskOperations::MaskedBlur(originalImage, mask, blurVariance, blurredImage.GetPointer());

  ITKHelpers::WriteRGBImage(blurredImage.GetPointer(), "BlurredImage.png");

  // Blur the image slightly so that the SSD comparisons are not so noisy
  typename BlurredImageType::Pointer slightBlurredImage = BlurredImageType::New();
  float slightBlurVariance = 1.0f;
  MaskOperations::MaskedBlur(originalImage, mask, slightBlurVariance, slightBlurredImage.GetPointer());

  ITKHelpers::WriteRGBImage(slightBlurredImage.GetPointer(), "SlightlyBlurredImage.png");

  // Create the graph
  typedef ImagePatchPixelDescriptor<TImage> ImagePatchPixelDescriptorType;

  typedef boost::grid_graph<2> VertexListGraphType;

  // We can't make this a signed type (size_t versus int) because we allow negative
  boost::array<std::size_t, 2> graphSideLengths = { { fullRegion.GetSize()[0],
                                                      fullRegion.GetSize()[1] } };
  VertexListGraphType graph(graphSideLengths);
  typedef boost::graph_traits<VertexListGraphType>::vertex_descriptor VertexDescriptorType;
  typedef boost::graph_traits<VertexListGraphType>::vertex_iterator VertexIteratorType;

  // Get the index map
  typedef boost::property_map<VertexListGraphType, boost::vertex_index_t>::const_type IndexMapType;
  IndexMapType indexMap(get(boost::vertex_index, graph));

  // Create the descriptor map. This is where the data for each pixel is stored.
  typedef boost::vector_property_map<ImagePatchPixelDescriptorType, IndexMapType> ImagePatchDescriptorMapType;
  ImagePatchDescriptorMapType imagePatchDescriptorMap(num_vertices(graph), indexMap);

  // Create the patch inpainter.
  typedef PatchInpainter<TImage> OriginalImageInpainterType;
  OriginalImageInpainterType originalImagePatchInpainter(patchHalfWidth, originalImage, mask);
  originalImagePatchInpainter.SetDebugImages(true);
  originalImagePatchInpainter.SetImageName("RGB");
  originalImagePatchInpainter.SetDebugLevel(2); // To also output TargetPatchAfter* files

  // Create an inpainter for the HSV image.
  typedef PatchInpainter<HSVImageType> HSVImageInpainterType;
  HSVImageInpainterType hsvImagePatchInpainter(patchHalfWidth, hsvImage, mask);

  // Create an inpainter for the blurred image.
  typedef PatchInpainter<BlurredImageType> BlurredImageInpainterType;
  BlurredImageInpainterType blurredImagePatchInpainter(patchHalfWidth, blurredImage, mask);

  // Create an inpainter for the slightly blurred image.
  typedef PatchInpainter<BlurredImageType> BlurredImageInpainterType;
  BlurredImageInpainterType slightlyBlurredImagePatchInpainter(patchHalfWidth, slightBlurredImage, mask);

  // Create a composite inpainter.
  CompositePatchInpainter inpainter;
  inpainter.AddInpainter(&originalImagePatchInpainter);
  inpainter.AddInpainter(&hsvImagePatchInpainter);
  inpainter.AddInpainter(&blurredImagePatchInpainter);
  inpainter.AddInpainter(&slightlyBlurredImagePatchInpainter);

  // Create the priority function
  typedef PriorityCriminisi<BlurredImageType> PriorityType;
  PriorityType priorityFunction(blurredImage, mask, patchHalfWidth);
//  priorityFunction.SetDebugLevel(1);

  // Queue
  typedef IndirectPriorityQueue<VertexListGraphType> BoundaryNodeQueueType;
  BoundaryNodeQueueType boundaryNodeQueue(graph);

  // Create the descriptor visitor (use the slightBlurredImage for SSD comparisons).
  typedef ImagePatchDescriptorVisitor<VertexListGraphType, TImage, ImagePatchDescriptorMapType>
      ImagePatchDescriptorVisitorType;
//  ImagePatchDescriptorVisitorType imagePatchDescriptorVisitor(originalImage, mask,
//                                  imagePatchDescriptorMap, patchHalfWidth); // Use the non-blurred image for the SSD comparisons
  ImagePatchDescriptorVisitorType imagePatchDescriptorVisitor(slightBlurredImage, mask,
                                  imagePatchDescriptorMap, patchHalfWidth); // Use the blurred image for the SSD comparisons

  typedef DefaultAcceptanceVisitor<VertexListGraphType> AcceptanceVisitorType;
  AcceptanceVisitorType acceptanceVisitor;

  // Create the inpainting visitor. (The mask is inpainted in FinishVertex)
  typedef InpaintingVisitor<VertexListGraphType, BoundaryNodeQueueType,
      ImagePatchDescriptorVisitorType, AcceptanceVisitorType, PriorityType,
      TImage>
      InpaintingVisitorType;
  InpaintingVisitorType inpaintingVisitor(mask, boundaryNodeQueue,
                                          imagePatchDescriptorVisitor, acceptanceVisitor,
                                          &priorityFunction, patchHalfWidth,
                                          "InpaintingVisitor", originalImage);
  inpaintingVisitor.SetDebugImages(true);
  inpaintingVisitor.SetAllowNewPatches(false);

  InitializePriority(mask, boundaryNodeQueue, &priorityFunction);

  // Initialize the boundary node queue from the user provided mask image.
  InitializeFromMaskImage<InpaintingVisitorType, VertexDescriptorType>(mask, &inpaintingVisitor);
  std::cout << "PatchBasedInpaintingNonInteractive: There are " << boundaryNodeQueue.CountValidNodes()
            << " nodes in the boundaryNodeQueue" << std::endl;

  // Select squared or absolute pixel error
//  typedef ImagePatchDifference<ImagePatchPixelDescriptorType, SumAbsolutePixelDifference<TImage::PixelType> > PatchDifferenceType;
  typedef ImagePatchDifference<ImagePatchPixelDescriptorType,
      SumSquaredPixelDifference<typename TImage::PixelType> > PatchDifferenceType;

  // Create the first (KNN) neighbor finder
  typedef LinearSearchKNNProperty<ImagePatchDescriptorMapType, PatchDifferenceType> KNNSearchType;
  KNNSearchType linearSearchKNN(imagePatchDescriptorMap, numberOfKNN);

  // Setup the second (1-NN) neighbor finder
  typedef std::vector<VertexDescriptorType>::iterator VertexDescriptorVectorIteratorType;

  // This is templated on TImage because we need it to write out debug patches from this searcher (since we are not using an RGB image to compute the histograms)
  typedef LinearSearchBestHistogramDifference<ImagePatchDescriptorMapType, HSVImageType,
      VertexDescriptorVectorIteratorType, TImage> BestSearchType;

  BestSearchType linearSearchBest(imagePatchDescriptorMap, hsvImage.GetPointer(), mask, originalImage);
  linearSearchBest.SetDebugImages(true);

  linearSearchBest.SetNumberOfBinsPerDimension(binsPerChannel);
  // The range (0,1) is used because we use the HSV image.
  linearSearchBest.SetRangeMin(0.0f);
  linearSearchBest.SetRangeMax(1.0f);

  // Setup the two step neighbor finder

  // Without writing top KNN patches
//  TwoStepNearestNeighbor<KNNSearchType, BestSearchType>
//      twoStepNearestNeighbor(linearSearchKNN, linearSearchBest);

  // With writing top KNN patches
  typedef LinearSearchBestFirstAndWrite<ImagePatchDescriptorMapType,
      TImage, PatchDifferenceType> TopPatchesWriterType;
  TopPatchesWriterType topPatchesWriter(imagePatchDescriptorMap, originalImage, mask);

  TwoStepNearestNeighbor<KNNSearchType, BestSearchType, TopPatchesWriterType>
      twoStepNearestNeighbor(linearSearchKNN, linearSearchBest, topPatchesWriter);

  // Perform the inpainting
  InpaintingAlgorithm(graph, inpaintingVisitor, &boundaryNodeQueue,
                      twoStepNearestNeighbor, &inpainter);

}

#endif


#include "sequential_lp.h"
#include "Pest.h"
#include "Jacobian_1to1.h"
#include "ClpSimplex.hpp"
#include "RunManagerAbstract.h"
#include "TerminationController.h"
#include "covariance.h"
#include "linear_analysis.h"
#include "FileManager.h"
#include "OutputFileWriter.h"
#include <Eigen/Sparse>
#include "CoinFinite.hpp"
#include "ClpPresolve.hpp"
#include <iomanip>
#include "utilities.h"

sequentialLP::sequentialLP(Pest &_pest_scenario, RunManagerAbstract* _run_mgr_ptr,
	Covariance &_parcov, FileManager* _file_mgr_ptr, OutputFileWriter _of_wr, PerformanceLog& _pfm)
	: pest_scenario(_pest_scenario), run_mgr_ptr(_run_mgr_ptr),
	parcov(_parcov), file_mgr_ptr(_file_mgr_ptr),jco(*_file_mgr_ptr,_of_wr), of_wr(_of_wr), pfm(_pfm),
	constraints(_pest_scenario,_file_mgr_ptr,_of_wr, _pfm)
{
	rand_gen = std::mt19937(pest_scenario.get_pestpp_options().get_random_seed());

	try
	{
		initialize_and_check();
	}
	catch (const runtime_error& error)
	{
		cout << "error initializing sequentialLP process: " << error.what() << endl;
		exit(1);
	}
}

sequentialLP::~sequentialLP()
{
	delete[] dec_var_lb;
	delete[] dec_var_ub;
	//delete[] constraint_lb;
	//delete[] constraint_ub;
	delete[] ctl_ord_obj_func_coefs;
	//delete[] row_price;
}

void sequentialLP::throw_sequentialLP_error(string message,const vector<string> &messages)
{
	stringstream ss;
	for (auto &mess : messages)
		ss << mess + ',';
	throw_sequentialLP_error(message + ss.str());
}

void sequentialLP::throw_sequentialLP_error(string message, const set<string> &messages)
{
	stringstream ss;
	for (auto &mess : messages)
		ss << mess + ',';
	throw_sequentialLP_error(message + ss.str());
}

void sequentialLP::throw_sequentialLP_error(string message)
{
	string error_message = "error in sequentialLP process: " + message;
	file_mgr_ptr->rec_ofstream() << error_message << endl;
	file_mgr_ptr->close_file("rec");
	cout << endl << endl << error_message << endl << endl;
	throw runtime_error(error_message);
}

void sequentialLP::initial_report()
{
	ofstream &f_rec = file_mgr_ptr->rec_ofstream();
	f_rec << endl << "  -------------------------------------------------------------" << endl;
	f_rec << "  ---  sequential linear programming problem information  ---  " << endl;
	f_rec << "  -------------------------------------------------------------" << endl << endl;

	f_rec << "-->number of iterations of sequential linear programming (noptmax): " << pest_scenario.get_control_info().noptmax << endl;

	f_rec << "-->objective function sense (direction): " << obj_sense << endl;

	f_rec << "-->number of decision variable: " << num_dec_vars() << endl;
	f_rec << "-->number of observation constraints: " << constraints.num_obs_constraints() << endl;
	f_rec << "-->number of prior information constraints: " << constraints.num_pi_constraints() << endl;
	if (iter_derinc_fac != 1.0)
		f_rec << "-->iteration DERINC reduction factor (++opt_iter_derinc_fac): " << iter_derinc_fac << endl;

	string bj = pest_scenario.get_pestpp_options().get_basejac_filename();
	if (!bj.empty())
		f_rec << "-->start with existing jacobian: " << bj << endl;
	string hotstart = pest_scenario.get_pestpp_options().get_hotstart_resfile();
	if (!hotstart.empty())
		f_rec << "-->hot start with residual file: " << hotstart << endl;
	bool sf = pest_scenario.get_pestpp_options().get_opt_skip_final();
	super_secret_option = false;
	if (sf)
	{
		f_rec << "-->skipping final, optimal model run" << endl;
		if ((!bj.empty()) && (pest_scenario.get_control_info().noptmax == 1))
		{
			f_rec << "-->super secrect option to skip final run and upgrade run activated..." << endl;
			super_secret_option = true;
		}

	}
	f_rec << endl << endl << "  ---  decision variables active in SLP  ---  " << endl;
	map<string, double>::iterator end = obj_func_coef_map.end();
	vector<string> missing;
	if (obj_func_coef_map.size() == 0)
		f_rec << "objective function coefficients defined by observation: " << pest_scenario.get_pestpp_options().get_opt_obj_func() << endl;
	else
	{
		f_rec << "  ---  objective function coefficients  ---  " << endl;
		obj_init = obj_func_report();
		f_rec << endl << "  ---  objective function value (using initial dec var values): " << obj_init << endl << endl;
		cout << endl << "  ---  objective function value (using initial dec var values): " << obj_init << endl << endl;

	}
	f_rec << " note: bound and initial value info reported in 'parameter data' section" << endl << endl;

	constraints.initial_report();

	return;
}

double sequentialLP::obj_func_report()
{
	vector<string> missing;
	ofstream &f_rec = file_mgr_ptr->rec_ofstream();
	map<string, double>::iterator end = obj_func_coef_map.end();
	f_rec << setw(20) << left << "name" << setw(25) << "obj func coefficient" << endl;
	for (auto &name : ctl_ord_dec_var_names)
	{
		f_rec << setw(20) << left << name;
		if (obj_func_coef_map.find(name) != end)
		{
			f_rec << setw(25) << obj_func_coef_map.at(name) << endl;
		}
		else
		{
			f_rec << setw(25) << "not listed" << endl;
			missing.push_back(name);
		}
	}
	double obj = 0.0;
	for (auto &p : ctl_ord_dec_var_names)
	{
		obj += all_pars_and_dec_vars_initial[p] * obj_func_coef_map[p];
	}
	if (missing.size() > 0)
	{
		f_rec << endl << endl << "WARNING: the following decision variables have '0.0' objective function coef:" << endl;
		cout << endl << endl << "WARNING: the following decision variables have '0.0' objective function coef:" << endl;

		for (auto &name : missing)
		{
			f_rec << "    " << name << endl;
			f_rec << "    " << name << endl;
		}
	}
	return obj;

}

map<string,double> sequentialLP::get_out_of_bounds_dec_vars(Parameters &upgrade_pars)
{
	double opt_tol = pest_scenario.get_pestpp_options().get_opt_iter_tol();
	Parameters ubnd = pest_scenario.get_ctl_parameter_info().get_up_bnd(ctl_ord_dec_var_names);
	Parameters lbnd = pest_scenario.get_ctl_parameter_info().get_low_bnd(ctl_ord_dec_var_names);
	double sim_val;
	map<string, double> invalid_dec_vars;
	for (auto &name : ctl_ord_dec_var_names)
	{
		sim_val = upgrade_pars[name];
		if (sim_val > ubnd[name])
			invalid_dec_vars[name] = sim_val - ubnd[name];
		else if (sim_val < lbnd[name])
			invalid_dec_vars[name] = lbnd[name] - sim_val;
	}

	return invalid_dec_vars;
}

pair<double,double> sequentialLP::postsolve_decision_var_report(Parameters &upgrade_pars)
{
	ofstream &f_rec = file_mgr_ptr->rec_ofstream();
	const double *reduced_cost = model.getReducedCost();
	f_rec << endl << endl << "     decision variable information at end of SLP iteration " << slp_iter << endl << endl;
	f_rec << setw(20) << left << "name" << right << setw(15) << "current" << setw(15)  << "new";
	f_rec << setw(15) << "objfunc coef" << setw(15) << "cur contrib" << setw(15) << "new contrib" << setw(15) << "reduced cost";
	f_rec << setw(25) << "simplex status" << endl;
	string name;
	ClpSimplex::Status status;
	double obj_coef, cur_val, new_val, upgrade, init_val;
	double cur_obj=0.0, new_obj=0.0;
	Parameters actual_pars = upgrade_pars;
	for (int i = 0; i < num_dec_vars(); ++i)
	{
		name = ctl_ord_dec_var_names[i];
		status = model.getColumnStatus(i);
		obj_coef = ctl_ord_obj_func_coefs[i];
		init_val = all_pars_and_dec_vars_initial[name];
		cur_val = all_pars_and_dec_vars[name];

		new_val = upgrade_pars[name];
		actual_pars.update_rec(name, new_val);
		f_rec << setw(20) << left << name;
		f_rec << setw(15) << right << cur_val;
		f_rec << setw(15) << new_val;
		f_rec << setw(15) << obj_coef;
		f_rec << setw(15) << cur_val * obj_coef;
		f_rec << setw(15) << new_val * obj_coef;
		f_rec << setw(15) << reduced_cost[i];
		f_rec << setw(25) << status_name_map[status] << endl;
		cur_obj += cur_val * obj_coef;
		new_obj += new_val * obj_coef;

	}
	stringstream ss;
	ss << slp_iter << ".par";

	of_wr.write_par(file_mgr_ptr->open_ofile_ext(ss.str()),actual_pars,*par_trans.get_offset_ptr(),*par_trans.get_scale_ptr());
	file_mgr_ptr->close_file(ss.str());
	of_wr.write_par(file_mgr_ptr->open_ofile_ext("par"), actual_pars, *par_trans.get_offset_ptr(), *par_trans.get_scale_ptr());
	file_mgr_ptr->close_file("par");
	return pair<double,double>(cur_obj,new_obj);
}

pair<sequentialLP::ConstraintSense,string> sequentialLP::get_sense_from_group_name(const string &name)
{

	if ((name.compare(0, 2, "L_") == 0) || (name.compare(0, 4, "LESS")==0))
		return pair<ConstraintSense,string>(ConstraintSense::less_than,"less_than");
	else if ((name.compare(0, 2, "G_") == 0) || (name.compare(0, 7, "GREATER")==0))
		return pair<ConstraintSense, string>(ConstraintSense::greater_than,"greater_than");
	else if ((name.compare(0, 2, "N_") == 0) || (name.compare(0, 2, "E_") == 0) || (name.compare(0, 5, "EQUAL")==0))
		return pair<ConstraintSense,string>(ConstraintSense::equal_to,"equal_to");
	else
		return pair<ConstraintSense,string>(ConstraintSense::undefined,"undefined");
}

void sequentialLP::initialize_and_check()
{
	ofstream &f_rec = file_mgr_ptr->rec_ofstream();
	//TODO: handle restart condition

	if (pest_scenario.get_control_info().pestmode != ControlInfo::PestMode::ESTIMATION)
	{
		string mess = "'pestmode' != 'estimation'.  pestpp-opt really only operates in kind-of 'estimation' mode.  ignoring";
		cout << endl << mess << endl;
		f_rec << endl << mess << endl;
	}

	if (pest_scenario.get_control_info().noptmax < 1)
		throw_sequentialLP_error("noptmax must be greater than 0");

	coin_log_ptr = fopen(file_mgr_ptr->build_filename("coin_log").c_str(), "w");
	coin_hr = CoinMessageHandler(coin_log_ptr);
	model.passInMessageHandler(&coin_hr);

	terminate = false;

	iter_derinc_fac = pest_scenario.get_pestpp_options().get_opt_iter_derinc_fac();
	if ((iter_derinc_fac > 1.0) || (iter_derinc_fac <= 0.0))
		throw_sequentialLP_error("++opt_iter_derinc_fac must be greater than 0.0 and less than or equal to 1.0");

	//-----------------------------
	//  ---  decision vars  ---
	//-----------------------------

	all_pars_and_dec_vars = pest_scenario.get_ctl_parameters();
	all_pars_and_dec_vars_initial = Parameters(all_pars_and_dec_vars);
	all_pars_and_dec_vars_best = Parameters(all_pars_and_dec_vars);
	par_trans = pest_scenario.get_base_par_tran_seq();
	//set ordered dec var name vec
	//and check for illegal parameter transformations
	vector<string> dec_var_groups = pest_scenario.get_pestpp_options().get_opt_dec_var_groups();
	vector<string> ext_var_groups = pest_scenario.get_pestpp_options().get_opt_ext_var_groups();
	dec_var_groups.insert(dec_var_groups.begin(), ext_var_groups.begin(), ext_var_groups.end());
	ctl_ord_dec_var_names.clear();
	//if the ++opt_dec_var_groups arg was passed
	if (dec_var_groups.size() != 0)
	{
		//first make sure all the groups are actually listed in the control file
		vector<string> missing;
		vector<string> pst_groups = pest_scenario.get_ctl_ordered_par_group_names();
		vector<string>::iterator end = pst_groups.end();
		vector<string>::iterator start = pst_groups.begin();
		for (auto grp : dec_var_groups)
			if (find(start, end, grp) == end)
				missing.push_back(grp);
		if (missing.size() > 0)
			throw_sequentialLP_error("the following ++opt_dec_var_groups were not found: ", missing);

		//find the parameter in the dec var groups
		ParameterGroupInfo pinfo = pest_scenario.get_base_group_info();
		string group;
		end = dec_var_groups.end();
		start = dec_var_groups.begin();
		for (auto &par_name : pest_scenario.get_ctl_ordered_par_names())
		{
			group = pinfo.get_group_name(par_name);
			if (find(start, end, group) != end)
			{
				ctl_ord_dec_var_names.push_back(par_name);
				//check if this is an ext var
				if (find(ext_var_groups.begin(), ext_var_groups.end(), group) != ext_var_groups.end())
					ctl_ord_ext_var_names.push_back(par_name);
			}
		}

		if (num_dec_vars() == 0)
			throw_sequentialLP_error("no decision variables found in groups: ", dec_var_groups);
	}
	//if not ++opt_dec_var_names was passed, use all parameter as decision variables
	else
		ctl_ord_dec_var_names = pest_scenario.get_ctl_ordered_par_names();

	//if any decision vars have a transformation that is not allowed
	vector<string> problem_trans;
	for (auto &name : ctl_ord_dec_var_names)
		if (pest_scenario.get_ctl_parameter_info().get_parameter_rec_ptr(name)->tranform_type != ParameterRec::TRAN_TYPE::NONE)
			problem_trans.push_back(name);
	if (problem_trans.size() > 0)
		throw_sequentialLP_error("the following decision variables don't have 'none' type parameter transformation: ", problem_trans);

	current_constraints_sim = pest_scenario.get_ctl_observations();

 	constraints.initialize(ctl_ord_dec_var_names, &all_pars_and_dec_vars, &current_constraints_sim, COIN_DBL_MAX);

	//--------------------------------
	//  ---  objective function  ---
	//--------------------------------

	//initialize the objective function
	obj_func_str = pest_scenario.get_pestpp_options().get_opt_obj_func();
	obj_sense = (pest_scenario.get_pestpp_options().get_opt_direction() == 1) ? "minimize" : "maximize";

	//check if the obj_str is an observation
	use_obj_obs = false;
	if (pest_scenario.get_ctl_observations().find(obj_func_str) != pest_scenario.get_ctl_observations().end())
	{
		use_obj_obs = true;
		obj_obs = obj_func_str;
		//check
		vector<string> cnames = constraints.get_obs_constraint_names();
		set<string> names(cnames.begin(), cnames.end());
		if (names.find(obj_obs) != names.end())
		{
			throw runtime_error("objective function obs is a constraint, #sad");
		}
		names.clear();
		cnames = constraints.get_nz_obs_names();
		names.insert(cnames.begin(),cnames.end());
		if (names.find(obj_obs) != names.end())
		{
			throw runtime_error("objective function obs has non-zero weight and chance constraints are active");
		}

	}

	else
	{
		if (obj_func_str.size() == 0)
		{
			f_rec << " warning: no ++opt_objective_function-->forming a generic objective function (1.0 coef for each decision var)" << endl;
			for (auto &name : ctl_ord_dec_var_names)
				obj_func_coef_map[name] = 1.0;
		}

		//or if it is a prior info equation
		else if (pest_scenario.get_prior_info().find(obj_func_str) != pest_scenario.get_prior_info().end())
		{
			obj_func_coef_map = pest_scenario.get_prior_info().get_pi_rec_ptr(obj_func_str).get_atom_factors();
			//throw_sequentialLP_error("prior-information-based objective function not implemented");
		}
		else
		{
			//check if this obj_str is a filename
			ifstream if_obj(obj_func_str);
			if (!if_obj.good())
				throw_sequentialLP_error("unrecognized ++opt_objective_function arg: " + obj_func_str);
			else
				obj_func_coef_map = pest_utils::read_twocol_ascii_to_map(obj_func_str);
		}

		//check that all obj_coefs are decsision vars
		vector<string> missing_vars;
		for (auto &coef : obj_func_coef_map)
			if (find(ctl_ord_dec_var_names.begin(), ctl_ord_dec_var_names.end(), coef.first) == ctl_ord_dec_var_names.end())
				missing_vars.push_back(coef.first);
		if (missing_vars.size() > 0)
			throw_sequentialLP_error("the following objective function components are not decision variables: ", missing_vars);

	}

	jco.set_base_numeric_pars(all_pars_and_dec_vars);
	jco.set_base_sim_obs(pest_scenario.get_ctl_observations());
	if (pest_scenario.get_pestpp_options().get_opt_coin_log())
		model.setLogLevel(4 + 8 + 16 + 32);
	initial_report();
	return;
}

void sequentialLP::build_obj_func_coef_array()
{
	ctl_ord_obj_func_coefs = new double[num_dec_vars()];
	double coef;
	map<string, double>::iterator end = obj_func_coef_map.end();
	int i = 0;
	for (auto &name : ctl_ord_dec_var_names)
	{
		if (obj_func_coef_map.find(name) != end)
			ctl_ord_obj_func_coefs[i] = obj_func_coef_map.at(name);
		else
			ctl_ord_obj_func_coefs[i] = 0.0;
		i++;
	}
	return;
}

void sequentialLP::iter_infeasible_report()
{
	ofstream &f_rec = file_mgr_ptr->rec_ofstream();
	int num_inf = model.numberPrimalInfeasibilities();

	double * inf_ray = new double[num_dec_vars()];
	inf_ray = model.infeasibilityRay(true);
	//inf_ray = model.unboundedRay();

	double sum_inf_primal = model.sumPrimalInfeasibilities();
	double sum_inf_dual = model.sumDualInfeasibilities();
	double inf_cost = model.infeasibilityCost();
	f_rec << "----------------------------------" << endl;
	f_rec << "  ---  infeasibility report  ---  " << endl;
	f_rec << "----------------------------------" << endl;

	f_rec << "-->number primary infeasible constraints: " << num_inf << endl;
	f_rec << "-->sum of primal infeasibilites: " << sum_inf_primal << endl;
	//ClpSimplex::Status stat = model.getRowStatus(0);
	//f_rec << "-->infeasibility cost: " << inf_cost << endl;
	if (model.rayExists())
		for (int i=0;i<num_dec_vars();++i)
			f_rec << inf_ray[i] << endl;
	f_rec << endl << endl;
	return;
}

void sequentialLP::build_dec_var_bounds()
{
	//set the decision var lower and upper bound arrays
	dec_var_lb = new double[num_dec_vars()];
	dec_var_ub = new double[num_dec_vars()];
	Parameters parlbnd = pest_scenario.get_ctl_parameter_info().get_low_bnd(ctl_ord_dec_var_names);
	Parameters parubnd = pest_scenario.get_ctl_parameter_info().get_up_bnd(ctl_ord_dec_var_names);
	for (int i = 0; i < num_dec_vars(); ++i)
	{
		dec_var_lb[i] = parlbnd.get_rec(ctl_ord_dec_var_names[i]) - all_pars_and_dec_vars.get_rec(ctl_ord_dec_var_names[i]);
		dec_var_ub[i] = parubnd.get_rec(ctl_ord_dec_var_names[i]) - all_pars_and_dec_vars.get_rec(ctl_ord_dec_var_names[i]);
	}
}

void sequentialLP::iter_solve()
{

	ofstream &f_rec = file_mgr_ptr->rec_ofstream();

	//convert Jacobian_1to1 to CoinPackedMatrix
	cout << "  ---  forming LP model  --- " << endl;
	CoinPackedMatrix matrix = jacobian_to_coinpackedmatrix();

	build_dec_var_bounds();

	pair<vector<double>, vector<double>> bounds = constraints.get_constraint_bound_vectors();
	constraints.presolve_report(slp_iter);
	//load the linear simplex model
	//model.loadProblem(matrix, dec_var_lb, dec_var_ub, ctl_ord_obj_func_coefs, constraint_lb, constraint_ub);
	model.loadProblem(matrix, dec_var_lb, dec_var_ub, ctl_ord_obj_func_coefs, bounds.first.data(), bounds.second.data());
	vector<string> obs_constraint_names = constraints.get_obs_constraint_names();
	vector<string> pi_constraint_names = constraints.get_pi_constraint_names();
	for (int i = 0; i < constraints.num_obs_constraints(); ++i)
		model.setRowName(i, obs_constraint_names[i]);
	for (int i = 0; i < pi_constraint_names.size(); ++i)
		model.setRowName(i+constraints.num_obs_constraints(), pi_constraint_names[i]);
	for (int i = 0; i < num_dec_vars(); ++i)
		model.setColumnName(i, ctl_ord_dec_var_names[i]);

	model.setOptimizationDirection(pest_scenario.get_pestpp_options().get_opt_direction());
	//if maximum ++opt_coin_loglev, then also write iteration specific mps files
	if (pest_scenario.get_pestpp_options().get_opt_coin_log())
	{
		stringstream ss;
		ss << slp_iter << ".mps";
		string mps_name = file_mgr_ptr->build_filename(ss.str());
		model.writeMps(mps_name.c_str(),0,1);
	}
	f_rec << "  ---  solving linear program for iteration " << slp_iter << "  ---  " << endl;
	cout << "  ---  solving linear program for iteration " << slp_iter << "  ---  " << endl;

	//solve the linear program
	ClpPresolve presolve_info;
	ClpSimplex* presolved_model = presolve_info.presolvedModel(model);

	//if presolvedModel is Null, then it is primal infeasible, so
	//try the dual
	if (!presolved_model)
	{
		f_rec << "  ---  primal presolve model infeasible, crashing solution with additional dual and primal solves..." << endl;
		cout << "  ---  primal presolve model infeasible, crashing solution..." << endl;

		model.moveTowardsPrimalFeasible();
		model.dual(1);
		model.checkSolution();
		model.primal(1);
	}

	//update the status arrays of both the presolve and original models
	presolve_info.postsolve(true);

	//this seems to help with some test problems
	model.dual(1);
	//int all_slack_basis = model.crash(0.001,0);

	//model.primal();
	model.checkSolution();
	if (!model.primalFeasible())
	{
		model.dual(1);
		model.checkSolution();
		model.primal(1);
	}

	//check the solution
	model.checkSolution();
	if (model.isProvenOptimal())
	{
		f_rec << " iteration " << slp_iter << " linear solution is proven optimal" << endl << endl;
		cout << " iteration " << slp_iter << " linear solution is proven optimal" << endl << endl;
	}

	else if (!model.primalFeasible())
	{
		f_rec << "  ---  warning: primal solution infeasible, terminating iterations ---  " << endl;
		cout << "  ---  warning: primal solution infeasible, terminating iterations  ---  " << endl;
		iter_infeasible_report();
		terminate = true;
	}

	else
	{
		f_rec << endl << "iteration " << slp_iter << " linear solution is not proven optimal...continuing" << endl << endl;
		cout << endl << "iteration " << slp_iter << " linear solution is not proven optimal...continuing" << endl << endl;
	}

	f_rec << endl << "  ---  linear program solution complete for iteration " << slp_iter << "  ---  " << endl;
	cout << endl << "  ---  linear program solution complete for iteration " << slp_iter << "  ---  " << endl;

	return;
}

CoinPackedMatrix sequentialLP::jacobian_to_coinpackedmatrix()
{

	Eigen::SparseMatrix<double> eig_ord_jco = jco.get_matrix(constraints.get_obs_constraint_names(), ctl_ord_dec_var_names);

	file_mgr_ptr->rec_ofstream() << "number of nonzero elements in response matrix: " << eig_ord_jco.nonZeros() << " of " << eig_ord_jco.size() << endl;
	cout << "number of nonzero elements in response matrix: " << eig_ord_jco.nonZeros() << " of " << eig_ord_jco.size() << endl;

	if (eig_ord_jco.nonZeros() == 0)
		throw_sequentialLP_error("response matrix all zeros...cannot continue");

	//cout << eig_ord_jco << endl;

	//the triplet elements to pass to the coinpackedmatrix constructor
	int num_elems = eig_ord_jco.nonZeros() + constraints.get_num_nz_pi_constraint_elements();
	int * row_idx = new int[num_elems];
	int * col_idx = new int[num_elems];
	double * elems = new double[num_elems];
	int elem_count = 0;

	//iterate through the eigen sparse matrix
	int elems_par;
	int npar_zelems = 0;
	for (int i = 0; i < eig_ord_jco.outerSize(); ++i)
	{
		elems_par = 0;
		for (Eigen::SparseMatrix<double>::InnerIterator it(eig_ord_jco, i); it; ++it)
		{
			row_idx[elem_count] = it.row();
			col_idx[elem_count] = it.col();
			elems[elem_count] = it.value();
			elems_par++;
			elem_count++;
		}
		if (elems_par == 0)
		{
			//cout << "all zero elements for decision variable: " << ctl_ord_dec_var_names[i] << endl;
			npar_zelems++;
		}
	}
	cout << "number of decision variables with all zero elements: " << npar_zelems << endl;
	if (elem_count != eig_ord_jco.nonZeros())
	{
		throw_sequentialLP_error("sequentialLP::jacobian_to_coinpackedmatrix() error: wrong number of triplet components");
	}

	if (elem_count == 0)
	{
		throw_sequentialLP_error("sequentialLP::jacobian_to_coinpackedmatrix() error: zero triplets found");
	}

	//prior information constraints
	int irow = constraints.num_obs_constraints();
	int jcol;
	vector<string>::iterator start = ctl_ord_dec_var_names.begin();
	vector<string>::iterator end = ctl_ord_dec_var_names.end();
	PriorInformation constraints_pi = constraints.get_pi_constraints();
	for (auto &pi_name : constraints.get_pi_constraint_names())
	{
		for (auto &pi_factor : constraints_pi.get_pi_rec_ptr(pi_name).get_atom_factors())
		{
			jcol = find(start, end, pi_factor.first) - start;
			row_idx[elem_count] = irow;
			col_idx[elem_count] = jcol;
			elems[elem_count] = pi_factor.second;
			elem_count++;
		}
		irow++;
	}
	if (elem_count != num_elems)
		throw_sequentialLP_error("problem packing prior information constraints into CoinPackedMatrix...");

	CoinPackedMatrix matrix(true,row_idx,col_idx,elems,elem_count);

	//this is useful for debugging
	//cout << eig_ord_jco << endl << endl;
	//matrix.dumpMatrix();
	return matrix;
}

void sequentialLP::solve()
{
	ofstream &f_rec = file_mgr_ptr->rec_ofstream();

	slp_iter = 1;
	while (true)
	{
		f_rec << endl << endl << "  ---------------------------------" << endl;
		f_rec <<         "  --- starting LP iteration " << slp_iter << "  ---  " << endl;
		f_rec << "  ---------------------------------" << endl << endl << endl;
		cout << endl << endl << "  ----------------------------------" << endl;
		cout << "  --- starting LP iteration " << slp_iter << "  ---  " << endl;
		cout << "  ---------------------------------" << endl << endl << endl;

		iter_presolve();
		iter_solve();
		iter_postsolve();
		if (terminate) break;
		slp_iter++;
		if (slp_iter > pest_scenario.get_control_info().noptmax)
			break;
	}
	f_rec << endl << "  ---  objective function sequence  ---   " << endl << setw(10) << "iteration" << setw(15) << "obj func" << endl;
	int i = 0;
	for (auto &obj : iter_obj_values)
	{
		f_rec << setw(10) << i << setw(15) << obj << endl;
		i++;
	}
	f_rec << "  ---  best objective function value: " << obj_best << endl;
	cout << "  ---  best objective function value: " << obj_best << endl;

	if (!pest_scenario.get_pestpp_options().get_opt_skip_final())
	{
		f_rec << "  ---  running model one last time with best decision variables  ---  " << endl;
		cout << "  ---  running model one last time with best decision variables  ---  " << endl;

		bool success = make_upgrade_run(all_pars_and_dec_vars_best, current_constraints_sim);
		if (!success)
		{
			throw_sequentialLP_error("error running model with best decision variable values");
		}
		//write 'best' rei
		of_wr.write_opt_constraint_rei(file_mgr_ptr->open_ofile_ext("res"), slp_iter, all_pars_and_dec_vars,
			pest_scenario.get_ctl_observations(), current_constraints_sim);
		file_mgr_ptr->close_file("res");

	}
}

void sequentialLP::iter_postsolve()
{
	ofstream &f_rec = file_mgr_ptr->rec_ofstream();
	f_rec << "  ---  processing results for iteration " << slp_iter << " LP solution  ---  " << endl << endl;

	//extract (optimal) decision vars
	//and track some info for convergence checking
	const double *dec_var_vals = model.getColSolution();
	double max_abs_dec_var_change = -1.0E+10;
	double max_abs_dec_var_val = -1.0E+10;

	double diff, val;
	Parameters upgrade_pars(all_pars_and_dec_vars);
	if (!model.primalFeasible())
	{
		for (auto &name : ctl_ord_dec_var_names)
		{
			upgrade_pars.update_rec(name, numeric_limits<double>::lowest());

		}
		stringstream ss;
		ss << slp_iter << ".par";
		of_wr.write_par(file_mgr_ptr->open_ofile_ext(ss.str()), upgrade_pars, *par_trans.get_offset_ptr(), *par_trans.get_scale_ptr());
		file_mgr_ptr->close_file(ss.str());
		of_wr.write_par(file_mgr_ptr->open_ofile_ext("par"), upgrade_pars, *par_trans.get_offset_ptr(), *par_trans.get_scale_ptr());
		file_mgr_ptr->close_file("par");
		f_rec << " --- warning: parameter file " << ss.str() << "contains double min (extreme) values" << endl;
		f_rec << "     and no res / rei files are being written b/c solution is infeasible" << endl;

		return;
	}

	//check for denormal solution values and values (slightly) out of bounds - reset accordingly...
	string name;
	vector<double> optimal_vals;

	for (int i = 0; i < num_dec_vars(); ++i)
	{
		name = ctl_ord_dec_var_names[i];
		val = dec_var_vals[i];

		if (val < dec_var_lb[i])
		{
			f_rec << "resetting optimal value for " << name << "from " << val << "to lower bound of" << dec_var_lb[i] << endl;
			optimal_vals.push_back(dec_var_lb[i]);
		}
		else if (val > dec_var_ub[i])
		{
			f_rec << "resetting optimal value for " << name << "from " << val << "to upper bound of" << dec_var_ub[i] << endl;
			optimal_vals.push_back(dec_var_ub[i]);
		}
		else
		{
			if (abs(val) < 1.0e-30) // in case forward model is single precision
			{
				f_rec << "flushing optimal value for " << name << "from " << val << "to 0.0" << endl;
				optimal_vals.push_back(0.0);
			}
			else
				optimal_vals.push_back(val);
		}
	}
	//delete dec_var_vals;

	Parameters dv_changes = upgrade_pars;
	for (int i = 0; i < num_dec_vars(); ++i)
	{
		name = ctl_ord_dec_var_names[i];
		val = all_pars_and_dec_vars[name];
		diff = abs(optimal_vals[i] - all_pars_and_dec_vars[name]);
		upgrade_pars.update_rec(name,optimal_vals[i] + val);
		dv_changes.update_rec(name,upgrade_pars[name] - val);
		max_abs_dec_var_change = (diff > max_abs_dec_var_change) ? diff : max_abs_dec_var_change;
		max_abs_dec_var_val = (abs(val) > max_abs_dec_var_val) ? val : max_abs_dec_var_val;
	}
	max_abs_dec_var_change /= max(max_abs_dec_var_val,1.0);

	pair<double, double> cur_new_obj = postsolve_decision_var_report(upgrade_pars);

	double obj_val = model.getObjValue();

	f_rec << endl << endl << "  ---  iteration " << slp_iter << " objective function value: " << setw(15) << cur_new_obj.second << "  ---  " << endl << endl;
	cout << endl << endl << "  ---  iteration " << slp_iter << " objective function value: " << setw(15) << cur_new_obj.second << "  ---  " << endl << endl;

	row_price = model.getRowPrice();

	constraints.postsolve_pi_constraints_report(upgrade_pars,slp_iter);

	Observations upgrade_obs = current_constraints_sim;

	Eigen::VectorXd est_obs_vec = current_constraints_sim.get_data_eigen_vec(constraints.get_obs_constraint_names()) +
		jco.get_matrix(constraints.get_obs_constraint_names(), ctl_ord_dec_var_names) *
		dv_changes.get_partial_data_eigen_vec(ctl_ord_dec_var_names);
	upgrade_obs.update_without_clear(constraints.get_obs_constraint_names(), est_obs_vec);
	constraints.postsolve_obs_constraints_report(upgrade_obs, "estimated",slp_iter);
	constraints.write_res_files(upgrade_obs, upgrade_pars, "est",slp_iter);

	if (!super_secret_option)
	{
		f_rec << "  ---  running the model once with optimal decision var values" << endl;
		bool success = make_upgrade_run(upgrade_pars, upgrade_obs);
		if (!success)
		{
			f_rec << " --- optimal decision var value run failed, cannot continue... " << endl;
			return;
		}

		//postsolve_constraint_report(upgrade_obs, upgrade_pars, "simulated");
		constraints.postsolve_obs_constraints_report(upgrade_obs, "simulated", slp_iter);
		constraints.write_res_files(upgrade_obs, upgrade_pars, "sim", slp_iter);

	}

	//track the objective function values
	if (slp_iter == 1)
	{
		iter_obj_values.push_back(cur_new_obj.first);
		obj_best = cur_new_obj.second;
	}
	iter_obj_values.push_back(cur_new_obj.second);
	double obj_func_change = abs(cur_new_obj.first - cur_new_obj.second) / abs(max(max(cur_new_obj.first,cur_new_obj.second),1.0));
	//if this is a max problem
	if (pest_scenario.get_pestpp_options().get_opt_direction() == -1)
	{
		if (cur_new_obj.second > obj_best)
		{
			all_pars_and_dec_vars_best.update_without_clear(ctl_ord_dec_var_names, upgrade_pars.get_data_vec(ctl_ord_dec_var_names));
			obj_best = cur_new_obj.second;
		}
	}
	else
		if (cur_new_obj.second < obj_best)
		{
			all_pars_and_dec_vars_best.update_without_clear(ctl_ord_dec_var_names, upgrade_pars.get_data_vec(ctl_ord_dec_var_names));
			obj_best = cur_new_obj.second;
		}

	//check for changes for in constraints
	double max_abs_constraint_change = constraints.get_max_constraint_change(upgrade_obs);
	double max_abs_constraint_val = -1.0E+10;
	Observations constraints_sim = current_constraints_sim;
	for (auto &name : constraints.get_obs_constraint_names())
	{

		diff = abs(constraints_sim[name] - upgrade_obs[name]);
		max_abs_constraint_change = (diff > max_abs_constraint_change) ? diff : max_abs_constraint_change;
		max_abs_constraint_val = (val > max_abs_constraint_val) ? val : max_abs_constraint_val;
	}
	max_abs_constraint_change /= max(max_abs_constraint_val,1.0);

	map<string,double> invalid_dec_vars = get_out_of_bounds_dec_vars(upgrade_pars);

	if (super_secret_option)
	{
		f_rec << "super secret option active...done" << endl;
		return;
	}

	//convergence check
	double opt_iter_tol = pest_scenario.get_pestpp_options().get_opt_iter_tol();

	f_rec << endl << "  ---  convergence check iteration " << slp_iter << "  ---  " << endl << endl;
	f_rec << "-->                     ++opt_iter_tol:" << setw(15) << opt_iter_tol << endl;
	f_rec << "-->scaled max decision variable change:" << setw(15) << max_abs_dec_var_change << endl;
	f_rec << "-->      scaled  max constraint change:" << setw(15) << max_abs_constraint_change << endl;
	f_rec << "-->   scaled objective function change:" << setw(15) << obj_func_change << endl;

	bool valid = true;
	if (invalid_dec_vars.size() > 0)
	{
		valid = false;
		f_rec << "-->the following decision variables are out of bounds (distance shown):" << endl;
		for (auto &name : invalid_dec_vars)
			f_rec << "-->   " << name.first << setw(15) << name.second << endl;
	}
	map<string, double> invalid_constraints = constraints.get_unsatified_obs_constraints(upgrade_obs, opt_iter_tol);
	map<string, double> invalid_pi_constraints = constraints.get_unsatified_pi_constraints(upgrade_pars, opt_iter_tol);
	invalid_constraints.insert(invalid_pi_constraints.begin(), invalid_pi_constraints.end());
	if (invalid_constraints.size() > 0)
	{
		valid = false;
		f_rec << "-->the following constraints are not satisfied (distance shown):" << endl;
		for (auto &name : invalid_constraints)
			f_rec << "-->   " << name.first << setw(15) << name.second << endl;
	}

	//if three or more iters have passed, start testing the last three
	//obj func vals to see if we have stagnated
	bool obj_func_stag = false;

	int num_obj_vals = iter_obj_values.size();
	if (num_obj_vals > 3)
	{
		obj_func_stag = true;
		double last = iter_obj_values[num_obj_vals-1];
		for (int i = num_obj_vals-2; i >= num_obj_vals-4; --i)
		{
			diff = abs((iter_obj_values[i] - last) / max(1.0, last));
			if (diff > opt_iter_tol)
			{
				obj_func_stag = false;
				break;
			}
			last = iter_obj_values[i];
		}
	}

	//if the obj func has stopped changing or all the other criteria are met
	if ((obj_func_stag) || ((valid) && (max_abs_dec_var_change <= opt_iter_tol) && (max_abs_constraint_change <= opt_iter_tol) && (obj_func_change <= opt_iter_tol)))
	{
		f_rec << endl << "  ---  SLP convergence  ---  " << endl << endl;
		cout << endl << "  ---  SLP convergence  ---  " << endl << endl;
		terminate = true;
	}
	else
	{

	}

	//if continuing, update the master decision var instance
	all_pars_and_dec_vars.update_without_clear(ctl_ord_dec_var_names, upgrade_pars.get_data_vec(ctl_ord_dec_var_names));
	current_constraints_sim = upgrade_obs;
	return;
}

bool sequentialLP::make_upgrade_run(Parameters &upgrade_pars, Observations &upgrade_obs)
{

	cout << "  ---  running the model once with optimal decision variables  ---  " << endl;
	int run_id = run_mgr_ptr->add_run(par_trans.ctl2model_cp(upgrade_pars));
	run_mgr_ptr->run();
	bool success = run_mgr_ptr->get_run(run_id, upgrade_pars, upgrade_obs);
	if (success)
		par_trans.model2ctl_ip(upgrade_pars);
	return success;
}

void sequentialLP::iter_presolve()
{
	ofstream &f_rec = file_mgr_ptr->rec_ofstream();
	f_rec << "  ---  calculating response matrix for iteration " << slp_iter << "  ---  " << endl;
	cout << "  ---  calculating response matrix for iteration " << slp_iter << "  ---  " << endl;

	//read an existing jacobain
	string basejac_filename = pest_scenario.get_pestpp_options().get_basejac_filename();
	Parameters pars = all_pars_and_dec_vars_best;
	if ((slp_iter == 1) && (basejac_filename.size() > 0))
	{
		jco.read(basejac_filename);
		//check to make sure decision vars and constraints are found
		vector<string> temp = jco.get_base_numeric_par_names();
		set<string> names(temp.begin(),temp.end());
		set<string>::iterator start = names.begin();
		set<string>::iterator end = names.end();
		//vector<string>::iterator ext_start = ctl_ord_ext_var_names.begin();
		//vector<string>::iterator ext_end = ctl_ord_ext_var_names.end();
		set<string> ext_set(ctl_ord_ext_var_names.begin(), ctl_ord_ext_var_names.end());
		vector<string> missing;
		for (auto &name : ctl_ord_dec_var_names)
			//if this dec var is not in the jco and is not an external var
			//if ((find(start, end, name) == end) && (find(ext_start,ext_end,name) == ext_end))
			if ((names.find(name) == end) && (ext_set.find(name) == ext_set.end()))
				missing.push_back(name);
		if (missing.size() > 0)
			throw_sequentialLP_error("the following decision vars were not found in the jacobian " + basejac_filename + " : ", missing);
		if ((constraints.get_use_chance()) && (constraints.get_use_fosm()))
		{
			for (auto& name : constraints.get_adj_par_names())
				//if (find(start, end, name) == end)
				if ((names.find(name) == end))
					missing.push_back(name);
			if (missing.size() > 0)
				throw_sequentialLP_error("the following adjustable parameters were not found in the jacobian " + basejac_filename + " : ", missing);
		}

		names.clear();
		temp = jco.get_sim_obs_names();
		names.insert(temp.begin(),temp.end());
		start = names.begin();
		end = names.end();
		for (auto &name : constraints.get_obs_constraint_names())
			if (find(start, end, name) == end)
				missing.push_back(name);
		if (missing.size() > 0)
			throw_sequentialLP_error("the following constraints were not found in the jacobian " + basejac_filename + " : ", missing);

		for (auto &name : constraints.get_nz_obs_names())
			if (find(start, end, name) == end)
				missing.push_back(name);
		if (missing.size() > 0)
			throw_sequentialLP_error("the following non-zero weight observations were not found in the jacobian " + basejac_filename + " : ", missing);
		if ((constraints.get_use_chance()) && (constraints.get_use_fosm()))
			constraints.set_jco(jco);
		string res_filename = pest_scenario.get_pestpp_options().get_hotstart_resfile();
		//Observations constraints_sim = constraints.get_current_constaints_sim();
		if (!res_filename.empty())
		{
			stringstream message;
			message << "  reading  residual file " << res_filename << " for hot-start...";
			cout << message.str();
			f_rec << message.str();
			for (auto &oname : pest_scenario.get_ctl_ordered_obs_names())
				current_constraints_sim[oname] = -1.0e+30;
			pest_utils::read_res(res_filename, current_constraints_sim);
			f_rec << "done" << endl;
			cout << "done" << endl;
			if ((constraints.should_update_chance(slp_iter)) && (!constraints.get_use_fosm()))
				constraints.add_runs(run_mgr_ptr);

		}
		else
		{
			//make the intial base run
			cout << "  ---  running the model once with initial decision variables  ---  " << endl;
			int run_id = run_mgr_ptr->add_run(par_trans.ctl2model_cp(all_pars_and_dec_vars));
			//this would be only for stack runs since the fosm runs should have been in the jco
			if ((constraints.should_update_chance(slp_iter)) && (!constraints.get_use_fosm()))
				constraints.add_runs(run_mgr_ptr);
			/*else
			{
				cout << "  ---  running the model once with initial decision variables  ---  " << endl;
			}*/
			run_mgr_ptr->run();
			Parameters pars;
			bool success = run_mgr_ptr->get_run(run_id, pars, current_constraints_sim);
			if (!success)
				throw_sequentialLP_error("initial (base) run with initial decision vars failed...cannot continue");

		}
		constraints.process_runs(run_mgr_ptr, slp_iter);
	}

	//otherwise, fill the jacobian
	else
	{
		set<string> out_of_bounds;
		vector<string> names_to_run;
		for (auto& name : ctl_ord_dec_var_names)
		{
			if (find(ctl_ord_ext_var_names.begin(), ctl_ord_ext_var_names.end(), name) == ctl_ord_ext_var_names.end())
				names_to_run.push_back(name);
		}
		if ((constraints.should_update_chance(slp_iter)) && (constraints.get_use_fosm()))
		{
				//names_to_run.insert(names_to_run.end(), adj_par_names.begin(), adj_par_names.end());
				vector<string> fosm_par_names = constraints.get_fosm_par_names();
				names_to_run.insert(names_to_run.end(), fosm_par_names.begin(), fosm_par_names.end());
		}

		//turn down the purb value each iteration
		if ((slp_iter > 1) && (iter_derinc_fac != 1.0))
		{
			cout << "  ---  decreasing derinc for active decision variables " << endl;
			file_mgr_ptr->rec_ofstream() << "  ---  decreasing derinc for active decision variables " << endl;

			//find group names for active dec vars
			vector<string> act_dv_grps;
			ParameterGroupInfo* pinfo = pest_scenario.get_base_group_info_ptr();

			const ParameterGroupRec *gr_ptr;
			for (auto &name : ctl_ord_dec_var_names)
			{
				gr_ptr = pinfo->get_group_rec_ptr(name);
				if (find(act_dv_grps.begin(), act_dv_grps.end(), gr_ptr->name) == act_dv_grps.end())
					act_dv_grps.push_back(gr_ptr->name);
			}
			double org_derinc, new_derinc;
			ofstream &frec = file_mgr_ptr->rec_ofstream();
			frec << setw(20) << left << "group_name" << setw(20) << left << "old_derinc" << setw(20) << left << "new_derinc" << endl;
			for (auto &name : act_dv_grps)
			{
				org_derinc = pinfo->get_group_by_groupname_4_mod(name)->derinc;
				new_derinc = org_derinc * iter_derinc_fac;
				pinfo->get_group_by_groupname_4_mod(name)->derinc = new_derinc;
				frec << setw(20) << left << name << setw(20) << left << org_derinc << setw(20) << left << new_derinc << endl;
			}
		}

		bool init_obs = false;
		if (slp_iter == 1) init_obs = true;

		bool success = jco.build_runs(all_pars_and_dec_vars, current_constraints_sim, names_to_run, par_trans,
			pest_scenario.get_base_group_info(), pest_scenario.get_ctl_parameter_info(),
			*run_mgr_ptr, out_of_bounds,false,init_obs);
		if (!success)
		{
			const set<string> failed = jco.get_failed_parameter_names();
			throw_sequentialLP_error("failed to calc derviatives for the following decision vars: ", failed);
		}

		if ((constraints.should_update_chance(slp_iter)) && (!constraints.get_use_fosm()))
		{
			constraints.add_runs(run_mgr_ptr);
		}

		//jco.make_runs(*run_mgr_ptr);
		run_mgr_ptr->run();
		set<int> failed = run_mgr_ptr->get_failed_run_ids();

		//process the remaining responses
		success = jco.process_runs(par_trans, pest_scenario.get_base_group_info(), *run_mgr_ptr,
			*null_prior, false,false);
		if (!success)
			throw_sequentialLP_error("error processing response matrix runs ", jco.get_failed_parameter_names());
		constraints.process_runs(run_mgr_ptr, slp_iter);
		stringstream ss;
		ss << slp_iter << ".jcb";
		string rspmat_file = file_mgr_ptr->build_filename(ss.str());
		f_rec << endl << "saving iteration " << slp_iter << " reponse matrix to file: " << rspmat_file << endl;
		jco.save(ss.str());

		//check for failed runs
		//TODO: something better than just dying
		if (failed.size() > 0)
			throw_sequentialLP_error("failed runs when filling decision var response matrix...cannot continue ");

		//get the base run and update simulated constraint values

		if (init_obs)
		{
			//Observations temp_obs;
			run_mgr_ptr->get_run(0, all_pars_and_dec_vars, current_constraints_sim, false);
		}

	}

	if (use_obj_obs)
	{
		obj_func_coef_map.clear();
		vector<string> onames = jco.get_sim_obs_names();
		vector<string> pnames = jco.get_base_numeric_par_names();
		set<string> sdecvar(ctl_ord_dec_var_names.begin(), ctl_ord_dec_var_names.end());

		int idx = find(onames.begin(), onames.end(), obj_obs) - onames.begin();
		if (idx >= onames.size())
			throw runtime_error("obj function obs not found in jco row names, #sad");
		Eigen::VectorXd vec = Eigen::VectorXd(jco.get_matrix_ptr()->row(idx));
		for (int i = 0; i < vec.size(); i++)
		{
			if (sdecvar.find(pnames[i]) == sdecvar.end())
				continue;
			obj_func_coef_map[pnames[i]] = vec[i];
		}
		f_rec << "  ---  objective function coefficients for iteration " << slp_iter << "  ---  " << endl;
		double obj = obj_func_report();
		if (slp_iter == 1)
		{
			obj_init = obj;
			f_rec << endl << "  ---  objective function value (using initial dec var values): " << obj_init << endl << endl;
			cout << endl << "  ---  objective function value (using initial dec var values): " << obj_init << endl << endl;
		}

	}

	//build the objective function
	build_obj_func_coef_array();

	stringstream ss;
	if ((constraints.get_use_chance()) && (constraints.get_use_fosm()))
		constraints.set_jco(jco);
	constraints.update_chance_offsets();
	constraints.presolve_chance_report(slp_iter);
	constraints.write_res_files(current_constraints_sim, pars,"jcb",slp_iter);
	return;
}


#include "renderers.hpp"
#include "mesh_util.hpp"
#include "gl_errors.hpp"

#include <Eigen/Dense>

#include <algorithm>
#include <unordered_set>

using Eigen::Affine3f;
using Eigen::Matrix3f;
using Eigen::Vector3f;
using Eigen::Scaling;
using Eigen::Translation3f;

namespace merely3d
{
    template <typename Shape>
    Affine3f build_model_transform(const Renderable<Shape> & renderable)
    {
        return Translation3f(renderable.position)
               * renderable.orientation
               * Scaling(renderable.scale);
    }

    static void enable_wireframe_rendering(bool enable)
    {
        if (enable)
        {
            glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
        }
        else
        {
            glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
        }
    }

    /// Renders a set of static meshes that all share the same GlTriangleMesh. This is useful if the same 3D model
    /// is being rendered many times, but with different transforms or materials.
    ///
    /// NB! Assumes that the uniforms not specific
    /// to the individual renderable are all correctly set.
    void render_static_meshes(std::vector<Renderable<StaticMesh>> & renderables,
                              GlTriangleMesh & gl_mesh,
                              ShaderCollection & shaders)
    {
        gl_mesh.bind();
        auto & line_shader = shaders.line_shader();
        auto & mesh_shader = shaders.mesh_shader();

        auto draw_primitive = [&] (const Renderable<StaticMesh> & renderable)
        {
            const auto ref_transform = Eigen::Matrix3f::Identity();
            const auto model = build_model_transform(renderable);

            if (renderable.material.wireframe)
            {
                line_shader.set_model_transform(model);
                line_shader.set_object_color(renderable.material.color);
                glDrawElements(GL_TRIANGLES, gl_mesh.index_count(), GL_UNSIGNED_INT, 0);
            }
            else
            {
                const auto normal_transform = Matrix3f(model.linear().inverse().transpose());
                mesh_shader.set_model_transform(model);
                mesh_shader.set_normal_transform(normal_transform);
                mesh_shader.set_object_color(renderable.material.color);
                mesh_shader.set_reference_transform(ref_transform);
                mesh_shader.set_pattern_grid_size(std::max(0.0f, renderable.material.pattern_grid_size));
                glDrawElements(GL_TRIANGLES, gl_mesh.index_count(), GL_UNSIGNED_INT, 0);
            }
        };

        // Partition vector so that renderables that are to be rendered as wireframes
        // come first
        auto filled_begin = std::partition(renderables.begin(), renderables.end(),
                                           [] (const Renderable<StaticMesh> & renderable)
                                           {
                                               return renderable.material.wireframe;
                                           });

        // Don't cull faces when rendering wireframes
        glDisable(GL_CULL_FACE);
        line_shader.use();
        enable_wireframe_rendering(true);
        std::for_each(renderables.begin(), filled_begin, draw_primitive);

        // But do cull back faces for everything else
        // (Note: This is especially important for correct rendering of "flat" meshes,
        // in which a given triangle has two faces pointing opposite directions)
        glEnable(GL_CULL_FACE);
        glCullFace(GL_BACK);
        mesh_shader.use();
        enable_wireframe_rendering(false);
        std::for_each(filled_begin, renderables.end(), draw_primitive);
    }

    /// Render primitives.
    ///
    /// NB! Assumes that the uniforms not specific
    /// to the individual renderable are all correctly set.
    template <typename ReferenceTransform, typename Shape>
    void render_primitives(std::vector<Renderable<Shape>> & renderables,
                           ShaderCollection & shaders,
                           GlPrimitive & primitive,
                           ReferenceTransform && reference_transform)
    {
        primitive.bind();
        auto & line_shader = shaders.line_shader();
        auto & mesh_shader = shaders.mesh_shader();

        auto draw_primitive = [&] (const Renderable<Shape> & renderable)
        {
            const auto ref_transform = reference_transform(renderable.shape);
            const auto model = build_model_transform(renderable) * ref_transform;

            if (renderable.material.wireframe)
            {
                line_shader.set_model_transform(model);
                line_shader.set_object_color(renderable.material.color);
                glDrawArrays(GL_TRIANGLES, 0, primitive.vertex_count());
            }
            else
            {
                const auto normal_transform = Matrix3f(model.linear().inverse().transpose());
                mesh_shader.set_model_transform(model);
                mesh_shader.set_normal_transform(normal_transform);
                mesh_shader.set_object_color(renderable.material.color);
                mesh_shader.set_reference_transform(Matrix3f(ref_transform));
                mesh_shader.set_pattern_grid_size(std::max(0.0f, renderable.material.pattern_grid_size));
                glDrawArrays(GL_TRIANGLES, 0, primitive.vertex_count());
            }
        };

        // Partition vector so that renderables that are to be rendered as wireframes
        // come first
        auto filled_begin = std::partition(renderables.begin(), renderables.end(),
            [] (const Renderable<Shape> & renderable)
            {
                return renderable.material.wireframe;
            });

        // Don't cull faces when rendering wireframes
        glDisable(GL_CULL_FACE);
        line_shader.use();
        enable_wireframe_rendering(true);
        std::for_each(renderables.begin(), filled_begin, draw_primitive);

        // But do cull back faces for everything else
        // (Note: this is absolutely necessary for rectangles)
        glEnable(GL_CULL_FACE);
        glCullFace(GL_BACK);
        mesh_shader.use();
        enable_wireframe_rendering(false);
        std::for_each(filled_begin, renderables.end(), draw_primitive);
    }

    /// Returns the linear transformation that
    /// transforms a reference cube into the provided Box.
    Eigen::AlignedScaling3f box_reference_transform(const Box & box)
    {
        return Scaling(box.extents);
    }

    Eigen::AlignedScaling3f rectangle_reference_transform(const Rectangle & rectangle)
    {
        const auto & extents = rectangle.extents;
        return Scaling(extents.x(), extents.y(), 1.0f);
    }

    Eigen::AlignedScaling3f sphere_reference_transform(const Sphere & sphere)
    {
        const auto r = sphere.radius;
        return Scaling(r, r, r);
    }

    TrianglePrimitiveRenderer TrianglePrimitiveRenderer::build()
    {
        const auto cube_verts = unit_cube_vertices_and_normals();
        auto gl_cube = GlPrimitive::create(cube_verts);

        const auto rect_verts = unit_rectangle_vertices_and_normals();
        auto gl_rect = GlPrimitive::create(rect_verts);

        const auto sphere_verts = unit_sphere_vertices_and_normals();
        auto gl_sphere = GlPrimitive::create(sphere_verts);

        return TrianglePrimitiveRenderer(std::move(gl_cube),
                                         std::move(gl_rect),
                                         std::move(gl_sphere));
    }

    void TrianglePrimitiveRenderer::render(
                ShaderCollection & shaders,
                CommandBuffer & buffer,
                const Camera & camera,
                const Eigen::Matrix4f & projection)
    {
        auto & mesh_shader = shaders.mesh_shader();
        auto & line_shader = shaders.line_shader();

        const Eigen::Affine3f view = camera.transform().inverse();

        // TODO: Make lighting configurable rather than hard-coded
        const auto light_color = Color(1.0, 1.0, 1.0);
        const Eigen::Vector3f light_dir = Eigen::Vector3f(0.9, 1.2, -0.8).normalized();

        // Set up uniforms that are invariant across renderables
        mesh_shader.use();
        mesh_shader.set_light_color(light_color);
        mesh_shader.set_light_direction(light_dir);
        mesh_shader.set_view_transform(view);
        mesh_shader.set_projection_transform(projection);
        mesh_shader.set_camera_position(camera.position());
        line_shader.use();
        line_shader.set_projection_transform(projection);
        line_shader.set_view_transform(view);

        render_primitives(buffer.rectangles(), shaders, gl_rectangle, rectangle_reference_transform);
        render_primitives(buffer.boxes(), shaders, gl_cube, box_reference_transform);
        render_primitives(buffer.spheres(), shaders, gl_sphere, sphere_reference_transform);
    }

    MeshRenderer MeshRenderer::build(const std::shared_ptr<GlGarbagePile> & garbage)
    {
        return MeshRenderer(garbage);
    }

    void MeshRenderer::render(ShaderCollection &shaders,
                              CommandBuffer &buffer,
                              const Camera &camera,
                              const Eigen::Matrix4f &projection)
    {
        // TODO: Merge some of the code here with the code in TrianglePrimitiveRenderer
        auto & mesh_shader = shaders.mesh_shader();
        auto & line_shader = shaders.line_shader();

        const Eigen::Affine3f view = camera.transform().inverse();

        // TODO: Make lighting configurable rather than hard-coded
        const auto light_color = Color(1.0, 1.0, 1.0);
        const Eigen::Vector3f light_dir = Eigen::Vector3f(0.9, 1.2, -0.8).normalized();

        // Set up uniforms that are invariant across renderables
        mesh_shader.use();
        mesh_shader.set_light_color(light_color);
        mesh_shader.set_light_direction(light_dir);
        mesh_shader.set_view_transform(view);
        mesh_shader.set_projection_transform(projection);
        mesh_shader.set_camera_position(camera.position());
        line_shader.use();
        line_shader.set_projection_transform(projection);
        line_shader.set_view_transform(view);

        auto & meshes = buffer.meshes();

        // Make sure that meshes that share the same underlying data are consecutive in the buffer
        std::sort(meshes.begin(), meshes.end(),
                  [] (const Renderable<StaticMesh> & mesh1, const Renderable<StaticMesh> & mesh2)
        {
            const auto ptr1 = mesh1.shape._data.get();
            const auto ptr2 = mesh2.shape._data.get();
            return ptr1 < ptr2;
        });

        std::vector<Renderable<StaticMesh>> consecutive_meshes;

        auto outer_iter = meshes.cbegin();
        auto inner_iter = meshes.cbegin();

        // Keep track of which meshes were actually rendered, so that we may throw out
        // the rest from our cache
        std::unordered_set<detail::UniqueMeshId> rendered_meshes;

        while (outer_iter != meshes.cend())
        {
            const auto outer_id = outer_iter->shape._data->id;
            while (inner_iter != meshes.cend() && inner_iter->shape._data->id == outer_id)
            {
                ++inner_iter;
            }
            consecutive_meshes.assign(outer_iter, inner_iter);

            auto cache_iter = _mesh_cache.find(outer_id);
            if (cache_iter == _mesh_cache.end())
            {
                const auto & mesh_data = *outer_iter->shape._data;
                auto gl_mesh = GlTriangleMesh::create(_garbage, mesh_data.vertices_and_normals, mesh_data.faces);
                _mesh_cache.insert(std::make_pair(outer_id, std::move(gl_mesh)));
            }

            cache_iter = _mesh_cache.find(outer_id);
            auto & gl_mesh = cache_iter->second;

            render_static_meshes(consecutive_meshes, gl_mesh, shaders);
            rendered_meshes.insert(outer_id);

            outer_iter = inner_iter;
        }

        std::vector<detail::UniqueMeshId> meshes_to_remove;
        for (const auto & pair : _mesh_cache)
        {
            const auto & id = pair.first;
            if (rendered_meshes.count(id) == 0)
            {
                meshes_to_remove.push_back(id);
            }
        }

        for (const auto & id : meshes_to_remove)
        {
            _mesh_cache.erase(id);
        }

    }

    void ParticleRenderer::render(ShaderCollection & shaders,
                                  CommandBuffer & buffer,
                                  const Camera & camera,
                                  const Eigen::Matrix4f & projection)
    {
        auto & shader = shaders.particle_shader();

        const Eigen::Affine3f view = camera.transform().inverse();

        GLint viewport[4];
        glGetIntegerv(GL_VIEWPORT, viewport);
        const float viewport_width = static_cast<float>(viewport[2]);
        const float viewport_height = static_cast<float>(viewport[3]);

        // Compute the distance to the near plane by transforming from a point on the near plane in
        // NDC to view space. We have that
        //  n = P * X / N,
        // where N is the distance to the near plane, X = [x, 1] and x are the coordinate in
        // view space, and n represents the coordinates in NDC. Letting Y = X / N,
        // we have that N = 1 / Y_w, where Y = P^-1 * n.
        const float near_plane_dist = 1.0 / (projection.inverse() * Eigen::Vector4f(0.0, 0.0, -1.0, 1.0)).w();

        // TODO: Make lighting configurable rather than hard-coded
        const auto light_color = Color(1.0, 1.0, 1.0);
        const Eigen::Vector3f light_dir_world = Eigen::Vector3f(0.9, 1.2, -0.8).normalized();
        const Eigen::Vector3f light_dir_eye = view.linear() * light_dir_world;

        shader.use();
        shader.set_view_transform(view);
        shader.set_projection_transform(projection);
        shader.set_viewport_dimensions(viewport_width, viewport_height);
        shader.set_near_plane_dist(near_plane_dist);
        shader.set_light_color(light_color);
        shader.set_light_eye_direction(light_dir_eye);

        assert(buffer.particle_data().size() % 7 == 0);
        const auto num_particles = buffer.particle_data().size() / 7;
        _particle_buffer.update_buffer(buffer.particle_data().data(), num_particles);
        _particle_buffer.bind();

        MERELY_CHECK_GL_ERRORS();

        glEnable(GL_PROGRAM_POINT_SIZE);
        // The following line MAY be required on Windows, or in some configurations. On the other hand,
        // this caused an error on my Linux machine. TODO: Remove this once we know whether or not we need it.
        // glEnable(0x8861/*GL_POINT_SPRITE*/); // should be enabled by default in OpenGL 3.3, but isn't
        glPointParameteri(GL_POINT_SPRITE_COORD_ORIGIN, GL_LOWER_LEFT);
        MERELY_CHECK_GL_ERRORS();

        glDrawArrays(GL_POINTS, 0, num_particles);
        MERELY_CHECK_GL_ERRORS();
        _particle_buffer.unbind();
    }

    ParticleRenderer ParticleRenderer::build(const std::shared_ptr<GlGarbagePile> & garbage)
    {
        return ParticleRenderer(GlParticleBuffer::create(garbage));
    }
}


#include <Eigen/Core>

#include <geos/geom.h>
#include <geos/opBuffer.h>
#include <geos/opDistance.h>

#include <Pita/Node.hpp>
#include <Pita/Context.hpp>
#include <Pita/Vectorizer.hpp>
#include <Pita/Printer.hpp>

namespace cgl
{
	PackedRecord WritePathPacked(const Path& path)
	{
		PackedRecord result;
		auto& cs = path.cs;

		PackedList polygonList;
		for (size_t i = 0; i < cs->size(); ++i)
		{
			polygonList.add(MakeRecord("x", cs->getX(i), "y", cs->getY(i)));
		}

		result.add("line", polygonList);
		return result;
	}

	PackedPolyData GetPolygonVertices(const gg::Polygon* poly)
	{
		std::vector<PackedList> result;

		{
			PackedList polygonList;
			const gg::LineString* outer = poly->getExteriorRing();

			//TODO: Geosの頂点の向きは決まっている？
			if (IsClockWise(outer))
			{
				//始点と終点は同じ座標なので始点は飛ばす
				for (int i = 1; i < static_cast<int>(outer->getNumPoints()); ++i)
				{
					const gg::Coordinate& p = outer->getCoordinateN(i);
					polygonList.add(MakeVec2(p.x, p.y));
				}
			}
			else
			{
				for (int i = static_cast<int>(outer->getNumPoints()) - 1; 0 < i; --i)
				{
					const gg::Coordinate& p = outer->getCoordinateN(i);
					polygonList.add(MakeVec2(p.x, p.y));
				}
			}

			result.push_back(polygonList);
		}

		for (size_t i = 0; i < poly->getNumInteriorRing(); ++i)
		{
			PackedList holeVertexList;
			const gg::LineString* hole = poly->getInteriorRingN(i);

			if (IsClockWise(hole))
			{
				for (int n = static_cast<int>(hole->getNumPoints()) - 1; 0 < n; --n)
				{
					gg::Point* pp = hole->getPointN(n);
					holeVertexList.add(MakeVec2(pp->getX(), pp->getY()));
				}
			}
			else
			{
				for (int n = 1; n < static_cast<int>(hole->getNumPoints()); ++n)
				{
					gg::Point* pp = hole->getPointN(n);
					holeVertexList.add(MakeVec2(pp->getX(), pp->getY()));
				}
			}

			result.push_back(holeVertexList);
		}

		return result;
	}

	PackedList AsPackedListPolygons(const std::vector<PackedList>& polygons)
	{
		PackedList result;
		for (const auto& polygon : polygons)
		{
			result.add(polygon);
		}

		return result;
	}

	PackedPolyDataType GetPackedListType(const PackedList& packedList)
	{
		if (packedList.data.empty())
		{
			return PackedPolyDataType::POLYGON;
		}

		const auto& front = packedList.data.front().value;
		if (auto opt = AsOpt<PackedRecord>(front))
		{
			return PackedPolyDataType::POLYGON;
		}

		return PackedPolyDataType::MULTIPOLYGON;
	}

	PackedRecord GetLineStringPacked(const gg::LineString* line)
	{
		const auto appendCoord = [&](PackedList& list, double x, double y)
		{
			list.add(MakeRecord("x", x, "y", y));
		};

		PackedRecord result;
		{
			PackedList polygonList;

			//LineStringはPolygonと異なりループしない（頂点が重ならない）ので飛ばさずに読む
			for (int i = 0; i < static_cast<int>(line->getNumPoints()); ++i)
			{
				const gg::Coordinate& p = line->getCoordinateN(i);
				appendCoord(polygonList, p.x, p.y);
			}

			result.add("line", polygonList);
		}

		return result;
	}

#ifdef commentout
	PackedList GetShapesFromGeosPacked(const std::vector<gg::Geometry*>& polygons)
	{
		PackedList resultShapes;

		for (size_t i = 0; i < polygons.size(); ++i)
		{
			const gg::Geometry* shape = polygons[i];

			gg::GeometryTypeId id;
			switch (shape->getGeometryTypeId())
			{
			case geos::geom::GEOS_POINT:
				break;
			case geos::geom::GEOS_LINESTRING:
			{
				resultShapes.add(GetLineStringPacked(dynamic_cast<const gg::LineString*>(shape)));
				break;
			}
			case geos::geom::GEOS_LINEARRING:
				break;
			case geos::geom::GEOS_POLYGON:
			{
				resultShapes.add(GetPolygonPacked(dynamic_cast<const gg::Polygon*>(shape)));
				break;
			}
			case geos::geom::GEOS_MULTIPOINT:
				break;
			case geos::geom::GEOS_MULTILINESTRING:
			{
				const gg::MultiLineString* lines = dynamic_cast<const gg::MultiLineString*>(shape);
				for (int i = 0; i < lines->getNumGeometries(); ++i)
				{
					const gg::LineString* line = dynamic_cast<const gg::LineString*>(lines->getGeometryN(i));
					resultShapes.add(GetLineStringPacked(line));
				}
				break;
			}
			case geos::geom::GEOS_MULTIPOLYGON:
			{
				const gg::MultiPolygon* polygons = dynamic_cast<const gg::MultiPolygon*>(shape);
				for (int i = 0; i < polygons->getNumGeometries(); ++i)
				{
					const gg::Polygon* polygon = dynamic_cast<const gg::Polygon*>(polygons->getGeometryN(i));
					resultShapes.add(GetPolygonPacked(polygon));
				}
				break;
			}
			case geos::geom::GEOS_GEOMETRYCOLLECTION:
				break;
			default:
				break;
			}
		}

		return resultShapes;
	}
#endif
}