/*    Copyright (c) 2010-2012, Delft University of Technology
 *    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.
 *      - Neither the name of the Delft University of Technology nor the names of its contributors
 *        may be used to endorse or promote products derived from this software without specific
 *        prior written permission.
 *
 *    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.
 *
 *    Changelog
 *      YYMMDD    Author            Comment
 *      120611    P. Musegaas       First creation of code.
 *      120827    P. Musegaas       Adaptation to own ephemeris type.
 *
 *    References
 *      Musegaas, P. (2012). Optimization of Space Trajectories Including Multiple Gravity Assists
 *          and Deep Space Maneuvers. MSc Thesis, Delft University of Technology, Delft,
 *          The Netherlands.
 *
 */

#define BOOST_TEST_MAIN

#include <vector>

#include <boost/make_shared.hpp>
#include <memory>
#include <boost/test/floating_point_comparison.hpp>
#include <boost/test/unit_test.hpp>

#include <Eigen/Core>

#include <Tudat/Astrodynamics/BasicAstrodynamics/physicalConstants.h>
#include <Tudat/Basics/testMacros.h>
#include <Tudat/Mathematics/BasicMathematics/mathematicalConstants.h>
#include "Tudat/Astrodynamics/BasicAstrodynamics/unitConversions.h"
#include <Tudat/Astrodynamics/BasicAstrodynamics/orbitalElementConversions.h>

#include <Tudat/InputOutput/basicInputOutput.h>

#include "Tudat/Astrodynamics/Ephemerides/approximatePlanetPositions.h"
#include "Tudat/Astrodynamics/TrajectoryDesign/trajectory.h"

