/* * (C) Copyright Nick Thompson 2018. * Use, modification and distribution are subject to the * Boost Software License, Version 1.0. (See accompanying file * LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) */ #include #include #include #include #include #include #include #include #include #include #include #include #include using std::abs; using std::pow; using std::sqrt; using boost::multiprecision::cpp_bin_float_50; using boost::multiprecision::cpp_complex_50; using boost::math::tools::lp_norm; using boost::math::tools::l1_norm; using boost::math::tools::l2_norm; using boost::math::tools::sup_norm; using boost::math::tools::lp_distance; using boost::math::tools::l1_distance; using boost::math::tools::l2_distance; using boost::math::tools::sup_distance; using boost::math::tools::total_variation; /* * Test checklist: * 1) Does it work with multiprecision? * 2) Does it work with .cbegin()/.cend() if the data is not altered? * 3) Does it work with ublas and std::array? (Checking Eigen and Armadillo will make the CI system really unhappy.) * 4) Does it work with std::forward_list if a forward iterator is all that is required? * 5) Does it work with complex data if complex data is sensible? */ // To stress test, set global_seed = 0, global_size = huge. static const constexpr size_t global_seed = 834; static const constexpr size_t global_size = 64; template std::vector generate_random_vector(size_t size, size_t seed) { if (seed == 0) { std::random_device rd; seed = rd(); } std::vector v(size); std::mt19937 gen(seed); if constexpr (std::is_floating_point::value) { std::normal_distribution dis(0, 1); for (size_t i = 0; i < v.size(); ++i) { v[i] = dis(gen); } return v; } else if constexpr (std::is_integral::value) { // Rescaling by larger than 2 is UB! std::uniform_int_distribution dis(std::numeric_limits::lowest()/2, (std::numeric_limits::max)()/2); for (size_t i = 0; i < v.size(); ++i) { v[i] = dis(gen); } return v; } else if constexpr (boost::is_complex::value) { std::normal_distribution dis(0, 1); for (size_t i = 0; i < v.size(); ++i) { v[i] = {dis(gen), dis(gen)}; } return v; } else if constexpr (boost::multiprecision::number_category::value == boost::multiprecision::number_kind_complex) { std::normal_distribution dis(0, 1); for (size_t i = 0; i < v.size(); ++i) { v[i] = {dis(gen), dis(gen)}; } return v; } else if constexpr (boost::multiprecision::number_category::value == boost::multiprecision::number_kind_floating_point) { std::normal_distribution dis(0, 1); for (size_t i = 0; i < v.size(); ++i) { v[i] = dis(gen); } return v; } else { BOOST_ASSERT_MSG(false, "Could not identify type for random vector generation."); return v; } } template void test_lp() { Real tol = 50*std::numeric_limits::epsilon(); std::array u{1,0,0}; Real l3 = lp_norm(u.begin(), u.end(), 3); BOOST_TEST(abs(l3 - 1) < tol); u[0] = -8; l3 = lp_norm(u.cbegin(), u.cend(), 3); BOOST_TEST(abs(l3 - 8) < tol); std::vector v(500); for (size_t i = 0; i < v.size(); ++i) { v[i] = 7; } Real l8 = lp_norm(v, 8); Real expected = 7*pow(v.size(), static_cast(1)/static_cast(8)); BOOST_TEST(abs(l8 - expected) < tol*abs(expected)); // Does it work with ublas vectors? // Does it handle the overflow of intermediates? boost::numeric::ublas::vector w(4); Real bignum = sqrt((std::numeric_limits::max)())/256; for (size_t i = 0; i < w.size(); ++i) { w[i] = bignum; } Real l20 = lp_norm(w.cbegin(), w.cend(), 4); expected = bignum*pow(w.size(), static_cast(1)/static_cast(4)); BOOST_TEST(abs(l20 - expected) < tol*expected); v = generate_random_vector(global_size, global_seed); Real scale = 8; Real l7 = scale*lp_norm(v, 7); for (auto & x : v) { x *= -scale; } Real l7_ = lp_norm(v, 7); BOOST_TEST(abs(l7_ - l7) < tol*l7); } template void test_complex_lp() { typedef typename Complex::value_type Real; Real tol = 50*std::numeric_limits::epsilon(); std::vector v{{1,0}, {0,0}, {0,0}}; Real l3 = lp_norm(v.cbegin(), v.cend(), 3); BOOST_TEST(abs(l3 - 1) < tol); l3 = lp_norm(v, 3); BOOST_TEST(abs(l3 - 1) < tol); v = generate_random_vector(global_size, global_seed); Real scale = 8; Real l7 = scale*lp_norm(v, 7); for (auto & x : v) { x *= -scale; } Real l7_ = lp_norm(v, 7); BOOST_TEST(abs(l7_ - l7) < tol*l7); } template void test_integer_lp() { double tol = 100*std::numeric_limits::epsilon(); std::array u{1,0,0}; double l3 = lp_norm(u.begin(), u.end(), 3); BOOST_TEST(abs(l3 - 1) < tol); auto v = generate_random_vector(global_size, global_seed); Z scale = 2; double l7 = scale*lp_norm(v, 7); for (auto & x : v) { x *= scale; } double l7_ = lp_norm(v, 7); BOOST_TEST(abs(l7_ - l7) < tol*l7); } template void test_lp_distance() { Real tol = 100*std::numeric_limits::epsilon(); std::vector u{1,0,0}; std::vector v{0,0,0}; Real dist = lp_distance(u,u, 3); BOOST_TEST(abs(dist) < tol); dist = lp_distance(u,v, 3); BOOST_TEST(abs(dist - 1) < tol); v = generate_random_vector(global_size, global_seed); u = generate_random_vector(global_size, global_seed+1); Real dist1 = lp_distance(u, v, 7); Real dist2 = lp_distance(v, u, 7); BOOST_TEST(abs(dist1 - dist2) < tol*dist1); } template void test_complex_lp_distance() { using Real = typename Complex::value_type; Real tol = 100*std::numeric_limits::epsilon(); std::vector u{{1,0},{0,0},{0,0}}; std::vector v{{0,0},{0,0},{0,0}}; Real dist = boost::math::tools::lp_distance(u,u, 3); BOOST_TEST(abs(dist) < tol); dist = boost::math::tools::lp_distance(u,v, 3); BOOST_TEST(abs(dist - 1) < tol); v = generate_random_vector(global_size, global_seed); u = generate_random_vector(global_size, global_seed + 1); Real dist1 = lp_distance(u, v, 7); Real dist2 = lp_distance(v, u, 7); BOOST_TEST(abs(dist1 - dist2) < tol*dist1); } template void test_integer_lp_distance() { double tol = 100*std::numeric_limits::epsilon(); std::array u{1,0,0}; std::array w{0,0,0}; double l3 = lp_distance(u, w, 3); BOOST_TEST(abs(l3 - 1) < tol); auto v = generate_random_vector(global_size, global_seed); Z scale = 2; for (auto & x : v) { x *= scale; } auto s = generate_random_vector(global_size, global_seed + 1); double dist1 = lp_distance(v, s, 7); double dist2 = lp_distance(s, v, 7); BOOST_TEST(abs(dist1 - dist2) < tol*dist2); } template void test_integer_total_variation() { double eps = std::numeric_limits::epsilon(); std::vector v{1,1}; double tv = boost::math::tools::total_variation(v); BOOST_TEST_EQ(tv, 0); v[1] = 2; tv = boost::math::tools::total_variation(v.begin(), v.end()); BOOST_TEST_EQ(tv, 1); v.resize(16); for (size_t i = 0; i < v.size(); ++i) { v[i] = i; } tv = boost::math::tools::total_variation(v); BOOST_TEST_EQ(tv, v.size() -1); for (size_t i = 0; i < v.size(); ++i) { v[i] = i*i; } tv = boost::math::tools::total_variation(v); BOOST_TEST_EQ(tv, (v.size() - 1)*(v.size() - 1)); // Work with std::array? std::array w{1,1}; tv = boost::math::tools::total_variation(w); BOOST_TEST_EQ(tv,0); std::array u{1, 2, 1, 2}; tv = boost::math::tools::total_variation(u); BOOST_TEST_EQ(tv, 3); v = generate_random_vector(global_size, global_seed); double tv1 = 2*total_variation(v); Z scale = 2; for (auto & x : v) { x *= scale; } double tv2 = total_variation(v); BOOST_TEST(abs(tv1 - tv2) < tv1*eps); } template void test_total_variation() { Real tol = std::numeric_limits::epsilon(); std::vector v{1,1}; Real tv = total_variation(v.begin(), v.end()); BOOST_TEST(tv >= 0 && abs(tv) < tol); tv = total_variation(v); BOOST_TEST(tv >= 0 && abs(tv) < tol); v[1] = 2; tv = total_variation(v.begin(), v.end()); BOOST_TEST(abs(tv - 1) < tol); v.resize(50); for (size_t i = 0; i < v.size(); ++i) { v[i] = i; } tv = total_variation(v.begin(), v.end()); BOOST_TEST(abs(tv - (v.size() -1)) < tol); for (size_t i = 0; i < v.size(); ++i) { v[i] = i*i; } tv = total_variation(v.begin(), v.end()); BOOST_TEST(abs(tv - (v.size() - 1)*(v.size() - 1)) < tol); v = generate_random_vector(global_size, global_seed); Real scale = 8; Real tv1 = scale*total_variation(v); for (auto & x : v) { x *= -scale; } Real tv2 = total_variation(v); BOOST_TEST(abs(tv1 - tv2) < tol*tv1); } template void test_sup_norm() { Real tol = std::numeric_limits::epsilon(); std::vector v{-2,1,0}; Real s = boost::math::tools::sup_norm(v.begin(), v.end()); BOOST_TEST(abs(s - 2) < tol); s = boost::math::tools::sup_norm(v); BOOST_TEST(abs(s - 2) < tol); // Work with std::array? std::array w{-2,1,0}; s = boost::math::tools::sup_norm(w); BOOST_TEST(abs(s - 2) < tol); v = generate_random_vector(global_size, global_seed); Real scale = 8; Real sup1 = scale*sup_norm(v); for (auto & x : v) { x *= -scale; } Real sup2 = sup_norm(v); BOOST_TEST(abs(sup1 - sup2) < tol*sup1); } template void test_integer_sup_norm() { double eps = std::numeric_limits::epsilon(); std::vector v{2,1,0}; Z s = sup_norm(v.begin(), v.end()); BOOST_TEST_EQ(s, 2); s = sup_norm(v); BOOST_TEST_EQ(s,2); v = generate_random_vector(global_size, global_seed); double sup1 = 2*sup_norm(v); Z scale = 2; for (auto & x : v) { x *= scale; } double sup2 = sup_norm(v); BOOST_TEST(abs(sup1 - sup2) < sup1*eps); } template void test_complex_sup_norm() { typedef typename Complex::value_type Real; Real tol = std::numeric_limits::epsilon(); std::vector w{{0,-8}, {1,1}, {3,2}}; Real s = sup_norm(w.cbegin(), w.cend()); BOOST_TEST(abs(s-8) < tol); s = sup_norm(w); BOOST_TEST(abs(s-8) < tol); auto v = generate_random_vector(global_size, global_seed); Real scale = 8; Real sup1 = scale*sup_norm(v); for (auto & x : v) { x *= -scale; } Real sup2 = sup_norm(v); BOOST_TEST(abs(sup1 - sup2) < tol*sup1); } template void test_l0_pseudo_norm() { std::vector v{0,0,1}; size_t count = boost::math::tools::l0_pseudo_norm(v.begin(), v.end()); BOOST_TEST_EQ(count, 1); // Compiles with cbegin()/cend()? count = boost::math::tools::l0_pseudo_norm(v.cbegin(), v.cend()); BOOST_TEST_EQ(count, 1); count = boost::math::tools::l0_pseudo_norm(v); BOOST_TEST_EQ(count, 1); std::array w{0,0,1}; count = boost::math::tools::l0_pseudo_norm(w); BOOST_TEST_EQ(count, 1); } template void test_complex_l0_pseudo_norm() { std::vector v{{0,0}, {0,0}, {1,0}}; size_t count = boost::math::tools::l0_pseudo_norm(v.begin(), v.end()); BOOST_TEST_EQ(count, 1); count = boost::math::tools::l0_pseudo_norm(v); BOOST_TEST_EQ(count, 1); } template void test_hamming_distance() { std::vector v{1,2,3}; std::vector w{1,2,4}; size_t count = boost::math::tools::hamming_distance(v, w); BOOST_TEST_EQ(count, 1); count = boost::math::tools::hamming_distance(v, v); BOOST_TEST_EQ(count, 0); } template void test_l1_norm() { Real tol = std::numeric_limits::epsilon(); std::vector v{1,1,1}; Real l1 = l1_norm(v.begin(), v.end()); BOOST_TEST(abs(l1 - 3) < tol); l1 = l1_norm(v); BOOST_TEST(abs(l1 - 3) < tol); std::array w{1,1,1}; l1 = l1_norm(w); BOOST_TEST(abs(l1 - 3) < tol); v = generate_random_vector(global_size, global_seed); Real scale = 8; Real l1_1 = scale*l1_norm(v); for (auto & x : v) { x *= -scale; } Real l1_2 = l1_norm(v); BOOST_TEST(abs(l1_1 - l1_2) < tol*l1_1); } template void test_integer_l1_norm() { double eps = std::numeric_limits::epsilon(); std::vector v{1,1,1}; Z l1 = boost::math::tools::l1_norm(v.begin(), v.end()); BOOST_TEST_EQ(l1, 3); v = generate_random_vector(global_size, global_seed); double l1_1 = 2*l1_norm(v); Z scale = 2; for (auto & x : v) { x *= scale; } double l1_2 = l1_norm(v); BOOST_TEST(l1_1 > 0); BOOST_TEST(l1_2 > 0); if (abs(l1_1 - l1_2) > 2*l1_1*eps) { std::cout << std::setprecision(std::numeric_limits::digits10); std::cout << "L1_1 = " << l1_1 << "\n"; std::cout << "L1_2 = " << l1_2 << "\n"; BOOST_TEST(abs(l1_1 - l1_2) < 2*l1_1*eps); } } template void test_complex_l1_norm() { typedef typename Complex::value_type Real; Real tol = std::numeric_limits::epsilon(); std::vector v{{1,0}, {0,1},{0,-1}}; Real l1 = l1_norm(v.begin(), v.end()); BOOST_TEST(abs(l1 - 3) < tol); l1 = l1_norm(v); BOOST_TEST(abs(l1 - 3) < tol); v = generate_random_vector(global_size, global_seed); Real scale = 8; Real l1_1 = scale*l1_norm(v); for (auto & x : v) { x *= -scale; } Real l1_2 = l1_norm(v); BOOST_TEST(abs(l1_1 - l1_2) < tol*l1_1); } template void test_l1_distance() { Real tol = std::numeric_limits::epsilon(); std::vector v{1,2,3}; std::vector w{1,1,1}; Real l1 = boost::math::tools::l1_distance(v, v); BOOST_TEST(abs(l1) < tol); l1 = boost::math::tools::l1_distance(w, v); BOOST_TEST(abs(l1 - 3) < tol); l1 = boost::math::tools::l1_distance(v, w); BOOST_TEST(abs(l1 - 3) < tol); v = generate_random_vector(global_size, global_seed); w = generate_random_vector(global_size, global_seed+1); Real dist1 = l1_distance(v, w); Real dist2 = l1_distance(w, v); BOOST_TEST(abs(dist1 - dist2) < tol*dist1); } template void test_integer_l1_distance() { double tol = std::numeric_limits::epsilon(); std::vector v{1,2,3}; std::vector w{1,1,1}; double l1 = boost::math::tools::l1_distance(v, v); BOOST_TEST(abs(l1) < tol); l1 = boost::math::tools::l1_distance(w, v); BOOST_TEST(abs(l1 - 3) < tol); l1 = boost::math::tools::l1_distance(v, w); BOOST_TEST(abs(l1 - 3) < tol); v = generate_random_vector(global_size, global_seed); w = generate_random_vector(global_size, global_seed + 1); double dist1 = l1_distance(v, w); double dist2 = l1_distance(w, v); BOOST_TEST(abs(dist1 - dist2) < tol*dist1); } template void test_complex_l1_distance() { typedef typename Complex::value_type Real; Real tol = std::numeric_limits::epsilon(); std::vector v{{1,0}, {0,1},{0,-1}}; Real l1 = boost::math::tools::l1_distance(v, v); BOOST_TEST(abs(l1) < tol); std::vector w{{2,0}, {0,1},{0,-1}}; l1 = boost::math::tools::l1_distance(v.cbegin(), v.cend(), w.cbegin()); BOOST_TEST(abs(l1 - 1) < tol); v = generate_random_vector(global_size, global_seed); w = generate_random_vector(global_size, global_seed + 1); Real dist1 = l1_distance(v, w); Real dist2 = l1_distance(w, v); BOOST_TEST(abs(dist1 - dist2) < tol*dist1); } template void test_l2_norm() { using std::sqrt; Real tol = std::numeric_limits::epsilon(); std::vector v{1,1,1,1}; Real l2 = boost::math::tools::l2_norm(v.begin(), v.end()); BOOST_TEST(abs(l2 - 2) < tol); l2 = boost::math::tools::l2_norm(v); BOOST_TEST(abs(l2 - 2) < tol); std::array w{1,1,1,1}; l2 = boost::math::tools::l2_norm(w); BOOST_TEST(abs(l2 - 2) < tol); Real bignum = 4*sqrt((std::numeric_limits::max)()); v[0] = bignum; v[1] = 0; v[2] = 0; v[3] = 0; l2 = boost::math::tools::l2_norm(v.begin(), v.end()); BOOST_TEST(abs(l2 - bignum) < tol*l2); v = generate_random_vector(global_size, global_seed); Real scale = 8; Real l2_1 = scale*l2_norm(v); for (auto & x : v) { x *= -scale; } Real l2_2 = l2_norm(v); BOOST_TEST(l2_1 > 0); BOOST_TEST(l2_2 > 0); BOOST_TEST(abs(l2_1 - l2_2) < tol*l2_1); } template void test_integer_l2_norm() { double tol = 100*std::numeric_limits::epsilon(); std::vector v{1,1,1,1}; double l2 = boost::math::tools::l2_norm(v.begin(), v.end()); BOOST_TEST(abs(l2 - 2) < tol); v = generate_random_vector(global_size, global_seed); Z scale = 2; double l2_1 = scale*l2_norm(v); for (auto & x : v) { x *= scale; } double l2_2 = l2_norm(v); BOOST_TEST(l2_1 > 0); BOOST_TEST(l2_2 > 0); BOOST_TEST(abs(l2_1 - l2_2) < tol*l2_1); } template void test_complex_l2_norm() { typedef typename Complex::value_type Real; Real tol = 100*std::numeric_limits::epsilon(); std::vector v{{1,0}, {0,1},{0,-1}, {1,0}}; Real l2 = boost::math::tools::l2_norm(v.begin(), v.end()); BOOST_TEST(abs(l2 - 2) < tol); l2 = boost::math::tools::l2_norm(v); BOOST_TEST(abs(l2 - 2) < tol); v = generate_random_vector(global_size, global_seed); Real scale = 8; Real l2_1 = scale*l2_norm(v); for (auto & x : v) { x *= -scale; } Real l2_2 = l2_norm(v); BOOST_TEST(abs(l2_1 - l2_2) < tol*l2_1); } template void test_l2_distance() { Real tol = std::numeric_limits::epsilon(); std::vector v{1,1,1,1}; Real l2 = boost::math::tools::l2_distance(v, v); BOOST_TEST(abs(l2) < tol); v = generate_random_vector(global_size, global_seed); auto w = generate_random_vector(global_size, global_seed + 1); Real dist1 = l2_distance(v, w); Real dist2 = l2_distance(w, v); BOOST_TEST(abs(dist1 - dist2) < tol*dist1); } template void test_integer_l2_distance() { double tol = std::numeric_limits::epsilon(); std::vector v{1,1,1,1}; double l2 = boost::math::tools::l2_distance(v, v); BOOST_TEST(abs(l2) < tol); v = generate_random_vector(global_size, global_seed); auto w = generate_random_vector(global_size, global_seed + 1); double dist1 = l2_distance(v, w); double dist2 = l2_distance(w, v); BOOST_TEST(abs(dist1 - dist2) < tol*dist1); } template void test_complex_l2_distance() { typedef typename Complex::value_type Real; Real tol = 100*std::numeric_limits::epsilon(); std::vector v{{1,0}, {0,1},{0,-1}, {1,0}}; Real l2 = boost::math::tools::l2_distance(v, v); BOOST_TEST(abs(l2) < tol); v = generate_random_vector(global_size, global_seed); auto w = generate_random_vector(global_size, global_seed + 1); Real dist1 = l2_distance(v, w); Real dist2 = l2_distance(w, v); BOOST_TEST(abs(dist1 - dist2) < tol*dist1); } template void test_sup_distance() { Real tol = std::numeric_limits::epsilon(); std::vector v{1,1,1,1}; std::vector w{0,0,0,0}; Real sup = boost::math::tools::sup_distance(v, v); BOOST_TEST(abs(sup) < tol); sup = boost::math::tools::sup_distance(v, w); BOOST_TEST(abs(sup -1) < tol); v = generate_random_vector(global_size, global_seed); w = generate_random_vector(global_size, global_seed + 1); Real dist1 = sup_distance(v, w); Real dist2 = sup_distance(w, v); BOOST_TEST(abs(dist1 - dist2) < tol*dist1); } template void test_integer_sup_distance() { double tol = std::numeric_limits::epsilon(); std::vector v{1,1,1,1}; std::vector w{0,0,0,0}; double sup = boost::math::tools::sup_distance(v, v); BOOST_TEST(abs(sup) < tol); sup = boost::math::tools::sup_distance(v, w); BOOST_TEST(abs(sup -1) < tol); v = generate_random_vector(global_size, global_seed); w = generate_random_vector(global_size, global_seed + 1); double dist1 = sup_distance(v, w); double dist2 = sup_distance(w, v); BOOST_TEST(abs(dist1 - dist2) < tol*dist1); } template void test_complex_sup_distance() { typedef typename Complex::value_type Real; Real tol = 100*std::numeric_limits::epsilon(); std::vector v{{1,0}, {0,1},{0,-1}, {1,0}}; Real sup = boost::math::tools::sup_distance(v, v); BOOST_TEST(abs(sup) < tol); v = generate_random_vector(global_size, global_seed); auto w = generate_random_vector(global_size, global_seed + 1); Real dist1 = sup_distance(v, w); Real dist2 = sup_distance(w, v); BOOST_TEST(abs(dist1 - dist2) < tol*dist1); } int main() { test_l0_pseudo_norm(); test_l0_pseudo_norm(); test_l0_pseudo_norm(); test_l0_pseudo_norm(); test_l0_pseudo_norm(); test_l0_pseudo_norm(); test_complex_l0_pseudo_norm>(); test_complex_l0_pseudo_norm>(); test_complex_l0_pseudo_norm>(); test_complex_l0_pseudo_norm(); test_hamming_distance(); test_hamming_distance(); test_l1_norm(); test_l1_norm(); test_l1_norm(); test_l1_norm(); test_integer_l1_norm(); test_integer_l1_norm(); test_complex_l1_norm>(); test_complex_l1_norm>(); test_complex_l1_norm>(); test_complex_l1_norm(); test_l1_distance(); test_l1_distance(); test_integer_l1_distance(); test_integer_l1_distance(); test_complex_l1_distance>(); test_complex_l1_distance(); test_complex_l2_norm>(); test_complex_l2_norm>(); test_complex_l2_norm>(); test_complex_l2_norm(); test_l2_norm(); test_l2_norm(); test_l2_norm(); test_l2_norm(); test_integer_l2_norm(); test_integer_l2_norm(); test_l2_distance(); test_l2_distance(); test_integer_l2_distance(); test_integer_l2_distance(); test_complex_l2_distance>(); test_complex_l2_distance(); test_lp(); test_lp(); test_lp(); test_lp(); test_complex_lp>(); test_complex_lp>(); test_complex_lp>(); test_complex_lp(); test_integer_lp(); test_integer_lp(); test_lp_distance(); test_lp_distance(); test_complex_lp_distance>(); test_complex_lp_distance(); test_integer_lp_distance(); test_integer_lp_distance(); test_sup_norm(); test_sup_norm(); test_sup_norm(); test_sup_norm(); test_integer_sup_norm(); test_integer_sup_norm(); test_complex_sup_norm>(); test_complex_sup_norm>(); test_complex_sup_norm>(); test_complex_sup_norm(); test_sup_distance(); test_sup_distance(); test_integer_sup_distance(); test_integer_sup_distance(); test_complex_sup_distance>(); test_complex_sup_distance(); test_total_variation(); test_total_variation(); test_total_variation(); test_total_variation(); test_integer_total_variation(); test_integer_total_variation(); return boost::report_errors(); }