// copyright (c) 2012-2013 the moorecoin core developers
// distributed under the mit software license, see the accompanying
// file copying or http://www.opensource.org/licenses/mit-license.php.

#include "merkleblock.h"
#include "serialize.h"
#include "streams.h"
#include "uint256.h"
#include "arith_uint256.h"
#include "version.h"
#include "random.h"
#include "test/test_moorecoin.h"

#include <vector>

#include <boost/assign/list_of.hpp>
#include <boost/test/unit_test.hpp>

using namespace std;

class cpartialmerkletreetester : public cpartialmerkletree
{
public:
    // flip one bit in one of the hashes - this should break the authentication
    void damage() {
        unsigned int n = insecure_rand() % vhash.size();
        int bit = insecure_rand() % 256;
        *(vhash[n].begin() + (bit>>3)) ^= 1<<(bit&7);
    }
};

boost_fixture_test_suite(pmt_tests, basictestingsetup)

boost_auto_test_case(pmt_test1)
{
    seed_insecure_rand(false);
    static const unsigned int ntxcounts[] = {1, 4, 7, 17, 56, 100, 127, 256, 312, 513, 1000, 4095};

    for (int n = 0; n < 12; n++) {
        unsigned int ntx = ntxcounts[n];

        // build a block with some dummy transactions
        cblock block;
        for (unsigned int j=0; j<ntx; j++) {
            cmutabletransaction tx;
            tx.nlocktime = j; // actual transaction data doesn't matter; just make the nlocktime's unique
            block.vtx.push_back(ctransaction(tx));
        }

        // calculate actual merkle root and height
        uint256 merkleroot1 = block.buildmerkletree();
        std::vector<uint256> vtxid(ntx, uint256());
        for (unsigned int j=0; j<ntx; j++)
            vtxid[j] = block.vtx[j].gethash();
        int nheight = 1, ntx_ = ntx;
        while (ntx_ > 1) {
            ntx_ = (ntx_+1)/2;
            nheight++;
        }

        // check with random subsets with inclusion chances 1, 1/2, 1/4, ..., 1/128
        for (int att = 1; att < 15; att++) {
            // build random subset of txid's
            std::vector<bool> vmatch(ntx, false);
            std::vector<uint256> vmatchtxid1;
            for (unsigned int j=0; j<ntx; j++) {
                bool finclude = (insecure_rand() & ((1 << (att/2)) - 1)) == 0;
                vmatch[j] = finclude;
                if (finclude)
                    vmatchtxid1.push_back(vtxid[j]);
            }

            // build the partial merkle tree
            cpartialmerkletree pmt1(vtxid, vmatch);

            // serialize
            cdatastream ss(ser_network, protocol_version);
            ss << pmt1;

            // verify cpartialmerkletree's size guarantees
            unsigned int n = std::min<unsigned int>(ntx, 1 + vmatchtxid1.size()*nheight);
            boost_check(ss.size() <= 10 + (258*n+7)/8);

            // deserialize into a tester copy
            cpartialmerkletreetester pmt2;
            ss >> pmt2;

            // extract merkle root and matched txids from copy
            std::vector<uint256> vmatchtxid2;
            uint256 merkleroot2 = pmt2.extractmatches(vmatchtxid2);

            // check that it has the same merkle root as the original, and a valid one
            boost_check(merkleroot1 == merkleroot2);
            boost_check(!merkleroot2.isnull());

            // check that it contains the matched transactions (in the same order!)
            boost_check(vmatchtxid1 == vmatchtxid2);

            // check that random bit flips break the authentication
            for (int j=0; j<4; j++) {
                cpartialmerkletreetester pmt3(pmt2);
                pmt3.damage();
                std::vector<uint256> vmatchtxid3;
                uint256 merkleroot3 = pmt3.extractmatches(vmatchtxid3);
                boost_check(merkleroot3 != merkleroot1);
            }
        }
    }
}

boost_auto_test_case(pmt_malleability)
{
    std::vector<uint256> vtxid = boost::assign::list_of
        (arithtouint256(1))(arithtouint256(2))
        (arithtouint256(3))(arithtouint256(4))
        (arithtouint256(5))(arithtouint256(6))
        (arithtouint256(7))(arithtouint256(8))
        (arithtouint256(9))(arithtouint256(10))
        (arithtouint256(9))(arithtouint256(10));
    std::vector<bool> vmatch = boost::assign::list_of(false)(false)(false)(false)(false)(false)(false)(false)(false)(true)(true)(false);

    cpartialmerkletree tree(vtxid, vmatch);
    std::vector<uint256> vtxid2;
    boost_check(tree.extractmatches(vtxid).isnull());
}

boost_auto_test_suite_end()


//==================================================================================================
/*!

  Copyright 2016 NumScale SAS

  Distributed under the Boost Software License, Version 1.0.
  (See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt)
*/
//==================================================================================================
#include <boost/simd/constant/logeps.hpp>
#include <boost/simd/as.hpp>
#include <scalar_test.hpp>

STF_CASE_TPL( "Check logeps behavior for integral types"
            , (std::uint8_t)(std::uint16_t)(std::uint32_t)(std::uint64_t)
              (std::int8_t)(std::int16_t)(std::int32_t)(std::int64_t)
            )
{
  using boost::simd::as;
  using boost::simd::detail::logeps;
  using boost::simd::Logeps;

  STF_TYPE_IS(decltype(Logeps<T>()), T);
  STF_EQUAL(Logeps<T>(), T(0));
  STF_EQUAL(logeps( as(T{}) ),T(0));
}

STF_CASE_TPL( "Check logeps behavior for double type"
            , (double)
            )
{
  using boost::simd::as;
  using boost::simd::detail::logeps;
  using boost::simd::Logeps;

  STF_TYPE_IS(decltype(Logeps<T>()), T);
  STF_IEEE_EQUAL(Logeps<T>(), -36.043653389117156089696070315825);
  STF_IEEE_EQUAL(logeps( as(T{}) ), -36.043653389117156089696070315825);
}

STF_CASE_TPL( "Check logeps behavior for float type"
            , (float)
            )
{
  using boost::simd::as;
  using boost::simd::detail::logeps;
  using boost::simd::Logeps;

  STF_TYPE_IS(decltype(Logeps<T>()), T);
  STF_IEEE_EQUAL(Logeps<T>(), -15.9423847f);
  STF_IEEE_EQUAL(logeps( as(T{}) ), -15.9423847f);
}


//==============================================================================
//         Copyright 2003 - 2011 LASMEA UMR 6602 CNRS/Univ. Clermont II
//         Copyright 2009 - 2011 LRI    UMR 8623 CNRS/Univ Paris Sud XI
//         Copyright 2012 - 2014 MetaScale SAS
//
//          Distributed under the Boost Software License, Version 1.0.
//                 See accompanying file LICENSE.txt or copy at
//                     http://www.boost.org/LICENSE_1_0.txt
//==============================================================================
#ifndef NT2_BOOLEAN_FUNCTIONS_COMPLEX_GENERIC_IF_ZERO_ELSE_HPP_INCLUDED
#define NT2_BOOLEAN_FUNCTIONS_COMPLEX_GENERIC_IF_ZERO_ELSE_HPP_INCLUDED