namespace tudat
{
namespace unit_tests
{

// Additional namespaces to be used.
using namespace tudat::input_output;
using namespace tudat::input_output::parsed_data_vector_utilities;
using namespace tudat::transfer_trajectories;

//! Test implementation of trajectory class
BOOST_AUTO_TEST_SUITE( test_trajectory )

//! Test delta-V computation for simple MGA trajectory model.
BOOST_AUTO_TEST_CASE( testMGATrajectory )
{
    // Set tolerance. Due to ephemeris differences, higher accuracy cannot be achieved.
    const double tolerance = 15.0e-2;

    // Expected test result based on the ideal Cassini 1 trajectory as modelled by GTOP software
    // distributed and downloadable from the ESA website, or within the PaGMO Astrotoolbox.
    const double expectedDeltaV = 4930.72686847243;

    // Specify required parameters
    // Specify the number of legs and type of legs.
    const int numberOfLegs = 6;
    std::vector< int > legTypeVector;
    legTypeVector.resize( numberOfLegs );
    legTypeVector[ 0 ] = mga_Departure; legTypeVector[ 1 ] = mga_Swingby;
    legTypeVector[ 2 ] = mga_Swingby; legTypeVector[ 3 ] = mga_Swingby; legTypeVector[ 4 ] = mga_Swingby;
    legTypeVector[ 5 ] = capture;

    // Create the ephemeris vector.
    std::vector< ephemerides::EphemerisPointer >
            ephemerisVector( numberOfLegs );
    ephemerisVector[ 0 ] = std::make_shared< ephemerides::ApproximatePlanetPositions >( ephemerides::ApproximatePlanetPositionsBase::BodiesWithEphemerisData::earthMoonBarycenter );
    ephemerisVector[ 1 ] = std::make_shared< ephemerides::ApproximatePlanetPositions >( ephemerides::ApproximatePlanetPositionsBase::BodiesWithEphemerisData::venus );
    ephemerisVector[ 2 ] = std::make_shared< ephemerides::ApproximatePlanetPositions >( ephemerides::ApproximatePlanetPositionsBase::BodiesWithEphemerisData::venus );
    ephemerisVector[ 3 ] = std::make_shared< ephemerides::ApproximatePlanetPositions >( ephemerides::ApproximatePlanetPositionsBase::BodiesWithEphemerisData::earthMoonBarycenter );
    ephemerisVector[ 4 ] = std::make_shared< ephemerides::ApproximatePlanetPositions >( ephemerides::ApproximatePlanetPositionsBase::BodiesWithEphemerisData::jupiter );
    ephemerisVector[ 5 ] = std::make_shared< ephemerides::ApproximatePlanetPositions >( ephemerides::ApproximatePlanetPositionsBase::BodiesWithEphemerisData::saturn );

    // Create gravitational parameter vector
    Eigen::VectorXd gravitationalParameterVector( numberOfLegs );
    gravitationalParameterVector << 3.9860119e14, 3.24860e14, 3.24860e14, 3.9860119e14, 1.267e17,
            3.79e16;

    // Create variable vector.
    Eigen::VectorXd variableVector ( numberOfLegs + 1 );
    variableVector << -789.8117, 158.302027105278, 449.385873819743, 54.7489684339665,
                      1024.36205846918, 4552.30796805542, 1/*dummy*/;
    variableVector *= physical_constants::JULIAN_DAY;

    // Create departure and capture variables.
    Eigen::VectorXd semiMajorAxes ( 2 ), eccentricities ( 2 );
    semiMajorAxes << std::numeric_limits< double >::infinity( ), 1.0895e8 / 0.02;
    eccentricities << 0., 0.98;

    // Sun gravitational parameter
    const double sunGravitationalParameter = 1.32712428e20;

    // Create minimum pericenter radii vector
    Eigen::VectorXd minimumPericenterRadii ( numberOfLegs );
    minimumPericenterRadii << 6778000., 6351800., 6351800., 6778000., 600000000., 600000000.;

    // Create the trajectory problem.
    Trajectory Cassini1 ( numberOfLegs, legTypeVector, ephemerisVector,
                          gravitationalParameterVector, variableVector, sunGravitationalParameter,
                          minimumPericenterRadii, semiMajorAxes, eccentricities );

    // Start the deltaV vector.
    double resultingDeltaV;
    Cassini1.calculateTrajectory( resultingDeltaV );

    // Test if the computed delta-V corresponds to the expected value within the specified
    // tolerance and if the computed velocity before target planet matches the expected velocity
    // within the specified tolerance.
    BOOST_CHECK_CLOSE_FRACTION( expectedDeltaV, resultingDeltaV, tolerance );
}

//! Test delta-V computation for MGA-1DSM Velocity Formulation trajectory model.
BOOST_AUTO_TEST_CASE( testMGA1DSMVFTrajectory1 )
{
    // Set tolerance. Due to ephemeris differences, higher accuracy cannot be achieved.
    const double tolerance = 2.0e-3;

    // Expected test result based on the ideal Messenger trajectory as modelled by GTOP software
    // distributed and downloadable from the ESA website, or within the PaGMO Astrotoolbox.
    const double expectedDeltaV = 8630.83256199051;

    // Specify required parameters
    // Specify the number of legs and type of legs.
    const int numberOfLegs = 5;
    std::vector< int > legTypeVector;
    legTypeVector.resize( numberOfLegs );
    legTypeVector[0] = mga1DsmVelocity_Departure; legTypeVector[1] = mga1DsmVelocity_Swingby;
    legTypeVector[2] = mga1DsmVelocity_Swingby; legTypeVector[3] = mga1DsmVelocity_Swingby;
    legTypeVector[4] = capture;

    // Create the ephemeris vector.
    std::vector< ephemerides::EphemerisPointer >
            ephemerisVector( numberOfLegs );
    ephemerisVector[ 0 ] = std::make_shared< ephemerides::ApproximatePlanetPositions >( ephemerides::ApproximatePlanetPositionsBase::BodiesWithEphemerisData::earthMoonBarycenter );
    ephemerisVector[ 1 ] = std::make_shared< ephemerides::ApproximatePlanetPositions >( ephemerides::ApproximatePlanetPositionsBase::BodiesWithEphemerisData::earthMoonBarycenter );
    ephemerisVector[ 2 ] = std::make_shared< ephemerides::ApproximatePlanetPositions >( ephemerides::ApproximatePlanetPositionsBase::BodiesWithEphemerisData::venus );
    ephemerisVector[ 3 ] = std::make_shared< ephemerides::ApproximatePlanetPositions >( ephemerides::ApproximatePlanetPositionsBase::BodiesWithEphemerisData::venus );
    ephemerisVector[ 4 ] = std::make_shared< ephemerides::ApproximatePlanetPositions >( ephemerides::ApproximatePlanetPositionsBase::BodiesWithEphemerisData::mercury );

    // Create gravitational parameter vector
    Eigen::VectorXd gravitationalParameterVector( numberOfLegs );
    gravitationalParameterVector << 3.9860119e14, 3.9860119e14, 3.24860e14, 3.24860e14, 2.2321e13;

    // Create variable vector.
    Eigen::VectorXd variableVector ( numberOfLegs /*time of flight*/ + 1 /*start epoch*/ +
                                     4 * ( numberOfLegs - 1 ) /*additional variables for model,
                                                                except the final capture leg*/ );

    // Add the time of flight and start epoch, which are in JD.
    variableVector << 1171.64503236 * physical_constants::JULIAN_DAY,
                      399.999999715 * physical_constants::JULIAN_DAY,
                      178.372255301 * physical_constants::JULIAN_DAY,
                      299.223139512 * physical_constants::JULIAN_DAY,
                      180.510754824 * physical_constants::JULIAN_DAY,
                      1, // The capture time is irrelevant for the final leg.
    // Add the additional variables.
                      0.234594654679, 1408.99421278, 0.37992647165 * 2 * 3.14159265358979,
                      std::acos(  2 * 0.498004040298 - 1. ) - 3.14159265358979 / 2, // 1st leg.
                      0.0964769387134, 1.35077257078, 1.80629232251 * 6.378e6, 0.0, // 2nd leg.
                      0.829948744508, 1.09554368115, 3.04129845698 * 6.052e6, 0.0, // 3rd leg.
                      0.317174785637, 1.34317576594, 1.10000000891 * 6.052e6, 0.0; //4th leg.

    // Sun gravitational parameter
    const double sunGravitationalParameter = 1.32712428e20;

    // Create minimum pericenter radii vector
    Eigen::VectorXd minimumPericenterRadii ( numberOfLegs );
    minimumPericenterRadii << TUDAT_NAN, TUDAT_NAN, TUDAT_NAN, TUDAT_NAN, TUDAT_NAN;

    // Create departure and capture variables.
    Eigen::VectorXd semiMajorAxes ( 2 ), eccentricities ( 2 );
    semiMajorAxes << std::numeric_limits< double >::infinity( ),
                     std::numeric_limits< double >::infinity( );
    eccentricities << 0., 0.;

    // Create the trajectory problem.
    Trajectory Messenger ( numberOfLegs, legTypeVector, ephemerisVector,
                           gravitationalParameterVector, variableVector, sunGravitationalParameter,
                           minimumPericenterRadii, semiMajorAxes, eccentricities );

    // Start the deltaV vector.
    double resultingDeltaV;
    Messenger.calculateTrajectory( resultingDeltaV );

    // Test if the computed delta-V corresponds to the expected value within the specified
    // tolerance and if the computed velocity before target planet matches the expected velocity
    // within the specified tolerance.
    BOOST_CHECK_CLOSE_FRACTION( expectedDeltaV, resultingDeltaV, tolerance );
}

//! Test delta-V computation for another MGA-1DSM Velocity Formulation trajectory model.
BOOST_AUTO_TEST_CASE( testMGA1DSMVFTrajectory2 )
{
    // Set tolerance. Due to ephemeris differences, higher accuracy cannot be achieved.
    const double tolerance = 4.0e-2;

    // Expected test result based on the ideal Cassini 2 trajectory as modelled by GTOP software
    // distributed and downloadable from the ESA website, or within the PaGMO Astrotoolbox.
    const double expectedDeltaV = 8385.15784516116;

    // Specify required parameters
    // Specify the number of legs and type of legs.
    const int numberOfLegs = 6;
    std::vector< int > legTypeVector;
    legTypeVector.resize( numberOfLegs );
    legTypeVector[0] = mga1DsmVelocity_Departure; legTypeVector[1] = mga1DsmVelocity_Swingby;
    legTypeVector[2] = mga1DsmVelocity_Swingby; legTypeVector[3] = mga1DsmVelocity_Swingby;
    legTypeVector[4] = mga1DsmVelocity_Swingby; legTypeVector[5] = capture;

    // Create the ephemeris vector.
    std::vector< ephemerides::EphemerisPointer >
            ephemerisVector( numberOfLegs );
    ephemerisVector[ 0 ] = std::make_shared< ephemerides::ApproximatePlanetPositions >( ephemerides::ApproximatePlanetPositionsBase::BodiesWithEphemerisData::earthMoonBarycenter );
    ephemerisVector[ 1 ] = std::make_shared< ephemerides::ApproximatePlanetPositions >( ephemerides::ApproximatePlanetPositionsBase::BodiesWithEphemerisData::venus );
    ephemerisVector[ 2 ] = std::make_shared< ephemerides::ApproximatePlanetPositions >( ephemerides::ApproximatePlanetPositionsBase::BodiesWithEphemerisData::venus );
    ephemerisVector[ 3 ] = std::make_shared< ephemerides::ApproximatePlanetPositions >( ephemerides::ApproximatePlanetPositionsBase::BodiesWithEphemerisData::earthMoonBarycenter );
    ephemerisVector[ 4 ] = std::make_shared< ephemerides::ApproximatePlanetPositions >( ephemerides::ApproximatePlanetPositionsBase::BodiesWithEphemerisData::jupiter );
    ephemerisVector[ 5 ] = std::make_shared< ephemerides::ApproximatePlanetPositions >( ephemerides::ApproximatePlanetPositionsBase::BodiesWithEphemerisData::saturn );
    // Create gravitational parameter vector
    Eigen::VectorXd gravitationalParameterVector( numberOfLegs );
    gravitationalParameterVector << 3.9860119e14, 3.24860e14, 3.24860e14, 3.9860119e14, 1.267e17,
                                    3.79e16;

    // Create variable vector.
    Eigen::VectorXd variableVector ( numberOfLegs /*time of flight*/ + 1 /*start epoch*/ +
                                     4 * ( numberOfLegs - 1 ) /*additional variables for model,
                                                                except the final capture leg*/ );

    // Add the time of flight and start epoch, which are in JD.
    variableVector << -779.046753814506 * physical_constants::JULIAN_DAY,
                      167.378952534645 * physical_constants::JULIAN_DAY,
                      424.028254165204 * physical_constants::JULIAN_DAY,
                      53.2897409769205 * physical_constants::JULIAN_DAY,
                      589.766954923325 * physical_constants::JULIAN_DAY,
                      2200.00000000000 * physical_constants::JULIAN_DAY,
                      1, // The capture time is irrelevant for the final leg.
    // Add the additional variables.
            0.769483451363201, 3259.11446832345, 0.525976214695235 * 2 * 3.14159265358979,
            std::acos(  2 * 0.38086496458657 - 1 ) - 3.14159265358979 / 2, // 1st leg.
            0.513289529822621, -1.5937371121191, 1.34877968657176 * 6.052e6, 0.0, // 2nd leg.
            0.0274175362264024, -1.95952512232447, 1.05 * 6.052e6, 0.0, // 3rd leg.
            0.263985256705873, -1.55498859283059, 1.30730278372017 * 6.378e6, 0.0, //4th leg.
            0.599984695281461, -1.5134625299674, 69.8090142993495 * 7.1492e7, 0.0; //5th leg.

    // Create departure and capture variables.
    Eigen::VectorXd semiMajorAxes ( 2 ), eccentricities ( 2 );
    semiMajorAxes << std::numeric_limits< double >::infinity( ),
                     std::numeric_limits< double >::infinity( );
    eccentricities << 0., 0.;

    // Sun gravitational parameter
    const double sunGravitationalParameter = 1.32712428e20;

    // Create minimum pericenter radii vector
    Eigen::VectorXd minimumPericenterRadii ( numberOfLegs );
    minimumPericenterRadii << TUDAT_NAN, TUDAT_NAN, TUDAT_NAN, TUDAT_NAN, TUDAT_NAN, TUDAT_NAN;

    // Create the trajectory problem.
    Trajectory Cassini2 ( numberOfLegs, legTypeVector, ephemerisVector,
                          gravitationalParameterVector, variableVector, sunGravitationalParameter,
                          minimumPericenterRadii, semiMajorAxes, eccentricities );

    // Start the deltaV vector.
    double resultingDeltaV;
    Cassini2.calculateTrajectory( resultingDeltaV );

    // Test if the computed delta-V corresponds to the expected value within the specified
    // tolerance and if the computed velocity before target planet matches the expected velocity
    // within the specified tolerance.
    BOOST_CHECK_CLOSE_FRACTION( expectedDeltaV, resultingDeltaV, tolerance );
}

//! Test update functionality for simple MGA trajectory model.
BOOST_AUTO_TEST_CASE( testUpdateMGATrajectory )
{
    // Set tolerance. Due to ephemeris differences, higher accuracy cannot be achieved.
    const double tolerance = 15.0e-2;

    // Expected test result based on the ideal Cassini 1 trajectory as modelled by GTOP software
    // distributed and downloadable from the ESA website, or within the PaGMO Astrotoolbox.
    const double expectedDeltaV = 4930.72686847243;

    // Specify required parameters
    // Specify the number of legs and type of legs.
    const int numberOfLegs = 6;
    std::vector< int > legTypeVector;
    legTypeVector.resize( numberOfLegs );
    legTypeVector[0] = mga_Departure; legTypeVector[1] = mga_Swingby;
    legTypeVector[2] = mga_Swingby; legTypeVector[3] = mga_Swingby; legTypeVector[4] = mga_Swingby;
    legTypeVector[5] = capture;

    // Create the ephemeris vector.
    std::vector< ephemerides::EphemerisPointer >
            ephemerisVector( numberOfLegs );
    ephemerisVector[ 0 ] = std::make_shared< ephemerides::ApproximatePlanetPositions >( ephemerides::ApproximatePlanetPositionsBase::BodiesWithEphemerisData::earthMoonBarycenter );
    ephemerisVector[ 1 ] = std::make_shared< ephemerides::ApproximatePlanetPositions >( ephemerides::ApproximatePlanetPositionsBase::BodiesWithEphemerisData::venus );
    ephemerisVector[ 2 ] = std::make_shared< ephemerides::ApproximatePlanetPositions >( ephemerides::ApproximatePlanetPositionsBase::BodiesWithEphemerisData::venus );
    ephemerisVector[ 3 ] = std::make_shared< ephemerides::ApproximatePlanetPositions >( ephemerides::ApproximatePlanetPositionsBase::BodiesWithEphemerisData::earthMoonBarycenter );
    ephemerisVector[ 4 ] = std::make_shared< ephemerides::ApproximatePlanetPositions >( ephemerides::ApproximatePlanetPositionsBase::BodiesWithEphemerisData::jupiter );
    ephemerisVector[ 5 ] = std::make_shared< ephemerides::ApproximatePlanetPositions >( ephemerides::ApproximatePlanetPositionsBase::BodiesWithEphemerisData::saturn );

    // Create gravitational parameter vector
    Eigen::VectorXd gravitationalParameterVector( numberOfLegs );
    gravitationalParameterVector << 3.9860119e14, 3.24860e14, 3.24860e14, 3.9860119e14, 1.267e17,
            3.79e16;

    // Create a dummy variable vector. This dummy variable vector is finally multiplied by 1.01 to
    // ensure that the code breaks if the update function is not complete. This is done in favour
    // of passing the NaNs, which was done in the mission legs tests, because this will generate
    // error messages caused by an impossible conversion betweeen mean and eccentric anomaly.
    Eigen::VectorXd dummyVariableVector ( numberOfLegs + 1 );
    dummyVariableVector << -789.8117, 158.302027105278, 449.385873819743, 54.7489684339665,
                           1024.36205846918, 4552.30796805542, 1/*dummy*/;
    dummyVariableVector *= physical_constants::JULIAN_DAY;
    dummyVariableVector *= 1.01;

    // Create departure and capture variables.
    Eigen::VectorXd semiMajorAxes ( 2 ), eccentricities ( 2 );
    semiMajorAxes << std::numeric_limits< double >::infinity( ), 1.0895e8 / 0.02;
    eccentricities << 0., 0.98;

    // Sun gravitational parameter
    const double sunGravitationalParameter = 1.32712428e20;

    // Create minimum pericenter radii vector
    Eigen::VectorXd minimumPericenterRadii ( numberOfLegs );
    minimumPericenterRadii << 6778000., 6351800., 6351800., 6778000., 600000000., 600000000.;

    // Create the trajectory problem.
    Trajectory cassini1 ( numberOfLegs, legTypeVector, ephemerisVector,
                          gravitationalParameterVector, dummyVariableVector,
                          sunGravitationalParameter, minimumPericenterRadii, semiMajorAxes,
                          eccentricities );

    // Start the deltaV vector.
    double resultingDeltaV;
    cassini1.calculateTrajectory( resultingDeltaV );

    // Create variable vector containing the updated variables.
    Eigen::VectorXd variableVector ( numberOfLegs + 1 );
    variableVector << -789.8117, 158.302027105278, 449.385873819743, 54.7489684339665,
                      1024.36205846918, 4552.30796805542, 1/*dummy*/;
    variableVector *= physical_constants::JULIAN_DAY;

    // Pass the variable to the class.
    cassini1.updateVariableVector( variableVector );
    cassini1.updateEphemeris( );

    // Recalculate the trajectory.
    cassini1.calculateTrajectory( resultingDeltaV );

    // Test if the computed delta-V corresponds to the expected value within the specified
    // tolerance and if the computed velocity before target planet matches the expected velocity
    // within the specified tolerance.
    BOOST_CHECK_CLOSE_FRACTION( expectedDeltaV, resultingDeltaV, tolerance );
}

//! Test update functionality for MGA-1DSM Velocity Formulation trajectory model.
BOOST_AUTO_TEST_CASE( testUpdateMGA1DSMVFTrajectory )
{
    // Set tolerance. Due to ephemeris differences, higher accuracy cannot be achieved.
    const double tolerance = 2.0e-3;

    // Expected test result based on the ideal Messenger trajectory as modelled by GTOP software
    // distributed and downloadable from the ESA website, or within the PaGMO Astrotoolbox.
    const double expectedDeltaV = 8630.83256199051;

    // Specify required parameters
    // Specify the number of legs and type of legs.
    const int numberOfLegs = 5;
    std::vector< int > legTypeVector;
    legTypeVector.resize( numberOfLegs );
    legTypeVector[0] = mga1DsmVelocity_Departure; legTypeVector[1] = mga1DsmVelocity_Swingby;
    legTypeVector[2] = mga1DsmVelocity_Swingby; legTypeVector[3] = mga1DsmVelocity_Swingby;
    legTypeVector[4] = capture;

    // Create the ephemeris vector.
    std::vector< ephemerides::EphemerisPointer >
            ephemerisVector( numberOfLegs );
    ephemerisVector[ 0 ] = std::make_shared< ephemerides::ApproximatePlanetPositions >( ephemerides::ApproximatePlanetPositionsBase::BodiesWithEphemerisData::earthMoonBarycenter );
    ephemerisVector[ 1 ] = std::make_shared< ephemerides::ApproximatePlanetPositions >( ephemerides::ApproximatePlanetPositionsBase::BodiesWithEphemerisData::earthMoonBarycenter );
    ephemerisVector[ 2 ] = std::make_shared< ephemerides::ApproximatePlanetPositions >( ephemerides::ApproximatePlanetPositionsBase::BodiesWithEphemerisData::venus );
    ephemerisVector[ 3 ] = std::make_shared< ephemerides::ApproximatePlanetPositions >( ephemerides::ApproximatePlanetPositionsBase::BodiesWithEphemerisData::venus );
    ephemerisVector[ 4 ] = std::make_shared< ephemerides::ApproximatePlanetPositions >( ephemerides::ApproximatePlanetPositionsBase::BodiesWithEphemerisData::mercury );

    // Create gravitational parameter vector
    Eigen::VectorXd gravitationalParameterVector( numberOfLegs );
    gravitationalParameterVector << 3.9860119e14, 3.9860119e14, 3.24860e14, 3.24860e14, 2.2321e13;

    // Create a dummy variable vector. This dummy variable vector is finally multiplied by 1.01 to
    // ensure that the code breaks if the update function is not complete. This is done in favour
    // of passing the NaNs, which was done in the mission legs tests, because this will generate
    // error messages caused by an impossible conversion betweeen mean and eccentric anomaly.
    Eigen::VectorXd dummyVariableVector ( numberOfLegs /*time of flight*/ + 1 /*start epoch*/ +
                                          4 * ( numberOfLegs - 1 ) /*additional variables for
                                          model, except the final capture leg*/ );

    // Add the time of flight and start epoch, which are in JD.
    dummyVariableVector << 1171.64503236 * physical_constants::JULIAN_DAY,
                           399.999999715 * physical_constants::JULIAN_DAY,
                           178.372255301 * physical_constants::JULIAN_DAY,
                           299.223139512 * physical_constants::JULIAN_DAY,
                           180.510754824 * physical_constants::JULIAN_DAY,
                           1, // The capture time is irrelevant for the final leg.
    // Add the additional variables.
                           0.234594654679, 1408.99421278, 0.37992647165 * 2 * 3.14159265358979,
                           std::acos(  2 * 0.498004040298 - 1 ) - 3.14159265358979 / 2, // 1st leg.
                           0.0964769387134, 1.35077257078, 1.80629232251 * 6.378e6, 0.0,// 2nd leg.
                           0.829948744508, 1.09554368115, 3.04129845698 * 6.052e6, 0.0, // 3rd leg.
                           0.317174785637, 1.34317576594, 1.10000000891 * 6.052e6, 0.0; //4th leg.
    dummyVariableVector *= 1.01;

    // Sun gravitational parameter.
    const double sunGravitationalParameter = 1.32712428e20;

    // Create minimum pericenter radii vector.
    Eigen::VectorXd minimumPericenterRadii ( numberOfLegs );
    minimumPericenterRadii << TUDAT_NAN, TUDAT_NAN, TUDAT_NAN, TUDAT_NAN, TUDAT_NAN;

    // Create departure and capture variables.
    Eigen::VectorXd semiMajorAxes ( 2 ), eccentricities ( 2 );
    semiMajorAxes << std::numeric_limits< double >::infinity( ),
                     std::numeric_limits< double >::infinity( );
    eccentricities << 0., 0.;

    // Create the trajectory problem.
    Trajectory messenger ( numberOfLegs, legTypeVector, ephemerisVector,
                           gravitationalParameterVector, dummyVariableVector,
                           sunGravitationalParameter, minimumPericenterRadii, semiMajorAxes,
                           eccentricities );

    // Start the deltaV vector.
    double resultingDeltaV;
    messenger.calculateTrajectory( resultingDeltaV );

    // Create variable vector containing the updated variables.
    Eigen::VectorXd variableVector ( numberOfLegs /*time of flight*/ + 1 /*start epoch*/ +
                                     4 * ( numberOfLegs - 1 ) /*additional variables for model,
                                                                except the final capture leg*/ );

    // Add the time of flight and start epoch, which are in JD.
    variableVector << 1171.64503236 * physical_constants::JULIAN_DAY,
                      399.999999715 * physical_constants::JULIAN_DAY,
                      178.372255301 * physical_constants::JULIAN_DAY,
                      299.223139512 * physical_constants::JULIAN_DAY,
                      180.510754824 * physical_constants::JULIAN_DAY,
                      1, // The capture time is irrelevant for the final leg.
    // Add the additional variables.
                      0.234594654679, 1408.99421278, 0.37992647165 * 2 * 3.14159265358979,
                      std::acos(  2 * 0.498004040298 - 1 ) - 3.14159265358979 / 2, // 1st leg.
                      0.0964769387134, 1.35077257078, 1.80629232251 * 6.378e6, 0.0, // 2nd leg.
                      0.829948744508, 1.09554368115, 3.04129845698 * 6.052e6, 0.0, // 3rd leg.
                      0.317174785637, 1.34317576594, 1.10000000891 * 6.052e6, 0.0; //4th leg.

    // Pass the variable to the class.
    messenger.updateVariableVector( variableVector );
    messenger.updateEphemeris( );

    // Recalculate the trajectory.
    messenger.calculateTrajectory( resultingDeltaV );

    // Test if the computed delta-V corresponds to the expected value within the specified
    // tolerance and if the computed velocity before target planet matches the expected velocity
    // within the specified tolerance.
    BOOST_CHECK_CLOSE_FRACTION( expectedDeltaV, resultingDeltaV, tolerance );
}

BOOST_AUTO_TEST_SUITE_END( )

} // namespace unit_tests
} // namespace tudat


