| 1 | /* -*- mode: c++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ |
| 2 | |
| 3 | /* |
| 4 | Copyright (C) 2005 StatPro Italia srl |
| 5 | |
| 6 | This file is part of QuantLib, a free-software/open-source library |
| 7 | for financial quantitative analysts and developers - http://quantlib.org/ |
| 8 | |
| 9 | QuantLib is free software: you can redistribute it and/or modify it |
| 10 | under the terms of the QuantLib license. You should have received a |
| 11 | copy of the license along with this program; if not, please email |
| 12 | <quantlib-dev@lists.sf.net>. The license is also available online at |
| 13 | <http://quantlib.org/license.shtml>. |
| 14 | |
| 15 | This program is distributed in the hope that it will be useful, but WITHOUT |
| 16 | ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS |
| 17 | FOR A PARTICULAR PURPOSE. See the license for more details. |
| 18 | */ |
| 19 | |
| 20 | #include "array.hpp" |
| 21 | #include "utilities.hpp" |
| 22 | #include <ql/math/array.hpp> |
| 23 | #include <ql/utilities/dataformatters.hpp> |
| 24 | |
| 25 | using namespace QuantLib; |
| 26 | using namespace boost::unit_test_framework; |
| 27 | |
| 28 | namespace array_test { |
| 29 | class FSquared { |
| 30 | public: |
| 31 | Real operator()(Real x) const { return x*x; } |
| 32 | }; |
| 33 | } |
| 34 | |
| 35 | void ArrayTest::testConstruction() { |
| 36 | |
| 37 | BOOST_TEST_MESSAGE("Testing array construction..." ); |
| 38 | |
| 39 | using namespace array_test; |
| 40 | |
| 41 | // empty array |
| 42 | Array a1; |
| 43 | if (!a1.empty()) |
| 44 | BOOST_ERROR("default-initialized array is not empty " |
| 45 | "(size = " << a1.size() << ")" ); |
| 46 | |
| 47 | // sized array |
| 48 | Size size = 5; |
| 49 | Array a2(size); |
| 50 | if (a2.size() != size) |
| 51 | BOOST_ERROR("array not of the required size" |
| 52 | << "\n required: " << size |
| 53 | << "\n resulting: " << a2.size()); |
| 54 | |
| 55 | // sized array, constant values |
| 56 | Real value = 42.0; |
| 57 | Array a3(size, value); |
| 58 | if (a3.size() != size) |
| 59 | BOOST_ERROR("array not of the required size" |
| 60 | << "\n required: " << size |
| 61 | << "\n resulting: " << a3.size()); |
| 62 | Size i; |
| 63 | for (i=0; i<size; ++i) { |
| 64 | if (a3[i] != value) |
| 65 | BOOST_ERROR(io::ordinal(i+1) << " element not with required value" |
| 66 | << "\n required: " << value |
| 67 | << "\n resulting: " << a3[i]); |
| 68 | } |
| 69 | |
| 70 | // sized array, incremental values |
| 71 | Real increment = 3.0; |
| 72 | Array a4(size, value, increment); |
| 73 | if (a4.size() != size) |
| 74 | BOOST_ERROR("array not of the required size" |
| 75 | << "\n required: " << size |
| 76 | << "\n resulting: " << a4.size()); |
| 77 | for (i=0; i<size; i++) { |
| 78 | if (a4[i] != value + i*increment) |
| 79 | BOOST_ERROR(io::ordinal(i+1) << " element not with required value" |
| 80 | << "\n required: " << value + i*increment |
| 81 | << "\n resulting: " << a4[i]); |
| 82 | } |
| 83 | |
| 84 | // copy constructor |
| 85 | Array a5(a1); // NOLINT(performance-unnecessary-copy-initialization) |
| 86 | if (a5.size() != a1.size()) |
| 87 | BOOST_ERROR("copy not of the same size as original" |
| 88 | << "\n original: " << a1.size() |
| 89 | << "\n copy: " << a5.size()); |
| 90 | |
| 91 | Array a6(a3); |
| 92 | if (a6.size() != a3.size()) |
| 93 | BOOST_ERROR("copy not of the same size as original" |
| 94 | << "\n original: " << a3.size() |
| 95 | << "\n copy: " << a6.size()); |
| 96 | for (i=0; i<a3.size(); i++) { |
| 97 | if (a6[i] != a3[i]) |
| 98 | BOOST_ERROR(io::ordinal(i+1) << " element of copy " |
| 99 | "not with same value as original" |
| 100 | << "\n original: " << a3[i] |
| 101 | << "\n copy: " << a6[i]); |
| 102 | } |
| 103 | |
| 104 | // transform |
| 105 | Array a10(5); |
| 106 | for (i=0; i < a10.size(); i++) { |
| 107 | a10[i] = static_cast<Real>(i); |
| 108 | } |
| 109 | FSquared f2; |
| 110 | std::transform(first: a10.begin(), last: a10.end(), result: a10.begin(), unary_op: FSquared()); |
| 111 | for (i=0; i < a10.size(); i++) { |
| 112 | Real calculated = f2(static_cast<Real>(i)); |
| 113 | if (std::fabs(x: a10[i] - calculated) >= 1e-5) { |
| 114 | BOOST_ERROR("Array transform test failed " << a10[i] << " " |
| 115 | << calculated); |
| 116 | } |
| 117 | } |
| 118 | } |
| 119 | |
| 120 | void ArrayTest::testArrayFunctions() { |
| 121 | |
| 122 | BOOST_TEST_MESSAGE("Testing array functions..." ); |
| 123 | |
| 124 | auto get_array = []() { |
| 125 | Array a(5); |
| 126 | for (Size i=0; i < a.size(); ++i) { |
| 127 | a[i] = std::sin(x: Real(i))+1.1; |
| 128 | } |
| 129 | return a; |
| 130 | }; |
| 131 | |
| 132 | const Array a = get_array(); |
| 133 | |
| 134 | constexpr double exponential = -2.3; |
| 135 | const Array p_lvalue = Pow(v: a, alpha: exponential); |
| 136 | const Array e_lvalue = Exp(v: a); |
| 137 | const Array l_lvalue = Log(v: a); |
| 138 | const Array s_lvalue = Sqrt(v: a); |
| 139 | const Array a_lvalue = Abs(v: a); |
| 140 | const Array p_rvalue = Pow(v: get_array(), alpha: exponential); |
| 141 | const Array e_rvalue = Exp(v: get_array()); |
| 142 | const Array l_rvalue = Log(v: get_array()); |
| 143 | const Array s_rvalue = Sqrt(v: get_array()); |
| 144 | const Array a_rvalue = Abs(v: get_array()); |
| 145 | |
| 146 | constexpr double tol = 10*QL_EPSILON; |
| 147 | for (Size i=0; i < a.size(); ++i) { |
| 148 | if (std::fabs(x: p_lvalue[i]-std::pow(x: a[i], y: exponential)) > tol) { |
| 149 | BOOST_FAIL("Array function test Pow failed (lvalue)" ); |
| 150 | } |
| 151 | if (std::fabs(x: p_rvalue[i]-std::pow(x: a[i], y: exponential)) > tol) { |
| 152 | BOOST_FAIL("Array function test Pow failed (lvalue)" ); |
| 153 | } |
| 154 | if (std::fabs(x: e_lvalue[i]-std::exp(x: a[i])) > tol) { |
| 155 | BOOST_FAIL("Array function test Exp failed (lvalue)" ); |
| 156 | } |
| 157 | if (std::fabs(x: e_rvalue[i]-std::exp(x: a[i])) > tol) { |
| 158 | BOOST_FAIL("Array function test Exp failed (rvalue)" ); |
| 159 | } |
| 160 | if (std::fabs(x: l_lvalue[i]-std::log(x: a[i])) > tol) { |
| 161 | BOOST_FAIL("Array function test Log failed (lvalue)" ); |
| 162 | } |
| 163 | if (std::fabs(x: l_rvalue[i]-std::log(x: a[i])) > tol) { |
| 164 | BOOST_FAIL("Array function test Log failed (rvalue)" ); |
| 165 | } |
| 166 | if (std::fabs(x: s_lvalue[i]-std::sqrt(x: a[i])) > tol) { |
| 167 | BOOST_FAIL("Array function test Sqrt failed (lvalue)" ); |
| 168 | } |
| 169 | if (std::fabs(x: s_rvalue[i]-std::sqrt(x: a[i])) > tol) { |
| 170 | BOOST_FAIL("Array function test Sqrt failed (rvalue)" ); |
| 171 | } |
| 172 | if (std::fabs(x: a_lvalue[i]-std::abs(x: a[i])) > tol) { |
| 173 | BOOST_FAIL("Array function test Abs failed (lvalue)" ); |
| 174 | } |
| 175 | if (std::fabs(x: a_rvalue[i]-std::abs(x: a[i])) > tol) { |
| 176 | BOOST_FAIL("Array function test Abs failed (rvalue)" ); |
| 177 | } |
| 178 | } |
| 179 | } |
| 180 | |
| 181 | void ArrayTest::testArrayResize() { |
| 182 | BOOST_TEST_MESSAGE("Testing array resize..." ); |
| 183 | |
| 184 | Array a(10,1.0,1.0); |
| 185 | |
| 186 | for (Size i=0; i < 10; ++i) |
| 187 | QL_CHECK_CLOSE(a[i], Real(1+i), 10*QL_EPSILON); |
| 188 | |
| 189 | a.resize(n: 5); |
| 190 | BOOST_CHECK(a.size() == 5); |
| 191 | |
| 192 | for (Size i=0; i < 5; ++i) |
| 193 | QL_CHECK_CLOSE(a[i], Real(1+i), 10*QL_EPSILON); |
| 194 | |
| 195 | a.resize(n: 15); |
| 196 | BOOST_CHECK(a.size() == 15); |
| 197 | |
| 198 | for (Size i=0; i < 5; ++i) |
| 199 | QL_CHECK_CLOSE(a[i], Real(1+i), 10*QL_EPSILON); |
| 200 | |
| 201 | const Array::const_iterator iter = a.begin(); |
| 202 | a.resize(n: a.size()); |
| 203 | BOOST_CHECK(iter == a.begin()); |
| 204 | |
| 205 | a.resize(n: 10); |
| 206 | BOOST_CHECK(a.size() == 10); |
| 207 | BOOST_CHECK(iter == a.begin()); |
| 208 | } |
| 209 | |
| 210 | #define QL_CHECK_CLOSE_ARRAY(actual, expected) \ |
| 211 | BOOST_REQUIRE(actual.size() == expected.size()); \ |
| 212 | for (auto i = 0u; i < actual.size(); i++) { \ |
| 213 | QL_CHECK_CLOSE(actual[i], expected[i], 100 * QL_EPSILON); \ |
| 214 | } \ |
| 215 | |
| 216 | void ArrayTest::testArrayOperators() { |
| 217 | BOOST_TEST_MESSAGE("Testing array operators..." ); |
| 218 | |
| 219 | auto get_array = []() { |
| 220 | return Array{1.1, 2.2, 3.3}; |
| 221 | }; |
| 222 | |
| 223 | const auto a = get_array(); |
| 224 | |
| 225 | const auto positive = Array{1.1, 2.2, 3.3}; |
| 226 | const auto lvalue_positive = +a; |
| 227 | const auto rvalue_positive = +get_array(); |
| 228 | |
| 229 | QL_CHECK_CLOSE_ARRAY(lvalue_positive, positive); |
| 230 | QL_CHECK_CLOSE_ARRAY(rvalue_positive, positive); |
| 231 | |
| 232 | const auto negative = Array{-1.1, -2.2, -3.3}; |
| 233 | const auto lvalue_negative = -a; |
| 234 | const auto rvalue_negative = -get_array(); |
| 235 | |
| 236 | QL_CHECK_CLOSE_ARRAY(lvalue_negative, negative); |
| 237 | QL_CHECK_CLOSE_ARRAY(rvalue_negative, negative); |
| 238 | |
| 239 | const auto array_sum = Array{2.2, 4.4, 6.6}; |
| 240 | const auto lvalue_lvalue_sum = a + a; |
| 241 | const auto lvalue_rvalue_sum = a + get_array(); |
| 242 | const auto rvalue_lvalue_sum = get_array() + a; |
| 243 | const auto rvalue_rvalue_sum = get_array() + get_array(); |
| 244 | |
| 245 | QL_CHECK_CLOSE_ARRAY(lvalue_lvalue_sum, array_sum); |
| 246 | QL_CHECK_CLOSE_ARRAY(lvalue_rvalue_sum, array_sum); |
| 247 | QL_CHECK_CLOSE_ARRAY(rvalue_lvalue_sum, array_sum); |
| 248 | QL_CHECK_CLOSE_ARRAY(rvalue_rvalue_sum, array_sum); |
| 249 | |
| 250 | const auto scalar_sum = Array{2.2, 3.3, 4.4}; |
| 251 | const auto lvalue_real_sum = a + 1.1; |
| 252 | const auto rvalue_real_sum = get_array() + 1.1; |
| 253 | const auto real_lvalue_sum = 1.1 + a; |
| 254 | const auto real_rvalue_sum = 1.1 + get_array(); |
| 255 | |
| 256 | QL_CHECK_CLOSE_ARRAY(lvalue_real_sum, scalar_sum); |
| 257 | QL_CHECK_CLOSE_ARRAY(rvalue_real_sum, scalar_sum); |
| 258 | QL_CHECK_CLOSE_ARRAY(real_lvalue_sum, scalar_sum); |
| 259 | QL_CHECK_CLOSE_ARRAY(real_rvalue_sum, scalar_sum); |
| 260 | |
| 261 | const auto array_difference = Array{0.0, 0.0, 0.0}; |
| 262 | const auto lvalue_lvalue_difference = a - a; // NOLINT(misc-redundant-expression) |
| 263 | const auto lvalue_rvalue_difference = a - get_array(); |
| 264 | const auto rvalue_lvalue_difference = get_array() - a; |
| 265 | const auto rvalue_rvalue_difference = get_array() - get_array(); |
| 266 | |
| 267 | QL_CHECK_CLOSE_ARRAY(lvalue_lvalue_difference, array_difference); |
| 268 | QL_CHECK_CLOSE_ARRAY(lvalue_rvalue_difference, array_difference); |
| 269 | QL_CHECK_CLOSE_ARRAY(rvalue_lvalue_difference, array_difference); |
| 270 | QL_CHECK_CLOSE_ARRAY(rvalue_rvalue_difference, array_difference); |
| 271 | |
| 272 | const auto scalar_difference_1 = Array{0.0, +1.1, +2.2}; |
| 273 | const auto scalar_difference_2 = Array{0.0, -1.1, -2.2}; |
| 274 | const auto lvalue_real_difference = a - 1.1; |
| 275 | const auto rvalue_real_difference = get_array() - 1.1; |
| 276 | const auto real_lvalue_difference = 1.1 - a; |
| 277 | const auto real_rvalue_difference = 1.1 - get_array(); |
| 278 | |
| 279 | QL_CHECK_CLOSE_ARRAY(lvalue_real_difference, scalar_difference_1); |
| 280 | QL_CHECK_CLOSE_ARRAY(rvalue_real_difference, scalar_difference_1); |
| 281 | QL_CHECK_CLOSE_ARRAY(real_lvalue_difference, scalar_difference_2); |
| 282 | QL_CHECK_CLOSE_ARRAY(real_rvalue_difference, scalar_difference_2); |
| 283 | |
| 284 | const auto array_product = Array{1.1 * 1.1, 2.2 * 2.2, 3.3 * 3.3}; |
| 285 | const auto lvalue_lvalue_product = a * a; |
| 286 | const auto lvalue_rvalue_product = a * get_array(); |
| 287 | const auto rvalue_lvalue_product = get_array() * a; |
| 288 | const auto rvalue_rvalue_product = get_array() * get_array(); |
| 289 | |
| 290 | QL_CHECK_CLOSE_ARRAY(lvalue_lvalue_product, array_product); |
| 291 | QL_CHECK_CLOSE_ARRAY(lvalue_rvalue_product, array_product); |
| 292 | QL_CHECK_CLOSE_ARRAY(rvalue_lvalue_product, array_product); |
| 293 | QL_CHECK_CLOSE_ARRAY(rvalue_rvalue_product, array_product); |
| 294 | |
| 295 | const auto scalar_product = Array{1.1 * 1.1, 2.2 * 1.1, 3.3 * 1.1}; |
| 296 | const auto lvalue_real_product = a * 1.1; |
| 297 | const auto rvalue_real_product = get_array() * 1.1; |
| 298 | const auto real_lvalue_product = 1.1 * a; |
| 299 | const auto real_rvalue_product = 1.1 * get_array(); |
| 300 | |
| 301 | QL_CHECK_CLOSE_ARRAY(lvalue_real_product, scalar_product); |
| 302 | QL_CHECK_CLOSE_ARRAY(rvalue_real_product, scalar_product); |
| 303 | QL_CHECK_CLOSE_ARRAY(real_lvalue_product, scalar_product); |
| 304 | QL_CHECK_CLOSE_ARRAY(real_rvalue_product, scalar_product); |
| 305 | |
| 306 | const auto array_quotient = Array{1.0, 1.0, 1.0}; |
| 307 | const auto lvalue_lvalue_quotient = a / a; // NOLINT(misc-redundant-expression) |
| 308 | const auto lvalue_rvalue_quotient = a / get_array(); |
| 309 | const auto rvalue_lvalue_quotient = get_array() / a; |
| 310 | const auto rvalue_rvalue_quotient = get_array() / get_array(); |
| 311 | |
| 312 | QL_CHECK_CLOSE_ARRAY(lvalue_lvalue_quotient, array_quotient); |
| 313 | QL_CHECK_CLOSE_ARRAY(lvalue_rvalue_quotient, array_quotient); |
| 314 | QL_CHECK_CLOSE_ARRAY(rvalue_lvalue_quotient, array_quotient); |
| 315 | QL_CHECK_CLOSE_ARRAY(rvalue_rvalue_quotient, array_quotient); |
| 316 | |
| 317 | const auto scalar_quotient_1 = Array{1.1 / 1.1, 2.2 / 1.1, 3.3 / 1.1}; |
| 318 | const auto scalar_quotient_2 = Array{1.1 / 1.1, 1.1 / 2.2, 1.1 / 3.3}; |
| 319 | const auto lvalue_real_quotient = a / 1.1; |
| 320 | const auto rvalue_real_quotient = get_array() / 1.1; |
| 321 | const auto real_lvalue_quotient = 1.1 / a; |
| 322 | const auto real_rvalue_quotient = 1.1 / get_array(); |
| 323 | |
| 324 | QL_CHECK_CLOSE_ARRAY(lvalue_real_quotient, scalar_quotient_1); |
| 325 | QL_CHECK_CLOSE_ARRAY(rvalue_real_quotient, scalar_quotient_1); |
| 326 | QL_CHECK_CLOSE_ARRAY(real_lvalue_quotient, scalar_quotient_2); |
| 327 | QL_CHECK_CLOSE_ARRAY(real_rvalue_quotient, scalar_quotient_2); |
| 328 | } |
| 329 | |
| 330 | test_suite* ArrayTest::suite() { |
| 331 | auto* suite = BOOST_TEST_SUITE("array tests" ); |
| 332 | suite->add(QUANTLIB_TEST_CASE(&ArrayTest::testConstruction)); |
| 333 | suite->add(QUANTLIB_TEST_CASE(&ArrayTest::testArrayFunctions)); |
| 334 | suite->add(QUANTLIB_TEST_CASE(&ArrayTest::testArrayResize)); |
| 335 | suite->add(QUANTLIB_TEST_CASE(&ArrayTest::testArrayOperators)); |
| 336 | return suite; |
| 337 | } |
| 338 | |
| 339 | |