#include "app_config.h"
#include "palette_manager.h"

#include <sstream>
#include <mutex>

#include <cinder/Json.h>
#include <cinder/params/Params.h>
#include <cinder/audio/Context.h>

#include <boost/algorithm/string/replace.hpp>

namespace cistft
{

namespace {
const std::string TEMPLATE("{\n\
	\"record_duration\":@RECORD_DURATION@,\n\
	\"time_range\":@TIME_RANGE@,\n\
	\"window_duration\":@WINDOW_DURATION@,\n\
	\"hop_duration\":@HOP_DURATION@,\n\
	\"viewable_bins\":@VIEWABLE_BINS@,\n\
	\"bandpass\":{\n\
		\"low_pass\":@FREQ_LOWPASS@,\n\
		\"high_pass\":@FREQ_HIGHPASS@\n\
	},\n\
	\"color_palette\":{\n\
		\"index\":@CP_INDEX@,\n\
		\"db_mode\":@CP_DB_MODE@,\n\
		\"db_mode_divisor\":@CP_DB_MODE_DIV@,\n\
		\"linear_mode_coeff\":@CP_LIN_MODE_COEFF@,\n\
		\"saturation_level\":{\n\
			\"low\":@SAT_LOW@,\n\
			\"high\":@SAT_HIGH@\n\
		}\n\
	}\n\
}");
const std::string CONFIG_FILENAME("stft.conf");
}

AppConfig::AppConfig()
	: mConfigFile(CONFIG_FILENAME, std::ios::in)
	, mRecordDuration(20.0f * 60.0f) // every 20 minutes, internals will reset
	, mTimeRange(20.0f) // 20 seconds in one screen
	, mWindowDuration(0.02f) // about 1024 samples in 20 seconds
	, mHopDuration(0.01f) // about 512 samples in 20 seconds
	, mMinimumViewableBins(256)
	, mLowPassFrequency(10000.0f) //10KHz
	, mHighPassFrequency(100.0f) //100Hz
	, mActualViewableBins(0)
	, mActualLowPassFrequency(0)
	, mActualHighPassFrequency(0)
	, mMagnitudeIndexStart(0)
	, mCalculatedFftSize(0)
	, mSampleRate(0)
	, mSamplesCacheSize(50)
	, mPreparedForLaunch(false)
	, mDirty(true)
{
	if (mConfigFile)
	{
		std::stringstream buf;
		buf << mConfigFile.rdbuf();
		try {
			ci::JsonTree _tree(buf.str());
			if (_tree.hasChild("record_duration")) {
				mRecordDuration = _tree.getChild("record_duration").getValue<float>();
			}
			if (_tree.hasChild("time_range")) {
				mTimeRange = _tree.getChild("time_range").getValue<float>();
			}
			if (_tree.hasChild("window_duration")) {
				mWindowDuration = _tree.getChild("window_duration").getValue<float>();
			}
			if (_tree.hasChild("hop_duration")) {
				mHopDuration = _tree.getChild("hop_duration").getValue<float>();
			}
			if (_tree.hasChild("viewable_bins")) {
				mMinimumViewableBins = _tree.getChild("viewable_bins").getValue<int>();
			}
			if (_tree.hasChild("bandpass"))
			{
				if (_tree.hasChild("bandpass.low_pass"))
				{
					mLowPassFrequency = _tree.getChild("bandpass.low_pass").getValue<float>();
				}
				if (_tree.hasChild("bandpass.high_pass"))
				{
					mHighPassFrequency = _tree.getChild("bandpass.high_pass").getValue<float>();
				}
			}
			if (_tree.hasChild("color_palette"))
			{
				if (_tree.hasChild("color_palette.saturation_level")) {
					if (_tree.hasChild("color_palette.saturation_level.low")) {
						palette::Manager::instance().setMinThreshold(_tree.getChild("color_palette.saturation_level.low").getValue<float>());
					}
					if (_tree.hasChild("color_palette.saturation_level.high")) {
						palette::Manager::instance().setMaxThreshold(_tree.getChild("color_palette.saturation_level.high").getValue<float>());
					}
				}

				if (_tree.hasChild("color_palette.index")) {
					palette::Manager::instance().setActivePalette(_tree.getChild("color_palette.index").getValue<int>());
				}

				if (_tree.hasChild("color_palette.db_mode")) {
					palette::Manager::instance().setConvertToDb(_tree.getChild("color_palette.db_mode").getValue<bool>());
				}

				if (_tree.hasChild("color_palette.db_mode_divisor")) {
					palette::Manager::instance().setDbDivisor(_tree.getChild("color_palette.db_mode_divisor").getValue<float>());
				}

				if (_tree.hasChild("color_palette.linear_mode_coeff")) {
					palette::Manager::instance().setLinearCoefficient(_tree.getChild("color_palette.linear_mode_coeff").getValue<float>());
				}
			}
		}
		catch (...) { /*no op*/ }
	}

	checkSanity();
}

namespace {
//! in terms of samples
static int MINIMUM_ZERO_PADDING_OFFSET = 128;
} //!namespace

void AppConfig::buildBandPass() const
{
	mCalculatedFftSize = static_cast<int>((mMinimumViewableBins * mSampleRate) / (mLowPassFrequency - mHighPassFrequency));

	if (!ci::isPowerOf2(mCalculatedFftSize))
		mCalculatedFftSize = ci::nextPowerOf2(mCalculatedFftSize);

	if (mCalculatedFftSize < static_cast<int>(getWindowDurationInSamples()) + MINIMUM_ZERO_PADDING_OFFSET)
	{
		// The while loop guarantees we ALWAYS get zero padding
		while (mCalculatedFftSize < static_cast<int>(getWindowDurationInSamples()) + MINIMUM_ZERO_PADDING_OFFSET)
		{
			mCalculatedFftSize = ci::nextPowerOf2(mCalculatedFftSize);
		}
	}

	// Less than 1024? get out of here.
	if (mCalculatedFftSize < 1024) mCalculatedFftSize = 1024;

	// a temporary place to calculate min / max frequency
	auto _temp_frequency = 0.0f;
	// index of operation placeholder
	auto index = 0;
	// Based on the calculated FFT size, calculate the frequency step per bin
	const auto _frequency_step = (1.0f / mCalculatedFftSize) * mSampleRate;

	// Let's find the high pass index first. This is where we start showing FFT data on screen
	for (index = 0; index < mCalculatedFftSize / 2 && _temp_frequency < mHighPassFrequency; ++index)
	{
		_temp_frequency += _frequency_step;
	}

	// double check if the above loop's operation is not passed beyond high pass frequency
	if (_temp_frequency > mHighPassFrequency)
	{
		// if so, subtract one from index and also go back one frequency step
		_temp_frequency -= _frequency_step;
		index--;
	}

	// We got the actual high pass frequency calculated here, in an ideal world, this is equal to what user has supplied
	mActualHighPassFrequency = _temp_frequency;
	// Get the index, we need it.
	mMagnitudeIndexStart = index;

	// Let's find the low pass index. This is where we finish showing FFT data on screen
	for (/* no op */; index < mCalculatedFftSize / 2 && _temp_frequency < mLowPassFrequency; ++index)
	{
		_temp_frequency += _frequency_step;
	}

	// double check if the above loop's operation is not passed beyond low pass frequency
	if (_temp_frequency > mLowPassFrequency)
	{
		// if so, subtract one from index and also go back one frequency step
		_temp_frequency -= _frequency_step;
		index--;
	}

	// Calculated the actual number of bins shown to the user
	mActualViewableBins = (index - mMagnitudeIndexStart) + 1;
	// We got the actual low pass frequency calculated here, in an ideal world, this is equal to what user has supplied
	mActualLowPassFrequency = _temp_frequency;
}

AppConfig::~AppConfig()
{
	if (mConfigFile) mConfigFile.close();
	mConfigFile.open(CONFIG_FILENAME, std::ios::out);
	if (mConfigFile)
	{
		mConfigFile << generateConfig() << std::endl;
	}
}

std::string AppConfig::generateConfig() const
{
	std::string _template_copy(TEMPLATE);

	boost::algorithm::replace_first(_template_copy, "@RECORD_DURATION@", std::to_string(mRecordDuration));
	boost::algorithm::replace_first(_template_copy, "@TIME_RANGE@", std::to_string(mTimeRange));
	boost::algorithm::replace_first(_template_copy, "@WINDOW_DURATION@", std::to_string(mWindowDuration));
	boost::algorithm::replace_first(_template_copy, "@HOP_DURATION@", std::to_string(mHopDuration));
	boost::algorithm::replace_first(_template_copy, "@VIEWABLE_BINS@", std::to_string(mMinimumViewableBins));
	boost::algorithm::replace_first(_template_copy, "@FREQ_LOWPASS@", std::to_string(mLowPassFrequency));
	boost::algorithm::replace_first(_template_copy, "@FREQ_HIGHPASS@", std::to_string(mHighPassFrequency));
	boost::algorithm::replace_first(_template_copy, "@CP_INDEX@", std::to_string(palette::Manager::instance().getActivePalette()));
	boost::algorithm::replace_first(_template_copy, "@CP_DB_MODE@", palette::Manager::instance().getConvertToDb() ? "true" : "false");
	boost::algorithm::replace_first(_template_copy, "@CP_DB_MODE_DIV@", std::to_string(palette::Manager::instance().getDbDivisor()));
	boost::algorithm::replace_first(_template_copy, "@CP_LIN_MODE_COEFF@", std::to_string(palette::Manager::instance().getLinearCoefficient()));
	boost::algorithm::replace_first(_template_copy, "@SAT_LOW@", std::to_string(palette::Manager::instance().getMinThreshold()));
	boost::algorithm::replace_first(_template_copy, "@SAT_HIGH@", std::to_string(palette::Manager::instance().getMaxThreshold()));

	return _template_copy;
}

void AppConfig::checkSanity()
{
	if (mTimeRange > mRecordDuration) mTimeRange = mRecordDuration;
	if (mWindowDuration > mTimeRange) mWindowDuration = mTimeRange;
	if (mHopDuration > mWindowDuration) mHopDuration = mWindowDuration;
	if (mHighPassFrequency < 0) mHighPassFrequency = 0;
	if (mHighPassFrequency > mLowPassFrequency) mHighPassFrequency = mLowPassFrequency;
	if (mMinimumViewableBins < 0) mMinimumViewableBins = 0;
}

void AppConfig::prepareLaunch()
{
	static std::once_flag _prepare_it;
	std::call_once(_prepare_it, [this]{ mPreparedForLaunch = true; });
}

bool AppConfig::shouldLaunch() const
{
	return mPreparedForLaunch;
}

void AppConfig::performLaunch()
{
	if (shouldLaunch())
	{
		static std::once_flag _launch;
		std::call_once(_launch, [this]{ mPreparedForLaunch = false; });
	}
}

AppConfig& AppConfig::minimumViewableBins(int val)
{
	mMinimumViewableBins = val;

	mDirty = true;
	return *this;
}

AppConfig& AppConfig::lowPassFrequency(float val)
{
	if (mSampleRate / 2.0f < val)
		val = mSampleRate / 2.0f;

	mLowPassFrequency = val;

	if (mHighPassFrequency > mLowPassFrequency)
		mLowPassFrequency = mHighPassFrequency;

	mDirty = true;
	return *this;
}

AppConfig& AppConfig::highPassFrequency(float val)
{
	if (val < 0.0f) val = 0.0f;

	mHighPassFrequency = val;

	if (mHighPassFrequency > mLowPassFrequency)
		mHighPassFrequency = mLowPassFrequency;

	mDirty = true;
	return *this;
}

AppConfig& AppConfig::recordDuration(float val)
{
	mRecordDuration = val;

	if (mRecordDuration < 0)
		mRecordDuration = 0;

	mDirty = true;
	return *this;
}

AppConfig& AppConfig::timeRange(float val)
{
	mTimeRange = val;

	if (mTimeRange < 0)
		mTimeRange = 0;

	mDirty = true;
	return *this;
}

AppConfig& AppConfig::windowDuration(float val)
{
	mWindowDuration = val;

	if (mWindowDuration < 0)
		mWindowDuration = 0;

	mDirty = true;
	return *this;
}

AppConfig& AppConfig::hopDuration(float val)
{
	mHopDuration = val;

	if (mHopDuration < 0)
		mHopDuration = 0;

	mDirty = true;
	return *this;
}

AppConfig& AppConfig::samplesCacheSize(int val)
{
	mSamplesCacheSize = val;

	if (mSamplesCacheSize < 0)
		mSamplesCacheSize = 0;

	return *this;
}

int AppConfig::getMinimumViewableBins() const
{
	checkDirty();
	return mMinimumViewableBins;
}

float AppConfig::getLowPassFrequency() const
{
	checkDirty();
	return mLowPassFrequency;
}

float AppConfig::getHighPassFrequency() const
{
	checkDirty();
	return mHighPassFrequency;
}

int AppConfig::getActualViewableBins() const
{
	checkDirty();
	return mActualViewableBins;
}

float AppConfig::getActualLowPassFrequency() const
{
	checkDirty();
	return mActualLowPassFrequency;
}

float AppConfig::getActualHighPassFrequency() const
{
	checkDirty();
	return mActualHighPassFrequency;
}

int AppConfig::getMagnitudeIndexStart() const
{
	checkDirty();
	return mMagnitudeIndexStart;
}

int AppConfig::getCalculatedFftSize() const
{
	checkDirty();
	return mCalculatedFftSize;
}

float AppConfig::getRecordDuration() const
{
	checkDirty();
	return mRecordDuration;
}

float AppConfig::getTimeRange() const
{
	checkDirty();
	return mTimeRange;
}

float AppConfig::getWindowDuration() const
{
	checkDirty();
	return mWindowDuration;
}

float AppConfig::getHopDuration() const
{
	checkDirty();
	return mHopDuration;
}

int AppConfig::getSamplesCacheSize() const
{
	checkDirty();
	return mSamplesCacheSize;
}

int AppConfig::getRecordDurationInSamples() const
{
	checkDirty();
	return static_cast<int>(getRecordDuration() * mSampleRate);
}

int AppConfig::getTimeSpanInSamples() const
{
	checkDirty();
	return static_cast<int>(getTimeRange() * mSampleRate);
}

int AppConfig::getHopDurationInSamples() const
{
	checkDirty();
	return static_cast<int>(getHopDuration() * mSampleRate);
}

int AppConfig::getWindowDurationInSamples() const
{
	checkDirty();
	return static_cast<int>(getWindowDuration() * mSampleRate);
}

int AppConfig::getSampleRate() const
{
	checkDirty();
	return mSampleRate;
}

void AppConfig::checkDirty() const
{
	if (mDirty) { setup(); }
}

namespace {
namespace GUI_STATICS {
const static std::string SAMPLE_CACHE_SIZE_KEY("Sample cache size (Pixels)");
const static std::string SAMPLE_RATE_KEY("Sample Rate (Hz)");
const static std::string BINS_TEXT_KEY("Guaranteed FFT bins");
const static std::string LOW_PASS_FREQ_KEY("Low pass frequency (Hz)");
const static std::string HIGH_PASS_FREQ_KEY("High pass frequency (Hz)");
const static std::string CALCULATED_FFT_KEY("Calculated FFT size");
const static std::string ACTUAL_VIEWABLE_BINS_KEY("Calculated viewable bins");
const static std::string ACTUAL_LP_FREQ_KEY("Calculated Low pass frequency (Hz)");
const static std::string ACTUAL_HP_FREQ_KEY("Calculated High pass frequency (Hz)");
const static std::string CONFIGURE_TEXT_KEY("Configure the parameters below and hit START");
const static std::string RECORD_TEXT_KEY("Record duration (s)");
const static std::string VIEWABLE_TEXT_KEY("Viewable Time range (s)");
const static std::string WINDOW_TEXT_KEY("Window duration (s)");
const static std::string HOP_TEXT_KEY("Hop duration (s)");
const static std::string START_BUTTON_KEY("START");
}}

void AppConfig::setupPreLaunchGUI(cinder::params::InterfaceGl* const gui)
{
	gui->addText("Filter parameters");
	gui->addParam<int>(GUI_STATICS::SAMPLE_CACHE_SIZE_KEY, &mSamplesCacheSize);
	gui->addParam<int>(GUI_STATICS::SAMPLE_RATE_KEY, &mSampleRate).optionsStr("readonly=true");
	gui->addParam<int>(GUI_STATICS::BINS_TEXT_KEY, [this](int val){ minimumViewableBins(val); }, [this]()->int{ return getMinimumViewableBins(); });
	gui->addParam<float>(GUI_STATICS::LOW_PASS_FREQ_KEY, [this](float val){ lowPassFrequency(val); }, [this]()->float{ return getLowPassFrequency(); });
	gui->addParam<float>(GUI_STATICS::HIGH_PASS_FREQ_KEY, [this](float val){ highPassFrequency(val); }, [this]()->float{ return getHighPassFrequency(); });

	gui->addParam(GUI_STATICS::CALCULATED_FFT_KEY, &mCalculatedFftSize, "readonly=true");
	gui->addParam(GUI_STATICS::ACTUAL_VIEWABLE_BINS_KEY, &mActualViewableBins, "readonly=true");
	gui->addParam(GUI_STATICS::ACTUAL_LP_FREQ_KEY, &mLowPassFrequency, "readonly=true");
	gui->addParam(GUI_STATICS::ACTUAL_HP_FREQ_KEY, &mHighPassFrequency, "readonly=true");
	gui->addSeparator();

	gui->addText(GUI_STATICS::CONFIGURE_TEXT_KEY);
	gui->addParam(GUI_STATICS::RECORD_TEXT_KEY, &mRecordDuration).min(300.0f).max(6000.0f).step(10.0f);
	gui->addParam(GUI_STATICS::VIEWABLE_TEXT_KEY, &mTimeRange).min(2.0f).max(20.0f).step(0.5f);
	gui->addParam(GUI_STATICS::WINDOW_TEXT_KEY, &mWindowDuration).min(0.01f).max(0.5f).step(0.01f);
	gui->addParam(GUI_STATICS::HOP_TEXT_KEY, &mHopDuration).min(0.0f).max(0.5f).step(0.005f);
	gui->addSeparator();

	gui->addButton(GUI_STATICS::START_BUTTON_KEY, [this, gui] {
		gui->minimize();
		prepareLaunch();
	});
}

void AppConfig::setupPostLaunchGUI(cinder::params::InterfaceGl* const gui)
{
	gui->setOptions(GUI_STATICS::SAMPLE_CACHE_SIZE_KEY, "readonly=true");
	gui->setOptions(GUI_STATICS::BINS_TEXT_KEY, "readonly=true");
	gui->setOptions(GUI_STATICS::LOW_PASS_FREQ_KEY, "readonly=true");
	gui->setOptions(GUI_STATICS::HIGH_PASS_FREQ_KEY, "readonly=true");

	gui->removeParam(GUI_STATICS::START_BUTTON_KEY);
	gui->removeParam(GUI_STATICS::CONFIGURE_TEXT_KEY);

	gui->setOptions(GUI_STATICS::RECORD_TEXT_KEY, "readonly=true");
	gui->setOptions(GUI_STATICS::WINDOW_TEXT_KEY, "readonly=true");
	gui->setOptions(GUI_STATICS::HOP_TEXT_KEY, "readonly=true");
	gui->setOptions(GUI_STATICS::VIEWABLE_TEXT_KEY, "readonly=true");
}

namespace {
static size_t _get_sample_rate()
{
return ci::audio::Context::deviceManager()->getDefaultOutput()->getSampleRate();
}} //!namespace

void AppConfig::setup() const
{
	if (!mDirty) return;

	mDirty = false;

	mSampleRate = _get_sample_rate();
	buildBandPass();
}

} //!cistft


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