#include "vep/VariantEffect.h"
#include <vector>
#include <boost/algorithm/string.hpp>
#include <vep/VepFile.h>
#include <util/VepUtil.h>

VariantEffect::VariantEffectType VariantEffect::getType(const std::string &refCodon, const std::string &altCodon) {

	unsigned refLength = refCodon.length();
	unsigned altLength = altCodon.length();

	if(refLength == altLength) return VariantEffectType::SNV;
	if(refLength % 3 != 0 || altLength % 3 != 0) return VariantEffectType::FRAMESHIFT_VARIANT;
	return (refLength < altLength) ? VariantEffectType::INFRAME_INSERTION : VariantEffectType::INFRAME_DELETION;

}

std::optional<VariantEffect> VariantEffect::parse(const std::string &line) {

	std::vector<std::string> tokens;
	boost::split(tokens, line, boost::is_any_of("\t"));

	if(VepUtil::filter(tokens)) return std::nullopt;

	unsigned id = std::stoul(tokens[VepFile::UPLOADED_VARIATION]);

	// parse location
	auto location = std::string_view(tokens[VepFile::LOCATION]);
	size_t colonPos = location.find(':');
	size_t positionEnd = location.find(colonPos, '-');

	Contig contig(location.substr(0, colonPos));
	unsigned position = std::stoul(location.substr(colonPos+1, positionEnd-colonPos-1).data());

	std::string& gene = tokens[VepFile::GENE];
	std::string& transcript = tokens[VepFile::FEATURE];

	auto proteinPosStr = std::string_view(tokens[VepFile::PROTEIN_POSITION]);
	unsigned proteinPosition = std::stoul(proteinPosStr.substr(0, proteinPosStr.find('-')).data());

	// Parse extras
	auto extras = std::string_view(tokens[VepFile::EXTRA]);
	size_t enspStart = extras.find("ENSP=");
	size_t enspEnd = extras.find(enspStart, ';');

	size_t strandStart = extras.find("STRAND=");
	size_t strandEnd = extras.find(strandStart, ';');

	std::string ensp = extras.substr(enspStart+5, enspEnd-enspStart-5).data();
	int strand = std::stoi(extras.substr(strandStart+7, strandEnd - strandStart - 7).data());

	// parse codons
	boost::erase_all(tokens[VepFile::CODONS], "-");
	auto codons = std::string_view(tokens[VepFile::CODONS]);
	size_t split = codons.find('/');
	auto ref = std::string(codons.substr(0, split));
	auto alt = std::string(codons.substr(split+1));

	return VariantEffect(id, contig, position, gene, transcript, strand, ensp, ref, alt, proteinPosition);

}