#include <nt2/boolean/functions/if_zero_else.hpp>
#include <nt2/include/functions/real.hpp>
#include <nt2/include/functions/imag.hpp>
#include <nt2/include/functions/is_nez.hpp>
#include <boost/dispatch/attributes.hpp>

namespace boost { namespace simd { namespace ext
{
  BOOST_DISPATCH_IMPLEMENT  ( if_zero_else_, tag::cpu_, (A0)(A1)
                            , (generic_< fundamental_<A0> >)
                              (generic_< complex_<floating_<A1> > >)
                            )
  {
    typedef A1 result_type;
    result_type  operator()(A0 const& a0, A1 const& a1) const
    {
      return result_type(if_zero_else(a0, nt2::real(a1)), if_zero_else(a0, nt2::imag(a1)));
    }
  };
  BOOST_DISPATCH_IMPLEMENT  ( if_zero_else_, tag::cpu_, (A0)(A1)
                            , (generic_< complex_<floating_<A0> > >)
                              (generic_< unspecified_<A1> >)
                            )
  {
    typedef A1 result_type;
    result_type  operator()(A0 const& a0, A1 const& a1) const
    {
      return if_zero_else(is_nez(a0), a1);
    }
  };
} } }

#endif


<<<<<<< HEAD
/*    Copyright (c) 2010-2018, Delft University of Technology
=======
/*    Copyright (c) 2010-2019, Delft University of Technology
>>>>>>> origin/master
 *    All rigths reserved
 *
 *    This file is part of the Tudat. Redistribution and use in source and
 *    binary forms, with or without modification, are permitted exclusively
 *    under the terms of the Modified BSD license. You should have received
 *    a copy of the license with this file. If not, please or visit:
 *    http://tudat.tudelft.nl/LICENSE.
 *
 */

#define BOOST_TEST_MAIN

#include <limits>
#include "Tudat/Basics/testMacros.h"
#include "Tudat/Basics/utilityMacros.h"

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

#include "Tudat/Astrodynamics/EarthOrientation/earthOrientationCalculator.h"
#include "Tudat/Astrodynamics/BasicAstrodynamics/timeConversions.h"