#include <boost/accumulators/statistics/rolling_window.hpp>


<<<<<<< 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 <string>
#include <thread>

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

#include "Tudat/Astrodynamics/BasicAstrodynamics/unitConversions.h"
#include "Tudat/Mathematics/BasicMathematics/linearAlgebra.h"
#include "Tudat/Astrodynamics/BasicAstrodynamics/physicalConstants.h"
#include "Tudat/Astrodynamics/BasicAstrodynamics/orbitalElementConversions.h"
#include "Tudat/External/SpiceInterface/spiceInterface.h"
#include "Tudat/Mathematics/NumericalIntegrators/rungeKuttaCoefficients.h"
#include "Tudat/Astrodynamics/BasicAstrodynamics/accelerationModel.h"
#include "Tudat/InputOutput/basicInputOutput.h"
#include "Tudat/SimulationSetup/PropagationSetup/dynamicsSimulator.h"
#include "Tudat/SimulationSetup/EnvironmentSetup/defaultBodies.h"
#include "Tudat/SimulationSetup/EnvironmentSetup/createBodies.h"
#include "Tudat/SimulationSetup/EstimationSetup/createNumericalSimulator.h"
#include "Tudat/Mathematics/NumericalIntegrators/createNumericalIntegrator.h"

namespace tudat
{
namespace unit_tests
{

using namespace tudat;
using namespace simulation_setup;
using namespace propagators;
using namespace numerical_integrators;
using namespace orbital_element_conversions;
using namespace basic_mathematics;
using namespace basic_astrodynamics;
using namespace gravitation;
using namespace numerical_integrators;
using namespace unit_conversions;
using namespace propagators;

BOOST_AUTO_TEST_SUITE( test_empirical_acceleration )

//! Test if empirical accelerations are computed correctly in propagation.
BOOST_AUTO_TEST_CASE( testEmpiricalAccelerations )
{
    //Load spice kernels.
    spice_interface::loadStandardSpiceKernels( );

    // Test three different cases of empirical acceleration values
    for( unsigned testCase = 0; testCase < 3; testCase++ )
    {
        // Set simulation end epoch.
        const double simulationEndEpoch = 2.0 * tudat::physical_constants::JULIAN_DAY;

        // Set numerical integration fixed step size.
        const double fixedStepSize = 15.0;

        // Define body settings for simulation.
        std::map< std::string, std::shared_ptr< BodySettings > > bodySettings;
        bodySettings[ "Earth" ] = std::make_shared< BodySettings >( );
        bodySettings[ "Earth" ]->ephemerisSettings = std::make_shared< ConstantEphemerisSettings >(
                    Eigen::Vector6d::Zero( ), "SSB", "J2000" );
        bodySettings[ "Earth" ]->gravityFieldSettings = std::make_shared< GravityFieldSettings >( central_spice );

        // Create Earth object
        NamedBodyMap bodyMap = createBodies( bodySettings );

        // Create spacecraft object.
        bodyMap[ "Asterix" ] = std::make_shared< simulation_setup::Body >( );

        // Finalize body creation.
        setGlobalFrameBodyEphemerides( bodyMap, "SSB", "J2000" );

        // Define propagator settings variables.
        SelectedAccelerationMap accelerationMap;
        std::vector< std::string > bodiesToPropagate;
        std::vector< std::string > centralBodies;

        // Define propagation settings.
        std::map< std::string, std::vector< std::shared_ptr< AccelerationSettings > > > accelerationsOfAsterix;
        accelerationsOfAsterix[ "Earth" ].push_back( std::make_shared< AccelerationSettings >(
                                                         basic_astrodynamics::central_gravity ) );

        // Define empirical acceleration values for current case
        double empiricalAccelerationNorm = 1.0E-8;
        if( testCase == 0 )
        {
            accelerationsOfAsterix[ "Earth" ].push_back( std::make_shared< EmpiricalAccelerationSettings >(
                                                             empiricalAccelerationNorm * Eigen::Vector3d::UnitX( ) ) );
        }
        else if( testCase == 1 )
        {
            accelerationsOfAsterix[ "Earth" ].push_back( std::make_shared< EmpiricalAccelerationSettings >(
                                                             Eigen::Vector3d::Zero( ),
                                                             empiricalAccelerationNorm * Eigen::Vector3d::UnitX( ),
                                                             empiricalAccelerationNorm * Eigen::Vector3d::UnitY( ) ) );
        }
        else if( testCase == 2 )
        {
            accelerationsOfAsterix[ "Earth" ].push_back( std::make_shared< EmpiricalAccelerationSettings >(
                                                             empiricalAccelerationNorm * Eigen::Vector3d::UnitX( ),
                                                             empiricalAccelerationNorm * Eigen::Vector3d::UnitY( ),
                                                             empiricalAccelerationNorm * Eigen::Vector3d::UnitZ( ) ) );
        }
        accelerationMap[ "Asterix" ] = accelerationsOfAsterix;
        bodiesToPropagate.push_back( "Asterix" );
        centralBodies.push_back( "Earth" );

        // Create acceleration models and propagation settings.
        basic_astrodynamics::AccelerationMap accelerationModelMap = createAccelerationModelsMap(
                    bodyMap, accelerationMap, bodiesToPropagate, centralBodies );

        // Set Keplerian elements for Asterix.
        Eigen::Vector6d asterixInitialStateInKeplerianElements;
        asterixInitialStateInKeplerianElements( semiMajorAxisIndex ) = 7500.0E3;
        asterixInitialStateInKeplerianElements( eccentricityIndex ) = 0.1;
        asterixInitialStateInKeplerianElements( inclinationIndex ) = convertDegreesToRadians( 85.3 );
        asterixInitialStateInKeplerianElements( argumentOfPeriapsisIndex )
                = convertDegreesToRadians( 235.7 );
        asterixInitialStateInKeplerianElements( longitudeOfAscendingNodeIndex )
                = convertDegreesToRadians( 23.4 );
        asterixInitialStateInKeplerianElements( trueAnomalyIndex ) = convertDegreesToRadians( 139.87 );

        // Convert Asterix state from Keplerian elements to Cartesian elements.
        double earthGravitationalParameter = bodyMap.at( "Earth" )->getGravityFieldModel( )->getGravitationalParameter( );
        Eigen::VectorXd systemInitialState = convertKeplerianToCartesianElements(
                    asterixInitialStateInKeplerianElements,
                    earthGravitationalParameter );

        // Define dependent variable settings
        std::vector< std::shared_ptr< SingleDependentVariableSaveSettings > > dependentVariables;
        dependentVariables.push_back(
                    std::make_shared< SingleAccelerationDependentVariableSaveSettings >(
                        empirical_acceleration, "Asterix", "Earth", 0 ) );

        // Define propagator settings
        TranslationalPropagatorType propagatorType = encke;
        std::shared_ptr< TranslationalStatePropagatorSettings< double > > propagatorSettings =
                std::make_shared< TranslationalStatePropagatorSettings< double > >
                ( centralBodies, accelerationModelMap, bodiesToPropagate, systemInitialState, simulationEndEpoch, propagatorType,
                  std::make_shared< DependentVariableSaveSettings >( dependentVariables ) );

        // Define integrator settings
        std::shared_ptr< IntegratorSettings< > > integratorSettings =
                std::make_shared< RungeKuttaVariableStepSizeSettings< > >
                ( 0.0, fixedStepSize,
                  RungeKuttaCoefficients::rungeKuttaFehlberg78, 1.0E-4, 3600.0, 1.0E-14, 1.0E-14 );

        // Create simulation object and propagate dynamics.
        SingleArcDynamicsSimulator< > dynamicsSimulator(
                    bodyMap, integratorSettings, propagatorSettings, true, false, false );
        std::map< double, Eigen::VectorXd > integrationResult = dynamicsSimulator.getEquationsOfMotionNumericalSolution( );
        std::map< double, Eigen::VectorXd > dependentVariableResult = dynamicsSimulator.getDependentVariableHistory( );

        // Compute multiplier used in tests
        double testMultiplier = 1.0;
        if( testCase == 2 )
        {
            testMultiplier = std::sqrt( 2.0 );
        }

        Eigen::Vector3d vectorRInRswFrame;
        Eigen::Vector3d vectorVInRswFrame;
        Eigen::Vector3d vectorWInRswFrame;

        // Compare computed and expected empirical acceleration and its properties
        for( std::map< double, Eigen::VectorXd >::const_iterator resultIterator = integrationResult.begin( );
             resultIterator != integrationResult.end( ); resultIterator++ )
        {
            Eigen::Vector3d totalEmpiricalAcceleration =
                    dependentVariableResult.at( resultIterator->first ).segment( 0, 3 );

            // Check expected norm of empirical acceleration
            BOOST_CHECK_CLOSE_FRACTION( totalEmpiricalAcceleration.norm( ), testMultiplier * empiricalAccelerationNorm,
                                        8.0 * std::numeric_limits< double >::epsilon( ) );
            Eigen::Matrix3d currentRotationMatrix =
                    reference_frames::getInertialToRswSatelliteCenteredFrameRotationMatrix( resultIterator->second );
            Eigen::Vector3d totalEmpiricalAccelerationInRswFrame = currentRotationMatrix * totalEmpiricalAcceleration;

            // Compute vector directions in RSW frame
            vectorRInRswFrame = currentRotationMatrix * resultIterator->second.segment( 0, 3 );
            vectorVInRswFrame = currentRotationMatrix * resultIterator->second.segment( 3, 3 );
            vectorWInRswFrame = currentRotationMatrix * ( Eigen::Vector3d( resultIterator->second.segment( 0, 3 ) ).cross(
                                                       Eigen::Vector3d( resultIterator->second.segment( 3, 3 ) ) ) );

            // Check direction of vectors
            BOOST_CHECK_EQUAL( vectorRInRswFrame( 0 ) > 0, true );
            BOOST_CHECK_EQUAL( vectorVInRswFrame( 1 ) > 0, true );
            BOOST_CHECK_EQUAL( vectorWInRswFrame( 2 ) > 0, true );

            // Check direction of R vector in RSW frame
            BOOST_CHECK_EQUAL( vectorRInRswFrame( 0 ) / ( 10.0 * std::numeric_limits< double >::epsilon( ) ) >
                               std::fabs( vectorRInRswFrame( 1 ) ), true );
            BOOST_CHECK_EQUAL( vectorRInRswFrame( 0 ) / ( 10.0 * std::numeric_limits< double >::epsilon( ) ) >
                               std::fabs( vectorRInRswFrame( 2 ) ), true );

            // Check direction of V vector in RSW frame
            BOOST_CHECK_EQUAL( vectorVInRswFrame( 1 ) / 5.0 > std::fabs( vectorVInRswFrame( 0 ) ) , true );
            BOOST_CHECK_EQUAL( vectorVInRswFrame( 1 ) / ( 10.0 * std::numeric_limits< double >::epsilon( ) ) >
                               std::fabs( vectorVInRswFrame( 2 ) ), true );

            // Check direction of W vector in RSW frame
            BOOST_CHECK_EQUAL( vectorWInRswFrame( 2 ) / ( 10.0 * std::numeric_limits< double >::epsilon( ) ) >
                               std::fabs( vectorWInRswFrame( 0 ) ), true );
            BOOST_CHECK_EQUAL( vectorWInRswFrame( 2 ) / ( 10.0 * std::numeric_limits< double >::epsilon( ) ) >
                               std::fabs( vectorWInRswFrame( 1 ) ), true );

            // Compute expected empirical acceleration
            Eigen::VectorXd currentKeplerianState = convertCartesianToKeplerianElements(
                        Eigen::Vector6d( resultIterator->second ), earthGravitationalParameter );
            Eigen::Vector3d expectedAccelerationInRswFrame;
            if( testCase == 0 )
            {
                expectedAccelerationInRswFrame = empiricalAccelerationNorm * Eigen::Vector3d::UnitX( );
            }
            else if( testCase == 1 )
            {
                expectedAccelerationInRswFrame =
                        empiricalAccelerationNorm * Eigen::Vector3d::UnitX( ) * std::sin( currentKeplerianState( 5 ) ) +
                        empiricalAccelerationNorm * Eigen::Vector3d::UnitY( ) * std::cos( currentKeplerianState( 5 ) );
            }
            else if( testCase == 2 )
            {
                expectedAccelerationInRswFrame =
                        empiricalAccelerationNorm * Eigen::Vector3d::UnitX( ) +
                        empiricalAccelerationNorm * Eigen::Vector3d::UnitY( ) * std::sin( currentKeplerianState( 5 ) ) +
                        empiricalAccelerationNorm * Eigen::Vector3d::UnitZ( ) * std::cos( currentKeplerianState( 5 ) );
            }

            // Compare expected and computed empirical acceleration
            for( unsigned int i = 0; i < 3; i++ )
            {
                BOOST_CHECK_SMALL( std::fabs( expectedAccelerationInRswFrame( i ) - totalEmpiricalAccelerationInRswFrame( i ) ),
                    8.0 * std::numeric_limits< double >::epsilon( ) * empiricalAccelerationNorm );
            }
        }
    }

}

BOOST_AUTO_TEST_SUITE_END( )

}

}


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

  Program:   Visualization Toolkit
  Module:    $RCSfile: vtkBoostBiconnectedComponents.cxx,v $

  Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen
  All rights reserved.
  See Copyright.txt or http://www.kitware.com/Copyright.htm for details.

     This software is distributed WITHOUT ANY WARRANTY; without even
     the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
     PURPOSE.  See the above copyright notice for more information.