VariantEffect::VariantEffect(unsigned id, Contig &chr, unsigned pos, std::string &gene, std::string &transcript,
                             int strand, std::string &ensp, std::string &cRef, std::string &cAlt, unsigned proteinPos)
     : variantId(id), chromosome(std::move(chr)), position(pos), gene(std::move(gene)),
       transcript(std::move(transcript)), strand(strand), proteinId(std::move(ensp)), codonRef(std::move(cRef)),
       codonAlt(std::move(cAlt)), proteinPosition(proteinPos){

	static unsigned counter = 0;
	type = getType(codonRef, codonAlt);
	variantEffectId = counter++;

}

const std::string& VariantEffect::getTypeAsString() const {
	static const std::string snv = "SNV";
	static const std::string ins = "Insertion";
	static const std::string del = "Deletion";
	static const std::string fs = "Frameshift";
	static const std::string empty;

	switch(type){
		case SNV:
			return snv;
		case INFRAME_INSERTION:
			return ins;
		case INFRAME_DELETION:
			return del;
		case FRAMESHIFT_VARIANT:
			return fs;
		default:
			return empty;
	}
}


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

#include <boost/variant.hpp>

#include <openssl/evp.h>

#include <tuple>

#include "mococrw/padding_mode.h"
#include "mococrw/hash.h"

namespace mococrw {

using namespace openssl;

using SSL_RSA_OAEP_LABEL_Ptr = std::unique_ptr<uint8_t, SSLFree<uint8_t>>;

/*
 * Interface Destructors
 */

RSAEncryptionPadding::~RSAEncryptionPadding() = default;
RSASignaturePadding::~RSASignaturePadding() = default;

/*
 * NoPadding
 */

bool NoPadding::checkMessageSize(const AsymmetricPublicKey &key, size_t messageSize) const
{
    size_t keySizeInBit = key.getKeySize();
    return messageSize*8 == keySizeInBit;
}

void NoPadding::prepareOpenSSLContext(openssl::SSL_EVP_PKEY_CTX_Ptr& ctx) const
{
    openssl::_EVP_PKEY_CTX_set_rsa_padding(ctx.get(), static_cast<int>(openssl::RSAPaddingMode::NONE));
}

/*
 * PKCSPadding
 */

bool PKCSPadding::checkMessageSize(const AsymmetricPublicKey &key, size_t messageSize) const
{
    int remainingSize = key.getKeySize() - PKCS_MAX_SIZE_OVERHEAD*8;
    return remainingSize > 0 && messageSize*8 <= static_cast<size_t>(remainingSize);
}

void PKCSPadding::prepareOpenSSLContext(openssl::SSL_EVP_PKEY_CTX_Ptr& ctx) const
{
    _EVP_PKEY_CTX_set_rsa_padding(ctx.get(), static_cast<int>(openssl::RSAPaddingMode::PKCS1));
}

void PKCSPadding::prepareOpenSSLContext(openssl::SSL_EVP_PKEY_CTX_Ptr& ctx,
                                                 openssl::DigestTypes hashFunction) const
{
    _EVP_PKEY_CTX_set_rsa_padding(ctx.get(), static_cast<int>(openssl::RSAPaddingMode::PKCS1));
    _EVP_PKEY_CTX_set_signature_md(ctx.get(), _getMDPtrFromDigestType(hashFunction));
}

/*
 * MGF1
 */

MGF1::MGF1(openssl::DigestTypes hashFunction) : _hashFunction(hashFunction) {}

void MGF1::prepareOpenSSLContext(openssl::SSL_EVP_PKEY_CTX_Ptr& ctx) const {
    _EVP_PKEY_CTX_set_rsa_mgf1_md(ctx.get(),
                                  _getMDPtrFromDigestType(_hashFunction));
}

/*
 * PSSPadding
 */

class PSSPadding::Impl {
public:
    Impl(std::shared_ptr<MaskGenerationFunction> maskGenerationFunction, boost::optional<int> saltLength)
        : maskGenerationFunction(std::move(maskGenerationFunction)), saltLength(std::move(saltLength)) {}

    void prepareOpenSSLContext(openssl::SSL_EVP_PKEY_CTX_Ptr& ctx, openssl::DigestTypes hashFunction) const {

        _EVP_PKEY_CTX_set_rsa_padding(ctx.get(), static_cast<int>(openssl::RSAPaddingMode::PSS));
        _EVP_PKEY_CTX_set_signature_md(ctx.get(), _getMDPtrFromDigestType(hashFunction));

        int saltLength;
        if (this->saltLength != boost::none) {
            saltLength = *(this->saltLength);
        } else {
            saltLength = Hash::getDigestSize(hashFunction);
        }

        _EVP_PKEY_CTX_set_rsa_pss_saltlen(ctx.get(), saltLength);

        if (maskGenerationFunction != nullptr) {
            maskGenerationFunction->prepareOpenSSLContext(ctx);
        } else {
            MGF1(hashFunction).prepareOpenSSLContext(ctx);
        }
    }

