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

#include <cstdio>
#include <iostream>

#include <boost/algorithm/string/predicate.hpp>
#include <boost/algorithm/string/case_conv.hpp>

#include <Utils/File/Logfile.hpp>
#include <Utils/File/LineReader.hpp>

#include "Utils/TriangleNormals.hpp"
#include "StressTrajectoriesDatLoader.hpp"

#ifdef USE_EIGEN
#include <Eigen/Eigenvalues>

void computePrincipalStresses(
        float xx, float yy, float zz, float xy, float yz, float zx,
        float& majorStress, float& mediumStress, float& minorStress/*,
        glm::vec3& v0, glm::vec3& v1, glm::vec3& v2*/) {
    Eigen::Matrix3f stressTensor;
    stressTensor.row(0) << xx, xy, zx;
    stressTensor.row(1) << xy, yy, yz;
    stressTensor.row(2) << zx, yz, zz;

    Eigen::SelfAdjointEigenSolver<Eigen::Matrix3f> selfAdjointEigenSolver;
    selfAdjointEigenSolver.compute(stressTensor);
    Eigen::Vector3f eigenvalues = selfAdjointEigenSolver.eigenvalues();
    /*Eigen::Matrix3f eigenvectors = selfAdjointEigenSolver.eigenvectors();
    v0 = glm::vec3(eigenvectors(0, 0), eigenvectors(1, 0), eigenvectors(2, 0));
    v1 = glm::vec3(eigenvectors(0, 1), eigenvectors(1, 1), eigenvectors(2, 1));
    v2 = glm::vec3(eigenvectors(0, 2), eigenvectors(1, 2), eigenvectors(2, 2));*/

    minorStress = eigenvalues(0);
    mediumStress = eigenvalues(1);
    majorStress = eigenvalues(2);
}

float computeDegeneracyMeasure(float sigma1, float sigma2, float sigma3) {
    float degeneracyMeasure = 0.5f * std::abs((sigma1 - sigma2) / (sigma1 + sigma2));
    degeneracyMeasure = std::min(degeneracyMeasure, 0.5f * std::abs((sigma3 - sigma2) / (sigma3 + sigma2)));
    return degeneracyMeasure;
}
#endif

void loadStressLineHierarchyFromDat(
        const std::vector<std::string>& filenamesHierarchy,
        std::vector<StressTrajectoriesData>& stressTrajectoriesDataPs) {
    size_t psIdx = 0;
    for (size_t fileIdx = 0; fileIdx < filenamesHierarchy.size(); fileIdx++) {
        const std::string& filename = filenamesHierarchy.at(fileIdx);

        sgl::LineReader lineReader(filename);
        while (lineReader.isLineLeft()) {
            assert(psIdx < stressTrajectoriesDataPs.size());
            StressTrajectoriesData& stressTrajectoriesData = stressTrajectoriesDataPs.at(psIdx);
            std::vector<std::string> linesInfo = lineReader.readVectorLine<std::string>();
            // Line metadata saved?
            uint32_t numLines = 0;
            if (linesInfo.size() == 1) {
                numLines = sgl::fromString<uint32_t>(linesInfo.at(0));
            } else if (linesInfo.size() == 2) {
                numLines = sgl::fromString<uint32_t>(linesInfo.at(1));
            } else {
                sgl::Logfile::get()->writeError(
                        std::string() + "ERROR in loadStressLineHierarchyFromDat: Invalid line metadata in file \""
                        + filename + "\".");
            }
            assert(stressTrajectoriesData.size() == numLines);
            for (uint32_t lineIdx = 0; lineIdx < numLines; lineIdx++) {
                StressTrajectoryData& stressTrajectoryData = stressTrajectoriesData.at(lineIdx);
                float lineHierarchyLevel = lineReader.readScalarLine<float>();
                stressTrajectoryData.hierarchyLevels.push_back(lineHierarchyLevel);
            }

            psIdx++;
        }
    }
}

void loadStressTrajectoriesFromDat_v1(
        const std::vector<std::string>& filenamesTrajectories,
        const std::vector<std::string>& filenamesHierarchy,
        std::vector<int>& loadedPsIndices,
        std::vector<Trajectories>& trajectoriesPs,
        std::vector<StressTrajectoriesData>& stressTrajectoriesDataPs) {
    trajectoriesPs.reserve(filenamesTrajectories.size());
    stressTrajectoriesDataPs.reserve(filenamesTrajectories.size());
    size_t geometryByteSize = 0;

    size_t psIdx = 0;
    for (size_t fileIdx = 0; fileIdx < filenamesTrajectories.size(); fileIdx++) {
        const std::string& filename = filenamesTrajectories.at(fileIdx);

        sgl::LineReader lineReader(filename);
        while (lineReader.isLineLeft()) {
            Trajectories trajectories;
            StressTrajectoriesData stressTrajectoriesData;
            std::vector<std::string> linesInfo = lineReader.readVectorLine<std::string>();
            // Line metadata saved?
            uint32_t numLines = 0;
            if (linesInfo.size() == 1) {
                numLines = sgl::fromString<uint32_t>(linesInfo.at(0));
            } else if (linesInfo.size() == 2) {
                numLines = sgl::fromString<uint32_t>(linesInfo.at(1));
                boost::algorithm::to_lower(linesInfo.at(0));
                if (boost::ends_with(linesInfo.at(0), "major")) {
                    loadedPsIndices.push_back(0);
                } else if (boost::ends_with(linesInfo.at(0), "medium")) {
                    loadedPsIndices.push_back(1);
                } else if (boost::ends_with(linesInfo.at(0), "minor")) {
                    loadedPsIndices.push_back(2);
                } else {
                    sgl::Logfile::get()->writeError(
                            std::string() + "ERROR in loadStressTrajectoriesFromDat_: "
                            + "Invalid principal stress identifier \"" + linesInfo.at(0) + "\".");
                }
            } else {
                sgl::Logfile::get()->writeError(
                        std::string() + "ERROR in loadStressTrajectoriesFromDat_v1: Invalid line metadata in file \""
                        + filename + "\".");
            }
            trajectories.resize(numLines);
            stressTrajectoriesData.resize(numLines);
            for (uint32_t lineIdx = 0; lineIdx < numLines; lineIdx++) {
                Trajectory& trajectory = trajectories.at(lineIdx);
                StressTrajectoryData& stressTrajectoryData = stressTrajectoriesData.at(lineIdx);

                uint32_t lineLength = lineReader.readScalarLine<uint32_t>();
                trajectory.positions.reserve(lineLength);
                stressTrajectoryData.majorPs.reserve(lineLength);
                stressTrajectoryData.mediumPs.reserve(lineLength);
                stressTrajectoryData.minorPs.reserve(lineLength);
                stressTrajectoryData.majorPsDir.reserve(lineLength);
                stressTrajectoryData.mediumPsDir.reserve(lineLength);
                stressTrajectoryData.minorPsDir.reserve(lineLength);
                trajectory.attributes.resize(2);
                trajectory.attributes.front().reserve(lineLength);
                std::vector<float> positionData = lineReader.readVectorLine<float>(lineLength * 3);
                std::vector<float> psData = lineReader.readVectorLine<float>(lineLength * 12);
                std::vector<float> vonMisesData = lineReader.readVectorLine<float>(lineLength);

                for (uint32_t pointIdx = 0; pointIdx < lineLength; pointIdx++) {
                    trajectory.positions.push_back(glm::vec3(
                            positionData.at(pointIdx * 3),
                            positionData.at(pointIdx * 3 + 1),
                            positionData.at(pointIdx * 3 + 2)));
                    stressTrajectoryData.majorPs.push_back(psData.at(pointIdx * 12));
                    stressTrajectoryData.majorPsDir.push_back(glm::vec3(
                            psData.at(pointIdx * 12 + 1),
                            psData.at(pointIdx * 12 + 2),
                            psData.at(pointIdx * 12 + 3)));
                    stressTrajectoryData.mediumPs.push_back(psData.at(pointIdx * 12 + 4));
                    stressTrajectoryData.mediumPsDir.push_back(glm::vec3(
                            psData.at(pointIdx * 12 + 5),
                            psData.at(pointIdx * 12 + 6),
                            psData.at(pointIdx * 12 + 7)));
                    stressTrajectoryData.minorPs.push_back(psData.at(pointIdx * 12 + 8));
                    stressTrajectoryData.minorPsDir.push_back(glm::vec3(
                            psData.at(pointIdx * 12 + 9),
                            psData.at(pointIdx * 12 + 10),
                            psData.at(pointIdx * 12 + 11)));
                    trajectory.attributes.at(0).push_back(vonMisesData.at(pointIdx));
                    if (psIdx == 0) {
                        trajectory.attributes.at(1).push_back(std::abs(stressTrajectoryData.majorPs.back()));
                    } else if (psIdx == 1) {
                        trajectory.attributes.at(1).push_back(std::abs(stressTrajectoryData.mediumPs.back()));
                    } else {
                        trajectory.attributes.at(1).push_back(std::abs(stressTrajectoryData.minorPs.back()));
                    }
                }
            }

            for (size_t trajectoryIdx = 0; trajectoryIdx < trajectories.size(); trajectoryIdx++) {
                Trajectory& trajectory = trajectories.at(trajectoryIdx);
                StressTrajectoryData& stressTrajectoryData = stressTrajectoriesData.at(trajectoryIdx);
                geometryByteSize += trajectory.positions.size() * sizeof(float) * 3;
                geometryByteSize += sizeof(float); // hierarchy level
                geometryByteSize += stressTrajectoryData.majorPs.size() * sizeof(float);
                geometryByteSize += stressTrajectoryData.mediumPs.size() * sizeof(float);
                geometryByteSize += stressTrajectoryData.minorPs.size() * sizeof(float);
                geometryByteSize += stressTrajectoryData.majorPsDir.size() * sizeof(float) * 3;
                geometryByteSize += stressTrajectoryData.mediumPsDir.size() * sizeof(float) * 3;
                geometryByteSize += stressTrajectoryData.minorPsDir.size() * sizeof(float) * 3;
                for (const std::vector<float>& attributes : trajectory.attributes) {
                    geometryByteSize += attributes.size() * sizeof(float);
                }
            }

            trajectoriesPs.emplace_back(trajectories);
            stressTrajectoriesDataPs.emplace_back(stressTrajectoriesData);
            psIdx++;
        }
    }

    // Check if there's additional line hierarchy data.
    if (filenamesHierarchy.size() > 0) {
        loadStressLineHierarchyFromDat(filenamesHierarchy, stressTrajectoriesDataPs);
    }

    // Assume that all three PS directions are provided for the v1 .dat format.
    if (loadedPsIndices.size() == 0 && trajectoriesPs.size() == 3) {
        loadedPsIndices = {0, 1, 2};
    }

    std::cout << "Size of line geometry data (MiB): " << (geometryByteSize / (1024.0 * 1024.0)) << std::endl;
}