=========================================================================*/
/*----------------------------------------------------------------------------
 Copyright (c) Sandia Corporation
 See Copyright.txt or http://www.paraview.org/HTML/Copyright.html for details.
----------------------------------------------------------------------------*/
#include "vtkBoostBiconnectedComponents.h"

#include <vtkCellData.h>
#include <vtkInformation.h>
#include <vtkInformationVector.h>
#include <vtkObjectFactory.h>
#include <vtkPointData.h>
#include <vtkIdTypeArray.h>

#include "vtkGraph.h"
#include "vtkGraphToBoostAdapter.h"
#include <boost/graph/biconnected_components.hpp>
#include <boost/vector_property_map.hpp>
#include <vtksys/stl/vector>
#include <vtksys/stl/utility>

using namespace boost;
using vtksys_stl::vector;
using vtksys_stl::pair;

vtkCxxRevisionMacro(vtkBoostBiconnectedComponents, "$Revision: 1.3 $");
vtkStandardNewMacro(vtkBoostBiconnectedComponents);

vtkBoostBiconnectedComponents::vtkBoostBiconnectedComponents()
{
}

vtkBoostBiconnectedComponents::~vtkBoostBiconnectedComponents()
{
}

int vtkBoostBiconnectedComponents::RequestData(
  vtkInformation *vtkNotUsed(request),
  vtkInformationVector **inputVector,
  vtkInformationVector *outputVector)
{
  // get the info objects
  vtkInformation *inInfo = inputVector[0]->GetInformationObject(0);
  vtkInformation *outInfo = outputVector->GetInformationObject(0);

  // get the input and ouptut
  vtkGraph *input = vtkGraph::SafeDownCast(
    inInfo->Get(vtkDataObject::DATA_OBJECT()));
  vtkGraph *output = vtkGraph::SafeDownCast(
    outInfo->Get(vtkDataObject::DATA_OBJECT()));

  // Send the data to output.
  output->ShallowCopy(input);

  // Biconnected components only works on undirected graphs,
  // so treat the graph as undirected.
  output->SetDirected(false);

  // Run the boost algorithm
  vtkBoostUndirectedGraph g(output);
  typedef vector_property_map<vtkIdType> PMap;
  PMap p;
  vtkGraphEdgePropertyMapHelper<PMap> helper(p);
  vector<vtkIdType> artPoints;
  pair<size_t, vtksys_stl::back_insert_iterator<vector<vtkIdType> > >
    res(0, vtksys_stl::back_inserter(artPoints));

  // Initialize the helper pmap to all -1
  for (vtkIdType e = 0; e < output->GetNumberOfEdges(); e++)
    {
    helper.pmap[e] = -1;
    }

  // Call BGL biconnected_components
  res = biconnected_components(
    g, helper, vtksys_stl::back_inserter(artPoints), vtkGraphIndexMap());
  size_t numComp = res.first;

  vtkIdTypeArray* edgeComps = vtkIdTypeArray::New();
  edgeComps->SetName("biconnected component");
  edgeComps->Allocate(output->GetNumberOfEdges());
  for (vtkIdType e = 0; e < output->GetNumberOfEdges(); e++)
    {
    edgeComps->InsertNextValue(helper.pmap[e]);
    }
  //edgeComps->SetArray(&(helper.pmap[0]), output->GetNumberOfEdges(), 1);
  output->GetEdgeData()->AddArray(edgeComps);
  edgeComps->Delete();

  // Assign component values to vertices based on the first edge
  // If isolated, assign a new value
  vtkIdTypeArray* vertComps = vtkIdTypeArray::New();
  vertComps->SetName("biconnected component");
  vertComps->Allocate(output->GetNumberOfVertices());
  for (vtkIdType u = 0; u < output->GetNumberOfVertices(); u++)
    {
    const vtkIdType* edges;
    vtkIdType nedges;
    output->GetIncidentEdges(u, nedges, edges);
    int comp;
    if (nedges > 0)
      {
      int edgeIndex =0;
      int value = edgeComps->GetValue(edges[edgeIndex]);
      while( (value == -1) && (edgeIndex < nedges-1))
        {
        edgeIndex++;
        value = edgeComps->GetValue(edges[edgeIndex]);
        }
      comp = value;
      }
    else
      {
      comp = numComp;
      numComp++;
      }
    vertComps->InsertNextValue(comp);
    }

  // Articulation points belong to multiple biconnected components.
  // Indicate these by assigning a component value of -1.
  // It belongs to whatever components its incident edges belong to.
  vector<vtkIdType>::size_type i;
  for (i = 0; i < artPoints.size(); i++)
    {
    vertComps->SetValue(artPoints[i], -1);
    }

  output->GetVertexData()->AddArray(vertComps);
  vertComps->Delete();

  // Set the graph back to the correct directedness
  output->SetDirected(input->GetDirected());

  return 1;
}