namespace tudat
{
namespace unit_tests
{

struct SofaTimeOutput
{
    double expectedUtcFractionalDays;
    double expectedUtcDays;
    double expectedUt1Seconds;
    double expectedUt1Days;
    double expectedTaiFractionalDays;
    double expectedTaiDays;
    double expectedTtFractionalDays;
    double expectedTtDays;
    double expectedTdbFractionalDays;
    double expectedTdbDays;
};

SofaTimeOutput getSofaDirectTimes( )
{
    SofaTimeOutput sofaTimes;

    int latnd, latnm, lonwd, lonwm, j, iy, mo, id, ih, im;
    double slatn, slonw, hm, elon, phi, xyz[3], u, v, sec,
            utc1, utc2, dut, ut11, ut12, ut, tai1, tai2, tt1, tt2,
            tcg1, tcg2, dtr, tdb1, tdb2, tcb1, tcb2;

    TUDAT_UNUSED_PARAMETER( j );

    /* Site terrestrial coordinates (WGS84). */
    latnd = 19;
    latnm = 28;
    slatn = 52.5;
    lonwd = 155;
    lonwm = 55;
    slonw = 59.6;
    hm = 0.0;

    /* Transform to geocentric. */
    j = iauAf2a ( '+', latnd, latnm, slatn, &phi );
    j = iauAf2a ( '-', lonwd, lonwm, slonw, &elon );
    j = iauGd2gc ( 1, elon, phi, hm, xyz );
    u = sqrt ( xyz[0]*xyz[0] + xyz[1]*xyz[1] );
    v = xyz[2];

    /* UTC date and time. */
    iy = 2006;
    mo = 1;
    id = 15;
    ih = 21;
    im = 24;
    sec = 37.5;

    /* Transform into internal format. */
    j = iauDtf2d ( "UTC", iy, mo, id, ih, im, sec, &utc1, &utc2 );

    /* UT1-UTC (s, from IERS). */
    dut = 0.3340960443019867; // Value modified to coincide with value in code (difference is of order microsecond, only influences utc<->ut1).

    /* UTC -> UT1. */

    j = iauUtcut1 ( utc1, utc2, dut, &ut11, &ut12 );

    /* Extract fraction for TDB-TT calculation, later. */
    ut = fmod ( fmod(ut11,1.0) + fmod(ut12,1.0), 1.0 ) + 0.5;

    /* UTC -> TAI -> TT -> TCG. */
    j = iauUtctai ( utc1, utc2, &tai1, &tai2 );
    j = iauTaitt ( tai1, tai2, &tt1, &tt2 );
    j = iauTttcg ( tt1, tt2, &tcg1, &tcg2 );

    /* TDB-TT (using TT as a substitute for TDB). */
    dtr = iauDtdb ( tt1, tt2, ut, elon, u / 1000.0, v / 1000.0 );

    /* TT -> TDB -> TCB. */
    j = iauTttdb ( tt1, tt2, dtr, &tdb1, &tdb2 );
    j = iauTdbtcb ( tdb1, tdb2, &tcb1, &tcb2 );

    /* Report. */
    sofaTimes.expectedUtcDays = utc1;
    sofaTimes.expectedUtcFractionalDays = utc2;

    sofaTimes.expectedUt1Days = ut11;
    sofaTimes.expectedUt1Seconds = ut12;

    sofaTimes.expectedTaiDays = tai1;
    sofaTimes.expectedTaiFractionalDays = tai2;

    sofaTimes.expectedTtDays = tt1;
    sofaTimes.expectedTtFractionalDays = tt2;

    sofaTimes.expectedTdbDays = tdb1;
    sofaTimes.expectedTdbFractionalDays = tdb2;
    return sofaTimes;
}

double convertSofaOutputToSecondsSinceJ2000(
        const double fullJulianDays, const double fractionalJulianDays )
{
    return ( fullJulianDays - basic_astrodynamics::JULIAN_DAY_ON_J2000 ) *
            physical_constants::JULIAN_DAY + fractionalJulianDays * physical_constants::JULIAN_DAY ;
}

using namespace tudat::earth_orientation;
using namespace tudat::basic_astrodynamics;

BOOST_AUTO_TEST_SUITE( test_time_scale_converters )

//! Compare time conversions to example from SOFA Cookbook. Only change w.r.t. cookbook is UT1-UTC, which is set to correspond to
//! value used here
BOOST_AUTO_TEST_CASE( testDifferentTimeScaleConversions )
{
    // Get times from SOFA cookbook
    SofaTimeOutput sofaTimes = getSofaDirectTimes( );

    // Create default time converter.
    std::shared_ptr< TerrestrialTimeScaleConverter > timeScaleConverter =
            createStandardEarthOrientationCalculator( )->getTerrestrialTimeScaleConverter( );

    // Set station position
    Eigen::Vector3d stationCartesianPosition;
    stationCartesianPosition << -5492333.306498738, -2453018.508911721, 2113645.653406073;

    // Retrieve SOFA times in double and Time precision.
    std::map< TimeScales, double > sofaSecondsSinceJ2000;
    sofaSecondsSinceJ2000[ tt_scale ] = convertSofaOutputToSecondsSinceJ2000(
                sofaTimes.expectedTtDays, sofaTimes.expectedTtFractionalDays );
    sofaSecondsSinceJ2000[ utc_scale ] = convertSofaOutputToSecondsSinceJ2000(
                sofaTimes.expectedUtcDays, sofaTimes.expectedUtcFractionalDays );
    sofaSecondsSinceJ2000[ ut1_scale ] = convertSofaOutputToSecondsSinceJ2000(
                sofaTimes.expectedUt1Days, sofaTimes.expectedUt1Seconds );
    sofaSecondsSinceJ2000[ tai_scale ] = convertSofaOutputToSecondsSinceJ2000(
                sofaTimes.expectedTaiDays, sofaTimes.expectedTaiFractionalDays );
    sofaSecondsSinceJ2000[ tdb_scale ] = convertSofaOutputToSecondsSinceJ2000(
                sofaTimes.expectedTdbDays, sofaTimes.expectedTdbFractionalDays );

    // Define list of time scales
    std::vector< TimeScales > originScales;
    originScales.push_back( tt_scale );
    originScales.push_back( utc_scale );
    originScales.push_back( ut1_scale );
    originScales.push_back( tai_scale );
    originScales.push_back( tdb_scale );

    // Test conversion between time scales, single update function at each iteration
    for( unsigned int i = 0; i < originScales.size( ); i++ )
    {
        timeScaleConverter->resetTimes< double >( );
        timeScaleConverter->updateTimes( originScales.at( i ), sofaSecondsSinceJ2000[ originScales.at( i ) ], stationCartesianPosition );

        double ut1 = timeScaleConverter->getCurrentTime(
                    originScales.at( i ), ut1_scale, sofaSecondsSinceJ2000[ originScales.at( i ) ], stationCartesianPosition );
        BOOST_CHECK_SMALL( ut1 - sofaSecondsSinceJ2000[ ut1_scale ], std::numeric_limits< double >::epsilon( ) );

        double utc = timeScaleConverter->getCurrentTime(
                    originScales.at( i ), utc_scale, sofaSecondsSinceJ2000[ originScales.at( i ) ], stationCartesianPosition );
        BOOST_CHECK_SMALL( utc - sofaSecondsSinceJ2000[ utc_scale ], std::numeric_limits< double >::epsilon( ) );

        double tdb = timeScaleConverter->getCurrentTime(
                    originScales.at( i ), tdb_scale, sofaSecondsSinceJ2000[ originScales.at( i ) ], stationCartesianPosition );
        BOOST_CHECK_SMALL( tdb - sofaSecondsSinceJ2000[ tdb_scale ], std::numeric_limits< double >::epsilon( ) );

        double tai = timeScaleConverter->getCurrentTime(
                    originScales.at( i ), tai_scale, sofaSecondsSinceJ2000[ originScales.at( i ) ], stationCartesianPosition );
        BOOST_CHECK_SMALL( tai -sofaSecondsSinceJ2000[ tai_scale ], std::numeric_limits< double >::epsilon( ) );

        double tt = timeScaleConverter->getCurrentTime(
                    originScales.at( i ), tt_scale, sofaSecondsSinceJ2000[ originScales.at( i ) ], stationCartesianPosition );
        BOOST_CHECK_SMALL( tt -sofaSecondsSinceJ2000[ tt_scale ], std::numeric_limits< double >::epsilon( ) );

    }

    // Test conversion between time scales, without caching of values (reset function called after each iteration).
    for( unsigned int i = 0; i < originScales.size( ); i++ )
    {
        timeScaleConverter->resetTimes< double >( );
        timeScaleConverter->updateTimes(
                    originScales.at( i ), sofaSecondsSinceJ2000[ originScales.at( i ) ], stationCartesianPosition );

        double ut1 = timeScaleConverter->getCurrentTime(
                    originScales.at( i ), ut1_scale, sofaSecondsSinceJ2000[ originScales.at( i ) ], stationCartesianPosition );
        BOOST_CHECK_SMALL( ut1 - sofaSecondsSinceJ2000[ ut1_scale ], std::numeric_limits< double >::epsilon( ) );

        timeScaleConverter->resetTimes< double >( );
        double utc = timeScaleConverter->getCurrentTime(
                    originScales.at( i ), utc_scale, sofaSecondsSinceJ2000[ originScales.at( i ) ], stationCartesianPosition );
        BOOST_CHECK_SMALL( utc - sofaSecondsSinceJ2000[ utc_scale ], std::numeric_limits< double >::epsilon( ) );

        timeScaleConverter->resetTimes< double >( );
        double tdb = timeScaleConverter->getCurrentTime(
                    originScales.at( i ), tdb_scale, sofaSecondsSinceJ2000[ originScales.at( i ) ], stationCartesianPosition );
        BOOST_CHECK_SMALL( tdb - sofaSecondsSinceJ2000[ tdb_scale ], std::numeric_limits< double >::epsilon( ) );

        timeScaleConverter->resetTimes< double >( );
        double tai = timeScaleConverter->getCurrentTime(
                    originScales.at( i ), tai_scale, sofaSecondsSinceJ2000[ originScales.at( i ) ], stationCartesianPosition );
        BOOST_CHECK_SMALL( tai -sofaSecondsSinceJ2000[ tai_scale ], std::numeric_limits< double >::epsilon( ) );

        timeScaleConverter->resetTimes< double >( );
        double tt = timeScaleConverter->getCurrentTime(
                    originScales.at( i ), tt_scale, sofaSecondsSinceJ2000[ originScales.at( i ) ], stationCartesianPosition );
        BOOST_CHECK_SMALL( tt -sofaSecondsSinceJ2000[ tt_scale ], std::numeric_limits< double >::epsilon( ) );

    }

    // Test conversion between time scales, single update function at each iteration, with Time input.
    for( unsigned int i = 0; i < originScales.size( ); i++ )
    {
        timeScaleConverter->resetTimes< Time >( );
        timeScaleConverter->updateTimes< Time >( originScales.at( i ), sofaSecondsSinceJ2000[ originScales.at( i ) ], stationCartesianPosition );

        double ut1 = timeScaleConverter->getCurrentTime< Time >( originScales.at( i ), ut1_scale, sofaSecondsSinceJ2000[ originScales.at( i ) ], stationCartesianPosition );
        BOOST_CHECK_SMALL( ut1 - sofaSecondsSinceJ2000[ ut1_scale ], std::numeric_limits< double >::epsilon( ) );

        double utc = timeScaleConverter->getCurrentTime< Time >( originScales.at( i ), utc_scale, sofaSecondsSinceJ2000[ originScales.at( i ) ], stationCartesianPosition );
        BOOST_CHECK_SMALL( utc - sofaSecondsSinceJ2000[ utc_scale ], std::numeric_limits< double >::epsilon( ) );

        double tdb = timeScaleConverter->getCurrentTime< Time >( originScales.at( i ), tdb_scale, sofaSecondsSinceJ2000[ originScales.at( i ) ], stationCartesianPosition );
        BOOST_CHECK_SMALL( tdb - sofaSecondsSinceJ2000[ tdb_scale ], std::numeric_limits< double >::epsilon( ) );

        double tai = timeScaleConverter->getCurrentTime< Time >( originScales.at( i ), tai_scale, sofaSecondsSinceJ2000[ originScales.at( i ) ], stationCartesianPosition );
        BOOST_CHECK_SMALL( tai -sofaSecondsSinceJ2000[ tai_scale ], std::numeric_limits< double >::epsilon( ) );

        double tt = timeScaleConverter->getCurrentTime< Time >( originScales.at( i ), tt_scale, sofaSecondsSinceJ2000[ originScales.at( i ) ], stationCartesianPosition );
        BOOST_CHECK_SMALL( tt -sofaSecondsSinceJ2000[ tt_scale ], std::numeric_limits< double >::epsilon( ) );

    }
}

//! Test if converting back and forth is done correctly
BOOST_AUTO_TEST_CASE( testTimeScaleConversionPrecisionWithTimeType )
{
    // Create time scale converters
    std::shared_ptr< TerrestrialTimeScaleConverter > timeScaleConverter =
            createStandardEarthOrientationCalculator( )->getTerrestrialTimeScaleConverter( );
    std::shared_ptr< TerrestrialTimeScaleConverter > comparisonTimeScaleConverter =
            createStandardEarthOrientationCalculator( )->getTerrestrialTimeScaleConverter( );

    // Define time scales
    std::vector< TimeScales > originScales;
    originScales.push_back( tt_scale );
    originScales.push_back( utc_scale );
    originScales.push_back( ut1_scale );
    originScales.push_back( tai_scale );
    originScales.push_back( tdb_scale );

    // Define maps of times
    std::map< TimeScales, Time > currentTimes;
    std::map< TimeScales, Time > comparisonCurrentTimes;

    // Define current time/position
    Time baseTime = Time( -20.0 * physical_constants::JULIAN_YEAR );
    Eigen::Vector3d stationCartesianPosition;
    stationCartesianPosition << -5492333.306498738, -2453018.508911721, 2113645.653406073;

    for( unsigned int i = 0; i < originScales.size( ); i++ )
    {
        currentTimes.clear( );

        // Convert time in scale( i ) to all other times.
        timeScaleConverter->resetTimes< Time >( );
        timeScaleConverter->updateTimes< Time >( originScales.at( i ), baseTime, stationCartesianPosition );
        for( unsigned int j = 0; j < originScales.size( ); j++ )
        {
            currentTimes[ originScales.at( j ) ] =
                    timeScaleConverter->getCurrentTime< Time >(
                        originScales.at( i ), originScales.at( j ), baseTime, stationCartesianPosition );
        }

        // Convert back and compare results. Tolerance is set at only ps level, since TDB-TT, and UT1-UTC computations, are still
        // done at only double precision.
        for( unsigned int j = 0; j < originScales.size( ); j++ )
        {
            Time currentBackConvertedTime =
                    comparisonTimeScaleConverter->getCurrentTime< Time >(
                        originScales.at( j ), originScales.at( i ), currentTimes[ originScales.at( j ) ],
                    stationCartesianPosition );
            BOOST_CHECK_SMALL( std::fabs( static_cast< long double >( currentBackConvertedTime - baseTime ) ), 1.0E-12L );
        }
    }
}

//! Test validity of time scale converter around leap seconds
BOOST_AUTO_TEST_CASE( testTimeScaleConversionDuringLeapSeconds )
{
    std::shared_ptr< TerrestrialTimeScaleConverter > timeScaleConverter =
            createStandardEarthOrientationCalculator( )->getTerrestrialTimeScaleConverter( );

    // Define leap second list (day, month, year)
    Eigen::Matrix< int, Eigen::Dynamic, 3 > leapSecondDays;
    leapSecondDays.resize( 27, 3 );
    leapSecondDays << 1, 7, 1972,
            1, 1, 1973,
            1, 1, 1974,
            1, 1, 1975,
            1, 1, 1976,
            1, 1, 1977,
            1, 1, 1978,
            1, 1, 1979,
            1, 1, 1980,
            1, 7, 1981,
            1, 7, 1982,
            1, 7, 1983,
            1, 7, 1985,
            1, 1, 1988,
            1, 1, 1990,
            1, 1, 1991,
            1, 7, 1992,
            1, 7, 1993,
            1, 7, 1994,
            1, 1, 1996,
            1, 7, 1997,
            1, 1, 1999,
            1, 1, 2006,
            1, 1, 2009,
            1, 7, 2012,
            1, 7, 2015,
            1, 1, 2017;

    // Convert UTC to TAI, and back, in microsecond before and after leap second, and check results in double precision.
    for( int i = 0; i < leapSecondDays.rows( ); i++ )
    {
        double utcTimeOfLeapSeconds = basic_astrodynamics::convertCalendarDateToJulianDaysSinceEpoch(
                    leapSecondDays( i, 2 ), leapSecondDays( i, 1 ), leapSecondDays( i, 0 ), 0, 0, 0.0,
                    basic_astrodynamics::JULIAN_DAY_ON_J2000 );

        double taiPreLeap = timeScaleConverter->getCurrentTime< double >(
                    utc_scale, tai_scale, utcTimeOfLeapSeconds * physical_constants::JULIAN_DAY - 1.0E-6,
                    Eigen::Vector3d::Zero( ) );
        double taiPostLeap = timeScaleConverter->getCurrentTime< double >(
                    utc_scale, tai_scale, utcTimeOfLeapSeconds * physical_constants::JULIAN_DAY + 1.0E-6,
                    Eigen::Vector3d::Zero( ) );
        BOOST_CHECK_SMALL(  std::fabs( taiPostLeap - taiPreLeap - ( 1.0 + 2.0E-6 ) ), 1.0E-7 );

        timeScaleConverter->resetTimes< double >( );

        double utcPreLeap = timeScaleConverter->getCurrentTime< double >(
                    tai_scale, utc_scale, taiPreLeap, Eigen::Vector3d::Zero( ) );
        double utcPostLeap = timeScaleConverter->getCurrentTime< double >(
                    tai_scale, utc_scale, taiPostLeap, Eigen::Vector3d::Zero( ) );

        BOOST_CHECK_SMALL( std::fabs( utcPostLeap - utcPreLeap - ( 2.0E-6 ) ), 1.0E-7 );
    }

    // Convert UTC to TAI, and back, in microsecond before and after leap second, and check results in Time precision.
    for( int i = 0; i < leapSecondDays.rows( ); i++ )
    {
        Time utcTimeOfLeapSeconds = basic_astrodynamics::convertCalendarDateToJulianDaysSinceEpoch(
                    leapSecondDays( i, 2 ), leapSecondDays( i, 1 ), leapSecondDays( i, 0 ), 0, 0, 0.0,
                    basic_astrodynamics::JULIAN_DAY_ON_J2000 );

        Time utcInputPreLeap = utcTimeOfLeapSeconds * physical_constants::JULIAN_DAY - 1.0E-6;
        Time utcInputPostLeap = utcTimeOfLeapSeconds * physical_constants::JULIAN_DAY + 1.0E-6;

        Time taiPreLeap = timeScaleConverter->getCurrentTime< Time >(
                    utc_scale, tai_scale, utcInputPreLeap, Eigen::Vector3d::Zero( ) );
        Time taiPostLeap = timeScaleConverter->getCurrentTime< Time >(
                    utc_scale, tai_scale, utcInputPostLeap, Eigen::Vector3d::Zero( ) );

        long double timeDifferenceTai = static_cast< long double >( taiPostLeap - taiPreLeap ) - ( 1.0L + 2.0E-6 );

        BOOST_CHECK_SMALL( std::fabs( timeDifferenceTai ),
                            3600.0L * std::numeric_limits< long double >::epsilon( ) );

        timeScaleConverter->resetTimes< Time >( );

        Time utcPreLeap = timeScaleConverter->getCurrentTime< Time >(
                    tai_scale, utc_scale, taiPreLeap, Eigen::Vector3d::Zero( ) );
        Time utcPostLeap = timeScaleConverter->getCurrentTime< Time >(
                    tai_scale, utc_scale, taiPostLeap, Eigen::Vector3d::Zero( ) );

        long double timeDifferenceUtc = static_cast< long double >( utcPostLeap - utcPreLeap ) - ( 2.0E-6 );
        BOOST_CHECK_SMALL( std::fabs( timeDifferenceUtc ),
                           3600.0L * std::numeric_limits< long double >::epsilon( ) );
    }
}

BOOST_AUTO_TEST_SUITE_END( )

} // namespace unit_tests

} // namespace tudat