void loadStressTrajectoriesFromDat_v2(
        const std::vector<std::string>& filenamesTrajectories,
        std::vector<int>& loadedPsIndices,
        std::vector<Trajectories>& trajectoriesPs,
        std::vector<StressTrajectoriesData>& stressTrajectoriesDataPs,
        std::vector<std::vector<std::vector<glm::vec3>>>& bandPointsListLeftPs,
        std::vector<std::vector<std::vector<glm::vec3>>>& bandPointsListRightPs) {
    trajectoriesPs.reserve(filenamesTrajectories.size());
    stressTrajectoriesDataPs.reserve(filenamesTrajectories.size());
    bandPointsListLeftPs.reserve(filenamesTrajectories.size());
    bandPointsListRightPs.reserve(filenamesTrajectories.size());
    size_t geometryByteSize = 0;

    size_t psIdx = 0;
    for (size_t fileIdx = 0; fileIdx < filenamesTrajectories.size(); fileIdx++) {
        const std::string& filename = filenamesTrajectories.at(fileIdx);

        sgl::LineReader lineReader(filename);
        while (lineReader.isLineLeft()) {
            Trajectories trajectories;
            StressTrajectoriesData stressTrajectoriesData;
            std::vector<std::vector<glm::vec3>> bandPointsListLeft;
            std::vector<std::vector<glm::vec3>> bandPointsListRight;
            std::vector<std::string> linesInfo = lineReader.readVectorLine<std::string>();
            // Line metadata saved?
            uint32_t numLines = 0;
            if (linesInfo.size() == 1) {
                numLines = sgl::fromString<uint32_t>(linesInfo.at(0));
            } else if (linesInfo.size() == 2) {
                boost::algorithm::to_lower(linesInfo.at(0));
                if (boost::ends_with(linesInfo.at(0), "major")) {
                    loadedPsIndices.push_back(0);
                } else if (boost::ends_with(linesInfo.at(0), "medium")) {
                    loadedPsIndices.push_back(1);
                } else if (boost::ends_with(linesInfo.at(0), "minor")) {
                    loadedPsIndices.push_back(2);
                } else {
                    sgl::Logfile::get()->writeError(
                            std::string() + "ERROR in loadStressTrajectoriesFromDat_v2: "
                            + "Invalid principal stress identifier \"" + linesInfo.at(0) + "\".");
                }
                numLines = sgl::fromString<uint32_t>(linesInfo.at(1));
            } else {
                sgl::Logfile::get()->writeError(
                        std::string() + "ERROR in loadStressTrajectoriesFromDat_v2: "
                        + "Invalid line metadata in file \"" + filename + "\".");
            }
            trajectories.resize(numLines);
            stressTrajectoriesData.resize(numLines);
            bandPointsListLeft.resize(numLines);
            bandPointsListRight.resize(numLines);
            for (uint32_t lineIdx = 0; lineIdx < numLines; lineIdx++) {
                Trajectory& trajectory = trajectories.at(lineIdx);
                StressTrajectoryData& stressTrajectoryData = stressTrajectoriesData.at(lineIdx);
                std::vector<glm::vec3>& bandPointsLeft = bandPointsListLeft.at(lineIdx);
                std::vector<glm::vec3>& bandPointsRight = bandPointsListRight.at(lineIdx);

                std::vector<std::string> firstLineVector = lineReader.readVectorLine<std::string>();
                if (firstLineVector.size() != 2) {
                    sgl::Logfile::get()->writeError(
                            std::string() + "ERROR in loadStressTrajectoriesFromDat_v2: "
                            + "Invalid per line metadata in file \"" + filename + "\".");
                }
                uint32_t lineLength = sgl::fromString<uint32_t>(firstLineVector.at(0));
                float hierarchyLevel = sgl::fromString<float>(firstLineVector.at(1));
                stressTrajectoryData.hierarchyLevels.push_back(hierarchyLevel);
                trajectory.positions.reserve(lineLength);
                trajectory.attributes.resize(1);
                trajectory.attributes.front().reserve(lineLength);
                bandPointsLeft.reserve(lineLength);
                bandPointsRight.reserve(lineLength);
                std::vector<float> positionData = lineReader.readVectorLine<float>(lineLength * 3);
                std::vector<float> bandVertexData = lineReader.readVectorLine<float>(lineLength * 6);
                std::vector<float> scalarFieldData = lineReader.readVectorLine<float>(lineLength);

                for (uint32_t pointIdx = 0; pointIdx < lineLength; pointIdx++) {
                    trajectory.positions.push_back(glm::vec3(
                            positionData.at(pointIdx * 3),
                            positionData.at(pointIdx * 3 + 1),
                            positionData.at(pointIdx * 3 + 2)));
                    bandPointsLeft.push_back(glm::vec3(
                            bandVertexData.at(pointIdx * 6 + 0),
                            bandVertexData.at(pointIdx * 6 + 1),
                            bandVertexData.at(pointIdx * 6 + 2)));
                    bandPointsRight.push_back(glm::vec3(
                            bandVertexData.at(pointIdx * 6 + 3),
                            bandVertexData.at(pointIdx * 6 + 4),
                            bandVertexData.at(pointIdx * 6 + 5)));
                    trajectory.attributes.at(0).push_back(scalarFieldData.at(pointIdx));
                }
            }

            for (size_t trajectoryIdx = 0; trajectoryIdx < trajectories.size(); trajectoryIdx++) {
                Trajectory& trajectory = trajectories.at(trajectoryIdx);
                StressTrajectoryData& stressTrajectoryData = stressTrajectoriesData.at(trajectoryIdx);
                geometryByteSize += trajectory.positions.size() * sizeof(float) * 3;
                geometryByteSize += sizeof(float); // hierarchy level
                geometryByteSize += stressTrajectoryData.majorPs.size() * sizeof(float);
                geometryByteSize += stressTrajectoryData.mediumPs.size() * sizeof(float);
                geometryByteSize += stressTrajectoryData.minorPs.size() * sizeof(float);
                geometryByteSize += stressTrajectoryData.majorPsDir.size() * sizeof(float) * 3;
                geometryByteSize += stressTrajectoryData.mediumPsDir.size() * sizeof(float) * 3;
                geometryByteSize += stressTrajectoryData.minorPsDir.size() * sizeof(float) * 3;
                for (const std::vector<float>& attributes : trajectory.attributes) {
                    geometryByteSize += attributes.size() * sizeof(float);
                }
                geometryByteSize += bandPointsListLeft.at(trajectoryIdx).size() * sizeof(float) * 3;
                geometryByteSize += bandPointsListRight.at(trajectoryIdx).size() * sizeof(float) * 3;
            }

            trajectoriesPs.emplace_back(trajectories);
            stressTrajectoriesDataPs.emplace_back(stressTrajectoriesData);
            bandPointsListLeftPs.emplace_back(bandPointsListLeft);
            bandPointsListRightPs.emplace_back(bandPointsListRight);
            psIdx++;
        }
    }

    std::cout << "Size of line geometry data (MiB): " << (geometryByteSize / (1024.0 * 1024.0)) << std::endl;
}

void parseOutlineMeshHull(
        sgl::LineReader& lineReader,
        std::vector<uint32_t>& simulationMeshOutlineTriangleIndices,
        std::vector<glm::vec3>& simulationMeshOutlineVertexPositions) {
    std::vector<std::string> numVerticesLine = lineReader.readVectorLine<std::string>();
    if (numVerticesLine.size() != 2 || numVerticesLine.front() != "#Vertices") {
        sgl::Logfile::get()->writeError("Error in parseOutlineMeshHull: Invalid vertex information.");
    }
    uint32_t numVertices = sgl::fromString<uint32_t>(numVerticesLine.at(1));
    for (uint32_t vertexIdx = 0; vertexIdx < numVertices; vertexIdx++) {
        std::vector<float> vertexPosition = lineReader.readVectorLine<float>(3);
        assert(vertexPosition.size() == 3);
        simulationMeshOutlineVertexPositions.push_back(
                glm::vec3(vertexPosition.at(0), vertexPosition.at(1), vertexPosition.at(2)));
    }

    std::vector<std::string> numFacesLine = lineReader.readVectorLine<std::string>();
    if (numFacesLine.size() != 2 || numFacesLine.front() != "#Faces") {
        sgl::Logfile::get()->writeError("Error in parseOutlineMeshHull: Invalid face information.");
    }
    uint32_t numFaces = sgl::fromString<uint32_t>(numFacesLine.at(1));
    for (uint32_t faceIdx = 0; faceIdx < numFaces; faceIdx++) {
        std::vector<uint32_t> faceIndices = lineReader.readVectorLine<uint32_t>(4);
        assert(faceIndices.size() == 4);

        simulationMeshOutlineTriangleIndices.push_back(faceIndices.at(0));
        simulationMeshOutlineTriangleIndices.push_back(faceIndices.at(1));
        simulationMeshOutlineTriangleIndices.push_back(faceIndices.at(2));

        simulationMeshOutlineTriangleIndices.push_back(faceIndices.at(0));
        simulationMeshOutlineTriangleIndices.push_back(faceIndices.at(2));
        simulationMeshOutlineTriangleIndices.push_back(faceIndices.at(3));
    }
}