    std::shared_ptr<MaskGenerationFunction> maskGenerationFunction;
    boost::optional<int> saltLength;
};

PSSPadding::PSSPadding(std::shared_ptr<MaskGenerationFunction> maskGenerationFunction,
                       boost::optional<int> saltLength)
    : _impl(std::make_unique<PSSPadding::Impl>(maskGenerationFunction, std::move(saltLength)))
{}

PSSPadding::PSSPadding(const PSSPadding& other)
    : _impl(std::make_unique<PSSPadding::Impl>(*(other._impl))) {}

PSSPadding& PSSPadding::operator=(const PSSPadding& other) {
    _impl = std::make_unique<PSSPadding::Impl>(*(other._impl));
    return *this;
}

PSSPadding::~PSSPadding() = default;

void PSSPadding::prepareOpenSSLContext(openssl::SSL_EVP_PKEY_CTX_Ptr& ctx,
                                       openssl::DigestTypes hashFunction) const
{
    _impl->prepareOpenSSLContext(ctx, hashFunction);
}

/*
 * OAEPPadding
 */

class OAEPPadding::Impl {
public:
    Impl(openssl::DigestTypes hashFunction, std::shared_ptr<MaskGenerationFunction> maskGenerationFunction, const std::string& label)
        : hashFunction(hashFunction), label(label) {
        if (maskGenerationFunction != nullptr) {
            this->maskGenerationFunction = maskGenerationFunction;
        } else {
            this->maskGenerationFunction = std::make_shared<MGF1>(hashFunction);
        }
    }

    openssl::DigestTypes hashFunction;
    std::string label;
    std::shared_ptr<MaskGenerationFunction> maskGenerationFunction;
};

OAEPPadding::OAEPPadding(openssl::DigestTypes hashFunction, std::shared_ptr<MaskGenerationFunction> maskGenerationFunction, const std::string& label)
    : _impl(std::make_unique<OAEPPadding::Impl>(hashFunction, maskGenerationFunction, label)) {}

OAEPPadding::OAEPPadding(const OAEPPadding& other) : _impl(std::make_unique<OAEPPadding::Impl>(*(other._impl))) {}

OAEPPadding& OAEPPadding::operator=(const OAEPPadding& other) {
    _impl = std::make_unique<OAEPPadding::Impl>(*(other._impl));
    return *this;
}

OAEPPadding::~OAEPPadding() = default;

bool OAEPPadding::checkMessageSize(const AsymmetricPublicKey &key, size_t messageSize) const
{
    int remainingSize = key.getKeySize() -
            (2 * openssl::_EVP_MD_size(_getMDPtrFromDigestType(_impl->hashFunction)))*8 - 2*8;
    return remainingSize > 0 && messageSize*8 <= static_cast<size_t>(remainingSize);
}

void OAEPPadding::prepareOpenSSLContext(openssl::SSL_EVP_PKEY_CTX_Ptr& ctx) const
{
    SSL_RSA_OAEP_LABEL_Ptr label_copy{nullptr};

    try {
        _EVP_PKEY_CTX_set_rsa_padding(ctx.get(), static_cast<int>(openssl::RSAPaddingMode::OAEP));

        _EVP_PKEY_CTX_set_rsa_oaep_md(ctx.get(),
                                      _getMDPtrFromDigestType(_impl->hashFunction));

        _impl->maskGenerationFunction->prepareOpenSSLContext(ctx);

        if (!_impl->label.empty()) {

            /* Make a copy of the label, since the context takes ownership of it when calling
             * '_EVP_PKEY_CTX_set_rsa_oaep_label()' function */
            label_copy.reset(static_cast<uint8_t *>(
                                     _OPENSSL_malloc(_impl->label.size())));
            memcpy(label_copy.get(),
                   _impl->label.data(), _impl->label.size());

            _EVP_PKEY_CTX_set_rsa_oaep_label(ctx.get(),
                                             static_cast<unsigned char *>(label_copy.get()),
                                             static_cast<int>(_impl->label.size()));

            /* Release ownership from the unique_ptr since the function above takes ownership of
             * the label pointer unless it throws an exception*/
            std::ignore = label_copy.release();
        }
    } catch (const OpenSSLException &e) {
        throw MoCOCrWException(e.what());
    }
}

}


/*===================================================================

The Medical Imaging Interaction Toolkit (MITK)

Copyright (c) German Cancer Research Center,
Division of Medical and Biological Informatics.
All rights reserved.

This software is distributed WITHOUT ANY WARRANTY; without
even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE.

See LICENSE.txt or http://www.mitk.org for details.

===================================================================*/

// Blueberry
#include <berryISelectionService.h>
#include <berryIWorkbenchWindow.h>

// Qmitk
#include "QmitkFiberProcessingView.h"
#include <QmitkStdMultiWidget.h>

// Qt
#include <QMessageBox>

// MITK
#include <mitkNodePredicateProperty.h>
#include <mitkImageCast.h>
#include <mitkPointSet.h>
#include <mitkPlanarCircle.h>
#include <mitkPlanarPolygon.h>
#include <mitkPlanarRectangle.h>
#include <mitkPlanarFigureInteractor.h>
#include <mitkGlobalInteraction.h>
#include <mitkImageAccessByItk.h>
#include <mitkDataNodeObject.h>
#include <mitkDiffusionImage.h>
#include <mitkTensorImage.h>

// ITK
#include <itkResampleImageFilter.h>
#include <itkGaussianInterpolateImageFunction.h>
#include <itkImageRegionIteratorWithIndex.h>
#include <itkTractsToFiberEndingsImageFilter.h>
#include <itkTractDensityImageFilter.h>
#include <itkImageRegion.h>
#include <itkTractsToRgbaImageFilter.h>
#include <itkTractsToVectorImageFilter.h>

#include <math.h>
#include <boost/lexical_cast.hpp>

const std::string QmitkFiberProcessingView::VIEW_ID = "org.mitk.views.fiberprocessing";
const std::string id_DataManager = "org.mitk.views.datamanager";
using namespace mitk;

QmitkFiberProcessingView::QmitkFiberProcessingView()
    : QmitkFunctionality()
    , m_Controls( 0 )
    , m_MultiWidget( NULL )
    , m_UpsamplingFactor(5)
{

}

// Destructor
QmitkFiberProcessingView::~QmitkFiberProcessingView()
{

}

void QmitkFiberProcessingView::CreateQtPartControl( QWidget *parent )
{
    // build up qt view, unless already done
    if ( !m_Controls )
    {
        // create GUI widgets from the Qt Designer's .ui file
        m_Controls = new Ui::QmitkFiberProcessingViewControls;
        m_Controls->setupUi( parent );

        connect( m_Controls->m_ProcessFiberBundleButton, SIGNAL(clicked()), this, SLOT(ProcessSelectedBundles()) );
        connect( m_Controls->m_ResampleFibersButton, SIGNAL(clicked()), this, SLOT(ResampleSelectedBundles()) );
        connect(m_Controls->m_FaColorFibersButton, SIGNAL(clicked()), this, SLOT(DoImageColorCoding()));
        connect( m_Controls->m_PruneFibersButton, SIGNAL(clicked()), this, SLOT(PruneBundle()) );
        connect( m_Controls->m_CurvatureThresholdButton, SIGNAL(clicked()), this, SLOT(ApplyCurvatureThreshold()) );
        connect( m_Controls->m_MirrorFibersButton, SIGNAL(clicked()), this, SLOT(MirrorFibers()) );
        connect( m_Controls->m_CompressFibersButton, SIGNAL(clicked()), this, SLOT(CompressSelectedBundles()) );
        connect( m_Controls->m_ExtractFiberPeaks, SIGNAL(clicked()), this, SLOT(CalculateFiberDirections()) );
    }
}

void QmitkFiberProcessingView::StdMultiWidgetAvailable (QmitkStdMultiWidget &stdMultiWidget)
{
    m_MultiWidget = &stdMultiWidget;
}

void QmitkFiberProcessingView::StdMultiWidgetNotAvailable()
{
    m_MultiWidget = NULL;
}

