diff --git a/cmake/unit_testing.cmake b/cmake/unit_testing.cmake index eb73f6e..eef9374 100644 --- a/cmake/unit_testing.cmake +++ b/cmake/unit_testing.cmake @@ -78,8 +78,11 @@ add_subdirectory( src/scion/math/LinearLinearTable/test ) add_subdirectory( src/scion/math/LinearLinearTableFunction/test ) add_subdirectory( src/scion/math/HistogramTable/test ) add_subdirectory( src/scion/math/LogLinearTable/test ) +add_subdirectory( src/scion/math/LogLinearTableFunction/test ) add_subdirectory( src/scion/math/LinearLogTable/test ) +add_subdirectory( src/scion/math/LinearLogTableFunction/test ) add_subdirectory( src/scion/math/LogLogTable/test ) +add_subdirectory( src/scion/math/LogLogTableFunction/test ) add_subdirectory( src/scion/math/InterpolationTable/test ) add_subdirectory( src/scion/math/ChebyshevSeries/test ) add_subdirectory( src/scion/math/ChebyshevApproximation/test ) diff --git a/src/scion/math/LinearLinearTableFunction.hpp b/src/scion/math/LinearLinearTableFunction.hpp index 6d11d5b..e98fb19 100644 --- a/src/scion/math/LinearLinearTableFunction.hpp +++ b/src/scion/math/LinearLinearTableFunction.hpp @@ -18,8 +18,8 @@ namespace math { * @class * @brief Tabulated x,f(y) data with linear-linear interpolation (f(y) is linear in x) * - * The LinearLinearTable is templatised on the container type used for the - * x and y values in addition to the actual x and y types. This allows us to + * The LinearLinearTableFunction is templatised on the actual x, y and z types, the function + * type F and the container type used for the x values and the functions. This allows us to * use something like utility::IteratorView instead of std::vector. */ template < typename X, typename Y, typename Z, typename F, diff --git a/src/scion/math/LinearLogTableFunction.hpp b/src/scion/math/LinearLogTableFunction.hpp new file mode 100644 index 0000000..1da89ea --- /dev/null +++ b/src/scion/math/LinearLogTableFunction.hpp @@ -0,0 +1,75 @@ +#ifndef NJOY_SCION_MATH_LINEARLOGTABLEFUNCTION +#define NJOY_SCION_MATH_LINEARLOGTABLEFUNCTION + +// system includes +#include + +// other includes +#include "scion/interpolation/InterpolationType.hpp" +#include "scion/interpolation/LinearLogarithmic.hpp" +#include "scion/linearisation/ToleranceConvergence.hpp" +#include "scion/math/SingleTableFunctionBase.hpp" + +namespace njoy { +namespace scion { +namespace math { + + /** + * @class + * @brief Tabulated x,f(y) data with linear-log interpolation (f(y) is linear in ln(x)) + * + * The LinearLogTableFunction is templatised on the actual x, y and z types, the function + * type F and the container type used for the x values and the functions. This allows us to + * use something like utility::IteratorView instead of std::vector. + */ + template < typename X, typename Y, typename Z, typename F, + typename XContainer = std::vector< X >, + typename FContainer = std::vector< F > > + class LinearLogTableFunction : + public SingleTableFunctionBase< LinearLogTableFunction< X, Y, Z, F, XContainer, FContainer >, + interpolation::LinearLogarithmic, X, Y, Z, F, + XContainer, FContainer > { + + /* friend declarations */ + friend class SingleTableFunctionBase< LinearLogTableFunction< X, Y, Z, F, XContainer, FContainer >, + interpolation::LinearLogarithmic, X, Y, Z, F, + XContainer, FContainer >; + + /* type aliases */ + using Parent = SingleTableFunctionBase< LinearLogTableFunction< X, Y, Z, F, XContainer, FContainer >, + interpolation::LinearLogarithmic, X, Y, Z, F, + XContainer, FContainer >; + + /* fields */ + + /* auxiliary function */ + + /* interface implementation functions */ + + /** + * @brief Return the interpolation type + */ + static constexpr interpolation::InterpolationType type() noexcept { + + return interpolation::InterpolationType::LinearLog; + } + + public: + + /* constructor */ + #include "scion/math/LinearLogTableFunction/src/ctor.hpp" + + /* methods */ + + using Parent::interpolation; + using Parent::x; + using Parent::f; + using Parent::numberPoints; + using Parent::operator(); + }; + +} // math namespace +} // scion namespace +} // njoy namespace + +#endif diff --git a/src/scion/math/LinearLogTableFunction/src/ctor.hpp b/src/scion/math/LinearLogTableFunction/src/ctor.hpp new file mode 100644 index 0000000..9886030 --- /dev/null +++ b/src/scion/math/LinearLogTableFunction/src/ctor.hpp @@ -0,0 +1,8 @@ +/** + * @brief Constructor + * + * @param x the x values of the tabulated data + * @param f the f(y) functions of the tabulated data + */ +LinearLogTableFunction( XContainer x, FContainer f ) : + Parent( std::move( x ), std::move( f ) ) {} diff --git a/src/scion/math/LinearLogTableFunction/test/CMakeLists.txt b/src/scion/math/LinearLogTableFunction/test/CMakeLists.txt new file mode 100644 index 0000000..70289a1 --- /dev/null +++ b/src/scion/math/LinearLogTableFunction/test/CMakeLists.txt @@ -0,0 +1 @@ +add_cpp_test( math.LinearLogTableFunction LinearLogTableFunction.test.cpp ) diff --git a/src/scion/math/LinearLogTableFunction/test/LinearLogTableFunction.test.cpp b/src/scion/math/LinearLogTableFunction/test/LinearLogTableFunction.test.cpp new file mode 100644 index 0000000..ae687a0 --- /dev/null +++ b/src/scion/math/LinearLogTableFunction/test/LinearLogTableFunction.test.cpp @@ -0,0 +1,249 @@ +// include Catch2 +#include +#include +using Catch::Matchers::WithinRel; + +// what we are testing +#include "scion/math/InterpolationTable.hpp" +#include "scion/math/LinearLogTableFunction.hpp" + +// other includes +#include "utility/IteratorView.hpp" + +// convenience typedefs +using namespace njoy::scion; +template < typename X, typename Y = X > +using InterpolationTable = math::InterpolationTable< X, Y >; +template < typename X, typename Y = X, typename Z = X, + typename F = InterpolationTable< X >, + typename XContainer = std::vector< X >, + typename FContainer = std::vector< F > > +using LinearLogTableFunction = math::LinearLogTableFunction< X, Y, Z, F, XContainer, FContainer >; +using InterpolationType = interpolation::InterpolationType; + +SCENARIO( "LinearLogTableFunction" ) { + + GIVEN( "tabulated x,f(y) data" ) { + + WHEN( "the data is given explicitly using vectors" ) { + + const std::vector< double > x = { 1., 2., 3., 4. }; + const std::vector< InterpolationTable< double > > f = { + + { { -1., +1. }, { 0.5, 0.5 } }, + { { -1., 0., +1. }, { 0.49, 0.5, 0.51 } }, + { { -1., 0., +1. }, { 0.4, 0.5, 0.6 } }, + { { -1., +1. }, { 0.1, 0.9 } } + }; + + LinearLogTableFunction< double > chunk( std::move( x ), std::move( f ) ); + + THEN( "a LinearLogTable can be constructed and members can be tested" ) { + + CHECK( InterpolationType::LinearLog == chunk.interpolation() ); + CHECK( 4 == chunk.numberPoints() ); + CHECK( 4 == chunk.x().size() ); + CHECK( 4 == chunk.f().size() ); + CHECK_THAT( 1., WithinRel( chunk.x()[0] ) ); + CHECK_THAT( 2., WithinRel( chunk.x()[1] ) ); + CHECK_THAT( 3., WithinRel( chunk.x()[2] ) ); + CHECK_THAT( 4., WithinRel( chunk.x()[3] ) ); + CHECK( 2 == chunk.f()[0].x().size() ); + CHECK( 2 == chunk.f()[0].y().size() ); + CHECK( 3 == chunk.f()[1].x().size() ); + CHECK( 3 == chunk.f()[1].y().size() ); + CHECK( 3 == chunk.f()[2].x().size() ); + CHECK( 3 == chunk.f()[2].y().size() ); + CHECK( 2 == chunk.f()[3].x().size() ); + CHECK( 2 == chunk.f()[3].y().size() ); + CHECK_THAT( -1. , WithinRel( chunk.f()[0].x()[0] ) ); + CHECK_THAT( 1. , WithinRel( chunk.f()[0].x()[1] ) ); + CHECK_THAT( 0.5 , WithinRel( chunk.f()[0].y()[0] ) ); + CHECK_THAT( 0.5 , WithinRel( chunk.f()[0].y()[1] ) ); + CHECK_THAT( -1. , WithinRel( chunk.f()[1].x()[0] ) ); + CHECK_THAT( 0. , WithinRel( chunk.f()[1].x()[1] ) ); + CHECK_THAT( 1. , WithinRel( chunk.f()[1].x()[2] ) ); + CHECK_THAT( 0.49, WithinRel( chunk.f()[1].y()[0] ) ); + CHECK_THAT( 0.5 , WithinRel( chunk.f()[1].y()[1] ) ); + CHECK_THAT( 0.51, WithinRel( chunk.f()[1].y()[2] ) ); + CHECK_THAT( -1. , WithinRel( chunk.f()[2].x()[0] ) ); + CHECK_THAT( 0. , WithinRel( chunk.f()[2].x()[1] ) ); + CHECK_THAT( 1. , WithinRel( chunk.f()[2].x()[2] ) ); + CHECK_THAT( 0.4 , WithinRel( chunk.f()[2].y()[0] ) ); + CHECK_THAT( 0.5 , WithinRel( chunk.f()[2].y()[1] ) ); + CHECK_THAT( 0.6 , WithinRel( chunk.f()[2].y()[2] ) ); + CHECK_THAT( -1. , WithinRel( chunk.f()[3].x()[0] ) ); + CHECK_THAT( 1. , WithinRel( chunk.f()[3].x()[1] ) ); + CHECK_THAT( 0.1, WithinRel( chunk.f()[3].y()[0] ) ); + CHECK_THAT( 0.9, WithinRel( chunk.f()[3].y()[1] ) ); + } // THEN + + THEN( "a LinearLogTable can be evaluated" ) { + + // values of x in the x grid + CHECK_THAT( 0.5 , WithinRel( chunk( 1., -0.5 ) ) ); + CHECK_THAT( 0.495, WithinRel( chunk( 2., -0.5 ) ) ); + CHECK_THAT( 0.45 , WithinRel( chunk( 3., -0.5 ) ) ); + CHECK_THAT( 0.3 , WithinRel( chunk( 4., -0.5 ) ) ); + + // values of x outside the x grid + CHECK_THAT( 0., WithinRel( chunk( 0., -0.5 ) ) ); + CHECK_THAT( 0., WithinRel( chunk( 5., -0.5 ) ) ); + + // values of x inside the x grid + CHECK_THAT( 0.49707518749639, WithinRel( chunk( 1.5, -0.5 ) ) ); + CHECK_THAT( 0.47023471290541, WithinRel( chunk( 2.5, -0.5 ) ) ); + CHECK_THAT( 0.36962445981765, WithinRel( chunk( 3.5, -0.5 ) ) ); + } // THEN + } // WHEN + + WHEN( "the data is given explicitly using iterator views" ) { + + using XView = njoy::utility::IteratorView< std::vector< double >::const_iterator >; + using FView = njoy::utility::IteratorView< std::vector< InterpolationTable< double > >::const_iterator >; + + const std::vector< double > xvalues = { 1., 2., 3., 4. }; + const std::vector< InterpolationTable< double > > fvalues = { + + { { -1., +1. }, { 0.5, 0.5 } }, + { { -1., 0., +1. }, { 0.49, 0.5, 0.51 } }, + { { -1., 0., +1. }, { 0.4, 0.5, 0.6 } }, + { { -1., +1. }, { 0.1, 0.9 } } + }; + + + XView x = njoy::utility::make_view( xvalues ); + FView f = njoy::utility::make_view( fvalues ); + + LinearLogTableFunction< double, double, double, InterpolationTable< double >, + XView, FView > chunk( std::move( x ), std::move( f ) ); + + THEN( "a LinearLogTable can be constructed and members can be tested" ) { + + CHECK( InterpolationType::LinearLog == chunk.interpolation() ); + CHECK( 4 == chunk.numberPoints() ); + CHECK( 4 == chunk.x().size() ); + CHECK( 4 == chunk.f().size() ); + CHECK_THAT( 1., WithinRel( chunk.x()[0] ) ); + CHECK_THAT( 2., WithinRel( chunk.x()[1] ) ); + CHECK_THAT( 3., WithinRel( chunk.x()[2] ) ); + CHECK_THAT( 4., WithinRel( chunk.x()[3] ) ); + CHECK( 2 == chunk.f()[0].x().size() ); + CHECK( 2 == chunk.f()[0].y().size() ); + CHECK( 3 == chunk.f()[1].x().size() ); + CHECK( 3 == chunk.f()[1].y().size() ); + CHECK( 3 == chunk.f()[2].x().size() ); + CHECK( 3 == chunk.f()[2].y().size() ); + CHECK( 2 == chunk.f()[3].x().size() ); + CHECK( 2 == chunk.f()[3].y().size() ); + CHECK_THAT( -1. , WithinRel( chunk.f()[0].x()[0] ) ); + CHECK_THAT( 1. , WithinRel( chunk.f()[0].x()[1] ) ); + CHECK_THAT( 0.5 , WithinRel( chunk.f()[0].y()[0] ) ); + CHECK_THAT( 0.5 , WithinRel( chunk.f()[0].y()[1] ) ); + CHECK_THAT( -1. , WithinRel( chunk.f()[1].x()[0] ) ); + CHECK_THAT( 0. , WithinRel( chunk.f()[1].x()[1] ) ); + CHECK_THAT( 1. , WithinRel( chunk.f()[1].x()[2] ) ); + CHECK_THAT( 0.49, WithinRel( chunk.f()[1].y()[0] ) ); + CHECK_THAT( 0.5 , WithinRel( chunk.f()[1].y()[1] ) ); + CHECK_THAT( 0.51, WithinRel( chunk.f()[1].y()[2] ) ); + CHECK_THAT( -1. , WithinRel( chunk.f()[2].x()[0] ) ); + CHECK_THAT( 0. , WithinRel( chunk.f()[2].x()[1] ) ); + CHECK_THAT( 1. , WithinRel( chunk.f()[2].x()[2] ) ); + CHECK_THAT( 0.4 , WithinRel( chunk.f()[2].y()[0] ) ); + CHECK_THAT( 0.5 , WithinRel( chunk.f()[2].y()[1] ) ); + CHECK_THAT( 0.6 , WithinRel( chunk.f()[2].y()[2] ) ); + CHECK_THAT( -1. , WithinRel( chunk.f()[3].x()[0] ) ); + CHECK_THAT( 1. , WithinRel( chunk.f()[3].x()[1] ) ); + CHECK_THAT( 0.1, WithinRel( chunk.f()[3].y()[0] ) ); + CHECK_THAT( 0.9, WithinRel( chunk.f()[3].y()[1] ) ); + } // THEN + + THEN( "a LinearLogTable can be evaluated" ) { + + // values of x in the x grid + CHECK_THAT( 0.5 , WithinRel( chunk( 1., -0.5 ) ) ); + CHECK_THAT( 0.495, WithinRel( chunk( 2., -0.5 ) ) ); + CHECK_THAT( 0.45 , WithinRel( chunk( 3., -0.5 ) ) ); + CHECK_THAT( 0.3 , WithinRel( chunk( 4., -0.5 ) ) ); + + // values of x outside the x grid + CHECK_THAT( 0., WithinRel( chunk( 0., -0.5 ) ) ); + CHECK_THAT( 0., WithinRel( chunk( 5., -0.5 ) ) ); + + // values of x inside the x grid + CHECK_THAT( 0.49707518749639, WithinRel( chunk( 1.5, -0.5 ) ) ); + CHECK_THAT( 0.47023471290541, WithinRel( chunk( 2.5, -0.5 ) ) ); + CHECK_THAT( 0.36962445981765, WithinRel( chunk( 3.5, -0.5 ) ) ); + } // THEN + } // WHEN + } // GIVEN + + GIVEN( "invalid data for a LinearLogTable object" ) { + + WHEN( "there are not enough values in the x or y grid" ) { + + std::vector< double > xempty = {}; + std::vector< double > xone = { 1. }; + std::vector< InterpolationTable< double > > fempty = {}; + std::vector< InterpolationTable< double > > fone = { { { 1., 2. }, { 3., 4. } } }; + + THEN( "an exception is thrown" ) { + + CHECK_THROWS( LinearLogTableFunction< double >( xempty, fempty ) ); + CHECK_THROWS( LinearLogTableFunction< double >( xone, fone ) ); + CHECK_THROWS( LinearLogTableFunction< double >( xempty, fone ) ); + CHECK_THROWS( LinearLogTableFunction< double >( xone, fempty ) ); + } // THEN + } // WHEN + + WHEN( "the x and y grid do not have the same number of points" ) { + + std::vector< double > x = { 1., 2., 3., 4. }; + std::vector< InterpolationTable< double > > f = { + + { { -1., +1. }, { 0.5, 0.5 } }, + { { -1., 0., +1. }, { 0.49, 0.5, 0.51 } }, + { { -1., 0., +1. }, { 0.4, 0.5, 0.6 } } + }; + + THEN( "an exception is thrown" ) { + + CHECK_THROWS( LinearLogTableFunction< double >( std::move( x ), std::move( f ) ) ); + } // THEN + } // WHEN + + WHEN( "the x grid is not sorted" ) { + + std::vector< double > x = { 1., 3., 2., 4. }; + std::vector< InterpolationTable< double > > f = { + + { { -1., +1. }, { 0.5, 0.5 } }, + { { -1., 0., +1. }, { 0.49, 0.5, 0.51 } }, + { { -1., 0., +1. }, { 0.4, 0.5, 0.6 } }, + { { -1., +1. }, { 0.1, 0.9 } } + }; + + THEN( "an exception is thrown" ) { + + CHECK_THROWS( LinearLogTableFunction< double >( std::move( x ), std::move( f ) ) ); + } // THEN + } // WHEN + + WHEN( "the x grid has a duplicate point" ) { + + std::vector< double > x = { 1., 2., 2., 4. }; + std::vector< InterpolationTable< double > > f = { + + { { -1., +1. }, { 0.5, 0.5 } }, + { { -1., 0., +1. }, { 0.49, 0.5, 0.51 } }, + { { -1., 0., +1. }, { 0.4, 0.5, 0.6 } }, + { { -1., +1. }, { 0.1, 0.9 } } + }; + + THEN( "an exception is thrown" ) { + + CHECK_THROWS( LinearLogTableFunction< double >( std::move( x ), std::move( f ) ) ); + } // THEN + } // WHEN + } // GIVEN +} // SCENARIO diff --git a/src/scion/math/LogLinearTableFunction.hpp b/src/scion/math/LogLinearTableFunction.hpp new file mode 100644 index 0000000..85a0620 --- /dev/null +++ b/src/scion/math/LogLinearTableFunction.hpp @@ -0,0 +1,75 @@ +#ifndef NJOY_SCION_MATH_LogLinearTABLEFUNCTION +#define NJOY_SCION_MATH_LogLinearTABLEFUNCTION + +// system includes +#include + +// other includes +#include "scion/interpolation/InterpolationType.hpp" +#include "scion/interpolation/LogarithmicLinear.hpp" +#include "scion/linearisation/ToleranceConvergence.hpp" +#include "scion/math/SingleTableFunctionBase.hpp" + +namespace njoy { +namespace scion { +namespace math { + + /** + * @class + * @brief Tabulated x,f(y) data with log-log interpolation (ln(f(y)) is linear in ln(x)) + * + * The LogLinearTableFunction is templatised on the actual x, y and z types, the function + * type F and the container type used for the x values and the functions. This allows us to + * use something like utility::IteratorView instead of std::vector. + */ + template < typename X, typename Y, typename Z, typename F, + typename XContainer = std::vector< X >, + typename FContainer = std::vector< F > > + class LogLinearTableFunction : + public SingleTableFunctionBase< LogLinearTableFunction< X, Y, Z, F, XContainer, FContainer >, + interpolation::LogarithmicLinear, X, Y, Z, F, + XContainer, FContainer > { + + /* friend declarations */ + friend class SingleTableFunctionBase< LogLinearTableFunction< X, Y, Z, F, XContainer, FContainer >, + interpolation::LogarithmicLinear, X, Y, Z, F, + XContainer, FContainer >; + + /* type aliases */ + using Parent = SingleTableFunctionBase< LogLinearTableFunction< X, Y, Z, F, XContainer, FContainer >, + interpolation::LogarithmicLinear, X, Y, Z, F, + XContainer, FContainer >; + + /* fields */ + + /* auxiliary function */ + + /* interface implementation functions */ + + /** + * @brief Return the interpolation type + */ + static constexpr interpolation::InterpolationType type() noexcept { + + return interpolation::InterpolationType::LogLinear; + } + + public: + + /* constructor */ + #include "scion/math/LogLinearTableFunction/src/ctor.hpp" + + /* methods */ + + using Parent::interpolation; + using Parent::x; + using Parent::f; + using Parent::numberPoints; + using Parent::operator(); + }; + +} // math namespace +} // scion namespace +} // njoy namespace + +#endif diff --git a/src/scion/math/LogLinearTableFunction/src/ctor.hpp b/src/scion/math/LogLinearTableFunction/src/ctor.hpp new file mode 100644 index 0000000..24fb4a1 --- /dev/null +++ b/src/scion/math/LogLinearTableFunction/src/ctor.hpp @@ -0,0 +1,8 @@ +/** + * @brief Constructor + * + * @param x the x values of the tabulated data + * @param f the f(y) functions of the tabulated data + */ +LogLinearTableFunction( XContainer x, FContainer f ) : + Parent( std::move( x ), std::move( f ) ) {} diff --git a/src/scion/math/LogLinearTableFunction/test/CMakeLists.txt b/src/scion/math/LogLinearTableFunction/test/CMakeLists.txt new file mode 100644 index 0000000..eb93008 --- /dev/null +++ b/src/scion/math/LogLinearTableFunction/test/CMakeLists.txt @@ -0,0 +1 @@ +add_cpp_test( math.LogLinearTableFunction LogLinearTableFunction.test.cpp ) diff --git a/src/scion/math/LogLinearTableFunction/test/LogLinearTableFunction.test.cpp b/src/scion/math/LogLinearTableFunction/test/LogLinearTableFunction.test.cpp new file mode 100644 index 0000000..6484d8a --- /dev/null +++ b/src/scion/math/LogLinearTableFunction/test/LogLinearTableFunction.test.cpp @@ -0,0 +1,249 @@ +// include Catch2 +#include +#include +using Catch::Matchers::WithinRel; + +// what we are testing +#include "scion/math/InterpolationTable.hpp" +#include "scion/math/LogLinearTableFunction.hpp" + +// other includes +#include "utility/IteratorView.hpp" + +// convenience typedefs +using namespace njoy::scion; +template < typename X, typename Y = X > +using InterpolationTable = math::InterpolationTable< X, Y >; +template < typename X, typename Y = X, typename Z = X, + typename F = InterpolationTable< X >, + typename XContainer = std::vector< X >, + typename FContainer = std::vector< F > > +using LogLinearTableFunction = math::LogLinearTableFunction< X, Y, Z, F, XContainer, FContainer >; +using InterpolationType = interpolation::InterpolationType; + +SCENARIO( "LogLinearTableFunction" ) { + + GIVEN( "tabulated x,f(y) data" ) { + + WHEN( "the data is given explicitly using vectors" ) { + + const std::vector< double > x = { 1., 2., 3., 4. }; + const std::vector< InterpolationTable< double > > f = { + + { { -1., +1. }, { 0.5, 0.5 } }, + { { -1., 0., +1. }, { 0.49, 0.5, 0.51 } }, + { { -1., 0., +1. }, { 0.4, 0.5, 0.6 } }, + { { -1., +1. }, { 0.1, 0.9 } } + }; + + LogLinearTableFunction< double > chunk( std::move( x ), std::move( f ) ); + + THEN( "a LogLinearTable can be constructed and members can be tested" ) { + + CHECK( InterpolationType::LogLinear == chunk.interpolation() ); + CHECK( 4 == chunk.numberPoints() ); + CHECK( 4 == chunk.x().size() ); + CHECK( 4 == chunk.f().size() ); + CHECK_THAT( 1., WithinRel( chunk.x()[0] ) ); + CHECK_THAT( 2., WithinRel( chunk.x()[1] ) ); + CHECK_THAT( 3., WithinRel( chunk.x()[2] ) ); + CHECK_THAT( 4., WithinRel( chunk.x()[3] ) ); + CHECK( 2 == chunk.f()[0].x().size() ); + CHECK( 2 == chunk.f()[0].y().size() ); + CHECK( 3 == chunk.f()[1].x().size() ); + CHECK( 3 == chunk.f()[1].y().size() ); + CHECK( 3 == chunk.f()[2].x().size() ); + CHECK( 3 == chunk.f()[2].y().size() ); + CHECK( 2 == chunk.f()[3].x().size() ); + CHECK( 2 == chunk.f()[3].y().size() ); + CHECK_THAT( -1. , WithinRel( chunk.f()[0].x()[0] ) ); + CHECK_THAT( 1. , WithinRel( chunk.f()[0].x()[1] ) ); + CHECK_THAT( 0.5 , WithinRel( chunk.f()[0].y()[0] ) ); + CHECK_THAT( 0.5 , WithinRel( chunk.f()[0].y()[1] ) ); + CHECK_THAT( -1. , WithinRel( chunk.f()[1].x()[0] ) ); + CHECK_THAT( 0. , WithinRel( chunk.f()[1].x()[1] ) ); + CHECK_THAT( 1. , WithinRel( chunk.f()[1].x()[2] ) ); + CHECK_THAT( 0.49, WithinRel( chunk.f()[1].y()[0] ) ); + CHECK_THAT( 0.5 , WithinRel( chunk.f()[1].y()[1] ) ); + CHECK_THAT( 0.51, WithinRel( chunk.f()[1].y()[2] ) ); + CHECK_THAT( -1. , WithinRel( chunk.f()[2].x()[0] ) ); + CHECK_THAT( 0. , WithinRel( chunk.f()[2].x()[1] ) ); + CHECK_THAT( 1. , WithinRel( chunk.f()[2].x()[2] ) ); + CHECK_THAT( 0.4 , WithinRel( chunk.f()[2].y()[0] ) ); + CHECK_THAT( 0.5 , WithinRel( chunk.f()[2].y()[1] ) ); + CHECK_THAT( 0.6 , WithinRel( chunk.f()[2].y()[2] ) ); + CHECK_THAT( -1. , WithinRel( chunk.f()[3].x()[0] ) ); + CHECK_THAT( 1. , WithinRel( chunk.f()[3].x()[1] ) ); + CHECK_THAT( 0.1, WithinRel( chunk.f()[3].y()[0] ) ); + CHECK_THAT( 0.9, WithinRel( chunk.f()[3].y()[1] ) ); + } // THEN + + THEN( "a LogLinearTable can be evaluated" ) { + + // values of x in the x grid + CHECK_THAT( 0.5 , WithinRel( chunk( 1., -0.5 ) ) ); + CHECK_THAT( 0.495, WithinRel( chunk( 2., -0.5 ) ) ); + CHECK_THAT( 0.45 , WithinRel( chunk( 3., -0.5 ) ) ); + CHECK_THAT( 0.3 , WithinRel( chunk( 4., -0.5 ) ) ); + + // values of x outside the x grid + CHECK_THAT( 0., WithinRel( chunk( 0., -0.5 ) ) ); + CHECK_THAT( 0., WithinRel( chunk( 5., -0.5 ) ) ); + + // values of x inside the x grid + CHECK_THAT( 0.49749371855331, WithinRel( chunk( 1.5, -0.5 ) ) ); + CHECK_THAT( 0.47196398167657, WithinRel( chunk( 2.5, -0.5 ) ) ); + CHECK_THAT( 0.36742346141748, WithinRel( chunk( 3.5, -0.5 ) ) ); + } // THEN + } // WHEN + + WHEN( "the data is given explicitly using iterator views" ) { + + using XView = njoy::utility::IteratorView< std::vector< double >::const_iterator >; + using FView = njoy::utility::IteratorView< std::vector< InterpolationTable< double > >::const_iterator >; + + const std::vector< double > xvalues = { 1., 2., 3., 4. }; + const std::vector< InterpolationTable< double > > fvalues = { + + { { -1., +1. }, { 0.5, 0.5 } }, + { { -1., 0., +1. }, { 0.49, 0.5, 0.51 } }, + { { -1., 0., +1. }, { 0.4, 0.5, 0.6 } }, + { { -1., +1. }, { 0.1, 0.9 } } + }; + + + XView x = njoy::utility::make_view( xvalues ); + FView f = njoy::utility::make_view( fvalues ); + + LogLinearTableFunction< double, double, double, InterpolationTable< double >, + XView, FView > chunk( std::move( x ), std::move( f ) ); + + THEN( "a LogLinearTable can be constructed and members can be tested" ) { + + CHECK( InterpolationType::LogLinear == chunk.interpolation() ); + CHECK( 4 == chunk.numberPoints() ); + CHECK( 4 == chunk.x().size() ); + CHECK( 4 == chunk.f().size() ); + CHECK_THAT( 1., WithinRel( chunk.x()[0] ) ); + CHECK_THAT( 2., WithinRel( chunk.x()[1] ) ); + CHECK_THAT( 3., WithinRel( chunk.x()[2] ) ); + CHECK_THAT( 4., WithinRel( chunk.x()[3] ) ); + CHECK( 2 == chunk.f()[0].x().size() ); + CHECK( 2 == chunk.f()[0].y().size() ); + CHECK( 3 == chunk.f()[1].x().size() ); + CHECK( 3 == chunk.f()[1].y().size() ); + CHECK( 3 == chunk.f()[2].x().size() ); + CHECK( 3 == chunk.f()[2].y().size() ); + CHECK( 2 == chunk.f()[3].x().size() ); + CHECK( 2 == chunk.f()[3].y().size() ); + CHECK_THAT( -1. , WithinRel( chunk.f()[0].x()[0] ) ); + CHECK_THAT( 1. , WithinRel( chunk.f()[0].x()[1] ) ); + CHECK_THAT( 0.5 , WithinRel( chunk.f()[0].y()[0] ) ); + CHECK_THAT( 0.5 , WithinRel( chunk.f()[0].y()[1] ) ); + CHECK_THAT( -1. , WithinRel( chunk.f()[1].x()[0] ) ); + CHECK_THAT( 0. , WithinRel( chunk.f()[1].x()[1] ) ); + CHECK_THAT( 1. , WithinRel( chunk.f()[1].x()[2] ) ); + CHECK_THAT( 0.49, WithinRel( chunk.f()[1].y()[0] ) ); + CHECK_THAT( 0.5 , WithinRel( chunk.f()[1].y()[1] ) ); + CHECK_THAT( 0.51, WithinRel( chunk.f()[1].y()[2] ) ); + CHECK_THAT( -1. , WithinRel( chunk.f()[2].x()[0] ) ); + CHECK_THAT( 0. , WithinRel( chunk.f()[2].x()[1] ) ); + CHECK_THAT( 1. , WithinRel( chunk.f()[2].x()[2] ) ); + CHECK_THAT( 0.4 , WithinRel( chunk.f()[2].y()[0] ) ); + CHECK_THAT( 0.5 , WithinRel( chunk.f()[2].y()[1] ) ); + CHECK_THAT( 0.6 , WithinRel( chunk.f()[2].y()[2] ) ); + CHECK_THAT( -1. , WithinRel( chunk.f()[3].x()[0] ) ); + CHECK_THAT( 1. , WithinRel( chunk.f()[3].x()[1] ) ); + CHECK_THAT( 0.1, WithinRel( chunk.f()[3].y()[0] ) ); + CHECK_THAT( 0.9, WithinRel( chunk.f()[3].y()[1] ) ); + } // THEN + + THEN( "a LogLinearTable can be evaluated" ) { + + // values of x in the x grid + CHECK_THAT( 0.5 , WithinRel( chunk( 1., -0.5 ) ) ); + CHECK_THAT( 0.495, WithinRel( chunk( 2., -0.5 ) ) ); + CHECK_THAT( 0.45 , WithinRel( chunk( 3., -0.5 ) ) ); + CHECK_THAT( 0.3 , WithinRel( chunk( 4., -0.5 ) ) ); + + // values of x outside the x grid + CHECK_THAT( 0., WithinRel( chunk( 0., -0.5 ) ) ); + CHECK_THAT( 0., WithinRel( chunk( 5., -0.5 ) ) ); + + // values of x inside the x grid + CHECK_THAT( 0.49749371855331, WithinRel( chunk( 1.5, -0.5 ) ) ); + CHECK_THAT( 0.47196398167657, WithinRel( chunk( 2.5, -0.5 ) ) ); + CHECK_THAT( 0.36742346141748, WithinRel( chunk( 3.5, -0.5 ) ) ); + } // THEN + } // WHEN + } // GIVEN + + GIVEN( "invalid data for a LogLinearTable object" ) { + + WHEN( "there are not enough values in the x or y grid" ) { + + std::vector< double > xempty = {}; + std::vector< double > xone = { 1. }; + std::vector< InterpolationTable< double > > fempty = {}; + std::vector< InterpolationTable< double > > fone = { { { 1., 2. }, { 3., 4. } } }; + + THEN( "an exception is thrown" ) { + + CHECK_THROWS( LogLinearTableFunction< double >( xempty, fempty ) ); + CHECK_THROWS( LogLinearTableFunction< double >( xone, fone ) ); + CHECK_THROWS( LogLinearTableFunction< double >( xempty, fone ) ); + CHECK_THROWS( LogLinearTableFunction< double >( xone, fempty ) ); + } // THEN + } // WHEN + + WHEN( "the x and y grid do not have the same number of points" ) { + + std::vector< double > x = { 1., 2., 3., 4. }; + std::vector< InterpolationTable< double > > f = { + + { { -1., +1. }, { 0.5, 0.5 } }, + { { -1., 0., +1. }, { 0.49, 0.5, 0.51 } }, + { { -1., 0., +1. }, { 0.4, 0.5, 0.6 } } + }; + + THEN( "an exception is thrown" ) { + + CHECK_THROWS( LogLinearTableFunction< double >( std::move( x ), std::move( f ) ) ); + } // THEN + } // WHEN + + WHEN( "the x grid is not sorted" ) { + + std::vector< double > x = { 1., 3., 2., 4. }; + std::vector< InterpolationTable< double > > f = { + + { { -1., +1. }, { 0.5, 0.5 } }, + { { -1., 0., +1. }, { 0.49, 0.5, 0.51 } }, + { { -1., 0., +1. }, { 0.4, 0.5, 0.6 } }, + { { -1., +1. }, { 0.1, 0.9 } } + }; + + THEN( "an exception is thrown" ) { + + CHECK_THROWS( LogLinearTableFunction< double >( std::move( x ), std::move( f ) ) ); + } // THEN + } // WHEN + + WHEN( "the x grid has a duplicate point" ) { + + std::vector< double > x = { 1., 2., 2., 4. }; + std::vector< InterpolationTable< double > > f = { + + { { -1., +1. }, { 0.5, 0.5 } }, + { { -1., 0., +1. }, { 0.49, 0.5, 0.51 } }, + { { -1., 0., +1. }, { 0.4, 0.5, 0.6 } }, + { { -1., +1. }, { 0.1, 0.9 } } + }; + + THEN( "an exception is thrown" ) { + + CHECK_THROWS( LogLinearTableFunction< double >( std::move( x ), std::move( f ) ) ); + } // THEN + } // WHEN + } // GIVEN +} // SCENARIO diff --git a/src/scion/math/LogLogTableFunction.hpp b/src/scion/math/LogLogTableFunction.hpp new file mode 100644 index 0000000..37e47b8 --- /dev/null +++ b/src/scion/math/LogLogTableFunction.hpp @@ -0,0 +1,75 @@ +#ifndef NJOY_SCION_MATH_LOGLOGTABLEFUNCTION +#define NJOY_SCION_MATH_LOGLOGTABLEFUNCTION + +// system includes +#include + +// other includes +#include "scion/interpolation/InterpolationType.hpp" +#include "scion/interpolation/LogarithmicLogarithmic.hpp" +#include "scion/linearisation/ToleranceConvergence.hpp" +#include "scion/math/SingleTableFunctionBase.hpp" + +namespace njoy { +namespace scion { +namespace math { + + /** + * @class + * @brief Tabulated x,f(y) data with log-log interpolation (ln(f(y)) is linear in ln(x)) + * + * The LogLogTableFunction is templatised on the actual x, y and z types, the function + * type F and the container type used for the x values and the functions. This allows us to + * use something like utility::IteratorView instead of std::vector. + */ + template < typename X, typename Y, typename Z, typename F, + typename XContainer = std::vector< X >, + typename FContainer = std::vector< F > > + class LogLogTableFunction : + public SingleTableFunctionBase< LogLogTableFunction< X, Y, Z, F, XContainer, FContainer >, + interpolation::LogarithmicLogarithmic, X, Y, Z, F, + XContainer, FContainer > { + + /* friend declarations */ + friend class SingleTableFunctionBase< LogLogTableFunction< X, Y, Z, F, XContainer, FContainer >, + interpolation::LogarithmicLogarithmic, X, Y, Z, F, + XContainer, FContainer >; + + /* type aliases */ + using Parent = SingleTableFunctionBase< LogLogTableFunction< X, Y, Z, F, XContainer, FContainer >, + interpolation::LogarithmicLogarithmic, X, Y, Z, F, + XContainer, FContainer >; + + /* fields */ + + /* auxiliary function */ + + /* interface implementation functions */ + + /** + * @brief Return the interpolation type + */ + static constexpr interpolation::InterpolationType type() noexcept { + + return interpolation::InterpolationType::LogLog; + } + + public: + + /* constructor */ + #include "scion/math/LogLogTableFunction/src/ctor.hpp" + + /* methods */ + + using Parent::interpolation; + using Parent::x; + using Parent::f; + using Parent::numberPoints; + using Parent::operator(); + }; + +} // math namespace +} // scion namespace +} // njoy namespace + +#endif diff --git a/src/scion/math/LogLogTableFunction/src/ctor.hpp b/src/scion/math/LogLogTableFunction/src/ctor.hpp new file mode 100644 index 0000000..ba02abb --- /dev/null +++ b/src/scion/math/LogLogTableFunction/src/ctor.hpp @@ -0,0 +1,8 @@ +/** + * @brief Constructor + * + * @param x the x values of the tabulated data + * @param f the f(y) functions of the tabulated data + */ +LogLogTableFunction( XContainer x, FContainer f ) : + Parent( std::move( x ), std::move( f ) ) {} diff --git a/src/scion/math/LogLogTableFunction/test/CMakeLists.txt b/src/scion/math/LogLogTableFunction/test/CMakeLists.txt new file mode 100644 index 0000000..c05080a --- /dev/null +++ b/src/scion/math/LogLogTableFunction/test/CMakeLists.txt @@ -0,0 +1 @@ +add_cpp_test( math.LogLogTableFunction LogLogTableFunction.test.cpp ) diff --git a/src/scion/math/LogLogTableFunction/test/LogLogTableFunction.test.cpp b/src/scion/math/LogLogTableFunction/test/LogLogTableFunction.test.cpp new file mode 100644 index 0000000..e3f30ae --- /dev/null +++ b/src/scion/math/LogLogTableFunction/test/LogLogTableFunction.test.cpp @@ -0,0 +1,249 @@ +// include Catch2 +#include +#include +using Catch::Matchers::WithinRel; + +// what we are testing +#include "scion/math/InterpolationTable.hpp" +#include "scion/math/LogLogTableFunction.hpp" + +// other includes +#include "utility/IteratorView.hpp" + +// convenience typedefs +using namespace njoy::scion; +template < typename X, typename Y = X > +using InterpolationTable = math::InterpolationTable< X, Y >; +template < typename X, typename Y = X, typename Z = X, + typename F = InterpolationTable< X >, + typename XContainer = std::vector< X >, + typename FContainer = std::vector< F > > +using LogLogTableFunction = math::LogLogTableFunction< X, Y, Z, F, XContainer, FContainer >; +using InterpolationType = interpolation::InterpolationType; + +SCENARIO( "LogLogTableFunction" ) { + + GIVEN( "tabulated x,f(y) data" ) { + + WHEN( "the data is given explicitly using vectors" ) { + + const std::vector< double > x = { 1., 2., 3., 4. }; + const std::vector< InterpolationTable< double > > f = { + + { { -1., +1. }, { 0.5, 0.5 } }, + { { -1., 0., +1. }, { 0.49, 0.5, 0.51 } }, + { { -1., 0., +1. }, { 0.4, 0.5, 0.6 } }, + { { -1., +1. }, { 0.1, 0.9 } } + }; + + LogLogTableFunction< double > chunk( std::move( x ), std::move( f ) ); + + THEN( "a LogLogTable can be constructed and members can be tested" ) { + + CHECK( InterpolationType::LogLog == chunk.interpolation() ); + CHECK( 4 == chunk.numberPoints() ); + CHECK( 4 == chunk.x().size() ); + CHECK( 4 == chunk.f().size() ); + CHECK_THAT( 1., WithinRel( chunk.x()[0] ) ); + CHECK_THAT( 2., WithinRel( chunk.x()[1] ) ); + CHECK_THAT( 3., WithinRel( chunk.x()[2] ) ); + CHECK_THAT( 4., WithinRel( chunk.x()[3] ) ); + CHECK( 2 == chunk.f()[0].x().size() ); + CHECK( 2 == chunk.f()[0].y().size() ); + CHECK( 3 == chunk.f()[1].x().size() ); + CHECK( 3 == chunk.f()[1].y().size() ); + CHECK( 3 == chunk.f()[2].x().size() ); + CHECK( 3 == chunk.f()[2].y().size() ); + CHECK( 2 == chunk.f()[3].x().size() ); + CHECK( 2 == chunk.f()[3].y().size() ); + CHECK_THAT( -1. , WithinRel( chunk.f()[0].x()[0] ) ); + CHECK_THAT( 1. , WithinRel( chunk.f()[0].x()[1] ) ); + CHECK_THAT( 0.5 , WithinRel( chunk.f()[0].y()[0] ) ); + CHECK_THAT( 0.5 , WithinRel( chunk.f()[0].y()[1] ) ); + CHECK_THAT( -1. , WithinRel( chunk.f()[1].x()[0] ) ); + CHECK_THAT( 0. , WithinRel( chunk.f()[1].x()[1] ) ); + CHECK_THAT( 1. , WithinRel( chunk.f()[1].x()[2] ) ); + CHECK_THAT( 0.49, WithinRel( chunk.f()[1].y()[0] ) ); + CHECK_THAT( 0.5 , WithinRel( chunk.f()[1].y()[1] ) ); + CHECK_THAT( 0.51, WithinRel( chunk.f()[1].y()[2] ) ); + CHECK_THAT( -1. , WithinRel( chunk.f()[2].x()[0] ) ); + CHECK_THAT( 0. , WithinRel( chunk.f()[2].x()[1] ) ); + CHECK_THAT( 1. , WithinRel( chunk.f()[2].x()[2] ) ); + CHECK_THAT( 0.4 , WithinRel( chunk.f()[2].y()[0] ) ); + CHECK_THAT( 0.5 , WithinRel( chunk.f()[2].y()[1] ) ); + CHECK_THAT( 0.6 , WithinRel( chunk.f()[2].y()[2] ) ); + CHECK_THAT( -1. , WithinRel( chunk.f()[3].x()[0] ) ); + CHECK_THAT( 1. , WithinRel( chunk.f()[3].x()[1] ) ); + CHECK_THAT( 0.1, WithinRel( chunk.f()[3].y()[0] ) ); + CHECK_THAT( 0.9, WithinRel( chunk.f()[3].y()[1] ) ); + } // THEN + + THEN( "a LogLogTable can be evaluated" ) { + + // values of x in the x grid + CHECK_THAT( 0.5 , WithinRel( chunk( 1., -0.5 ) ) ); + CHECK_THAT( 0.495, WithinRel( chunk( 2., -0.5 ) ) ); + CHECK_THAT( 0.45 , WithinRel( chunk( 3., -0.5 ) ) ); + CHECK_THAT( 0.3 , WithinRel( chunk( 4., -0.5 ) ) ); + + // values of x outside the x grid + CHECK_THAT( 0., WithinRel( chunk( 0., -0.5 ) ) ); + CHECK_THAT( 0., WithinRel( chunk( 5., -0.5 ) ) ); + + // values of x inside the x grid + CHECK_THAT( 0.49706908915929, WithinRel( chunk( 1.5, -0.5 ) ) ); + CHECK_THAT( 0.46970497533108, WithinRel( chunk( 2.5, -0.5 ) ) ); + CHECK_THAT( 0.36212316985370, WithinRel( chunk( 3.5, -0.5 ) ) ); + } // THEN + } // WHEN + + WHEN( "the data is given explicitly using iterator views" ) { + + using XView = njoy::utility::IteratorView< std::vector< double >::const_iterator >; + using FView = njoy::utility::IteratorView< std::vector< InterpolationTable< double > >::const_iterator >; + + const std::vector< double > xvalues = { 1., 2., 3., 4. }; + const std::vector< InterpolationTable< double > > fvalues = { + + { { -1., +1. }, { 0.5, 0.5 } }, + { { -1., 0., +1. }, { 0.49, 0.5, 0.51 } }, + { { -1., 0., +1. }, { 0.4, 0.5, 0.6 } }, + { { -1., +1. }, { 0.1, 0.9 } } + }; + + + XView x = njoy::utility::make_view( xvalues ); + FView f = njoy::utility::make_view( fvalues ); + + LogLogTableFunction< double, double, double, InterpolationTable< double >, + XView, FView > chunk( std::move( x ), std::move( f ) ); + + THEN( "a LogLogTable can be constructed and members can be tested" ) { + + CHECK( InterpolationType::LogLog == chunk.interpolation() ); + CHECK( 4 == chunk.numberPoints() ); + CHECK( 4 == chunk.x().size() ); + CHECK( 4 == chunk.f().size() ); + CHECK_THAT( 1., WithinRel( chunk.x()[0] ) ); + CHECK_THAT( 2., WithinRel( chunk.x()[1] ) ); + CHECK_THAT( 3., WithinRel( chunk.x()[2] ) ); + CHECK_THAT( 4., WithinRel( chunk.x()[3] ) ); + CHECK( 2 == chunk.f()[0].x().size() ); + CHECK( 2 == chunk.f()[0].y().size() ); + CHECK( 3 == chunk.f()[1].x().size() ); + CHECK( 3 == chunk.f()[1].y().size() ); + CHECK( 3 == chunk.f()[2].x().size() ); + CHECK( 3 == chunk.f()[2].y().size() ); + CHECK( 2 == chunk.f()[3].x().size() ); + CHECK( 2 == chunk.f()[3].y().size() ); + CHECK_THAT( -1. , WithinRel( chunk.f()[0].x()[0] ) ); + CHECK_THAT( 1. , WithinRel( chunk.f()[0].x()[1] ) ); + CHECK_THAT( 0.5 , WithinRel( chunk.f()[0].y()[0] ) ); + CHECK_THAT( 0.5 , WithinRel( chunk.f()[0].y()[1] ) ); + CHECK_THAT( -1. , WithinRel( chunk.f()[1].x()[0] ) ); + CHECK_THAT( 0. , WithinRel( chunk.f()[1].x()[1] ) ); + CHECK_THAT( 1. , WithinRel( chunk.f()[1].x()[2] ) ); + CHECK_THAT( 0.49, WithinRel( chunk.f()[1].y()[0] ) ); + CHECK_THAT( 0.5 , WithinRel( chunk.f()[1].y()[1] ) ); + CHECK_THAT( 0.51, WithinRel( chunk.f()[1].y()[2] ) ); + CHECK_THAT( -1. , WithinRel( chunk.f()[2].x()[0] ) ); + CHECK_THAT( 0. , WithinRel( chunk.f()[2].x()[1] ) ); + CHECK_THAT( 1. , WithinRel( chunk.f()[2].x()[2] ) ); + CHECK_THAT( 0.4 , WithinRel( chunk.f()[2].y()[0] ) ); + CHECK_THAT( 0.5 , WithinRel( chunk.f()[2].y()[1] ) ); + CHECK_THAT( 0.6 , WithinRel( chunk.f()[2].y()[2] ) ); + CHECK_THAT( -1. , WithinRel( chunk.f()[3].x()[0] ) ); + CHECK_THAT( 1. , WithinRel( chunk.f()[3].x()[1] ) ); + CHECK_THAT( 0.1, WithinRel( chunk.f()[3].y()[0] ) ); + CHECK_THAT( 0.9, WithinRel( chunk.f()[3].y()[1] ) ); + } // THEN + + THEN( "a LogLogTable can be evaluated" ) { + + // values of x in the x grid + CHECK_THAT( 0.5 , WithinRel( chunk( 1., -0.5 ) ) ); + CHECK_THAT( 0.495, WithinRel( chunk( 2., -0.5 ) ) ); + CHECK_THAT( 0.45 , WithinRel( chunk( 3., -0.5 ) ) ); + CHECK_THAT( 0.3 , WithinRel( chunk( 4., -0.5 ) ) ); + + // values of x outside the x grid + CHECK_THAT( 0., WithinRel( chunk( 0., -0.5 ) ) ); + CHECK_THAT( 0., WithinRel( chunk( 5., -0.5 ) ) ); + + // values of x inside the x grid + CHECK_THAT( 0.49706908915929, WithinRel( chunk( 1.5, -0.5 ) ) ); + CHECK_THAT( 0.46970497533108, WithinRel( chunk( 2.5, -0.5 ) ) ); + CHECK_THAT( 0.36212316985370, WithinRel( chunk( 3.5, -0.5 ) ) ); + } // THEN + } // WHEN + } // GIVEN + + GIVEN( "invalid data for a LogLogTable object" ) { + + WHEN( "there are not enough values in the x or y grid" ) { + + std::vector< double > xempty = {}; + std::vector< double > xone = { 1. }; + std::vector< InterpolationTable< double > > fempty = {}; + std::vector< InterpolationTable< double > > fone = { { { 1., 2. }, { 3., 4. } } }; + + THEN( "an exception is thrown" ) { + + CHECK_THROWS( LogLogTableFunction< double >( xempty, fempty ) ); + CHECK_THROWS( LogLogTableFunction< double >( xone, fone ) ); + CHECK_THROWS( LogLogTableFunction< double >( xempty, fone ) ); + CHECK_THROWS( LogLogTableFunction< double >( xone, fempty ) ); + } // THEN + } // WHEN + + WHEN( "the x and y grid do not have the same number of points" ) { + + std::vector< double > x = { 1., 2., 3., 4. }; + std::vector< InterpolationTable< double > > f = { + + { { -1., +1. }, { 0.5, 0.5 } }, + { { -1., 0., +1. }, { 0.49, 0.5, 0.51 } }, + { { -1., 0., +1. }, { 0.4, 0.5, 0.6 } } + }; + + THEN( "an exception is thrown" ) { + + CHECK_THROWS( LogLogTableFunction< double >( std::move( x ), std::move( f ) ) ); + } // THEN + } // WHEN + + WHEN( "the x grid is not sorted" ) { + + std::vector< double > x = { 1., 3., 2., 4. }; + std::vector< InterpolationTable< double > > f = { + + { { -1., +1. }, { 0.5, 0.5 } }, + { { -1., 0., +1. }, { 0.49, 0.5, 0.51 } }, + { { -1., 0., +1. }, { 0.4, 0.5, 0.6 } }, + { { -1., +1. }, { 0.1, 0.9 } } + }; + + THEN( "an exception is thrown" ) { + + CHECK_THROWS( LogLogTableFunction< double >( std::move( x ), std::move( f ) ) ); + } // THEN + } // WHEN + + WHEN( "the x grid has a duplicate point" ) { + + std::vector< double > x = { 1., 2., 2., 4. }; + std::vector< InterpolationTable< double > > f = { + + { { -1., +1. }, { 0.5, 0.5 } }, + { { -1., 0., +1. }, { 0.49, 0.5, 0.51 } }, + { { -1., 0., +1. }, { 0.4, 0.5, 0.6 } }, + { { -1., +1. }, { 0.1, 0.9 } } + }; + + THEN( "an exception is thrown" ) { + + CHECK_THROWS( LogLogTableFunction< double >( std::move( x ), std::move( f ) ) ); + } // THEN + } // WHEN + } // GIVEN +} // SCENARIO