/*    This file is part of the Gudhi Library - https://gudhi.inria.fr/ - which is released under MIT.
 *    See file LICENSE or go to https://gudhi.inria.fr/licensing/ for full license details.
 *    Author(s):       Vincent Rouvreau
 *
 *    Copyright (C) 2017 Inria
 *
 *    Modification(s):
 *      - YYYY/MM Author: Description of the modification
 */

#include <gudhi/reader_utils.h>

#include <iostream>
#include <vector>
#include <utility>  // for pair
#include <tuple>
#include <limits>  // for inf
#include <map>

#define BOOST_TEST_DYN_LINK
#define BOOST_TEST_MODULE "persistence_intervals_reader"
#include <boost/test/unit_test.hpp>

using Persistence_intervals_by_dimension = std::map<int, std::vector<std::pair<double, double>>>;
using Persistence_intervals = std::vector<std::pair<double, double>>;
// Test files with only 2 parameters (persistence birth and death) per line in file
BOOST_AUTO_TEST_CASE( persistence_intervals_without_dimension )
{
  Persistence_intervals_by_dimension expected_intervals_by_dimension;
  expected_intervals_by_dimension[-1].push_back(std::make_pair(2.7, 3.7));
  expected_intervals_by_dimension[-1].push_back(std::make_pair(9.6, 14.));
  expected_intervals_by_dimension[-1].push_back(std::make_pair(34.2, 34.974));
  expected_intervals_by_dimension[-1].push_back(std::make_pair(3., std::numeric_limits<double>::infinity()));

  Persistence_intervals_by_dimension persistence_intervals_by_dimension =
      Gudhi::read_persistence_intervals_grouped_by_dimension("persistence_intervals_without_dimension.pers");

  std::clog << "\nread_persistence_intervals_grouped_by_dimension - expected\n";
  for (auto map_iter : expected_intervals_by_dimension) {
    std::clog << "key=" << map_iter.first;
    for (auto vec_iter : map_iter.second)
      std::clog << " [" << vec_iter.first << " ," << vec_iter.second << "] ";
  }

  std::clog << "\nread_persistence_intervals_grouped_by_dimension - read\n";
  for (auto map_iter : persistence_intervals_by_dimension) {
    std::clog << "key=" << map_iter.first;
    for (auto vec_iter : map_iter.second)
      std::clog << " [" << vec_iter.first << " ," << vec_iter.second << "] ";
  }

  BOOST_CHECK(persistence_intervals_by_dimension == expected_intervals_by_dimension);

  Persistence_intervals expected_intervals_in_dimension;
  expected_intervals_in_dimension.push_back(std::make_pair(2.7, 3.7));
  expected_intervals_in_dimension.push_back(std::make_pair(9.6, 14.));
  expected_intervals_in_dimension.push_back(std::make_pair(34.2, 34.974));
  expected_intervals_in_dimension.push_back(std::make_pair(3., std::numeric_limits<double>::infinity()));

  Persistence_intervals persistence_intervals_in_dimension =
      Gudhi::read_persistence_intervals_in_dimension("persistence_intervals_without_dimension.pers");

  std::clog << "\nread_persistence_intervals_in_dimension - expected\n";
  for (auto vec_iter : expected_intervals_in_dimension)
    std::clog << " [" << vec_iter.first << " ," << vec_iter.second << "] ";

  std::clog << "\nread_persistence_intervals_in_dimension - read\n";
  for (auto vec_iter : expected_intervals_in_dimension)
    std::clog << " [" << vec_iter.first << " ," << vec_iter.second << "] ";

  BOOST_CHECK(persistence_intervals_in_dimension == expected_intervals_in_dimension);

  expected_intervals_in_dimension.clear();
  persistence_intervals_in_dimension =
        Gudhi::read_persistence_intervals_in_dimension("persistence_intervals_without_dimension.pers", 0);
  BOOST_CHECK(persistence_intervals_in_dimension == expected_intervals_in_dimension);

  expected_intervals_in_dimension.clear();
  persistence_intervals_in_dimension =
        Gudhi::read_persistence_intervals_in_dimension("persistence_intervals_without_dimension.pers", 1);
  BOOST_CHECK(persistence_intervals_in_dimension == expected_intervals_in_dimension);

  expected_intervals_in_dimension.clear();
  persistence_intervals_in_dimension =
        Gudhi::read_persistence_intervals_in_dimension("persistence_intervals_without_dimension.pers", 2);
  BOOST_CHECK(persistence_intervals_in_dimension == expected_intervals_in_dimension);

  expected_intervals_in_dimension.clear();
  persistence_intervals_in_dimension =
        Gudhi::read_persistence_intervals_in_dimension("persistence_intervals_without_dimension.pers", 3);
  BOOST_CHECK(persistence_intervals_in_dimension == expected_intervals_in_dimension);

}
// Test files with 3 parameters (dimension birth death) per line in file
BOOST_AUTO_TEST_CASE( persistence_intervals_with_dimension )
{
  Persistence_intervals_by_dimension expected_intervals_by_dimension;
  expected_intervals_by_dimension[0].push_back(std::make_pair(2.7, 3.7));
  expected_intervals_by_dimension[1].push_back(std::make_pair(9.6, 14.));
  expected_intervals_by_dimension[3].push_back(std::make_pair(34.2, 34.974));
  expected_intervals_by_dimension[1].push_back(std::make_pair(3., std::numeric_limits<double>::infinity()));

  Persistence_intervals_by_dimension persistence_intervals_by_dimension =
      Gudhi::read_persistence_intervals_grouped_by_dimension("persistence_intervals_with_dimension.pers");

  std::clog << "\nread_persistence_intervals_grouped_by_dimension - expected\n";
  for (auto map_iter : expected_intervals_by_dimension) {
    std::clog << "key=" << map_iter.first;
    for (auto vec_iter : map_iter.second)
      std::clog << " [" << vec_iter.first << " ," << vec_iter.second << "] ";
  }

  std::clog << "\nread_persistence_intervals_grouped_by_dimension - read\n";
  for (auto map_iter : persistence_intervals_by_dimension) {
    std::clog << "key=" << map_iter.first;
    for (auto vec_iter : map_iter.second)
      std::clog << " [" << vec_iter.first << " ," << vec_iter.second << "] ";
  }

  BOOST_CHECK(persistence_intervals_by_dimension == expected_intervals_by_dimension);

  Persistence_intervals expected_intervals_in_dimension;
  expected_intervals_in_dimension.push_back(std::make_pair(2.7, 3.7));
  expected_intervals_in_dimension.push_back(std::make_pair(9.6, 14.));
  expected_intervals_in_dimension.push_back(std::make_pair(34.2, 34.974));
  expected_intervals_in_dimension.push_back(std::make_pair(3., std::numeric_limits<double>::infinity()));

  Persistence_intervals persistence_intervals_in_dimension =
      Gudhi::read_persistence_intervals_in_dimension("persistence_intervals_with_dimension.pers");

  std::clog << "\nread_persistence_intervals_in_dimension - expected\n";
  for (auto vec_iter : expected_intervals_in_dimension)
    std::clog << " [" << vec_iter.first << " ," << vec_iter.second << "] ";

  std::clog << "\nread_persistence_intervals_in_dimension - read\n";
  for (auto vec_iter : persistence_intervals_in_dimension)
    std::clog << " [" << vec_iter.first << " ," << vec_iter.second << "] ";

  BOOST_CHECK(persistence_intervals_in_dimension == expected_intervals_in_dimension);

  expected_intervals_in_dimension.clear();
  expected_intervals_in_dimension.push_back(std::make_pair(2.7, 3.7));
  persistence_intervals_in_dimension =
        Gudhi::read_persistence_intervals_in_dimension("persistence_intervals_with_dimension.pers", 0);

  std::clog << "\nread_persistence_intervals_in_dimension 0 - expected\n";
  for (auto vec_iter : expected_intervals_in_dimension)
    std::clog << " [" << vec_iter.first << " ," << vec_iter.second << "] ";

  std::clog << "\nread_persistence_intervals_in_dimension 0 - read\n";
  for (auto vec_iter : persistence_intervals_in_dimension)
    std::clog << " [" << vec_iter.first << " ," << vec_iter.second << "] ";

  BOOST_CHECK(persistence_intervals_in_dimension == expected_intervals_in_dimension);

  expected_intervals_in_dimension.clear();
  expected_intervals_in_dimension.push_back(std::make_pair(9.6, 14.));
  expected_intervals_in_dimension.push_back(std::make_pair(3., std::numeric_limits<double>::infinity()));
  persistence_intervals_in_dimension =
        Gudhi::read_persistence_intervals_in_dimension("persistence_intervals_with_dimension.pers", 1);

  std::clog << "\nread_persistence_intervals_in_dimension 1 - expected\n";
  for (auto vec_iter : expected_intervals_in_dimension)
    std::clog << " [" << vec_iter.first << " ," << vec_iter.second << "] ";

  std::clog << "\nread_persistence_intervals_in_dimension 1 - read\n";
  for (auto vec_iter : persistence_intervals_in_dimension)
    std::clog << " [" << vec_iter.first << " ," << vec_iter.second << "] ";

  BOOST_CHECK(persistence_intervals_in_dimension == expected_intervals_in_dimension);

  expected_intervals_in_dimension.clear();
  persistence_intervals_in_dimension =
        Gudhi::read_persistence_intervals_in_dimension("persistence_intervals_with_dimension.pers", 2);

  std::clog << "\nread_persistence_intervals_in_dimension 2 - expected\n";
  for (auto vec_iter : expected_intervals_in_dimension)
    std::clog << " [" << vec_iter.first << " ," << vec_iter.second << "] ";

  std::clog << "\nread_persistence_intervals_in_dimension 2 - read\n";
  for (auto vec_iter : persistence_intervals_in_dimension)
    std::clog << " [" << vec_iter.first << " ," << vec_iter.second << "] ";

  BOOST_CHECK(persistence_intervals_in_dimension == expected_intervals_in_dimension);

  expected_intervals_in_dimension.clear();
  expected_intervals_in_dimension.push_back(std::make_pair(34.2, 34.974));
  persistence_intervals_in_dimension =
        Gudhi::read_persistence_intervals_in_dimension("persistence_intervals_with_dimension.pers", 3);

  std::clog << "\nread_persistence_intervals_in_dimension 3 - expected\n";
  for (auto vec_iter : expected_intervals_in_dimension)
    std::clog << " [" << vec_iter.first << " ," << vec_iter.second << "] ";

  std::clog << "\nread_persistence_intervals_in_dimension 3 - read\n";
  for (auto vec_iter : persistence_intervals_in_dimension)
    std::clog << " [" << vec_iter.first << " ," << vec_iter.second << "] ";

  BOOST_CHECK(persistence_intervals_in_dimension == expected_intervals_in_dimension);

}