void loadStressTrajectoriesFromDat_v3(
        const std::vector<std::string>& filenamesTrajectories,
        std::vector<int>& loadedPsIndices, MeshType& meshType,
        std::vector<Trajectories>& trajectoriesPs,
        std::vector<StressTrajectoriesData>& stressTrajectoriesDataPs,
        std::vector<std::vector<std::vector<glm::vec3>>>& bandPointsUnsmoothedListLeftPs,
        std::vector<std::vector<std::vector<glm::vec3>>>& bandPointsUnsmoothedListRightPs,
        std::vector<std::vector<std::vector<glm::vec3>>>& bandPointsSmoothedListLeftPs,
        std::vector<std::vector<std::vector<glm::vec3>>>& bandPointsSmoothedListRightPs,
        std::vector<uint32_t>& simulationMeshOutlineTriangleIndices,
        std::vector<glm::vec3>& simulationMeshOutlineVertexPositions) {
    trajectoriesPs.reserve(filenamesTrajectories.size());
    stressTrajectoriesDataPs.reserve(filenamesTrajectories.size());
    bandPointsUnsmoothedListLeftPs.reserve(filenamesTrajectories.size());
    bandPointsUnsmoothedListRightPs.reserve(filenamesTrajectories.size());
    bandPointsSmoothedListLeftPs.reserve(filenamesTrajectories.size());
    bandPointsSmoothedListRightPs.reserve(filenamesTrajectories.size());
    size_t geometryByteSize = 0;

    size_t psIdx = 0;
    for (size_t fileIdx = 0; fileIdx < filenamesTrajectories.size(); fileIdx++) {
        const std::string& filename = filenamesTrajectories.at(fileIdx);

        sgl::LineReader lineReader(filename);
        while (lineReader.isLineLeft()) {
            Trajectories trajectories;
            StressTrajectoriesData stressTrajectoriesData;
            std::vector<std::vector<glm::vec3>> bandPointsUnsmoothedListLeft;
            std::vector<std::vector<glm::vec3>> bandPointsUnsmoothedListRight;
            std::vector<std::vector<glm::vec3>> bandPointsSmoothedListLeft;
            std::vector<std::vector<glm::vec3>> bandPointsSmoothedListRight;
            std::vector<std::string> linesInfo = lineReader.readVectorLine<std::string>();

            if (linesInfo.front() == "#Outline") {
                if (linesInfo.size() == 1) {
                    meshType = MeshType::CARTESIAN;
                } else {
                    if (linesInfo.at(1) == "Cartesian") {
                        meshType = MeshType::CARTESIAN;
                    } else {
                        meshType = MeshType::UNSTRUCTURED;
                    }
                }
                parseOutlineMeshHull(
                        lineReader, simulationMeshOutlineTriangleIndices, simulationMeshOutlineVertexPositions);
                continue;
            }

            // Line metadata saved?
            uint32_t numLines = 0;
            if (linesInfo.size() == 1) {
                numLines = sgl::fromString<uint32_t>(linesInfo.at(0));
                if (numLines == 0) {
                    continue;
                }
            } else if (linesInfo.size() == 2) {
                numLines = sgl::fromString<uint32_t>(linesInfo.at(1));
                if (numLines == 0) {
                    continue;
                }
                boost::algorithm::to_lower(linesInfo.at(0));
                if (boost::ends_with(linesInfo.at(0), "major")) {
                    loadedPsIndices.push_back(0);
                } else if (boost::ends_with(linesInfo.at(0), "medium")) {
                    loadedPsIndices.push_back(1);
                } else if (boost::ends_with(linesInfo.at(0), "minor")) {
                    loadedPsIndices.push_back(2);
                } else {
                    sgl::Logfile::get()->writeError(
                            std::string() + "ERROR in loadStressTrajectoriesFromDat_v2: "
                            + "Invalid principal stress identifier \"" + linesInfo.at(0) + "\".");
                }
            } else {
                sgl::Logfile::get()->writeError(
                        std::string() + "ERROR in loadStressTrajectoriesFromDat_v2: "
                        + "Invalid line metadata in file \"" + filename + "\".");
            }
            trajectories.resize(numLines);
            stressTrajectoriesData.resize(numLines);
            bandPointsUnsmoothedListLeft.resize(numLines);
            bandPointsUnsmoothedListRight.resize(numLines);
            bandPointsSmoothedListLeft.resize(numLines);
            bandPointsSmoothedListRight.resize(numLines);
            for (uint32_t lineIdx = 0; lineIdx < numLines; lineIdx++) {
                Trajectory& trajectory = trajectories.at(lineIdx);
                StressTrajectoryData& stressTrajectoryData = stressTrajectoriesData.at(lineIdx);
                std::vector<glm::vec3>& bandPointsUnsmoothedLeft = bandPointsUnsmoothedListLeft.at(lineIdx);
                std::vector<glm::vec3>& bandPointsUnsmoothedRight = bandPointsUnsmoothedListRight.at(lineIdx);
                std::vector<glm::vec3>& bandPointsSmoothedLeft = bandPointsSmoothedListLeft.at(lineIdx);
                std::vector<glm::vec3>& bandPointsSmoothedRight = bandPointsSmoothedListRight.at(lineIdx);

                std::vector<std::string> firstLineVector = lineReader.readVectorLine<std::string>();
                if (firstLineVector.size() == 0) {
                    sgl::Logfile::get()->writeError(
                            std::string() + "ERROR in loadStressTrajectoriesFromDat_v2: "
                            + "Invalid per line metadata in file \"" + filename + "\".");
                }
                uint32_t lineLength = sgl::fromString<uint32_t>(firstLineVector.at(0));

                // Add the hierarchy levels.
                for (int hierarchyIdx = 1; hierarchyIdx < std::max(int(firstLineVector.size()), 5); hierarchyIdx++) {
                    float hierarchyLevel = sgl::fromString<float>(firstLineVector.at(hierarchyIdx));
                    stressTrajectoryData.hierarchyLevels.push_back(hierarchyLevel);
                }
                if (firstLineVector.size() == 9) {
                    stressTrajectoryData.appearanceOrder = sgl::fromString<int>(firstLineVector.at(5)) - 1;
                    stressTrajectoryData.seedPosition = glm::vec3(
                            sgl::fromString<float>(firstLineVector.at(6)),
                            sgl::fromString<float>(firstLineVector.at(7)),
                            sgl::fromString<float>(firstLineVector.at(8)));
                }

                trajectory.positions.reserve(lineLength);
                bandPointsUnsmoothedLeft.reserve(lineLength);
                bandPointsUnsmoothedRight.reserve(lineLength);
                bandPointsSmoothedLeft.reserve(lineLength);
                bandPointsSmoothedRight.reserve(lineLength);
                std::vector<float> positionData = lineReader.readVectorLine<float>(
                        lineLength * 3);
                std::vector<float> bandVertexDataUnsmoothed = lineReader.readVectorLine<float>(
                        lineLength * 6);
                std::vector<float> bandVertexDataSmoothed = lineReader.readVectorLine<float>(
                        lineLength * 6);

                for (uint32_t pointIdx = 0; pointIdx < lineLength; pointIdx++) {
                    trajectory.positions.emplace_back(
                            positionData.at(pointIdx * 3),
                            positionData.at(pointIdx * 3 + 1),
                            positionData.at(pointIdx * 3 + 2));
                    bandPointsUnsmoothedLeft.emplace_back(
                            bandVertexDataUnsmoothed.at(pointIdx * 6 + 0),
                            bandVertexDataUnsmoothed.at(pointIdx * 6 + 1),
                            bandVertexDataUnsmoothed.at(pointIdx * 6 + 2));
                    bandPointsUnsmoothedRight.emplace_back(
                            bandVertexDataUnsmoothed.at(pointIdx * 6 + 3),
                            bandVertexDataUnsmoothed.at(pointIdx * 6 + 4),
                            bandVertexDataUnsmoothed.at(pointIdx * 6 + 5));
                    bandPointsSmoothedLeft.emplace_back(
                            bandVertexDataSmoothed.at(pointIdx * 6 + 0),
                            bandVertexDataSmoothed.at(pointIdx * 6 + 1),
                            bandVertexDataSmoothed.at(pointIdx * 6 + 2));
                    bandPointsSmoothedRight.emplace_back(
                            bandVertexDataSmoothed.at(pointIdx * 6 + 3),
                            bandVertexDataSmoothed.at(pointIdx * 6 + 4),
                            bandVertexDataSmoothed.at(pointIdx * 6 + 5));
                }

#ifdef USE_EIGEN
                trajectory.attributes.resize(13);
#else
                trajectory.attributes.resize(9);
#endif

                // Principal stress.
                trajectory.attributes.at(0).reserve(lineLength);
                std::vector<float> scalarFieldData = lineReader.readVectorLine<float>(lineLength);
                for (uint32_t pointIdx = 0; pointIdx < lineLength; pointIdx++) {
                    trajectory.attributes.at(0).push_back(scalarFieldData.at(pointIdx));
                }

                // Principal stress magnitude.
                for (uint32_t pointIdx = 0; pointIdx < lineLength; pointIdx++) {
                    trajectory.attributes.at(1).push_back(std::abs(scalarFieldData.at(pointIdx)));
                }

                // Von Mises stress, normal stress (xx), normal stress (yy), normal stress (zz), shear stress (yz),
                // shear stress (zx), shear stress (xy).
                for (int varIdx = 2; varIdx < 9; varIdx++) {
                    trajectory.attributes.at(varIdx).reserve(lineLength);
                    std::vector<float> scalarFieldData = lineReader.readVectorLine<float>(lineLength);
                    for (uint32_t pointIdx = 0; pointIdx < lineLength; pointIdx++) {
                        trajectory.attributes.at(varIdx).push_back(scalarFieldData.at(pointIdx));
                    }
                }

#ifdef USE_EIGEN
                int xxIdx = 3;
                int yyIdx = 4;
                int zzIdx = 5;
                int xyIdx = 8;
                int yzIdx = 6;
                int zxIdx = 7;

                float majorStress, mediumStress, minorStress;
                //glm::vec3 v0, v1, v2;
                for (uint32_t pointIdx = 0; pointIdx < lineLength; pointIdx++) {
                    computePrincipalStresses(
                            trajectory.attributes.at(xxIdx).at(pointIdx),
                            trajectory.attributes.at(yyIdx).at(pointIdx),
                            trajectory.attributes.at(zzIdx).at(pointIdx),
                            trajectory.attributes.at(xyIdx).at(pointIdx),
                            trajectory.attributes.at(yzIdx).at(pointIdx),
                            trajectory.attributes.at(zxIdx).at(pointIdx),
                            majorStress, mediumStress, minorStress/*, v0, v1, v2*/);
                    float degeneracyMeasure = computeDegeneracyMeasure(
                            minorStress, mediumStress, majorStress);
                    trajectory.attributes.at(9).push_back(majorStress);
                    trajectory.attributes.at(10).push_back(mediumStress);
                    trajectory.attributes.at(11).push_back(minorStress);
                    trajectory.attributes.at(12).push_back(degeneracyMeasure);
                }
#endif
            }

            for (size_t trajectoryIdx = 0; trajectoryIdx < trajectories.size(); trajectoryIdx++) {
                Trajectory& trajectory = trajectories.at(trajectoryIdx);
                StressTrajectoryData& stressTrajectoryData = stressTrajectoriesData.at(trajectoryIdx);
                geometryByteSize += trajectory.positions.size() * sizeof(float) * 3;
                geometryByteSize += sizeof(float); // hierarchy level
                geometryByteSize += stressTrajectoryData.majorPs.size() * sizeof(float);
                geometryByteSize += stressTrajectoryData.mediumPs.size() * sizeof(float);
                geometryByteSize += stressTrajectoryData.minorPs.size() * sizeof(float);
                geometryByteSize += stressTrajectoryData.majorPsDir.size() * sizeof(float) * 3;
                geometryByteSize += stressTrajectoryData.mediumPsDir.size() * sizeof(float) * 3;
                geometryByteSize += stressTrajectoryData.minorPsDir.size() * sizeof(float) * 3;
                for (const std::vector<float>& attributes : trajectory.attributes) {
                    geometryByteSize += attributes.size() * sizeof(float);
                }
                geometryByteSize += bandPointsUnsmoothedListLeft.at(trajectoryIdx).size() * sizeof(float) * 3;
                geometryByteSize += bandPointsUnsmoothedListRight.at(trajectoryIdx).size() * sizeof(float) * 3;
                geometryByteSize += bandPointsSmoothedListLeft.at(trajectoryIdx).size() * sizeof(float) * 3;
                geometryByteSize += bandPointsSmoothedListRight.at(trajectoryIdx).size() * sizeof(float) * 3;
            }

            trajectoriesPs.emplace_back(trajectories);
            stressTrajectoriesDataPs.emplace_back(stressTrajectoriesData);
            bandPointsUnsmoothedListLeftPs.emplace_back(bandPointsUnsmoothedListLeft);
            bandPointsUnsmoothedListRightPs.emplace_back(bandPointsUnsmoothedListRight);
            bandPointsSmoothedListLeftPs.emplace_back(bandPointsSmoothedListLeft);
            bandPointsSmoothedListRightPs.emplace_back(bandPointsSmoothedListRight);
            psIdx++;
        }
    }

    std::cout << "Size of line geometry data (MiB): " << (geometryByteSize / (1024.0 * 1024.0)) << std::endl;
}


// Copyright (c) 2018 by University Paris-Est Marne-la-Vallee
// MetaData.cpp
// This file is part of the Garamon Generator.
// Authors: Stephane Breuils and Vincent Nozick
// Contact: vincent.nozick@u-pem.fr
//
// Licence MIT
// A a copy of the MIT License is given along with this program

#include <math.h>
#include <set>

#include <Eigen/Eigenvalues>

#include "MetaData.hpp"
#include "ConfigParser.hpp"

MetaData::MetaData() : dimension(0), inputMetricDiagonal(false), identityMetric(false), inputMetricPermutationOfDiagonal(false), maxDimPrecomputedProducts(256) {}

MetaData::~MetaData() {}

void MetaData::display() const {
    std::cout << "MetaData" << std::endl;
    std::cout << "dimension         : " << dimension << std::endl;
    std::cout << "namespace         : " << namespaceName << std::endl;

    std::cout << "refinement        : " << (useEigenRefinement==true?"true":"false") << std::endl;
    std::cout << "cleanup           : " << (useNumericalCleanUp==true?"true":"false") << std::endl;
    std::cout << "maxDim prec func  : " << maxDimPrecomputedProducts << std::endl;
    std::cout << "maxDim accessors  : " << maxDimBasisAccessor << std::endl;
    std::cout << "epsilon           : " << epsilon << std::endl;
    std::cout << "basis vector name : ";
    for(unsigned int i=0; i<basisVectorName.size(); ++i)
        std::cout << basisVectorName[i] << " ";
    std::cout << std::endl;
    std::cout << "metric            : \n" << metric << std::endl;
    std::cout << "is initialy diag  : " << (inputMetricDiagonal==true?"true":"false") << std::endl;
    std::cout << "is identity       : " << (identityMetric==true?"true":"false") << std::endl;
    std::cout << "is full rank      : " << (fullRankMetric==true?"true":"false") << std::endl;
    std::cout << "is permutation of diagonal matrix : " << (inputMetricPermutationOfDiagonal==true?"true":"false") << std::endl;
    std::cout << "diag metric       : " << diagonalMetric.transpose() << std::endl;
    if(!inputMetricDiagonal){
        std::cout << "Vector transformation matrix : \n" << transformationMatrix << std::endl;
        std::cout << "Vector inverse Transformation  : \n" << inverseTransformationMatrix<< std::endl;
    }
}