void QmitkFiberProcessingView::CalculateFiberDirections()
{
    typedef itk::Image<unsigned char, 3>                                            ItkUcharImgType;
    typedef itk::Image< itk::Vector< float, 3>, 3 >                                 ItkDirectionImage3DType;
    typedef itk::VectorContainer< unsigned int, ItkDirectionImage3DType::Pointer >  ItkDirectionImageContainerType;

    // load fiber bundle
    mitk::FiberBundleX::Pointer inputTractogram = dynamic_cast<mitk::FiberBundleX*>(m_SelectedFB.back()->GetData());

    itk::TractsToVectorImageFilter<float>::Pointer fOdfFilter = itk::TractsToVectorImageFilter<float>::New();
    if (m_SelectedImage.IsNotNull())
    {
        ItkUcharImgType::Pointer itkMaskImage = ItkUcharImgType::New();
        mitk::CastToItkImage<ItkUcharImgType>(m_SelectedImage, itkMaskImage);
        fOdfFilter->SetMaskImage(itkMaskImage);
    }

    // extract directions from fiber bundle
    fOdfFilter->SetFiberBundle(inputTractogram);
    fOdfFilter->SetAngularThreshold(cos(m_Controls->m_AngularThreshold->value()*M_PI/180));
    fOdfFilter->SetNormalizeVectors(true);
    fOdfFilter->SetUseWorkingCopy(true);
    fOdfFilter->SetCreateDirectionImages(m_Controls->m_DirectionImagesBox->isChecked());
    fOdfFilter->SetSizeThreshold(m_Controls->m_PeakThreshold->value());
    fOdfFilter->SetMaxNumDirections(m_Controls->m_MaxNumDirections->value());
    fOdfFilter->Update();

    QString name = m_SelectedFB.back()->GetName().c_str();

    if (m_Controls->m_VectorFieldBox->isChecked())
    {
        float minSpacing = 1;
        if (m_SelectedImage.IsNotNull())
        {
            mitk::Vector3D outImageSpacing = m_SelectedImage->GetGeometry()->GetSpacing();

            if(outImageSpacing[0]<outImageSpacing[1] && outImageSpacing[0]<outImageSpacing[2])
                minSpacing = outImageSpacing[0];
            else if (outImageSpacing[1] < outImageSpacing[2])
                minSpacing = outImageSpacing[1];
            else
                minSpacing = outImageSpacing[2];
        }

        mitk::FiberBundleX::Pointer directions = fOdfFilter->GetOutputFiberBundle();
        mitk::DataNode::Pointer node = mitk::DataNode::New();
        node->SetData(directions);
        node->SetName((name+"_vectorfield").toStdString().c_str());
        node->SetProperty("Fiber2DSliceThickness", mitk::FloatProperty::New(minSpacing));
        node->SetProperty("Fiber2DfadeEFX", mitk::BoolProperty::New(false));
        node->SetProperty("color", mitk::ColorProperty::New(1.0f, 1.0f, 1.0f));

        GetDefaultDataStorage()->Add(node, m_SelectedFB.back());
    }

    if (m_Controls->m_NumDirectionsBox->isChecked())
    {
        mitk::Image::Pointer mitkImage = mitk::Image::New();
        mitkImage->InitializeByItk( fOdfFilter->GetNumDirectionsImage().GetPointer() );
        mitkImage->SetVolume( fOdfFilter->GetNumDirectionsImage()->GetBufferPointer() );

        mitk::DataNode::Pointer node = mitk::DataNode::New();
        node->SetData(mitkImage);
        node->SetName((name+"_numdirections").toStdString().c_str());
        GetDefaultDataStorage()->Add(node, m_SelectedFB.back());
    }

    if (m_Controls->m_DirectionImagesBox->isChecked())
    {
        ItkDirectionImageContainerType::Pointer directionImageContainer = fOdfFilter->GetDirectionImageContainer();
        for (unsigned int i=0; i<directionImageContainer->Size(); i++)
        {
            itk::TractsToVectorImageFilter<float>::ItkDirectionImageType::Pointer itkImg = directionImageContainer->GetElement(i);

            if (itkImg.IsNull())
                return;

            mitk::Image::Pointer mitkImage = mitk::Image::New();
            mitkImage->InitializeByItk( itkImg.GetPointer() );
            mitkImage->SetVolume( itkImg->GetBufferPointer() );

            mitk::DataNode::Pointer node = mitk::DataNode::New();
            node->SetData(mitkImage);
            node->SetName( (name+"_direction_"+boost::lexical_cast<std::string>(i).c_str()).toStdString().c_str());
            node->SetVisibility(false);
            GetDefaultDataStorage()->Add(node, m_SelectedFB.back());
        }
    }
}

void QmitkFiberProcessingView::UpdateGui()
{
    m_Controls->m_CompressFibersButton->setEnabled(!m_SelectedFB.empty());
    m_Controls->m_ProcessFiberBundleButton->setEnabled(!m_SelectedFB.empty());
    m_Controls->m_ResampleFibersButton->setEnabled(!m_SelectedFB.empty());
    m_Controls->m_FaColorFibersButton->setEnabled(!m_SelectedFB.empty());
    m_Controls->m_PruneFibersButton->setEnabled(!m_SelectedFB.empty());
    m_Controls->m_CurvatureThresholdButton->setEnabled(!m_SelectedFB.empty());
    m_Controls->m_ExtractFiberPeaks->setEnabled(!m_SelectedFB.empty());

    // are fiber bundles selected?
    if ( m_SelectedFB.empty() )
    {
        if (m_SelectedSurfaces.size()>0)
            m_Controls->m_MirrorFibersButton->setEnabled(true);
        else
            m_Controls->m_MirrorFibersButton->setEnabled(false);
    }
    else
    {
        if (m_SelectedImage.IsNotNull())
            m_Controls->m_FaColorFibersButton->setEnabled(true);
    }
}

void QmitkFiberProcessingView::OnSelectionChanged( std::vector<mitk::DataNode*> nodes )
{
    //reset existing Vectors containing FiberBundles and PlanarFigures from a previous selection
    m_SelectedFB.clear();
    m_SelectedSurfaces.clear();
    m_SelectedImage = NULL;

    for( std::vector<mitk::DataNode*>::iterator it = nodes.begin(); it != nodes.end(); ++it )
    {
        mitk::DataNode::Pointer node = *it;
        if ( dynamic_cast<mitk::FiberBundleX*>(node->GetData()) )
        {
            m_SelectedFB.push_back(node);
        }
        else if (dynamic_cast<mitk::Image*>(node->GetData()))
            m_SelectedImage = dynamic_cast<mitk::Image*>(node->GetData());
        else if (dynamic_cast<mitk::Surface*>(node->GetData()))
        {
            m_SelectedSurfaces.push_back(dynamic_cast<mitk::Surface*>(node->GetData()));
        }
    }
    UpdateGui();
    GenerateStats();
}

void QmitkFiberProcessingView::Activated()
{

}

void QmitkFiberProcessingView::PruneBundle()
{
    int minLength = this->m_Controls->m_PruneFibersSpinBox->value();
    int maxLength = this->m_Controls->m_MaxPruneFibersSpinBox->value();
    for (int i=0; i<m_SelectedFB.size(); i++)
    {
        mitk::FiberBundleX::Pointer fib = dynamic_cast<mitk::FiberBundleX*>(m_SelectedFB.at(i)->GetData());
        if (!fib->RemoveShortFibers(minLength))
            QMessageBox::information(NULL, "No output generated:", "The resulting fiber bundle contains no fibers.");
        else if (!fib->RemoveLongFibers(maxLength))
            QMessageBox::information(NULL, "No output generated:", "The resulting fiber bundle contains no fibers.");
    }
    GenerateStats();
    RenderingManager::GetInstance()->RequestUpdateAll();
}

void QmitkFiberProcessingView::ApplyCurvatureThreshold()
{
    int mm = this->m_Controls->m_MinCurvatureRadiusBox->value();
    for (int i=0; i<m_SelectedFB.size(); i++)
    {
        mitk::FiberBundleX::Pointer fib = dynamic_cast<mitk::FiberBundleX*>(m_SelectedFB.at(i)->GetData());
        if (!fib->ApplyCurvatureThreshold(mm, this->m_Controls->m_RemoveFiberDueToCurvatureCheckbox->isChecked()))
            QMessageBox::information(NULL, "No output generated:", "The resulting fiber bundle contains no fibers.");
    }
    GenerateStats();
    RenderingManager::GetInstance()->RequestUpdateAll();
}

void QmitkFiberProcessingView::GenerateStats()
{
    if ( m_SelectedFB.empty() )
        return;

    QString stats("");

    for( int i=0; i<m_SelectedFB.size(); i++ )
    {
        mitk::DataNode::Pointer node = m_SelectedFB[i];
        if (node.IsNotNull() && dynamic_cast<mitk::FiberBundleX*>(node->GetData()))
        {
            if (i>0)
                stats += "\n-----------------------------\n";
            stats += QString(node->GetName().c_str()) + "\n";
            mitk::FiberBundleX::Pointer fib = dynamic_cast<mitk::FiberBundleX*>(node->GetData());
            stats += "Number of fibers: "+ QString::number(fib->GetNumFibers()) + "\n";
            stats += "Number of points: "+ QString::number(fib->GetNumberOfPoints()) + "\n";
            stats += "Min. length:         "+ QString::number(fib->GetMinFiberLength(),'f',1) + " mm\n";
            stats += "Max. length:         "+ QString::number(fib->GetMaxFiberLength(),'f',1) + " mm\n";
            stats += "Mean length:         "+ QString::number(fib->GetMeanFiberLength(),'f',1) + " mm\n";
            stats += "Median length:       "+ QString::number(fib->GetMedianFiberLength(),'f',1) + " mm\n";
            stats += "Standard deviation:  "+ QString::number(fib->GetLengthStDev(),'f',1) + " mm\n";
        }
    }
    this->m_Controls->m_StatsTextEdit->setText(stats);
}