// Test files with 4 parameters (field dimension birth death) per line in file
BOOST_AUTO_TEST_CASE( persistence_intervals_with_field )
{
  Persistence_intervals_by_dimension expected_intervals_by_dimension;
  expected_intervals_by_dimension[0].push_back(std::make_pair(2.7, 3.7));
  expected_intervals_by_dimension[1].push_back(std::make_pair(9.6, 14.));
  expected_intervals_by_dimension[3].push_back(std::make_pair(34.2, 34.974));
  expected_intervals_by_dimension[1].push_back(std::make_pair(3., std::numeric_limits<double>::infinity()));

  Persistence_intervals_by_dimension persistence_intervals_by_dimension =
      Gudhi::read_persistence_intervals_grouped_by_dimension("persistence_intervals_with_field.pers");

  std::clog << "\nread_persistence_intervals_grouped_by_dimension - expected\n";
  for (auto map_iter : expected_intervals_by_dimension) {
    std::clog << "key=" << map_iter.first;
    for (auto vec_iter : map_iter.second)
      std::clog << " [" << vec_iter.first << " ," << vec_iter.second << "] ";
  }

  std::clog << "\nread_persistence_intervals_grouped_by_dimension - read\n";
  for (auto map_iter : persistence_intervals_by_dimension) {
    std::clog << "key=" << map_iter.first;
    for (auto vec_iter : map_iter.second)
      std::clog << " [" << vec_iter.first << " ," << vec_iter.second << "] ";
  }

  BOOST_CHECK(persistence_intervals_by_dimension == expected_intervals_by_dimension);

  Persistence_intervals expected_intervals_in_dimension;
  expected_intervals_in_dimension.push_back(std::make_pair(2.7, 3.7));
  expected_intervals_in_dimension.push_back(std::make_pair(9.6, 14.));
  expected_intervals_in_dimension.push_back(std::make_pair(34.2, 34.974));
  expected_intervals_in_dimension.push_back(std::make_pair(3., std::numeric_limits<double>::infinity()));

  Persistence_intervals persistence_intervals_in_dimension =
      Gudhi::read_persistence_intervals_in_dimension("persistence_intervals_with_field.pers");

  std::clog << "\nread_persistence_intervals_in_dimension - expected\n";
  for (auto vec_iter : expected_intervals_in_dimension)
    std::clog << " [" << vec_iter.first << " ," << vec_iter.second << "] ";

  std::clog << "\nread_persistence_intervals_in_dimension - read\n";
  for (auto vec_iter : persistence_intervals_in_dimension)
    std::clog << " [" << vec_iter.first << " ," << vec_iter.second << "] ";

  BOOST_CHECK(persistence_intervals_in_dimension == expected_intervals_in_dimension);

  expected_intervals_in_dimension.clear();
  expected_intervals_in_dimension.push_back(std::make_pair(2.7, 3.7));
  persistence_intervals_in_dimension =
        Gudhi::read_persistence_intervals_in_dimension("persistence_intervals_with_field.pers", 0);

  std::clog << "\nread_persistence_intervals_in_dimension 0 - expected\n";
  for (auto vec_iter : expected_intervals_in_dimension)
    std::clog << " [" << vec_iter.first << " ," << vec_iter.second << "] ";

  std::clog << "\nread_persistence_intervals_in_dimension 0 - read\n";
  for (auto vec_iter : persistence_intervals_in_dimension)
    std::clog << " [" << vec_iter.first << " ," << vec_iter.second << "] ";

  BOOST_CHECK(persistence_intervals_in_dimension == expected_intervals_in_dimension);

  expected_intervals_in_dimension.clear();
  expected_intervals_in_dimension.push_back(std::make_pair(9.6, 14.));
  expected_intervals_in_dimension.push_back(std::make_pair(3., std::numeric_limits<double>::infinity()));
  persistence_intervals_in_dimension =
        Gudhi::read_persistence_intervals_in_dimension("persistence_intervals_with_field.pers", 1);

  std::clog << "\nread_persistence_intervals_in_dimension 1 - expected\n";
  for (auto vec_iter : expected_intervals_in_dimension)
    std::clog << " [" << vec_iter.first << " ," << vec_iter.second << "] ";

  std::clog << "\nread_persistence_intervals_in_dimension 1 - read\n";
  for (auto vec_iter : persistence_intervals_in_dimension)
    std::clog << " [" << vec_iter.first << " ," << vec_iter.second << "] ";

  BOOST_CHECK(persistence_intervals_in_dimension == expected_intervals_in_dimension);

  expected_intervals_in_dimension.clear();
  persistence_intervals_in_dimension =
        Gudhi::read_persistence_intervals_in_dimension("persistence_intervals_with_field.pers", 2);

  std::clog << "\nread_persistence_intervals_in_dimension 2 - expected\n";
  for (auto vec_iter : expected_intervals_in_dimension)
    std::clog << " [" << vec_iter.first << " ," << vec_iter.second << "] ";

  std::clog << "\nread_persistence_intervals_in_dimension 2 - read\n";
  for (auto vec_iter : persistence_intervals_in_dimension)
    std::clog << " [" << vec_iter.first << " ," << vec_iter.second << "] ";

  BOOST_CHECK(persistence_intervals_in_dimension == expected_intervals_in_dimension);

  expected_intervals_in_dimension.clear();
  expected_intervals_in_dimension.push_back(std::make_pair(34.2, 34.974));
  persistence_intervals_in_dimension =
        Gudhi::read_persistence_intervals_in_dimension("persistence_intervals_with_field.pers", 3);

  std::clog << "\nread_persistence_intervals_in_dimension 3 - expected\n";
  for (auto vec_iter : expected_intervals_in_dimension)
    std::clog << " [" << vec_iter.first << " ," << vec_iter.second << "] ";

  std::clog << "\nread_persistence_intervals_in_dimension 3 - read\n";
  for (auto vec_iter : persistence_intervals_in_dimension)
    std::clog << " [" << vec_iter.first << " ," << vec_iter.second << "] ";

  BOOST_CHECK(persistence_intervals_in_dimension == expected_intervals_in_dimension);

}