bool MetaData::checkConsistency() const {

    bool consistencyCheck = true;

    // maxDimBasisAccessor consistency: at least for vectors
    if(maxDimBasisAccessor == 0){
        std::cout << "error: at least vector accessors are required, see 'max dimension basis accessor' in the conf file." << std::endl;
        consistencyCheck = false;
    }

    // dimension consistency
    if(dimension == 0){
        std::cout << "error: dimension should not be 0." << std::endl;
        consistencyCheck = false;
    }

    // basis vector name dimension consistency
    if(basisVectorName.size() != dimension){
        std::cout << "error: 'basis vector name' size is not consistent with 'dimension'." << std::endl;
        consistencyCheck = false;
    }

    // metric empty
    bool metricDefined = true;
    if( (metric.rows()==0) || (metric.cols()==0) ) {
        metricDefined = false;
        std::cout << "error: the metric matrix is not defined." << std::endl;
        consistencyCheck = false;
    }

    // metric matrix ?
    if(metricDefined) {
        // metric : square matrix ?
        bool squareMatrix = true;
        if( metric.rows() != metric.cols() ){
            std::cout << "error: the metric is not a square matrix." << std::endl;
            consistencyCheck = false;
            squareMatrix = false;
        }

        // metric : dimension consistency
        if( ((unsigned int)metric.cols() != dimension) || ((unsigned int)metric.rows() != dimension) ) {
            std::cout << "error: the metric dimension is not consistent with 'dimension'." << std::endl;
            consistencyCheck = false;
        }

        if(squareMatrix) {
            // check if metric is symetric
            bool symetric = true;
            for(unsigned int i=0; i<(unsigned int)metric.rows(); ++i)
                for(unsigned int j=i; j<(unsigned int)metric.cols(); ++j)
                    if(fabs(metric(i,j) - metric(j,i)) > epsilon)
                        symetric = false;
            if (!symetric) {
                std::cout << "error: the metric is not a symmetric matrix." << std::endl;
                consistencyCheck = false;
            }
        }
    }

    // check if the name of the vector basis are not ambiguous for high dimensions
    // i.e. in dimension 15: e12 is for twelve or one-two ?
    std::set<std::string> basis;

    // represent a k-vector with a binary number (k-st bit to 1 means the k-st basis is used)
    for(unsigned int i=1; i<=pow(2,dimension); ++i){

        std::string kvector;
        for(unsigned int k=0; k<dimension; ++k)
            if(i & (1 << k))
                kvector = kvector + basisVectorName[k];

        if(basis.count(kvector) != 0){
            std::cout << "error in the basis vector name: " << kvector << " is ambiguous." << std::endl;
            consistencyCheck = false;
        }else{
            basis.insert(kvector);
        }
    }

    // namespace name compatible with C++
    if(isalpha(namespaceName[0]) == 0){
        std::cout << "error in the namespace name: the first character of '" << namespaceName << "' should be an alphabetic letter for C++ compliance." << std::endl;
        consistencyCheck = false;
    }

    return consistencyCheck;
}

MetaData::MetaData(const std::string &filename):inputMetricDiagonal(false), identityMetric(false) {

    // open the parser
    ConfigParser parser(filename);

    // load all components of the meta data
    if(!parser.readString("namespace", namespaceName)) {
        std::cerr << "error: failed to find " << "namespace" << std::endl;
        exit(EXIT_FAILURE);
    }

    if(!parser.readUInt("dimension", dimension)){
        std::cerr << "error: failed to find " << "dimension" << std::endl;
        exit(EXIT_FAILURE);
    }

    if(!parser.readUInt("max dimension precomputed products", maxDimPrecomputedProducts)){
        std::cerr << "error: failed to find " << "max dimension precomputed products" << std::endl;
        exit(EXIT_FAILURE);
    }

    if(!parser.readUInt("max dimension basis accessor", maxDimBasisAccessor)){
        std::cerr << "error: failed to find " << "max dimension basis accessor" << std::endl;
        exit(EXIT_FAILURE);
    }

    if(!parser.readStringList("basis vector name", basisVectorName)){
        std::cerr << "error: failed to find " << "basis vector name" << std::endl;
        exit(EXIT_FAILURE);
    }

    if(!parser.readMatrix("metric", metric)){
        std::cerr << "error: failed to find " << "metric" << std::endl;
        exit(EXIT_FAILURE);
    }

    if(!parser.readBool("metric decomposition refinement", useEigenRefinement)){
        std::cerr << "error: failed to find " << "metric decomposition refinement" << std::endl;
        exit(EXIT_FAILURE);
    }

    if(!parser.readBool("metric decomposition numerical cleanup", useNumericalCleanUp)){
        std::cerr << "error: failed to find " << "metric decomposition numerical cleanup" << std::endl;
        exit(EXIT_FAILURE);
    }

    if(!parser.readDouble("metric decomposition numerical cleanup espilon", epsilon)){
        std::cerr << "error: failed to find " << "metric decomposition numerical cleanup espilon" << std::endl;
        exit(EXIT_FAILURE);
    }

    // check the data consistency
    if(!checkConsistency()){
        std::cerr << "configuration inconsistent ... abord" << std::endl;
        exit(EXIT_FAILURE);
    }

    // metric diagonalization
    if(!metricDiagonalization()){
        std::cerr << "metric diagonalization ... failed" << std::endl;
        std::cerr << "Please try again without the metric decomposition refinement or without the numerical cleanup." << std::endl;
        exit(EXIT_FAILURE);
    }
}

bool MetaData::metricDiagonalization() {

	// compute the metric rank
	if (getRank(metric) == dimension)
		fullRankMetric = true;
	else fullRankMetric = false;

    // check if the metric is already diagonal
    if(isMatrixDiagonal(metric, epsilon)){
        diagonalMetric = metric.diagonal();
        inputMetricDiagonal = true;
        if(isMatrixIdentity(metric,epsilon))
            identityMetric = true;
        return true;
    }

    // ckeck if the metric is a permutation of a diagonal matrix (for fast dual)
    inputMetricPermutationOfDiagonal = isMatrixPermutationOfDiagonal(metric, epsilon);

    // compute the diagonalization
    Eigen::MatrixXd diagonalMatrix;
    eigenDecomposition(metric, transformationMatrix, diagonalMatrix);
//    std::cout << "metric\n" << metric << std::endl;
//    std::cout << "P\n" << transformationMatrix << std::endl;
//    std::cout << "D\n" << diagonalMatrix << std::endl;
//    std::cout << "metric ??\n" << transformationMatrix * diagonalMatrix * transformationMatrix.transpose() << std::endl;

    // invert the transformation matrix
    inverseTransformationMatrix = Eigen::MatrixXd(transformationMatrix.transpose());

    // convert floating points to nearest integers (when possible)
    Eigen::MatrixXd scaleMatrix = Eigen::MatrixXd::Identity(metric.rows(),metric.cols()); // refer to issue #9
    if(useEigenRefinement)
        scaleMatrix = eigenRefinement(transformationMatrix, diagonalMatrix, inverseTransformationMatrix);
//    std::cout << "metric\n" << metric << std::endl;
//    std::cout << "P\n" << transformationMatrix << std::endl;
//    std::cout << "D\n" << diagonalMatrix << std::endl;
//    std::cout << "Pinv\n" << inverseTransformationMatrix << std::endl;
//    std::cout << "metric ??\n" << transformationMatrix * diagonalMatrix * inverseTransformationMatrix << std::endl;

    //numerical clean up
    if(useNumericalCleanUp) {
        transformationMatrix = numericalCleanUp(transformationMatrix,epsilon);
        diagonalMatrix = numericalCleanUp(diagonalMatrix, epsilon);
        inverseTransformationMatrix = numericalCleanUp(inverseTransformationMatrix,epsilon);
    }

    // check if the new metric is identity
    if(isMatrixIdentity(diagonalMatrix,epsilon))
        identityMetric = true;

    // check decomposition
    if(!checkNumericalCleanUp(metric,transformationMatrix,diagonalMatrix,inverseTransformationMatrix, epsilon))
        return false;

    // diagonal metric must be changed if we consider only inverse
    diagonalMatrix = (scaleMatrix*scaleMatrix)*diagonalMatrix;
    if(useNumericalCleanUp)
        diagonalMatrix = numericalCleanUp(diagonalMatrix, epsilon);

    // compact form of the diagonal matrix
    diagonalMetric = diagonalMatrix.diagonal();

    return true;
}


// Software License for MTL
//
// Copyright (c) 2007 The Trustees of Indiana University.
//               2008 Dresden University of Technology and the Trustees of Indiana University.
//               2010 SimuNova UG (haftungsbeschränkt), www.simunova.com.
// All rights reserved.
// Authors: Peter Gottschling and Andrew Lumsdaine
//
// This file is part of the Matrix Template Library
//
// See also license.mtl.txt in the distribution.

#ifndef ITL_PC_IC_0_INCLUDE
#define ITL_PC_IC_0_INCLUDE

#include <boost/mpl/bool.hpp>

#include <boost/numeric/linear_algebra/identity.hpp>
#include <boost/numeric/linear_algebra/inverse.hpp>

#include <boost/numeric/mtl/concept/collection.hpp>
#include <boost/numeric/mtl/utility/ashape.hpp>
#include <boost/numeric/mtl/utility/tag.hpp>
#include <boost/numeric/mtl/utility/category.hpp>
#include <boost/numeric/mtl/utility/exception.hpp>
#include <boost/numeric/mtl/operation/lower_trisolve.hpp>
#include <boost/numeric/mtl/operation/upper_trisolve.hpp>
#include <boost/numeric/mtl/matrix/upper.hpp>
#include <boost/numeric/mtl/matrix/strict_lower.hpp>
#include <boost/numeric/mtl/matrix/compressed2D.hpp>
#include <boost/numeric/mtl/matrix/parameter.hpp>
#include <boost/numeric/mtl/matrix/transposed_view.hpp>
#include <boost/numeric/mtl/interface/vpt.hpp>
#include <boost/numeric/itl/pc/solver.hpp>

namespace itl { namespace pc {

template <typename Matrix, typename Value= typename mtl::Collection<Matrix>::value_type>
class ic_0
{
  public:
    typedef Value                                                    value_type;
    typedef typename mtl::Collection<Matrix>::size_type              size_type;
    typedef ic_0                                                     self;

    typedef mtl::matrix::parameters<mtl::row_major, mtl::index::c_index, mtl::non_fixed::dimensions, false, size_type> para;
    typedef mtl::matrix::compressed2D<value_type, para>                      U_type;
#ifndef ITL_IC_0_ONE_MATRIX
    typedef U_type                                                   L_type;
#else
    typedef typename mtl::matrix::transposed_view<U_type>            L_type;
#endif
    typedef mtl::matrix::detail::lower_trisolve_t<L_type, mtl::tag::inverse_diagonal, true> lower_solver_t;
    typedef mtl::matrix::detail::upper_trisolve_t<U_type, mtl::tag::inverse_diagonal, true> upper_solver_t;

    ic_0(const Matrix& A) : f(A, U), L(trans(U)), lower_solver(L), upper_solver(U) {}

    // solve x = U^* U y --> y= U^{-1} U^{-*} x
    template <typename Vector>
    Vector solve(const Vector& x) const
    {
	mtl::vampir_trace<5036> tracer;
	return inverse_upper_trisolve(U, inverse_lower_trisolve(adjoint(U), x));
    }

    // solve x = U^* y --> y0= U^{-*} x
    template <typename VectorIn, typename VectorOut>
    const VectorOut& solve_lower(const VectorIn& x, VectorOut&) const
    {
	static VectorOut y0;
	y0.change_resource(resource(x));
	lower_solver(x, y0);
	return y0;
    }

    // solve x = U^* U y --> y= U^{-1} U^{-*} x
    template <typename VectorIn, typename VectorOut>
    void solve(const VectorIn& x, VectorOut& y) const
    {
	mtl::vampir_trace<5037> tracer;
	const VectorOut& y0= solve_lower(x, y);

	y.checked_change_resource(x);
	upper_solver(y0, y);
    }

    // solve x = (LU)^* y --> y= L^{-*} U^{-*} x
    template <typename Vector>
    Vector adjoint_solve(const Vector& x) const
    {
	mtl::vampir_trace<5044> tracer;
	return solve(x);
    }

