diff --git a/benchmarks/CMakeLists.txt b/benchmarks/CMakeLists.txt index aedb0d57bd3..56d22d72198 100644 --- a/benchmarks/CMakeLists.txt +++ b/benchmarks/CMakeLists.txt @@ -111,6 +111,7 @@ add_benchmark(locale_classic src/locale_classic.cpp) add_benchmark(minmax_element src/minmax_element.cpp) add_benchmark(mismatch src/mismatch.cpp) add_benchmark(move_only_function src/move_only_function.cpp) +add_benchmark(nth_element src/nth_element.cpp) add_benchmark(path_lexically_normal src/path_lexically_normal.cpp) add_benchmark(priority_queue_push_range src/priority_queue_push_range.cpp) add_benchmark(random_integer_generation src/random_integer_generation.cpp) diff --git a/benchmarks/src/nth_element.cpp b/benchmarks/src/nth_element.cpp new file mode 100644 index 00000000000..7c6cc99affb --- /dev/null +++ b/benchmarks/src/nth_element.cpp @@ -0,0 +1,140 @@ +// Copyright (c) Microsoft Corporation. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#include +#include +#include +#include +#include + +using namespace std; + +constexpr int tukey_ninther_adversary1[] = {0, 6, 12, 18, 22, 28, 34, 38, 44, 50, 54, 60, 66, 70, 76, 82, 86, 92, 98, + 102, 108, 114, 118, 124, 130, 134, 140, 146, 150, 156, 162, 166, 172, 178, 182, 188, 194, 198, 204, 210, 214, 220, + 226, 230, 236, 242, 246, 252, 258, 262, 268, 274, 278, 284, 290, 294, 300, 306, 310, 316, 322, 326, 332, 338, 342, + 348, 354, 358, 364, 370, 374, 380, 386, 390, 396, 402, 406, 412, 418, 422, 428, 434, 438, 444, 450, 454, 460, 466, + 470, 476, 482, 486, 492, 498, 502, 508, 514, 518, 524, 530, 534, 540, 546, 550, 556, 562, 566, 572, 578, 582, 588, + 594, 598, 604, 610, 614, 620, 626, 630, 636, 642, 646, 652, 658, 662, 668, 674, 678, 1, 7, 13, 684, 19, 23, 29, 690, + 35, 39, 45, 694, 51, 55, 61, 700, 67, 71, 77, 706, 83, 87, 93, 710, 99, 103, 109, 716, 115, 119, 125, 722, 131, 135, + 141, 726, 147, 151, 157, 732, 163, 167, 173, 738, 179, 183, 189, 742, 195, 199, 205, 748, 211, 215, 221, 754, 227, + 231, 237, 758, 243, 247, 253, 764, 259, 263, 269, 770, 275, 279, 285, 774, 291, 295, 301, 780, 307, 311, 317, 786, + 323, 327, 333, 790, 339, 343, 349, 796, 355, 359, 365, 802, 371, 375, 381, 806, 387, 391, 397, 812, 403, 407, 413, + 818, 419, 423, 429, 822, 435, 439, 445, 828, 451, 455, 461, 834, 467, 471, 477, 838, 483, 487, 493, 844, 499, 503, + 509, 850, 515, 519, 525, 854, 531, 535, 541, 860, 547, 551, 557, 866, 563, 567, 573, 870, 579, 583, 589, 876, 595, + 599, 605, 882, 611, 615, 621, 886, 627, 631, 637, 892, 643, 647, 653, 898, 659, 663, 669, 902, 675, 679, 685, 908, + 691, 695, 701, 914, 707, 711, 717, 918, 723, 727, 733, 924, 739, 743, 749, 930, 755, 759, 765, 934, 771, 775, 781, + 940, 787, 791, 797, 946, 803, 807, 813, 950, 819, 823, 829, 956, 835, 839, 845, 962, 851, 855, 861, 966, 867, 871, + 877, 972, 883, 887, 893, 978, 899, 903, 909, 982, 915, 919, 925, 988, 931, 935, 941, 990, 947, 951, 957, 992, 963, + 967, 973, 1024, 979, 983, 993, 994, 995, 996, 997, 998, 999, 1000, 1001, 1002, 2, 20, 8, 14, 24, 36, 30, 40, 52, 46, + 56, 68, 62, 72, 84, 78, 88, 100, 94, 104, 116, 110, 120, 132, 126, 136, 148, 142, 152, 164, 158, 168, 180, 174, 184, + 196, 190, 200, 212, 206, 216, 228, 222, 232, 244, 238, 248, 260, 254, 264, 276, 270, 280, 292, 286, 296, 308, 302, + 312, 324, 318, 328, 340, 334, 344, 356, 350, 360, 372, 366, 376, 388, 382, 392, 404, 398, 408, 420, 414, 424, 436, + 430, 440, 452, 446, 456, 468, 462, 472, 484, 478, 488, 500, 494, 504, 516, 510, 520, 532, 526, 536, 548, 542, 552, + 564, 558, 568, 580, 574, 584, 596, 590, 600, 612, 606, 616, 628, 622, 632, 644, 638, 648, 660, 654, 664, 676, 670, + 512, 3, 991, 9, 773, 768, 15, 680, 901, 21, 522, 25, 777, 527, 31, 692, 965, 37, 528, 41, 960, 778, 47, 686, 905, + 53, 538, 57, 936, 543, 63, 696, 1007, 69, 544, 73, 789, 784, 79, 708, 1014, 85, 554, 89, 793, 559, 95, 702, 969, + 101, 560, 105, 911, 794, 111, 712, 974, 117, 570, 121, 948, 575, 127, 724, 1015, 133, 576, 137, 805, 800, 143, 718, + 917, 149, 586, 153, 809, 591, 159, 728, 1008, 165, 592, 169, 970, 810, 175, 740, 921, 181, 602, 185, 942, 607, 191, + 734, 1009, 197, 608, 201, 821, 816, 207, 744, 975, 213, 618, 217, 825, 623, 223, 756, 1005, 229, 624, 233, 927, 826, + 239, 750, 984, 245, 634, 249, 952, 639, 255, 760, 1010, 261, 640, 265, 837, 832, 271, 772, 933, 277, 650, 281, 841, + 655, 287, 766, 981, 293, 656, 297, 976, 842, 303, 776, 937, 309, 666, 313, 964, 671, 319, 788, 1011, 325, 672, 329, + 853, 848, 335, 782, 1016, 341, 682, 345, 857, 687, 351, 792, 985, 357, 688, 361, 943, 858, 367, 804, 1003, 373, 698, + 377, 958, 703, 383, 798, 1017, 389, 704, 393, 869, 864, 399, 808, 949, 405, 714, 409, 873, 719, 415, 820, 1012, 421, + 720, 425, 986, 874, 431, 814, 953, 437, 730, 441, 968, 735, 447, 824, 1013, 453, 736, 457, 885, 880, 463, 836, 989, + 469, 746, 473, 889, 751, 479, 830, 1006, 485, 752, 489, 959, 890, 495, 840, 1004, 501, 762, 505, 980, 767, 511, 852, + 4, 517, 10, 521, 16, 896, 26, 846, 32, 533, 42, 537, 48, 783, 58, 856, 64, 549, 74, 553, 80, 906, 90, 868, 96, 565, + 106, 569, 112, 799, 122, 862, 128, 581, 138, 585, 144, 912, 154, 72, 160, 597, 170, 601, 176, 815, 186, 884, 192, + 613, 202, 617, 208, 922, 218, 878, 224, 629, 234, 633, 240, 831, 250, 888, 256, 645, 266, 649, 272, 928, 282, 900, + 288, 661, 298, 665, 304, 847, 314, 894, 320, 677, 330, 681, 336, 938, 346, 904, 352, 693, 362, 697, 368, 863, 378, + 916, 384, 709, 394, 713, 400, 944, 410, 910, 416, 725, 426, 729, 432, 879, 442, 920, 448, 741, 458, 745, 464, 954, + 474, 932, 480, 757, 490, 761, 496, 895, 506, 926, 5, 11, 17, 27, 33, 43, 49, 59, 65, 75, 81, 91, 97, 107, 113, 123, + 129, 139, 145, 155, 161, 71, 177, 187, 193, 203, 209, 219, 225, 235, 241, 251, 257, 267, 273, 283, 289, 299, 305, + 315, 321, 331, 337, 347, 353, 363, 369, 379, 385, 395, 401, 411, 417, 427, 433, 443, 449, 459, 465, 475, 481, 491, + 497, 507, 513, 523, 529, 539, 545, 555, 561, 571, 577, 587, 593, 603, 609, 619, 625, 635, 641, 651, 657, 667, 673, + 683, 689, 699, 705, 715, 721, 731, 737, 747, 753, 763, 769, 779, 785, 795, 801, 811, 817, 827, 833, 843, 849, 859, + 865, 875, 881, 891, 897, 907, 913, 923, 929, 939, 945, 955, 961, 971, 977, 987, 1018, 1019, 1020, 1021, 1022}; + +constexpr int tukey_ninther_adversary2[] = {1024, 31, 30, 29, 28, 36, 46, 51, 61, 67, 77, 82, 92, 98, 108, 118, 123, + 133, 139, 149, 154, 164, 170, 180, 190, 195, 205, 211, 221, 226, 236, 242, 252, 262, 267, 277, 283, 293, 298, 308, + 314, 324, 334, 339, 349, 355, 365, 370, 380, 386, 396, 406, 411, 421, 427, 437, 442, 452, 458, 468, 478, 483, 493, + 499, 509, 514, 524, 530, 540, 550, 555, 565, 571, 581, 586, 596, 602, 612, 622, 627, 637, 643, 653, 658, 668, 674, + 684, 694, 699, 709, 715, 725, 730, 740, 746, 756, 766, 771, 781, 787, 797, 802, 812, 818, 828, 838, 843, 853, 859, + 869, 874, 884, 890, 900, 910, 915, 925, 931, 941, 946, 956, 962, 972, 982, 987, 997, 1003, 1013, 1018, 343, 523, + 127, 529, 86, 539, 358, 549, 178, 554, 107, 564, 374, 570, 183, 580, 384, 585, 389, 595, 394, 601, 399, 611, 404, + 621, 409, 626, 18, 636, 419, 642, 425, 652, 318, 657, 435, 667, 440, 673, 169, 683, 450, 693, 224, 698, 256, 708, + 179, 714, 66, 724, 234, 729, 487, 739, 189, 745, 34, 755, 502, 765, 71, 770, 188, 780, 518, 786, 76, 796, 528, 801, + 533, 811, 538, 817, 543, 827, 548, 837, 553, 842, 163, 852, 563, 858, 569, 868, 286, 873, 579, 883, 584, 889, 70, + 899, 594, 909, 219, 914, 97, 924, 235, 930, 91, 940, 462, 945, 631, 955, 173, 961, 241, 971, 646, 981, 322, 986, + 272, 996, 662, 1002, 327, 1012, 672, 1017, 677, 1023, 682, 184, 687, 44, 692, 80, 697, 703, 390, 347, 707, 193, 713, + 718, 534, 276, 723, 26, 728, 734, 32, 200, 738, 744, 368, 749, 137, 754, 15, 759, 25, 764, 378, 769, 775, 575, 90, + 779, 148, 785, 790, 297, 215, 795, 225, 800, 806, 447, 56, 810, 816, 27, 821, 153, 826, 616, 831, 152, 836, 59, 841, + 847, 472, 632, 851, 116, 857, 862, 430, 214, 867, 647, 872, 878, 106, 251, 882, 888, 21, 893, 332, 898, 87, 903, + 132, 908, 678, 913, 919, 456, 168, 923, 230, 929, 934, 519, 466, 939, 19, 944, 950, 353, 471, 954, 960, 364, 965, + 719, 970, 271, 975, 544, 980, 369, 985, 991, 210, 491, 995, 282, 1001, 1006, 750, 250, 1011, 121, 1016, 1022, 385, + 292, 507, 65, 512, 255, 102, 776, 395, 45, 159, 287, 522, 260, 591, 791, 405, 117, 204, 307, 49, 265, 302, 807, 606, + 400, 22, 111, 16, 344, 822, 420, 313, 303, 415, 832, 275, 203, 559, 23, 416, 426, 323, 848, 281, 231, 209, 317, 574, + 436, 17, 863, 142, 333, 220, 488, 431, 441, 81, 879, 75, 590, 451, 663, 328, 446, 894, 296, 375, 600, 261, 904, 457, + 348, 605, 337, 610, 60, 688, 920, 467, 96, 615, 461, 620, 306, 354, 935, 477, 704, 625, 24, 312, 128, 143, 951, 476, + 635, 174, 131, 641, 240, 966, 492, 481, 138, 359, 976, 40, 363, 651, 735, 656, 498, 379, 992, 20, 497, 39, 560, 666, + 508, 112, 1007, 246, 158, 162, 760, 503, 513, 482, 291, 410, 245, 338, 199, 266, 147, 194, 101, 122, 55, 50, 14, 13, + 43, 48, 58, 64, 74, 79, 95, 105, 115, 120, 130, 136, 146, 151, 167, 177, 187, 192, 202, 208, 218, 223, 239, 249, + 259, 264, 274, 280, 290, 295, 311, 321, 331, 336, 346, 352, 362, 367, 383, 393, 403, 408, 418, 424, 434, 439, 455, + 465, 475, 480, 490, 496, 506, 511, 527, 537, 547, 552, 562, 568, 578, 583, 599, 609, 619, 624, 634, 640, 650, 655, + 671, 681, 691, 696, 706, 712, 722, 727, 743, 753, 763, 768, 778, 784, 794, 799, 815, 825, 835, 840, 850, 856, 866, + 871, 887, 897, 907, 912, 922, 928, 938, 943, 959, 969, 979, 984, 994, 1000, 1010, 1015, 1021, 1005, 990, 953, 974, + 964, 949, 933, 918, 881, 902, 892, 877, 861, 846, 809, 830, 820, 805, 789, 774, 737, 758, 748, 733, 717, 702, 665, + 686, 676, 661, 645, 630, 593, 614, 604, 589, 573, 558, 521, 542, 532, 517, 501, 486, 449, 470, 460, 445, 429, 414, + 377, 398, 388, 373, 357, 342, 305, 326, 316, 301, 285, 270, 233, 254, 244, 229, 213, 198, 161, 182, 172, 157, 141, + 126, 89, 110, 100, 85, 69, 54, 12, 38, 11, 10, 9, 8, 53, 84, 99, 125, 156, 171, 197, 228, 243, 269, 300, 315, 341, + 372, 387, 413, 444, 459, 485, 516, 531, 557, 588, 603, 629, 660, 675, 701, 732, 747, 773, 804, 819, 845, 876, 891, + 917, 948, 963, 989, 1020, 1014, 63, 253, 258, 263, 279, 273, 284, 289, 294, 310, 304, 320, 73, 325, 330, 335, 351, + 345, 356, 361, 366, 382, 376, 392, 88, 397, 402, 407, 423, 417, 428, 433, 438, 454, 448, 464, 109, 469, 474, 479, + 495, 489, 500, 505, 510, 526, 520, 536, 135, 541, 546, 551, 567, 561, 572, 577, 582, 598, 592, 608, 145, 613, 618, + 623, 639, 633, 644, 649, 654, 670, 664, 680, 160, 685, 690, 695, 711, 705, 716, 721, 726, 742, 736, 752, 181, 757, + 762, 767, 783, 777, 788, 793, 798, 814, 808, 824, 207, 829, 834, 839, 855, 849, 860, 865, 870, 886, 880, 896, 217, + 901, 906, 911, 927, 921, 932, 937, 942, 958, 952, 968, 232, 973, 978, 983, 999, 993, 1004, 1009, 248, 238, 222, 212, + 201, 191, 186, 37, 176, 166, 150, 140, 129, 119, 114, 3, 104, 94, 78, 68, 57, 47, 42, 6, 7, 4, 5, 2, -1, 33, 35, 41, + 52, 62, 72, 83, 93, 103, 113, 124, 134, 144, 155, 165, 175, 185, 196, 206, 216, 227, 237, 247, 257, 268, 278, 288, + 299, 309, 319, 329, 340, 350, 360, 371, 381, 391, 401, 412, 422, 432, 443, 453, 463, 473, 484, 494, 504, 515, 525, + 535, 545, 556, 566, 576, 587, 597, 607, 617, 628, 638, 648, 659, 669, 679, 689, 700, 710, 720, 731, 741, 751, 761, + 772, 782, 792, 803, 813, 823, 833, 844, 854, 864, 875, 885, 895, 905, 916, 926, 936, 947, 957, 967, 977, 988, 998, + 1008, 1019}; + +enum class alg_type { std_fn, rng }; + +template +void benchmark_common(benchmark::State& state, const Src& src) { + vector v; + v.reserve(size(src)); + + for (auto _ : state) { + v.assign(begin(src), end(src)); + benchmark::DoNotOptimize(v); + auto mid = v.begin() + (v.size() / 2); + if constexpr (Type == alg_type::std_fn) { + nth_element(v.begin(), mid, v.end()); + } else { + ranges::nth_element(v.begin(), mid, v.end()); + } + benchmark::DoNotOptimize(*mid); + } +} + +template +void bm_uniform(benchmark::State& state) { + vector src(static_cast(state.range())); + mt19937 gen(84710); + uniform_int_distribution dis(1, 580); + ranges::generate(src, [&] { return dis(gen); }); + benchmark_common(state, src); +} + +BENCHMARK(bm_uniform)->Arg(1024)->Arg(2048)->Arg(4096)->Arg(8192); +BENCHMARK(bm_uniform)->Arg(1024)->Arg(2048)->Arg(4096)->Arg(8192); + +BENCHMARK_CAPTURE(benchmark_common, adversary1, tukey_ninther_adversary1); +BENCHMARK_CAPTURE(benchmark_common, adversary1, tukey_ninther_adversary1); +BENCHMARK_CAPTURE(benchmark_common, adversary2, tukey_ninther_adversary2); +BENCHMARK_CAPTURE(benchmark_common, adversary2, tukey_ninther_adversary2); + +BENCHMARK_MAIN(); diff --git a/stl/inc/algorithm b/stl/inc/algorithm index 5da863e79a1..b49c4e041a7 100644 --- a/stl/inc/algorithm +++ b/stl/inc/algorithm @@ -3901,6 +3901,56 @@ namespace ranges { _EXPORT_STD template using swap_ranges_result = in_in_result<_In1, _In2>; + template + _NODISCARD constexpr swap_ranges_result<_It1, _It2> _Swap_ranges_common( + _It1 _First1, const _Se1 _Last1, _It2 _First2, const _Se2 _Last2) { + _STL_INTERNAL_STATIC_ASSERT(input_iterator<_It1>); + _STL_INTERNAL_STATIC_ASSERT(sentinel_for<_Se1, _It1>); + _STL_INTERNAL_STATIC_ASSERT(input_iterator<_It2>); + _STL_INTERNAL_STATIC_ASSERT(sentinel_for<_Se2, _It2>); + _STL_INTERNAL_STATIC_ASSERT(indirectly_swappable<_It1, _It2>); + +#if _USE_STD_VECTOR_ALGORITHMS + using _Elem1 = remove_reference_t>; + using _Elem2 = remove_reference_t>; + if constexpr (same_as<_Elem1, _Elem2> && _Is_trivially_ranges_swappable_v<_Elem1> // + && contiguous_iterator<_It1> && _Sized_or_unreachable_sentinel_for<_Se1, _It1> // + && contiguous_iterator<_It2> && _Sized_or_unreachable_sentinel_for<_Se2, _It2>) { + if (!_STD is_constant_evaluated()) { + constexpr bool _Is_sized1 = sized_sentinel_for<_Se1, _It1>; + constexpr bool _Is_sized2 = sized_sentinel_for<_Se2, _It2>; + const auto _First1_addr = _STD to_address(_First1); + const auto _First2_addr = _STD to_address(_First2); + if constexpr (_Is_sized1 && _Is_sized2) { + const size_t _Count = + (_STD min)(static_cast(_Last1 - _First1), static_cast(_Last2 - _First2)); + const auto _Last1_addr = _First1_addr + _Count; + ::__std_swap_ranges_trivially_swappable_noalias(_First1_addr, _Last1_addr, _First2_addr); + return {_First1 + _Count, _First2 + _Count}; + } else if constexpr (_Is_sized1) { + const auto _Final1 = _RANGES next(_First1, _Last1); + const auto _Last1_addr = _STD to_address(_Final1); + ::__std_swap_ranges_trivially_swappable_noalias(_First1_addr, _Last1_addr, _First2_addr); + return {_Final1, _First2 + (_Last1 - _First1)}; + } else if constexpr (_Is_sized2) { + const auto _Final2 = _RANGES next(_First2, _Last2); + const auto _Last2_addr = _STD to_address(_Final2); + ::__std_swap_ranges_trivially_swappable_noalias(_First2_addr, _Last2_addr, _First1_addr); + return {_First1 + (_Last2 - _First2), _Final2}; + } else { + _STL_ASSERT(false, "Tried to swap_ranges with two unreachable sentinels"); + } + } + } +#endif // _USE_STD_VECTOR_ALGORITHMS + + for (; _First1 != _Last1 && _First2 != _Last2; ++_First1, (void) ++_First2) { + _RANGES iter_swap(_First1, _First2); + } + + return {_STD move(_First1), _STD move(_First2)}; + } + class _Swap_ranges_fn { public: template _Se1, input_iterator _It2, sentinel_for<_It2> _Se2> @@ -3910,7 +3960,7 @@ namespace ranges { _STD _Adl_verify_range(_First1, _Last1); _STD _Adl_verify_range(_First2, _Last2); - auto _UResult = _Swap_ranges_unchecked(_RANGES _Unwrap_iter<_Se1>(_STD move(_First1)), + auto _UResult = _RANGES _Swap_ranges_common(_RANGES _Unwrap_iter<_Se1>(_STD move(_First1)), _RANGES _Unwrap_sent<_It1>(_STD move(_Last1)), _RANGES _Unwrap_iter<_Se2>(_STD move(_First2)), _RANGES _Unwrap_sent<_It2>(_STD move(_Last2))); @@ -3926,64 +3976,13 @@ namespace ranges { auto _First1 = _RANGES begin(_Range1); auto _First2 = _RANGES begin(_Range2); - auto _UResult = _Swap_ranges_unchecked(_RANGES _Unwrap_range_iter<_Rng1>(_STD move(_First1)), + auto _UResult = _RANGES _Swap_ranges_common(_RANGES _Unwrap_range_iter<_Rng1>(_STD move(_First1)), _Uend(_Range1), _RANGES _Unwrap_range_iter<_Rng2>(_STD move(_First2)), _Uend(_Range2)); _STD _Seek_wrapped(_First1, _STD move(_UResult.in1)); _STD _Seek_wrapped(_First2, _STD move(_UResult.in2)); return {_STD move(_First1), _STD move(_First2)}; } - - private: - template - _NODISCARD static constexpr swap_ranges_result<_It1, _It2> _Swap_ranges_unchecked( - _It1 _First1, const _Se1 _Last1, _It2 _First2, const _Se2 _Last2) { - _STL_INTERNAL_STATIC_ASSERT(input_iterator<_It1>); - _STL_INTERNAL_STATIC_ASSERT(sentinel_for<_Se1, _It1>); - _STL_INTERNAL_STATIC_ASSERT(input_iterator<_It2>); - _STL_INTERNAL_STATIC_ASSERT(sentinel_for<_Se2, _It2>); - _STL_INTERNAL_STATIC_ASSERT(indirectly_swappable<_It1, _It2>); - -#if _USE_STD_VECTOR_ALGORITHMS - using _Elem1 = remove_reference_t>; - using _Elem2 = remove_reference_t>; - if constexpr (same_as<_Elem1, _Elem2> && _Is_trivially_ranges_swappable_v<_Elem1> // - && contiguous_iterator<_It1> && _Sized_or_unreachable_sentinel_for<_Se1, _It1> // - && contiguous_iterator<_It2> && _Sized_or_unreachable_sentinel_for<_Se2, _It2>) { - if (!_STD is_constant_evaluated()) { - constexpr bool _Is_sized1 = sized_sentinel_for<_Se1, _It1>; - constexpr bool _Is_sized2 = sized_sentinel_for<_Se2, _It2>; - const auto _First1_addr = _STD to_address(_First1); - const auto _First2_addr = _STD to_address(_First2); - if constexpr (_Is_sized1 && _Is_sized2) { - const size_t _Count = - (_STD min)(static_cast(_Last1 - _First1), static_cast(_Last2 - _First2)); - const auto _Last1_addr = _First1_addr + _Count; - ::__std_swap_ranges_trivially_swappable_noalias(_First1_addr, _Last1_addr, _First2_addr); - return {_First1 + _Count, _First2 + _Count}; - } else if constexpr (_Is_sized1) { - const auto _Final1 = _RANGES next(_First1, _Last1); - const auto _Last1_addr = _STD to_address(_Final1); - ::__std_swap_ranges_trivially_swappable_noalias(_First1_addr, _Last1_addr, _First2_addr); - return {_Final1, _First2 + (_Last1 - _First1)}; - } else if constexpr (_Is_sized2) { - const auto _Final2 = _RANGES next(_First2, _Last2); - const auto _Last2_addr = _STD to_address(_Final2); - ::__std_swap_ranges_trivially_swappable_noalias(_First2_addr, _Last2_addr, _First1_addr); - return {_First1 + (_Last2 - _First2), _Final2}; - } else { - _STL_ASSERT(false, "Tried to swap_ranges with two unreachable sentinels"); - } - } - } -#endif // _USE_STD_VECTOR_ALGORITHMS - - for (; _First1 != _Last1 && _First2 != _Last2; ++_First1, (void) ++_First2) { - _RANGES iter_swap(_First1, _First2); - } - - return {_STD move(_First1), _STD move(_First2)}; - } }; _EXPORT_STD inline constexpr _Swap_ranges_fn swap_ranges; @@ -8654,12 +8653,10 @@ _CONSTEXPR20 void _Guess_median_unchecked(_RanIt _First, _RanIt _Mid, _RanIt _La } template -_CONSTEXPR20 pair<_RanIt, _RanIt> _Partition_by_median_guess_unchecked(_RanIt _First, _RanIt _Last, _Pr _Pred) { - // partition [_First, _Last) - _RanIt _Mid = _First + ((_Last - _First) >> 1); // shift for codegen - _STD _Guess_median_unchecked(_First, _Mid, _STD _Prev_iter(_Last), _Pred); - _RanIt _Pfirst = _Mid; - _RanIt _Plast = _STD _Next_iter(_Pfirst); +_CONSTEXPR20 pair<_RanIt, _RanIt> _Partition_by_pivot_unchecked( + _RanIt _First, _RanIt _Pfirst, _RanIt _Last, _Pr _Pred) { + // partition [_First, _Last) by pivot _Pfirst + _RanIt _Plast = _STD _Next_iter(_Pfirst); while (_First < _Pfirst && !_DEBUG_LT_PRED(_Pred, *_STD _Prev_iter(_Pfirst), *_Pfirst) && !_Pred(*_Pfirst, *_STD _Prev_iter(_Pfirst))) { @@ -8724,6 +8721,15 @@ _CONSTEXPR20 pair<_RanIt, _RanIt> _Partition_by_median_guess_unchecked(_RanIt _F } } +template +_CONSTEXPR20 pair<_RanIt, _RanIt> _Partition_by_median_guess_unchecked(_RanIt _First, _RanIt _Last, _Pr _Pred) { + // partition [_First, _Last) + _RanIt _Mid = _First + ((_Last - _First) >> 1); // shift for codegen + _STD _Guess_median_unchecked(_First, _Mid, _STD _Prev_iter(_Last), _Pred); + + return _STD _Partition_by_pivot_unchecked(_First, _Mid, _Last, _Pred); +} + template _CONSTEXPR20 void _Sort_unchecked(_RanIt _First, _RanIt _Last, _Iter_diff_t<_RanIt> _Ideal, _Pr _Pred) { // order [_First, _Last) @@ -8849,14 +8855,12 @@ namespace ranges { template requires sortable<_It, _Pr, _Pj> - _NODISCARD constexpr subrange<_It> _Partition_by_median_guess_common(_It _First, _It _Last, _Pr _Pred, _Pj _Proj) { - // Choose a pivot, partition [_First, _Last) into elements less than pivot, elements equal to pivot, and + _NODISCARD constexpr subrange<_It> _Partition_by_pivot_common( + _It _First, _It _Pfirst, _It _Last, _Pr _Pred, _Pj _Proj) { + // Partition [_First, _Last) by pivot _Pfirst into elements less than pivot, elements equal to pivot, and // elements greater than pivot; return the equal partition as a subrange. - _It _Mid = _First + ((_Last - _First) >> 1); // shift for codegen - _RANGES _Guess_median_common(_First, _Mid, _RANGES prev(_Last), _Pred, _Proj); - _It _Pfirst = _Mid; - _It _Plast = _RANGES next(_Pfirst); + _It _Plast = _RANGES next(_Pfirst); while (_First < _Pfirst && !_STD invoke(_Pred, _STD invoke(_Proj, *_RANGES prev(_Pfirst)), _STD invoke(_Proj, *_Pfirst)) @@ -8923,6 +8927,17 @@ namespace ranges { } } + template + requires sortable<_It, _Pr, _Pj> + _NODISCARD constexpr subrange<_It> _Partition_by_median_guess_common(_It _First, _It _Last, _Pr _Pred, _Pj _Proj) { + // Choose a pivot, partition [_First, _Last) into elements less than pivot, elements equal to pivot, and + // elements greater than pivot; return the equal partition as a subrange. + + _It _Mid = _First + ((_Last - _First) >> 1); // shift for codegen + _RANGES _Guess_median_common(_First, _Mid, _RANGES prev(_Last), _Pred, _Proj); + return _RANGES _Partition_by_pivot_common(_First, _Mid, _Last, _Pred, _Proj); + } + class _Sort_fn { public: template _Se, class _Pr = ranges::less, class _Pj = identity> @@ -9709,6 +9724,121 @@ namespace ranges { #endif // _HAS_CXX20 #endif // _HAS_CXX17 +template +_CONSTEXPR20 void _Swap_med5_unchecked(_RanIt _First, _RanIt _Dest, _Pr _Pred) { + _RanIt _Guess = _First; + ++_Guess; + _RanIt _Third = _Guess; + ++_Third; + _STD _Med3_unchecked(_First, _Guess, _Third, _Pred); + + _RanIt _Fourth = _Third; + ++_Fourth; + _RanIt _Fifth = _Fourth; + ++_Fifth; + if (_DEBUG_LT_PRED(_Pred, *_Fifth, *_Fourth)) { + swap(*_Fourth, *_Fifth); // intentional ADL + } + + // we have _First <= _Guess (second) <= _Third and _Fourth <= _Fifth + if (_DEBUG_LT_PRED(_Pred, *_Fifth, *_Guess)) { + if (_DEBUG_LT_PRED(_Pred, *_First, *_Fifth)) { + _Guess = _Fifth; + } else { + _Guess = _First; + } + } else if (_DEBUG_LT_PRED(_Pred, *_Guess, *_Fourth)) { + if (_DEBUG_LT_PRED(_Pred, *_Third, *_Fourth)) { + _Guess = _Third; + } else { + _Guess = _Fourth; + } + } + + swap(*_Dest, *_Guess); // intentional ADL +} + +template +_CONSTEXPR20 void _Nth_element_median_of_medians(_RanIt _First, _RanIt _Nth, _RanIt _Last, _Pr _Pred) { + using _Diff = _Iter_diff_t<_RanIt>; + for (;;) { + const auto _Length = _Last - _First; + if (_Length <= _ISORT_MAX) { + _STD _Insertion_sort_unchecked(_First, _Last, _Pred); // sort any remainder + return; + } + + // collect the medians of five at the beginning of the range + const _Diff _Median_count = _Length / static_cast<_Diff>(5); + const auto _Median_last = _First + _Median_count; + auto _Current = _First; + for (auto _Median_dest = _First; _Median_dest != _Median_last; ++_Median_dest) { + _STD _Swap_med5_unchecked(_Current, _Median_dest, _Pred); + _Current += static_cast<_Diff>(5); + } + + // find the median of medians + const auto _Median_mid = _First + (_Median_count >> 1); + _STD _Nth_element_median_of_medians(_First, _Median_mid, _Median_last, _Pred); + + // The medians before and after _Median_mid are + // less-equal and greater-equal _Median_mid, respectively. + // Move the ones after _Median_mid near _Last and then partition + // the elements between the medians at front and back by the pivot _Median_mid + // so that they do not have to be considered or moved in the partitioning pass. + const _Diff _Median_greater_count = (_Median_last - _Median_mid) - 1; + const auto _Median_greater_first = _Last - _Median_greater_count; + _STD _Swap_ranges_unchecked(_Median_mid + static_cast<_Diff>(1), _Median_last, _Median_greater_first); + + // Swap the median of medians to the middle + // to reduce the number of times it will be moved by the partition function. + const auto _Middle = _First + (_Length >> 1); + swap(*_Median_mid, *_Middle); // intentional ADL + + const auto _Mid = _STD _Partition_by_pivot_unchecked(_Median_mid, _Middle, _Median_greater_first, _Pred); + + if (_Mid.second <= _Nth) { + _First = _Mid.second; + } else if (_Mid.first <= _Nth) { + return; // _Nth is in the subrange of elements equal to the pivot; done + } else { + _Last = _Mid.first; + } + } +} + +template +_CONSTEXPR20 void _Nth_element_unchecked(_RanIt _First, _RanIt _Nth, _RanIt _Last, _Pr _Pred) { + auto _Ideal = _Last - _First; + + for (;;) { + const auto _Length = _Last - _First; + if (_Length <= _ISORT_MAX) { + _STD _Insertion_sort_unchecked(_First, _Last, _Pred); // sort any remainder + return; + } + + if (_Ideal < (_Length >> 1)) { + // fall back to medians-of-medians-of-five algorithm to guarantee O(n) runtime performance + _STD _Nth_element_median_of_medians(_First, _Nth, _Last, _Pred); + return; + } + + const auto _Mid = _STD _Partition_by_median_guess_unchecked(_First, _Last, _Pred); + + if (_Mid.second <= _Nth) { + _First = _Mid.second; + } else if (_Mid.first <= _Nth) { + return; // _Nth is in the subrange of elements equal to the pivot; done + } else { + _Last = _Mid.first; + } + + // processed range should be reduced by 25% per iteration on average + _Ideal = (_Ideal >> 1) + (_Ideal >> 2); + } +} + _EXPORT_STD template _CONSTEXPR20 void nth_element(_RanIt _First, _RanIt _Nth, _RanIt _Last, _Pr _Pred) { // order Nth element @@ -9721,19 +9851,7 @@ _CONSTEXPR20 void nth_element(_RanIt _First, _RanIt _Nth, _RanIt _Last, _Pr _Pre return; // nothing to do } - while (_ISORT_MAX < _ULast - _UFirst) { // divide and conquer, ordering partition containing Nth - auto _UMid = _STD _Partition_by_median_guess_unchecked(_UFirst, _ULast, _STD _Pass_fn(_Pred)); - - if (_UMid.second <= _UNth) { - _UFirst = _UMid.second; - } else if (_UMid.first <= _UNth) { - return; // _Nth is in the subrange of elements equal to the pivot; done - } else { - _ULast = _UMid.first; - } - } - - _STD _Insertion_sort_unchecked(_UFirst, _ULast, _STD _Pass_fn(_Pred)); // sort any remainder + _STD _Nth_element_unchecked(_UFirst, _UNth, _ULast, _STD _Pass_fn(_Pred)); } _EXPORT_STD template @@ -9801,6 +9919,94 @@ namespace ranges { } private: + template + static constexpr void _Swap_med5(_It _First, _It _Dest, _Pr _Pred, _Pj _Proj) { + _It _Guess = _First; + ++_Guess; + _It _Third = _Guess; + ++_Third; + _RANGES _Med3_common(_First, _Guess, _Third, _Pred, _Proj); + + _It _Fourth = _Third; + ++_Fourth; + _It _Fifth = _Fourth; + ++_Fifth; + if (_STD invoke(_Pred, _STD invoke(_Proj, *_Fifth), _STD invoke(_Proj, *_Fourth))) { + _RANGES iter_swap(_Fourth, _Fifth); + } + + // we have _First <= _Guess (second) <= _Third and _Fourth <= _Fifth + if (_STD invoke(_Pred, _STD invoke(_Proj, *_Fifth), _STD invoke(_Proj, *_Guess))) { + if (_STD invoke(_Pred, _STD invoke(_Proj, *_First), _STD invoke(_Proj, *_Fifth))) { + _Guess = _STD move(_Fifth); + } else { + _Guess = _STD move(_First); + } + } else if (_STD invoke(_Pred, _STD invoke(_Proj, *_Guess), _STD invoke(_Proj, *_Fourth))) { + if (_STD invoke(_Pred, _STD invoke(_Proj, *_Third), _STD invoke(_Proj, *_Fourth))) { + _Guess = _STD move(_Third); + } else { + _Guess = _STD move(_Fourth); + } + } + + _RANGES iter_swap(_Dest, _Guess); + } + + template + static constexpr void _Nth_element_median_of_medians_unchecked( + _It _First, _It _Nth, _It _Last, _Pr _Pred, _Pj _Proj) { + + using _Diff = iter_difference_t<_It>; + for (;;) { + const auto _Length = _Last - _First; + if (_Length <= _ISORT_MAX) { + // sort any remainder + _RANGES _Insertion_sort_common(_STD move(_First), _STD move(_Last), _Pred, _Proj); + return; + } + + // collect the medians of five at the beginning of the range + const _Diff _Median_count = _Length / static_cast<_Diff>(5); + auto _Median_last = _First + _Median_count; + auto _Current = _First; + for (auto _Median_dest = _First; _Median_dest != _Median_last; ++_Median_dest) { + _Swap_med5(_Current, _Median_dest, _Pred, _Proj); + _Current += static_cast<_Diff>(5); + } + + // find the median of medians + auto _Median_mid = _First + (_Median_count >> 1); + _Nth_element_median_of_medians_unchecked(_First, _Median_mid, _Median_last, _Pred, _Proj); + + // The medians before and after _Median_mid are + // less-equal and greater-equal _Median_mid, respectively. + // Move the ones after _Median_mid near _Last and then partition + // the elements between the medians at front and back by the pivot _Median_mid + // so that they do not have to be considered or moved in the partitioning pass. + const _Diff _Median_greater_count = (_Median_last - _Median_mid) - 1; + auto _Median_greater_first = _Last - _Median_greater_count; + (void) _RANGES _Swap_ranges_common( + _Median_mid + static_cast<_Diff>(1), _STD move(_Median_last), _Median_greater_first, _Last); + + // Swap the median of medians to the middle + // to reduce the number of times it will be moved by the partition function. + auto _Middle = _First + (_Length >> 1); + _RANGES iter_swap(_Median_mid, _Middle); + + const subrange<_It> _Mid = _RANGES _Partition_by_pivot_common( + _STD move(_Median_mid), _STD move(_Middle), _STD move(_Median_greater_first), _Pred, _Proj); + + if (_Mid.end() <= _Nth) { + _First = _Mid.end(); + } else if (_Mid.begin() <= _Nth) { + return; // _Nth is in the subrange of elements equal to the pivot; done + } else { + _Last = _Mid.begin(); + } + } + } + template static constexpr void _Nth_element_common(_It _First, _It _Nth, _It _Last, _Pr _Pred, _Pj _Proj) { _STL_INTERNAL_STATIC_ASSERT(random_access_iterator<_It>); @@ -9810,8 +10016,24 @@ namespace ranges { return; // nothing to do } - while (_ISORT_MAX < _Last - _First) { // divide and conquer, ordering partition containing Nth - subrange<_It> _Mid = _RANGES _Partition_by_median_guess_common(_First, _Last, _Pred, _Proj); + auto _Ideal = _Last - _First; + + for (;;) { + const auto _Length = _Last - _First; + if (_Length <= _ISORT_MAX) { + // sort any remainder + _RANGES _Insertion_sort_common(_STD move(_First), _STD move(_Last), _Pred, _Proj); + return; + } + + if (_Ideal < (_Length >> 1)) { + // fall back to medians-of-medians-of-five algorithm to guarantee O(n) runtime performance + _Nth_element_median_of_medians_unchecked( + _STD move(_First), _STD move(_Nth), _STD move(_Last), _Pred, _Proj); + return; + } + + const subrange<_It> _Mid = _RANGES _Partition_by_median_guess_common(_First, _Last, _Pred, _Proj); if (_Mid.end() <= _Nth) { _First = _Mid.end(); @@ -9820,10 +10042,10 @@ namespace ranges { } else { _Last = _Mid.begin(); } - } - // sort any remainder - _RANGES _Insertion_sort_common(_STD move(_First), _STD move(_Last), _Pred, _Proj); + // processed range should be reduced by 25% per iteration on average + _Ideal = (_Ideal >> 1) + (_Ideal >> 2); + } } }; diff --git a/tests/std/test.lst b/tests/std/test.lst index ebe3c13fc79..ba093a6b814 100644 --- a/tests/std/test.lst +++ b/tests/std/test.lst @@ -175,6 +175,7 @@ tests\GH_000625_vector_bool_optimization tests\GH_000685_condition_variable_any tests\GH_000690_overaligned_function tests\GH_000732_hash_reserve +tests\GH_000856_nth_element_linear tests\GH_000890_pow_template tests\GH_000935_complex_numerical_accuracy tests\GH_000940_missing_valarray_copy diff --git a/tests/std/tests/GH_000856_nth_element_linear/env.lst b/tests/std/tests/GH_000856_nth_element_linear/env.lst new file mode 100644 index 00000000000..19f025bd0e6 --- /dev/null +++ b/tests/std/tests/GH_000856_nth_element_linear/env.lst @@ -0,0 +1,4 @@ +# Copyright (c) Microsoft Corporation. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +RUNALL_INCLUDE ..\usual_matrix.lst diff --git a/tests/std/tests/GH_000856_nth_element_linear/test.cpp b/tests/std/tests/GH_000856_nth_element_linear/test.cpp new file mode 100644 index 00000000000..f8b0e5d28fc --- /dev/null +++ b/tests/std/tests/GH_000856_nth_element_linear/test.cpp @@ -0,0 +1,149 @@ +// Copyright (c) Microsoft Corporation. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#include +#include +#include + +using namespace std; + +constexpr int tukey_ninther_adversary1[] = {0, 6, 12, 18, 22, 28, 34, 38, 44, 50, 54, 60, 66, 70, 76, 82, 86, 92, 98, + 102, 108, 114, 118, 124, 130, 134, 140, 146, 150, 156, 162, 166, 172, 178, 182, 188, 194, 198, 204, 210, 214, 220, + 226, 230, 236, 242, 246, 252, 258, 262, 268, 274, 278, 284, 290, 294, 300, 306, 310, 316, 322, 326, 332, 338, 342, + 348, 354, 358, 364, 370, 374, 380, 386, 390, 396, 402, 406, 412, 418, 422, 428, 434, 438, 444, 450, 454, 460, 466, + 470, 476, 482, 486, 492, 498, 502, 508, 514, 518, 524, 530, 534, 540, 546, 550, 556, 562, 566, 572, 578, 582, 588, + 594, 598, 604, 610, 614, 620, 626, 630, 636, 642, 646, 652, 658, 662, 668, 674, 678, 1, 7, 13, 684, 19, 23, 29, 690, + 35, 39, 45, 694, 51, 55, 61, 700, 67, 71, 77, 706, 83, 87, 93, 710, 99, 103, 109, 716, 115, 119, 125, 722, 131, 135, + 141, 726, 147, 151, 157, 732, 163, 167, 173, 738, 179, 183, 189, 742, 195, 199, 205, 748, 211, 215, 221, 754, 227, + 231, 237, 758, 243, 247, 253, 764, 259, 263, 269, 770, 275, 279, 285, 774, 291, 295, 301, 780, 307, 311, 317, 786, + 323, 327, 333, 790, 339, 343, 349, 796, 355, 359, 365, 802, 371, 375, 381, 806, 387, 391, 397, 812, 403, 407, 413, + 818, 419, 423, 429, 822, 435, 439, 445, 828, 451, 455, 461, 834, 467, 471, 477, 838, 483, 487, 493, 844, 499, 503, + 509, 850, 515, 519, 525, 854, 531, 535, 541, 860, 547, 551, 557, 866, 563, 567, 573, 870, 579, 583, 589, 876, 595, + 599, 605, 882, 611, 615, 621, 886, 627, 631, 637, 892, 643, 647, 653, 898, 659, 663, 669, 902, 675, 679, 685, 908, + 691, 695, 701, 914, 707, 711, 717, 918, 723, 727, 733, 924, 739, 743, 749, 930, 755, 759, 765, 934, 771, 775, 781, + 940, 787, 791, 797, 946, 803, 807, 813, 950, 819, 823, 829, 956, 835, 839, 845, 962, 851, 855, 861, 966, 867, 871, + 877, 972, 883, 887, 893, 978, 899, 903, 909, 982, 915, 919, 925, 988, 931, 935, 941, 990, 947, 951, 957, 992, 963, + 967, 973, 1024, 979, 983, 993, 994, 995, 996, 997, 998, 999, 1000, 1001, 1002, 2, 20, 8, 14, 24, 36, 30, 40, 52, 46, + 56, 68, 62, 72, 84, 78, 88, 100, 94, 104, 116, 110, 120, 132, 126, 136, 148, 142, 152, 164, 158, 168, 180, 174, 184, + 196, 190, 200, 212, 206, 216, 228, 222, 232, 244, 238, 248, 260, 254, 264, 276, 270, 280, 292, 286, 296, 308, 302, + 312, 324, 318, 328, 340, 334, 344, 356, 350, 360, 372, 366, 376, 388, 382, 392, 404, 398, 408, 420, 414, 424, 436, + 430, 440, 452, 446, 456, 468, 462, 472, 484, 478, 488, 500, 494, 504, 516, 510, 520, 532, 526, 536, 548, 542, 552, + 564, 558, 568, 580, 574, 584, 596, 590, 600, 612, 606, 616, 628, 622, 632, 644, 638, 648, 660, 654, 664, 676, 670, + 512, 3, 991, 9, 773, 768, 15, 680, 901, 21, 522, 25, 777, 527, 31, 692, 965, 37, 528, 41, 960, 778, 47, 686, 905, + 53, 538, 57, 936, 543, 63, 696, 1007, 69, 544, 73, 789, 784, 79, 708, 1014, 85, 554, 89, 793, 559, 95, 702, 969, + 101, 560, 105, 911, 794, 111, 712, 974, 117, 570, 121, 948, 575, 127, 724, 1015, 133, 576, 137, 805, 800, 143, 718, + 917, 149, 586, 153, 809, 591, 159, 728, 1008, 165, 592, 169, 970, 810, 175, 740, 921, 181, 602, 185, 942, 607, 191, + 734, 1009, 197, 608, 201, 821, 816, 207, 744, 975, 213, 618, 217, 825, 623, 223, 756, 1005, 229, 624, 233, 927, 826, + 239, 750, 984, 245, 634, 249, 952, 639, 255, 760, 1010, 261, 640, 265, 837, 832, 271, 772, 933, 277, 650, 281, 841, + 655, 287, 766, 981, 293, 656, 297, 976, 842, 303, 776, 937, 309, 666, 313, 964, 671, 319, 788, 1011, 325, 672, 329, + 853, 848, 335, 782, 1016, 341, 682, 345, 857, 687, 351, 792, 985, 357, 688, 361, 943, 858, 367, 804, 1003, 373, 698, + 377, 958, 703, 383, 798, 1017, 389, 704, 393, 869, 864, 399, 808, 949, 405, 714, 409, 873, 719, 415, 820, 1012, 421, + 720, 425, 986, 874, 431, 814, 953, 437, 730, 441, 968, 735, 447, 824, 1013, 453, 736, 457, 885, 880, 463, 836, 989, + 469, 746, 473, 889, 751, 479, 830, 1006, 485, 752, 489, 959, 890, 495, 840, 1004, 501, 762, 505, 980, 767, 511, 852, + 4, 517, 10, 521, 16, 896, 26, 846, 32, 533, 42, 537, 48, 783, 58, 856, 64, 549, 74, 553, 80, 906, 90, 868, 96, 565, + 106, 569, 112, 799, 122, 862, 128, 581, 138, 585, 144, 912, 154, 72, 160, 597, 170, 601, 176, 815, 186, 884, 192, + 613, 202, 617, 208, 922, 218, 878, 224, 629, 234, 633, 240, 831, 250, 888, 256, 645, 266, 649, 272, 928, 282, 900, + 288, 661, 298, 665, 304, 847, 314, 894, 320, 677, 330, 681, 336, 938, 346, 904, 352, 693, 362, 697, 368, 863, 378, + 916, 384, 709, 394, 713, 400, 944, 410, 910, 416, 725, 426, 729, 432, 879, 442, 920, 448, 741, 458, 745, 464, 954, + 474, 932, 480, 757, 490, 761, 496, 895, 506, 926, 5, 11, 17, 27, 33, 43, 49, 59, 65, 75, 81, 91, 97, 107, 113, 123, + 129, 139, 145, 155, 161, 71, 177, 187, 193, 203, 209, 219, 225, 235, 241, 251, 257, 267, 273, 283, 289, 299, 305, + 315, 321, 331, 337, 347, 353, 363, 369, 379, 385, 395, 401, 411, 417, 427, 433, 443, 449, 459, 465, 475, 481, 491, + 497, 507, 513, 523, 529, 539, 545, 555, 561, 571, 577, 587, 593, 603, 609, 619, 625, 635, 641, 651, 657, 667, 673, + 683, 689, 699, 705, 715, 721, 731, 737, 747, 753, 763, 769, 779, 785, 795, 801, 811, 817, 827, 833, 843, 849, 859, + 865, 875, 881, 891, 897, 907, 913, 923, 929, 939, 945, 955, 961, 971, 977, 987, 1018, 1019, 1020, 1021, 1022}; + +constexpr int tukey_ninther_adversary2[] = {1024, 31, 30, 29, 28, 36, 46, 51, 61, 67, 77, 82, 92, 98, 108, 118, 123, + 133, 139, 149, 154, 164, 170, 180, 190, 195, 205, 211, 221, 226, 236, 242, 252, 262, 267, 277, 283, 293, 298, 308, + 314, 324, 334, 339, 349, 355, 365, 370, 380, 386, 396, 406, 411, 421, 427, 437, 442, 452, 458, 468, 478, 483, 493, + 499, 509, 514, 524, 530, 540, 550, 555, 565, 571, 581, 586, 596, 602, 612, 622, 627, 637, 643, 653, 658, 668, 674, + 684, 694, 699, 709, 715, 725, 730, 740, 746, 756, 766, 771, 781, 787, 797, 802, 812, 818, 828, 838, 843, 853, 859, + 869, 874, 884, 890, 900, 910, 915, 925, 931, 941, 946, 956, 962, 972, 982, 987, 997, 1003, 1013, 1018, 343, 523, + 127, 529, 86, 539, 358, 549, 178, 554, 107, 564, 374, 570, 183, 580, 384, 585, 389, 595, 394, 601, 399, 611, 404, + 621, 409, 626, 18, 636, 419, 642, 425, 652, 318, 657, 435, 667, 440, 673, 169, 683, 450, 693, 224, 698, 256, 708, + 179, 714, 66, 724, 234, 729, 487, 739, 189, 745, 34, 755, 502, 765, 71, 770, 188, 780, 518, 786, 76, 796, 528, 801, + 533, 811, 538, 817, 543, 827, 548, 837, 553, 842, 163, 852, 563, 858, 569, 868, 286, 873, 579, 883, 584, 889, 70, + 899, 594, 909, 219, 914, 97, 924, 235, 930, 91, 940, 462, 945, 631, 955, 173, 961, 241, 971, 646, 981, 322, 986, + 272, 996, 662, 1002, 327, 1012, 672, 1017, 677, 1023, 682, 184, 687, 44, 692, 80, 697, 703, 390, 347, 707, 193, 713, + 718, 534, 276, 723, 26, 728, 734, 32, 200, 738, 744, 368, 749, 137, 754, 15, 759, 25, 764, 378, 769, 775, 575, 90, + 779, 148, 785, 790, 297, 215, 795, 225, 800, 806, 447, 56, 810, 816, 27, 821, 153, 826, 616, 831, 152, 836, 59, 841, + 847, 472, 632, 851, 116, 857, 862, 430, 214, 867, 647, 872, 878, 106, 251, 882, 888, 21, 893, 332, 898, 87, 903, + 132, 908, 678, 913, 919, 456, 168, 923, 230, 929, 934, 519, 466, 939, 19, 944, 950, 353, 471, 954, 960, 364, 965, + 719, 970, 271, 975, 544, 980, 369, 985, 991, 210, 491, 995, 282, 1001, 1006, 750, 250, 1011, 121, 1016, 1022, 385, + 292, 507, 65, 512, 255, 102, 776, 395, 45, 159, 287, 522, 260, 591, 791, 405, 117, 204, 307, 49, 265, 302, 807, 606, + 400, 22, 111, 16, 344, 822, 420, 313, 303, 415, 832, 275, 203, 559, 23, 416, 426, 323, 848, 281, 231, 209, 317, 574, + 436, 17, 863, 142, 333, 220, 488, 431, 441, 81, 879, 75, 590, 451, 663, 328, 446, 894, 296, 375, 600, 261, 904, 457, + 348, 605, 337, 610, 60, 688, 920, 467, 96, 615, 461, 620, 306, 354, 935, 477, 704, 625, 24, 312, 128, 143, 951, 476, + 635, 174, 131, 641, 240, 966, 492, 481, 138, 359, 976, 40, 363, 651, 735, 656, 498, 379, 992, 20, 497, 39, 560, 666, + 508, 112, 1007, 246, 158, 162, 760, 503, 513, 482, 291, 410, 245, 338, 199, 266, 147, 194, 101, 122, 55, 50, 14, 13, + 43, 48, 58, 64, 74, 79, 95, 105, 115, 120, 130, 136, 146, 151, 167, 177, 187, 192, 202, 208, 218, 223, 239, 249, + 259, 264, 274, 280, 290, 295, 311, 321, 331, 336, 346, 352, 362, 367, 383, 393, 403, 408, 418, 424, 434, 439, 455, + 465, 475, 480, 490, 496, 506, 511, 527, 537, 547, 552, 562, 568, 578, 583, 599, 609, 619, 624, 634, 640, 650, 655, + 671, 681, 691, 696, 706, 712, 722, 727, 743, 753, 763, 768, 778, 784, 794, 799, 815, 825, 835, 840, 850, 856, 866, + 871, 887, 897, 907, 912, 922, 928, 938, 943, 959, 969, 979, 984, 994, 1000, 1010, 1015, 1021, 1005, 990, 953, 974, + 964, 949, 933, 918, 881, 902, 892, 877, 861, 846, 809, 830, 820, 805, 789, 774, 737, 758, 748, 733, 717, 702, 665, + 686, 676, 661, 645, 630, 593, 614, 604, 589, 573, 558, 521, 542, 532, 517, 501, 486, 449, 470, 460, 445, 429, 414, + 377, 398, 388, 373, 357, 342, 305, 326, 316, 301, 285, 270, 233, 254, 244, 229, 213, 198, 161, 182, 172, 157, 141, + 126, 89, 110, 100, 85, 69, 54, 12, 38, 11, 10, 9, 8, 53, 84, 99, 125, 156, 171, 197, 228, 243, 269, 300, 315, 341, + 372, 387, 413, 444, 459, 485, 516, 531, 557, 588, 603, 629, 660, 675, 701, 732, 747, 773, 804, 819, 845, 876, 891, + 917, 948, 963, 989, 1020, 1014, 63, 253, 258, 263, 279, 273, 284, 289, 294, 310, 304, 320, 73, 325, 330, 335, 351, + 345, 356, 361, 366, 382, 376, 392, 88, 397, 402, 407, 423, 417, 428, 433, 438, 454, 448, 464, 109, 469, 474, 479, + 495, 489, 500, 505, 510, 526, 520, 536, 135, 541, 546, 551, 567, 561, 572, 577, 582, 598, 592, 608, 145, 613, 618, + 623, 639, 633, 644, 649, 654, 670, 664, 680, 160, 685, 690, 695, 711, 705, 716, 721, 726, 742, 736, 752, 181, 757, + 762, 767, 783, 777, 788, 793, 798, 814, 808, 824, 207, 829, 834, 839, 855, 849, 860, 865, 870, 886, 880, 896, 217, + 901, 906, 911, 927, 921, 932, 937, 942, 958, 952, 968, 232, 973, 978, 983, 999, 993, 1004, 1009, 248, 238, 222, 212, + 201, 191, 186, 37, 176, 166, 150, 140, 129, 119, 114, 3, 104, 94, 78, 68, 57, 47, 42, 6, 7, 4, 5, 2, -1, 33, 35, 41, + 52, 62, 72, 83, 93, 103, 113, 124, 134, 144, 155, 165, 175, 185, 196, 206, 216, 227, 237, 247, 257, 268, 278, 288, + 299, 309, 319, 329, 340, 350, 360, 371, 381, 391, 401, 412, 422, 432, 443, 453, 463, 473, 484, 494, 504, 515, 525, + 535, 545, 556, 566, 576, 587, 597, 607, 617, 628, 638, 648, 659, 669, 679, 689, 700, 710, 720, 731, 741, 751, 761, + 772, 782, 792, 803, 813, 823, 833, 844, 854, 864, 875, 885, 895, 905, 916, 926, 936, 947, 957, 967, 977, 988, 998, + 1008, 1019}; + +template +void check_result(const vector& expected, const vector& computed, const Diff nth) { + assert(is_permutation(computed.begin(), computed.end(), expected.begin(), expected.end())); + const auto mid = computed.begin() + nth; + const int val_nth = *mid; + assert(*(expected.begin() + nth) == val_nth); + for (auto it = computed.begin(); it < mid; ++it) { + assert(*it <= val_nth); + } + for (auto it = mid; it < computed.end(); ++it) { + assert(*it >= val_nth); + } +} + +template +void test_nth_element(Algorithm alg, const Src& src) { + const auto src_ssize = end(src) - begin(src); + + vector sorted(begin(src), end(src)); + sort(sorted.begin(), sorted.end()); + vector v; + + for (vector::difference_type nth{}; nth < src_ssize; nth += 15) { + v.assign(begin(src), end(src)); + alg(v, nth); + check_result(sorted, v, nth); + } + v.assign(begin(src), end(src)); + alg(v, src_ssize - 1); + check_result(sorted, v, src_ssize - 1); +} + +template +void test_nth_element_tukey_adversary(Algorithm alg) { + test_nth_element(alg, tukey_ninther_adversary1); + test_nth_element(alg, tukey_ninther_adversary2); +} + +int main() { + test_nth_element_tukey_adversary([](vector& v, const vector::difference_type nth) { + nth_element(v.begin(), v.begin() + nth, v.end()); + }); + +#if _HAS_CXX20 + test_nth_element_tukey_adversary([](vector& v, const vector::difference_type nth) { + ranges::nth_element(v.begin(), v.begin() + nth, v.end()); + }); +#endif +}