|
2 | 2 | * Copyright (c) 2021-2025 Morwenn |
3 | 3 | * SPDX-License-Identifier: MIT |
4 | 4 | */ |
| 5 | +#include <algorithm> |
5 | 6 | #include <numeric> |
| 7 | +#include <random> |
6 | 8 | #include <type_traits> |
7 | 9 | #include <vector> |
8 | 10 | #include <catch2/catch_template_test_macros.hpp> |
9 | 11 | #include <rapidcheck.h> |
10 | 12 | #include <rapidcheck/catch.h> |
11 | 13 | #include <cpp-sort/probes.h> |
| 14 | +#include "testing-tools/random.h" |
12 | 15 |
|
13 | 16 | // |
14 | 17 | // Common tests for measures of presortedness |
@@ -161,3 +164,80 @@ TEMPLATE_TEST_CASE( "test M(aX) <= |X| + M(X) for most probes M", "[probe]", |
161 | 164 | return mop(sequence) <= (size - 1) + mop(sequence.begin() + 1, sequence.end()); |
162 | 165 | }); |
163 | 166 | } |
| 167 | + |
| 168 | +TEMPLATE_TEST_CASE( "test prefix monoticity", "[probe]", |
| 169 | + decltype(cppsort::probe::dis), |
| 170 | + decltype(cppsort::probe::enc), |
| 171 | + decltype(cppsort::probe::exc), |
| 172 | + decltype(cppsort::probe::ham), |
| 173 | + decltype(cppsort::probe::inv), |
| 174 | + decltype(cppsort::probe::max), |
| 175 | + decltype(cppsort::probe::rem), |
| 176 | + decltype(cppsort::probe::runs), |
| 177 | + decltype(cppsort::probe::spear), |
| 178 | + decltype(cppsort::probe::sus) ) |
| 179 | +{ |
| 180 | + // Property formalized by Estivill-Castro in *Sorting and Measures of Disorder* |
| 181 | + // The following probes don't satisfy it: Block, Mono, Osc |
| 182 | + |
| 183 | + // Note: the original paper claims that Osc also satisfies this property, |
| 184 | + // but it fails for X=⟨3, 0⟩ Y=⟨⟩ Z=⟨4, 2⟩ |
| 185 | + |
| 186 | + rc::prop("prefix monoticity", [](std::vector<int> sequence) { |
| 187 | + using diff_t = std::vector<int>::difference_type; |
| 188 | + using param_t = std::uniform_int_distribution<diff_t>::param_type; |
| 189 | + |
| 190 | + auto size = static_cast<diff_t>(sequence.size()); |
| 191 | + if (size < 3) { |
| 192 | + return true; |
| 193 | + } |
| 194 | + |
| 195 | + // Split the sequence into three consequent subsequences X, Y and Z |
| 196 | + std::uniform_int_distribution<diff_t> dist; |
| 197 | + auto x_begin = sequence.begin(); |
| 198 | + auto y_begin = x_begin + dist(hasard::engine(), param_t{0, size - 1}); |
| 199 | + auto z_begin = x_begin + dist(hasard::engine(), param_t{y_begin - x_begin, size - 1}); |
| 200 | + |
| 201 | + // Ensure that all elements of Z are greater than all elements of X and Y |
| 202 | + std::nth_element(x_begin, z_begin, sequence.end()); |
| 203 | + |
| 204 | + std::decay_t<TestType> mop; |
| 205 | + auto disorder_x = mop(x_begin, y_begin); |
| 206 | + auto disorder_y = mop(y_begin, z_begin); |
| 207 | + auto disorder_yz = mop(y_begin, sequence.end()); |
| 208 | + auto new_x_begin = std::rotate(x_begin, y_begin, z_begin); |
| 209 | + auto disorder_xz = mop(new_x_begin, sequence.end()); |
| 210 | + |
| 211 | + return disorder_x <= disorder_y |
| 212 | + ? disorder_xz <= disorder_yz |
| 213 | + : disorder_yz <= disorder_xz; |
| 214 | + }); |
| 215 | +} |
| 216 | + |
| 217 | +TEMPLATE_TEST_CASE( "test M(XY) = M(X) + M(Y) if X <= Y for most probes M", "[probe]", |
| 218 | + decltype(cppsort::probe::exc), |
| 219 | + decltype(cppsort::probe::ham), |
| 220 | + decltype(cppsort::probe::inv), |
| 221 | + decltype(cppsort::probe::rem), |
| 222 | + decltype(cppsort::probe::runs), |
| 223 | + decltype(cppsort::probe::spear) ) |
| 224 | +{ |
| 225 | + // Property formalized by Estivill-Castro in *Sorting and Measures of Disorder* |
| 226 | + // Not all measures of presortedness satisfy it, but a lot do |
| 227 | + |
| 228 | + rc::prop("M(XY) = M(X) + M(Y) if X ≤ Y", [](std::vector<int> sequence) { |
| 229 | + using diff_t = std::vector<int>::difference_type; |
| 230 | + using param_t = std::uniform_int_distribution<diff_t>::param_type; |
| 231 | + |
| 232 | + // Split the sequence into two consequent subsequences X and Y |
| 233 | + auto size = static_cast<diff_t>(sequence.size()); |
| 234 | + std::uniform_int_distribution<diff_t> dist; |
| 235 | + auto x_begin = sequence.begin(); |
| 236 | + auto y_begin = x_begin + dist(hasard::engine(), param_t{0, size}); |
| 237 | + std::cout << "nooooo: " << (y_begin - x_begin) << std::endl; |
| 238 | + std::nth_element(x_begin, y_begin, sequence.end()); |
| 239 | + |
| 240 | + std::decay_t<TestType> mop; |
| 241 | + return mop(sequence) == mop(x_begin, y_begin) + mop(y_begin, sequence.end()); |
| 242 | + }); |
| 243 | +} |
0 commit comments