    // solve x = (LU)^* y --> y= L^{-*} U^{-*} x
    template <typename VectorIn, typename VectorOut>
    void adjoint_solve(const VectorIn& x, VectorOut& y) const
    {
	mtl::vampir_trace<5044> tracer;
	solve(x, y);
    }

    L_type get_L() { return L_type(L); }
    U_type get_U() { return U; }

  protected:
    template <typename VectorOut, typename Solver> friend struct ic_0_evaluator;

    // Dummy type to perform factorization in initializer list not in
    struct factorizer
    {
	factorizer(const Matrix &A, U_type& U)
	{   factorize(A, U, mtl::traits::is_sparse<Matrix>(), boost::is_same<Value, typename mtl::Collection<Matrix>::value_type>());  }

	template <typename T>
	void factorize(const Matrix&, U_type&, boost::mpl::false_, T)
	{   MTL_THROW_IF(true, mtl::logic_error("IC(0) is not suited for dense matrices"));	}

	// When we change the value_type then the factorization is still performed with that of A
	template <typename UF>
	void factorize(const Matrix& A, UF& U, boost::mpl::true_, boost::mpl::false_)
	{
	    typedef mtl::matrix::compressed2D<typename mtl::Collection<Matrix>::value_type, para> tmp_type;
	    tmp_type U_tmp;
	    factorize(A, U_tmp, boost::mpl::true_(), boost::mpl::true_());
	    U= U_tmp;
	}

	// Factorization adapted from Saad
	// Undefined (runtime) behavior if matrix is not symmetric
	// UF is type for the factorization
	template <typename UF>
	void factorize(const Matrix& A, UF& U, boost::mpl::true_, boost::mpl::true_)
	{
	    using namespace mtl; using namespace mtl::tag;  using mtl::traits::range_generator;
	    using math::reciprocal; using mtl::matrix::upper;
	    mtl::vampir_trace<5035> tracer;

	    // For the factorization we take still the value_type of A and later we copy it maybe to another value_type
	    typedef typename mtl::Collection<Matrix>::value_type      value_type;
	    typedef typename range_generator<row, UF>::type       cur_type;
	    typedef typename range_generator<nz, cur_type>::type      icur_type;

	    MTL_THROW_IF(num_rows(A) != num_cols(A), mtl::matrix_not_square());
	    U= upper(A);

	    typename mtl::traits::col<UF>::type                   col(U);
	    typename mtl::traits::value<UF>::type                 value(U);

	    cur_type kc= begin<row>(U), kend= end<row>(U);
	    for (size_type k= 0; kc != kend; ++kc, ++k) {

		icur_type ic= begin<nz>(kc), iend= end<nz>(kc);
		MTL_DEBUG_THROW_IF(col(*ic) != k, mtl::missing_diagonal());

		// U[k][k]= 1.0 / sqrt(U[k][k]);
		value_type inv_dia= reciprocal(sqrt(value(*ic)));
		value(*ic, inv_dia);
		// icur_type jbegin=
		++ic;
		for (; ic != iend; ++ic) {
		    // U[k][i] *= U[k][k]
		    value_type d= value(*ic) * inv_dia;
		    value(*ic, d);
		    size_type i= col(*ic);

		    // find non-zeros U[j][i] below U[k][i] for j in (k, i]
		    // 1. Go to ith row in U (== ith column in U)
		    cur_type irow(i, U); // = begin<row>(U); irow+= i;
		    // 2. Find nonzeros with col() in (k, i]
		    icur_type jc= begin<nz>(irow), jend= end<nz>(irow);
		    while (col(*jc) <= k)  ++jc;
		    while (col(*--jend) > i) ;
		    ++jend;

		    for (; jc != jend; ++jc) {
			size_type j= col(*jc);
			U.lvalue(j, i)-= d * U[k][j];
		    }
		    // std::cout << "U after eliminating U[" << i << "][" << k << "] =\n" << U;
		}
	    }
	}
    };

    U_type                       U;
    factorizer                   f;
    L_type                       L;
    lower_solver_t               lower_solver;
    upper_solver_t               upper_solver;
};

#if 0
template <typename Matrix, typename Value, typename Vector>
struct ic_0_solver
  : mtl::vector::assigner<ic_0_solver<Matrix, Value, Vector> >
{
    typedef ic_0<Matrix, Value> pc_type;

    ic_0_solver(const ic_0<Matrix, Value>& P, const Vector& x) : P(P), x(x) {}

    template <typename VectorOut>
    void assign_to(VectorOut& y) const
    {	P.solve(x, y);    }

    const ic_0<Matrix, Value>& P;
    const Vector&              x;
};
#endif

template <typename VectorOut, typename Solver>
struct ic_0_evaluator
{
    typedef typename Solver::pc_type                        pc_type;
    typedef typename pc_type::size_type                     size_type;
    typedef typename mtl::Collection<VectorOut>::value_type out_value_type;

    ic_0_evaluator(VectorOut& y, const Solver& s)
      : y(y), s(s), U(s.P.U), y0(s.P.solve_lower(s.x, y)) { MTL_DEBUG_ARG(lr= 99999999); }

    void operator()(size_type i) { at<0>(i); }
    void operator[](size_type i) { at<0>(i); }

    template <unsigned Offset>
    void at(size_type r)
    {
#ifndef NDEBUG
	MTL_THROW_IF(r+Offset >= lr, mtl::logic_error("Traversal must be backward")); lr= r+Offset;
#endif
	size_type j0= U.ref_major()[r+Offset];
	const size_type cj1= U.ref_major()[r+Offset+1];
	MTL_DEBUG_THROW_IF(j0 == cj1 || U.ref_minor()[j0] != r+Offset, mtl::missing_diagonal());
	out_value_type rr= y0[r+Offset], dia= U.data[j0++];
	for (; j0 != cj1; ++j0) {
	    MTL_DEBUG_THROW_IF(U.ref_minor()[j0] <= r+Offset, mtl::logic_error("Matrix entries must be sorted for this."));
	    rr-= U.data[j0] * y[U.ref_minor()[j0]];
	}
	y[r+Offset]= rr * dia;
    }

    VectorOut&                               y;
    const Solver&                            s;
    const typename pc_type::U_type&          U;
    const VectorOut&                         y0;
    MTL_DEBUG_ARG(size_type                  lr;)
};

template <typename VectorOut, typename Solver>
inline std::size_t size(const ic_0_evaluator<VectorOut, Solver>& eval)
{   return size(eval.y); }

template <typename Matrix, typename Value, typename Vector>
solver<ic_0<Matrix, Value>, Vector, false>
inline solve(const ic_0<Matrix, Value>& P, const Vector& x)
{
    return solver<ic_0<Matrix, Value>, Vector, false>(P, x);
}

template <typename Matrix, typename Value, typename Vector>
solver<ic_0<Matrix, Value>, Vector, true>
inline adjoint_solve(const ic_0<Matrix, Value>& P, const Vector& x)
{
    return solver<ic_0<Matrix, Value>, Vector, true>(P, x);
}

}} // namespace itl::pc

namespace mtl { namespace vector {
    using itl::pc::size;
}} // namespace mtl::vector

#endif // ITL_PC_IC_0_INCLUDE


/* =========================================================================
   Copyright (c) 2012-2014, Institute for Microelectronics,
                            Institute for Analysis and Scientific Computing,
                            TU Wien.
                             -----------------
               ViennaFEM - The Vienna Finite Element Method Library
                             -----------------

   Author:     Karl Rupp                          rupp@iue.tuwien.ac.at

   License:    MIT (X11), see file LICENSE in the ViennaFEM base directory
============================================================================ */

// remove assert() statements and the like in order to get reasonable performance
#ifndef NDEBUG
  #define NDEBUG
#endif

// include necessary system headers
#include <iostream>

// ViennaFEM includes:
#include "viennafem/fem.hpp"
#include "viennafem/io/vtk_writer.hpp"

// ViennaGrid includes:
#include "viennagrid/forwards.hpp"
#include "viennagrid/config/default_configs.hpp"
#include "viennagrid/io/netgen_reader.hpp"

// ViennaData includes:
#include "viennadata/api.hpp"

// ViennaMath includes:
#include "viennamath/expression.hpp"

// Boost.uBLAS includes:
#include <boost/numeric/ublas/io.hpp>
#include <boost/numeric/ublas/matrix_sparse.hpp>
#include <boost/numeric/ublas/matrix.hpp>
#include <boost/numeric/ublas/operation.hpp>
#include <boost/numeric/ublas/operation_sparse.hpp>

//ViennaCL includes:
#ifndef VIENNACL_HAVE_UBLAS
 #define VIENNACL_HAVE_UBLAS
#endif

#include "viennacl/linalg/cg.hpp"
#include "viennacl/linalg/norm_2.hpp"
#include "viennacl/linalg/prod.hpp"

int main()
{
  typedef viennagrid::hexahedral_3d_mesh                                                  DomainType;
  typedef viennagrid::result_of::segmentation<DomainType>::type                           SegmentationType;

  typedef viennagrid::result_of::element<DomainType, viennagrid::vertex_tag>::type        VertexType;
  typedef viennagrid::result_of::element_range<DomainType, viennagrid::vertex_tag>::type  VertexContainer;
  typedef viennagrid::result_of::iterator<VertexContainer>::type                          VertexIterator;

  typedef boost::numeric::ublas::compressed_matrix<viennafem::numeric_type>  MatrixType;
  typedef boost::numeric::ublas::vector<viennafem::numeric_type>             VectorType;

  typedef viennamath::function_symbol   FunctionSymbol;
  typedef viennamath::equation          Equation;

  //
  // Create a domain from file
  //
  DomainType my_domain;
  SegmentationType segments(my_domain);

  //
  // Create a storage object
  //
  typedef viennadata::storage<> StorageType;
  StorageType   storage;

  try
  {
    viennagrid::io::netgen_reader my_reader;
    my_reader(my_domain, segments, "../examples/data/cube343_hex.mesh");
  }
  catch (...)
  {
    std::cerr << "File-Reader failed. Aborting program..." << std::endl;
    exit(EXIT_FAILURE);
  }

  //
  // Specify two PDEs:
  //
  FunctionSymbol u(0, viennamath::unknown_tag<>());   //an unknown function used for PDE specification
  Equation poisson_equ_1 = viennamath::make_equation( viennamath::laplace(u), -1);
  Equation poisson_equ_2 = viennamath::make_equation( viennamath::laplace(u), -1);

  MatrixType system_matrix_1, system_matrix_2;
  VectorType load_vector_1, load_vector_2;

  //
  // Setting boundary information on domain (this should come from device specification)
  //
  //setting some boundary flags:
  VertexContainer vertices = viennagrid::elements<VertexType>(my_domain);
  for (VertexIterator vit = vertices.begin();
      vit != vertices.end();
      ++vit)
  {
    // First equation: Homogeneous boundary conditions at x=0, x=1, y=0, or y=1
    if ( viennagrid::point(my_domain, *vit)[0] == 0.0 || viennagrid::point(my_domain, *vit)[0] == 1.0
         || viennagrid::point(my_domain, *vit)[1] == 0.0 || viennagrid::point(my_domain, *vit)[1] == 1.0 )
      viennafem::set_dirichlet_boundary(storage, *vit, 0.0, 0);  //simulation with ID 0 uses homogeneous boundary data

    // Boundary for second equation (ID 1): 0 at left boundary, 1 at right boundary
    if ( viennagrid::point(my_domain, *vit)[0] == 0.0)
      viennafem::set_dirichlet_boundary(storage, *vit, 0.0, 1);
    else if ( viennagrid::point(my_domain, *vit)[0] == 1.0)
      viennafem::set_dirichlet_boundary(storage, *vit, 1.0, 1);
  }

  //
  // Create PDE solver functors: (discussion about proper interface required)
  //
  viennafem::pde_assembler<StorageType> fem_assembler(storage);

  //
  // Solve system and write solution vector to pde_result:
  // (discussion about proper interface required. Introduce a pde_result class?)
  //
  fem_assembler(viennafem::make_linear_pde_system(poisson_equ_1,
                                                  u,
                                                  viennafem::make_linear_pde_options(0,
                                                                                viennafem::lagrange_tag<1>(),
                                                                                viennafem::lagrange_tag<1>())
                                                 ),
                my_domain,
                system_matrix_1,
                load_vector_1
               );

  fem_assembler(viennafem::make_linear_pde_system(poisson_equ_2,
                                                  u,
                                                  viennafem::make_linear_pde_options(1,
                                                                                viennafem::lagrange_tag<1>(),
                                                                                viennafem::lagrange_tag<1>())
                                                 ),
                my_domain,
                system_matrix_2,
                load_vector_2
               );

  VectorType pde_result_1 = viennacl::linalg::solve(system_matrix_1, load_vector_1, viennacl::linalg::cg_tag());
  std::cout << "* solve(): Residual: " << norm_2(prod(system_matrix_1, pde_result_1) - load_vector_1) << std::endl;

  VectorType pde_result_2 = viennacl::linalg::solve(system_matrix_2, load_vector_2, viennacl::linalg::cg_tag());
  std::cout << "* solve(): Residual: " << norm_2(prod(system_matrix_2, pde_result_2) - load_vector_2) << std::endl;

  //
  // Writing solution back to domain (discussion about proper way of returning a solution required...)
  //
  viennafem::io::write_solution_to_VTK_file(pde_result_1, "poisson_3d_hex_1", my_domain, segments, storage, 0);
  viennafem::io::write_solution_to_VTK_file(pde_result_2, "poisson_3d_hex_2", my_domain, segments, storage, 1);

  std::cout << "*****************************************" << std::endl;
  std::cout << "* Poisson solver finished successfully! *" << std::endl;
  std::cout << "*****************************************" << std::endl;
  return EXIT_SUCCESS;
}