//=======================================================================
// Copyright 2007 Aaron Windsor
//
// Distributed under the Boost Software License, Version 1.0. (See
// accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
//=======================================================================

#include <boost/graph/adjacency_list.hpp>
#include <boost/graph/properties.hpp>
#include <boost/graph/make_maximal_planar.hpp>
#include <boost/graph/boyer_myrvold_planar_test.hpp>
#include <boost/property_map/property_map.hpp>
#include <boost/property_map/vector_property_map.hpp>
#include <boost/test/minimal.hpp>

using namespace boost;

template <typename Graph>
void reset_edge_index(Graph& g)
{
  typename property_map<Graph, edge_index_t>::type index = get(edge_index, g);
  typename graph_traits<Graph>::edge_iterator ei, ei_end;
  typename graph_traits<Graph>::edges_size_type cnt = 0;
  for(boost::tie(ei,ei_end) = edges(g); ei != ei_end; ++ei)
    put(index, *ei, cnt++);
}

template <typename Graph>
void make_cycle(Graph& g, int size)
{
  typedef typename graph_traits<Graph>::vertex_descriptor vertex_t;

  vertex_t first_vertex = add_vertex(g);
  vertex_t prev_vertex = first_vertex;

  for(int i = 1; i < size; ++i)
    {
      vertex_t curr_vertex = add_vertex(g);
      add_edge(curr_vertex, prev_vertex, g);
      prev_vertex = curr_vertex;
    }

  add_edge(first_vertex, prev_vertex, g);
}