void vtkBoostBiconnectedComponents::PrintSelf(ostream& os, vtkIndent indent)
{
  this->Superclass::PrintSelf(os, indent);
}


/*
 *  sinker.cpp
 *  pybg
 *
 *  Created by BART MOSLEY on 8/12/12.
 *  Copyright 2012 BG Research LLC. All rights reserved.
 *
 */

#include <bg/bondgeek.hpp>

#include <iostream>

#include <boost/timer.hpp>

using namespace std;
using namespace QuantLib;
using namespace bondgeek;

#if defined(QL_ENABLE_SESSIONS)
namespace QuantLib {
    Integer sessionId() { return 0; }
}
#endif

void showCashFlows(Bond &bnd)
{
    Leg fixedLeg = bnd.cashflows();

    cout << endl << "Fixed Leg: " << endl;
    Leg::iterator fxIt;
    Date cfDate;
    double cfAmt;
    for (fxIt=fixedLeg.begin(); fxIt < fixedLeg.end(); fxIt++)
    {
        cfDate = (*fxIt)->date();
        cfAmt = (*fxIt)->amount();
        cout << cfDate << " | " << cfAmt << endl;
    }

}

void showAssetSwapCashFlows(AssetSwap &aswap)
{
    Leg fixedLeg = aswap.bondLeg();
    Leg floatLeg = aswap.floatingLeg();

    Date cfDate;
    double cfAmt;

    cout << endl << "Fixed Leg: " << endl;
    Leg::iterator fxIt;
    for (fxIt=fixedLeg.begin(); fxIt < fixedLeg.end(); fxIt++)
    {
        cfDate = (*fxIt)->date();
        cfAmt = (*fxIt)->amount();
        cout << cfDate << " | " << cfAmt << endl;
    }
    cout << endl << "Floating Leg: " << endl;
    Leg::iterator flIt;
    for (flIt=floatLeg.begin(); flIt < floatLeg.end(); flIt++)
    {
        cfDate = (*flIt)->date();
        cfAmt = (*flIt)->amount();
        cout << cfDate << " | " << cfAmt << endl;
    }

}