/**
* @file  AdBidStrategy.cpp
* @brief Implementation to class AdBidStrategy.
*/

#include <functional>
#include <limits>
#include <algorithm>
#include <utility>
#include <set>
#include <queue>
#include <cmath>
#include <time.h>
#include <cstdlib>
#include <cstdio>
#include <boost/unordered_map.hpp>
#include <boost/multi_array.hpp>
#include "AdBidStrategy.h"

namespace sf1r
{

namespace sponsored
{

static const double E = 2.71828;

AdBidStrategy::AdBidStrategy()
{

}

AdBidStrategy::~AdBidStrategy()
{

}

static bool isZero(double f)
{
    return std::abs(f) < std::numeric_limits<double>::epsilon();
}

struct Point
{
    double x;
    double y;

    Point(double X = 0.0, double Y = 0.0):x(X), y(Y) {}
};

static bool operator<(const Point& p1, const Point& p2)
{
    return (p1.x < p2.x) || (isZero(p1.x - p2.x) && p1.y < p2.y);
}

static bool xsmall(const Point& p1, const Point& p2)
{
    return (p1.x < p2.x) ;
}

//judge the turning direction for line p0->p1->p2 at point p1.
//turn left return 1, turn right return -1, in the same line return 0.
static int turnDirection(const Point& p0, const Point& p1, const Point& p2)
{
    double tmp = (p2.x - p0.x)*(p1.y - p0.y) - (p2.y - p0.y)*(p1.x - p0.x);
    if (tmp < 0) return 1;
    else if (tmp > 0) return -1;
    else return 0;
}

class AnglePredicate
{
public:
    AnglePredicate(const Point& p): base_(p) {}