struct UpdateVertexIndex
{
  template <typename Graph>
  void update(Graph& g)
  {
    typename property_map<Graph, vertex_index_t>::type index = get(vertex_index, g);
    typename graph_traits<Graph>::vertex_iterator vi, vi_end;
    typename graph_traits<Graph>::vertices_size_type cnt = 0;
    for(boost::tie(vi,vi_end) = vertices(g); vi != vi_end; ++vi)
      put(index, *vi, cnt++);
  }
};

struct NoVertexIndexUpdater
{
  template <typename Graph> void update(Graph&) {}
};

template <typename Graph, typename VertexIndexUpdater>
void test_cycle(VertexIndexUpdater vertex_index_updater, int size)
{

  Graph g;
  make_cycle(g, size);
  vertex_index_updater.update(g);
  reset_edge_index(g);

  typedef std::vector< typename graph_traits<Graph>::edge_descriptor > edge_vector_t;
  typedef std::vector< edge_vector_t > embedding_storage_t;
  typedef iterator_property_map
    < typename embedding_storage_t::iterator,
      typename property_map<Graph, vertex_index_t>::type
    > embedding_t;

  embedding_storage_t embedding_storage(num_vertices(g));
  embedding_t embedding(embedding_storage.begin(), get(vertex_index, g));

  typename graph_traits<Graph>::vertex_iterator vi, vi_end;
  for(boost::tie(vi,vi_end) = vertices(g); vi != vi_end; ++vi)
    std::copy(out_edges(*vi,g).first, out_edges(*vi,g).second, std::back_inserter(embedding[*vi]));

  BOOST_CHECK(boyer_myrvold_planarity_test(g));
  make_maximal_planar(g, embedding);
  reset_edge_index(g);

  // A graph is maximal planar exactly when it's both
  // planar and has 3 * num_vertices(g) - 6 edges.
  BOOST_CHECK(num_edges(g) == 3 * num_vertices(g) - 6);
  BOOST_CHECK(boyer_myrvold_planarity_test(g));

}