void QmitkFiberProcessingView::ProcessSelectedBundles()
{
    if ( m_SelectedFB.empty() ){
        QMessageBox::information( NULL, "Warning", "No fibe bundle selected!");
        MITK_WARN("QmitkFiberProcessingView") << "no fibe bundle selected";
        return;
    }

    int generationMethod = m_Controls->m_GenerationBox->currentIndex();

    for( int i=0; i<m_SelectedFB.size(); i++ )
    {
        mitk::DataNode::Pointer node = m_SelectedFB[i];
        if (node.IsNotNull() && dynamic_cast<mitk::FiberBundleX*>(node->GetData()))
        {
            mitk::FiberBundleX::Pointer fib = dynamic_cast<mitk::FiberBundleX*>(node->GetData());
            QString name(node->GetName().c_str());
            DataNode::Pointer newNode = NULL;
            switch(generationMethod){
            case 0:
                newNode = GenerateTractDensityImage(fib, false, true);
                name += "_TDI";
                break;
            case 1:
                newNode = GenerateTractDensityImage(fib, false, false);
                name += "_TDI";
                break;
            case 2:
                newNode = GenerateTractDensityImage(fib, true, false);
                name += "_envelope";
                break;
            case 3:
                newNode = GenerateColorHeatmap(fib);
                break;
            case 4:
                newNode = GenerateFiberEndingsImage(fib);
                name += "_fiber_endings";
                break;
            case 5:
                newNode = GenerateFiberEndingsPointSet(fib);
                name += "_fiber_endings";
                break;
            }
            if (newNode.IsNotNull())
            {
                newNode->SetName(name.toStdString());
                GetDataStorage()->Add(newNode);
            }
        }
    }
}

// generate pointset displaying the fiber endings
mitk::DataNode::Pointer QmitkFiberProcessingView::GenerateFiberEndingsPointSet(mitk::FiberBundleX::Pointer fib)
{
    mitk::PointSet::Pointer pointSet = mitk::PointSet::New();
    vtkSmartPointer<vtkPolyData> fiberPolyData = fib->GetFiberPolyData();
    vtkSmartPointer<vtkCellArray> vLines = fiberPolyData->GetLines();
    vLines->InitTraversal();

    int count = 0;
    int numFibers = fib->GetNumFibers();
    for( int i=0; i<numFibers; i++ )
    {
        vtkIdType   numPoints(0);
        vtkIdType*  points(NULL);
        vLines->GetNextCell ( numPoints, points );

        if (numPoints>0)
        {
            double* point = fiberPolyData->GetPoint(points[0]);
            itk::Point<float,3> itkPoint;
            itkPoint[0] = point[0];
            itkPoint[1] = point[1];
            itkPoint[2] = point[2];
            pointSet->InsertPoint(count, itkPoint);
            count++;
        }
        if (numPoints>2)
        {
            double* point = fiberPolyData->GetPoint(points[numPoints-1]);
            itk::Point<float,3> itkPoint;
            itkPoint[0] = point[0];
            itkPoint[1] = point[1];
            itkPoint[2] = point[2];
            pointSet->InsertPoint(count, itkPoint);
            count++;
        }
    }

    mitk::DataNode::Pointer node = mitk::DataNode::New();
    node->SetData( pointSet );
    return node;
}

// generate image displaying the fiber endings
mitk::DataNode::Pointer QmitkFiberProcessingView::GenerateFiberEndingsImage(mitk::FiberBundleX::Pointer fib)
{
    typedef unsigned char OutPixType;
    typedef itk::Image<OutPixType,3> OutImageType;

    typedef itk::TractsToFiberEndingsImageFilter< OutImageType > ImageGeneratorType;
    ImageGeneratorType::Pointer generator = ImageGeneratorType::New();
    generator->SetFiberBundle(fib);
    generator->SetUpsamplingFactor(m_Controls->m_UpsamplingSpinBox->value());
    if (m_SelectedImage.IsNotNull())
    {
        OutImageType::Pointer itkImage = OutImageType::New();
        CastToItkImage(m_SelectedImage, itkImage);
        generator->SetInputImage(itkImage);
        generator->SetUseImageGeometry(true);
    }
    generator->Update();

    // get output image
    OutImageType::Pointer outImg = generator->GetOutput();
    mitk::Image::Pointer img = mitk::Image::New();
    img->InitializeByItk(outImg.GetPointer());
    img->SetVolume(outImg->GetBufferPointer());

    // init data node
    mitk::DataNode::Pointer node = mitk::DataNode::New();
    node->SetData(img);
    return node;
}

// generate rgba heatmap from fiber bundle
mitk::DataNode::Pointer QmitkFiberProcessingView::GenerateColorHeatmap(mitk::FiberBundleX::Pointer fib)
{
    typedef itk::RGBAPixel<unsigned char> OutPixType;
    typedef itk::Image<OutPixType, 3> OutImageType;
    typedef itk::TractsToRgbaImageFilter< OutImageType > ImageGeneratorType;
    ImageGeneratorType::Pointer generator = ImageGeneratorType::New();
    generator->SetFiberBundle(fib);
    generator->SetUpsamplingFactor(m_Controls->m_UpsamplingSpinBox->value());
    if (m_SelectedImage.IsNotNull())
    {
        itk::Image<unsigned char, 3>::Pointer itkImage = itk::Image<unsigned char, 3>::New();
        CastToItkImage(m_SelectedImage, itkImage);
        generator->SetInputImage(itkImage);
        generator->SetUseImageGeometry(true);
    }
    generator->Update();

    // get output image
    typedef itk::Image<OutPixType,3> OutType;
    OutType::Pointer outImg = generator->GetOutput();
    mitk::Image::Pointer img = mitk::Image::New();
    img->InitializeByItk(outImg.GetPointer());
    img->SetVolume(outImg->GetBufferPointer());

    // init data node
    mitk::DataNode::Pointer node = mitk::DataNode::New();
    node->SetData(img);
    return node;
}

// generate tract density image from fiber bundle
mitk::DataNode::Pointer QmitkFiberProcessingView::GenerateTractDensityImage(mitk::FiberBundleX::Pointer fib, bool binary, bool absolute)
{
    typedef float OutPixType;
    typedef itk::Image<OutPixType, 3> OutImageType;

    itk::TractDensityImageFilter< OutImageType >::Pointer generator = itk::TractDensityImageFilter< OutImageType >::New();
    generator->SetFiberBundle(fib);
    generator->SetBinaryOutput(binary);
    generator->SetOutputAbsoluteValues(absolute);
    generator->SetUpsamplingFactor(m_Controls->m_UpsamplingSpinBox->value());
    if (m_SelectedImage.IsNotNull())
    {
        OutImageType::Pointer itkImage = OutImageType::New();
        CastToItkImage(m_SelectedImage, itkImage);
        generator->SetInputImage(itkImage);
        generator->SetUseImageGeometry(true);

    }
    generator->Update();

    // get output image
    typedef itk::Image<OutPixType,3> OutType;
    OutType::Pointer outImg = generator->GetOutput();
    mitk::Image::Pointer img = mitk::Image::New();
    img->InitializeByItk(outImg.GetPointer());
    img->SetVolume(outImg->GetBufferPointer());

    // init data node
    mitk::DataNode::Pointer node = mitk::DataNode::New();
    node->SetData(img);
    return node;
}

void QmitkFiberProcessingView::ResampleSelectedBundles()
{
    double factor = this->m_Controls->m_ResampleFibersSpinBox->value();
    for (int i=0; i<m_SelectedFB.size(); i++)
    {
        mitk::FiberBundleX::Pointer fib = dynamic_cast<mitk::FiberBundleX*>(m_SelectedFB.at(i)->GetData());
        fib->DoFiberSmoothing(factor);
    }
    GenerateStats();
    RenderingManager::GetInstance()->RequestUpdateAll();
}

void QmitkFiberProcessingView::CompressSelectedBundles()
{
    double factor = this->m_Controls->m_FiberErrorSpinBox->value();
    for (int i=0; i<m_SelectedFB.size(); i++)
    {
        mitk::FiberBundleX::Pointer fib = dynamic_cast<mitk::FiberBundleX*>(m_SelectedFB.at(i)->GetData());
        fib->CompressFibers(factor);
    }
    GenerateStats();
    RenderingManager::GetInstance()->RequestUpdateAll();
}

void QmitkFiberProcessingView::MirrorFibers()
{
    unsigned int axis = this->m_Controls->m_AxisSelectionBox->currentIndex();
    for (int i=0; i<m_SelectedFB.size(); i++)
    {
        mitk::FiberBundleX::Pointer fib = dynamic_cast<mitk::FiberBundleX*>(m_SelectedFB.at(i)->GetData());
        fib->MirrorFibers(axis);
    }
    if (m_SelectedFB.size()>0)
        GenerateStats();

    if (m_SelectedSurfaces.size()>0)
    {
        for (int i=0; i<m_SelectedSurfaces.size(); i++)
        {
            mitk::Surface::Pointer surf = m_SelectedSurfaces.at(i);
            vtkSmartPointer<vtkPolyData> poly = surf->GetVtkPolyData();
            vtkSmartPointer<vtkPoints> vtkNewPoints = vtkSmartPointer<vtkPoints>::New();

            for (int i=0; i<poly->GetNumberOfPoints(); i++)
            {
                double* point = poly->GetPoint(i);
                point[axis] *= -1;
                vtkNewPoints->InsertNextPoint(point);
            }
            poly->SetPoints(vtkNewPoints);
            surf->CalculateBoundingBox();
        }
    }

    RenderingManager::GetInstance()->RequestUpdateAll();
}