    bool operator()(const Point& p1, const Point& p2)
    {
        int tmp = turnDirection(base_, p1, p2);
        return (tmp > 0) || (tmp == 0 &&
                             (p1.x - base_.x)*(p1.x - base_.x) + (p1.y - base_.y)*(p1.y - base_.y) < (p2.x - base_.x)*(p2.x - base_.x) + (p2.y - base_.y)*(p2.y - base_.y)
                            );
    }

private:
    Point base_;
};

std::vector<std::pair<int, double> > AdBidStrategy::convexUniformBid( const std::vector<AdQueryStatisticInfo>& qsInfos, int budget)
{
    static const std::vector<std::pair<int, double> > NULLBID_(2, std::make_pair(0, 0.0));

    if (qsInfos.empty())
    {
        return NULLBID_;
    }

    //aggregate landscape
    boost::unordered_map<int, Point> landscape; //(cpc, <cost, clicks>)
    for (std::vector<AdQueryStatisticInfo>::const_iterator cit = qsInfos.begin(); cit != qsInfos.end(); ++cit)
    {
        std::vector<int>::const_iterator cpcit = cit->cpc_.begin();
        std::vector<double>::const_iterator ctrit = cit->ctr_.begin();
        for (; cpcit != cit->cpc_.end() && ctrit != cit->ctr_.end(); ++cpcit, ++ctrit)
        {
            Point& curP = landscape[*cpcit];
            curP.x += (*cpcit) * (*ctrit);
            curP.y += (*ctrit);
        }
    }

    std::vector<struct Point> allPoints;
    allPoints.reserve(landscape.size());
    Point minP(std::numeric_limits<double>::max(), std::numeric_limits<double>::max());
    size_t minI = -1;
    for (boost::unordered_map<int, Point>::const_iterator cit = landscape.begin(); cit != landscape.end(); ++cit)
    {
        if (cit->second.y > 0 && cit->second.x > 0)
        {
            allPoints.push_back(cit->second);

            if (cit->second < minP)
            {
                minP = cit->second;
                minI = allPoints.size() - 1;
            }
        }
    }
    //   landscape.clear();

    if (allPoints.empty())
    {
        std::vector<std::pair<int, double> > minBID_(2, std::make_pair(qsInfos.front().minBid_, 0.5));
        return minBID_;
    }

    std::swap(allPoints[0], allPoints[minI]);

    AnglePredicate ap(minP);
    std::sort(++allPoints.begin(), allPoints.end(), ap);

    //convex hull, Graham's Scan Algorithm.
    std::vector<struct Point> ch;
    int i = 0;
    while (i < 2 && i < (int)allPoints.size())
    {
        //init
        ch.push_back(allPoints[i++]);
    }

    for (; i < (int)allPoints.size(); ++i)
    {
        while(ch.size() >= 2)
        {
            int tmp = turnDirection(ch[ch.size() -2], ch[ch.size() - 1], allPoints[i]);
            if (tmp < 0)
            {
                ch.pop_back();
            }
            else
            {
                break;
            }
        }
        ch.push_back(allPoints[i]);
    }
    allPoints.clear();

    //candidate convex hull points for bid
    std::vector<struct Point> canch;
    int j = 0;
    for (; j < (int)ch.size(); ++j)
    {
        if (j < ((int)ch.size()) - 1 && ch[j] < ch[j+1])
        {
            continue;
        }
        else
        {
            break;
        }
    }

    //upper bipart of convex hull.
    canch.reserve(ch.size() - j + 2);
    canch.push_back(Point()); //zero point.
    if (ch.size()>= 2 && !isZero(ch.front().x - ch.back().x))
    {
        canch.push_back(ch[0]);
    }

    for (int i = ch.size() -1; i >= j; --i)
    {
        canch.push_back(ch[i]);
    }

    ch.clear();

    //convex combination
    std::vector<std::pair<int, double> > bid;
    int minBid = qsInfos.front().minBid_;

    int singleBudget = 1000000000;
    int totalImpression = 0;
    for (std::vector<AdQueryStatisticInfo>::const_iterator cit = qsInfos.begin(); cit != qsInfos.end(); ++cit)
    {
        totalImpression += cit->impression_;
        singleBudget = std::min(cit->minBid_, singleBudget);
    }
    if (totalImpression > 0)
    {
        singleBudget = std::max(singleBudget, (int)(budget / (totalImpression / (double)qsInfos.size())));
    }

    Point budgetPoint(singleBudget, 0.0);
    std::vector<struct Point>::const_iterator uit = std::upper_bound(canch.begin(), canch.end(), budgetPoint, xsmall);
    if (uit == canch.end())
    {
        int mybid = std::max(minBid, singleBudget);
        bid.push_back(std::make_pair(mybid, 0.5));
        bid.push_back(std::make_pair(mybid, 0.5));
    }
    else
    {
        std::vector<struct Point>::const_iterator preit = uit - 1;
        if (isZero(preit->x - singleBudget))
        {
            int mybid = isZero(preit->y) ? 0 : int(preit->x / preit->y);
            mybid = std::max(minBid, mybid);
            bid.push_back(std::make_pair(mybid, 0.5));
            bid.push_back(std::make_pair(mybid, 0.5));
        }
        else
        {
            double p = double((singleBudget - preit->x) / (uit->x - preit->x));
            int prebid = int(isZero(preit->y)? 0 : preit->x / preit->y);
            if(prebid > 0)
                prebid = std::max(prebid, minBid);
            int lastbid = std::max(int(uit->x / uit->y), minBid);

            bid.push_back(std::make_pair(prebid, p));
            bid.push_back(std::make_pair(lastbid, 1.0 - p));
        }
    }

    return bid;
}

int AdBidStrategy::realtimeBidWithRevenueMax(const AdQueryStatisticInfo& qsInfo,  int budgetUsed, int budgetLeft, int vpc /*= 1000*/)
{
    int budget = budgetLeft + budgetUsed;
    if (budget <= 0 || qsInfo.cpc_.empty())
    {
        return 0;
    }

    double U = std::numeric_limits<double>::max();
    int minBid = qsInfo.minBid_;
    if (minBid < 0)
    {
        minBid = 0;
    }
    if (minBid > 0)
    {
        U = ((double)vpc) / minBid;
    }

    double z = ((double)budgetUsed) / budget;

    //efficiency
    double eff = pow(U * E, z) / E;

    for (int i = 0; i < (int)qsInfo.cpc_.size(); ++i)
    {
        double avaiableB = budgetLeft / (qsInfo.ctr_[i] * qsInfo.impression_);
        double myeff = ((double)vpc) / qsInfo.cpc_[i];
        if (eff > myeff && qsInfo.cpc_[i] <= avaiableB)
        {
            eff = myeff;
        }
    }

    for (int i = 0; i < (int)qsInfo.cpc_.size(); ++i)
    {
        double myeff = ((double)vpc) / qsInfo.cpc_[i];
        if (myeff >= eff)
        {
            return qsInfo.cpc_[i];
        }
    }

    return int(vpc / (1 + eff));
}

int AdBidStrategy::realtimeBidWithProfitMax( const AdQueryStatisticInfo& qsInfo, int budgetUsed, int budgetLeft, int vpc /*= 1000*/)
{
    static const double MINEFF = 0.1;

    int budget = budgetLeft + budgetUsed;
    if (budget <= 0 || qsInfo.cpc_.empty())
    {
        return 0;
    }

    double U = std::numeric_limits<double>::max();
    int minBid = qsInfo.minBid_;
    if (minBid < 0)
    {
        minBid = 0;
    }
    if (minBid > 0)
    {
        U = ((double)vpc) / minBid - 1;
    }

    double z = ((double)budgetUsed) / budget;

    //efficiency
    double eff = pow(U * E / MINEFF, z) * MINEFF / E;

    for (int i = 0; i < (int)qsInfo.cpc_.size(); ++i)
    {
        double avaiableB = budgetLeft / (qsInfo.ctr_[i] * qsInfo.impression_);
        double myeff = ((double)vpc) / qsInfo.cpc_[i] - 1;
        if (eff > myeff && qsInfo.cpc_[i] <= avaiableB)
        {
            eff = myeff;
        }
    }

    int maxI = -1;
    double maxV = -1.0;
    for (int i = 0; i < (int)qsInfo.cpc_.size(); ++i)
    {
        double myeff = ((double)vpc) / qsInfo.cpc_[i] - 1;
        if (myeff >= eff)
        {
            double myV = (vpc - qsInfo.cpc_[i]) * qsInfo.ctr_[i];
            if (myV > maxV)
            {
                maxV = myV;
                maxI = i;
            }
        }
    }
    if (maxI >= 0)
    {
        return qsInfo.cpc_[maxI];
    }
    else
        return int(vpc / (1 + eff));
}

class LargerFit
{
public:
    LargerFit(const std::vector<double>& Fit): _fit(Fit) {}
    bool operator()(int l, int r)
    {
        return _fit[l] < _fit[r];
    }

private:
    const std::vector<double>& _fit;
};

static void enumKPRecursive(const std::vector<std::vector<double> >& W, const std::vector<std::vector<double> >& V, int i, std::vector<int>&curSol, double leftB, double curValue, double& maxValue, std::vector<int>& maxSol)
{
    if(i >= (int) W.size())
    {
        if(curValue > maxValue)
        {
            maxValue = curValue;
            maxSol = curSol;
        }
        return;
    }

    //do not choose cur item.
    curSol[i] = -1;
    enumKPRecursive(W, V, i+1, curSol, leftB, curValue, maxValue, maxSol);

    for(int j = 0; j < (int)W[i].size(); ++j)
    {
        if(leftB >= W[i][j])
        {
            curSol[i] = j;
            enumKPRecursive(W, V, i+1, curSol, leftB - W[i][j], curValue + V[i][j], maxValue, maxSol);
        }
    }
}

static std::vector<int> enumKP(const std::vector<std::vector<double> >& W, const std::vector<std::vector<double> >& V, int B)
{
    std::vector<int> curSol(W.size(), -1);
    std::vector<int> maxSol(W.size(), -1);
    double maxValue = 0.0;

    enumKPRecursive(W, V, 0, curSol, B, 0.0, maxValue, maxSol);

    return maxSol;
}

//dynamic programming for knapsack problem
static std::vector<int> dpKP(const std::vector<std::vector<double> >& W, const std::vector<std::vector<double> >& V, int B)
{
    typedef boost::multi_array<int, 2> array_type;
    typedef array_type::index index;
    array_type	S(boost::extents[W.size()][B+1]);
    std::vector<double> F(B + 1, 0.0);

    for(int i = 0; i < (int)W.size(); ++i)
    {
        for(int v = B; v >= 0; --v)
        {
            double maxF = F[v];
            int maxI = -1;
            for(int j = 0; j < (int)W[i].size(); ++j)
            {
                if(v >= W[i][j])
                {
                    double myF = F[v - W[i][j]] + V[i][j];
                    if(myF > maxF)
                    {
                        maxF = myF;
                        maxI = j;
                    }
                }
            }
            F[v] = maxF;
            S[i][v] = maxI;
        }
    }

    //construct solution.
    std::vector<int> Sol(W.size(), -1);
    for(int i = W.size() - 1, v = B; i >= 0; --i)
    {
        Sol[i] = S[i][v];
        if(S[i][v] != -1)
        {
            v = (int)(v - W[i][Sol[i]]);
        }
    }

    return Sol;
}

static void convertIndexToBid(const std::vector<AdQueryStatisticInfo>& qsInfos, const std::vector<int>& bidindex, std::vector<int>& bid)
{
    std::vector<AdQueryStatisticInfo>::const_iterator cit = qsInfos.begin();
    int i = 0;
    int bi = 0;
    for (; cit != qsInfos.end(); ++cit, ++i)
    {
        if (cit->bid_ != -1)
        {
            bid[i] = cit->bid_;
        }
        else
        {
            if (bidindex[bi] != -1)
            {
                bid[i] = cit->cpc_[bidindex[bi]];
            }
            else
            {
                bid[i] = 0;
            }

            ++bi;
        }
    }

}

//for debug.
static double computeValue(const std::vector<AdQueryStatisticInfo>& qsInfos, const std::vector<int>& sol)
{
    double myV = 0.0;
    std::vector<AdQueryStatisticInfo>::const_iterator cit = qsInfos.begin();
    std::vector<int>::const_iterator sit = sol.begin();
    for (; cit != qsInfos.end(); ++cit)
    {
        if (cit->bid_ == -1)
        {
            if (*sit != -1)
            {
                myV += cit->ctr_[*sit] * cit->impression_;
            }
            ++sit;
        }
    }

    return myV;
}

static void checkGABid(const std::vector<AdQueryStatisticInfo>& qsInfos, int budget, std::vector<int>& bid)
{
    bool allZeroFlag = true;
    std::vector<AdQueryStatisticInfo>::const_iterator qsIt = qsInfos.begin();
    for (std::vector<int>::iterator it = bid.begin(); it != bid.end(); ++it, ++qsIt)
    {
        if (*it > 0)
        {
            if (*it < qsIt->minBid_)
            {
                *it = qsIt->minBid_;
            }

            allZeroFlag = false;
        }
    }
    if (!allZeroFlag)
    {
        return;
    }

    int minBid = qsInfos.front().minBid_;
    std::vector<std::pair<int, double> > ubid = AdBidStrategy::convexUniformBid(qsInfos, budget);
    int lastBid = int(ubid.front().first * ubid.front().second + ubid.back().first * ubid.back().second);
    lastBid = std::max(lastBid, minBid);
    for (std::vector<int>::iterator it = bid.begin(); it != bid.end(); ++it)
    {
        *it = lastBid;
    }
}

std::vector<int> AdBidStrategy::geneticBid( const std::vector<AdQueryStatisticInfo>& qsInfos, int budget )
{
    std::vector<int> bid(qsInfos.size(), 0);

    //support for predefined bidding.
    int tmpKeywordNum = 0, tmpBidIndex = 0, tmpBudget = budget;
    for (std::vector<AdQueryStatisticInfo>::const_iterator cit = qsInfos.begin(); cit != qsInfos.end(); ++cit, ++tmpBidIndex)
    {
        if (cit->bid_ == -1)
        {
            ++tmpKeywordNum;
            bid[tmpBidIndex] = 0;
        }
        else
        {
            bid[tmpBidIndex] = cit->bid_;
            int i = 0;
            for (; i <(int)cit->cpc_.size(); ++i)
            {
                if (cit->bid_ >= cit->cpc_[i])
                {
                    break;
                }
            }
            if (i < (int)cit->cpc_.size())
            {
                tmpBudget -= cit->cpc_[i] * cit->ctr_[i] * cit->impression_;
            }
        }
    }

    const int KeywordNum = tmpKeywordNum;
    const int AvaiableBudget = tmpBudget;

    const int MaxAllowedEvolutions = 300 * KeywordNum;
    const int MinAllowedEvolutions = 50 * KeywordNum;
    static const double EndPopulationRate = 0.90;  //when 90% of the population has same fitness value, stop evolute.
    static const int PopulationSize = 40; //must be even
    static const int ElitismSize = 2;
    static const double MinFitVariance = 0.001;  //minimum max fitness variance ratio. variance / fit^2
    static const long long MaxLoopNum = 10000000000;
    static const long long MaxDPSpace = 100000000;

    if (KeywordNum <= 0 || AvaiableBudget <= 0)
    {
        return bid;
    }

    typedef std::vector<std::vector<double> > TQSDataType;
    TQSDataType W(KeywordNum), V(KeywordNum);
    std::vector<int> adP(KeywordNum);  //ad position num for each keyword.
    int kNum = 0;
    for (std::vector<AdQueryStatisticInfo>::const_iterator cit = qsInfos.begin(); cit != qsInfos.end(); ++cit)
    {
        if (cit->bid_ != -1)
        {
            continue;
        }

        const std::vector<int>& cpc = cit->cpc_;
        const std::vector<double>& ctr = cit->ctr_;

        W[kNum].reserve(cpc.size());
        V[kNum].reserve(cpc.size());
        adP[kNum] = cpc.size();

        for (int j = 0; j < (int)cpc.size(); ++j)
        {
            W[kNum].push_back(cpc[j] * cit->impression_ * ctr[j]);
            V[kNum].push_back(cit->impression_ * ctr[j]);  //max traffics. value is defined as click traffics.
        }

        ++kNum;
    }

    // judge whether problem can be solved by enumerating to directly get optimal solution.
    {
        long long timeCP = 1;
        for(int i = 0; i < KeywordNum; ++i)
        {
            timeCP *= (W[i].size() + 1);
            if(timeCP > MaxLoopNum) break;
        }
        if(timeCP < MaxLoopNum)
        {
            const std::vector<int>& mySol = enumKP(W, V, AvaiableBudget);
            convertIndexToBid(qsInfos, mySol, bid);
            checkGABid(qsInfos, budget, bid);
            return bid;
        }
    }

    // judge whether problem can be solved by dynamic programming to directly get optimal solution.
    {
        long long spaceComplexity = KeywordNum;
        spaceComplexity *= (AvaiableBudget + 1);
        if (spaceComplexity <= MaxDPSpace)
        {
            const std::vector<int>& mySol = dpKP(W, V, AvaiableBudget);
            convertIndexToBid(qsInfos, mySol, bid);
            checkGABid(qsInfos, budget, bid);
            return bid;
        }
    }

    typedef std::vector<std::vector<int> > TPopType; //every individual is a vector of index of ad position for each keyword, 0-based.
    TPopType P(PopulationSize);
    TPopType newP(PopulationSize);

    srand(time(NULL));
    for (int i = 0; i < PopulationSize; ++i)
    {
        P[i].reserve(KeywordNum);
        newP[i].reserve(KeywordNum);
        for (size_t j = 0; j < KeywordNum; ++j)
        {
            int N = adP[j] + 1;
            P[i].push_back(rand() % N - 1); //ad position is 0-based, -1 means do not bid for that keyword.
            newP[i].push_back(0);
        }

        //clear population for budget requirement.
        for (int i = 0; i < PopulationSize; ++i)
        {
            std::vector<std::pair<int, int> > kw; //(keyword index, selected ad position index)
            for (int j = 0; j < (int)(P[i].size()); ++j)
            {
                //for each keyword
                if (P[i][j] != -1)
                {
                    kw.push_back(std::make_pair(j, P[i][j]));
                }
            }

            double totalW = 0.0;
            for (std::vector<std::pair<int, int> >::const_iterator cit = kw.begin(); cit != kw.end(); ++cit)
            {
                totalW += W[cit->first][cit->second];
            }

            int aN = kw.size();
            while(totalW > AvaiableBudget)
            {
                int r = rand() % aN;
                totalW -= W[kw[r].first][kw[r].second];
                P[i][kw[r].first] = -1;
                std::swap(kw[r], kw[aN - 1]);
                --aN;
            }
        }
    }

    int iterNum = MaxAllowedEvolutions;
    double averageFit = 0.0, averageSquareFit = 0.0;

    while(iterNum--)
    {
        //selection,
        std::vector<int> SP(PopulationSize); //selected individual's index in P
        int GASize = PopulationSize - ElitismSize;

        {
            std::vector<double> fitness(PopulationSize); //total fitness.
            std::vector<double> fit(PopulationSize);    //each individual fitness.
            double fn = 0.0;
            for (int i = 0; i < PopulationSize; ++i)
            {
                double curfn = 0.0;
                for (int j = 0; j < (int)(P[i].size()); ++j)
                {
                    //for each keyword
                    if (P[i][j] != -1)
                    {
                        curfn += V[j][P[i][j]];
                    }
                }
                fn += curfn;
                fit[i] = curfn;
                fitness[i] = fn;
            }

            //check condition of stop evolution.
            if (MaxAllowedEvolutions - iterNum > MinAllowedEvolutions)
            {
                boost::unordered_map<double, int> fitNum;
                for (int i = 0; i < PopulationSize; ++i)
                {
                    fitNum[fit[i]]++;
                }
                int maxNum = -1;
                for (boost::unordered_map<double, int>::const_iterator cit = fitNum.begin(); cit != fitNum.end(); ++cit)
                {
                    if (cit->second > maxNum)
                    {
                        maxNum = cit->second;
                    }
                }

                if ((double)maxNum / PopulationSize >= EndPopulationRate)
                {
                    break;
                }
            }

            //stochastic universal sampling.
            double fstep = fn / GASize;
            double fstart = ((double)rand()) / RAND_MAX * fstep;

            if (!isZero(fstep))
            {
                for (int i = 0, j = 0; i < GASize; ++i)
                {
                    double fcur = fstart + i * fstep;
                    for (int k = j; k < PopulationSize; ++k)
                    {
                        if (fitness[k] > fcur)
                        {
                            SP[i] = k;
                            j = k;
                            break;
                        }
                    }
                }
            }
            else
            {
                for (int i = 0; i < PopulationSize; ++i)
                {
                    SP[i] = i;
                }
            }

            //random to select crossover pair
            for (int i = GASize; i >= 1; --i)
            {
                int j = rand() % i;
                std::swap(SP[j], SP[i - 1]);
            }

            //elitism
            LargerFit mylf(fit);
            std::priority_queue<int, std::vector<int>, LargerFit> pq(mylf);
            for (int i = 0; i < PopulationSize; ++i)
            {
                pq.push(i);
            }

            {
                //average fit
                int eNum = MaxAllowedEvolutions - iterNum;
                double curMaxFit = fit[pq.top()];
                if (!isZero(curMaxFit))
                {
                    averageFit = ((eNum - 1) * averageFit + curMaxFit) / eNum;
                    averageSquareFit = ((eNum - 1) * averageSquareFit + curMaxFit * curMaxFit) / eNum;
                    //variance
                    double variance = averageSquareFit - averageFit * averageFit;
                    if (eNum > MinAllowedEvolutions && variance / (curMaxFit*curMaxFit) < MinFitVariance)
                    {
                        break;
                    }
                }
            }

            for (int i = GASize; i < PopulationSize; ++i)
            {
                SP[i] = pq.top();
                pq.pop();
            }

        }

        //crossover
        int crossoverRate = 85;
        for (int i = 0; i < GASize; i += 2)
        {
            int myRate = rand() % 100;
            if (myRate < crossoverRate)
            {
                const std::vector<int>& lp = P[SP[i]];
                const std::vector<int>& rp = P[SP[i+1]];

                for (int j = 0; j < KeywordNum; ++j)
                {
                    double p = (double)rand() / RAND_MAX * 1.50 - 0.25;

                    if (lp[j] != -1 && rp[j] != -1)
                    {
                        //newP[i][j]
                        int v = (int)(lp[j] * p + rp[j] * (1.0 - p) + 0.5);
                        v = std::max(v, 0);
                        v = std::min(v, adP[j] - 1);
                        newP[i][j] = v;

                        //newP[i+1]
                        //newP[i]
                        v = (int)(lp[j] * ( 1.0 - p ) + rp[j] * p + 0.5);
                        v = std::max(v, 0);
                        v = std::min(v, adP[j] - 1);
                        newP[i+1][j] = v;
                    }
                    else if (lp[j] == -1 && rp[j] == -1)
                    {
                        newP[i][j] = -1;
                        newP[i+1][j] = -1;
                    }
                    else
                    {
                        //discrete
                        int ip = rand() % 2;
                        if (ip)
                        {
                            newP[i][j] = -1;
                            newP[i+1][j] = -1;
                        }
                        else
                        {
                            int tmp = std::max(lp[j], rp[j]);
                            newP[i][j] = tmp;
                            newP[i+1][j] = tmp;
                        }
                    }

                }
            }
            else
            {
                newP[i] = P[SP[i]];
                newP[i+1] = P[SP[i+1]];
            }
        }
        for (int i = GASize; i < PopulationSize; ++i)
        {
            newP[i] = P[SP[i]];
        }

        //mutation, mutation rate, 1/countof(var)
        int mRate = KeywordNum;
        for (int i = 0; i < GASize; ++i)
        {
            for (int j = 0; j < KeywordNum; ++j)
            {
                if (rand() % mRate == 0)
                {
                    //do mutation
                    newP[i][j] = rand() % (adP[j] + 1) - 1;
                }
            }
        }

        newP.swap(P);

        //clear population for budget requirement.
        for (int i = 0; i < PopulationSize; ++i)
        {
            std::vector<std::pair<int, int> > kw; //(keyword index, selected ad position index)
            for (int j = 0; j < (int)(P[i].size()); ++j)
            {
                //for each keyword
                if (P[i][j] != -1)
                {
                    kw.push_back(std::make_pair(j, P[i][j]));
                }
            }

            double totalW = 0.0;
            for (std::vector<std::pair<int, int> >::const_iterator cit = kw.begin(); cit != kw.end(); ++cit)
            {
                totalW += W[cit->first][cit->second];
            }

            int aN = kw.size();
            while(totalW > AvaiableBudget)
            {
                int r = rand() % aN;
                totalW -= W[kw[r].first][kw[r].second];
                P[i][kw[r].first] = -1;
                std::swap(kw[r], kw[aN - 1]);
                --aN;
            }
        }

    }

    //max fitness in population
    int maxI = -1;
    double maxfit = -1.0;
    for (int i = 0; i < PopulationSize; ++i)
    {
        double ft = 0.0;
        for (int j = 0; j < KeywordNum; ++j)
        {
            if (P[i][j] != -1)
            {
                ft += V[j][P[i][j]];
            }
        }
        if (ft > maxfit)
        {
            maxfit = ft;
            maxI = i;
        }
    }

    if (maxI != -1)
    {
        convertIndexToBid(qsInfos, P[maxI], bid);

    }

    checkGABid(qsInfos, budget, bid);
    return bid;
}

}
}