int test_main(int, char* [])
{
  typedef adjacency_list
    <vecS,
    vecS,
    undirectedS,
    property<vertex_index_t, int>,
    property<edge_index_t, int>
    >
    VVgraph_t;

  typedef adjacency_list
    <vecS,
    listS,
    undirectedS,
    property<vertex_index_t, int>,
    property<edge_index_t, int>
    >
    VLgraph_t;

  typedef adjacency_list
    <listS,
    vecS,
    undirectedS,
    property<vertex_index_t, int>,
    property<edge_index_t, int>
    >
    LVgraph_t;

  typedef adjacency_list
    <listS,
    listS,
    undirectedS,
    property<vertex_index_t, int>,
    property<edge_index_t, int>
    >
    LLgraph_t;

  typedef adjacency_list
    <setS,
    setS,
    undirectedS,
    property<vertex_index_t, int>,
    property<edge_index_t, int>
    >
    SSgraph_t;

  test_cycle<VVgraph_t>(NoVertexIndexUpdater(), 10);
  test_cycle<VVgraph_t>(NoVertexIndexUpdater(), 50);

  test_cycle<VLgraph_t>(UpdateVertexIndex(), 3);
  test_cycle<VLgraph_t>(UpdateVertexIndex(), 30);

  test_cycle<LVgraph_t>(NoVertexIndexUpdater(), 15);
  test_cycle<LVgraph_t>(NoVertexIndexUpdater(), 45);

  test_cycle<LLgraph_t>(UpdateVertexIndex(), 8);
  test_cycle<LLgraph_t>(UpdateVertexIndex(), 19);

  test_cycle<SSgraph_t>(UpdateVertexIndex(), 13);
  test_cycle<SSgraph_t>(UpdateVertexIndex(), 20);

  return 0;
}


#include <catch2/catch_test_macros.hpp>

#include <iomanip>
#include <iostream>
#include <ostream>
//class PinchDifferentiator;

/**
 * @brief Differentiable, piecewise quadratic function of distance from a 1D
 * agent going from one "inside" the agent to
 */
class FixedBandwidthQuadraticPlateau {
  double innerRadius_m_;
  double outerRadius_m_;
};

/**
 * @brief 1D projection of a non-ego vehicle model whose position can be
 * queried over time. In this case the agent has a fixed speed. (It could be
 * generalized to support
 */
class FixedSpeedAgent {
  double speed_mps_;
  double initialPosition_m_;
public:
  FixedSpeedAgent(double speed_mps, double initialPosition_m)
      : speed_mps_(speed_mps), initialPosition_m_(initialPosition_m) {}
  double GetPosition(double time_s) const {
    return initialPosition_m_ + speed_mps_ * time_s;
  }
};

TEST_CASE("GetPosition", "[FixedSpeedAgent]") {
  CHECK(FixedSpeedAgent(-25.0, 50.0).GetPosition(2.0) == 0.0);
}

class

#include <boost/numeric/odeint.hpp>
#include <array>

typedef std::array<double, 3> state_type;

TEST_CASE("operator[]", "[state_type]") {
    state_type state;
    size_t n = state.size();
    for (size_t i = 0; i < n; ++i) {
      CHECK_NOTHROW(state[i]);
    }
}

std::ostream &operator<<(std::ostream &os, const state_type &state) {
  for (const auto & dim : state) {
    os << std::setw(16) << dim;
  }
  return os;
}

const double gam = 0.15;

struct harm_osc {
  harm_osc( double gam ) : m_gam(gam) { }
  void operator() ( const state_type &x , state_type &dxdt , const double /* t */ ) {
    dxdt[0] = x[1];
    dxdt[1] = -x[0] - m_gam*x[1];
  }
private:
  double m_gam;
};

struct push_back_state_and_time
{
  std::vector< state_type >& m_states;
  std::vector< double >& m_times;
  push_back_state_and_time( std::vector< state_type > &states , std::vector< double > &times )
      : m_states( states ) , m_times( times ) {}
  void operator()( const state_type &x , double t ) {
    m_states.push_back( x );
    m_times.push_back( t );
  }
};

struct write_state {
  void operator()( const state_type &x ) const {
    std::cout << x << "\n";
  }
};

int main2(int /* argc */ , char** /* argv */ )
{
  using namespace std;
  using namespace boost::numeric::odeint;

  state_type x;
  x[0] = 1.0; // start at x=1.0, p=0.0
  x[1] = 0.0;

  vector<state_type> x_vec;
  vector<double> times;

  harm_osc ho(0.15);

  auto steps = integrate(ho, x, 0.0, 10.0, 0.1,
                         push_back_state_and_time(x_vec, times));
  for (size_t i = 0; i < times.size(); ++i) {
    cout << setw(8) << times[i] << " " << x_vec[i] << endl;
  }
}