void showRedemptions(Bond &bnd)
{
    Leg fixedLeg = bnd.redemptions();

    cout << endl << "Redemptions: " << endl;
    Leg::iterator fxIt;
    Date cfDate;
    double cfAmt;
    for (fxIt=fixedLeg.begin(); fxIt < fixedLeg.end(); fxIt++)
    {
        cfDate = (*fxIt)->date();
        cfAmt = (*fxIt)->amount();
        cout << cfDate << " | " << cfAmt << endl;
    }

}

void showNotionals(Bond &bnd)
{
    std::vector<Real> bnd_notionals = bnd.notionals();

    cout << endl << "Notionals: " << endl;
    std::vector<Real>::iterator it;
    int notional_count = 0;
    for (it=bnd_notionals.begin(); it < bnd_notionals.end(); it++) {
        notional_count++;
        cout << notional_count << ": " << *it << endl;
    }

}

int main (int argc, char * const argv[])
{

    Date today = Date(16,October,2007);
    Settings::instance().evaluationDate() = today;

    cout << endl << "Today: " << today << endl;
    cout << "Price with a bg curve " << endl;
    string depotenors[] = {"1W", "1M", "3M", "6M", "9M"};
    double depospots[] = {0.0018400, 0.0023050, 0.0041825,
                          0.0070765, 0.0086780};
    string swaptenors[] = {"2y", "3y", "5y", "10Y",
                           "15Y", "20Y", "30Y"};
    double swapspots[] = {.0040100, 0.0042600, 0.0077300, 0.0106595,
                          0.0211555, 0.02310205, 0.0247007};

    cout << "Test with new curve " << endl;
    RateHelperCurve usdLiborCurve = RateHelperCurve(USDLiborCurve("3M"));
    usdLiborCurve.update(depotenors,
                         depospots,
                         5,
                         swaptenors,
                         swapspots,
                         7,
                         today);

    boost::shared_ptr<PricingEngine> discEngine = \
    createPriceEngine<DiscountingBondEngine>(
                                             usdLiborCurve.discountingTermStructure()
                                             );

    // set up the callable bond
    Real coupon = .06;
    Date maturity(15, September,2012);
    Date maturity1(15, September, 2011);
    Date maturity0(15, September, 2010);

    Date dated(16,September,2004);

    Natural settlementDays = 3;  // Bloomberg OAS1 settle is Oct 19, 2007
    Frequency frequency = Semiannual;

    Calendar bondCalendar = UnitedStates(UnitedStates::GovernmentBond);
    DayCounter bondDayCounter = ActualActual(ActualActual::Bond);

    Real faceAmount = 100.0; // Notional amount
    Real redemption = 100.0; // Amount paid on redemption

    BusinessDayConvention accrualConvention = Unadjusted;
    BusinessDayConvention paymentConvention = Unadjusted;

    cout << endl << endl
    << "Generic Bond"
    << endl;
    SinkingFundBond bnd(coupon,
                        maturity,
                        std::vector<Real>(1, faceAmount),
                        Annual,
                        dated,
                        bondCalendar,
                        settlementDays,
                        bondDayCounter,
                        frequency,
                        redemption,
                        faceAmount,
                        accrualConvention,
                        paymentConvention
                        );

    showCashFlows(bnd);

    cout << "\n\none year shorter" << endl;
    SinkingFundBond bnd0(coupon,
                         maturity0,
                         std::vector<Real>(1, faceAmount),
                         Annual,
                         dated,
                         bondCalendar,
                         settlementDays,
                         bondDayCounter,
                         frequency,
                         redemption,
                         faceAmount,
                         accrualConvention,
                         paymentConvention
                         );

    showCashFlows(bnd0);

    cout << "\n\ntwo years shorter" << endl;
    SinkingFundBond bnd1(coupon,
                         maturity1,
                         std::vector<Real>(1, faceAmount),
                         Annual,
                         dated,
                         bondCalendar,
                         settlementDays,
                         bondDayCounter,
                         frequency,
                         redemption,
                         faceAmount,
                         accrualConvention,
                         paymentConvention
                         );
    showCashFlows(bnd1);

    /* Sinkfing fund amortization:

     notionals start at par on the dated date and end at zero on maturity.
     redemptions are the prices redemption prices (pct of par, e.g. 101.)

     */

    double sf_sch[] = {
        40000, 40000, 40000, 40000, 40000, 40000,
        40000, 40000, 40000, 40000, 40000, 40000,
        40000
    };
    int sf_num = 3;
    Frequency sf_freq = Annual;

    std::vector<double> sf_bal;
    sf_bal.assign(sf_sch, sf_sch+sf_num);

    Real rval = 100.;

    cout << endl << endl
    << "Amortizing Generic Bond "
    << endl;
    SinkingFundBond bnd2(coupon,
                         maturity,
                         sf_bal,
                         sf_freq,
                         dated,
                         bondCalendar,
                         settlementDays,
                         bondDayCounter,
                         frequency,
                         rval,
                         faceAmount,
                         accrualConvention,
                         paymentConvention
                         );

    // show the results
    showCashFlows(bnd2);

    bnd0.setPricingEngine(discEngine);
    bnd1.setPricingEngine(discEngine);
    bnd.setPricingEngine(discEngine);
    bnd2.setPricingEngine(discEngine);

    double prc0 = bnd0.cleanPrice();
    double prc1 = bnd1.cleanPrice();
    double prc = bnd.cleanPrice();
    double prc2 = bnd2.cleanPrice();

    cout << endl << "Pricing component bonds" << endl
    << "priced0: " << prc0 << endl
    << "priced1: " << prc1 << endl
    << "priced: " << prc << endl
    << "avg price: " << (prc0+prc1+prc)/3. << endl
    << endl
    << "priced sinking fund bond: " << prc2 << endl;
    cout << "cf yield: " << bnd.toYield(prc2) << endl;

    return 0;
}