#pragma once

#include <Eigen/Dense>
#include <stdexcept>
#include <boost/foreach.hpp>

namespace argus
{

VectorType flatten_matrices( const std::vector<MatrixType>& mats )
{
	unsigned int n = 0;
	BOOST_FOREACH( const MatrixType& mat, mats )
	{
		n += mat.size();
	}
	VectorType out(n);
	unsigned int ind = 0;
	BOOST_FOREACH( const MatrixType& mat, mats )
	{
		Eigen::Map<const VectorType> v( mat.data(), mat.size(), 1 );
		out.segment(ind, mat.size()) = v;
		ind += v.size();
	}
	return out;
}

MatrixType vstack_matrices( const std::vector<MatrixType>& mats )
{
	unsigned int rows = 0;
	unsigned int cols = mats[0].cols();

	BOOST_FOREACH( const MatrixType& mat, mats )
	{
		rows += mat.rows();
		if( mat.cols() != cols )
		{
			throw std::invalid_argument( "Not all mats same width!" );
		}
	}

	MatrixType out( rows, cols );
	unsigned int ind = 0;
	BOOST_FOREACH( const MatrixType& mat, mats )
	{
		out.block(ind, 0, mat.rows(), mat.cols() ) = mat;
		ind += mat.rows();
	}
	return out;
}

MatrixType hstack_matrices( const std::vector<MatrixType>& mats )
{
	unsigned int rows = mats[0].rows();
	unsigned int cols = 0;

	BOOST_FOREACH( const MatrixType& mat, mats )
	{
		cols += mat.cols();
		if( mat.rows() != rows )
		{
			throw std::invalid_argument( "Not all mats same height!" );
		}
	}

	MatrixType out( rows, cols );
	unsigned int ind = 0;
	BOOST_FOREACH( const MatrixType& mat, mats )
	{
		out.block(0, ind, mat.rows(), mat.cols() ) = mat;
		ind += mat.cols();
	}
	return out;
}

// template <class Derived>
// Derived ConcatenateHor( const Eigen::DenseBase<Derived>& l,
//                         const Eigen::DenseBase<Derived>& r )
// {
// 	if( l.size() == 0 )
// 	{
// 		return r;
// 	}
// 	if( r.size() == 0 )
// 	{
// 		return l;
// 	}

// 	if( l.rows() != r.rows() )
// 	{
// 		throw std::runtime_error( "ConcatenateHor: Dimension mismatch." );
// 	}
// 	Derived out( l.rows(), l.cols() + r.cols() );
// 	out.leftCols( l.cols() ) = l;
// 	out.rightCols( r.cols() ) = r;
// 	return out;
// }

// template <class Derived>
// Derived ConcatenateVer( const Eigen::DenseBase<Derived>& l,
//                         const Eigen::DenseBase<Derived>& r )
// {
// 	if( l.size() == 0 )
// 	{
// 		return r;
// 	}
// 	if( r.size() == 0 )
// 	{
// 		return l;
// 	}

// 	if( l.cols() != r.cols() )
// 	{
// 		throw std::runtime_error( "ConcatenateVer: Dimension mismatch." );
// 	}
// 	Derived out( l.rows() + r.rows(), l.cols() );
// 	out.topRows( l.rows() ) = l;
// 	out.bottomRows( r.rows() ) = r;
// 	return out;
// }

}


//
// Copyright (c) 2015-2019 CNRS INRIA
// Copyright (c) 2015 Wandercraft, 86 rue de Paris 91400 Orsay, France.
//

#include "pinocchio/bindings/python/spatial/explog.hpp"
#include <boost/python.hpp>

namespace pinocchio
{
  namespace python
  {
    namespace bp = boost::python;

    void exposeExplog()
    {

      bp::def("exp3",&exp3_proxy<Eigen::Vector3d>,
              bp::arg("Angular velocity (vector of size 3)"),
              "Exp: so3 -> SO3. Return the integral of the input"
              " angular velocity during time 1.");

      bp::def("Jexp3",&Jexp3_proxy<Eigen::Vector3d>,
              bp::arg("v: Angular velocity (vector of size 3)"),
              "Jacobian of exp(R) which maps from the tangent of SO(3) at exp(v) to"
              " the tangent of SO(3) at Identity.");

      bp::def("log3",&log3_proxy<Eigen::Matrix3d>,
              bp::arg("Rotation matrix (matrix of size 3x3))"),
              "Log: SO3 -> so3. Pseudo-inverse of log from SO3"
              " -> { v in so3, ||v|| < 2pi }.Exp: so3 -> SO3.");

      bp::def("Jlog3",&Jlog3_proxy<Eigen::Matrix3d>,
              bp::arg("Rotation matrix R (matrix of size 3x3)"),
              "Jacobian of log(R) which maps from the tangent of SO(3) at R to"
              " the tangent of SO(3) at Identity.");

      bp::def("exp6",&exp6_proxy<double,0>,
              bp::arg("Spatial velocity (Motion)"),
              "Exp: se3 -> SE3. Return the integral of the input"
              " spatial velocity during time 1.");

      bp::def("exp6",&exp6_proxy<Motion::Vector6>,
              bp::arg("Spatial velocity (vector 6x1)"),
              "Exp: se3 -> SE3. Return the integral of the input"
              " spatial velocity during time 1.");

      bp::def("Jexp6",&Jexp6_proxy<double,0>,
              bp::arg("v: Spatial velocity (Motion)"),
              "Jacobian of exp(v) which maps from the tangent of SE(3) at exp(v) to"
              " the tangent of SE(3) at Identity.");

      bp::def("Jexp6",&Jexp6_proxy<Motion::Vector6>,
              bp::arg("v: Spatial velocity (vector 6x1)"),
              "Jacobian of exp(v) which maps from the tangent of SE(3) at exp(v) to"
              " the tangent of SE(3) at Identity.");

      bp::def("log6",(Motion (*)(const SE3 &))&log6<double,0>,
              bp::arg("Spatial transform (SE3)"),
              "Log: SE3 -> se3. Pseudo-inverse of exp from SE3"
              " -> { v,w in se3, ||w|| < 2pi }.");

      bp::def("log6",&log6_proxy<Eigen::Matrix4d>,
              bp::arg("Homegenious matrix (matrix 4x4)"),
              "Log: SE3 -> se3. Pseudo-inverse of exp from SE3"
              " -> { v,w in se3, ||w|| < 2pi }.");

      bp::def("Jlog6",&Jlog6_proxy<double,0>,
              bp::arg("Spatial transform M (SE3)"),
              "Jacobian of log(M) which maps from the tangent of SE(3) at M to"
              " the tangent of SE(3) at Identity.");

    }

  } // namespace python
} // namespace pinocchio