void QmitkFiberProcessingView::DoImageColorCoding()
{
    if (m_SelectedImage.IsNull())
        return;

    for( int i=0; i<m_SelectedFB.size(); i++ )
    {
        mitk::FiberBundleX::Pointer fib = dynamic_cast<mitk::FiberBundleX*>(m_SelectedFB.at(i)->GetData());
        fib->SetFAMap(m_SelectedImage);
        fib->SetColorCoding(mitk::FiberBundleX::COLORCODING_FA_BASED);
        fib->DoColorCodingFaBased();
    }

    if(m_MultiWidget)
        m_MultiWidget->RequestUpdate();
}


// Copyright (c) Prevail Verifier contributors.
// SPDX-License-Identifier: MIT
#include <set>
#include <regex>

#include <boost/lexical_cast.hpp>

#include "string_constraints.hpp"

#include "crab/split_dbm.hpp"
#include "crab/variable.hpp"
#include "crab/linear_constraint.hpp"
#include "crab/dsl_syntax.hpp"

using crab::data_kind_t;
using crab::variable_t;

using std::regex;
using std::string;
using std::map;

#define REG R"_(\s*(r\d\d?)\s*)_"
#define KIND R"_(\s*(type|value|ctx_offset|map_fd|packet_offset|shared_offset|stack_offset|shared_region_size|stack_numeric_size)\s*)_"
#define IMM R"_(\s*\[?([-+]?\d+)\]?\s*)_"
#define INTERVAL R"_(\s*\[([-+]?\d+),\s*([-+]?\d+)\]?\s*)_"
#define ARRAY_RANGE R"_(\s*\[([-+]?\d+)\.\.\.\s*([-+]?\d+)\]?\s*)_"

#define DOT "[.]"
#define TYPE R"_(\s*(shared|number|packet|stack|ctx|map_fd|map_fd_program)\s*)_"

static uint8_t regnum(const string& s) {
    return (uint8_t)boost::lexical_cast<uint16_t>(s.substr(1));
}

static crab::data_kind_t regkind(const string& s) {
    if (s == "type") return crab::data_kind_t::types;
    if (s == "ctx_offset") return crab::data_kind_t::ctx_offsets;
    if (s == "map_fd") return crab::data_kind_t::map_fds;
    if (s == "packet_offset") return crab::data_kind_t::packet_offsets;
    if (s == "shared_offset") return crab::data_kind_t::shared_offsets;
    if (s == "stack_offset") return crab::data_kind_t::stack_offsets;
    if (s == "shared_region_size") return crab::data_kind_t::shared_region_sizes;
    if (s == "stack_numeric_size") return crab::data_kind_t::stack_numeric_sizes;
    if (s == "value") return crab::data_kind_t::values;
    throw std::runtime_error(string() + "Bad kind: " + s);
}

static long number(const string& s) {
    try {
        return (long)boost::lexical_cast<int64_t>(s);
    } catch (const boost::bad_lexical_cast&) {
        throw std::invalid_argument("number too large");
    }
}

static type_encoding_t string_to_type_encoding(const string& s) {
    static map<string, type_encoding_t> string_to_type{
        {string("uninit"), T_UNINIT},
        {string("map_fd_programs"), T_MAP_PROGRAMS},
        {string("map_fd"), T_MAP},
        {string("number"), T_NUM},
        {string("ctx"), T_CTX},
        {string("stack"), T_STACK},
        {string("packet"), T_PACKET},
        {string("shared"), T_SHARED},
    };
    if (string_to_type.count(s)) {
        return string_to_type[s];
    }
    throw std::runtime_error(string("Unsupported type name: ") + s);
}

std::vector<linear_constraint_t> parse_linear_constraints(const std::set<string>& constraints, std::vector<crab::interval_t>& numeric_ranges) {
    using namespace crab::dsl_syntax;

    std::vector<linear_constraint_t> res;
    for (const string& cst_text : constraints) {
        std::smatch m;
        if (regex_match(cst_text, m, regex("meta_offset" "=" IMM))) {
            res.push_back(variable_t::meta_offset() == number(m[1]));
        } else if (regex_match(cst_text, m, regex("packet_size" "=" INTERVAL))) {
            variable_t d = variable_t::packet_size();
            res.push_back(number(m[1]) <= d);
            res.push_back(d <= number(m[2]));
        } else if (regex_match(cst_text, m, regex("packet_size" "=" REG DOT KIND))) {
            variable_t d = variable_t::packet_size();
            variable_t s = variable_t::reg(regkind(m[2]), regnum(m[1]));
            res.push_back(equals(d, s));
        } else if (regex_match(cst_text, m, regex(REG DOT KIND "=" "packet_size"))) {
            variable_t d = variable_t::reg(regkind(m[2]), regnum(m[1]));
            res.push_back(equals(d, variable_t::packet_size()));
        } else if (regex_match(cst_text, m, regex(REG DOT KIND "=" REG DOT KIND))) {
            variable_t d = variable_t::reg(regkind(m[2]), regnum(m[1]));
            variable_t s = variable_t::reg(regkind(m[4]), regnum(m[3]));
            res.push_back(equals(d, s));
        } else if (regex_match(cst_text, m, regex(REG DOT "type" "=" TYPE))) {
            variable_t d = variable_t::reg(data_kind_t::types, regnum(m[1]));
            res.push_back(d == string_to_type_encoding(m[2]));
        } else if (regex_match(cst_text, m, regex(REG DOT KIND "=" IMM))) {
            variable_t d = variable_t::reg(regkind(m[2]), regnum(m[1]));
            res.push_back(d == number(m[3]));
        } else if (regex_match(cst_text, m, regex(REG DOT KIND "=" INTERVAL))) {
            variable_t d = variable_t::reg(regkind(m[2]), regnum(m[1]));
            res.push_back(number(m[3]) <= d);
            res.push_back(d <= number(m[4]));
        } else if (regex_match(cst_text, m, regex(REG DOT KIND "-" REG DOT KIND "<=" IMM))) {
            variable_t d = variable_t::reg(regkind(m[2]), regnum(m[1]));
            variable_t s = variable_t::reg(regkind(m[4]), regnum(m[3]));
            long diff = number(m[5]);
            res.push_back(d - s <= number_t(diff));
        } else if (regex_match(cst_text, m, regex("s" ARRAY_RANGE DOT "type" "=" TYPE))) {
            type_encoding_t type = string_to_type_encoding(m[3]);
            if (type == type_encoding_t::T_NUM) {
                numeric_ranges.push_back(crab::interval_t(number(m[1]), number(m[2])));
            } else {
                long lb = number(m[1]);
                long ub = number(m[2]);
                variable_t d = variable_t::cell_var(data_kind_t::types, lb, ub - lb + 1);
                res.push_back(d == type);
            }
        } else {
            throw std::runtime_error(string("Unknown constraint: ") + cst_text);
        }
    }
    return res;
}

// return a-b, taking account potential optional-none
string_invariant string_invariant::operator-(const string_invariant& b) const {
    if (this->is_bottom()) return string_invariant::bottom();
    string_invariant res = string_invariant::top();
    for (const std::string& cst : this->value()) {
        if (b.is_bottom() || !b.contains(cst))
            res.maybe_inv->insert(cst);
    }
    return res;
}

// return a+b, taking account potential optional-none
string_invariant string_invariant::operator+(const string_invariant& b) const {
    if (this->is_bottom())
        return b;
    string_invariant res = *this;
    for (const std::string& cst : b.value()) {
        if (res.is_bottom() || !res.contains(cst))
            res.maybe_inv->insert(cst);
    }
    return res;
}

std::ostream& operator<<(std::ostream& o, const string_invariant& inv) {
    if (inv.is_bottom()) {
        return o << "_|_";
    }
    // Intervals
    bool first = true;
    o << "[";
    auto& set = inv.maybe_inv.value();
    std::string lastbase;
    for (const auto& item : set) {
        if (first)
            first = false;
        else
            o << ", ";
        size_t pos = item.find_first_of(".=[");
        std::string base = item.substr(0, pos);
        if (base != lastbase) {
            o << "\n    ";
            lastbase = base;
        }
        o << item;
    }
    o << "]";
    return o;
}


#include <dlib/geometry/point_transforms.h>

int gauze_main(int argc, char *argv[])
{
    using fpoint = dlib::vector<float, 2>;
    using PointVecf = std::vector<fpoint>;
    const dlib::rectangle rect(0,0,128,96 );
    PointVecf to_points{ rect.tl_corner(), rect.tr_corner(), rect.br_corner() };
    PointVecf from_points{ { 0, 0 }, { 1, 0 }, { 1, 1 } };

    // Note that BEFORE/AFTER are buffered in gauze tests
    // via cmake's execute_process() so we won't actually
    // see IO until the process completes.  If it hangs we
    // won't see anything in the terminal.
    std::cout << "BEFORE" << std::endl;

    // Comment this line to void the hang
    dlib::find_affine_transform(from_points, to_points);

    // We won't see "AFTER" until the hang is addressed
    std::cout << "AFTER" << std::endl;

    return 0;
}