Showing the changes to gcc/libphobos/src/std/algorithm (8 of 31387 files). Show all
Unified · Split
| @@ -0,0 +1,2159 @@ | |||
| 1 | // Written in the D programming language. | ||
| 2 | /** | ||
| 3 | This is a submodule of $(MREF std, algorithm). | ||
| 4 | It contains generic _comparison algorithms. | ||
| 5 | |||
| 6 | $(SCRIPT inhibitQuickIndex = 1;) | ||
| 7 | $(BOOKTABLE Cheat Sheet, | ||
| 8 | $(TR $(TH Function Name) $(TH Description)) | ||
| 9 | $(T2 among, | ||
| 10 | Checks if a value is among a set of values, e.g. | ||
| 11 | $(D if (v.among(1, 2, 3)) // `v` is 1, 2 or 3)) | ||
| 12 | $(T2 castSwitch, | ||
| 13 | $(D (new A()).castSwitch((A a)=>1,(B b)=>2)) returns $(D 1).) | ||
| 14 | $(T2 clamp, | ||
| 15 | $(D clamp(1, 3, 6)) returns $(D 3). $(D clamp(4, 3, 6)) returns $(D 4).) | ||
| 16 | $(T2 cmp, | ||
| 17 | $(D cmp("abc", "abcd")) is $(D -1), $(D cmp("abc", "aba")) is $(D 1), | ||
| 18 | and $(D cmp("abc", "abc")) is $(D 0).) | ||
| 19 | $(T2 either, | ||
| 20 | Return first parameter $(D p) that passes an $(D if (p)) test, e.g. | ||
| 21 | $(D either(0, 42, 43)) returns $(D 42).) | ||
| 22 | $(T2 equal, | ||
| 23 | Compares ranges for element-by-element equality, e.g. | ||
| 24 | $(D equal([1, 2, 3], [1.0, 2.0, 3.0])) returns $(D true).) | ||
| 25 | $(T2 isPermutation, | ||
| 26 | $(D isPermutation([1, 2], [2, 1])) returns $(D true).) | ||
| 27 | $(T2 isSameLength, | ||
| 28 | $(D isSameLength([1, 2, 3], [4, 5, 6])) returns $(D true).) | ||
| 29 | $(T2 levenshteinDistance, | ||
| 30 | $(D levenshteinDistance("kitten", "sitting")) returns $(D 3) by using | ||
| 31 | the $(LINK2 https://en.wikipedia.org/wiki/Levenshtein_distance, | ||
| 32 | Levenshtein distance _algorithm).) | ||
| 33 | $(T2 levenshteinDistanceAndPath, | ||
| 34 | $(D levenshteinDistanceAndPath("kitten", "sitting")) returns | ||
| 35 | $(D tuple(3, "snnnsni")) by using the | ||
| 36 | $(LINK2 https://en.wikipedia.org/wiki/Levenshtein_distance, | ||
| 37 | Levenshtein distance _algorithm).) | ||
| 38 | $(T2 max, | ||
| 39 | $(D max(3, 4, 2)) returns $(D 4).) | ||
| 40 | $(T2 min, | ||
| 41 | $(D min(3, 4, 2)) returns $(D 2).) | ||
| 42 | $(T2 mismatch, | ||
| 43 | $(D mismatch("oh hi", "ohayo")) returns $(D tuple(" hi", "ayo")).) | ||
| 44 | $(T2 predSwitch, | ||
| 45 | $(D 2.predSwitch(1, "one", 2, "two", 3, "three")) returns $(D "two").) | ||
| 46 | ) | ||
| 47 | |||
| 48 | Copyright: Andrei Alexandrescu 2008-. | ||
| 49 | |||
| 50 | License: $(HTTP boost.org/LICENSE_1_0.txt, Boost License 1.0). | ||
| 51 | |||
| 52 | Authors: $(HTTP erdani.com, Andrei Alexandrescu) | ||
| 53 | |||
| 54 | Source: $(PHOBOSSRC std/algorithm/_comparison.d) | ||
| 55 | |||
| 56 | Macros: | ||
| 57 | T2=$(TR $(TDNW $(LREF $1)) $(TD $+)) | ||
| 58 | */ | ||
| 59 | module std.algorithm.comparison; | ||
| 60 | |||
| 61 | // FIXME | ||
| 62 | import std.functional; // : unaryFun, binaryFun; | ||
| 63 | import std.range.primitives; | ||
| 64 | import std.traits; | ||
| 65 | // FIXME | ||
| 66 | import std.meta : allSatisfy; | ||
| 67 | import std.typecons; // : tuple, Tuple, Flag, Yes; | ||
| 68 | |||
| 69 | /** | ||
| 70 | Find $(D value) _among $(D values), returning the 1-based index | ||
| 71 | of the first matching value in $(D values), or $(D 0) if $(D value) | ||
| 72 | is not _among $(D values). The predicate $(D pred) is used to | ||
| 73 | compare values, and uses equality by default. | ||
| 74 | |||
| 75 | Params: | ||
| 76 | pred = The predicate used to compare the values. | ||
| 77 | value = The value to search for. | ||
| 78 | values = The values to compare the value to. | ||
| 79 | |||
| 80 | Returns: | ||
| 81 | 0 if value was not found among the values, otherwise the index of the | ||
| 82 | found value plus one is returned. | ||
| 83 | |||
| 84 | See_Also: | ||
| 85 | $(REF_ALTTEXT find, find, std,algorithm,searching) and $(REF_ALTTEXT canFind, canFind, std,algorithm,searching) for finding a value in a | ||
| 86 | range. | ||
| 87 | */ | ||
| 88 | uint among(alias pred = (a, b) => a == b, Value, Values...) | ||
| 89 | (Value value, Values values) | ||
| 90 | if (Values.length != 0) | ||
| 91 | { | ||
| 92 | foreach (uint i, ref v; values) | ||
| 93 | { | ||
| 94 | import std.functional : binaryFun; | ||
| 95 | if (binaryFun!pred(value, v)) return i + 1; | ||
| 96 | } | ||
| 97 | return 0; | ||
| 98 | } | ||
| 99 | |||
| 100 | /// Ditto | ||
| 101 | template among(values...) | ||
| 102 | if (isExpressionTuple!values) | ||
| 103 | { | ||
| 104 | uint among(Value)(Value value) | ||
| 105 | if (!is(CommonType!(Value, values) == void)) | ||
| 106 | { | ||
| 107 | switch (value) | ||
| 108 | { | ||
| 109 | foreach (uint i, v; values) | ||
| 110 | case v: | ||
| 111 | return i + 1; | ||
| 112 | default: | ||
| 113 | return 0; | ||
| 114 | } | ||
| 115 | } | ||
| 116 | } | ||
| 117 | |||
| 118 | /// | ||
| 119 | @safe unittest | ||
| 120 | { | ||
| 121 | assert(3.among(1, 42, 24, 3, 2)); | ||
| 122 | |||
| 123 | if (auto pos = "bar".among("foo", "bar", "baz")) | ||
| 124 | assert(pos == 2); | ||
| 125 | else | ||
| 126 | assert(false); | ||
| 127 | |||
| 128 | // 42 is larger than 24 | ||
| 129 | assert(42.among!((lhs, rhs) => lhs > rhs)(43, 24, 100) == 2); | ||
| 130 | } | ||
| 131 | |||
| 132 | /** | ||
| 133 | Alternatively, $(D values) can be passed at compile-time, allowing for a more | ||
| 134 | efficient search, but one that only supports matching on equality: | ||
| 135 | */ | ||
| 136 | @safe unittest | ||
| 137 | { | ||
| 138 | assert(3.among!(2, 3, 4)); | ||
| 139 | assert("bar".among!("foo", "bar", "baz") == 2); | ||
| 140 | } | ||
| 141 | |||
| 142 | @safe unittest | ||
| 143 | { | ||
| 144 | import std.meta : AliasSeq; | ||
| 145 | |||
| 146 | if (auto pos = 3.among(1, 2, 3)) | ||
| 147 | assert(pos == 3); | ||
| 148 | else | ||
| 149 | assert(false); | ||
| 150 | assert(!4.among(1, 2, 3)); | ||
| 151 | |||
| 152 | auto position = "hello".among("hello", "world"); | ||
| 153 | assert(position); | ||
| 154 | assert(position == 1); | ||
| 155 | |||
| 156 | alias values = AliasSeq!("foo", "bar", "baz"); | ||
| 157 | auto arr = [values]; | ||
| 158 | assert(arr[0 .. "foo".among(values)] == ["foo"]); | ||
| 159 | assert(arr[0 .. "bar".among(values)] == ["foo", "bar"]); | ||
| 160 | assert(arr[0 .. "baz".among(values)] == arr); | ||
| 161 | assert("foobar".among(values) == 0); | ||
| 162 | |||
| 163 | if (auto pos = 3.among!(1, 2, 3)) | ||
| 164 | assert(pos == 3); | ||
| 165 | else | ||
| 166 | assert(false); | ||
| 167 | assert(!4.among!(1, 2, 3)); | ||
| 168 | |||
| 169 | position = "hello".among!("hello", "world"); | ||
| 170 | assert(position); | ||
| 171 | assert(position == 1); | ||
| 172 | |||
| 173 | static assert(!__traits(compiles, "a".among!("a", 42))); | ||
| 174 | static assert(!__traits(compiles, (Object.init).among!(42, "a"))); | ||
| 175 | } | ||
| 176 | |||
| 177 | // Used in castSwitch to find the first choice that overshadows the last choice | ||
| 178 | // in a tuple. | ||
| 179 | private template indexOfFirstOvershadowingChoiceOnLast(choices...) | ||
| 180 | { | ||
| 181 | alias firstParameterTypes = Parameters!(choices[0]); | ||
| 182 | alias lastParameterTypes = Parameters!(choices[$ - 1]); | ||
| 183 | |||
| 184 | static if (lastParameterTypes.length == 0) | ||
| 185 | { | ||
| 186 | // If the last is null-typed choice, check if the first is null-typed. | ||
| 187 | enum isOvershadowing = firstParameterTypes.length == 0; | ||
| 188 | } | ||
| 189 | else static if (firstParameterTypes.length == 1) | ||
| 190 | { | ||
| 191 | // If the both first and last are not null-typed, check for overshadowing. | ||
| 192 | enum isOvershadowing = | ||
| 193 | is(firstParameterTypes[0] == Object) // Object overshadows all other classes!(this is needed for interfaces) | ||
| 194 | || is(lastParameterTypes[0] : firstParameterTypes[0]); | ||
| 195 | } | ||
| 196 | else | ||
| 197 | { | ||
| 198 | // If the first is null typed and the last is not - the is no overshadowing. | ||
| 199 | enum isOvershadowing = false; | ||
| 200 | } | ||
| 201 | |||
| 202 | static if (isOvershadowing) | ||
| 203 | { | ||
| 204 | enum indexOfFirstOvershadowingChoiceOnLast = 0; | ||
| 205 | } | ||
| 206 | else | ||
| 207 | { | ||
| 208 | enum indexOfFirstOvershadowingChoiceOnLast = | ||
| 209 | 1 + indexOfFirstOvershadowingChoiceOnLast!(choices[1..$]); | ||
| 210 | } | ||
| 211 | } | ||
| 212 | |||
| 213 | /** | ||
| 214 | Executes and returns one of a collection of handlers based on the type of the | ||
| 215 | switch object. | ||
| 216 | |||
| 217 | The first choice that $(D switchObject) can be casted to the type | ||
| 218 | of argument it accepts will be called with $(D switchObject) casted to that | ||
| 219 | type, and the value it'll return will be returned by $(D castSwitch). | ||
| 220 | |||
| 221 | If a choice's return type is void, the choice must throw an exception, unless | ||
| 222 | all the choices are void. In that case, castSwitch itself will return void. | ||
| 223 | |||
| 224 | Throws: If none of the choice matches, a $(D SwitchError) will be thrown. $(D | ||
| 225 | SwitchError) will also be thrown if not all the choices are void and a void | ||
| 226 | choice was executed without throwing anything. | ||
| 227 | |||
| 228 | Params: | ||
| 229 | choices = The $(D choices) needs to be composed of function or delegate | ||
| 230 | handlers that accept one argument. There can also be a choice that | ||
| 231 | accepts zero arguments. That choice will be invoked if the $(D | ||
| 232 | switchObject) is null. | ||
| 233 | switchObject = the object against which the tests are being made. | ||
| 234 | |||
| 235 | Returns: | ||
| 236 | The value of the selected choice. | ||
| 237 | |||
| 238 | Note: $(D castSwitch) can only be used with object types. | ||
| 239 | */ | ||
| 240 | auto castSwitch(choices...)(Object switchObject) | ||
| 241 | { | ||
| 242 | import core.exception : SwitchError; | ||
| 243 | import std.format : format; | ||
| 244 | |||
| 245 | // Check to see if all handlers return void. | ||
| 246 | enum areAllHandlersVoidResult = { | ||
| 247 | bool result = true; | ||
| 248 | foreach (index, choice; choices) | ||
| 249 | { | ||
| 250 | result &= is(ReturnType!choice == void); | ||
| 251 | } | ||
| 252 | return result; | ||
| 253 | }(); | ||
| 254 | |||
| 255 | if (switchObject !is null) | ||
| 256 | { | ||
| 257 | |||
| 258 | // Checking for exact matches: | ||
| 259 | const classInfo = typeid(switchObject); | ||
| 260 | foreach (index, choice; choices) | ||
| 261 | { | ||
| 262 | static assert(isCallable!choice, | ||
| 263 | "A choice handler must be callable"); | ||
| 264 | |||
| 265 | alias choiceParameterTypes = Parameters!choice; | ||
| 266 | static assert(choiceParameterTypes.length <= 1, | ||
| 267 | "A choice handler can not have more than one argument."); | ||
| 268 | |||
| 269 | static if (choiceParameterTypes.length == 1) | ||
| 270 | { | ||
| 271 | alias CastClass = choiceParameterTypes[0]; | ||
| 272 | static assert(is(CastClass == class) || is(CastClass == interface), | ||
| 273 | "A choice handler can have only class or interface typed argument."); | ||
| 274 | |||
| 275 | // Check for overshadowing: | ||
| 276 | immutable indexOfOvershadowingChoice = | ||
| 277 | indexOfFirstOvershadowingChoiceOnLast!(choices[0 .. index + 1]); | ||
| 278 | static assert(indexOfOvershadowingChoice == index, | ||
| 279 | "choice number %d(type %s) is overshadowed by choice number %d(type %s)".format( | ||
| 280 | index + 1, CastClass.stringof, indexOfOvershadowingChoice + 1, | ||
| 281 | Parameters!(choices[indexOfOvershadowingChoice])[0].stringof)); | ||
| 282 | |||
| 283 | if (classInfo == typeid(CastClass)) | ||
| 284 | { | ||
| 285 | static if (is(ReturnType!(choice) == void)) | ||
| 286 | { | ||
| 287 | choice(cast(CastClass) switchObject); | ||
| 288 | static if (areAllHandlersVoidResult) | ||
| 289 | { | ||
| 290 | return; | ||
| 291 | } | ||
| 292 | else | ||
| 293 | { | ||
| 294 | throw new SwitchError("Handlers that return void should throw"); | ||
| 295 | } | ||
| 296 | } | ||
| 297 | else | ||
| 298 | { | ||
| 299 | return choice(cast(CastClass) switchObject); | ||
| 300 | } | ||
| 301 | } | ||
| 302 | } | ||
| 303 | } | ||
| 304 | |||
| 305 | // Checking for derived matches: | ||
| 306 | foreach (choice; choices) | ||
| 307 | { | ||
| 308 | alias choiceParameterTypes = Parameters!choice; | ||
| 309 | static if (choiceParameterTypes.length == 1) | ||
| 310 | { | ||
| 311 | if (auto castedObject = cast(choiceParameterTypes[0]) switchObject) | ||
| 312 | { | ||
| 313 | static if (is(ReturnType!(choice) == void)) | ||
| 314 | { | ||
| 315 | choice(castedObject); | ||
| 316 | static if (areAllHandlersVoidResult) | ||
| 317 | { | ||
| 318 | return; | ||
| 319 | } | ||
| 320 | else | ||
| 321 | { | ||
| 322 | throw new SwitchError("Handlers that return void should throw"); | ||
| 323 | } | ||
| 324 | } | ||
| 325 | else | ||
| 326 | { | ||
| 327 | return choice(castedObject); | ||
| 328 | } | ||
| 329 | } | ||
| 330 | } | ||
| 331 | } | ||
| 332 | } | ||
| 333 | else // If switchObject is null: | ||
| 334 | { | ||
| 335 | // Checking for null matches: | ||
| 336 | foreach (index, choice; choices) | ||
| 337 | { | ||
| 338 | static if (Parameters!(choice).length == 0) | ||
| 339 | { | ||
| 340 | immutable indexOfOvershadowingChoice = | ||
| 341 | indexOfFirstOvershadowingChoiceOnLast!(choices[0 .. index + 1]); | ||
| 342 | |||
| 343 | // Check for overshadowing: | ||
| 344 | static assert(indexOfOvershadowingChoice == index, | ||
| 345 | "choice number %d(null reference) is overshadowed by choice number %d(null reference)".format( | ||
| 346 | index + 1, indexOfOvershadowingChoice + 1)); | ||
| 347 | |||
| 348 | if (switchObject is null) | ||
| 349 | { | ||
| 350 | static if (is(ReturnType!(choice) == void)) | ||
| 351 | { | ||
| 352 | choice(); | ||
| 353 | static if (areAllHandlersVoidResult) | ||
| 354 | { | ||
| 355 | return; | ||
| 356 | } | ||
| 357 | else | ||
| 358 | { | ||
| 359 | throw new SwitchError("Handlers that return void should throw"); | ||
| 360 | } | ||
| 361 | } | ||
| 362 | else | ||
| 363 | { | ||
| 364 | return choice(); | ||
| 365 | } | ||
| 366 | } | ||
| 367 | } | ||
| 368 | } | ||
| 369 | } | ||
| 370 | |||
| 371 | // In case nothing matched: | ||
| 372 | throw new SwitchError("Input not matched by any choice"); | ||
| 373 | } | ||
| 374 | |||
| 375 | /// | ||
| 376 | @system unittest | ||
| 377 | { | ||
| 378 | import std.algorithm.iteration : map; | ||
| 379 | import std.format : format; | ||
| 380 | |||
| 381 | class A | ||
| 382 | { | ||
| 383 | int a; | ||
| 384 | this(int a) {this.a = a;} | ||
| 385 | @property int i() { return a; } | ||
| 386 | } | ||
| 387 | interface I { } | ||
| 388 | class B : I { } | ||
| 389 | |||
| 390 | Object[] arr = [new A(1), new B(), null]; | ||
| 391 | |||
| 392 | auto results = arr.map!(castSwitch!( | ||
| 393 | (A a) => "A with a value of %d".format(a.a), | ||
| 394 | (I i) => "derived from I", | ||
| 395 | () => "null reference", | ||
| 396 | ))(); | ||
| 397 | |||
| 398 | // A is handled directly: | ||
| 399 | assert(results[0] == "A with a value of 1"); | ||
| 400 | // B has no handler - it is handled by the handler of I: | ||
| 401 | assert(results[1] == "derived from I"); | ||
| 402 | // null is handled by the null handler: | ||
| 403 | assert(results[2] == "null reference"); | ||
| 404 | } | ||
| 405 | |||
| 406 | /// Using with void handlers: | ||
| 407 | @system unittest | ||
| 408 | { | ||
| 409 | import std.exception : assertThrown; | ||
| 410 | |||
| 411 | class A { } | ||
| 412 | class B { } | ||
| 413 | // Void handlers are allowed if they throw: | ||
| 414 | assertThrown!Exception( | ||
| 415 | new B().castSwitch!( | ||
| 416 | (A a) => 1, | ||
| 417 | (B d) { throw new Exception("B is not allowed!"); } | ||
| 418 | )() | ||
| 419 | ); | ||
| 420 | |||
| 421 | // Void handlers are also allowed if all the handlers are void: | ||
| 422 | new A().castSwitch!( | ||
| 423 | (A a) { }, | ||
| 424 | (B b) { assert(false); }, | ||
| 425 | )(); | ||
| 426 | } | ||
| 427 | |||
| 428 | @system unittest | ||
| 429 | { | ||
| 430 | import core.exception : SwitchError; | ||
| 431 | import std.exception : assertThrown; | ||
| 432 | |||
| 433 | interface I { } | ||
| 434 | class A : I { } | ||
| 435 | class B { } | ||
| 436 | |||
| 437 | // Nothing matches: | ||
| 438 | assertThrown!SwitchError((new A()).castSwitch!( | ||
| 439 | (B b) => 1, | ||
| 440 | () => 2, | ||
| 441 | )()); | ||
| 442 | |||
| 443 | // Choices with multiple arguments are not allowed: | ||
| 444 | static assert(!__traits(compiles, | ||
| 445 | (new A()).castSwitch!( | ||
| 446 | (A a, B b) => 0, | ||
| 447 | )())); | ||
| 448 | |||
| 449 | // Only callable handlers allowed: | ||
| 450 | static assert(!__traits(compiles, | ||
| 451 | (new A()).castSwitch!( | ||
| 452 | 1234, | ||
| 453 | )())); | ||
| 454 | |||
| 455 | // Only object arguments allowed: | ||
| 456 | static assert(!__traits(compiles, | ||
| 457 | (new A()).castSwitch!( | ||
| 458 | (int x) => 0, | ||
| 459 | )())); | ||
| 460 | |||
| 461 | // Object overshadows regular classes: | ||
| 462 | static assert(!__traits(compiles, | ||
| 463 | (new A()).castSwitch!( | ||
| 464 | (Object o) => 0, | ||
| 465 | (A a) => 1, | ||
| 466 | )())); | ||
| 467 | |||
| 468 | // Object overshadows interfaces: | ||
| 469 | static assert(!__traits(compiles, | ||
| 470 | (new A()).castSwitch!( | ||
| 471 | (Object o) => 0, | ||
| 472 | (I i) => 1, | ||
| 473 | )())); | ||
| 474 | |||
| 475 | // No multiple null handlers allowed: | ||
| 476 | static assert(!__traits(compiles, | ||
| 477 | (new A()).castSwitch!( | ||
| 478 | () => 0, | ||
| 479 | () => 1, | ||
| 480 | )())); | ||
| 481 | |||
| 482 | // No non-throwing void handlers allowed(when there are non-void handlers): | ||
| 483 | assertThrown!SwitchError((new A()).castSwitch!( | ||
| 484 | (A a) {}, | ||
| 485 | (B b) => 2, | ||
| 486 | )()); | ||
| 487 | |||
| 488 | // All-void handlers work for the null case: | ||
| 489 | null.castSwitch!( | ||
| 490 | (Object o) { assert(false); }, | ||
| 491 | () { }, | ||
| 492 | )(); | ||
| 493 | |||
| 494 | // Throwing void handlers work for the null case: | ||
| 495 | assertThrown!Exception(null.castSwitch!( | ||
| 496 | (Object o) => 1, | ||
| 497 | () { throw new Exception("null"); }, | ||
| 498 | )()); | ||
| 499 | } | ||
| 500 | |||
| 501 | @system unittest | ||
| 502 | { | ||
| 503 | interface I { } | ||
| 504 | class B : I { } | ||
| 505 | class C : I { } | ||
| 506 | |||
| 507 | assert((new B()).castSwitch!( | ||
| 508 | (B b) => "class B", | ||
| 509 | (I i) => "derived from I", | ||
| 510 | ) == "class B"); | ||
| 511 | |||
| 512 | assert((new C()).castSwitch!( | ||
| 513 | (B b) => "class B", | ||
| 514 | (I i) => "derived from I", | ||
| 515 | ) == "derived from I"); | ||
| 516 | } | ||
| 517 | |||
| 518 | /** Clamps a value into the given bounds. | ||
| 519 | |||
| 520 | This functions is equivalent to $(D max(lower, min(upper,val))). | ||
| 521 | |||
| 522 | Params: | ||
| 523 | val = The value to _clamp. | ||
| 524 | lower = The _lower bound of the _clamp. | ||
| 525 | upper = The _upper bound of the _clamp. | ||
| 526 | |||
| 527 | Returns: | ||
| 528 | Returns $(D val), if it is between $(D lower) and $(D upper). | ||
| 529 | Otherwise returns the nearest of the two. | ||
| 530 | |||
| 531 | */ | ||
| 532 | auto clamp(T1, T2, T3)(T1 val, T2 lower, T3 upper) | ||
| 533 | in | ||
| 534 | { | ||
| 535 | import std.functional : greaterThan; | ||
| 536 | assert(!lower.greaterThan(upper)); | ||
| 537 | } | ||
| 538 | body | ||
| 539 | { | ||
| 540 | return max(lower, min(upper, val)); | ||
| 541 | } | ||
| 542 | |||
| 543 | /// | ||
| 544 | @safe unittest | ||
| 545 | { | ||
| 546 | assert(clamp(2, 1, 3) == 2); | ||
| 547 | assert(clamp(0, 1, 3) == 1); | ||
| 548 | assert(clamp(4, 1, 3) == 3); | ||
| 549 | |||
| 550 | assert(clamp(1, 1, 1) == 1); | ||
| 551 | |||
| 552 | assert(clamp(5, -1, 2u) == 2); | ||
| 553 | } | ||
| 554 | |||
| 555 | @safe unittest | ||
| 556 | { | ||
| 557 | int a = 1; | ||
| 558 | short b = 6; | ||
| 559 | double c = 2; | ||
| 560 | static assert(is(typeof(clamp(c,a,b)) == double)); | ||
| 561 | assert(clamp(c, a, b) == c); | ||
| 562 | assert(clamp(a-c, a, b) == a); | ||
| 563 | assert(clamp(b+c, a, b) == b); | ||
| 564 | // mixed sign | ||
| 565 | a = -5; | ||
| 566 | uint f = 5; | ||
| 567 | static assert(is(typeof(clamp(f, a, b)) == int)); | ||
| 568 | assert(clamp(f, a, b) == f); | ||
| 569 | // similar type deduction for (u)long | ||
| 570 | static assert(is(typeof(clamp(-1L, -2L, 2UL)) == long)); | ||
| 571 | |||
| 572 | // user-defined types | ||
| 573 | import std.datetime : Date; | ||
| 574 | assert(clamp(Date(1982, 1, 4), Date(1012, 12, 21), Date(2012, 12, 21)) == Date(1982, 1, 4)); | ||
| 575 | assert(clamp(Date(1982, 1, 4), Date.min, Date.max) == Date(1982, 1, 4)); | ||
| 576 | // UFCS style | ||
| 577 | assert(Date(1982, 1, 4).clamp(Date.min, Date.max) == Date(1982, 1, 4)); | ||
| 578 | |||
| 579 | } | ||
| 580 | |||
| 581 | // cmp | ||
| 582 | /********************************** | ||
| 583 | Performs three-way lexicographical comparison on two | ||
| 584 | $(REF_ALTTEXT input ranges, isInputRange, std,range,primitives) | ||
| 585 | according to predicate $(D pred). Iterating $(D r1) and $(D r2) in | ||
| 586 | lockstep, $(D cmp) compares each element $(D e1) of $(D r1) with the | ||
| 587 | corresponding element $(D e2) in $(D r2). If one of the ranges has been | ||
| 588 | finished, $(D cmp) returns a negative value if $(D r1) has fewer | ||
| 589 | elements than $(D r2), a positive value if $(D r1) has more elements | ||
| 590 | than $(D r2), and $(D 0) if the ranges have the same number of | ||
| 591 | elements. | ||
| 592 | |||
| 593 | If the ranges are strings, $(D cmp) performs UTF decoding | ||
| 594 | appropriately and compares the ranges one code point at a time. | ||
| 595 | |||
| 596 | Params: | ||
| 597 | pred = The predicate used for comparison. | ||
| 598 | r1 = The first range. | ||
| 599 | r2 = The second range. | ||
| 600 | |||
| 601 | Returns: | ||
| 602 | 0 if both ranges compare equal. -1 if the first differing element of $(D | ||
| 603 | r1) is less than the corresponding element of $(D r2) according to $(D | ||
| 604 | pred). 1 if the first differing element of $(D r2) is less than the | ||
| 605 | corresponding element of $(D r1) according to $(D pred). | ||
| 606 | |||
| 607 | */ | ||
| 608 | int cmp(alias pred = "a < b", R1, R2)(R1 r1, R2 r2) | ||
| 609 | if (isInputRange!R1 && isInputRange!R2 && !(isSomeString!R1 && isSomeString!R2)) | ||
| 610 | { | ||
| 611 | for (;; r1.popFront(), r2.popFront()) | ||
| 612 | { | ||
| 613 | if (r1.empty) return -cast(int)!r2.empty; | ||
| 614 | if (r2.empty) return !r1.empty; | ||
| 615 | auto a = r1.front, b = r2.front; | ||
| 616 | if (binaryFun!pred(a, b)) return -1; | ||
| 617 | if (binaryFun!pred(b, a)) return 1; | ||
| 618 | } | ||
| 619 | } | ||
| 620 | |||
| 621 | /// ditto | ||
| 622 | int cmp(alias pred = "a < b", R1, R2)(R1 r1, R2 r2) | ||
| 623 | if (isSomeString!R1 && isSomeString!R2) | ||
| 624 | { | ||
| 625 | import core.stdc.string : memcmp; | ||
| 626 | import std.utf : decode; | ||
| 627 | |||
| 628 | static if (is(typeof(pred) : string)) | ||
| 629 | enum isLessThan = pred == "a < b"; | ||
| 630 | else | ||
| 631 | enum isLessThan = false; | ||
| 632 | |||
| 633 | // For speed only | ||
| 634 | static int threeWay(size_t a, size_t b) | ||
| 635 | { | ||
| 636 | static if (size_t.sizeof == int.sizeof && isLessThan) | ||
| 637 | return a - b; | ||
| 638 | else | ||
| 639 | return binaryFun!pred(b, a) ? 1 : binaryFun!pred(a, b) ? -1 : 0; | ||
| 640 | } | ||
| 641 | // For speed only | ||
| 642 | // @@@BUG@@@ overloading should be allowed for nested functions | ||
| 643 | static int threeWayInt(int a, int b) | ||
| 644 | { | ||
| 645 | static if (isLessThan) | ||
| 646 | return a - b; | ||
| 647 | else | ||
| 648 | return binaryFun!pred(b, a) ? 1 : binaryFun!pred(a, b) ? -1 : 0; | ||
| 649 | } | ||
| 650 | |||
| 651 | static if (typeof(r1[0]).sizeof == typeof(r2[0]).sizeof && isLessThan) | ||
| 652 | { | ||
| 653 | static if (typeof(r1[0]).sizeof == 1) | ||
| 654 | { | ||
| 655 | immutable len = min(r1.length, r2.length); | ||
| 656 | immutable result = __ctfe ? | ||
| 657 | { | ||
| 658 | foreach (i; 0 .. len) | ||
| 659 | { | ||
| 660 | if (r1[i] != r2[i]) | ||
| 661 | return threeWayInt(r1[i], r2[i]); | ||
| 662 | } | ||
| 663 | return 0; | ||
| 664 | }() | ||
| 665 | : () @trusted { return memcmp(r1.ptr, r2.ptr, len); }(); | ||
| 666 | if (result) return result; | ||
| 667 | } | ||
| 668 | else | ||
| 669 | { | ||
| 670 | auto p1 = r1.ptr, p2 = r2.ptr, | ||
| 671 | pEnd = p1 + min(r1.length, r2.length); | ||
| 672 | for (; p1 != pEnd; ++p1, ++p2) | ||
| 673 | { | ||
| 674 | if (*p1 != *p2) return threeWayInt(cast(int) *p1, cast(int) *p2); | ||
| 675 | } | ||
| 676 | } | ||
| 677 | return threeWay(r1.length, r2.length); | ||
| 678 | } | ||
| 679 | else | ||
| 680 | { | ||
| 681 | for (size_t i1, i2;;) | ||
| 682 | { | ||
| 683 | if (i1 == r1.length) return threeWay(i2, r2.length); | ||
| 684 | if (i2 == r2.length) return threeWay(r1.length, i1); | ||
| 685 | immutable c1 = decode(r1, i1), | ||
| 686 | c2 = decode(r2, i2); | ||
| 687 | if (c1 != c2) return threeWayInt(cast(int) c1, cast(int) c2); | ||
| 688 | } | ||
| 689 | } | ||
| 690 | } | ||
| 691 | |||
| 692 | /// | ||
| 693 | @safe unittest | ||
| 694 | { | ||
| 695 | int result; | ||
| 696 | |||
| 697 | result = cmp("abc", "abc"); | ||
| 698 | assert(result == 0); | ||
| 699 | result = cmp("", ""); | ||
| 700 | assert(result == 0); | ||
| 701 | result = cmp("abc", "abcd"); | ||
| 702 | assert(result < 0); | ||
| 703 | result = cmp("abcd", "abc"); | ||
| 704 | assert(result > 0); | ||
| 705 | result = cmp("abc"d, "abd"); | ||
| 706 | assert(result < 0); | ||
| 707 | result = cmp("bbc", "abc"w); | ||
| 708 | assert(result > 0); | ||
| 709 | result = cmp("aaa", "aaaa"d); | ||
| 710 | assert(result < 0); | ||
| 711 | result = cmp("aaaa", "aaa"d); | ||
| 712 | assert(result > 0); | ||
| 713 | result = cmp("aaa", "aaa"d); | ||
| 714 | assert(result == 0); | ||
| 715 | result = cmp(cast(int[])[], cast(int[])[]); | ||
| 716 | assert(result == 0); | ||
| 717 | result = cmp([1, 2, 3], [1, 2, 3]); | ||
| 718 | assert(result == 0); | ||
| 719 | result = cmp([1, 3, 2], [1, 2, 3]); | ||
| 720 | assert(result > 0); | ||
| 721 | result = cmp([1, 2, 3], [1L, 2, 3, 4]); | ||
| 722 | assert(result < 0); | ||
| 723 | result = cmp([1L, 2, 3], [1, 2]); | ||
| 724 | assert(result > 0); | ||
| 725 | } | ||
| 726 | |||
| 727 | // equal | ||
| 728 | /** | ||
| 729 | Compares two ranges for equality, as defined by predicate $(D pred) | ||
| 730 | (which is $(D ==) by default). | ||
| 731 | */ | ||
| 732 | template equal(alias pred = "a == b") | ||
| 733 | { | ||
| 734 | enum isEmptyRange(R) = | ||
| 735 | isInputRange!R && __traits(compiles, {static assert(R.empty);}); | ||
| 736 | |||
| 737 | enum hasFixedLength(T) = hasLength!T || isNarrowString!T; | ||
| 738 | |||
| 739 | /++ | ||
| 740 | Compares two ranges for equality. The ranges may have | ||
| 741 | different element types, as long as $(D pred(r1.front, r2.front)) | ||
| 742 | evaluates to $(D bool). | ||
| 743 | Performs $(BIGOH min(r1.length, r2.length)) evaluations of $(D pred). | ||
| 744 | |||
| 745 | Params: | ||
| 746 | r1 = The first range to be compared. | ||
| 747 | r2 = The second range to be compared. | ||
| 748 | |||
| 749 | Returns: | ||
| 750 | $(D true) if and only if the two ranges compare _equal element | ||
| 751 | for element, according to binary predicate $(D pred). | ||
| 752 | |||
| 753 | See_Also: | ||
| 754 | $(HTTP sgi.com/tech/stl/_equal.html, STL's _equal) | ||
| 755 | +/ | ||
| 756 | bool equal(Range1, Range2)(Range1 r1, Range2 r2) | ||
| 757 | if (isInputRange!Range1 && isInputRange!Range2 && | ||
| 758 | is(typeof(binaryFun!pred(r1.front, r2.front)))) | ||
| 759 | { | ||
| 760 | static assert(!(isInfinite!Range1 && isInfinite!Range2), | ||
| 761 | "Both ranges are known to be infinite"); | ||
| 762 | |||
| 763 | //No pred calls necessary | ||
| 764 | static if (isEmptyRange!Range1 || isEmptyRange!Range2) | ||
| 765 | { | ||
| 766 | return r1.empty && r2.empty; | ||
| 767 | } | ||
| 768 | else static if ((isInfinite!Range1 && hasFixedLength!Range2) || | ||
| 769 | (hasFixedLength!Range1 && isInfinite!Range2)) | ||
| 770 | { | ||
| 771 | return false; | ||
| 772 | } | ||
| 773 | //Detect default pred and compatible dynamic array | ||
| 774 | else static if (is(typeof(pred) == string) && pred == "a == b" && | ||
| 775 | isArray!Range1 && isArray!Range2 && is(typeof(r1 == r2))) | ||
| 776 | { | ||
| 777 | return r1 == r2; | ||
| 778 | } | ||
| 779 | // if one of the arguments is a string and the other isn't, then auto-decoding | ||
| 780 | // can be avoided if they have the same ElementEncodingType | ||
| 781 | else static if (is(typeof(pred) == string) && pred == "a == b" && | ||
| 782 | isAutodecodableString!Range1 != isAutodecodableString!Range2 && | ||
| 783 | is(ElementEncodingType!Range1 == ElementEncodingType!Range2)) | ||
| 784 | { | ||
| 785 | import std.utf : byCodeUnit; | ||
| 786 | |||
| 787 | static if (isAutodecodableString!Range1) | ||
| 788 | { | ||
| 789 | return equal(r1.byCodeUnit, r2); | ||
| 790 | } | ||
| 791 | else | ||
| 792 | { | ||
| 793 | return equal(r2.byCodeUnit, r1); | ||
| 794 | } | ||
| 795 | } | ||
| 796 | //Try a fast implementation when the ranges have comparable lengths | ||
| 797 | else static if (hasLength!Range1 && hasLength!Range2 && is(typeof(r1.length == r2.length))) | ||
| 798 | { | ||
| 799 | immutable len1 = r1.length; | ||
| 800 | immutable len2 = r2.length; | ||
| 801 | if (len1 != len2) return false; //Short circuit return | ||
| 802 | |||
| 803 | //Lengths are the same, so we need to do an actual comparison | ||
| 804 | //Good news is we can squeeze out a bit of performance by not checking if r2 is empty | ||
| 805 | for (; !r1.empty; r1.popFront(), r2.popFront()) | ||
| 806 | { | ||
| 807 | if (!binaryFun!(pred)(r1.front, r2.front)) return false; | ||
| 808 | } | ||
| 809 | return true; | ||
| 810 | } | ||
| 811 | else | ||
| 812 | { | ||
| 813 | //Generic case, we have to walk both ranges making sure neither is empty | ||
| 814 | for (; !r1.empty; r1.popFront(), r2.popFront()) | ||
| 815 | { | ||
| 816 | if (r2.empty) return false; | ||
| 817 | if (!binaryFun!(pred)(r1.front, r2.front)) return false; | ||
| 818 | } | ||
| 819 | static if (!isInfinite!Range1) | ||
| 820 | return r2.empty; | ||
| 821 | } | ||
| 822 | } | ||
| 823 | } | ||
| 824 | |||
| 825 | /// | ||
| 826 | @safe unittest | ||
| 827 | { | ||
| 828 | import std.algorithm.comparison : equal; | ||
| 829 | import std.math : approxEqual; | ||
| 830 | |||
| 831 | int[] a = [ 1, 2, 4, 3 ]; | ||
| 832 | assert(!equal(a, a[1..$])); | ||
| 833 | assert(equal(a, a)); | ||
| 834 | assert(equal!((a, b) => a == b)(a, a)); | ||
| 835 | |||
| 836 | // different types | ||
| 837 | double[] b = [ 1.0, 2, 4, 3]; | ||
| 838 | assert(!equal(a, b[1..$])); | ||
| 839 | assert(equal(a, b)); | ||
| 840 | |||
| 841 | // predicated: ensure that two vectors are approximately equal | ||
| 842 | double[] c = [ 1.005, 2, 4, 3]; | ||
| 843 | assert(equal!approxEqual(b, c)); | ||
| 844 | } | ||
| 845 | |||
| 846 | /++ | ||
| 847 | Tip: $(D equal) can itself be used as a predicate to other functions. | ||
| 848 | This can be very useful when the element type of a range is itself a | ||
| 849 | range. In particular, $(D equal) can be its own predicate, allowing | ||
| 850 | range of range (of range...) comparisons. | ||
| 851 | +/ | ||
| 852 | @safe unittest | ||
| 853 | { | ||
| 854 | import std.algorithm.comparison : equal; | ||
| 855 | import std.range : iota, chunks; | ||
| 856 | assert(equal!(equal!equal)( | ||
| 857 | [[[0, 1], [2, 3]], [[4, 5], [6, 7]]], | ||
| 858 | iota(0, 8).chunks(2).chunks(2) | ||
| 859 | )); | ||
| 860 | } | ||
| 861 | |||
| 862 | @safe unittest | ||
| 863 | { | ||
| 864 | import std.algorithm.iteration : map; | ||
| 865 | import std.internal.test.dummyrange : ReferenceForwardRange, | ||
| 866 | ReferenceInputRange, ReferenceInfiniteForwardRange; | ||
| 867 | import std.math : approxEqual; | ||
| 868 | |||
| 869 | // various strings | ||
| 870 | assert(equal("æøå", "æøå")); //UTF8 vs UTF8 | ||
| 871 | assert(!equal("???", "æøå")); //UTF8 vs UTF8 | ||
| 872 | assert(equal("æøå"w, "æøå"d)); //UTF16 vs UTF32 | ||
| 873 | assert(!equal("???"w, "æøå"d));//UTF16 vs UTF32 | ||
| 874 | assert(equal("æøå"d, "æøå"d)); //UTF32 vs UTF32 | ||
| 875 | assert(!equal("???"d, "æøå"d));//UTF32 vs UTF32 | ||
| 876 | assert(!equal("hello", "world")); | ||
| 877 | |||
| 878 | // same strings, but "explicit non default" comparison (to test the non optimized array comparison) | ||
| 879 | assert( equal!("a == b")("æøå", "æøå")); //UTF8 vs UTF8 | ||
| 880 | assert(!equal!("a == b")("???", "æøå")); //UTF8 vs UTF8 | ||
| 881 | assert( equal!("a == b")("æøå"w, "æøå"d)); //UTF16 vs UTF32 | ||
| 882 | assert(!equal!("a == b")("???"w, "æøå"d));//UTF16 vs UTF32 | ||
| 883 | assert( equal!("a == b")("æøå"d, "æøå"d)); //UTF32 vs UTF32 | ||
| 884 | assert(!equal!("a == b")("???"d, "æøå"d));//UTF32 vs UTF32 | ||
| 885 | assert(!equal!("a == b")("hello", "world")); | ||
| 886 | |||
| 887 | //Array of string | ||
| 888 | assert(equal(["hello", "world"], ["hello", "world"])); | ||
| 889 | assert(!equal(["hello", "world"], ["hello"])); | ||
| 890 | assert(!equal(["hello", "world"], ["hello", "Bob!"])); | ||
| 891 | |||
| 892 | //Should not compile, because "string == dstring" is illegal | ||
| 893 | static assert(!is(typeof(equal(["hello", "world"], ["hello"d, "world"d])))); | ||
| 894 | //However, arrays of non-matching string can be compared using equal!equal. Neat-o! | ||
| 895 | equal!equal(["hello", "world"], ["hello"d, "world"d]); | ||
| 896 | |||
| 897 | //Tests, with more fancy map ranges | ||
| 898 | int[] a = [ 1, 2, 4, 3 ]; | ||
| 899 | assert(equal([2, 4, 8, 6], map!"a*2"(a))); | ||
| 900 | double[] b = [ 1.0, 2, 4, 3]; | ||
| 901 | double[] c = [ 1.005, 2, 4, 3]; | ||
| 902 | assert(equal!approxEqual(map!"a*2"(b), map!"a*2"(c))); | ||
| 903 | assert(!equal([2, 4, 1, 3], map!"a*2"(a))); | ||
| 904 | assert(!equal([2, 4, 1], map!"a*2"(a))); | ||
| 905 | assert(!equal!approxEqual(map!"a*3"(b), map!"a*2"(c))); | ||
| 906 | |||
| 907 | //Tests with some fancy reference ranges. | ||
| 908 | ReferenceInputRange!int cir = new ReferenceInputRange!int([1, 2, 4, 3]); | ||
| 909 | ReferenceForwardRange!int cfr = new ReferenceForwardRange!int([1, 2, 4, 3]); | ||
| 910 | assert(equal(cir, a)); | ||
| 911 | cir = new ReferenceInputRange!int([1, 2, 4, 3]); | ||
| 912 | assert(equal(cir, cfr.save)); | ||
| 913 | assert(equal(cfr.save, cfr.save)); | ||
| 914 | cir = new ReferenceInputRange!int([1, 2, 8, 1]); | ||
| 915 | assert(!equal(cir, cfr)); | ||
| 916 | |||
| 917 | //Test with an infinite range | ||
| 918 | auto ifr = new ReferenceInfiniteForwardRange!int; | ||
| 919 | assert(!equal(a, ifr)); | ||
| 920 | assert(!equal(ifr, a)); | ||
| 921 | //Test InputRange without length | ||
| 922 | assert(!equal(ifr, cir)); | ||
| 923 | assert(!equal(cir, ifr)); | ||
| 924 | } | ||
| 925 | |||
| 926 | @safe pure unittest | ||
| 927 | { | ||
| 928 | import std.utf : byChar, byWchar, byDchar; | ||
| 929 | |||
| 930 | assert(equal("æøå".byChar, "æøå")); | ||
| 931 | assert(equal("æøå", "æøå".byChar)); | ||
| 932 | assert(equal("æøå".byWchar, "æøå"w)); | ||
| 933 | assert(equal("æøå"w, "æøå".byWchar)); | ||
| 934 | assert(equal("æøå".byDchar, "æøå"d)); | ||
| 935 | assert(equal("æøå"d, "æøå".byDchar)); | ||
| 936 | } | ||
| 937 | |||
| 938 | @safe pure unittest | ||
| 939 | { | ||
| 940 | struct R(bool _empty) { | ||
| 941 | enum empty = _empty; | ||
| 942 | @property char front(){assert(0);} | ||
| 943 | void popFront(){assert(0);} | ||
| 944 | } | ||
| 945 | alias I = R!false; | ||
| 946 | static assert(!__traits(compiles, I().equal(I()))); | ||
| 947 | // strings have fixed length so don't need to compare elements | ||
| 948 | assert(!I().equal("foo")); | ||
| 949 | assert(!"bar".equal(I())); | ||
| 950 | |||
| 951 | alias E = R!true; | ||
| 952 | assert(E().equal(E())); | ||
| 953 | assert(E().equal("")); | ||
| 954 | assert("".equal(E())); | ||
| 955 | assert(!E().equal("foo")); | ||
| 956 | assert(!"bar".equal(E())); | ||
| 957 | } | ||
| 958 | |||
| 959 | // MaxType | ||
| 960 | private template MaxType(T...) | ||
| 961 | if (T.length >= 1) | ||
| 962 | { | ||
| 963 | static if (T.length == 1) | ||
| 964 | { | ||
| 965 | alias MaxType = T[0]; | ||
| 966 | } | ||
| 967 | else static if (T.length == 2) | ||
| 968 | { | ||
| 969 | static if (!is(typeof(T[0].min))) | ||
| 970 | alias MaxType = CommonType!T; | ||
| 971 | else static if (T[1].max > T[0].max) | ||
| 972 | alias MaxType = T[1]; | ||
| 973 | else | ||
| 974 | alias MaxType = T[0]; | ||
| 975 | } | ||
| 976 | else | ||
| 977 | { | ||
| 978 | alias MaxType = MaxType!(MaxType!(T[0 .. ($+1)/2]), MaxType!(T[($+1)/2 .. $])); | ||
| 979 | } | ||
| 980 | } | ||
| 981 | |||
| 982 | // levenshteinDistance | ||
| 983 | /** | ||
| 984 | Encodes $(HTTP realityinteractive.com/rgrzywinski/archives/000249.html, | ||
| 985 | edit operations) necessary to transform one sequence into | ||
| 986 | another. Given sequences $(D s) (source) and $(D t) (target), a | ||
| 987 | sequence of $(D EditOp) encodes the steps that need to be taken to | ||
| 988 | convert $(D s) into $(D t). For example, if $(D s = "cat") and $(D | ||
| 989 | "cars"), the minimal sequence that transforms $(D s) into $(D t) is: | ||
| 990 | skip two characters, replace 't' with 'r', and insert an 's'. Working | ||
| 991 | with edit operations is useful in applications such as spell-checkers | ||
| 992 | (to find the closest word to a given misspelled word), approximate | ||
| 993 | searches, diff-style programs that compute the difference between | ||
| 994 | files, efficient encoding of patches, DNA sequence analysis, and | ||
| 995 | plagiarism detection. | ||
| 996 | */ | ||
| 997 | |||
| 998 | enum EditOp : char | ||
| 999 | { | ||
| 1000 | /** Current items are equal; no editing is necessary. */ | ||
| 1001 | none = 'n', | ||
| 1002 | /** Substitute current item in target with current item in source. */ | ||
| 1003 | substitute = 's', | ||
| 1004 | /** Insert current item from the source into the target. */ | ||
| 1005 | insert = 'i', | ||
| 1006 | /** Remove current item from the target. */ | ||
| 1007 | remove = 'r' | ||
| 1008 | } | ||
| 1009 | |||
| 1010 | /// | ||
| 1011 | @safe unittest | ||
| 1012 | { | ||
| 1013 | with(EditOp) | ||
| 1014 | { | ||
| 1015 | assert(levenshteinDistanceAndPath("foo", "foobar")[1] == [none, none, none, insert, insert, insert]); | ||
| 1016 | assert(levenshteinDistanceAndPath("banana", "fazan")[1] == [substitute, none, substitute, none, none, remove]); | ||
| 1017 | } | ||
| 1018 | } | ||
| 1019 | |||
| 1020 | private struct Levenshtein(Range, alias equals, CostType = size_t) | ||
| 1021 | { | ||
| 1022 | EditOp[] path() | ||
| 1023 | { | ||
| 1024 | import std.algorithm.mutation : reverse; | ||
| 1025 | |||
| 1026 | EditOp[] result; | ||
| 1027 | size_t i = rows - 1, j = cols - 1; | ||
| 1028 | // restore the path | ||
| 1029 | while (i || j) | ||
| 1030 | { | ||
| 1031 | auto cIns = j == 0 ? CostType.max : matrix(i,j - 1); | ||
| 1032 | auto cDel = i == 0 ? CostType.max : matrix(i - 1,j); | ||
| 1033 | auto cSub = i == 0 || j == 0 | ||
| 1034 | ? CostType.max | ||
| 1035 | : matrix(i - 1,j - 1); | ||
| 1036 | switch (min_index(cSub, cIns, cDel)) | ||
| 1037 | { | ||
| 1038 | case 0: | ||
| 1039 | result ~= matrix(i - 1,j - 1) == matrix(i,j) | ||
| 1040 | ? EditOp.none | ||
| 1041 | : EditOp.substitute; | ||
| 1042 | --i; | ||
| 1043 | --j; | ||
| 1044 | break; | ||
| 1045 | case 1: | ||
| 1046 | result ~= EditOp.insert; | ||
| 1047 | --j; | ||
| 1048 | break; | ||
| 1049 | default: | ||
| 1050 | result ~= EditOp.remove; | ||
| 1051 | --i; | ||
| 1052 | break; | ||
| 1053 | } | ||
| 1054 | } | ||
| 1055 | reverse(result); | ||
| 1056 | return result; | ||
| 1057 | } | ||
| 1058 | |||
| 1059 | ~this() { | ||
| 1060 | FreeMatrix(); | ||
| 1061 | } | ||
| 1062 | |||
| 1063 | private: | ||
| 1064 | CostType _deletionIncrement = 1, | ||
| 1065 | _insertionIncrement = 1, | ||
| 1066 | _substitutionIncrement = 1; | ||
| 1067 | CostType[] _matrix; | ||
| 1068 | size_t rows, cols; | ||
| 1069 | |||
| 1070 | // Treat _matrix as a rectangular array | ||
| 1071 | ref CostType matrix(size_t row, size_t col) { return _matrix[row * cols + col]; } | ||
| 1072 | |||
| 1073 | void AllocMatrix(size_t r, size_t c) @trusted { | ||
| 1074 | import core.checkedint : mulu; | ||
| 1075 | bool overflow; | ||
| 1076 | const rc = mulu(r, c, overflow); | ||
| 1077 | if (overflow) assert(0); | ||
| 1078 | rows = r; | ||
| 1079 | cols = c; | ||
| 1080 | if (_matrix.length < rc) | ||
| 1081 | { | ||
| 1082 | import core.exception : onOutOfMemoryError; | ||
| 1083 | import core.stdc.stdlib : realloc; | ||
| 1084 | const nbytes = mulu(rc, _matrix[0].sizeof, overflow); | ||
| 1085 | if (overflow) assert(0); | ||
| 1086 | auto m = cast(CostType *) realloc(_matrix.ptr, nbytes); | ||
| 1087 | if (!m) | ||
| 1088 | onOutOfMemoryError(); | ||
| 1089 | _matrix = m[0 .. r * c]; | ||
| 1090 | InitMatrix(); | ||
| 1091 | } | ||
| 1092 | } | ||
| 1093 | |||
| 1094 | void FreeMatrix() @trusted { | ||
| 1095 | import core.stdc.stdlib : free; | ||
| 1096 | |||
| 1097 | free(_matrix.ptr); | ||
| 1098 | _matrix = null; | ||
| 1099 | } | ||
| 1100 | |||
| 1101 | void InitMatrix() { | ||
| 1102 | foreach (r; 0 .. rows) | ||
| 1103 | matrix(r,0) = r * _deletionIncrement; | ||
| 1104 | foreach (c; 0 .. cols) | ||
| 1105 | matrix(0,c) = c * _insertionIncrement; | ||
| 1106 | } | ||
| 1107 | |||
| 1108 | static uint min_index(CostType i0, CostType i1, CostType i2) | ||
| 1109 | { | ||
| 1110 | if (i0 <= i1) | ||
| 1111 | { | ||
| 1112 | return i0 <= i2 ? 0 : 2; | ||
| 1113 | } | ||
| 1114 | else | ||
| 1115 | { | ||
| 1116 | return i1 <= i2 ? 1 : 2; | ||
| 1117 | } | ||
| 1118 | } | ||
| 1119 | |||
| 1120 | CostType distanceWithPath(Range s, Range t) | ||
| 1121 | { | ||
| 1122 | auto slen = walkLength(s.save), tlen = walkLength(t.save); | ||
| 1123 | AllocMatrix(slen + 1, tlen + 1); | ||
| 1124 | foreach (i; 1 .. rows) | ||
| 1125 | { | ||
| 1126 | auto sfront = s.front; | ||
| 1127 | auto tt = t.save; | ||
| 1128 | foreach (j; 1 .. cols) | ||
| 1129 | { | ||
| 1130 | auto cSub = matrix(i - 1,j - 1) | ||
| 1131 | + (equals(sfront, tt.front) ? 0 : _substitutionIncrement); | ||
| 1132 | tt.popFront(); | ||
| 1133 | auto cIns = matrix(i,j - 1) + _insertionIncrement; | ||
| 1134 | auto cDel = matrix(i - 1,j) + _deletionIncrement; | ||
| 1135 | switch (min_index(cSub, cIns, cDel)) | ||
| 1136 | { | ||
| 1137 | case 0: | ||
| 1138 | matrix(i,j) = cSub; | ||
| 1139 | break; | ||
| 1140 | case 1: | ||
| 1141 | matrix(i,j) = cIns; | ||
| 1142 | break; | ||
| 1143 | default: | ||
| 1144 | matrix(i,j) = cDel; | ||
| 1145 | break; | ||
| 1146 | } | ||
| 1147 | } | ||
| 1148 | s.popFront(); | ||
| 1149 | } | ||
| 1150 | return matrix(slen,tlen); | ||
| 1151 | } | ||
| 1152 | |||
| 1153 | CostType distanceLowMem(Range s, Range t, CostType slen, CostType tlen) | ||
| 1154 | { | ||
| 1155 | CostType lastdiag, olddiag; | ||
| 1156 | AllocMatrix(slen + 1, 1); | ||
| 1157 | foreach (y; 1 .. slen + 1) | ||
| 1158 | { | ||
| 1159 | _matrix[y] = y; | ||
| 1160 | } | ||
| 1161 | foreach (x; 1 .. tlen + 1) | ||
| 1162 | { | ||
| 1163 | auto tfront = t.front; | ||
| 1164 | auto ss = s.save; | ||
| 1165 | _matrix[0] = x; | ||
| 1166 | lastdiag = x - 1; | ||
| 1167 | foreach (y; 1 .. rows) | ||
| 1168 | { | ||
| 1169 | olddiag = _matrix[y]; | ||
| 1170 | auto cSub = lastdiag + (equals(ss.front, tfront) ? 0 : _substitutionIncrement); | ||
| 1171 | ss.popFront(); | ||
| 1172 | auto cIns = _matrix[y - 1] + _insertionIncrement; | ||
| 1173 | auto cDel = _matrix[y] + _deletionIncrement; | ||
| 1174 | switch (min_index(cSub, cIns, cDel)) | ||
| 1175 | { | ||
| 1176 | case 0: | ||
| 1177 | _matrix[y] = cSub; | ||
| 1178 | break; | ||
| 1179 | case 1: | ||
| 1180 | _matrix[y] = cIns; | ||
| 1181 | break; | ||
| 1182 | default: | ||
| 1183 | _matrix[y] = cDel; | ||
| 1184 | break; | ||
| 1185 | } | ||
| 1186 | lastdiag = olddiag; | ||
| 1187 | } | ||
| 1188 | t.popFront(); | ||
| 1189 | } | ||
| 1190 | return _matrix[slen]; | ||
| 1191 | } | ||
| 1192 | } | ||
| 1193 | |||
| 1194 | /** | ||
| 1195 | Returns the $(HTTP wikipedia.org/wiki/Levenshtein_distance, Levenshtein | ||
| 1196 | distance) between $(D s) and $(D t). The Levenshtein distance computes | ||
| 1197 | the minimal amount of edit operations necessary to transform $(D s) | ||
| 1198 | into $(D t). Performs $(BIGOH s.length * t.length) evaluations of $(D | ||
| 1199 | equals) and occupies $(BIGOH s.length * t.length) storage. | ||
| 1200 | |||
| 1201 | Params: | ||
| 1202 | equals = The binary predicate to compare the elements of the two ranges. | ||
| 1203 | s = The original range. | ||
| 1204 | t = The transformation target | ||
| 1205 | |||
| 1206 | Returns: | ||
| 1207 | The minimal number of edits to transform s into t. | ||
| 1208 | |||
| 1209 | Does not allocate GC memory. | ||
| 1210 | */ | ||
| 1211 | size_t levenshteinDistance(alias equals = (a,b) => a == b, Range1, Range2) | ||
| 1212 | (Range1 s, Range2 t) | ||
| 1213 | if (isForwardRange!(Range1) && isForwardRange!(Range2)) | ||
| 1214 | { | ||
| 1215 | alias eq = binaryFun!(equals); | ||
| 1216 | |||
| 1217 | for (;;) | ||
| 1218 | { | ||
| 1219 | if (s.empty) return t.walkLength; | ||
| 1220 | if (t.empty) return s.walkLength; | ||
| 1221 | if (eq(s.front, t.front)) | ||
| 1222 | { | ||
| 1223 | s.popFront(); | ||
| 1224 | t.popFront(); | ||
| 1225 | continue; | ||
| 1226 | } | ||
| 1227 | static if (isBidirectionalRange!(Range1) && isBidirectionalRange!(Range2)) | ||
| 1228 | { | ||
| 1229 | if (eq(s.back, t.back)) | ||
| 1230 | { | ||
| 1231 | s.popBack(); | ||
| 1232 | t.popBack(); | ||
| 1233 | continue; | ||
| 1234 | } | ||
| 1235 | } | ||
| 1236 | break; | ||
| 1237 | } | ||
| 1238 | |||
| 1239 | auto slen = walkLength(s.save); | ||
| 1240 | auto tlen = walkLength(t.save); | ||
| 1241 | |||
| 1242 | if (slen == 1 && tlen == 1) | ||
| 1243 | { | ||
| 1244 | return eq(s.front, t.front) ? 0 : 1; | ||
| 1245 | } | ||
| 1246 | |||
| 1247 | if (slen > tlen) | ||
| 1248 | { | ||
| 1249 | Levenshtein!(Range1, eq, size_t) lev; | ||
| 1250 | return lev.distanceLowMem(s, t, slen, tlen); | ||
| 1251 | } | ||
| 1252 | else | ||
| 1253 | { | ||
| 1254 | Levenshtein!(Range2, eq, size_t) lev; | ||
| 1255 | return lev.distanceLowMem(t, s, tlen, slen); | ||
| 1256 | } | ||
| 1257 | } | ||
| 1258 | |||
| 1259 | /// | ||
| 1260 | @safe unittest | ||
| 1261 | { | ||
| 1262 | import std.algorithm.iteration : filter; | ||
| 1263 | import std.uni : toUpper; | ||
| 1264 | |||
| 1265 | assert(levenshteinDistance("cat", "rat") == 1); | ||
| 1266 | assert(levenshteinDistance("parks", "spark") == 2); | ||
| 1267 | assert(levenshteinDistance("abcde", "abcde") == 0); | ||
| 1268 | assert(levenshteinDistance("abcde", "abCde") == 1); | ||
| 1269 | assert(levenshteinDistance("kitten", "sitting") == 3); | ||
| 1270 | assert(levenshteinDistance!((a, b) => toUpper(a) == toUpper(b)) | ||
| 1271 | ("parks", "SPARK") == 2); | ||
| 1272 | assert(levenshteinDistance("parks".filter!"true", "spark".filter!"true") == 2); | ||
| 1273 | assert(levenshteinDistance("ID", "I♥D") == 1); | ||
| 1274 | } | ||
| 1275 | |||
| 1276 | @safe @nogc nothrow unittest | ||
| 1277 | { | ||
| 1278 | assert(levenshteinDistance("cat"d, "rat"d) == 1); | ||
| 1279 | } | ||
| 1280 | |||
| 1281 | /// ditto | ||
| 1282 | size_t levenshteinDistance(alias equals = (a,b) => a == b, Range1, Range2) | ||
| 1283 | (auto ref Range1 s, auto ref Range2 t) | ||
| 1284 | if (isConvertibleToString!Range1 || isConvertibleToString!Range2) | ||
| 1285 | { | ||
| 1286 | import std.meta : staticMap; | ||
| 1287 | alias Types = staticMap!(convertToString, Range1, Range2); | ||
| 1288 | return levenshteinDistance!(equals, Types)(s, t); | ||
| 1289 | } | ||
| 1290 | |||
| 1291 | @safe unittest | ||
| 1292 | { | ||
| 1293 | static struct S { string s; alias s this; } | ||
| 1294 | assert(levenshteinDistance(S("cat"), S("rat")) == 1); | ||
| 1295 | assert(levenshteinDistance("cat", S("rat")) == 1); | ||
| 1296 | assert(levenshteinDistance(S("cat"), "rat") == 1); | ||
| 1297 | } | ||
| 1298 | |||
| 1299 | @safe @nogc nothrow unittest | ||
| 1300 | { | ||
| 1301 | static struct S { dstring s; alias s this; } | ||
| 1302 | assert(levenshteinDistance(S("cat"d), S("rat"d)) == 1); | ||
| 1303 | assert(levenshteinDistance("cat"d, S("rat"d)) == 1); | ||
| 1304 | assert(levenshteinDistance(S("cat"d), "rat"d) == 1); | ||
| 1305 | } | ||
| 1306 | |||
| 1307 | /** | ||
| 1308 | Returns the Levenshtein distance and the edit path between $(D s) and | ||
| 1309 | $(D t). | ||
| 1310 | |||
| 1311 | Params: | ||
| 1312 | equals = The binary predicate to compare the elements of the two ranges. | ||
| 1313 | s = The original range. | ||
| 1314 | t = The transformation target | ||
| 1315 | |||
| 1316 | Returns: | ||
| 1317 | Tuple with the first element being the minimal amount of edits to transform s into t and | ||
| 1318 | the second element being the sequence of edits to effect this transformation. | ||
| 1319 | |||
| 1320 | Allocates GC memory for the returned EditOp[] array. | ||
| 1321 | */ | ||
| 1322 | Tuple!(size_t, EditOp[]) | ||
| 1323 | levenshteinDistanceAndPath(alias equals = (a,b) => a == b, Range1, Range2) | ||
| 1324 | (Range1 s, Range2 t) | ||
| 1325 | if (isForwardRange!(Range1) && isForwardRange!(Range2)) | ||
| 1326 | { | ||
| 1327 | Levenshtein!(Range1, binaryFun!(equals)) lev; | ||
| 1328 | auto d = lev.distanceWithPath(s, t); | ||
| 1329 | return tuple(d, lev.path()); | ||
| 1330 | } | ||
| 1331 | |||
| 1332 | /// | ||
| 1333 | @safe unittest | ||
| 1334 | { | ||
| 1335 | string a = "Saturday", b = "Sundays"; | ||
| 1336 | auto p = levenshteinDistanceAndPath(a, b); | ||
| 1337 | assert(p[0] == 4); | ||
| 1338 | assert(equal(p[1], "nrrnsnnni")); | ||
| 1339 | } | ||
| 1340 | |||
| 1341 | @safe unittest | ||
| 1342 | { | ||
| 1343 | assert(levenshteinDistance("a", "a") == 0); | ||
| 1344 | assert(levenshteinDistance("a", "b") == 1); | ||
| 1345 | assert(levenshteinDistance("aa", "ab") == 1); | ||
| 1346 | assert(levenshteinDistance("aa", "abc") == 2); | ||
| 1347 | assert(levenshteinDistance("Saturday", "Sunday") == 3); | ||
| 1348 | assert(levenshteinDistance("kitten", "sitting") == 3); | ||
| 1349 | } | ||
| 1350 | |||
| 1351 | /// ditto | ||
| 1352 | Tuple!(size_t, EditOp[]) | ||
| 1353 | levenshteinDistanceAndPath(alias equals = (a,b) => a == b, Range1, Range2) | ||
| 1354 | (auto ref Range1 s, auto ref Range2 t) | ||
| 1355 | if (isConvertibleToString!Range1 || isConvertibleToString!Range2) | ||
| 1356 | { | ||
| 1357 | import std.meta : staticMap; | ||
| 1358 | alias Types = staticMap!(convertToString, Range1, Range2); | ||
| 1359 | return levenshteinDistanceAndPath!(equals, Types)(s, t); | ||
| 1360 | } | ||
| 1361 | |||
| 1362 | @safe unittest | ||
| 1363 | { | ||
| 1364 | static struct S { string s; alias s this; } | ||
| 1365 | assert(levenshteinDistanceAndPath(S("cat"), S("rat"))[0] == 1); | ||
| 1366 | assert(levenshteinDistanceAndPath("cat", S("rat"))[0] == 1); | ||
| 1367 | assert(levenshteinDistanceAndPath(S("cat"), "rat")[0] == 1); | ||
| 1368 | } | ||
| 1369 | |||
| 1370 | // max | ||
| 1371 | /** | ||
| 1372 | Iterates the passed arguments and return the maximum value. | ||
| 1373 | |||
| 1374 | Params: | ||
| 1375 | args = The values to select the maximum from. At least two arguments must | ||
| 1376 | be passed. | ||
| 1377 | |||
| 1378 | Returns: | ||
| 1379 | The maximum of the passed-in args. The type of the returned value is | ||
| 1380 | the type among the passed arguments that is able to store the largest value. | ||
| 1381 | |||
| 1382 | See_Also: | ||
| 1383 | $(REF maxElement, std,algorithm,searching) | ||
| 1384 | */ | ||
| 1385 | MaxType!T max(T...)(T args) | ||
| 1386 | if (T.length >= 2) | ||
| 1387 | { | ||
| 1388 | //Get "a" | ||
| 1389 | static if (T.length <= 2) | ||
| 1390 | alias a = args[0]; | ||
| 1391 | else | ||
| 1392 | auto a = max(args[0 .. ($+1)/2]); | ||
| 1393 | alias T0 = typeof(a); | ||
| 1394 | |||
| 1395 | //Get "b" | ||
| 1396 | static if (T.length <= 3) | ||
| 1397 | alias b = args[$-1]; | ||
| 1398 | else | ||
| 1399 | auto b = max(args[($+1)/2 .. $]); | ||
| 1400 | alias T1 = typeof(b); | ||
| 1401 | |||
| 1402 | import std.algorithm.internal : algoFormat; | ||
| 1403 | static assert(is(typeof(a < b)), | ||
| 1404 | algoFormat("Invalid arguments: Cannot compare types %s and %s.", T0.stringof, T1.stringof)); | ||
| 1405 | |||
| 1406 | //Do the "max" proper with a and b | ||
| 1407 | import std.functional : lessThan; | ||
| 1408 | immutable chooseB = lessThan!(T0, T1)(a, b); | ||
| 1409 | return cast(typeof(return)) (chooseB ? b : a); | ||
| 1410 | } | ||
| 1411 | |||
| 1412 | /// | ||
| 1413 | @safe unittest | ||
| 1414 | { | ||
| 1415 | int a = 5; | ||
| 1416 | short b = 6; | ||
| 1417 | double c = 2; | ||
| 1418 | auto d = max(a, b); | ||
| 1419 | assert(is(typeof(d) == int)); | ||
| 1420 | assert(d == 6); | ||
| 1421 | auto e = min(a, b, c); | ||
| 1422 | assert(is(typeof(e) == double)); | ||
| 1423 | assert(e == 2); | ||
| 1424 | } | ||
| 1425 | |||
| 1426 | @safe unittest | ||
| 1427 | { | ||
| 1428 | int a = 5; | ||
| 1429 | short b = 6; | ||
| 1430 | double c = 2; | ||
| 1431 | auto d = max(a, b); | ||
| 1432 | static assert(is(typeof(d) == int)); | ||
| 1433 | assert(d == 6); | ||
| 1434 | auto e = max(a, b, c); | ||
| 1435 | static assert(is(typeof(e) == double)); | ||
| 1436 | assert(e == 6); | ||
| 1437 | // mixed sign | ||
| 1438 | a = -5; | ||
| 1439 | uint f = 5; | ||
| 1440 | static assert(is(typeof(max(a, f)) == uint)); | ||
| 1441 | assert(max(a, f) == 5); | ||
| 1442 | |||
| 1443 | //Test user-defined types | ||
| 1444 | import std.datetime : Date; | ||
| 1445 | assert(max(Date(2012, 12, 21), Date(1982, 1, 4)) == Date(2012, 12, 21)); | ||
| 1446 | assert(max(Date(1982, 1, 4), Date(2012, 12, 21)) == Date(2012, 12, 21)); | ||
| 1447 | assert(max(Date(1982, 1, 4), Date.min) == Date(1982, 1, 4)); | ||
| 1448 | assert(max(Date.min, Date(1982, 1, 4)) == Date(1982, 1, 4)); | ||
| 1449 | assert(max(Date(1982, 1, 4), Date.max) == Date.max); | ||
| 1450 | assert(max(Date.max, Date(1982, 1, 4)) == Date.max); | ||
| 1451 | assert(max(Date.min, Date.max) == Date.max); | ||
| 1452 | assert(max(Date.max, Date.min) == Date.max); | ||
| 1453 | } | ||
| 1454 | |||
| 1455 | // MinType | ||
| 1456 | private template MinType(T...) | ||
| 1457 | if (T.length >= 1) | ||
| 1458 | { | ||
| 1459 | static if (T.length == 1) | ||
| 1460 | { | ||
| 1461 | alias MinType = T[0]; | ||
| 1462 | } | ||
| 1463 | else static if (T.length == 2) | ||
| 1464 | { | ||
| 1465 | static if (!is(typeof(T[0].min))) | ||
| 1466 | alias MinType = CommonType!T; | ||
| 1467 | else | ||
| 1468 | { | ||
| 1469 | enum hasMostNegative = is(typeof(mostNegative!(T[0]))) && | ||
| 1470 | is(typeof(mostNegative!(T[1]))); | ||
| 1471 | static if (hasMostNegative && mostNegative!(T[1]) < mostNegative!(T[0])) | ||
| 1472 | alias MinType = T[1]; | ||
| 1473 | else static if (hasMostNegative && mostNegative!(T[1]) > mostNegative!(T[0])) | ||
| 1474 | alias MinType = T[0]; | ||
| 1475 | else static if (T[1].max < T[0].max) | ||
| 1476 | alias MinType = T[1]; | ||
| 1477 | else | ||
| 1478 | alias MinType = T[0]; | ||
| 1479 | } | ||
| 1480 | } | ||
| 1481 | else | ||
| 1482 | { | ||
| 1483 | alias MinType = MinType!(MinType!(T[0 .. ($+1)/2]), MinType!(T[($+1)/2 .. $])); | ||
| 1484 | } | ||
| 1485 | } | ||
| 1486 | |||
| 1487 | // min | ||
| 1488 | /** | ||
| 1489 | Iterates the passed arguments and returns the minimum value. | ||
| 1490 | |||
| 1491 | Params: args = The values to select the minimum from. At least two arguments | ||
| 1492 | must be passed, and they must be comparable with `<`. | ||
| 1493 | Returns: The minimum of the passed-in values. | ||
| 1494 | See_Also: | ||
| 1495 | $(REF minElement, std,algorithm,searching) | ||
| 1496 | */ | ||
| 1497 | MinType!T min(T...)(T args) | ||
| 1498 | if (T.length >= 2) | ||
| 1499 | { | ||
| 1500 | //Get "a" | ||
| 1501 | static if (T.length <= 2) | ||
| 1502 | alias a = args[0]; | ||
| 1503 | else | ||
| 1504 | auto a = min(args[0 .. ($+1)/2]); | ||
| 1505 | alias T0 = typeof(a); | ||
| 1506 | |||
| 1507 | //Get "b" | ||
| 1508 | static if (T.length <= 3) | ||
| 1509 | alias b = args[$-1]; | ||
| 1510 | else | ||
| 1511 | auto b = min(args[($+1)/2 .. $]); | ||
| 1512 | alias T1 = typeof(b); | ||
| 1513 | |||
| 1514 | import std.algorithm.internal : algoFormat; | ||
| 1515 | static assert(is(typeof(a < b)), | ||
| 1516 | algoFormat("Invalid arguments: Cannot compare types %s and %s.", T0.stringof, T1.stringof)); | ||
| 1517 | |||
| 1518 | //Do the "min" proper with a and b | ||
| 1519 | import std.functional : lessThan; | ||
| 1520 | immutable chooseA = lessThan!(T0, T1)(a, b); | ||
| 1521 | return cast(typeof(return)) (chooseA ? a : b); | ||
| 1522 | } | ||
| 1523 | |||
| 1524 | /// | ||
| 1525 | @safe unittest | ||
| 1526 | { | ||
| 1527 | int a = 5; | ||
| 1528 | short b = 6; | ||
| 1529 | double c = 2; | ||
| 1530 | auto d = min(a, b); | ||
| 1531 | static assert(is(typeof(d) == int)); | ||
| 1532 | assert(d == 5); | ||
| 1533 | auto e = min(a, b, c); | ||
| 1534 | static assert(is(typeof(e) == double)); | ||
| 1535 | assert(e == 2); | ||
| 1536 | |||
| 1537 | // With arguments of mixed signedness, the return type is the one that can | ||
| 1538 | // store the lowest values. | ||
| 1539 | a = -10; | ||
| 1540 | uint f = 10; | ||
| 1541 | static assert(is(typeof(min(a, f)) == int)); | ||
| 1542 | assert(min(a, f) == -10); | ||
| 1543 | |||
| 1544 | // User-defined types that support comparison with < are supported. | ||
| 1545 | import std.datetime; | ||
| 1546 | assert(min(Date(2012, 12, 21), Date(1982, 1, 4)) == Date(1982, 1, 4)); | ||
| 1547 | assert(min(Date(1982, 1, 4), Date(2012, 12, 21)) == Date(1982, 1, 4)); | ||
| 1548 | assert(min(Date(1982, 1, 4), Date.min) == Date.min); | ||
| 1549 | assert(min(Date.min, Date(1982, 1, 4)) == Date.min); | ||
| 1550 | assert(min(Date(1982, 1, 4), Date.max) == Date(1982, 1, 4)); | ||
| 1551 | assert(min(Date.max, Date(1982, 1, 4)) == Date(1982, 1, 4)); | ||
| 1552 | assert(min(Date.min, Date.max) == Date.min); | ||
| 1553 | assert(min(Date.max, Date.min) == Date.min); | ||
| 1554 | } | ||
| 1555 | |||
| 1556 | // mismatch | ||
| 1557 | /** | ||
| 1558 | Sequentially compares elements in $(D r1) and $(D r2) in lockstep, and | ||
| 1559 | stops at the first mismatch (according to $(D pred), by default | ||
| 1560 | equality). Returns a tuple with the reduced ranges that start with the | ||
| 1561 | two mismatched values. Performs $(BIGOH min(r1.length, r2.length)) | ||
| 1562 | evaluations of $(D pred). | ||
| 1563 | |||
| 1564 | See_Also: | ||
| 1565 | $(HTTP sgi.com/tech/stl/_mismatch.html, STL's _mismatch) | ||
| 1566 | */ | ||
| 1567 | Tuple!(Range1, Range2) | ||
| 1568 | mismatch(alias pred = "a == b", Range1, Range2)(Range1 r1, Range2 r2) | ||
| 1569 | if (isInputRange!(Range1) && isInputRange!(Range2)) | ||
| 1570 | { | ||
| 1571 | for (; !r1.empty && !r2.empty; r1.popFront(), r2.popFront()) | ||
| 1572 | { | ||
| 1573 | if (!binaryFun!(pred)(r1.front, r2.front)) break; | ||
| 1574 | } | ||
| 1575 | return tuple(r1, r2); | ||
| 1576 | } | ||
| 1577 | |||
| 1578 | /// | ||
| 1579 | @safe unittest | ||
| 1580 | { | ||
| 1581 | int[] x = [ 1, 5, 2, 7, 4, 3 ]; | ||
| 1582 | double[] y = [ 1.0, 5, 2, 7.3, 4, 8 ]; | ||
| 1583 | auto m = mismatch(x, y); | ||
| 1584 | assert(m[0] == x[3 .. $]); | ||
| 1585 | assert(m[1] == y[3 .. $]); | ||
| 1586 | } | ||
| 1587 | |||
| 1588 | @safe unittest | ||
| 1589 | { | ||
| 1590 | int[] a = [ 1, 2, 3 ]; | ||
| 1591 | int[] b = [ 1, 2, 4, 5 ]; | ||
| 1592 | auto mm = mismatch(a, b); | ||
| 1593 | assert(mm[0] == [3]); | ||
| 1594 | assert(mm[1] == [4, 5]); | ||
| 1595 | } | ||
| 1596 | |||
| 1597 | /** | ||
| 1598 | Returns one of a collection of expressions based on the value of the switch | ||
| 1599 | expression. | ||
| 1600 | |||
| 1601 | $(D choices) needs to be composed of pairs of test expressions and return | ||
| 1602 | expressions. Each test-expression is compared with $(D switchExpression) using | ||
| 1603 | $(D pred)($(D switchExpression) is the first argument) and if that yields true | ||
| 1604 | - the return expression is returned. | ||
| 1605 | |||
| 1606 | Both the test and the return expressions are lazily evaluated. | ||
| 1607 | |||
| 1608 | Params: | ||
| 1609 | |||
| 1610 | switchExpression = The first argument for the predicate. | ||
| 1611 | |||
| 1612 | choices = Pairs of test expressions and return expressions. The test | ||
| 1613 | expressions will be the second argument for the predicate, and the return | ||
| 1614 | expression will be returned if the predicate yields true with $(D | ||
| 1615 | switchExpression) and the test expression as arguments. May also have a | ||
| 1616 | default return expression, that needs to be the last expression without a test | ||
| 1617 | expression before it. A return expression may be of void type only if it | ||
| 1618 | always throws. | ||
| 1619 | |||
| 1620 | Returns: The return expression associated with the first test expression that | ||
| 1621 | made the predicate yield true, or the default return expression if no test | ||
| 1622 | expression matched. | ||
| 1623 | |||
| 1624 | Throws: If there is no default return expression and the predicate does not | ||
| 1625 | yield true with any test expression - $(D SwitchError) is thrown. $(D | ||
| 1626 | SwitchError) is also thrown if a void return expression was executed without | ||
| 1627 | throwing anything. | ||
| 1628 | */ | ||
| 1629 | auto predSwitch(alias pred = "a == b", T, R ...)(T switchExpression, lazy R choices) | ||
| 1630 | { | ||
| 1631 | import core.exception : SwitchError; | ||
| 1632 | alias predicate = binaryFun!(pred); | ||
| 1633 | |||
| 1634 | foreach (index, ChoiceType; R) | ||
| 1635 | { | ||
| 1636 | //The even places in `choices` are for the predicate. | ||
| 1637 | static if (index % 2 == 1) | ||
| 1638 | { | ||
| 1639 | if (predicate(switchExpression, choices[index - 1]())) | ||
| 1640 | { | ||
| 1641 | static if (is(typeof(choices[index]()) == void)) | ||
| 1642 | { | ||
| 1643 | choices[index](); | ||
| 1644 | throw new SwitchError("Choices that return void should throw"); | ||
| 1645 | } | ||
| 1646 | else | ||
| 1647 | { | ||
| 1648 | return choices[index](); | ||
| 1649 | } | ||
| 1650 | } | ||
| 1651 | } | ||
| 1652 | } | ||
| 1653 | |||
| 1654 | //In case nothing matched: | ||
| 1655 | static if (R.length % 2 == 1) //If there is a default return expression: | ||
| 1656 | { | ||
| 1657 | static if (is(typeof(choices[$ - 1]()) == void)) | ||
| 1658 | { | ||
| 1659 | choices[$ - 1](); | ||
| 1660 | throw new SwitchError("Choices that return void should throw"); | ||
| 1661 | } | ||
| 1662 | else | ||
| 1663 | { | ||
| 1664 | return choices[$ - 1](); | ||
| 1665 | } | ||
| 1666 | } | ||
| 1667 | else //If there is no default return expression: | ||
| 1668 | { | ||
| 1669 | throw new SwitchError("Input not matched by any pattern"); | ||
| 1670 | } | ||
| 1671 | } | ||
| 1672 | |||
| 1673 | /// | ||
| 1674 | @safe unittest | ||
| 1675 | { | ||
| 1676 | string res = 2.predSwitch!"a < b"( | ||
| 1677 | 1, "less than 1", | ||
| 1678 | 5, "less than 5", | ||
| 1679 | 10, "less than 10", | ||
| 1680 | "greater or equal to 10"); | ||
| 1681 | |||
| 1682 | assert(res == "less than 5"); | ||
| 1683 | |||
| 1684 | //The arguments are lazy, which allows us to use predSwitch to create | ||
| 1685 | //recursive functions: | ||
| 1686 | int factorial(int n) | ||
| 1687 | { | ||
| 1688 | return n.predSwitch!"a <= b"( | ||
| 1689 | -1, {throw new Exception("Can not calculate n! for n < 0");}(), | ||
| 1690 | 0, 1, // 0! = 1 | ||
| 1691 | n * factorial(n - 1) // n! = n * (n - 1)! for n >= 0 | ||
| 1692 | ); | ||
| 1693 | } | ||
| 1694 | assert(factorial(3) == 6); | ||
| 1695 | |||
| 1696 | //Void return expressions are allowed if they always throw: | ||
| 1697 | import std.exception : assertThrown; | ||
| 1698 | assertThrown!Exception(factorial(-9)); | ||
| 1699 | } | ||
| 1700 | |||
| 1701 | @system unittest | ||
| 1702 | { | ||
| 1703 | import core.exception : SwitchError; | ||
| 1704 | import std.exception : assertThrown; | ||
| 1705 | |||
| 1706 | //Nothing matches - with default return expression: | ||
| 1707 | assert(20.predSwitch!"a < b"( | ||
| 1708 | 1, "less than 1", | ||
| 1709 | 5, "less than 5", | ||
| 1710 | 10, "less than 10", | ||
| 1711 | "greater or equal to 10") == "greater or equal to 10"); | ||
| 1712 | |||
| 1713 | //Nothing matches - without default return expression: | ||
| 1714 | assertThrown!SwitchError(20.predSwitch!"a < b"( | ||
| 1715 | 1, "less than 1", | ||
| 1716 | 5, "less than 5", | ||
| 1717 | 10, "less than 10", | ||
| 1718 | )); | ||
| 1719 | |||
| 1720 | //Using the default predicate: | ||
| 1721 | assert(2.predSwitch( | ||
| 1722 | 1, "one", | ||
| 1723 | 2, "two", | ||
| 1724 | 3, "three", | ||
| 1725 | ) == "two"); | ||
| 1726 | |||
| 1727 | //Void return expressions must always throw: | ||
| 1728 | assertThrown!SwitchError(1.predSwitch( | ||
| 1729 | 0, "zero", | ||
| 1730 | 1, {}(), //A void return expression that doesn't throw | ||
| 1731 | 2, "two", | ||
| 1732 | )); | ||
| 1733 | } | ||
| 1734 | |||
| 1735 | /** | ||
| 1736 | Checks if the two ranges have the same number of elements. This function is | ||
| 1737 | optimized to always take advantage of the $(D length) member of either range | ||
| 1738 | if it exists. | ||
| 1739 | |||
| 1740 | If both ranges have a length member, this function is $(BIGOH 1). Otherwise, | ||
| 1741 | this function is $(BIGOH min(r1.length, r2.length)). | ||
| 1742 | |||
| 1743 | Params: | ||
| 1744 | r1 = a finite $(REF_ALTTEXT input range, isInputRange, std,range,primitives) | ||
| 1745 | r2 = a finite $(REF_ALTTEXT input range, isInputRange, std,range,primitives) | ||
| 1746 | |||
| 1747 | Returns: | ||
| 1748 | $(D true) if both ranges have the same length, $(D false) otherwise. | ||
| 1749 | */ | ||
| 1750 | bool isSameLength(Range1, Range2)(Range1 r1, Range2 r2) | ||
| 1751 | if (isInputRange!Range1 && | ||
| 1752 | isInputRange!Range2 && | ||
| 1753 | !isInfinite!Range1 && | ||
| 1754 | !isInfinite!Range2) | ||
| 1755 | { | ||
| 1756 | static if (hasLength!(Range1) && hasLength!(Range2)) | ||
| 1757 | { | ||
| 1758 | return r1.length == r2.length; | ||
| 1759 | } | ||
| 1760 | else static if (hasLength!(Range1) && !hasLength!(Range2)) | ||
| 1761 | { | ||
| 1762 | size_t length; | ||
| 1763 | |||
| 1764 | while (!r2.empty) | ||
| 1765 | { | ||
| 1766 | r2.popFront; | ||
| 1767 | |||
| 1768 | if (++length > r1.length) | ||
| 1769 | { | ||
| 1770 | return false; | ||
| 1771 | } | ||
| 1772 | } | ||
| 1773 | |||
| 1774 | return !(length < r1.length); | ||
| 1775 | } | ||
| 1776 | else static if (!hasLength!(Range1) && hasLength!(Range2)) | ||
| 1777 | { | ||
| 1778 | size_t length; | ||
| 1779 | |||
| 1780 | while (!r1.empty) | ||
| 1781 | { | ||
| 1782 | r1.popFront; | ||
| 1783 | |||
| 1784 | if (++length > r2.length) | ||
| 1785 | { | ||
| 1786 | return false; | ||
| 1787 | } | ||
| 1788 | } | ||
| 1789 | |||
| 1790 | return !(length < r2.length); | ||
| 1791 | } | ||
| 1792 | else | ||
| 1793 | { | ||
| 1794 | while (!r1.empty) | ||
| 1795 | { | ||
| 1796 | if (r2.empty) | ||
| 1797 | { | ||
| 1798 | return false; | ||
| 1799 | } | ||
| 1800 | |||
| 1801 | r1.popFront; | ||
| 1802 | r2.popFront; | ||
| 1803 | } | ||
| 1804 | |||
| 1805 | return r2.empty; | ||
| 1806 | } | ||
| 1807 | } | ||
| 1808 | |||
| 1809 | /// | ||
| 1810 | @safe nothrow pure unittest | ||
| 1811 | { | ||
| 1812 | assert(isSameLength([1, 2, 3], [4, 5, 6])); | ||
| 1813 | assert(isSameLength([0.3, 90.4, 23.7, 119.2], [42.6, 23.6, 95.5, 6.3])); | ||
| 1814 | assert(isSameLength("abc", "xyz")); | ||
| 1815 | |||
| 1816 | int[] a; | ||
| 1817 | int[] b; | ||
| 1818 | assert(isSameLength(a, b)); | ||
| 1819 | |||
| 1820 | assert(!isSameLength([1, 2, 3], [4, 5])); | ||
| 1821 | assert(!isSameLength([0.3, 90.4, 23.7], [42.6, 23.6, 95.5, 6.3])); | ||
| 1822 | assert(!isSameLength("abcd", "xyz")); | ||
| 1823 | } | ||
| 1824 | |||
| 1825 | // Test CTFE | ||
| 1826 | @safe pure unittest | ||
| 1827 | { | ||
| 1828 | enum result1 = isSameLength([1, 2, 3], [4, 5, 6]); | ||
| 1829 | static assert(result1); | ||
| 1830 | |||
| 1831 | enum result2 = isSameLength([0.3, 90.4, 23.7], [42.6, 23.6, 95.5, 6.3]); | ||
| 1832 | static assert(!result2); | ||
| 1833 | } | ||
| 1834 | |||
| 1835 | @safe nothrow pure unittest | ||
| 1836 | { | ||
| 1837 | import std.internal.test.dummyrange; | ||
| 1838 | |||
| 1839 | auto r1 = new ReferenceInputRange!int([1, 2, 3]); | ||
| 1840 | auto r2 = new ReferenceInputRange!int([4, 5, 6]); | ||
| 1841 | assert(isSameLength(r1, r2)); | ||
| 1842 | |||
| 1843 | auto r3 = new ReferenceInputRange!int([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]); | ||
| 1844 | DummyRange!(ReturnBy.Reference, Length.Yes, RangeType.Input) r4; | ||
| 1845 | assert(isSameLength(r3, r4)); | ||
| 1846 | |||
| 1847 | DummyRange!(ReturnBy.Reference, Length.Yes, RangeType.Input) r5; | ||
| 1848 | auto r6 = new ReferenceInputRange!int([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]); | ||
| 1849 | assert(isSameLength(r5, r6)); | ||
| 1850 | |||
| 1851 | auto r7 = new ReferenceInputRange!int([1, 2]); | ||
| 1852 | auto r8 = new ReferenceInputRange!int([4, 5, 6]); | ||
| 1853 | assert(!isSameLength(r7, r8)); | ||
| 1854 | |||
| 1855 | auto r9 = new ReferenceInputRange!int([1, 2, 3, 4, 5, 6, 7, 8]); | ||
| 1856 | DummyRange!(ReturnBy.Reference, Length.Yes, RangeType.Input) r10; | ||
| 1857 | assert(!isSameLength(r9, r10)); | ||
| 1858 | |||
| 1859 | DummyRange!(ReturnBy.Reference, Length.Yes, RangeType.Input) r11; | ||
| 1860 | auto r12 = new ReferenceInputRange!int([1, 2, 3, 4, 5, 6, 7, 8]); | ||
| 1861 | assert(!isSameLength(r11, r12)); | ||
| 1862 | } | ||
| 1863 | |||
| 1864 | /// For convenience | ||
| 1865 | alias AllocateGC = Flag!"allocateGC"; | ||
| 1866 | |||
| 1867 | /** | ||
| 1868 | Checks if both ranges are permutations of each other. | ||
| 1869 | |||
| 1870 | This function can allocate if the $(D Yes.allocateGC) flag is passed. This has | ||
| 1871 | the benefit of have better complexity than the $(D Yes.allocateGC) option. However, | ||
| 1872 | this option is only available for ranges whose equality can be determined via each | ||
| 1873 | element's $(D toHash) method. If customized equality is needed, then the $(D pred) | ||
| 1874 | template parameter can be passed, and the function will automatically switch to | ||
| 1875 | the non-allocating algorithm. See $(REF binaryFun, std,functional) for more details on | ||
| 1876 | how to define $(D pred). | ||
| 1877 | |||
| 1878 | Non-allocating forward range option: $(BIGOH n^2) | ||
| 1879 | Non-allocating forward range option with custom $(D pred): $(BIGOH n^2) | ||
| 1880 | Allocating forward range option: amortized $(BIGOH r1.length) + $(BIGOH r2.length) | ||
| 1881 | |||
| 1882 | Params: | ||
| 1883 | pred = an optional parameter to change how equality is defined | ||
| 1884 | allocate_gc = $(D Yes.allocateGC)/$(D No.allocateGC) | ||
| 1885 | r1 = A finite $(REF_ALTTEXT forward range, isForwardRange, std,range,primitives) | ||
| 1886 | r2 = A finite $(REF_ALTTEXT forward range, isForwardRange, std,range,primitives) | ||
| 1887 | |||
| 1888 | Returns: | ||
| 1889 | $(D true) if all of the elements in $(D r1) appear the same number of times in $(D r2). | ||
| 1890 | Otherwise, returns $(D false). | ||
| 1891 | */ | ||
| 1892 | |||
| 1893 | bool isPermutation(AllocateGC allocate_gc, Range1, Range2) | ||
| 1894 | (Range1 r1, Range2 r2) | ||
| 1895 | if (allocate_gc == Yes.allocateGC && | ||
| 1896 | isForwardRange!Range1 && | ||
| 1897 | isForwardRange!Range2 && | ||
| 1898 | !isInfinite!Range1 && | ||
| 1899 | !isInfinite!Range2) | ||
| 1900 | { | ||
| 1901 | alias E1 = Unqual!(ElementType!Range1); | ||
| 1902 | alias E2 = Unqual!(ElementType!Range2); | ||
| 1903 | |||
| 1904 | if (!isSameLength(r1.save, r2.save)) | ||
| 1905 | { | ||
| 1906 | return false; | ||
| 1907 | } | ||
| 1908 | |||
| 1909 | // Skip the elements at the beginning where r1.front == r2.front, | ||
| 1910 | // they are in the same order and don't need to be counted. | ||
| 1911 | while (!r1.empty && !r2.empty && r1.front == r2.front) | ||
| 1912 | { | ||
| 1913 | r1.popFront(); | ||
| 1914 | r2.popFront(); | ||
| 1915 | } | ||
| 1916 | |||
| 1917 | if (r1.empty && r2.empty) | ||
| 1918 | { | ||
| 1919 | return true; | ||
| 1920 | } | ||
| 1921 | |||
| 1922 | int[CommonType!(E1, E2)] counts; | ||
| 1923 | |||
| 1924 | foreach (item; r1) | ||
| 1925 | { | ||
| 1926 | ++counts[item]; | ||
| 1927 | } | ||
| 1928 | |||
| 1929 | foreach (item; r2) | ||
| 1930 | { | ||
| 1931 | if (--counts[item] < 0) | ||
| 1932 | { | ||
| 1933 | return false; | ||
| 1934 | } | ||
| 1935 | } | ||
| 1936 | |||
| 1937 | return true; | ||
| 1938 | } | ||
| 1939 | |||
| 1940 | /// ditto | ||
| 1941 | bool isPermutation(alias pred = "a == b", Range1, Range2) | ||
| 1942 | (Range1 r1, Range2 r2) | ||
| 1943 | if (is(typeof(binaryFun!(pred))) && | ||
| 1944 | isForwardRange!Range1 && | ||
| 1945 | isForwardRange!Range2 && | ||
| 1946 | !isInfinite!Range1 && | ||
| 1947 | !isInfinite!Range2) | ||
| 1948 | { | ||
| 1949 | import std.algorithm.searching : count; | ||
| 1950 | |||
| 1951 | alias predEquals = binaryFun!(pred); | ||
| 1952 | alias E1 = Unqual!(ElementType!Range1); | ||
| 1953 | alias E2 = Unqual!(ElementType!Range2); | ||
| 1954 | |||
| 1955 | if (!isSameLength(r1.save, r2.save)) | ||
| 1956 | { | ||
| 1957 | return false; | ||
| 1958 | } | ||
| 1959 | |||
| 1960 | // Skip the elements at the beginning where r1.front == r2.front, | ||
| 1961 | // they are in the same order and don't need to be counted. | ||
| 1962 | while (!r1.empty && !r2.empty && predEquals(r1.front, r2.front)) | ||
| 1963 | { | ||
| 1964 | r1.popFront(); | ||
| 1965 | r2.popFront(); | ||
| 1966 | } | ||
| 1967 | |||
| 1968 | if (r1.empty && r2.empty) | ||
| 1969 | { | ||
| 1970 | return true; | ||
| 1971 | } | ||
| 1972 | |||
| 1973 | size_t r1_count; | ||
| 1974 | size_t r2_count; | ||
| 1975 | |||
| 1976 | // At each element item, when computing the count of item, scan it while | ||
| 1977 | // also keeping track of the scanning index. If the first occurrence | ||
| 1978 | // of item in the scanning loop has an index smaller than the current index, | ||
| 1979 | // then you know that the element has been seen before | ||
| 1980 | size_t index; | ||
| 1981 | outloop: for (auto r1s1 = r1.save; !r1s1.empty; r1s1.popFront, index++) | ||
| 1982 | { | ||
| 1983 | auto item = r1s1.front; | ||
| 1984 | r1_count = 0; | ||
| 1985 | r2_count = 0; | ||
| 1986 | |||
| 1987 | size_t i; | ||
| 1988 | for (auto r1s2 = r1.save; !r1s2.empty; r1s2.popFront, i++) | ||
| 1989 | { | ||
| 1990 | auto e = r1s2.front; | ||
| 1991 | if (predEquals(e, item) && i < index) | ||
| 1992 | { | ||
| 1993 | continue outloop; | ||
| 1994 | } | ||
| 1995 | else if (predEquals(e, item)) | ||
| 1996 | { | ||
| 1997 | ++r1_count; | ||
| 1998 | } | ||
| 1999 | } | ||
| 2000 | |||
| 2001 | r2_count = r2.save.count!pred(item); | ||
| 2002 | |||
| 2003 | if (r1_count != r2_count) | ||
| 2004 | { | ||
| 2005 | return false; | ||
| 2006 | } | ||
| 2007 | } | ||
| 2008 | |||
| 2009 | return true; | ||
| 2010 | } | ||
| 2011 | |||
| 2012 | /// | ||
| 2013 | @safe pure unittest | ||
| 2014 | { | ||
| 2015 | import std.typecons : Yes; | ||
| 2016 | |||
| 2017 | assert(isPermutation([1, 2, 3], [3, 2, 1])); | ||
| 2018 | assert(isPermutation([1.1, 2.3, 3.5], [2.3, 3.5, 1.1])); | ||
| 2019 | assert(isPermutation("abc", "bca")); | ||
| 2020 | |||
| 2021 | assert(!isPermutation([1, 2], [3, 4])); | ||
| 2022 | assert(!isPermutation([1, 1, 2, 3], [1, 2, 2, 3])); | ||
| 2023 | assert(!isPermutation([1, 1], [1, 1, 1])); | ||
| 2024 | |||
| 2025 | // Faster, but allocates GC handled memory | ||
| 2026 | assert(isPermutation!(Yes.allocateGC)([1.1, 2.3, 3.5], [2.3, 3.5, 1.1])); | ||
| 2027 | assert(!isPermutation!(Yes.allocateGC)([1, 2], [3, 4])); | ||
| 2028 | } | ||
| 2029 | |||
| 2030 | // Test @nogc inference | ||
| 2031 | @safe @nogc pure unittest | ||
| 2032 | { | ||
| 2033 | static immutable arr1 = [1, 2, 3]; | ||
| 2034 | static immutable arr2 = [3, 2, 1]; | ||
| 2035 | assert(isPermutation(arr1, arr2)); | ||
| 2036 | |||
| 2037 | static immutable arr3 = [1, 1, 2, 3]; | ||
| 2038 | static immutable arr4 = [1, 2, 2, 3]; | ||
| 2039 | assert(!isPermutation(arr3, arr4)); | ||
| 2040 | } | ||
| 2041 | |||
| 2042 | @safe pure unittest | ||
| 2043 | { | ||
| 2044 | import std.internal.test.dummyrange; | ||
| 2045 | |||
| 2046 | auto r1 = new ReferenceForwardRange!int([1, 2, 3, 4]); | ||
| 2047 | auto r2 = new ReferenceForwardRange!int([1, 2, 4, 3]); | ||
| 2048 | assert(isPermutation(r1, r2)); | ||
| 2049 | |||
| 2050 | auto r3 = new ReferenceForwardRange!int([1, 2, 3, 4]); | ||
| 2051 | auto r4 = new ReferenceForwardRange!int([4, 2, 1, 3]); | ||
| 2052 | assert(isPermutation!(Yes.allocateGC)(r3, r4)); | ||
| 2053 | |||
| 2054 | auto r5 = new ReferenceForwardRange!int([1, 2, 3]); | ||
| 2055 | auto r6 = new ReferenceForwardRange!int([4, 2, 1, 3]); | ||
| 2056 | assert(!isPermutation(r5, r6)); | ||
| 2057 | |||
| 2058 | auto r7 = new ReferenceForwardRange!int([4, 2, 1, 3]); | ||
| 2059 | auto r8 = new ReferenceForwardRange!int([1, 2, 3]); | ||
| 2060 | assert(!isPermutation!(Yes.allocateGC)(r7, r8)); | ||
| 2061 | |||
| 2062 | DummyRange!(ReturnBy.Reference, Length.Yes, RangeType.Random) r9; | ||
| 2063 | DummyRange!(ReturnBy.Reference, Length.Yes, RangeType.Random) r10; | ||
| 2064 | assert(isPermutation(r9, r10)); | ||
| 2065 | |||
| 2066 | DummyRange!(ReturnBy.Reference, Length.Yes, RangeType.Random) r11; | ||
| 2067 | DummyRange!(ReturnBy.Reference, Length.Yes, RangeType.Random) r12; | ||
| 2068 | assert(isPermutation!(Yes.allocateGC)(r11, r12)); | ||
| 2069 | |||
| 2070 | alias mytuple = Tuple!(int, int); | ||
| 2071 | |||
| 2072 | assert(isPermutation!"a[0] == b[0]"( | ||
| 2073 | [mytuple(1, 4), mytuple(2, 5)], | ||
| 2074 | [mytuple(2, 3), mytuple(1, 2)] | ||
| 2075 | )); | ||
| 2076 | } | ||
| 2077 | |||
| 2078 | /** | ||
| 2079 | Get the _first argument `a` that passes an `if (unaryFun!pred(a))` test. If | ||
| 2080 | no argument passes the test, return the last argument. | ||
| 2081 | |||
| 2082 | Similar to behaviour of the `or` operator in dynamic languages such as Lisp's | ||
| 2083 | `(or ...)` and Python's `a or b or ...` except that the last argument is | ||
| 2084 | returned upon no match. | ||
| 2085 | |||
| 2086 | Simplifies logic, for instance, in parsing rules where a set of alternative | ||
| 2087 | matchers are tried. The _first one that matches returns it match result, | ||
| 2088 | typically as an abstract syntax tree (AST). | ||
| 2089 | |||
| 2090 | Bugs: | ||
| 2091 | Lazy parameters are currently, too restrictively, inferred by DMD to | ||
| 2092 | always throw even though they don't need to be. This makes it impossible to | ||
| 2093 | currently mark `either` as `nothrow`. See issue at $(BUGZILLA 12647). | ||
| 2094 | |||
| 2095 | Returns: | ||
| 2096 | The _first argument that passes the test `pred`. | ||
| 2097 | */ | ||
| 2098 | CommonType!(T, Ts) either(alias pred = a => a, T, Ts...)(T first, lazy Ts alternatives) | ||
| 2099 | if (alternatives.length >= 1 && | ||
| 2100 | !is(CommonType!(T, Ts) == void) && | ||
| 2101 | allSatisfy!(ifTestable, T, Ts)) | ||
| 2102 | { | ||
| 2103 | alias predFun = unaryFun!pred; | ||
| 2104 | |||
| 2105 | if (predFun(first)) return first; | ||
| 2106 | |||
| 2107 | foreach (e; alternatives[0 .. $ - 1]) | ||
| 2108 | if (predFun(e)) return e; | ||
| 2109 | |||
| 2110 | return alternatives[$ - 1]; | ||
| 2111 | } | ||
| 2112 | |||
| 2113 | /// | ||
| 2114 | @safe pure unittest | ||
| 2115 | { | ||
| 2116 | const a = 1; | ||
| 2117 | const b = 2; | ||
| 2118 | auto ab = either(a, b); | ||
| 2119 | static assert(is(typeof(ab) == const(int))); | ||
| 2120 | assert(ab == a); | ||
| 2121 | |||
| 2122 | auto c = 2; | ||
| 2123 | const d = 3; | ||
| 2124 | auto cd = either!(a => a == 3)(c, d); // use predicate | ||
| 2125 | static assert(is(typeof(cd) == int)); | ||
| 2126 | assert(cd == d); | ||
| 2127 | |||
| 2128 | auto e = 0; | ||
| 2129 | const f = 2; | ||
| 2130 | auto ef = either(e, f); | ||
| 2131 | static assert(is(typeof(ef) == int)); | ||
| 2132 | assert(ef == f); | ||
| 2133 | |||
| 2134 | immutable p = 1; | ||
| 2135 | immutable q = 2; | ||
| 2136 | auto pq = either(p, q); | ||
| 2137 | static assert(is(typeof(pq) == immutable(int))); | ||
| 2138 | assert(pq == p); | ||
| 2139 | |||
| 2140 | assert(either(3, 4) == 3); | ||
| 2141 | assert(either(0, 4) == 4); | ||
| 2142 | assert(either(0, 0) == 0); | ||
| 2143 | assert(either("", "a") == ""); | ||
| 2144 | |||
| 2145 | string r = null; | ||
| 2146 | assert(either(r, "a") == "a"); | ||
| 2147 | assert(either("a", "") == "a"); | ||
| 2148 | |||
| 2149 | immutable s = [1, 2]; | ||
| 2150 | assert(either(s, s) == s); | ||
| 2151 | |||
| 2152 | assert(either([0, 1], [1, 2]) == [0, 1]); | ||
| 2153 | assert(either([0, 1], [1]) == [0, 1]); | ||
| 2154 | assert(either("a", "b") == "a"); | ||
| 2155 | |||
| 2156 | static assert(!__traits(compiles, either(1, "a"))); | ||
| 2157 | static assert(!__traits(compiles, either(1.0, "a"))); | ||
| 2158 | static assert(!__traits(compiles, either('a', "a"))); | ||
| 2159 | } | ||
| @@ -0,0 +1,77 @@ | |||
| 1 | // Written in the D programming language. | ||
| 2 | |||
| 3 | /// Helper functions for std.algorithm package. | ||
| 4 | module std.algorithm.internal; | ||
| 5 | |||
| 6 | |||
| 7 | // Same as std.string.format, but "self-importing". | ||
| 8 | // Helps reduce code and imports, particularly in static asserts. | ||
| 9 | // Also helps with missing imports errors. | ||
| 10 | package template algoFormat() | ||
| 11 | { | ||
| 12 | import std.format : format; | ||
| 13 | alias algoFormat = format; | ||
| 14 | } | ||
| 15 | |||
| 16 | // Internal random array generators | ||
| 17 | version (unittest) | ||
| 18 | { | ||
| 19 | package enum size_t maxArraySize = 50; | ||
| 20 | package enum size_t minArraySize = maxArraySize - 1; | ||
| 21 | |||
| 22 | package string[] rndstuff(T : string)() | ||
| 23 | { | ||
| 24 | import std.random : Random, unpredictableSeed, uniform; | ||
| 25 | |||
| 26 | static Random rnd; | ||
| 27 | static bool first = true; | ||
| 28 | if (first) | ||
| 29 | { | ||
| 30 | rnd = Random(unpredictableSeed); | ||
| 31 | first = false; | ||
| 32 | } | ||
| 33 | string[] result = | ||
| 34 | new string[uniform(minArraySize, maxArraySize, rnd)]; | ||
| 35 | string alpha = "abcdefghijABCDEFGHIJ"; | ||
| 36 | foreach (ref s; result) | ||
| 37 | { | ||
| 38 | foreach (i; 0 .. uniform(0u, 20u, rnd)) | ||
| 39 | { | ||
| 40 | auto j = uniform(0, alpha.length - 1, rnd); | ||
| 41 | s ~= alpha[j]; | ||
| 42 | } | ||
| 43 | } | ||
| 44 | return result; | ||
| 45 | } | ||
| 46 | |||
| 47 | package int[] rndstuff(T : int)() | ||
| 48 | { | ||
| 49 | import std.random : Random, unpredictableSeed, uniform; | ||
| 50 | |||
| 51 | static Random rnd; | ||
| 52 | static bool first = true; | ||
| 53 | if (first) | ||
| 54 | { | ||
| 55 | rnd = Random(unpredictableSeed); | ||
| 56 | first = false; | ||
| 57 | } | ||
| 58 | int[] result = new int[uniform(minArraySize, maxArraySize, rnd)]; | ||
| 59 | foreach (ref i; result) | ||
| 60 | { | ||
| 61 | i = uniform(-100, 100, rnd); | ||
| 62 | } | ||
| 63 | return result; | ||
| 64 | } | ||
| 65 | |||
| 66 | package double[] rndstuff(T : double)() | ||
| 67 | { | ||
| 68 | double[] result; | ||
| 69 | foreach (i; rndstuff!(int)()) | ||
| 70 | { | ||
| 71 | result ~= i / 50.0; | ||
| 72 | } | ||
| 73 | return result; | ||
| 74 | } | ||
| 75 | } | ||
| 76 | |||
| 77 | package(std) T* addressOf(T)(ref T val) { return &val; } | ||
| @@ -0,0 +1,5187 @@ | |||
| 1 | // Written in the D programming language. | ||
| 2 | /** | ||
| 3 | This is a submodule of $(MREF std, algorithm). | ||
| 4 | It contains generic _iteration algorithms. | ||
| 5 | |||
| 6 | $(SCRIPT inhibitQuickIndex = 1;) | ||
| 7 | $(BOOKTABLE Cheat Sheet, | ||
| 8 | $(TR $(TH Function Name) $(TH Description)) | ||
| 9 | $(T2 cache, | ||
| 10 | Eagerly evaluates and caches another range's $(D front).) | ||
| 11 | $(T2 cacheBidirectional, | ||
| 12 | As above, but also provides $(D back) and $(D popBack).) | ||
| 13 | $(T2 chunkBy, | ||
| 14 | $(D chunkBy!((a,b) => a[1] == b[1])([[1, 1], [1, 2], [2, 2], [2, 1]])) | ||
| 15 | returns a range containing 3 subranges: the first with just | ||
| 16 | $(D [1, 1]); the second with the elements $(D [1, 2]) and $(D [2, 2]); | ||
| 17 | and the third with just $(D [2, 1]).) | ||
| 18 | $(T2 cumulativeFold, | ||
| 19 | $(D cumulativeFold!((a, b) => a + b)([1, 2, 3, 4])) returns a | ||
| 20 | lazily-evaluated range containing the successive reduced values `1`, | ||
| 21 | `3`, `6`, `10`.) | ||
| 22 | $(T2 each, | ||
| 23 | $(D each!writeln([1, 2, 3])) eagerly prints the numbers $(D 1), $(D 2) | ||
| 24 | and $(D 3) on their own lines.) | ||
| 25 | $(T2 filter, | ||
| 26 | $(D filter!(a => a > 0)([1, -1, 2, 0, -3])) iterates over elements $(D 1) | ||
| 27 | and $(D 2).) | ||
| 28 | $(T2 filterBidirectional, | ||
| 29 | Similar to $(D filter), but also provides $(D back) and $(D popBack) at | ||
| 30 | a small increase in cost.) | ||
| 31 | $(T2 fold, | ||
| 32 | $(D fold!((a, b) => a + b)([1, 2, 3, 4])) returns $(D 10).) | ||
| 33 | $(T2 group, | ||
| 34 | $(D group([5, 2, 2, 3, 3])) returns a range containing the tuples | ||
| 35 | $(D tuple(5, 1)), $(D tuple(2, 2)), and $(D tuple(3, 2)).) | ||
| 36 | $(T2 joiner, | ||
| 37 | $(D joiner(["hello", "world!"], "; ")) returns a range that iterates | ||
| 38 | over the characters $(D "hello; world!"). No new string is created - | ||
| 39 | the existing inputs are iterated.) | ||
| 40 | $(T2 map, | ||
| 41 | $(D map!(a => a * 2)([1, 2, 3])) lazily returns a range with the numbers | ||
| 42 | $(D 2), $(D 4), $(D 6).) | ||
| 43 | $(T2 permutations, | ||
| 44 | Lazily computes all permutations using Heap's algorithm.) | ||
| 45 | $(T2 reduce, | ||
| 46 | $(D reduce!((a, b) => a + b)([1, 2, 3, 4])) returns $(D 10). | ||
| 47 | This is the old implementation of `fold`.) | ||
| 48 | $(T2 splitter, | ||
| 49 | Lazily splits a range by a separator.) | ||
| 50 | $(T2 sum, | ||
| 51 | Same as $(D fold), but specialized for accurate summation.) | ||
| 52 | $(T2 uniq, | ||
| 53 | Iterates over the unique elements in a range, which is assumed sorted.) | ||
| 54 | ) | ||
| 55 | |||
| 56 | Copyright: Andrei Alexandrescu 2008-. | ||
| 57 | |||
| 58 | License: $(HTTP boost.org/LICENSE_1_0.txt, Boost License 1.0). | ||
| 59 | |||
| 60 | Authors: $(HTTP erdani.com, Andrei Alexandrescu) | ||
| 61 | |||
| 62 | Source: $(PHOBOSSRC std/algorithm/_iteration.d) | ||
| 63 | |||
| 64 | Macros: | ||
| 65 | T2=$(TR $(TDNW $(LREF $1)) $(TD $+)) | ||
| 66 | */ | ||
| 67 | module std.algorithm.iteration; | ||
| 68 | |||
| 69 | // FIXME | ||
| 70 | import std.functional; // : unaryFun, binaryFun; | ||
| 71 | import std.range.primitives; | ||
| 72 | import std.traits; | ||
| 73 | |||
| 74 | private template aggregate(fun...) | ||
| 75 | if (fun.length >= 1) | ||
| 76 | { | ||
| 77 | /* --Intentionally not ddoc-- | ||
| 78 | * Aggregates elements in each subrange of the given range of ranges using | ||
| 79 | * the given aggregating function(s). | ||
| 80 | * Params: | ||
| 81 | * fun = One or more aggregating functions (binary functions that return a | ||
| 82 | * single _aggregate value of their arguments). | ||
| 83 | * ror = A range of ranges to be aggregated. | ||
| 84 | * | ||
| 85 | * Returns: | ||
| 86 | * A range representing the aggregated value(s) of each subrange | ||
| 87 | * of the original range. If only one aggregating function is specified, | ||
| 88 | * each element will be the aggregated value itself; if multiple functions | ||
| 89 | * are specified, each element will be a tuple of the aggregated values of | ||
| 90 | * each respective function. | ||
| 91 | */ | ||
| 92 | auto aggregate(RoR)(RoR ror) | ||
| 93 | if (isInputRange!RoR && isIterable!(ElementType!RoR)) | ||
| 94 | { | ||
| 95 | return ror.map!(reduce!fun); | ||
| 96 | } | ||
| 97 | |||
| 98 | @safe unittest | ||
| 99 | { | ||
| 100 | import std.algorithm.comparison : equal, max, min; | ||
| 101 | |||
| 102 | auto data = [[4, 2, 1, 3], [4, 9, -1, 3, 2], [3]]; | ||
| 103 | |||
| 104 | // Single aggregating function | ||
| 105 | auto agg1 = data.aggregate!max; | ||
| 106 | assert(agg1.equal([4, 9, 3])); | ||
| 107 | |||
| 108 | // Multiple aggregating functions | ||
| 109 | import std.typecons : tuple; | ||
| 110 | auto agg2 = data.aggregate!(max, min); | ||
| 111 | assert(agg2.equal([ | ||
| 112 | tuple(4, 1), | ||
| 113 | tuple(9, -1), | ||
| 114 | tuple(3, 3) | ||
| 115 | ])); | ||
| 116 | } | ||
| 117 | } | ||
| 118 | |||
| 119 | /++ | ||
| 120 | $(D cache) eagerly evaluates $(D front) of $(D range) | ||
| 121 | on each construction or call to $(D popFront), | ||
| 122 | to store the result in a cache. | ||
| 123 | The result is then directly returned when $(D front) is called, | ||
| 124 | rather than re-evaluated. | ||
| 125 | |||
| 126 | This can be a useful function to place in a chain, after functions | ||
| 127 | that have expensive evaluation, as a lazy alternative to $(REF array, std,array). | ||
| 128 | In particular, it can be placed after a call to $(D map), or before a call | ||
| 129 | to $(D filter). | ||
| 130 | |||
| 131 | $(D cache) may provide | ||
| 132 | $(REF_ALTTEXT bidirectional range, isBidirectionalRange, std,range,primitives) | ||
| 133 | iteration if needed, but since this comes at an increased cost, it must be explicitly requested via the | ||
| 134 | call to $(D cacheBidirectional). Furthermore, a bidirectional cache will | ||
| 135 | evaluate the "center" element twice, when there is only one element left in | ||
| 136 | the range. | ||
| 137 | |||
| 138 | $(D cache) does not provide random access primitives, | ||
| 139 | as $(D cache) would be unable to cache the random accesses. | ||
| 140 | If $(D Range) provides slicing primitives, | ||
| 141 | then $(D cache) will provide the same slicing primitives, | ||
| 142 | but $(D hasSlicing!Cache) will not yield true (as the $(REF hasSlicing, std,_range,primitives) | ||
| 143 | trait also checks for random access). | ||
| 144 | |||
| 145 | Params: | ||
| 146 | range = an $(REF_ALTTEXT input range, isInputRange, std,range,primitives) | ||
| 147 | |||
| 148 | Returns: | ||
| 149 | an input range with the cached values of range | ||
| 150 | +/ | ||
| 151 | auto cache(Range)(Range range) | ||
| 152 | if (isInputRange!Range) | ||
| 153 | { | ||
| 154 | return _Cache!(Range, false)(range); | ||
| 155 | } | ||
| 156 | |||
| 157 | /// ditto | ||
| 158 | auto cacheBidirectional(Range)(Range range) | ||
| 159 | if (isBidirectionalRange!Range) | ||
| 160 | { | ||
| 161 | return _Cache!(Range, true)(range); | ||
| 162 | } | ||
| 163 | |||
| 164 | /// | ||
| 165 | @safe unittest | ||
| 166 | { | ||
| 167 | import std.algorithm.comparison : equal; | ||
| 168 | import std.range, std.stdio; | ||
| 169 | import std.typecons : tuple; | ||
| 170 | |||
| 171 | ulong counter = 0; | ||
| 172 | double fun(int x) | ||
| 173 | { | ||
| 174 | ++counter; | ||
| 175 | // http://en.wikipedia.org/wiki/Quartic_function | ||
| 176 | return ( (x + 4.0) * (x + 1.0) * (x - 1.0) * (x - 3.0) ) / 14.0 + 0.5; | ||
| 177 | } | ||
| 178 | // Without cache, with array (greedy) | ||
| 179 | auto result1 = iota(-4, 5).map!(a =>tuple(a, fun(a)))() | ||
| 180 | .filter!(a => a[1] < 0)() | ||
| 181 | .map!(a => a[0])() | ||
| 182 | .array(); | ||
| 183 | |||
| 184 | // the values of x that have a negative y are: | ||
| 185 | assert(equal(result1, [-3, -2, 2])); | ||
| 186 | |||
| 187 | // Check how many times fun was evaluated. | ||
| 188 | // As many times as the number of items in both source and result. | ||
| 189 | assert(counter == iota(-4, 5).length + result1.length); | ||
| 190 | |||
| 191 | counter = 0; | ||
| 192 | // Without array, with cache (lazy) | ||
| 193 | auto result2 = iota(-4, 5).map!(a =>tuple(a, fun(a)))() | ||
| 194 | .cache() | ||
| 195 | .filter!(a => a[1] < 0)() | ||
| 196 | .map!(a => a[0])(); | ||
| 197 | |||
| 198 | // the values of x that have a negative y are: | ||
| 199 | assert(equal(result2, [-3, -2, 2])); | ||
| 200 | |||
| 201 | // Check how many times fun was evaluated. | ||
| 202 | // Only as many times as the number of items in source. | ||
| 203 | assert(counter == iota(-4, 5).length); | ||
| 204 | } | ||
| 205 | |||
| 206 | /++ | ||
| 207 | Tip: $(D cache) is eager when evaluating elements. If calling front on the | ||
| 208 | underlying _range has a side effect, it will be observable before calling | ||
| 209 | front on the actual cached _range. | ||
| 210 | |||
| 211 | Furthermore, care should be taken composing $(D cache) with $(REF take, std,_range). | ||
| 212 | By placing $(D take) before $(D cache), then $(D cache) will be "aware" | ||
| 213 | of when the _range ends, and correctly stop caching elements when needed. | ||
| 214 | If calling front has no side effect though, placing $(D take) after $(D cache) | ||
| 215 | may yield a faster _range. | ||
| 216 | |||
| 217 | Either way, the resulting ranges will be equivalent, but maybe not at the | ||
| 218 | same cost or side effects. | ||
| 219 | +/ | ||
| 220 | @safe unittest | ||
| 221 | { | ||
| 222 | import std.algorithm.comparison : equal; | ||
| 223 | import std.range; | ||
| 224 | int i = 0; | ||
| 225 | |||
| 226 | auto r = iota(0, 4).tee!((a){i = a;}, No.pipeOnPop); | ||
| 227 | auto r1 = r.take(3).cache(); | ||
| 228 | auto r2 = r.cache().take(3); | ||
| 229 | |||
| 230 | assert(equal(r1, [0, 1, 2])); | ||
| 231 | assert(i == 2); //The last "seen" element was 2. The data in cache has been cleared. | ||
| 232 | |||
| 233 | assert(equal(r2, [0, 1, 2])); | ||
| 234 | assert(i == 3); //cache has accessed 3. It is still stored internally by cache. | ||
| 235 | } | ||
| 236 | |||
| 237 | @safe unittest | ||
| 238 | { | ||
| 239 | import std.algorithm.comparison : equal; | ||
| 240 | import std.range; | ||
| 241 | auto a = [1, 2, 3, 4]; | ||
| 242 | assert(equal(a.map!(a => (a - 1) * a)().cache(), [ 0, 2, 6, 12])); | ||
| 243 | assert(equal(a.map!(a => (a - 1) * a)().cacheBidirectional().retro(), [12, 6, 2, 0])); | ||
| 244 | auto r1 = [1, 2, 3, 4].cache() [1 .. $]; | ||
| 245 | auto r2 = [1, 2, 3, 4].cacheBidirectional()[1 .. $]; | ||
| 246 | assert(equal(r1, [2, 3, 4])); | ||
| 247 | assert(equal(r2, [2, 3, 4])); | ||
| 248 | } | ||
| 249 | |||
| 250 | @safe unittest | ||
| 251 | { | ||
| 252 | import std.algorithm.comparison : equal; | ||
| 253 | |||
| 254 | //immutable test | ||
| 255 | static struct S | ||
| 256 | { | ||
| 257 | int i; | ||
| 258 | this(int i) | ||
| 259 | { | ||
| 260 | //this.i = i; | ||
| 261 | } | ||
| 262 | } | ||
| 263 | immutable(S)[] s = [S(1), S(2), S(3)]; | ||
| 264 | assert(equal(s.cache(), s)); | ||
| 265 | assert(equal(s.cacheBidirectional(), s)); | ||
| 266 | } | ||
| 267 | |||
| 268 | @safe pure nothrow unittest | ||
| 269 | { | ||
| 270 | import std.algorithm.comparison : equal; | ||
| 271 | |||
| 272 | //safety etc | ||
| 273 | auto a = [1, 2, 3, 4]; | ||
| 274 | assert(equal(a.cache(), a)); | ||
| 275 | assert(equal(a.cacheBidirectional(), a)); | ||
| 276 | } | ||
| 277 | |||
| 278 | @safe unittest | ||
| 279 | { | ||
| 280 | char[][] stringbufs = ["hello".dup, "world".dup]; | ||
| 281 | auto strings = stringbufs.map!((a)=>a.idup)().cache(); | ||
| 282 | assert(strings.front is strings.front); | ||
| 283 | } | ||
| 284 | |||
| 285 | @safe unittest | ||
| 286 | { | ||
| 287 | import std.range : cycle; | ||
| 288 | import std.algorithm.comparison : equal; | ||
| 289 | |||
| 290 | auto c = [1, 2, 3].cycle().cache(); | ||
| 291 | c = c[1 .. $]; | ||
| 292 | auto d = c[0 .. 1]; | ||
| 293 | assert(d.equal([2])); | ||
| 294 | } | ||
| 295 | |||
| 296 | @safe unittest | ||
| 297 | { | ||
| 298 | static struct Range | ||
| 299 | { | ||
| 300 | bool initialized = false; | ||
| 301 | bool front() @property {return initialized = true;} | ||
| 302 | void popFront() {initialized = false;} | ||
| 303 | enum empty = false; | ||
| 304 | } | ||
| 305 | auto r = Range().cache(); | ||
| 306 | assert(r.source.initialized == true); | ||
| 307 | } | ||
| 308 | |||
| 309 | private struct _Cache(R, bool bidir) | ||
| 310 | { | ||
| 311 | import core.exception : RangeError; | ||
| 312 | |||
| 313 | private | ||
| 314 | { | ||
| 315 | import std.algorithm.internal : algoFormat; | ||
| 316 | import std.meta : AliasSeq; | ||
| 317 | |||
| 318 | alias E = ElementType!R; | ||
| 319 | alias UE = Unqual!E; | ||
| 320 | |||
| 321 | R source; | ||
| 322 | |||
| 323 | static if (bidir) alias CacheTypes = AliasSeq!(UE, UE); | ||
| 324 | else alias CacheTypes = AliasSeq!UE; | ||
| 325 | CacheTypes caches; | ||
| 326 | |||
| 327 | static assert(isAssignable!(UE, E) && is(UE : E), | ||
| 328 | algoFormat( | ||
| 329 | "Cannot instantiate range with %s because %s elements are not assignable to %s.", | ||
| 330 | R.stringof, | ||
| 331 | E.stringof, | ||
| 332 | UE.stringof | ||
| 333 | ) | ||
| 334 | ); | ||
| 335 | } | ||
| 336 | |||
| 337 | this(R range) | ||
| 338 | { | ||
| 339 | source = range; | ||
| 340 | if (!range.empty) | ||
| 341 | { | ||
| 342 | caches[0] = source.front; | ||
| 343 | static if (bidir) | ||
| 344 | caches[1] = source.back; | ||
| 345 | } | ||
| 346 | } | ||
| 347 | |||
| 348 | static if (isInfinite!R) | ||
| 349 | enum empty = false; | ||
| 350 | else | ||
| 351 | bool empty() @property | ||
| 352 | { | ||
| 353 | return source.empty; | ||
| 354 | } | ||
| 355 | |||
| 356 | static if (hasLength!R) auto length() @property | ||
| 357 | { | ||
| 358 | return source.length; | ||
| 359 | } | ||
| 360 | |||
| 361 | E front() @property | ||
| 362 | { | ||
| 363 | version (assert) if (empty) throw new RangeError(); | ||
| 364 | return caches[0]; | ||
| 365 | } | ||
| 366 | static if (bidir) E back() @property | ||
| 367 | { | ||
| 368 | version (assert) if (empty) throw new RangeError(); | ||
| 369 | return caches[1]; | ||
| 370 | } | ||
| 371 | |||
| 372 | void popFront() | ||
| 373 | { | ||
| 374 | version (assert) if (empty) throw new RangeError(); | ||
| 375 | source.popFront(); | ||
| 376 | if (!source.empty) | ||
| 377 | caches[0] = source.front; | ||
| 378 | else | ||
| 379 | caches = CacheTypes.init; | ||
| 380 | } | ||
| 381 | static if (bidir) void popBack() | ||
| 382 | { | ||
| 383 | version (assert) if (empty) throw new RangeError(); | ||
| 384 | source.popBack(); | ||
| 385 | if (!source.empty) | ||
| 386 | caches[1] = source.back; | ||
| 387 | else | ||
| 388 | caches = CacheTypes.init; | ||
| 389 | } | ||
| 390 | |||
| 391 | static if (isForwardRange!R) | ||
| 392 | { | ||
| 393 | private this(R source, ref CacheTypes caches) | ||
| 394 | { | ||
| 395 | this.source = source; | ||
| 396 | this.caches = caches; | ||
| 397 | } | ||
| 398 | typeof(this) save() @property | ||
| 399 | { | ||
| 400 | return typeof(this)(source.save, caches); | ||
| 401 | } | ||
| 402 | } | ||
| 403 | |||
| 404 | static if (hasSlicing!R) | ||
| 405 | { | ||
| 406 | enum hasEndSlicing = is(typeof(source[size_t.max .. $])); | ||
| 407 | |||
| 408 | static if (hasEndSlicing) | ||
| 409 | { | ||
| 410 | private static struct DollarToken{} | ||
| 411 | enum opDollar = DollarToken.init; | ||
| 412 | |||
| 413 | auto opSlice(size_t low, DollarToken) | ||
| 414 | { | ||
| 415 | return typeof(this)(source[low .. $]); | ||
| 416 | } | ||
| 417 | } | ||
| 418 | |||
| 419 | static if (!isInfinite!R) | ||
| 420 | { | ||
| 421 | typeof(this) opSlice(size_t low, size_t high) | ||
| 422 | { | ||
| 423 | return typeof(this)(source[low .. high]); | ||
| 424 | } | ||
| 425 | } | ||
| 426 | else static if (hasEndSlicing) | ||
| 427 | { | ||
| 428 | auto opSlice(size_t low, size_t high) | ||
| 429 | in | ||
| 430 | { | ||
| 431 | assert(low <= high, "Bounds error when slicing cache."); | ||
| 432 | } | ||
| 433 | body | ||
| 434 | { | ||
| 435 | import std.range : takeExactly; | ||
| 436 | return this[low .. $].takeExactly(high - low); | ||
| 437 | } | ||
| 438 | } | ||
| 439 | } | ||
| 440 | } | ||
| 441 | |||
| 442 | /** | ||
| 443 | $(D auto map(Range)(Range r) if (isInputRange!(Unqual!Range));) | ||
| 444 | |||
| 445 | Implements the homonym function (also known as $(D transform)) present | ||
| 446 | in many languages of functional flavor. The call $(D map!(fun)(range)) | ||
| 447 | returns a range of which elements are obtained by applying $(D fun(a)) | ||
| 448 | left to right for all elements $(D a) in $(D range). The original ranges are | ||
| 449 | not changed. Evaluation is done lazily. | ||
| 450 | |||
| 451 | Params: | ||
| 452 | fun = one or more transformation functions | ||
| 453 | r = an $(REF_ALTTEXT input range, isInputRange, std,range,primitives) | ||
| 454 | |||
| 455 | Returns: | ||
| 456 | a range with each fun applied to all the elements. If there is more than one | ||
| 457 | fun, the element type will be $(D Tuple) containing one element for each fun. | ||
| 458 | |||
| 459 | See_Also: | ||
| 460 | $(HTTP en.wikipedia.org/wiki/Map_(higher-order_function), Map (higher-order function)) | ||
| 461 | */ | ||
| 462 | template map(fun...) | ||
| 463 | if (fun.length >= 1) | ||
| 464 | { | ||
| 465 | auto map(Range)(Range r) if (isInputRange!(Unqual!Range)) | ||
| 466 | { | ||
| 467 | import std.meta : AliasSeq, staticMap; | ||
| 468 | |||
| 469 | alias RE = ElementType!(Range); | ||
| 470 | static if (fun.length > 1) | ||
| 471 | { | ||
| 472 | import std.functional : adjoin; | ||
| 473 | import std.meta : staticIndexOf; | ||
| 474 | |||
| 475 | alias _funs = staticMap!(unaryFun, fun); | ||
| 476 | alias _fun = adjoin!_funs; | ||
| 477 | |||
| 478 | // Once DMD issue #5710 is fixed, this validation loop can be moved into a template. | ||
| 479 | foreach (f; _funs) | ||
| 480 | { | ||
| 481 | static assert(!is(typeof(f(RE.init)) == void), | ||
| 482 | "Mapping function(s) must not return void: " ~ _funs.stringof); | ||
| 483 | } | ||
| 484 | } | ||
| 485 | else | ||
| 486 | { | ||
| 487 | alias _fun = unaryFun!fun; | ||
| 488 | alias _funs = AliasSeq!(_fun); | ||
| 489 | |||
| 490 | // Do the validation separately for single parameters due to DMD issue #15777. | ||
| 491 | static assert(!is(typeof(_fun(RE.init)) == void), | ||
| 492 | "Mapping function(s) must not return void: " ~ _funs.stringof); | ||
| 493 | } | ||
| 494 | |||
| 495 | return MapResult!(_fun, Range)(r); | ||
| 496 | } | ||
| 497 | } | ||
| 498 | |||
| 499 | /// | ||
| 500 | @safe unittest | ||
| 501 | { | ||
| 502 | import std.algorithm.comparison : equal; | ||
| 503 | import std.range : chain; | ||
| 504 | int[] arr1 = [ 1, 2, 3, 4 ]; | ||
| 505 | int[] arr2 = [ 5, 6 ]; | ||
| 506 | auto squares = map!(a => a * a)(chain(arr1, arr2)); | ||
| 507 | assert(equal(squares, [ 1, 4, 9, 16, 25, 36 ])); | ||
| 508 | } | ||
| 509 | |||
| 510 | /** | ||
| 511 | Multiple functions can be passed to $(D map). In that case, the | ||
| 512 | element type of $(D map) is a tuple containing one element for each | ||
| 513 | function. | ||
| 514 | */ | ||
| 515 | @safe unittest | ||
| 516 | { | ||
| 517 | auto sums = [2, 4, 6, 8]; | ||
| 518 | auto products = [1, 4, 9, 16]; | ||
| 519 | |||
| 520 | size_t i = 0; | ||
| 521 | foreach (result; [ 1, 2, 3, 4 ].map!("a + a", "a * a")) | ||
| 522 | { | ||
| 523 | assert(result[0] == sums[i]); | ||
| 524 | assert(result[1] == products[i]); | ||
| 525 | ++i; | ||
| 526 | } | ||
| 527 | } | ||
| 528 | |||
| 529 | /** | ||
| 530 | You may alias $(D map) with some function(s) to a symbol and use | ||
| 531 | it separately: | ||
| 532 | */ | ||
| 533 | @safe unittest | ||
| 534 | { | ||
| 535 | import std.algorithm.comparison : equal; | ||
| 536 | import std.conv : to; | ||
| 537 | |||
| 538 | alias stringize = map!(to!string); | ||
| 539 | assert(equal(stringize([ 1, 2, 3, 4 ]), [ "1", "2", "3", "4" ])); | ||
| 540 | } | ||
| 541 | |||
| 542 | @safe unittest | ||
| 543 | { | ||
| 544 | // Verify workaround for DMD #15777 | ||
| 545 | |||
| 546 | import std.algorithm.mutation, std.string; | ||
| 547 | auto foo(string[] args) | ||
| 548 | { | ||
| 549 | return args.map!strip; | ||
| 550 | } | ||
| 551 | } | ||
| 552 | |||
| 553 | private struct MapResult(alias fun, Range) | ||
| 554 | { | ||
| 555 | alias R = Unqual!Range; | ||
| 556 | R _input; | ||
| 557 | |||
| 558 | static if (isBidirectionalRange!R) | ||
| 559 | { | ||
| 560 | @property auto ref back()() | ||
| 561 | { | ||
| 562 | assert(!empty, "Attempting to fetch the back of an empty map."); | ||
| 563 | return fun(_input.back); | ||
| 564 | } | ||
| 565 | |||
| 566 | void popBack()() | ||
| 567 | { | ||
| 568 | assert(!empty, "Attempting to popBack an empty map."); | ||
| 569 | _input.popBack(); | ||
| 570 | } | ||
| 571 | } | ||
| 572 | |||
| 573 | this(R input) | ||
| 574 | { | ||
| 575 | _input = input; | ||
| 576 | } | ||
| 577 | |||
| 578 | static if (isInfinite!R) | ||
| 579 | { | ||
| 580 | // Propagate infinite-ness. | ||
| 581 | enum bool empty = false; | ||
| 582 | } | ||
| 583 | else | ||
| 584 | { | ||
| 585 | @property bool empty() | ||
| 586 | { | ||
| 587 | return _input.empty; | ||
| 588 | } | ||
| 589 | } | ||
| 590 | |||
| 591 | void popFront() | ||
| 592 | { | ||
| 593 | assert(!empty, "Attempting to popFront an empty map."); | ||
| 594 | _input.popFront(); | ||
| 595 | } | ||
| 596 | |||
| 597 | @property auto ref front() | ||
| 598 | { | ||
| 599 | assert(!empty, "Attempting to fetch the front of an empty map."); | ||
| 600 | return fun(_input.front); | ||
| 601 | } | ||
| 602 | |||
| 603 | static if (isRandomAccessRange!R) | ||
| 604 | { | ||
| 605 | static if (is(typeof(_input[ulong.max]))) | ||
| 606 | private alias opIndex_t = ulong; | ||
| 607 | else | ||
| 608 | private alias opIndex_t = uint; | ||
| 609 | |||
| 610 | auto ref opIndex(opIndex_t index) | ||
| 611 | { | ||
| 612 | return fun(_input[index]); | ||
| 613 | } | ||
| 614 | } | ||
| 615 | |||
| 616 | static if (hasLength!R) | ||
| 617 | { | ||
| 618 | @property auto length() | ||
| 619 | { | ||
| 620 | return _input.length; | ||
| 621 | } | ||
| 622 | |||
| 623 | alias opDollar = length; | ||
| 624 | } | ||
| 625 | |||
| 626 | static if (hasSlicing!R) | ||
| 627 | { | ||
| 628 | static if (is(typeof(_input[ulong.max .. ulong.max]))) | ||
| 629 | private alias opSlice_t = ulong; | ||
| 630 | else | ||
| 631 | private alias opSlice_t = uint; | ||
| 632 | |||
| 633 | static if (hasLength!R) | ||
| 634 | { | ||
| 635 | auto opSlice(opSlice_t low, opSlice_t high) | ||
| 636 | { | ||
| 637 | return typeof(this)(_input[low .. high]); | ||
| 638 | } | ||
| 639 | } | ||
| 640 | else static if (is(typeof(_input[opSlice_t.max .. $]))) | ||
| 641 | { | ||
| 642 | struct DollarToken{} | ||
| 643 | enum opDollar = DollarToken.init; | ||
| 644 | auto opSlice(opSlice_t low, DollarToken) | ||
| 645 | { | ||
| 646 | return typeof(this)(_input[low .. $]); | ||
| 647 | } | ||
| 648 | |||
| 649 | auto opSlice(opSlice_t low, opSlice_t high) | ||
| 650 | { | ||
| 651 | import std.range : takeExactly; | ||
| 652 | return this[low .. $].takeExactly(high - low); | ||
| 653 | } | ||
| 654 | } | ||
| 655 | } | ||
| 656 | |||
| 657 | static if (isForwardRange!R) | ||
| 658 | { | ||
| 659 | @property auto save() | ||
| 660 | { | ||
| 661 | return typeof(this)(_input.save); | ||
| 662 | } | ||
| 663 | } | ||
| 664 | } | ||
| 665 | |||
| 666 | @safe unittest | ||
| 667 | { | ||
| 668 | import std.algorithm.comparison : equal; | ||
| 669 | import std.conv : to; | ||
| 670 | import std.functional : adjoin; | ||
| 671 | |||
| 672 | alias stringize = map!(to!string); | ||
| 673 | assert(equal(stringize([ 1, 2, 3, 4 ]), [ "1", "2", "3", "4" ])); | ||
| 674 | |||
| 675 | uint counter; | ||
| 676 | alias count = map!((a) { return counter++; }); | ||
| 677 | assert(equal(count([ 10, 2, 30, 4 ]), [ 0, 1, 2, 3 ])); | ||
| 678 | |||
| 679 | counter = 0; | ||
| 680 | adjoin!((a) { return counter++; }, (a) { return counter++; })(1); | ||
| 681 | alias countAndSquare = map!((a) { return counter++; }, (a) { return counter++; }); | ||
| 682 | //assert(equal(countAndSquare([ 10, 2 ]), [ tuple(0u, 100), tuple(1u, 4) ])); | ||
| 683 | } | ||
| 684 | |||
| 685 | @safe unittest | ||
| 686 | { | ||
| 687 | import std.algorithm.comparison : equal; | ||
| 688 | import std.ascii : toUpper; | ||
| 689 | import std.internal.test.dummyrange; | ||
| 690 | import std.range; | ||
| 691 | import std.typecons : tuple; | ||
| 692 | import std.random : unpredictableSeed, uniform, Random; | ||
| 693 | |||
| 694 | int[] arr1 = [ 1, 2, 3, 4 ]; | ||
| 695 | const int[] arr1Const = arr1; | ||
| 696 | int[] arr2 = [ 5, 6 ]; | ||
| 697 | auto squares = map!("a * a")(arr1Const); | ||
| 698 | assert(squares[$ - 1] == 16); | ||
| 699 | assert(equal(squares, [ 1, 4, 9, 16 ][])); | ||
| 700 | assert(equal(map!("a * a")(chain(arr1, arr2)), [ 1, 4, 9, 16, 25, 36 ][])); | ||
| 701 | |||
| 702 | // Test the caching stuff. | ||
| 703 | assert(squares.back == 16); | ||
| 704 | auto squares2 = squares.save; | ||
| 705 | assert(squares2.back == 16); | ||
| 706 | |||
| 707 | assert(squares2.front == 1); | ||
| 708 | squares2.popFront(); | ||
| 709 | assert(squares2.front == 4); | ||
| 710 | squares2.popBack(); | ||
| 711 | assert(squares2.front == 4); | ||
| 712 | assert(squares2.back == 9); | ||
| 713 | |||
| 714 | assert(equal(map!("a * a")(chain(arr1, arr2)), [ 1, 4, 9, 16, 25, 36 ][])); | ||
| 715 | |||
| 716 | uint i; | ||
| 717 | foreach (e; map!("a", "a * a")(arr1)) | ||
| 718 | { | ||
| 719 | assert(e[0] == ++i); | ||
| 720 | assert(e[1] == i * i); | ||
| 721 | } | ||
| 722 | |||
| 723 | // Test length. | ||
| 724 | assert(squares.length == 4); | ||
| 725 | assert(map!"a * a"(chain(arr1, arr2)).length == 6); | ||
| 726 | |||
| 727 | // Test indexing. | ||
| 728 | assert(squares[0] == 1); | ||
| 729 | assert(squares[1] == 4); | ||
| 730 | assert(squares[2] == 9); | ||
| 731 | assert(squares[3] == 16); | ||
| 732 | |||
| 733 | // Test slicing. | ||
| 734 | auto squareSlice = squares[1 .. squares.length - 1]; | ||
| 735 | assert(equal(squareSlice, [4, 9][])); | ||
| 736 | assert(squareSlice.back == 9); | ||
| 737 | assert(squareSlice[1] == 9); | ||
| 738 | |||
| 739 | // Test on a forward range to make sure it compiles when all the fancy | ||
| 740 | // stuff is disabled. | ||
| 741 | auto fibsSquares = map!"a * a"(recurrence!("a[n-1] + a[n-2]")(1, 1)); | ||
| 742 | assert(fibsSquares.front == 1); | ||
| 743 | fibsSquares.popFront(); | ||
| 744 | fibsSquares.popFront(); | ||
| 745 | assert(fibsSquares.front == 4); | ||
| 746 | fibsSquares.popFront(); | ||
| 747 | assert(fibsSquares.front == 9); | ||
| 748 | |||
| 749 | auto repeatMap = map!"a"(repeat(1)); | ||
| 750 | auto gen = Random(unpredictableSeed); | ||
| 751 | auto index = uniform(0, 1024, gen); | ||
| 752 | static assert(isInfinite!(typeof(repeatMap))); | ||
| 753 | assert(repeatMap[index] == 1); | ||
| 754 | |||
| 755 | auto intRange = map!"a"([1,2,3]); | ||
| 756 | static assert(isRandomAccessRange!(typeof(intRange))); | ||
| 757 | assert(equal(intRange, [1, 2, 3])); | ||
| 758 | |||
| 759 | foreach (DummyType; AllDummyRanges) | ||
| 760 | { | ||
| 761 | DummyType d; | ||
| 762 | auto m = map!"a * a"(d); | ||
| 763 | |||
| 764 | static assert(propagatesRangeType!(typeof(m), DummyType)); | ||
| 765 | assert(equal(m, [1,4,9,16,25,36,49,64,81,100])); | ||
| 766 | } | ||
| 767 | |||
| 768 | //Test string access | ||
| 769 | string s1 = "hello world!"; | ||
| 770 | dstring s2 = "日本語"; | ||
| 771 | dstring s3 = "hello world!"d; | ||
| 772 | auto ms1 = map!(std.ascii.toUpper)(s1); | ||
| 773 | auto ms2 = map!(std.ascii.toUpper)(s2); | ||
| 774 | auto ms3 = map!(std.ascii.toUpper)(s3); | ||
| 775 | static assert(!is(ms1[0])); //narrow strings can't be indexed | ||
| 776 | assert(ms2[0] == '日'); | ||
| 777 | assert(ms3[0] == 'H'); | ||
| 778 | static assert(!is(ms1[0 .. 1])); //narrow strings can't be sliced | ||
| 779 | assert(equal(ms2[0 .. 2], "日本"w)); | ||
| 780 | assert(equal(ms3[0 .. 2], "HE")); | ||
| 781 | |||
| 782 | // Issue 5753 | ||
| 783 | static void voidFun(int) {} | ||
| 784 | static int nonvoidFun(int) { return 0; } | ||
| 785 | static assert(!__traits(compiles, map!voidFun([1]))); | ||
| 786 | static assert(!__traits(compiles, map!(voidFun, voidFun)([1]))); | ||
| 787 | static assert(!__traits(compiles, map!(nonvoidFun, voidFun)([1]))); | ||
| 788 | static assert(!__traits(compiles, map!(voidFun, nonvoidFun)([1]))); | ||
| 789 | static assert(!__traits(compiles, map!(a => voidFun(a))([1]))); | ||
| 790 | |||
| 791 | // Phobos issue #15480 | ||
| 792 | auto dd = map!(z => z * z, c => c * c * c)([ 1, 2, 3, 4 ]); | ||
| 793 | assert(dd[0] == tuple(1, 1)); | ||
| 794 | assert(dd[1] == tuple(4, 8)); | ||
| 795 | assert(dd[2] == tuple(9, 27)); | ||
| 796 | assert(dd[3] == tuple(16, 64)); | ||
| 797 | assert(dd.length == 4); | ||
| 798 | } | ||
| 799 | |||
| 800 | @safe unittest | ||
| 801 | { | ||
| 802 | import std.algorithm.comparison : equal; | ||
| 803 | import std.range; | ||
| 804 | auto LL = iota(1L, 4L); | ||
| 805 | auto m = map!"a*a"(LL); | ||
| 806 | assert(equal(m, [1L, 4L, 9L])); | ||
| 807 | } | ||
| 808 | |||
| 809 | @safe unittest | ||
| 810 | { | ||
| 811 | import std.range : iota; | ||
| 812 | |||
| 813 | // Issue #10130 - map of iota with const step. | ||
| 814 | const step = 2; | ||
| 815 | assert(map!(i => i)(iota(0, 10, step)).walkLength == 5); | ||
| 816 | |||
| 817 | // Need these to all by const to repro the float case, due to the | ||
| 818 | // CommonType template used in the float specialization of iota. | ||
| 819 | const floatBegin = 0.0; | ||
| 820 | const floatEnd = 1.0; | ||
| 821 | const floatStep = 0.02; | ||
| 822 | assert(map!(i => i)(iota(floatBegin, floatEnd, floatStep)).walkLength == 50); | ||
| 823 | } | ||
| 824 | |||
| 825 | @safe unittest | ||
| 826 | { | ||
| 827 | import std.algorithm.comparison : equal; | ||
| 828 | import std.range; | ||
| 829 | //slicing infinites | ||
| 830 | auto rr = iota(0, 5).cycle().map!"a * a"(); | ||
| 831 | alias RR = typeof(rr); | ||
| 832 | static assert(hasSlicing!RR); | ||
| 833 | rr = rr[6 .. $]; //Advances 1 cycle and 1 unit | ||
| 834 | assert(equal(rr[0 .. 5], [1, 4, 9, 16, 0])); | ||
| 835 | } | ||
| 836 | |||
| 837 | @safe unittest | ||
| 838 | { | ||
| 839 | import std.range; | ||
| 840 | struct S {int* p;} | ||
| 841 | auto m = immutable(S).init.repeat().map!"a".save; | ||
| 842 | assert(m.front == immutable(S)(null)); | ||
| 843 | } | ||
| 844 | |||
| 845 | // each | ||
| 846 | /** | ||
| 847 | Eagerly iterates over $(D r) and calls $(D pred) over _each element. | ||
| 848 | |||
| 849 | If no predicate is specified, $(D each) will default to doing nothing | ||
| 850 | but consuming the entire range. $(D .front) will be evaluated, but this | ||
| 851 | can be avoided by explicitly specifying a predicate lambda with a | ||
| 852 | $(D lazy) parameter. | ||
| 853 | |||
| 854 | $(D each) also supports $(D opApply)-based iterators, so it will work | ||
| 855 | with e.g. $(REF parallel, std,parallelism). | ||
| 856 | |||
| 857 | Params: | ||
| 858 | pred = predicate to apply to each element of the range | ||
| 859 | r = range or iterable over which each iterates | ||
| 860 | |||
| 861 | See_Also: $(REF tee, std,range) | ||
| 862 | |||
| 863 | */ | ||
| 864 | template each(alias pred = "a") | ||
| 865 | { | ||
| 866 | import std.meta : AliasSeq; | ||
| 867 | import std.traits : Parameters; | ||
| 868 | |||
| 869 | private: | ||
| 870 | alias BinaryArgs = AliasSeq!(pred, "i", "a"); | ||
| 871 | |||
| 872 | enum isRangeUnaryIterable(R) = | ||
| 873 | is(typeof(unaryFun!pred(R.init.front))); | ||
| 874 | |||
| 875 | enum isRangeBinaryIterable(R) = | ||
| 876 | is(typeof(binaryFun!BinaryArgs(0, R.init.front))); | ||
| 877 | |||
| 878 | enum isRangeIterable(R) = | ||
| 879 | isInputRange!R && | ||
| 880 | (isRangeUnaryIterable!R || isRangeBinaryIterable!R); | ||
| 881 | |||
| 882 | enum isForeachUnaryIterable(R) = | ||
| 883 | is(typeof((R r) { | ||
| 884 | foreach (ref a; r) | ||
| 885 | cast(void) unaryFun!pred(a); | ||
| 886 | })); | ||
| 887 | |||
| 888 | enum isForeachBinaryIterable(R) = | ||
| 889 | is(typeof((R r) { | ||
| 890 | foreach (ref i, ref a; r) | ||
| 891 | cast(void) binaryFun!BinaryArgs(i, a); | ||
| 892 | })); | ||
| 893 | |||
| 894 | enum isForeachIterable(R) = | ||
| 895 | (!isForwardRange!R || isDynamicArray!R) && | ||
| 896 | (isForeachUnaryIterable!R || isForeachBinaryIterable!R); | ||
| 897 | |||
| 898 | public: | ||
| 899 | void each(Range)(Range r) | ||
| 900 | if (!isForeachIterable!Range && ( | ||
| 901 | isRangeIterable!Range || | ||
| 902 | __traits(compiles, typeof(r.front).length))) | ||
| 903 | { | ||
| 904 | static if (isRangeIterable!Range) | ||
| 905 | { | ||
| 906 | debug(each) pragma(msg, "Using while for ", Range.stringof); | ||
| 907 | static if (isRangeUnaryIterable!Range) | ||
| 908 | { | ||
| 909 | while (!r.empty) | ||
| 910 | { | ||
| 911 | cast(void) unaryFun!pred(r.front); | ||
| 912 | r.popFront(); | ||
| 913 | } | ||
| 914 | } | ||
| 915 | else // if (isRangeBinaryIterable!Range) | ||
| 916 | { | ||
| 917 | size_t i = 0; | ||
| 918 | while (!r.empty) | ||
| 919 | { | ||
| 920 | cast(void) binaryFun!BinaryArgs(i, r.front); | ||
| 921 | r.popFront(); | ||
| 922 | i++; | ||
| 923 | } | ||
| 924 | } | ||
| 925 | } | ||
| 926 | else | ||
| 927 | { | ||
| 928 | // range interface with >2 parameters. | ||
| 929 | for (auto range = r; !range.empty; range.popFront()) | ||
| 930 | pred(range.front.expand); | ||
| 931 | } | ||
| 932 | } | ||
| 933 | |||
| 934 | void each(Iterable)(auto ref Iterable r) | ||
| 935 | if (isForeachIterable!Iterable || | ||
| 936 | __traits(compiles, Parameters!(Parameters!(r.opApply)))) | ||
| 937 | { | ||
| 938 | static if (isForeachIterable!Iterable) | ||
| 939 | { | ||
| 940 | debug(each) pragma(msg, "Using foreach for ", Iterable.stringof); | ||
| 941 | static if (isForeachUnaryIterable!Iterable) | ||
| 942 | { | ||
| 943 | foreach (ref e; r) | ||
| 944 | cast(void) unaryFun!pred(e); | ||
| 945 | } | ||
| 946 | else // if (isForeachBinaryIterable!Iterable) | ||
| 947 | { | ||
| 948 | foreach (ref i, ref e; r) | ||
| 949 | cast(void) binaryFun!BinaryArgs(i, e); | ||
| 950 | } | ||
| 951 | } | ||
| 952 | else | ||
| 953 | { | ||
| 954 | // opApply with >2 parameters. count the delegate args. | ||
| 955 | // only works if it is not templated (otherwise we cannot count the args) | ||
| 956 | auto dg(Parameters!(Parameters!(r.opApply)) params) { | ||
| 957 | pred(params); | ||
| 958 | return 0; // tells opApply to continue iteration | ||
| 959 | } | ||
| 960 | r.opApply(&dg); | ||
| 961 | } | ||
| 962 | } | ||
| 963 | } | ||
| 964 | |||
| 965 | /// | ||
| 966 | @system unittest | ||
| 967 | { | ||
| 968 | import std.range : iota; | ||
| 969 | |||
| 970 | long[] arr; | ||
| 971 | iota(5).each!(n => arr ~= n); | ||
| 972 | assert(arr == [0, 1, 2, 3, 4]); | ||
| 973 | |||
| 974 | // If the range supports it, the value can be mutated in place | ||
| 975 | arr.each!((ref n) => n++); | ||
| 976 | assert(arr == [1, 2, 3, 4, 5]); | ||
| 977 | |||
| 978 | arr.each!"a++"; | ||
| 979 | assert(arr == [2, 3, 4, 5, 6]); | ||
| 980 | |||
| 981 | // by-ref lambdas are not allowed for non-ref ranges | ||
| 982 | static assert(!is(typeof(arr.map!(n => n).each!((ref n) => n++)))); | ||
| 983 | |||
| 984 | // The default predicate consumes the range | ||
| 985 | auto m = arr.map!(n => n); | ||
| 986 | (&m).each(); | ||
| 987 | assert(m.empty); | ||
| 988 | |||
| 989 | // Indexes are also available for in-place mutations | ||
| 990 | arr[] = 0; | ||
| 991 | arr.each!"a=i"(); | ||
| 992 | assert(arr == [0, 1, 2, 3, 4]); | ||
| 993 | |||
| 994 | // opApply iterators work as well | ||
| 995 | static class S | ||
| 996 | { | ||
| 997 | int x; | ||
| 998 | int opApply(scope int delegate(ref int _x) dg) { return dg(x); } | ||
| 999 | } | ||
| 1000 | |||
| 1001 | auto s = new S; | ||
| 1002 | s.each!"a++"; | ||
| 1003 | assert(s.x == 1); | ||
| 1004 | } | ||
| 1005 | |||
| 1006 | // binary foreach with two ref args | ||
| 1007 | @system unittest | ||
| 1008 | { | ||
| 1009 | import std.range : lockstep; | ||
| 1010 | |||
| 1011 | auto a = [ 1, 2, 3 ]; | ||
| 1012 | auto b = [ 2, 3, 4 ]; | ||
| 1013 | |||
| 1014 | a.lockstep(b).each!((ref x, ref y) { ++x; ++y; }); | ||
| 1015 | |||
| 1016 | assert(a == [ 2, 3, 4 ]); | ||
| 1017 | assert(b == [ 3, 4, 5 ]); | ||
| 1018 | } | ||
| 1019 | |||
| 1020 | // #15358: application of `each` with >2 args (opApply) | ||
| 1021 | @system unittest | ||
| 1022 | { | ||
| 1023 | import std.range : lockstep; | ||
| 1024 | auto a = [0,1,2]; | ||
| 1025 | auto b = [3,4,5]; | ||
| 1026 | auto c = [6,7,8]; | ||
| 1027 | |||
| 1028 | lockstep(a, b, c).each!((ref x, ref y, ref z) { ++x; ++y; ++z; }); | ||
| 1029 | |||
| 1030 | assert(a == [1,2,3]); | ||
| 1031 | assert(b == [4,5,6]); | ||
| 1032 | assert(c == [7,8,9]); | ||
| 1033 | } | ||
| 1034 | |||
| 1035 | // #15358: application of `each` with >2 args (range interface) | ||
| 1036 | @safe unittest | ||
| 1037 | { | ||
| 1038 | import std.range : zip; | ||
| 1039 | auto a = [0,1,2]; | ||
| 1040 | auto b = [3,4,5]; | ||
| 1041 | auto c = [6,7,8]; | ||
| 1042 | |||
| 1043 | int[] res; | ||
| 1044 | |||
| 1045 | zip(a, b, c).each!((x, y, z) { res ~= x + y + z; }); | ||
| 1046 | |||
| 1047 | assert(res == [9, 12, 15]); | ||
| 1048 | } | ||
| 1049 | |||
| 1050 | // #16255: `each` on opApply doesn't support ref | ||
| 1051 | @safe unittest | ||
| 1052 | { | ||
| 1053 | int[] dynamicArray = [1, 2, 3, 4, 5]; | ||
| 1054 | int[5] staticArray = [1, 2, 3, 4, 5]; | ||
| 1055 | |||
| 1056 | dynamicArray.each!((ref x) => x++); | ||
| 1057 | assert(dynamicArray == [2, 3, 4, 5, 6]); | ||
| 1058 | |||
| 1059 | staticArray.each!((ref x) => x++); | ||
| 1060 | assert(staticArray == [2, 3, 4, 5, 6]); | ||
| 1061 | |||
| 1062 | staticArray[].each!((ref x) => x++); | ||
| 1063 | assert(staticArray == [3, 4, 5, 6, 7]); | ||
| 1064 | } | ||
| 1065 | |||
| 1066 | // #16255: `each` on opApply doesn't support ref | ||
| 1067 | @system unittest | ||
| 1068 | { | ||
| 1069 | struct S | ||
| 1070 | { | ||
| 1071 | int x; | ||
| 1072 | int opApply(int delegate(ref int _x) dg) { return dg(x); } | ||
| 1073 | } | ||
| 1074 | |||
| 1075 | S s; | ||
| 1076 | foreach (ref a; s) ++a; | ||
| 1077 | assert(s.x == 1); | ||
| 1078 | s.each!"++a"; | ||
| 1079 | assert(s.x == 2); | ||
| 1080 | } | ||
| 1081 | |||
| 1082 | // filter | ||
| 1083 | /** | ||
| 1084 | $(D auto filter(Range)(Range rs) if (isInputRange!(Unqual!Range));) | ||
| 1085 | |||
| 1086 | Implements the higher order _filter function. The predicate is passed to | ||
| 1087 | $(REF unaryFun, std,functional), and can either accept a string, or any callable | ||
| 1088 | that can be executed via $(D pred(element)). | ||
| 1089 | |||
| 1090 | Params: | ||
| 1091 | predicate = Function to apply to each element of range | ||
| 1092 | range = Input range of elements | ||
| 1093 | |||
| 1094 | Returns: | ||
| 1095 | $(D filter!(predicate)(range)) returns a new range containing only elements $(D x) in $(D range) for | ||
| 1096 | which $(D predicate(x)) returns $(D true). | ||
| 1097 | |||
| 1098 | See_Also: | ||
| 1099 | $(HTTP en.wikipedia.org/wiki/Filter_(higher-order_function), Filter (higher-order function)) | ||
| 1100 | */ | ||
| 1101 | template filter(alias predicate) | ||
| 1102 | if (is(typeof(unaryFun!predicate))) | ||
| 1103 | { | ||
| 1104 | auto filter(Range)(Range range) if (isInputRange!(Unqual!Range)) | ||
| 1105 | { | ||
| 1106 | return FilterResult!(unaryFun!predicate, Range)(range); | ||
| 1107 | } | ||
| 1108 | } | ||
| 1109 | |||
| 1110 | /// | ||
| 1111 | @safe unittest | ||
| 1112 | { | ||
| 1113 | import std.algorithm.comparison : equal; | ||
| 1114 | import std.math : approxEqual; | ||
| 1115 | import std.range; | ||
| 1116 | |||
| 1117 | int[] arr = [ 1, 2, 3, 4, 5 ]; | ||
| 1118 | |||
| 1119 | // Sum all elements | ||
| 1120 | auto small = filter!(a => a < 3)(arr); | ||
| 1121 | assert(equal(small, [ 1, 2 ])); | ||
| 1122 | |||
| 1123 | // Sum again, but with Uniform Function Call Syntax (UFCS) | ||
| 1124 | auto sum = arr.filter!(a => a < 3); | ||
| 1125 | assert(equal(sum, [ 1, 2 ])); | ||
| 1126 | |||
| 1127 | // In combination with chain() to span multiple ranges | ||
| 1128 | int[] a = [ 3, -2, 400 ]; | ||
| 1129 | int[] b = [ 100, -101, 102 ]; | ||
| 1130 | auto r = chain(a, b).filter!(a => a > 0); | ||
| 1131 | assert(equal(r, [ 3, 400, 100, 102 ])); | ||
| 1132 | |||
| 1133 | // Mixing convertible types is fair game, too | ||
| 1134 | double[] c = [ 2.5, 3.0 ]; | ||
| 1135 | auto r1 = chain(c, a, b).filter!(a => cast(int) a != a); | ||
| 1136 | assert(approxEqual(r1, [ 2.5 ])); | ||
| 1137 | } | ||
| 1138 | |||
| 1139 | private struct FilterResult(alias pred, Range) | ||
| 1140 | { | ||
| 1141 | alias R = Unqual!Range; | ||
| 1142 | R _input; | ||
| 1143 | private bool _primed; | ||
| 1144 | |||
| 1145 | private void prime() | ||
| 1146 | { | ||
| 1147 | if (_primed) return; | ||
| 1148 | while (!_input.empty && !pred(_input.front)) | ||
| 1149 | { | ||
| 1150 | _input.popFront(); | ||
| 1151 | } | ||
| 1152 | _primed = true; | ||
| 1153 | } | ||
| 1154 | |||
| 1155 | this(R r) | ||
| 1156 | { | ||
| 1157 | _input = r; | ||
| 1158 | } | ||
| 1159 | |||
| 1160 | private this(R r, bool primed) | ||
| 1161 | { | ||
| 1162 | _input = r; | ||
| 1163 | _primed = primed; | ||
| 1164 | } | ||
| 1165 | |||
| 1166 | auto opSlice() { return this; } | ||
| 1167 | |||
| 1168 | static if (isInfinite!Range) | ||
| 1169 | { | ||
| 1170 | enum bool empty = false; | ||
| 1171 | } | ||
| 1172 | else | ||
| 1173 | { | ||
| 1174 | @property bool empty() { prime; return _input.empty; } | ||
| 1175 | } | ||
| 1176 | |||
| 1177 | void popFront() | ||
| 1178 | { | ||
| 1179 | do | ||
| 1180 | { | ||
| 1181 | _input.popFront(); | ||
| 1182 | } while (!_input.empty && !pred(_input.front)); | ||
| 1183 | _primed = true; | ||
| 1184 | } | ||
| 1185 | |||
| 1186 | @property auto ref front() | ||
| 1187 | { | ||
| 1188 | prime; | ||
| 1189 | assert(!empty, "Attempting to fetch the front of an empty filter."); | ||
| 1190 | return _input.front; | ||
| 1191 | } | ||
| 1192 | |||
| 1193 | static if (isForwardRange!R) | ||
| 1194 | { | ||
| 1195 | @property auto save() | ||
| 1196 | { | ||
| 1197 | return typeof(this)(_input.save, _primed); | ||
| 1198 | } | ||
| 1199 | } | ||
| 1200 | } | ||
| 1201 | |||
| 1202 | @safe unittest | ||
| 1203 | { | ||
| 1204 | import std.algorithm.comparison : equal; | ||
| 1205 | import std.internal.test.dummyrange; | ||
| 1206 | import std.range; | ||
| 1207 | |||
| 1208 | auto shouldNotLoop4ever = repeat(1).filter!(x => x % 2 == 0); | ||
| 1209 | static assert(isInfinite!(typeof(shouldNotLoop4ever))); | ||
| 1210 | assert(!shouldNotLoop4ever.empty); | ||
| 1211 | |||
| 1212 | int[] a = [ 3, 4, 2 ]; | ||
| 1213 | auto r = filter!("a > 3")(a); | ||
| 1214 | static assert(isForwardRange!(typeof(r))); | ||
| 1215 | assert(equal(r, [ 4 ][])); | ||
| 1216 | |||
| 1217 | a = [ 1, 22, 3, 42, 5 ]; | ||
| 1218 | auto under10 = filter!("a < 10")(a); | ||
| 1219 | assert(equal(under10, [1, 3, 5][])); | ||
| 1220 | static assert(isForwardRange!(typeof(under10))); | ||
| 1221 | under10.front = 4; | ||
| 1222 | assert(equal(under10, [4, 3, 5][])); | ||
| 1223 | under10.front = 40; | ||
| 1224 | assert(equal(under10, [40, 3, 5][])); | ||
| 1225 | under10.front = 1; | ||
| 1226 | |||
| 1227 | auto infinite = filter!"a > 2"(repeat(3)); | ||
| 1228 | static assert(isInfinite!(typeof(infinite))); | ||
| 1229 | static assert(isForwardRange!(typeof(infinite))); | ||
| 1230 | assert(infinite.front == 3); | ||
| 1231 | |||
| 1232 | foreach (DummyType; AllDummyRanges) | ||
| 1233 | { | ||
| 1234 | DummyType d; | ||
| 1235 | auto f = filter!"a & 1"(d); | ||
| 1236 | assert(equal(f, [1,3,5,7,9])); | ||
| 1237 | |||
| 1238 | static if (isForwardRange!DummyType) | ||
| 1239 | { | ||
| 1240 | static assert(isForwardRange!(typeof(f))); | ||
| 1241 | } | ||
| 1242 | } | ||
| 1243 | |||
| 1244 | // With delegates | ||
| 1245 | int x = 10; | ||
| 1246 | int overX(int a) { return a > x; } | ||
| 1247 | typeof(filter!overX(a)) getFilter() | ||
| 1248 | { | ||
| 1249 | return filter!overX(a); | ||
| 1250 | } | ||
| 1251 | auto r1 = getFilter(); | ||
| 1252 | assert(equal(r1, [22, 42])); | ||
| 1253 | |||
| 1254 | // With chain | ||
| 1255 | auto nums = [0,1,2,3,4]; | ||
| 1256 | assert(equal(filter!overX(chain(a, nums)), [22, 42])); | ||
| 1257 | |||
| 1258 | // With copying of inner struct Filter to Map | ||
| 1259 | auto arr = [1,2,3,4,5]; | ||
| 1260 | auto m = map!"a + 1"(filter!"a < 4"(arr)); | ||
| 1261 | assert(equal(m, [2, 3, 4])); | ||
| 1262 | } | ||
| 1263 | |||
| 1264 | @safe unittest | ||
| 1265 | { | ||
| 1266 | import std.algorithm.comparison : equal; | ||
| 1267 | |||
| 1268 | int[] a = [ 3, 4 ]; | ||
| 1269 | const aConst = a; | ||
| 1270 | auto r = filter!("a > 3")(aConst); | ||
| 1271 | assert(equal(r, [ 4 ][])); | ||
| 1272 | |||
| 1273 | a = [ 1, 22, 3, 42, 5 ]; | ||
| 1274 | auto under10 = filter!("a < 10")(a); | ||
| 1275 | assert(equal(under10, [1, 3, 5][])); | ||
| 1276 | assert(equal(under10.save, [1, 3, 5][])); | ||
| 1277 | assert(equal(under10.save, under10)); | ||
| 1278 | } | ||
| 1279 | |||
| 1280 | @safe unittest | ||
| 1281 | { | ||
| 1282 | import std.algorithm.comparison : equal; | ||
| 1283 | import std.functional : compose, pipe; | ||
| 1284 | |||
| 1285 | assert(equal(compose!(map!"2 * a", filter!"a & 1")([1,2,3,4,5]), | ||
| 1286 | [2,6,10])); | ||
| 1287 | assert(equal(pipe!(filter!"a & 1", map!"2 * a")([1,2,3,4,5]), | ||
| 1288 | [2,6,10])); | ||
| 1289 | } | ||
| 1290 | |||
| 1291 | @safe unittest | ||
| 1292 | { | ||
| 1293 | import std.algorithm.comparison : equal; | ||
| 1294 | |||
| 1295 | int x = 10; | ||
| 1296 | int underX(int a) { return a < x; } | ||
| 1297 | const(int)[] list = [ 1, 2, 10, 11, 3, 4 ]; | ||
| 1298 | assert(equal(filter!underX(list), [ 1, 2, 3, 4 ])); | ||
| 1299 | } | ||
| 1300 | |||
| 1301 | /** | ||
| 1302 | * $(D auto filterBidirectional(Range)(Range r) if (isBidirectionalRange!(Unqual!Range));) | ||
| 1303 | * | ||
| 1304 | * Similar to $(D filter), except it defines a | ||
| 1305 | * $(REF_ALTTEXT bidirectional range, isBidirectionalRange, std,range,primitives). | ||
| 1306 | * There is a speed disadvantage - the constructor spends time | ||
| 1307 | * finding the last element in the range that satisfies the filtering | ||
| 1308 | * condition (in addition to finding the first one). The advantage is | ||
| 1309 | * that the filtered range can be spanned from both directions. Also, | ||
| 1310 | * $(REF retro, std,range) can be applied against the filtered range. | ||
| 1311 | * | ||
| 1312 | * The predicate is passed to $(REF unaryFun, std,functional), and can either | ||
| 1313 | * accept a string, or any callable that can be executed via $(D pred(element)). | ||
| 1314 | * | ||
| 1315 | * Params: | ||
| 1316 | * pred = Function to apply to each element of range | ||
| 1317 | * r = Bidirectional range of elements | ||
| 1318 | * | ||
| 1319 | * Returns: | ||
| 1320 | * a new range containing only the elements in r for which pred returns $(D true). | ||
| 1321 | */ | ||
| 1322 | template filterBidirectional(alias pred) | ||
| 1323 | { | ||
| 1324 | auto filterBidirectional(Range)(Range r) if (isBidirectionalRange!(Unqual!Range)) | ||
| 1325 | { | ||
| 1326 | return FilterBidiResult!(unaryFun!pred, Range)(r); | ||
| 1327 | } | ||
| 1328 | } | ||
| 1329 | |||
| 1330 | /// | ||
| 1331 | @safe unittest | ||
| 1332 | { | ||
| 1333 | import std.algorithm.comparison : equal; | ||
| 1334 | import std.range; | ||
| 1335 | |||
| 1336 | int[] arr = [ 1, 2, 3, 4, 5 ]; | ||
| 1337 | auto small = filterBidirectional!("a < 3")(arr); | ||
| 1338 | static assert(isBidirectionalRange!(typeof(small))); | ||
| 1339 | assert(small.back == 2); | ||
| 1340 | assert(equal(small, [ 1, 2 ])); | ||
| 1341 | assert(equal(retro(small), [ 2, 1 ])); | ||
| 1342 | // In combination with chain() to span multiple ranges | ||
| 1343 | int[] a = [ 3, -2, 400 ]; | ||
| 1344 | int[] b = [ 100, -101, 102 ]; | ||
| 1345 | auto r = filterBidirectional!("a > 0")(chain(a, b)); | ||
| 1346 | assert(r.back == 102); | ||
| 1347 | } | ||
| 1348 | |||
| 1349 | private struct FilterBidiResult(alias pred, Range) | ||
| 1350 | { | ||
| 1351 | alias R = Unqual!Range; | ||
| 1352 | R _input; | ||
| 1353 | |||
| 1354 | this(R r) | ||
| 1355 | { | ||
| 1356 | _input = r; | ||
| 1357 | while (!_input.empty && !pred(_input.front)) _input.popFront(); | ||
| 1358 | while (!_input.empty && !pred(_input.back)) _input.popBack(); | ||
| 1359 | } | ||
| 1360 | |||
| 1361 | @property bool empty() { return _input.empty; } | ||
| 1362 | |||
| 1363 | void popFront() | ||
| 1364 | { | ||
| 1365 | do | ||
| 1366 | { | ||
| 1367 | _input.popFront(); | ||
| 1368 | } while (!_input.empty && !pred(_input.front)); | ||
| 1369 | } | ||
| 1370 | |||
| 1371 | @property auto ref front() | ||
| 1372 | { | ||
| 1373 | assert(!empty, "Attempting to fetch the front of an empty filterBidirectional."); | ||
| 1374 | return _input.front; | ||
| 1375 | } | ||
| 1376 | |||
| 1377 | void popBack() | ||
| 1378 | { | ||
| 1379 | do | ||
| 1380 | { | ||
| 1381 | _input.popBack(); | ||
| 1382 | } while (!_input.empty && !pred(_input.back)); | ||
| 1383 | } | ||
| 1384 | |||
| 1385 | @property auto ref back() | ||
| 1386 | { | ||
| 1387 | assert(!empty, "Attempting to fetch the back of an empty filterBidirectional."); | ||
| 1388 | return _input.back; | ||
| 1389 | } | ||
| 1390 | |||
| 1391 | @property auto save() | ||
| 1392 | { | ||
| 1393 | return typeof(this)(_input.save); | ||
| 1394 | } | ||
| 1395 | } | ||
| 1396 | |||
| 1397 | /** | ||
| 1398 | Groups consecutively equivalent elements into a single tuple of the element and | ||
| 1399 | the number of its repetitions. | ||
| 1400 | |||
| 1401 | Similarly to $(D uniq), $(D group) produces a range that iterates over unique | ||
| 1402 | consecutive elements of the given range. Each element of this range is a tuple | ||
| 1403 | of the element and the number of times it is repeated in the original range. | ||
| 1404 | Equivalence of elements is assessed by using the predicate $(D pred), which | ||
| 1405 | defaults to $(D "a == b"). The predicate is passed to $(REF binaryFun, std,functional), | ||
| 1406 | and can either accept a string, or any callable that can be executed via | ||
| 1407 | $(D pred(element, element)). | ||
| 1408 | |||
| 1409 | Params: | ||
| 1410 | pred = Binary predicate for determining equivalence of two elements. | ||
| 1411 | r = The $(REF_ALTTEXT input range, isInputRange, std,range,primitives) to | ||
| 1412 | iterate over. | ||
| 1413 | |||
| 1414 | Returns: A range of elements of type $(D Tuple!(ElementType!R, uint)), | ||
| 1415 | representing each consecutively unique element and its respective number of | ||
| 1416 | occurrences in that run. This will be an input range if $(D R) is an input | ||
| 1417 | range, and a forward range in all other cases. | ||
| 1418 | |||
| 1419 | See_Also: $(LREF chunkBy), which chunks an input range into subranges | ||
| 1420 | of equivalent adjacent elements. | ||
| 1421 | */ | ||
| 1422 | Group!(pred, Range) group(alias pred = "a == b", Range)(Range r) | ||
| 1423 | { | ||
| 1424 | return typeof(return)(r); | ||
| 1425 | } | ||
| 1426 | |||
| 1427 | /// ditto | ||
| 1428 | struct Group(alias pred, R) | ||
| 1429 | if (isInputRange!R) | ||
| 1430 | { | ||
| 1431 | import std.typecons : Rebindable, tuple, Tuple; | ||
| 1432 | |||
| 1433 | private alias comp = binaryFun!pred; | ||
| 1434 | |||
| 1435 | private alias E = ElementType!R; | ||
| 1436 | static if ((is(E == class) || is(E == interface)) && | ||
| 1437 | (is(E == const) || is(E == immutable))) | ||
| 1438 | { | ||
| 1439 | private alias MutableE = Rebindable!E; | ||
| 1440 | } | ||
| 1441 | else static if (is(E : Unqual!E)) | ||
| 1442 | { | ||
| 1443 | private alias MutableE = Unqual!E; | ||
| 1444 | } | ||
| 1445 | else | ||
| 1446 | { | ||
| 1447 | private alias MutableE = E; | ||
| 1448 | } | ||
| 1449 | |||
| 1450 | private R _input; | ||
| 1451 | private Tuple!(MutableE, uint) _current; | ||
| 1452 | |||
| 1453 | /// | ||
| 1454 | this(R input) | ||
| 1455 | { | ||
| 1456 | _input = input; | ||
| 1457 | if (!_input.empty) popFront(); | ||
| 1458 | } | ||
| 1459 | |||
| 1460 | /// | ||
| 1461 | void popFront() | ||
| 1462 | { | ||
| 1463 | if (_input.empty) | ||
| 1464 | { | ||
| 1465 | _current[1] = 0; | ||
| 1466 | } | ||
| 1467 | else | ||
| 1468 | { | ||
| 1469 | _current = tuple(_input.front, 1u); | ||
| 1470 | _input.popFront(); | ||
| 1471 | while (!_input.empty && comp(_current[0], _input.front)) | ||
| 1472 | { | ||
| 1473 | ++_current[1]; | ||
| 1474 | _input.popFront(); | ||
| 1475 | } | ||
| 1476 | } | ||
| 1477 | } | ||
| 1478 | |||
| 1479 | static if (isInfinite!R) | ||
| 1480 | { | ||
| 1481 | /// | ||
| 1482 | enum bool empty = false; // Propagate infiniteness. | ||
| 1483 | } | ||
| 1484 | else | ||
| 1485 | { | ||
| 1486 | /// | ||
| 1487 | @property bool empty() | ||
| 1488 | { | ||
| 1489 | return _current[1] == 0; | ||
| 1490 | } | ||
| 1491 | } | ||
| 1492 | |||
| 1493 | /// | ||
| 1494 | @property auto ref front() | ||
| 1495 | { | ||
| 1496 | assert(!empty, "Attempting to fetch the front of an empty Group."); | ||
| 1497 | return _current; | ||
| 1498 | } | ||
| 1499 | |||
| 1500 | static if (isForwardRange!R) | ||
| 1501 | { | ||
| 1502 | /// | ||
| 1503 | @property typeof(this) save() { | ||
| 1504 | typeof(this) ret = this; | ||
| 1505 | ret._input = this._input.save; | ||
| 1506 | ret._current = this._current; | ||
| 1507 | return ret; | ||
| 1508 | } | ||
| 1509 | } | ||
| 1510 | } | ||
| 1511 | |||
| 1512 | /// | ||
| 1513 | @safe unittest | ||
| 1514 | { | ||
| 1515 | import std.algorithm.comparison : equal; | ||
| 1516 | import std.typecons : tuple, Tuple; | ||
| 1517 | |||
| 1518 | int[] arr = [ 1, 2, 2, 2, 2, 3, 4, 4, 4, 5 ]; | ||
| 1519 | assert(equal(group(arr), [ tuple(1, 1u), tuple(2, 4u), tuple(3, 1u), | ||
| 1520 | tuple(4, 3u), tuple(5, 1u) ][])); | ||
| 1521 | } | ||
| 1522 | |||
| 1523 | /** | ||
| 1524 | * Using group, an associative array can be easily generated with the count of each | ||
| 1525 | * unique element in the range. | ||
| 1526 | */ | ||
| 1527 | @safe unittest | ||
| 1528 | { | ||
| 1529 | import std.algorithm.sorting : sort; | ||
| 1530 | import std.array : assocArray; | ||
| 1531 | |||
| 1532 | uint[string] result; | ||
| 1533 | auto range = ["a", "b", "a", "c", "b", "c", "c", "d", "e"]; | ||
| 1534 | result = range.sort!((a, b) => a < b) | ||
| 1535 | .group | ||
| 1536 | .assocArray; | ||
| 1537 | |||
| 1538 | assert(result == ["a": 2U, "b": 2U, "c": 3U, "d": 1U, "e": 1U]); | ||
| 1539 | } | ||
| 1540 | |||
| 1541 | @safe unittest | ||
| 1542 | { | ||
| 1543 | import std.algorithm.comparison : equal; | ||
| 1544 | import std.internal.test.dummyrange; | ||
| 1545 | import std.typecons : tuple, Tuple; | ||
| 1546 | |||
| 1547 | int[] arr = [ 1, 2, 2, 2, 2, 3, 4, 4, 4, 5 ]; | ||
| 1548 | assert(equal(group(arr), [ tuple(1, 1u), tuple(2, 4u), tuple(3, 1u), | ||
| 1549 | tuple(4, 3u), tuple(5, 1u) ][])); | ||
| 1550 | static assert(isForwardRange!(typeof(group(arr)))); | ||
| 1551 | |||
| 1552 | foreach (DummyType; AllDummyRanges) | ||
| 1553 | { | ||
| 1554 | DummyType d; | ||
| 1555 | auto g = group(d); | ||
| 1556 | |||
| 1557 | static assert(d.rt == RangeType.Input || isForwardRange!(typeof(g))); | ||
| 1558 | |||
| 1559 | assert(equal(g, [tuple(1, 1u), tuple(2, 1u), tuple(3, 1u), tuple(4, 1u), | ||
| 1560 | tuple(5, 1u), tuple(6, 1u), tuple(7, 1u), tuple(8, 1u), | ||
| 1561 | tuple(9, 1u), tuple(10, 1u)])); | ||
| 1562 | } | ||
| 1563 | } | ||
| 1564 | |||
| 1565 | @safe unittest | ||
| 1566 | { | ||
| 1567 | import std.algorithm.comparison : equal; | ||
| 1568 | import std.typecons : tuple; | ||
| 1569 | |||
| 1570 | // Issue 13857 | ||
| 1571 | immutable(int)[] a1 = [1,1,2,2,2,3,4,4,5,6,6,7,8,9,9,9]; | ||
| 1572 | auto g1 = group(a1); | ||
| 1573 | assert(equal(g1, [ tuple(1, 2u), tuple(2, 3u), tuple(3, 1u), | ||
| 1574 | tuple(4, 2u), tuple(5, 1u), tuple(6, 2u), | ||
| 1575 | tuple(7, 1u), tuple(8, 1u), tuple(9, 3u) | ||
| 1576 | ])); | ||
| 1577 | |||
| 1578 | // Issue 13162 | ||
| 1579 | immutable(ubyte)[] a2 = [1, 1, 1, 0, 0, 0]; | ||
| 1580 | auto g2 = a2.group; | ||
| 1581 | assert(equal(g2, [ tuple(1, 3u), tuple(0, 3u) ])); | ||
| 1582 | |||
| 1583 | // Issue 10104 | ||
| 1584 | const a3 = [1, 1, 2, 2]; | ||
| 1585 | auto g3 = a3.group; | ||
| 1586 | assert(equal(g3, [ tuple(1, 2u), tuple(2, 2u) ])); | ||
| 1587 | |||
| 1588 | interface I {} | ||
| 1589 | class C : I {} | ||
| 1590 | const C[] a4 = [new const C()]; | ||
| 1591 | auto g4 = a4.group!"a is b"; | ||
| 1592 | assert(g4.front[1] == 1); | ||
| 1593 | |||
| 1594 | immutable I[] a5 = [new immutable C()]; | ||
| 1595 | auto g5 = a5.group!"a is b"; | ||
| 1596 | assert(g5.front[1] == 1); | ||
| 1597 | |||
| 1598 | const(int[][]) a6 = [[1], [1]]; | ||
| 1599 | auto g6 = a6.group; | ||
| 1600 | assert(equal(g6.front[0], [1])); | ||
| 1601 | } | ||
| 1602 | |||
| 1603 | // Used by implementation of chunkBy for non-forward input ranges. | ||
| 1604 | private struct ChunkByChunkImpl(alias pred, Range) | ||
| 1605 | if (isInputRange!Range && !isForwardRange!Range) | ||
| 1606 | { | ||
| 1607 | alias fun = binaryFun!pred; | ||
| 1608 | |||
| 1609 | private Range r; | ||
| 1610 | private ElementType!Range prev; | ||
| 1611 | |||
| 1612 | this(Range range, ElementType!Range _prev) | ||
| 1613 | { | ||
| 1614 | r = range; | ||
| 1615 | prev = _prev; | ||
| 1616 | } | ||
| 1617 | |||
| 1618 | @property bool empty() | ||
| 1619 | { | ||
| 1620 | return r.empty || !fun(prev, r.front); | ||
| 1621 | } | ||
| 1622 | |||
| 1623 | @property ElementType!Range front() { return r.front; } | ||
| 1624 | void popFront() { r.popFront(); } | ||
| 1625 | } | ||
| 1626 | |||
| 1627 | private template ChunkByImplIsUnary(alias pred, Range) | ||
| 1628 | { | ||
| 1629 | static if (is(typeof(binaryFun!pred(ElementType!Range.init, | ||
| 1630 | ElementType!Range.init)) : bool)) | ||
| 1631 | enum ChunkByImplIsUnary = false; | ||
| 1632 | else static if (is(typeof( | ||
| 1633 | unaryFun!pred(ElementType!Range.init) == | ||
| 1634 | unaryFun!pred(ElementType!Range.init)))) | ||
| 1635 | enum ChunkByImplIsUnary = true; | ||
| 1636 | else | ||
| 1637 | static assert(0, "chunkBy expects either a binary predicate or "~ | ||
| 1638 | "a unary predicate on range elements of type: "~ | ||
| 1639 | ElementType!Range.stringof); | ||
| 1640 | } | ||
| 1641 | |||
| 1642 | // Implementation of chunkBy for non-forward input ranges. | ||
| 1643 | private struct ChunkByImpl(alias pred, Range) | ||
| 1644 | if (isInputRange!Range && !isForwardRange!Range) | ||
| 1645 | { | ||
| 1646 | enum bool isUnary = ChunkByImplIsUnary!(pred, Range); | ||
| 1647 | |||
| 1648 | static if (isUnary) | ||
| 1649 | alias eq = binaryFun!((a, b) => unaryFun!pred(a) == unaryFun!pred(b)); | ||
| 1650 | else | ||
| 1651 | alias eq = binaryFun!pred; | ||
| 1652 | |||
| 1653 | private Range r; | ||
| 1654 | private ElementType!Range _prev; | ||
| 1655 | |||
| 1656 | this(Range _r) | ||
| 1657 | { | ||
| 1658 | r = _r; | ||
| 1659 | if (!empty) | ||
| 1660 | { | ||
| 1661 | // Check reflexivity if predicate is claimed to be an equivalence | ||
| 1662 | // relation. | ||
| 1663 | assert(eq(r.front, r.front), | ||
| 1664 | "predicate is not reflexive"); | ||
| 1665 | |||
| 1666 | // _prev's type may be a nested struct, so must be initialized | ||
| 1667 | // directly in the constructor (cannot call savePred()). | ||
| 1668 | _prev = r.front; | ||
| 1669 | } | ||
| 1670 | else | ||
| 1671 | { | ||
| 1672 | // We won't use _prev, but must be initialized. | ||
| 1673 | _prev = typeof(_prev).init; | ||
| 1674 | } | ||
| 1675 | } | ||
| 1676 | @property bool empty() { return r.empty; } | ||
| 1677 | |||
| 1678 | @property auto front() | ||
| 1679 | { | ||
| 1680 | static if (isUnary) | ||
| 1681 | { | ||
| 1682 | import std.typecons : tuple; | ||
| 1683 | return tuple(unaryFun!pred(_prev), | ||
| 1684 | ChunkByChunkImpl!(eq, Range)(r, _prev)); | ||
| 1685 | } | ||
| 1686 | else | ||
| 1687 | { | ||
| 1688 | return ChunkByChunkImpl!(eq, Range)(r, _prev); | ||
| 1689 | } | ||
| 1690 | } | ||
| 1691 | |||
| 1692 | void popFront() | ||
| 1693 | { | ||
| 1694 | while (!r.empty) | ||
| 1695 | { | ||
| 1696 | if (!eq(_prev, r.front)) | ||
| 1697 | { | ||
| 1698 | _prev = r.front; | ||
| 1699 | break; | ||
| 1700 | } | ||
| 1701 | r.popFront(); | ||
| 1702 | } | ||
| 1703 | } | ||
| 1704 | } | ||
| 1705 | |||
| 1706 | // Single-pass implementation of chunkBy for forward ranges. | ||
| 1707 | private struct ChunkByImpl(alias pred, Range) | ||
| 1708 | if (isForwardRange!Range) | ||
| 1709 | { | ||
| 1710 | import std.typecons : RefCounted; | ||
| 1711 | |||
| 1712 | enum bool isUnary = ChunkByImplIsUnary!(pred, Range); | ||
| 1713 | |||
| 1714 | static if (isUnary) | ||
| 1715 | alias eq = binaryFun!((a, b) => unaryFun!pred(a) == unaryFun!pred(b)); | ||
| 1716 | else | ||
| 1717 | alias eq = binaryFun!pred; | ||
| 1718 | |||
| 1719 | // Outer range | ||
| 1720 | static struct Impl | ||
| 1721 | { | ||
| 1722 | size_t groupNum; | ||
| 1723 | Range current; | ||
| 1724 | Range next; | ||
| 1725 | } | ||
| 1726 | |||
| 1727 | // Inner range | ||
| 1728 | static struct Group | ||
| 1729 | { | ||
| 1730 | private size_t groupNum; | ||
| 1731 | private Range start; | ||
| 1732 | private Range current; | ||
| 1733 | |||
| 1734 | private RefCounted!Impl mothership; | ||
| 1735 | |||
| 1736 | this(RefCounted!Impl origin) | ||
| 1737 | { | ||
| 1738 | groupNum = origin.groupNum; | ||
| 1739 | |||
| 1740 | start = origin.current.save; | ||
| 1741 | current = origin.current.save; | ||
| 1742 | assert(!start.empty); | ||
| 1743 | |||
| 1744 | mothership = origin; | ||
| 1745 | |||
| 1746 | // Note: this requires reflexivity. | ||
| 1747 | assert(eq(start.front, current.front), | ||
| 1748 | "predicate is not reflexive"); | ||
| 1749 | } | ||
| 1750 | |||
| 1751 | @property bool empty() { return groupNum == size_t.max; } | ||
| 1752 | @property auto ref front() { return current.front; } | ||
| 1753 | |||
| 1754 | void popFront() | ||
| 1755 | { | ||
| 1756 | current.popFront(); | ||
| 1757 | |||
| 1758 | // Note: this requires transitivity. | ||
| 1759 | if (current.empty || !eq(start.front, current.front)) | ||
| 1760 | { | ||
| 1761 | if (groupNum == mothership.groupNum) | ||
| 1762 | { | ||
| 1763 | // If parent range hasn't moved on yet, help it along by | ||
| 1764 | // saving location of start of next Group. | ||
| 1765 | mothership.next = current.save; | ||
| 1766 | } | ||
| 1767 | |||
| 1768 | groupNum = size_t.max; | ||
| 1769 | } | ||
| 1770 | } | ||
| 1771 | |||
| 1772 | @property auto save() | ||
| 1773 | { | ||
| 1774 | auto copy = this; | ||
| 1775 | copy.current = current.save; | ||
| 1776 | return copy; | ||
| 1777 | } | ||
| 1778 | } | ||
| 1779 | static assert(isForwardRange!Group); | ||
| 1780 | |||
| 1781 | private RefCounted!Impl impl; | ||
| 1782 | |||
| 1783 | this(Range r) | ||
| 1784 | { | ||
| 1785 | impl = RefCounted!Impl(0, r, r.save); | ||
| 1786 | } | ||
| 1787 | |||
| 1788 | @property bool empty() { return impl.current.empty; } | ||
| 1789 | |||
| 1790 | @property auto front() | ||
| 1791 | { | ||
| 1792 | static if (isUnary) | ||
| 1793 | { | ||
| 1794 | import std.typecons : tuple; | ||
| 1795 | return tuple(unaryFun!pred(impl.current.front), Group(impl)); | ||
| 1796 | } | ||
| 1797 | else | ||
| 1798 | { | ||
| 1799 | return Group(impl); | ||
| 1800 | } | ||
| 1801 | } | ||
| 1802 | |||
| 1803 | void popFront() | ||
| 1804 | { | ||
| 1805 | // Scan for next group. If we're lucky, one of our Groups would have | ||
| 1806 | // already set .next to the start of the next group, in which case the | ||
| 1807 | // loop is skipped. | ||
| 1808 | while (!impl.next.empty && eq(impl.current.front, impl.next.front)) | ||
| 1809 | { | ||
| 1810 | impl.next.popFront(); | ||
| 1811 | } | ||
| 1812 | |||
| 1813 | impl.current = impl.next.save; | ||
| 1814 | |||
| 1815 | // Indicate to any remaining Groups that we have moved on. | ||
| 1816 | impl.groupNum++; | ||
| 1817 | } | ||
| 1818 | |||
| 1819 | @property auto save() | ||
| 1820 | { | ||
| 1821 | // Note: the new copy of the range will be detached from any existing | ||
| 1822 | // satellite Groups, and will not benefit from the .next acceleration. | ||
| 1823 | return typeof(this)(impl.current.save); | ||
| 1824 | } | ||
| 1825 | |||
| 1826 | static assert(isForwardRange!(typeof(this))); | ||
| 1827 | } | ||
| 1828 | |||
| 1829 | @system unittest | ||
| 1830 | { | ||
| 1831 | import std.algorithm.comparison : equal; | ||
| 1832 | |||
| 1833 | size_t popCount = 0; | ||
| 1834 | class RefFwdRange | ||
| 1835 | { | ||
| 1836 | int[] impl; | ||
| 1837 | |||
| 1838 | @safe nothrow: | ||
| 1839 | |||
| 1840 | this(int[] data) { impl = data; } | ||
| 1841 | @property bool empty() { return impl.empty; } | ||
| 1842 | @property auto ref front() { return impl.front; } | ||
| 1843 | void popFront() | ||
| 1844 | { | ||
| 1845 | impl.popFront(); | ||
| 1846 | popCount++; | ||
| 1847 | } | ||
| 1848 | @property auto save() { return new RefFwdRange(impl); } | ||
| 1849 | } | ||
| 1850 | static assert(isForwardRange!RefFwdRange); | ||
| 1851 | |||
| 1852 | auto testdata = new RefFwdRange([1, 3, 5, 2, 4, 7, 6, 8, 9]); | ||
| 1853 | auto groups = testdata.chunkBy!((a,b) => (a % 2) == (b % 2)); | ||
| 1854 | auto outerSave1 = groups.save; | ||
| 1855 | |||
| 1856 | // Sanity test | ||
| 1857 | assert(groups.equal!equal([[1, 3, 5], [2, 4], [7], [6, 8], [9]])); | ||
| 1858 | assert(groups.empty); | ||
| 1859 | |||
| 1860 | // Performance test for single-traversal use case: popFront should not have | ||
| 1861 | // been called more times than there are elements if we traversed the | ||
| 1862 | // segmented range exactly once. | ||
| 1863 | assert(popCount == 9); | ||
| 1864 | |||
| 1865 | // Outer range .save test | ||
| 1866 | groups = outerSave1.save; | ||
| 1867 | assert(!groups.empty); | ||
| 1868 | |||
| 1869 | // Inner range .save test | ||
| 1870 | auto grp1 = groups.front.save; | ||
| 1871 | auto grp1b = grp1.save; | ||
| 1872 | assert(grp1b.equal([1, 3, 5])); | ||
| 1873 | assert(grp1.save.equal([1, 3, 5])); | ||
| 1874 | |||
| 1875 | // Inner range should remain consistent after outer range has moved on. | ||
| 1876 | groups.popFront(); | ||
| 1877 | assert(grp1.save.equal([1, 3, 5])); | ||
| 1878 | |||
| 1879 | // Inner range should not be affected by subsequent inner ranges. | ||
| 1880 | assert(groups.front.equal([2, 4])); | ||
| 1881 | assert(grp1.save.equal([1, 3, 5])); | ||
| 1882 | } | ||
| 1883 | |||
| 1884 | /** | ||
| 1885 | * Chunks an input range into subranges of equivalent adjacent elements. | ||
| 1886 | * In other languages this is often called `partitionBy`, `groupBy` | ||
| 1887 | * or `sliceWhen`. | ||
| 1888 | * | ||
| 1889 | * Equivalence is defined by the predicate $(D pred), which can be either | ||
| 1890 | * binary, which is passed to $(REF binaryFun, std,functional), or unary, which is | ||
| 1891 | * passed to $(REF unaryFun, std,functional). In the binary form, two _range elements | ||
| 1892 | * $(D a) and $(D b) are considered equivalent if $(D pred(a,b)) is true. In | ||
| 1893 | * unary form, two elements are considered equivalent if $(D pred(a) == pred(b)) | ||
| 1894 | * is true. | ||
| 1895 | * | ||
| 1896 | * This predicate must be an equivalence relation, that is, it must be | ||
| 1897 | * reflexive ($(D pred(x,x)) is always true), symmetric | ||
| 1898 | * ($(D pred(x,y) == pred(y,x))), and transitive ($(D pred(x,y) && pred(y,z)) | ||
| 1899 | * implies $(D pred(x,z))). If this is not the case, the range returned by | ||
| 1900 | * chunkBy may assert at runtime or behave erratically. | ||
| 1901 | * | ||
| 1902 | * Params: | ||
| 1903 | * pred = Predicate for determining equivalence. | ||
| 1904 | * r = An $(REF_ALTTEXT input range, isInputRange, std,range,primitives) to be chunked. | ||
| 1905 | * | ||
| 1906 | * Returns: With a binary predicate, a range of ranges is returned in which | ||
| 1907 | * all elements in a given subrange are equivalent under the given predicate. | ||
| 1908 | * With a unary predicate, a range of tuples is returned, with the tuple | ||
| 1909 | * consisting of the result of the unary predicate for each subrange, and the | ||
| 1910 | * subrange itself. | ||
| 1911 | * | ||
| 1912 | * Notes: | ||
| 1913 | * | ||
| 1914 | * Equivalent elements separated by an intervening non-equivalent element will | ||
| 1915 | * appear in separate subranges; this function only considers adjacent | ||
| 1916 | * equivalence. Elements in the subranges will always appear in the same order | ||
| 1917 | * they appear in the original range. | ||
| 1918 | * | ||
| 1919 | * See_also: | ||
| 1920 | * $(LREF group), which collapses adjacent equivalent elements into a single | ||
| 1921 | * element. | ||
| 1922 | */ | ||
| 1923 | auto chunkBy(alias pred, Range)(Range r) | ||
| 1924 | if (isInputRange!Range) | ||
| 1925 | { | ||
| 1926 | return ChunkByImpl!(pred, Range)(r); | ||
| 1927 | } | ||
| 1928 | |||
| 1929 | /// Showing usage with binary predicate: | ||
| 1930 | /*FIXME: @safe*/ @system unittest | ||
| 1931 | { | ||
| 1932 | import std.algorithm.comparison : equal; | ||
| 1933 | |||
| 1934 | // Grouping by particular attribute of each element: | ||
| 1935 | auto data = [ | ||
| 1936 | [1, 1], | ||
| 1937 | [1, 2], | ||
| 1938 | [2, 2], | ||
| 1939 | [2, 3] | ||
| 1940 | ]; | ||
| 1941 | |||
| 1942 | auto r1 = data.chunkBy!((a,b) => a[0] == b[0]); | ||
| 1943 | assert(r1.equal!equal([ | ||
| 1944 | [[1, 1], [1, 2]], | ||
| 1945 | [[2, 2], [2, 3]] | ||
| 1946 | ])); | ||
| 1947 | |||
| 1948 | auto r2 = data.chunkBy!((a,b) => a[1] == b[1]); | ||
| 1949 | assert(r2.equal!equal([ | ||
| 1950 | [[1, 1]], | ||
| 1951 | [[1, 2], [2, 2]], | ||
| 1952 | [[2, 3]] | ||
| 1953 | ])); | ||
| 1954 | } | ||
| 1955 | |||
| 1956 | version (none) // this example requires support for non-equivalence relations | ||
| 1957 | @safe unittest | ||
| 1958 | { | ||
| 1959 | // Grouping by maximum adjacent difference: | ||
| 1960 | import std.math : abs; | ||
| 1961 | auto r3 = [1, 3, 2, 5, 4, 9, 10].chunkBy!((a, b) => abs(a-b) < 3); | ||
| 1962 | assert(r3.equal!equal([ | ||
| 1963 | [1, 3, 2], | ||
| 1964 | [5, 4], | ||
| 1965 | [9, 10] | ||
| 1966 | ])); | ||
| 1967 | |||
| 1968 | } | ||
| 1969 | |||
| 1970 | /// Showing usage with unary predicate: | ||
| 1971 | /* FIXME: pure @safe nothrow*/ @system unittest | ||
| 1972 | { | ||
| 1973 | import std.algorithm.comparison : equal; | ||
| 1974 | import std.range.primitives; | ||
| 1975 | import std.typecons : tuple; | ||
| 1976 | |||
| 1977 | // Grouping by particular attribute of each element: | ||
| 1978 | auto range = | ||
| 1979 | [ | ||
| 1980 | [1, 1], | ||
| 1981 | [1, 1], | ||
| 1982 | [1, 2], | ||
| 1983 | [2, 2], | ||
| 1984 | [2, 3], | ||
| 1985 | [2, 3], | ||
| 1986 | [3, 3] | ||
| 1987 | ]; | ||
| 1988 | |||
| 1989 | auto byX = chunkBy!(a => a[0])(range); | ||
| 1990 | auto expected1 = | ||
| 1991 | [ | ||
| 1992 | tuple(1, [[1, 1], [1, 1], [1, 2]]), | ||
| 1993 | tuple(2, [[2, 2], [2, 3], [2, 3]]), | ||
| 1994 | tuple(3, [[3, 3]]) | ||
| 1995 | ]; | ||
| 1996 | foreach (e; byX) | ||
| 1997 | { | ||
| 1998 | assert(!expected1.empty); | ||
| 1999 | assert(e[0] == expected1.front[0]); | ||
| 2000 | assert(e[1].equal(expected1.front[1])); | ||
| 2001 | expected1.popFront(); | ||
| 2002 | } | ||
| 2003 | |||
| 2004 | auto byY = chunkBy!(a => a[1])(range); | ||
| 2005 | auto expected2 = | ||
| 2006 | [ | ||
| 2007 | tuple(1, [[1, 1], [1, 1]]), | ||
| 2008 | tuple(2, [[1, 2], [2, 2]]), | ||
| 2009 | tuple(3, [[2, 3], [2, 3], [3, 3]]) | ||
| 2010 | ]; | ||
| 2011 | foreach (e; byY) | ||
| 2012 | { | ||
| 2013 | assert(!expected2.empty); | ||
| 2014 | assert(e[0] == expected2.front[0]); | ||
| 2015 | assert(e[1].equal(expected2.front[1])); | ||
| 2016 | expected2.popFront(); | ||
| 2017 | } | ||
| 2018 | } | ||
| 2019 | |||
| 2020 | /*FIXME: pure @safe nothrow*/ @system unittest | ||
| 2021 | { | ||
| 2022 | import std.algorithm.comparison : equal; | ||
| 2023 | import std.typecons : tuple; | ||
| 2024 | |||
| 2025 | struct Item { int x, y; } | ||
| 2026 | |||
| 2027 | // Force R to have only an input range API with reference semantics, so | ||
| 2028 | // that we're not unknowingly making use of array semantics outside of the | ||
| 2029 | // range API. | ||
| 2030 | class RefInputRange(R) | ||
| 2031 | { | ||
| 2032 | R data; | ||
| 2033 | this(R _data) pure @safe nothrow { data = _data; } | ||
| 2034 | @property bool empty() pure @safe nothrow { return data.empty; } | ||
| 2035 | @property auto front() pure @safe nothrow { return data.front; } | ||
| 2036 | void popFront() pure @safe nothrow { data.popFront(); } | ||
| 2037 | } | ||
| 2038 | auto refInputRange(R)(R range) { return new RefInputRange!R(range); } | ||
| 2039 | |||
| 2040 | { | ||
| 2041 | auto arr = [ Item(1,2), Item(1,3), Item(2,3) ]; | ||
| 2042 | static assert(isForwardRange!(typeof(arr))); | ||
| 2043 | |||
| 2044 | auto byX = chunkBy!(a => a.x)(arr); | ||
| 2045 | static assert(isForwardRange!(typeof(byX))); | ||
| 2046 | |||
| 2047 | auto byX_subrange1 = byX.front[1].save; | ||
| 2048 | auto byX_subrange2 = byX.front[1].save; | ||
| 2049 | static assert(isForwardRange!(typeof(byX_subrange1))); | ||
| 2050 | static assert(isForwardRange!(typeof(byX_subrange2))); | ||
| 2051 | |||
| 2052 | byX.popFront(); | ||
| 2053 | assert(byX_subrange1.equal([ Item(1,2), Item(1,3) ])); | ||
| 2054 | byX_subrange1.popFront(); | ||
| 2055 | assert(byX_subrange1.equal([ Item(1,3) ])); | ||
| 2056 | assert(byX_subrange2.equal([ Item(1,2), Item(1,3) ])); | ||
| 2057 | |||
| 2058 | auto byY = chunkBy!(a => a.y)(arr); | ||
| 2059 | static assert(isForwardRange!(typeof(byY))); | ||
| 2060 | |||
| 2061 | auto byY2 = byY.save; | ||
| 2062 | static assert(is(typeof(byY) == typeof(byY2))); | ||
| 2063 | byY.popFront(); | ||
| 2064 | assert(byY.front[0] == 3); | ||
| 2065 | assert(byY.front[1].equal([ Item(1,3), Item(2,3) ])); | ||
| 2066 | assert(byY2.front[0] == 2); | ||
| 2067 | assert(byY2.front[1].equal([ Item(1,2) ])); | ||
| 2068 | } | ||
| 2069 | |||
| 2070 | // Test non-forward input ranges. | ||
| 2071 | { | ||
| 2072 | auto range = refInputRange([ Item(1,1), Item(1,2), Item(2,2) ]); | ||
| 2073 | auto byX = chunkBy!(a => a.x)(range); | ||
| 2074 | assert(byX.front[0] == 1); | ||
| 2075 | assert(byX.front[1].equal([ Item(1,1), Item(1,2) ])); | ||
| 2076 | byX.popFront(); | ||
| 2077 | assert(byX.front[0] == 2); | ||
| 2078 | assert(byX.front[1].equal([ Item(2,2) ])); | ||
| 2079 | byX.popFront(); | ||
| 2080 | assert(byX.empty); | ||
| 2081 | assert(range.empty); | ||
| 2082 | |||
| 2083 | range = refInputRange([ Item(1,1), Item(1,2), Item(2,2) ]); | ||
| 2084 | auto byY = chunkBy!(a => a.y)(range); | ||
| 2085 | assert(byY.front[0] == 1); | ||
| 2086 | assert(byY.front[1].equal([ Item(1,1) ])); | ||
| 2087 | byY.popFront(); | ||
| 2088 | assert(byY.front[0] == 2); | ||
| 2089 | assert(byY.front[1].equal([ Item(1,2), Item(2,2) ])); | ||
| 2090 | byY.popFront(); | ||
| 2091 | assert(byY.empty); | ||
| 2092 | assert(range.empty); | ||
| 2093 | } | ||
| 2094 | } | ||
| 2095 | |||
| 2096 | // Issue 13595 | ||
| 2097 | version (none) // This requires support for non-equivalence relations | ||
| 2098 | @system unittest | ||
| 2099 | { | ||
| 2100 | import std.algorithm.comparison : equal; | ||
| 2101 | auto r = [1, 2, 3, 4, 5, 6, 7, 8, 9].chunkBy!((x, y) => ((x*y) % 3) == 0); | ||
| 2102 | assert(r.equal!equal([ | ||
| 2103 | [1], | ||
| 2104 | [2, 3, 4], | ||
| 2105 | [5, 6, 7], | ||
| 2106 | [8, 9] | ||
| 2107 | ])); | ||
| 2108 | } | ||
| 2109 | |||
| 2110 | // Issue 13805 | ||
| 2111 | @system unittest | ||
| 2112 | { | ||
| 2113 | [""].map!((s) => s).chunkBy!((x, y) => true); | ||
| 2114 | } | ||
| 2115 | |||
| 2116 | // joiner | ||
| 2117 | /** | ||
| 2118 | Lazily joins a range of ranges with a separator. The separator itself | ||
| 2119 | is a range. If a separator is not provided, then the ranges are | ||
| 2120 | joined directly without anything in between them (often called `flatten` | ||
| 2121 | in other languages). | ||
| 2122 | |||
| 2123 | Params: | ||
| 2124 | r = An $(REF_ALTTEXT input range, isInputRange, std,range,primitives) of input | ||
| 2125 | ranges to be joined. | ||
| 2126 | sep = A $(REF_ALTTEXT forward range, isForwardRange, std,range,primitives) of | ||
| 2127 | element(s) to serve as separators in the joined range. | ||
| 2128 | |||
| 2129 | Returns: | ||
| 2130 | A range of elements in the joined range. This will be a forward range if | ||
| 2131 | both outer and inner ranges of $(D RoR) are forward ranges; otherwise it will | ||
| 2132 | be only an input range. | ||
| 2133 | |||
| 2134 | See_also: | ||
| 2135 | $(REF chain, std,range), which chains a sequence of ranges with compatible elements | ||
| 2136 | into a single range. | ||
| 2137 | */ | ||
| 2138 | auto joiner(RoR, Separator)(RoR r, Separator sep) | ||
| 2139 | if (isInputRange!RoR && isInputRange!(ElementType!RoR) | ||
| 2140 | && isForwardRange!Separator | ||
| 2141 | && is(ElementType!Separator : ElementType!(ElementType!RoR))) | ||
| 2142 | { | ||
| 2143 | static struct Result | ||
| 2144 | { | ||
| 2145 | private RoR _items; | ||
| 2146 | private ElementType!RoR _current; | ||
| 2147 | private Separator _sep, _currentSep; | ||
| 2148 | |||
| 2149 | // This is a mixin instead of a function for the following reason (as | ||
| 2150 | // explained by Kenji Hara): "This is necessary from 2.061. If a | ||
| 2151 | // struct has a nested struct member, it must be directly initialized | ||
| 2152 | // in its constructor to avoid leaving undefined state. If you change | ||
| 2153 | // setItem to a function, the initialization of _current field is | ||
| 2154 | // wrapped into private member function, then compiler could not detect | ||
| 2155 | // that is correctly initialized while constructing. To avoid the | ||
| 2156 | // compiler error check, string mixin is used." | ||
| 2157 | private enum setItem = | ||
| 2158 | q{ | ||
| 2159 | if (!_items.empty) | ||
| 2160 | { | ||
| 2161 | // If we're exporting .save, we must not consume any of the | ||
| 2162 | // subranges, since RoR.save does not guarantee that the states | ||
| 2163 | // of the subranges are also saved. | ||
| 2164 | static if (isForwardRange!RoR && | ||
| 2165 | isForwardRange!(ElementType!RoR)) | ||
| 2166 | _current = _items.front.save; | ||
| 2167 | else | ||
| 2168 | _current = _items.front; | ||
| 2169 | } | ||
| 2170 | }; | ||
| 2171 | |||
| 2172 | private void useSeparator() | ||
| 2173 | { | ||
| 2174 | // Separator must always come after an item. | ||
| 2175 | assert(_currentSep.empty && !_items.empty, | ||
| 2176 | "joiner: internal error"); | ||
| 2177 | _items.popFront(); | ||
| 2178 | |||
| 2179 | // If there are no more items, we're done, since separators are not | ||
| 2180 | // terminators. | ||
| 2181 | if (_items.empty) return; | ||
| 2182 | |||
| 2183 | if (_sep.empty) | ||
| 2184 | { | ||
| 2185 | // Advance to the next range in the | ||
| 2186 | // input | ||
| 2187 | while (_items.front.empty) | ||
| 2188 | { | ||
| 2189 | _items.popFront(); | ||
| 2190 | if (_items.empty) return; | ||
| 2191 | } | ||
| 2192 | mixin(setItem); | ||
| 2193 | } | ||
| 2194 | else | ||
| 2195 | { | ||
| 2196 | _currentSep = _sep.save; | ||
| 2197 | assert(!_currentSep.empty); | ||
| 2198 | } | ||
| 2199 | } | ||
| 2200 | |||
| 2201 | private enum useItem = | ||
| 2202 | q{ | ||
| 2203 | // FIXME: this will crash if either _currentSep or _current are | ||
| 2204 | // class objects, because .init is null when the ctor invokes this | ||
| 2205 | // mixin. | ||
| 2206 | //assert(_currentSep.empty && _current.empty, | ||
| 2207 | // "joiner: internal error"); | ||
| 2208 | |||
| 2209 | // Use the input | ||
| 2210 | if (_items.empty) return; | ||
| 2211 | mixin(setItem); | ||
| 2212 | if (_current.empty) | ||
| 2213 | { | ||
| 2214 | // No data in the current item - toggle to use the separator | ||
| 2215 | useSeparator(); | ||
| 2216 | } | ||
| 2217 | }; | ||
| 2218 | |||
| 2219 | this(RoR items, Separator sep) | ||
| 2220 | { | ||
| 2221 | _items = items; | ||
| 2222 | _sep = sep; | ||
| 2223 | |||
| 2224 | //mixin(useItem); // _current should be initialized in place | ||
| 2225 | if (_items.empty) | ||
| 2226 | _current = _current.init; // set invalid state | ||
| 2227 | else | ||
| 2228 | { | ||
| 2229 | // If we're exporting .save, we must not consume any of the | ||
| 2230 | // subranges, since RoR.save does not guarantee that the states | ||
| 2231 | // of the subranges are also saved. | ||
| 2232 | static if (isForwardRange!RoR && | ||
| 2233 | isForwardRange!(ElementType!RoR)) | ||
| 2234 | _current = _items.front.save; | ||
| 2235 | else | ||
| 2236 | _current = _items.front; | ||
| 2237 | |||
| 2238 | if (_current.empty) | ||
| 2239 | { | ||
| 2240 | // No data in the current item - toggle to use the separator | ||
| 2241 | useSeparator(); | ||
| 2242 | } | ||
| 2243 | } | ||
| 2244 | } | ||
| 2245 | |||
| 2246 | @property auto empty() | ||
| 2247 | { | ||
| 2248 | return _items.empty; | ||
| 2249 | } | ||
| 2250 | |||
| 2251 | @property ElementType!(ElementType!RoR) front() | ||
| 2252 | { | ||
| 2253 | if (!_currentSep.empty) return _currentSep.front; | ||
| 2254 | assert(!_current.empty, "Attempting to fetch the front of an empty joiner."); | ||
| 2255 | return _current.front; | ||
| 2256 | } | ||
| 2257 | |||
| 2258 | void popFront() | ||
| 2259 | { | ||
| 2260 | assert(!_items.empty, "Attempting to popFront an empty joiner."); | ||
| 2261 | // Using separator? | ||
| 2262 | if (!_currentSep.empty) | ||
| 2263 | { | ||
| 2264 | _currentSep.popFront(); | ||
| 2265 | if (!_currentSep.empty) return; | ||
| 2266 | mixin(useItem); | ||
| 2267 | } | ||
| 2268 | else | ||
| 2269 | { | ||
| 2270 | // we're using the range | ||
| 2271 | _current.popFront(); | ||
| 2272 | if (!_current.empty) return; | ||
| 2273 | useSeparator(); | ||
| 2274 | } | ||
| 2275 | } | ||
| 2276 | |||
| 2277 | static if (isForwardRange!RoR && isForwardRange!(ElementType!RoR)) | ||
| 2278 | { | ||
| 2279 | @property auto save() | ||
| 2280 | { | ||
| 2281 | Result copy = this; | ||
| 2282 | copy._items = _items.save; | ||
| 2283 | copy._current = _current.save; | ||
| 2284 | copy._sep = _sep.save; | ||
| 2285 | copy._currentSep = _currentSep.save; | ||
| 2286 | return copy; | ||
| 2287 | } | ||
| 2288 | } | ||
| 2289 | } | ||
| 2290 | return Result(r, sep); | ||
| 2291 | } | ||
| 2292 | |||
| 2293 | /// | ||
| 2294 | @safe unittest | ||
| 2295 | { | ||
| 2296 | import std.algorithm.comparison : equal; | ||
| 2297 | import std.conv : text; | ||
| 2298 | |||
| 2299 | assert(["abc", "def"].joiner.equal("abcdef")); | ||
| 2300 | assert(["Mary", "has", "a", "little", "lamb"] | ||
| 2301 | .joiner("...") | ||
| 2302 | .equal("Mary...has...a...little...lamb")); | ||
| 2303 | assert(["", "abc"].joiner("xyz").equal("xyzabc")); | ||
| 2304 | assert([""].joiner("xyz").equal("")); | ||
| 2305 | assert(["", ""].joiner("xyz").equal("xyz")); | ||
| 2306 | } | ||
| 2307 | |||
| 2308 | @system unittest | ||
| 2309 | { | ||
| 2310 | import std.algorithm.comparison : equal; | ||
| 2311 | import std.range.interfaces; | ||
| 2312 | import std.range.primitives; | ||
| 2313 | // joiner() should work for non-forward ranges too. | ||
| 2314 | auto r = inputRangeObject(["abc", "def"]); | ||
| 2315 | assert(equal(joiner(r, "xyz"), "abcxyzdef")); | ||
| 2316 | } | ||
| 2317 | |||
| 2318 | @system unittest | ||
| 2319 | { | ||
| 2320 | import std.algorithm.comparison : equal; | ||
| 2321 | import std.range; | ||
| 2322 | |||
| 2323 | // Related to issue 8061 | ||
| 2324 | auto r = joiner([ | ||
| 2325 | inputRangeObject("abc"), | ||
| 2326 | inputRangeObject("def"), | ||
| 2327 | ], "-*-"); | ||
| 2328 | |||
| 2329 | assert(equal(r, "abc-*-def")); | ||
| 2330 | |||
| 2331 | // Test case where separator is specified but is empty. | ||
| 2332 | auto s = joiner([ | ||
| 2333 | inputRangeObject("abc"), | ||
| 2334 | inputRangeObject("def"), | ||
| 2335 | ], ""); | ||
| 2336 | |||
| 2337 | assert(equal(s, "abcdef")); | ||
| 2338 | |||
| 2339 | // Test empty separator with some empty elements | ||
| 2340 | auto t = joiner([ | ||
| 2341 | inputRangeObject("abc"), | ||
| 2342 | inputRangeObject(""), | ||
| 2343 | inputRangeObject("def"), | ||
| 2344 | inputRangeObject(""), | ||
| 2345 | ], ""); | ||
| 2346 | |||
| 2347 | assert(equal(t, "abcdef")); | ||
| 2348 | |||
| 2349 | // Test empty elements with non-empty separator | ||
| 2350 | auto u = joiner([ | ||
| 2351 | inputRangeObject(""), | ||
| 2352 | inputRangeObject("abc"), | ||
| 2353 | inputRangeObject(""), | ||
| 2354 | inputRangeObject("def"), | ||
| 2355 | inputRangeObject(""), | ||
| 2356 | ], "+-"); | ||
| 2357 | |||
| 2358 | assert(equal(u, "+-abc+-+-def+-")); | ||
| 2359 | |||
| 2360 | // Issue 13441: only(x) as separator | ||
| 2361 | string[][] lines = [null]; | ||
| 2362 | lines | ||
| 2363 | .joiner(only("b")) | ||
| 2364 | .array(); | ||
| 2365 | } | ||
| 2366 | |||
| 2367 | @safe unittest | ||
| 2368 | { | ||
| 2369 | import std.algorithm.comparison : equal; | ||
| 2370 | |||
| 2371 | // Transience correctness test | ||
| 2372 | struct TransientRange | ||
| 2373 | { | ||
| 2374 | @safe: | ||
| 2375 | int[][] src; | ||
| 2376 | int[] buf; | ||
| 2377 | |||
| 2378 | this(int[][] _src) | ||
| 2379 | { | ||
| 2380 | src = _src; | ||
| 2381 | buf.length = 100; | ||
| 2382 | } | ||
| 2383 | @property bool empty() { return src.empty; } | ||
| 2384 | @property int[] front() | ||
| 2385 | { | ||
| 2386 | assert(src.front.length <= buf.length); | ||
| 2387 | buf[0 .. src.front.length] = src.front[0..$]; | ||
| 2388 | return buf[0 .. src.front.length]; | ||
| 2389 | } | ||
| 2390 | void popFront() { src.popFront(); } | ||
| 2391 | } | ||
| 2392 | |||
| 2393 | // Test embedded empty elements | ||
| 2394 | auto tr1 = TransientRange([[], [1,2,3], [], [4]]); | ||
| 2395 | assert(equal(joiner(tr1, [0]), [0,1,2,3,0,0,4])); | ||
| 2396 | |||
| 2397 | // Test trailing empty elements | ||
| 2398 | auto tr2 = TransientRange([[], [1,2,3], []]); | ||
| 2399 | assert(equal(joiner(tr2, [0]), [0,1,2,3,0])); | ||
| 2400 | |||
| 2401 | // Test no empty elements | ||
| 2402 | auto tr3 = TransientRange([[1,2], [3,4]]); | ||
| 2403 | assert(equal(joiner(tr3, [0,1]), [1,2,0,1,3,4])); | ||
| 2404 | |||
| 2405 | // Test consecutive empty elements | ||
| 2406 | auto tr4 = TransientRange([[1,2], [], [], [], [3,4]]); | ||
| 2407 | assert(equal(joiner(tr4, [0,1]), [1,2,0,1,0,1,0,1,0,1,3,4])); | ||
| 2408 | |||
| 2409 | // Test consecutive trailing empty elements | ||
| 2410 | auto tr5 = TransientRange([[1,2], [3,4], [], []]); | ||
| 2411 | assert(equal(joiner(tr5, [0,1]), [1,2,0,1,3,4,0,1,0,1])); | ||
| 2412 | } | ||
| 2413 | |||
| 2414 | @safe unittest | ||
| 2415 | { | ||
| 2416 | static assert(isInputRange!(typeof(joiner([""], "")))); | ||
| 2417 | static assert(isForwardRange!(typeof(joiner([""], "")))); | ||
| 2418 | } | ||
| 2419 | |||
| 2420 | /// Ditto | ||
| 2421 | auto joiner(RoR)(RoR r) | ||
| 2422 | if (isInputRange!RoR && isInputRange!(ElementType!RoR)) | ||
| 2423 | { | ||
| 2424 | static struct Result | ||
| 2425 | { | ||
| 2426 | private: | ||
| 2427 | RoR _items; | ||
| 2428 | ElementType!RoR _current; | ||
| 2429 | enum prepare = | ||
| 2430 | q{ | ||
| 2431 | // Skip over empty subranges. | ||
| 2432 | if (_items.empty) return; | ||
| 2433 | while (_items.front.empty) | ||
| 2434 | { | ||
| 2435 | _items.popFront(); | ||
| 2436 | if (_items.empty) return; | ||
| 2437 | } | ||
| 2438 | // We cannot export .save method unless we ensure subranges are not | ||
| 2439 | // consumed when a .save'd copy of ourselves is iterated over. So | ||
| 2440 | // we need to .save each subrange we traverse. | ||
| 2441 | static if (isForwardRange!RoR && isForwardRange!(ElementType!RoR)) | ||
| 2442 | _current = _items.front.save; | ||
| 2443 | else | ||
| 2444 | _current = _items.front; | ||
| 2445 | }; | ||
| 2446 | this(RoR items, ElementType!RoR current) | ||
| 2447 | { | ||
| 2448 | _items = items; | ||
| 2449 | _current = current; | ||
| 2450 | } | ||
| 2451 | public: | ||
| 2452 | this(RoR r) | ||
| 2453 | { | ||
| 2454 | _items = r; | ||
| 2455 | //mixin(prepare); // _current should be initialized in place | ||
| 2456 | |||
| 2457 | // Skip over empty subranges. | ||
| 2458 | while (!_items.empty && _items.front.empty) | ||
| 2459 | _items.popFront(); | ||
| 2460 | |||
| 2461 | if (_items.empty) | ||
| 2462 | _current = _current.init; // set invalid state | ||
| 2463 | else | ||
| 2464 | { | ||
| 2465 | // We cannot export .save method unless we ensure subranges are not | ||
| 2466 | // consumed when a .save'd copy of ourselves is iterated over. So | ||
| 2467 | // we need to .save each subrange we traverse. | ||
| 2468 | static if (isForwardRange!RoR && isForwardRange!(ElementType!RoR)) | ||
| 2469 | _current = _items.front.save; | ||
| 2470 | else | ||
| 2471 | _current = _items.front; | ||
| 2472 | } | ||
| 2473 | } | ||
| 2474 | static if (isInfinite!RoR) | ||
| 2475 | { | ||
| 2476 | enum bool empty = false; | ||
| 2477 | } | ||
| 2478 | else | ||
| 2479 | { | ||
| 2480 | @property auto empty() | ||
| 2481 | { | ||
| 2482 | return _items.empty; | ||
| 2483 | } | ||
| 2484 | } | ||
| 2485 | @property auto ref front() | ||
| 2486 | { | ||
| 2487 | assert(!empty, "Attempting to fetch the front of an empty joiner."); | ||
| 2488 | return _current.front; | ||
| 2489 | } | ||
| 2490 | void popFront() | ||
| 2491 | { | ||
| 2492 | assert(!_current.empty, "Attempting to popFront an empty joiner."); | ||
| 2493 | _current.popFront(); | ||
| 2494 | if (_current.empty) | ||
| 2495 | { | ||
| 2496 | assert(!_items.empty); | ||
| 2497 | _items.popFront(); | ||
| 2498 | mixin(prepare); | ||
| 2499 | } | ||
| 2500 | } | ||
| 2501 | static if (isForwardRange!RoR && isForwardRange!(ElementType!RoR)) | ||
| 2502 | { | ||
| 2503 | @property auto save() | ||
| 2504 | { | ||
| 2505 | return Result(_items.save, _current.save); | ||
| 2506 | } | ||
| 2507 | } | ||
| 2508 | |||
| 2509 | static if (hasAssignableElements!(ElementType!RoR)) | ||
| 2510 | { | ||
| 2511 | @property void front(ElementType!(ElementType!RoR) element) | ||
| 2512 | { | ||
| 2513 | assert(!empty, "Attempting to assign to front of an empty joiner."); | ||
| 2514 | _current.front = element; | ||
| 2515 | } | ||
| 2516 | |||
| 2517 | @property void front(ref ElementType!(ElementType!RoR) element) | ||
| 2518 | { | ||
| 2519 | assert(!empty, "Attempting to assign to front of an empty joiner."); | ||
| 2520 | _current.front = element; | ||
| 2521 | } | ||
| 2522 | } | ||
| 2523 | } | ||
| 2524 | return Result(r); | ||
| 2525 | } | ||
| 2526 | |||
| 2527 | @safe unittest | ||
| 2528 | { | ||
| 2529 | import std.algorithm.comparison : equal; | ||
| 2530 | import std.range.interfaces : inputRangeObject; | ||
| 2531 | import std.range : repeat; | ||
| 2532 | |||
| 2533 | static assert(isInputRange!(typeof(joiner([""])))); | ||
| 2534 | static assert(isForwardRange!(typeof(joiner([""])))); | ||
| 2535 | assert(equal(joiner([""]), "")); | ||
| 2536 | assert(equal(joiner(["", ""]), "")); | ||
| 2537 | assert(equal(joiner(["", "abc"]), "abc")); | ||
| 2538 | assert(equal(joiner(["abc", ""]), "abc")); | ||
| 2539 | assert(equal(joiner(["abc", "def"]), "abcdef")); | ||
| 2540 | assert(equal(joiner(["Mary", "has", "a", "little", "lamb"]), | ||
| 2541 | "Maryhasalittlelamb")); | ||
| 2542 | assert(equal(joiner(repeat("abc", 3)), "abcabcabc")); | ||
| 2543 | |||
| 2544 | // joiner allows in-place mutation! | ||
| 2545 | auto a = [ [1, 2, 3], [42, 43] ]; | ||
| 2546 | auto j = joiner(a); | ||
| 2547 | j.front = 44; | ||
| 2548 | assert(a == [ [44, 2, 3], [42, 43] ]); | ||
| 2549 | assert(equal(j, [44, 2, 3, 42, 43])); | ||
| 2550 | } | ||
| 2551 | |||
| 2552 | |||
| 2553 | @system unittest | ||
| 2554 | { | ||
| 2555 | import std.algorithm.comparison : equal; | ||
| 2556 | import std.range.interfaces : inputRangeObject; | ||
| 2557 | |||
| 2558 | // bugzilla 8240 | ||
| 2559 | assert(equal(joiner([inputRangeObject("")]), "")); | ||
| 2560 | |||
| 2561 | // issue 8792 | ||
| 2562 | auto b = [[1], [2], [3]]; | ||
| 2563 | auto jb = joiner(b); | ||
| 2564 | auto js = jb.save; | ||
| 2565 | assert(equal(jb, js)); | ||
| 2566 | |||
| 2567 | auto js2 = jb.save; | ||
| 2568 | jb.popFront(); | ||
| 2569 | assert(!equal(jb, js)); | ||
| 2570 | assert(equal(js2, js)); | ||
| 2571 | js.popFront(); | ||
| 2572 | assert(equal(jb, js)); | ||
| 2573 | assert(!equal(js2, js)); | ||
| 2574 | } | ||
| 2575 | |||
| 2576 | @safe unittest | ||
| 2577 | { | ||
| 2578 | import std.algorithm.comparison : equal; | ||
| 2579 | |||
| 2580 | struct TransientRange | ||
| 2581 | { | ||
| 2582 | @safe: | ||
| 2583 | int[] _buf; | ||
| 2584 | int[][] _values; | ||
| 2585 | this(int[][] values) | ||
| 2586 | { | ||
| 2587 | _values = values; | ||
| 2588 | _buf = new int[128]; | ||
| 2589 | } | ||
| 2590 | @property bool empty() | ||
| 2591 | { | ||
| 2592 | return _values.length == 0; | ||
| 2593 | } | ||
| 2594 | @property auto front() | ||
| 2595 | { | ||
| 2596 | foreach (i; 0 .. _values.front.length) | ||
| 2597 | { | ||
| 2598 | _buf[i] = _values[0][i]; | ||
| 2599 | } | ||
| 2600 | return _buf[0 .. _values.front.length]; | ||
| 2601 | } | ||
| 2602 | void popFront() | ||
| 2603 | { | ||
| 2604 | _values = _values[1 .. $]; | ||
| 2605 | } | ||
| 2606 | } | ||
| 2607 | |||
| 2608 | auto rr = TransientRange([[1,2], [3,4,5], [], [6,7]]); | ||
| 2609 | |||
| 2610 | // Can't use array() or equal() directly because they fail with transient | ||
| 2611 | // .front. | ||
| 2612 | int[] result; | ||
| 2613 | foreach (c; rr.joiner()) | ||
| 2614 | { | ||
| 2615 | result ~= c; | ||
| 2616 | } | ||
| 2617 | |||
| 2618 | assert(equal(result, [1,2,3,4,5,6,7])); | ||
| 2619 | } | ||
| 2620 | |||
| 2621 | @safe unittest | ||
| 2622 | { | ||
| 2623 | import std.algorithm.comparison : equal; | ||
| 2624 | import std.algorithm.internal : algoFormat; | ||
| 2625 | |||
| 2626 | struct TransientRange | ||
| 2627 | { | ||
| 2628 | @safe: | ||
| 2629 | dchar[] _buf; | ||
| 2630 | dstring[] _values; | ||
| 2631 | this(dstring[] values) | ||
| 2632 | { | ||
| 2633 | _buf.length = 128; | ||
| 2634 | _values = values; | ||
| 2635 | } | ||
| 2636 | @property bool empty() | ||
| 2637 | { | ||
| 2638 | return _values.length == 0; | ||
| 2639 | } | ||
| 2640 | @property auto front() | ||
| 2641 | { | ||
| 2642 | foreach (i; 0 .. _values.front.length) | ||
| 2643 | { | ||
| 2644 | _buf[i] = _values[0][i]; | ||
| 2645 | } | ||
| 2646 | return _buf[0 .. _values.front.length]; | ||
| 2647 | } | ||
| 2648 | void popFront() | ||
| 2649 | { | ||
| 2650 | _values = _values[1 .. $]; | ||
| 2651 | } | ||
| 2652 | } | ||
| 2653 | |||
| 2654 | auto rr = TransientRange(["abc"d, "12"d, "def"d, "34"d]); | ||
| 2655 | |||
| 2656 | // Can't use array() or equal() directly because they fail with transient | ||
| 2657 | // .front. | ||
| 2658 | dchar[] result; | ||
| 2659 | foreach (c; rr.joiner()) | ||
| 2660 | { | ||
| 2661 | result ~= c; | ||
| 2662 | } | ||
| 2663 | |||
| 2664 | import std.conv : to; | ||
| 2665 | assert(equal(result, "abc12def34"d), | ||
| 2666 | //Convert to string for assert's message | ||
| 2667 | to!string("Unexpected result: '%s'"d.algoFormat(result))); | ||
| 2668 | } | ||
| 2669 | |||
| 2670 | // Issue 8061 | ||
| 2671 | @system unittest | ||
| 2672 | { | ||
| 2673 | import std.conv : to; | ||
| 2674 | import std.range.interfaces; | ||
| 2675 | |||
| 2676 | auto r = joiner([inputRangeObject("ab"), inputRangeObject("cd")]); | ||
| 2677 | assert(isForwardRange!(typeof(r))); | ||
| 2678 | |||
| 2679 | auto str = to!string(r); | ||
| 2680 | assert(str == "abcd"); | ||
| 2681 | } | ||
| 2682 | |||
| 2683 | @safe unittest | ||
| 2684 | { | ||
| 2685 | import std.range : repeat; | ||
| 2686 | |||
| 2687 | class AssignableRange | ||
| 2688 | { | ||
| 2689 | @safe: | ||
| 2690 | int element; | ||
| 2691 | @property int front() | ||
| 2692 | { | ||
| 2693 | return element; | ||
| 2694 | } | ||
| 2695 | |||
| 2696 | enum empty = false; | ||
| 2697 | |||
| 2698 | void popFront() | ||
| 2699 | { | ||
| 2700 | } | ||
| 2701 | |||
| 2702 | @property void front(int newValue) | ||
| 2703 | { | ||
| 2704 | element = newValue; | ||
| 2705 | } | ||
| 2706 | } | ||
| 2707 | |||
| 2708 | static assert(isInputRange!AssignableRange); | ||
| 2709 | static assert(is(ElementType!AssignableRange == int)); | ||
| 2710 | static assert(hasAssignableElements!AssignableRange); | ||
| 2711 | static assert(!hasLvalueElements!AssignableRange); | ||
| 2712 | |||
| 2713 | auto range = new AssignableRange(); | ||
| 2714 | assert(range.element == 0); | ||
| 2715 | |||
| 2716 | auto joined = joiner(repeat(range)); | ||
| 2717 | joined.front = 5; | ||
| 2718 | assert(range.element == 5); | ||
| 2719 | assert(joined.front == 5); | ||
| 2720 | |||
| 2721 | joined.popFront; | ||
| 2722 | int byRef = 7; | ||
| 2723 | joined.front = byRef; | ||
| 2724 | assert(range.element == byRef); | ||
| 2725 | assert(joined.front == byRef); | ||
| 2726 | } | ||
| 2727 | |||
| 2728 | /++ | ||
| 2729 | Implements the homonym function (also known as $(D accumulate), $(D | ||
| 2730 | compress), $(D inject), or $(D foldl)) present in various programming | ||
| 2731 | languages of functional flavor. There is also $(LREF fold) which does | ||
| 2732 | the same thing but with the opposite parameter order. | ||
| 2733 | The call $(D reduce!(fun)(seed, range)) first assigns $(D seed) to | ||
| 2734 | an internal variable $(D result), also called the accumulator. | ||
| 2735 | Then, for each element $(D x) in $(D range), $(D result = fun(result, x)) | ||
| 2736 | gets evaluated. Finally, $(D result) is returned. | ||
| 2737 | The one-argument version $(D reduce!(fun)(range)) | ||
| 2738 | works similarly, but it uses the first element of the range as the | ||
| 2739 | seed (the range must be non-empty). | ||
| 2740 | |||
| 2741 | Returns: | ||
| 2742 | the accumulated $(D result) | ||
| 2743 | |||
| 2744 | Params: | ||
| 2745 | fun = one or more functions | ||
| 2746 | |||
| 2747 | See_Also: | ||
| 2748 | $(HTTP en.wikipedia.org/wiki/Fold_(higher-order_function), Fold (higher-order function)) | ||
| 2749 | |||
| 2750 | $(LREF fold) is functionally equivalent to $(LREF reduce) with the argument order reversed, | ||
| 2751 | and without the need to use $(LREF tuple) for multiple seeds. This makes it easier | ||
| 2752 | to use in UFCS chains. | ||
| 2753 | |||
| 2754 | $(LREF sum) is similar to $(D reduce!((a, b) => a + b)) that offers | ||
| 2755 | pairwise summing of floating point numbers. | ||
| 2756 | +/ | ||
| 2757 | template reduce(fun...) | ||
| 2758 | if (fun.length >= 1) | ||
| 2759 | { | ||
| 2760 | import std.meta : staticMap; | ||
| 2761 | |||
| 2762 | alias binfuns = staticMap!(binaryFun, fun); | ||
| 2763 | static if (fun.length > 1) | ||
| 2764 | import std.typecons : tuple, isTuple; | ||
| 2765 | |||
| 2766 | /++ | ||
| 2767 | No-seed version. The first element of $(D r) is used as the seed's value. | ||
| 2768 | |||
| 2769 | For each function $(D f) in $(D fun), the corresponding | ||
| 2770 | seed type $(D S) is $(D Unqual!(typeof(f(e, e)))), where $(D e) is an | ||
| 2771 | element of $(D r): $(D ElementType!R) for ranges, | ||
| 2772 | and $(D ForeachType!R) otherwise. | ||
| 2773 | |||
| 2774 | Once S has been determined, then $(D S s = e;) and $(D s = f(s, e);) | ||
| 2775 | must both be legal. | ||
| 2776 | |||
| 2777 | If $(D r) is empty, an $(D Exception) is thrown. | ||
| 2778 | |||
| 2779 | Params: | ||
| 2780 | r = an iterable value as defined by $(D isIterable) | ||
| 2781 | |||
| 2782 | Returns: | ||
| 2783 | the final result of the accumulator applied to the iterable | ||
| 2784 | +/ | ||
| 2785 | auto reduce(R)(R r) | ||
| 2786 | if (isIterable!R) | ||
| 2787 | { | ||
| 2788 | import std.exception : enforce; | ||
| 2789 | alias E = Select!(isInputRange!R, ElementType!R, ForeachType!R); | ||
| 2790 | alias Args = staticMap!(ReduceSeedType!E, binfuns); | ||
| 2791 | |||
| 2792 | static if (isInputRange!R) | ||
| 2793 | { | ||
| 2794 | enforce(!r.empty, "Cannot reduce an empty input range w/o an explicit seed value."); | ||
| 2795 | Args result = r.front; | ||
| 2796 | r.popFront(); | ||
| 2797 | return reduceImpl!false(r, result); | ||
| 2798 | } | ||
| 2799 | else | ||
| 2800 | { | ||
| 2801 | auto result = Args.init; | ||
| 2802 | return reduceImpl!true(r, result); | ||
| 2803 | } | ||
| 2804 | } | ||
| 2805 | |||
| 2806 | /++ | ||
| 2807 | Seed version. The seed should be a single value if $(D fun) is a | ||
| 2808 | single function. If $(D fun) is multiple functions, then $(D seed) | ||
| 2809 | should be a $(REF Tuple, std,typecons), with one field per function in $(D f). | ||
| 2810 | |||
| 2811 | For convenience, if the seed is const, or has qualified fields, then | ||
| 2812 | $(D reduce) will operate on an unqualified copy. If this happens | ||
| 2813 | then the returned type will not perfectly match $(D S). | ||
| 2814 | |||
| 2815 | Use $(D fold) instead of $(D reduce) to use the seed version in a UFCS chain. | ||
| 2816 | |||
| 2817 | Params: | ||
| 2818 | seed = the initial value of the accumulator | ||
| 2819 | r = an iterable value as defined by $(D isIterable) | ||
| 2820 | |||
| 2821 | Returns: | ||
| 2822 | the final result of the accumulator applied to the iterable | ||
| 2823 | +/ | ||
| 2824 | auto reduce(S, R)(S seed, R r) | ||
| 2825 | if (isIterable!R) | ||
| 2826 | { | ||
| 2827 | static if (fun.length == 1) | ||
| 2828 | return reducePreImpl(r, seed); | ||
| 2829 | else | ||
| 2830 | { | ||
| 2831 | import std.algorithm.internal : algoFormat; | ||
| 2832 | static assert(isTuple!S, algoFormat("Seed %s should be a Tuple", S.stringof)); | ||
| 2833 | return reducePreImpl(r, seed.expand); | ||
| 2834 | } | ||
| 2835 | } | ||
| 2836 | |||
| 2837 | private auto reducePreImpl(R, Args...)(R r, ref Args args) | ||
| 2838 | { | ||
| 2839 | alias Result = staticMap!(Unqual, Args); | ||
| 2840 | static if (is(Result == Args)) | ||
| 2841 | alias result = args; | ||
| 2842 | else | ||
| 2843 | Result result = args; | ||
| 2844 | return reduceImpl!false(r, result); | ||
| 2845 | } | ||
| 2846 | |||
| 2847 | private auto reduceImpl(bool mustInitialize, R, Args...)(R r, ref Args args) | ||
| 2848 | if (isIterable!R) | ||
| 2849 | { | ||
| 2850 | import std.algorithm.internal : algoFormat; | ||
| 2851 | static assert(Args.length == fun.length, | ||
| 2852 | algoFormat("Seed %s does not have the correct amount of fields (should be %s)", Args.stringof, fun.length)); | ||
| 2853 | alias E = Select!(isInputRange!R, ElementType!R, ForeachType!R); | ||
| 2854 | |||
| 2855 | static if (mustInitialize) bool initialized = false; | ||
| 2856 | foreach (/+auto ref+/ E e; r) // @@@4707@@@ | ||
| 2857 | { | ||
| 2858 | foreach (i, f; binfuns) | ||
| 2859 | { | ||
| 2860 | static assert(!is(typeof(f(args[i], e))) || is(typeof(args[i] = f(args[i], e))), | ||
| 2861 | algoFormat( | ||
| 2862 | "Incompatible function/seed/element: %s/%s/%s", | ||
| 2863 | fullyQualifiedName!f, | ||
| 2864 | Args[i].stringof, | ||
| 2865 | E.stringof | ||
| 2866 | ) | ||
| 2867 | ); | ||
| 2868 | } | ||
| 2869 | |||
| 2870 | static if (mustInitialize) if (initialized == false) | ||
| 2871 | { | ||
| 2872 | import std.conv : emplaceRef; | ||
| 2873 | foreach (i, f; binfuns) | ||
| 2874 | emplaceRef!(Args[i])(args[i], e); | ||
| 2875 | initialized = true; | ||
| 2876 | continue; | ||
| 2877 | } | ||
| 2878 | |||
| 2879 | foreach (i, f; binfuns) | ||
| 2880 | args[i] = f(args[i], e); | ||
| 2881 | } | ||
| 2882 | static if (mustInitialize) | ||
| 2883 | if (!initialized) | ||
| 2884 | throw new Exception("Cannot reduce an empty iterable w/o an explicit seed value."); | ||
| 2885 | |||
| 2886 | static if (Args.length == 1) | ||
| 2887 | return args[0]; | ||
| 2888 | else | ||
| 2889 | return tuple(args); | ||
| 2890 | } | ||
| 2891 | } | ||
| 2892 | |||
| 2893 | /** | ||
| 2894 | Many aggregate range operations turn out to be solved with $(D reduce) | ||
| 2895 | quickly and easily. The example below illustrates $(D reduce)'s | ||
| 2896 | remarkable power and flexibility. | ||
| 2897 | */ | ||
| 2898 | @safe unittest | ||
| 2899 | { | ||
| 2900 | import std.algorithm.comparison : max, min; | ||
| 2901 | import std.math : approxEqual; | ||
| 2902 | import std.range; | ||
| 2903 | |||
| 2904 | int[] arr = [ 1, 2, 3, 4, 5 ]; | ||
| 2905 | // Sum all elements | ||
| 2906 | auto sum = reduce!((a,b) => a + b)(0, arr); | ||
| 2907 | assert(sum == 15); | ||
| 2908 | |||
| 2909 | // Sum again, using a string predicate with "a" and "b" | ||
| 2910 | sum = reduce!"a + b"(0, arr); | ||
| 2911 | assert(sum == 15); | ||
| 2912 | |||
| 2913 | // Compute the maximum of all elements | ||
| 2914 | auto largest = reduce!(max)(arr); | ||
| 2915 | assert(largest == 5); | ||
| 2916 | |||
| 2917 | // Max again, but with Uniform Function Call Syntax (UFCS) | ||
| 2918 | largest = arr.reduce!(max); | ||
| 2919 | assert(largest == 5); | ||
| 2920 | |||
| 2921 | // Compute the number of odd elements | ||
| 2922 | auto odds = reduce!((a,b) => a + (b & 1))(0, arr); | ||
| 2923 | assert(odds == 3); | ||
| 2924 | |||
| 2925 | // Compute the sum of squares | ||
| 2926 | auto ssquares = reduce!((a,b) => a + b * b)(0, arr); | ||
| 2927 | assert(ssquares == 55); | ||
| 2928 | |||
| 2929 | // Chain multiple ranges into seed | ||
| 2930 | int[] a = [ 3, 4 ]; | ||
| 2931 | int[] b = [ 100 ]; | ||
| 2932 | auto r = reduce!("a + b")(chain(a, b)); | ||
| 2933 | assert(r == 107); | ||
| 2934 | |||
| 2935 | // Mixing convertible types is fair game, too | ||
| 2936 | double[] c = [ 2.5, 3.0 ]; | ||
| 2937 | auto r1 = reduce!("a + b")(chain(a, b, c)); | ||
| 2938 | assert(approxEqual(r1, 112.5)); | ||
| 2939 | |||
| 2940 | // To minimize nesting of parentheses, Uniform Function Call Syntax can be used | ||
| 2941 | auto r2 = chain(a, b, c).reduce!("a + b"); | ||
| 2942 | assert(approxEqual(r2, 112.5)); | ||
| 2943 | } | ||
| 2944 | |||
| 2945 | /** | ||
| 2946 | Sometimes it is very useful to compute multiple aggregates in one pass. | ||
| 2947 | One advantage is that the computation is faster because the looping overhead | ||
| 2948 | is shared. That's why $(D reduce) accepts multiple functions. | ||
| 2949 | If two or more functions are passed, $(D reduce) returns a | ||
| 2950 | $(REF Tuple, std,typecons) object with one member per passed-in function. | ||
| 2951 | The number of seeds must be correspondingly increased. | ||
| 2952 | */ | ||
| 2953 | @safe unittest | ||
| 2954 | { | ||
| 2955 | import std.algorithm.comparison : max, min; | ||
| 2956 | import std.math : approxEqual, sqrt; | ||
| 2957 | import std.typecons : tuple, Tuple; | ||
| 2958 | |||
| 2959 | double[] a = [ 3.0, 4, 7, 11, 3, 2, 5 ]; | ||
| 2960 | // Compute minimum and maximum in one pass | ||
| 2961 | auto r = reduce!(min, max)(a); | ||
| 2962 | // The type of r is Tuple!(int, int) | ||
| 2963 | assert(approxEqual(r[0], 2)); // minimum | ||
| 2964 | assert(approxEqual(r[1], 11)); // maximum | ||
| 2965 | |||
| 2966 | // Compute sum and sum of squares in one pass | ||
| 2967 | r = reduce!("a + b", "a + b * b")(tuple(0.0, 0.0), a); | ||
| 2968 | assert(approxEqual(r[0], 35)); // sum | ||
| 2969 | assert(approxEqual(r[1], 233)); // sum of squares | ||
| 2970 | // Compute average and standard deviation from the above | ||
| 2971 | auto avg = r[0] / a.length; | ||
| 2972 | assert(avg == 5); | ||
| 2973 | auto stdev = sqrt(r[1] / a.length - avg * avg); | ||
| 2974 | assert(cast(int) stdev == 2); | ||
| 2975 | } | ||
| 2976 | |||
| 2977 | @safe unittest | ||
| 2978 | { | ||
| 2979 | import std.algorithm.comparison : max, min; | ||
| 2980 | import std.range : chain; | ||
| 2981 | import std.typecons : tuple, Tuple; | ||
| 2982 | |||
| 2983 | double[] a = [ 3, 4 ]; | ||
| 2984 | auto r = reduce!("a + b")(0.0, a); | ||
| 2985 | assert(r == 7); | ||
| 2986 | r = reduce!("a + b")(a); | ||
| 2987 | assert(r == 7); | ||
| 2988 | r = reduce!(min)(a); | ||
| 2989 | assert(r == 3); | ||
| 2990 | double[] b = [ 100 ]; | ||
| 2991 | auto r1 = reduce!("a + b")(chain(a, b)); | ||
| 2992 | assert(r1 == 107); | ||
| 2993 | |||
| 2994 | // two funs | ||
| 2995 | auto r2 = reduce!("a + b", "a - b")(tuple(0.0, 0.0), a); | ||
| 2996 | assert(r2[0] == 7 && r2[1] == -7); | ||
| 2997 | auto r3 = reduce!("a + b", "a - b")(a); | ||
| 2998 | assert(r3[0] == 7 && r3[1] == -1); | ||
| 2999 | |||
| 3000 | a = [ 1, 2, 3, 4, 5 ]; | ||
| 3001 | // Stringize with commas | ||
| 3002 | string rep = reduce!("a ~ `, ` ~ to!(string)(b)")("", a); | ||
| 3003 | assert(rep[2 .. $] == "1, 2, 3, 4, 5", "["~rep[2 .. $]~"]"); | ||
| 3004 | } | ||
| 3005 | |||
| 3006 | @system unittest | ||
| 3007 | { | ||
| 3008 | import std.algorithm.comparison : max, min; | ||
| 3009 | import std.exception : assertThrown; | ||
| 3010 | import std.range : iota; | ||
| 3011 | import std.typecons : tuple, Tuple; | ||
| 3012 | |||
| 3013 | // Test the opApply case. | ||
| 3014 | static struct OpApply | ||
| 3015 | { | ||
| 3016 | bool actEmpty; | ||
| 3017 | |||
| 3018 | int opApply(scope int delegate(ref int) dg) | ||
| 3019 | { | ||
| 3020 | int res; | ||
| 3021 | if (actEmpty) return res; | ||
| 3022 | |||
| 3023 | foreach (i; 0 .. 100) | ||
| 3024 | { | ||
| 3025 | res = dg(i); | ||
| 3026 | if (res) break; | ||
| 3027 | } | ||
| 3028 | return res; | ||
| 3029 | } | ||
| 3030 | } | ||
| 3031 | |||
| 3032 | OpApply oa; | ||
| 3033 | auto hundredSum = reduce!"a + b"(iota(100)); | ||
| 3034 | assert(reduce!"a + b"(5, oa) == hundredSum + 5); | ||
| 3035 | assert(reduce!"a + b"(oa) == hundredSum); | ||
| 3036 | assert(reduce!("a + b", max)(oa) == tuple(hundredSum, 99)); | ||
| 3037 | assert(reduce!("a + b", max)(tuple(5, 0), oa) == tuple(hundredSum + 5, 99)); | ||
| 3038 | |||
| 3039 | // Test for throwing on empty range plus no seed. | ||
| 3040 | assertThrown(reduce!"a + b"([1, 2][0 .. 0])); | ||
| 3041 | |||
| 3042 | oa.actEmpty = true; | ||
| 3043 | assertThrown(reduce!"a + b"(oa)); | ||
| 3044 | } | ||
| 3045 | |||
| 3046 | @safe unittest | ||
| 3047 | { | ||
| 3048 | const float a = 0.0; | ||
| 3049 | const float[] b = [ 1.2, 3, 3.3 ]; | ||
| 3050 | float[] c = [ 1.2, 3, 3.3 ]; | ||
| 3051 | auto r = reduce!"a + b"(a, b); | ||
| 3052 | r = reduce!"a + b"(a, c); | ||
| 3053 | assert(r == 7.5); | ||
| 3054 | } | ||
| 3055 | |||
| 3056 | @safe unittest | ||
| 3057 | { | ||
| 3058 | // Issue #10408 - Two-function reduce of a const array. | ||
| 3059 | import std.algorithm.comparison : max, min; | ||
| 3060 | import std.typecons : tuple, Tuple; | ||
| 3061 | |||
| 3062 | const numbers = [10, 30, 20]; | ||
| 3063 | immutable m = reduce!(min)(numbers); | ||
| 3064 | assert(m == 10); | ||
| 3065 | immutable minmax = reduce!(min, max)(numbers); | ||
| 3066 | assert(minmax == tuple(10, 30)); | ||
| 3067 | } | ||
| 3068 | |||
| 3069 | @safe unittest | ||
| 3070 | { | ||
| 3071 | //10709 | ||
| 3072 | import std.typecons : tuple, Tuple; | ||
| 3073 | |||
| 3074 | enum foo = "a + 0.5 * b"; | ||
| 3075 | auto r = [0, 1, 2, 3]; | ||
| 3076 | auto r1 = reduce!foo(r); | ||
| 3077 | auto r2 = reduce!(foo, foo)(r); | ||
| 3078 | assert(r1 == 3); | ||
| 3079 | assert(r2 == tuple(3, 3)); | ||
| 3080 | } | ||
| 3081 | |||
| 3082 | @system unittest | ||
| 3083 | { | ||
| 3084 | static struct OpApply | ||
| 3085 | { | ||
| 3086 | int opApply(int delegate(ref int) dg) | ||
| 3087 | { | ||
| 3088 | int[] a = [1, 2, 3]; | ||
| 3089 | |||
| 3090 | int res = 0; | ||
| 3091 | foreach (ref e; a) | ||
| 3092 | { | ||
| 3093 | res = dg(e); | ||
| 3094 | if (res) break; | ||
| 3095 | } | ||
| 3096 | return res; | ||
| 3097 | } | ||
| 3098 | } | ||
| 3099 | //test CTFE and functions with context | ||
| 3100 | int fun(int a, int b) @safe {return a + b + 1;} | ||
| 3101 | auto foo() | ||
| 3102 | { | ||
| 3103 | import std.algorithm.comparison : max; | ||
| 3104 | import std.typecons : tuple, Tuple; | ||
| 3105 | |||
| 3106 | auto a = reduce!(fun)([1, 2, 3]); | ||
| 3107 | auto b = reduce!(fun, fun)([1, 2, 3]); | ||
| 3108 | auto c = reduce!(fun)(0, [1, 2, 3]); | ||
| 3109 | auto d = reduce!(fun, fun)(tuple(0, 0), [1, 2, 3]); | ||
| 3110 | auto e = reduce!(fun)(0, OpApply()); | ||
| 3111 | auto f = reduce!(fun, fun)(tuple(0, 0), OpApply()); | ||
| 3112 | |||
| 3113 | return max(a, b.expand, c, d.expand, e, f.expand); | ||
| 3114 | } | ||
| 3115 | auto a = foo(); | ||
| 3116 | assert(a == 9); | ||
| 3117 | enum b = foo(); | ||
| 3118 | assert(b == 9); | ||
| 3119 | } | ||
| 3120 | |||
| 3121 | @safe unittest | ||
| 3122 | { | ||
| 3123 | import std.algorithm.comparison : max, min; | ||
| 3124 | import std.typecons : tuple, Tuple; | ||
| 3125 | |||
| 3126 | //http://forum.dlang.org/post/oghtttkopzjshsuflelk@forum.dlang.org | ||
| 3127 | //Seed is tuple of const. | ||
| 3128 | static auto minmaxElement(alias F = min, alias G = max, R)(in R range) | ||
| 3129 | @safe pure nothrow | ||
| 3130 | if (isInputRange!R) | ||
| 3131 | { | ||
| 3132 | return reduce!(F, G)(tuple(ElementType!R.max, | ||
| 3133 | ElementType!R.min), range); | ||
| 3134 | } | ||
| 3135 | assert(minmaxElement([1, 2, 3]) == tuple(1, 3)); | ||
| 3136 | } | ||
| 3137 | |||
| 3138 | @safe unittest //12569 | ||
| 3139 | { | ||
| 3140 | import std.algorithm.comparison : max, min; | ||
| 3141 | import std.typecons : tuple; | ||
| 3142 | dchar c = 'a'; | ||
| 3143 | reduce!(min, max)(tuple(c, c), "hello"); // OK | ||
| 3144 | static assert(!is(typeof(reduce!(min, max)(tuple(c), "hello")))); | ||
| 3145 | static assert(!is(typeof(reduce!(min, max)(tuple(c, c, c), "hello")))); | ||
| 3146 | |||
| 3147 | |||
| 3148 | //"Seed dchar should be a Tuple" | ||
| 3149 | static assert(!is(typeof(reduce!(min, max)(c, "hello")))); | ||
| 3150 | //"Seed (dchar) does not have the correct amount of fields (should be 2)" | ||
| 3151 | static assert(!is(typeof(reduce!(min, max)(tuple(c), "hello")))); | ||
| 3152 | //"Seed (dchar, dchar, dchar) does not have the correct amount of fields (should be 2)" | ||
| 3153 | static assert(!is(typeof(reduce!(min, max)(tuple(c, c, c), "hello")))); | ||
| 3154 | //"Incompatable function/seed/element: all(alias pred = "a")/int/dchar" | ||
| 3155 | static assert(!is(typeof(reduce!all(1, "hello")))); | ||
| 3156 | static assert(!is(typeof(reduce!(all, all)(tuple(1, 1), "hello")))); | ||
| 3157 | } | ||
| 3158 | |||
| 3159 | @safe unittest //13304 | ||
| 3160 | { | ||
| 3161 | int[] data; | ||
| 3162 | static assert(is(typeof(reduce!((a, b) => a + b)(data)))); | ||
| 3163 | assert(data.length == 0); | ||
| 3164 | } | ||
| 3165 | |||
| 3166 | //Helper for Reduce | ||
| 3167 | private template ReduceSeedType(E) | ||
| 3168 | { | ||
| 3169 | static template ReduceSeedType(alias fun) | ||
| 3170 | { | ||
| 3171 | import std.algorithm.internal : algoFormat; | ||
| 3172 | |||
| 3173 | alias ReduceSeedType = Unqual!(typeof(fun(lvalueOf!E, lvalueOf!E))); | ||
| 3174 | |||
| 3175 | //Check the Seed type is useable. | ||
| 3176 | ReduceSeedType s = ReduceSeedType.init; | ||
| 3177 | static assert(is(typeof({ReduceSeedType s = lvalueOf!E;})) && | ||
| 3178 | is(typeof(lvalueOf!ReduceSeedType = fun(lvalueOf!ReduceSeedType, lvalueOf!E))), | ||
| 3179 | algoFormat( | ||
| 3180 | "Unable to deduce an acceptable seed type for %s with element type %s.", | ||
| 3181 | fullyQualifiedName!fun, | ||
| 3182 | E.stringof | ||
| 3183 | ) | ||
| 3184 | ); | ||
| 3185 | } | ||
| 3186 | } | ||
| 3187 | |||
| 3188 | |||
| 3189 | /++ | ||
| 3190 | Implements the homonym function (also known as $(D accumulate), $(D | ||
| 3191 | compress), $(D inject), or $(D foldl)) present in various programming | ||
| 3192 | languages of functional flavor. The call $(D fold!(fun)(range, seed)) | ||
| 3193 | first assigns $(D seed) to an internal variable $(D result), | ||
| 3194 | also called the accumulator. Then, for each element $(D x) in $(D | ||
| 3195 | range), $(D result = fun(result, x)) gets evaluated. Finally, $(D | ||
| 3196 | result) is returned. The one-argument version $(D fold!(fun)(range)) | ||
| 3197 | works similarly, but it uses the first element of the range as the | ||
| 3198 | seed (the range must be non-empty). | ||
| 3199 | |||
| 3200 | Returns: | ||
| 3201 | the accumulated $(D result) | ||
| 3202 | |||
| 3203 | See_Also: | ||
| 3204 | $(HTTP en.wikipedia.org/wiki/Fold_(higher-order_function), Fold (higher-order function)) | ||
| 3205 | |||
| 3206 | $(LREF sum) is similar to $(D fold!((a, b) => a + b)) that offers | ||
| 3207 | precise summing of floating point numbers. | ||
| 3208 | |||
| 3209 | This is functionally equivalent to $(LREF reduce) with the argument order reversed, | ||
| 3210 | and without the need to use $(LREF tuple) for multiple seeds. | ||
| 3211 | +/ | ||
| 3212 | template fold(fun...) | ||
| 3213 | if (fun.length >= 1) | ||
| 3214 | { | ||
| 3215 | auto fold(R, S...)(R r, S seed) | ||
| 3216 | { | ||
| 3217 | static if (S.length < 2) | ||
| 3218 | { | ||
| 3219 | return reduce!fun(seed, r); | ||
| 3220 | } | ||
| 3221 | else | ||
| 3222 | { | ||
| 3223 | import std.typecons : tuple; | ||
| 3224 | return reduce!fun(tuple(seed), r); | ||
| 3225 | } | ||
| 3226 | } | ||
| 3227 | } | ||
| 3228 | |||
| 3229 | /// | ||
| 3230 | @safe pure unittest | ||
| 3231 | { | ||
| 3232 | immutable arr = [1, 2, 3, 4, 5]; | ||
| 3233 | |||
| 3234 | // Sum all elements | ||
| 3235 | assert(arr.fold!((a, b) => a + b) == 15); | ||
| 3236 | |||
| 3237 | // Sum all elements with explicit seed | ||
| 3238 | assert(arr.fold!((a, b) => a + b)(6) == 21); | ||
| 3239 | |||
| 3240 | import std.algorithm.comparison : min, max; | ||
| 3241 | import std.typecons : tuple; | ||
| 3242 | |||
| 3243 | // Compute minimum and maximum at the same time | ||
| 3244 | assert(arr.fold!(min, max) == tuple(1, 5)); | ||
| 3245 | |||
| 3246 | // Compute minimum and maximum at the same time with seeds | ||
| 3247 | assert(arr.fold!(min, max)(0, 7) == tuple(0, 7)); | ||
| 3248 | |||
| 3249 | // Can be used in a UFCS chain | ||
| 3250 | assert(arr.map!(a => a + 1).fold!((a, b) => a + b) == 20); | ||
| 3251 | |||
| 3252 | // Return the last element of any range | ||
| 3253 | assert(arr.fold!((a, b) => b) == 5); | ||
| 3254 | } | ||
| 3255 | |||
| 3256 | @safe @nogc pure nothrow unittest | ||
| 3257 | { | ||
| 3258 | int[1] arr; | ||
| 3259 | static assert(!is(typeof(arr.fold!()))); | ||
| 3260 | static assert(!is(typeof(arr.fold!(a => a)))); | ||
| 3261 | static assert(is(typeof(arr.fold!((a, b) => a)))); | ||
| 3262 | static assert(is(typeof(arr.fold!((a, b) => a)(1)))); | ||
| 3263 | assert(arr.length == 1); | ||
| 3264 | } | ||
| 3265 | |||
| 3266 | /++ | ||
| 3267 | Similar to `fold`, but returns a range containing the successive reduced values. | ||
| 3268 | The call $(D cumulativeFold!(fun)(range, seed)) first assigns `seed` to an | ||
| 3269 | internal variable `result`, also called the accumulator. | ||
| 3270 | The returned range contains the values $(D result = fun(result, x)) lazily | ||
| 3271 | evaluated for each element `x` in `range`. Finally, the last element has the | ||
| 3272 | same value as $(D fold!(fun)(seed, range)). | ||
| 3273 | The one-argument version $(D cumulativeFold!(fun)(range)) works similarly, but | ||
| 3274 | it returns the first element unchanged and uses it as seed for the next | ||
| 3275 | elements. | ||
| 3276 | This function is also known as | ||
| 3277 | $(HTTP en.cppreference.com/w/cpp/algorithm/partial_sum, partial_sum), | ||
| 3278 | $(HTTP docs.python.org/3/library/itertools.html#itertools.accumulate, accumulate), | ||
| 3279 | $(HTTP hackage.haskell.org/package/base-4.8.2.0/docs/Prelude.html#v:scanl, scan), | ||
| 3280 | $(HTTP mathworld.wolfram.com/CumulativeSum.html, Cumulative Sum). | ||
| 3281 | |||
| 3282 | Params: | ||
| 3283 | fun = one or more functions to use as fold operation | ||
| 3284 | |||
| 3285 | Returns: | ||
| 3286 | The function returns a range containing the consecutive reduced values. If | ||
| 3287 | there is more than one `fun`, the element type will be $(REF Tuple, | ||
| 3288 | std,typecons) containing one element for each `fun`. | ||
| 3289 | |||
| 3290 | See_Also: | ||
| 3291 | $(HTTP en.wikipedia.org/wiki/Prefix_sum, Prefix Sum) | ||
| 3292 | +/ | ||
| 3293 | template cumulativeFold(fun...) | ||
| 3294 | if (fun.length >= 1) | ||
| 3295 | { | ||
| 3296 | import std.meta : staticMap; | ||
| 3297 | private alias binfuns = staticMap!(binaryFun, fun); | ||
| 3298 | |||
| 3299 | /++ | ||
| 3300 | No-seed version. The first element of `r` is used as the seed's value. | ||
| 3301 | For each function `f` in `fun`, the corresponding seed type `S` is | ||
| 3302 | $(D Unqual!(typeof(f(e, e)))), where `e` is an element of `r`: | ||
| 3303 | `ElementType!R`. | ||
| 3304 | Once `S` has been determined, then $(D S s = e;) and $(D s = f(s, e);) must | ||
| 3305 | both be legal. | ||
| 3306 | |||
| 3307 | Params: | ||
| 3308 | range = An $(REF_ALTTEXT input range, isInputRange, std,range,primitives) | ||
| 3309 | Returns: | ||
| 3310 | a range containing the consecutive reduced values. | ||
| 3311 | +/ | ||
| 3312 | auto cumulativeFold(R)(R range) | ||
| 3313 | if (isInputRange!(Unqual!R)) | ||
| 3314 | { | ||
| 3315 | return cumulativeFoldImpl(range); | ||
| 3316 | } | ||
| 3317 | |||
| 3318 | /++ | ||
| 3319 | Seed version. The seed should be a single value if `fun` is a single | ||
| 3320 | function. If `fun` is multiple functions, then `seed` should be a | ||
| 3321 | $(REF Tuple, std,typecons), with one field per function in `f`. | ||
| 3322 | For convenience, if the seed is `const`, or has qualified fields, then | ||
| 3323 | `cumulativeFold` will operate on an unqualified copy. If this happens | ||
| 3324 | then the returned type will not perfectly match `S`. | ||
| 3325 | |||
| 3326 | Params: | ||
| 3327 | range = An $(REF_ALTTEXT input range, isInputRange, std,range,primitives) | ||
| 3328 | seed = the initial value of the accumulator | ||
| 3329 | Returns: | ||
| 3330 | a range containing the consecutive reduced values. | ||
| 3331 | +/ | ||
| 3332 | auto cumulativeFold(R, S)(R range, S seed) | ||
| 3333 | if (isInputRange!(Unqual!R)) | ||
| 3334 | { | ||
| 3335 | static if (fun.length == 1) | ||
| 3336 | return cumulativeFoldImpl(range, seed); | ||
| 3337 | else | ||
| 3338 | return cumulativeFoldImpl(range, seed.expand); | ||
| 3339 | } | ||
| 3340 | |||
| 3341 | private auto cumulativeFoldImpl(R, Args...)(R range, ref Args args) | ||
| 3342 | { | ||
| 3343 | import std.algorithm.internal : algoFormat; | ||
| 3344 | |||
| 3345 | static assert(Args.length == 0 || Args.length == fun.length, | ||
| 3346 | algoFormat("Seed %s does not have the correct amount of fields (should be %s)", | ||
| 3347 | Args.stringof, fun.length)); | ||
| 3348 | |||
| 3349 | static if (args.length) | ||
| 3350 | alias State = staticMap!(Unqual, Args); | ||
| 3351 | else | ||
| 3352 | alias State = staticMap!(ReduceSeedType!(ElementType!R), binfuns); | ||
| 3353 | |||
| 3354 | foreach (i, f; binfuns) | ||
| 3355 | { | ||
| 3356 | static assert(!__traits(compiles, f(args[i], e)) || __traits(compiles, | ||
| 3357 | { args[i] = f(args[i], e); }()), | ||
| 3358 | algoFormat("Incompatible function/seed/element: %s/%s/%s", | ||
| 3359 | fullyQualifiedName!f, Args[i].stringof, E.stringof)); | ||
| 3360 | } | ||
| 3361 | |||
| 3362 | static struct Result | ||
| 3363 | { | ||
| 3364 | private: | ||
| 3365 | R source; | ||
| 3366 | State state; | ||
| 3367 | |||
| 3368 | this(R range, ref Args args) | ||
| 3369 | { | ||
| 3370 | source = range; | ||
| 3371 | if (source.empty) | ||
| 3372 | return; | ||
| 3373 | |||
| 3374 | foreach (i, f; binfuns) | ||
| 3375 | { | ||
| 3376 | static if (args.length) | ||
| 3377 | state[i] = f(args[i], source.front); | ||
| 3378 | else | ||
| 3379 | state[i] = source.front; | ||
| 3380 | } | ||
| 3381 | } | ||
| 3382 | |||
| 3383 | public: | ||
| 3384 | @property bool empty() | ||
| 3385 | { | ||
| 3386 | return source.empty; | ||
| 3387 | } | ||
| 3388 | |||
| 3389 | @property auto front() | ||
| 3390 | { | ||
| 3391 | assert(!empty, "Attempting to fetch the front of an empty cumulativeFold."); | ||
| 3392 | static if (fun.length > 1) | ||
| 3393 | { | ||
| 3394 | import std.typecons : tuple; | ||
| 3395 | return tuple(state); | ||
| 3396 | } | ||
| 3397 | else | ||
| 3398 | { | ||
| 3399 | return state[0]; | ||
| 3400 | } | ||
| 3401 | } | ||
| 3402 | |||
| 3403 | void popFront() | ||
| 3404 | { | ||
| 3405 | assert(!empty, "Attempting to popFront an empty cumulativeFold."); | ||
| 3406 | source.popFront; | ||
| 3407 | |||
| 3408 | if (source.empty) | ||
| 3409 | return; | ||
| 3410 | |||
| 3411 | foreach (i, f; binfuns) | ||
| 3412 | state[i] = f(state[i], source.front); | ||
| 3413 | } | ||
| 3414 | |||
| 3415 | static if (isForwardRange!R) | ||
| 3416 | { | ||
| 3417 | @property auto save() | ||
| 3418 | { | ||
| 3419 | auto result = this; | ||
| 3420 | result.source = source.save; | ||
| 3421 | return result; | ||
| 3422 | } | ||
| 3423 | } | ||
| 3424 | |||
| 3425 | static if (hasLength!R) | ||
| 3426 | { | ||
| 3427 | @property size_t length() | ||
| 3428 | { | ||
| 3429 | return source.length; | ||
| 3430 | } | ||
| 3431 | } | ||
| 3432 | } | ||
| 3433 | |||
| 3434 | return Result(range, args); | ||
| 3435 | } | ||
| 3436 | } | ||
| 3437 | |||
| 3438 | /// | ||
| 3439 | @safe unittest | ||
| 3440 | { | ||
| 3441 | import std.algorithm.comparison : max, min; | ||
| 3442 | import std.array : array; | ||
| 3443 | import std.math : approxEqual; | ||
| 3444 | import std.range : chain; | ||
| 3445 | |||
| 3446 | int[] arr = [1, 2, 3, 4, 5]; | ||
| 3447 | // Partial sum of all elements | ||
| 3448 | auto sum = cumulativeFold!((a, b) => a + b)(arr, 0); | ||
| 3449 | assert(sum.array == [1, 3, 6, 10, 15]); | ||
| 3450 | |||
| 3451 | // Partial sum again, using a string predicate with "a" and "b" | ||
| 3452 | auto sum2 = cumulativeFold!"a + b"(arr, 0); | ||
| 3453 | assert(sum2.array == [1, 3, 6, 10, 15]); | ||
| 3454 | |||
| 3455 | // Compute the partial maximum of all elements | ||
| 3456 | auto largest = cumulativeFold!max(arr); | ||
| 3457 | assert(largest.array == [1, 2, 3, 4, 5]); | ||
| 3458 | |||
| 3459 | // Partial max again, but with Uniform Function Call Syntax (UFCS) | ||
| 3460 | largest = arr.cumulativeFold!max; | ||
| 3461 | assert(largest.array == [1, 2, 3, 4, 5]); | ||
| 3462 | |||
| 3463 | // Partial count of odd elements | ||
| 3464 | auto odds = arr.cumulativeFold!((a, b) => a + (b & 1))(0); | ||
| 3465 | assert(odds.array == [1, 1, 2, 2, 3]); | ||
| 3466 | |||
| 3467 | // Compute the partial sum of squares | ||
| 3468 | auto ssquares = arr.cumulativeFold!((a, b) => a + b * b)(0); | ||
| 3469 | assert(ssquares.array == [1, 5, 14, 30, 55]); | ||
| 3470 | |||
| 3471 | // Chain multiple ranges into seed | ||
| 3472 | int[] a = [3, 4]; | ||
| 3473 | int[] b = [100]; | ||
| 3474 | auto r = cumulativeFold!"a + b"(chain(a, b)); | ||
| 3475 | assert(r.array == [3, 7, 107]); | ||
| 3476 | |||
| 3477 | // Mixing convertible types is fair game, too | ||
| 3478 | double[] c = [2.5, 3.0]; | ||
| 3479 | auto r1 = cumulativeFold!"a + b"(chain(a, b, c)); | ||
| 3480 | assert(approxEqual(r1, [3, 7, 107, 109.5, 112.5])); | ||
| 3481 | |||
| 3482 | // To minimize nesting of parentheses, Uniform Function Call Syntax can be used | ||
| 3483 | auto r2 = chain(a, b, c).cumulativeFold!"a + b"; | ||
| 3484 | assert(approxEqual(r2, [3, 7, 107, 109.5, 112.5])); | ||
| 3485 | } | ||
| 3486 | |||
| 3487 | /** | ||
| 3488 | Sometimes it is very useful to compute multiple aggregates in one pass. | ||
| 3489 | One advantage is that the computation is faster because the looping overhead | ||
| 3490 | is shared. That's why `cumulativeFold` accepts multiple functions. | ||
| 3491 | If two or more functions are passed, `cumulativeFold` returns a $(REF Tuple, | ||
| 3492 | std,typecons) object with one member per passed-in function. | ||
| 3493 | The number of seeds must be correspondingly increased. | ||
| 3494 | */ | ||
| 3495 | @safe unittest | ||
| 3496 | { | ||
| 3497 | import std.algorithm.comparison : max, min; | ||
| 3498 | import std.algorithm.iteration : map; | ||
| 3499 | import std.math : approxEqual; | ||
| 3500 | import std.typecons : tuple; | ||
| 3501 | |||
| 3502 | double[] a = [3.0, 4, 7, 11, 3, 2, 5]; | ||
| 3503 | // Compute minimum and maximum in one pass | ||
| 3504 | auto r = a.cumulativeFold!(min, max); | ||
| 3505 | // The type of r is Tuple!(int, int) | ||
| 3506 | assert(approxEqual(r.map!"a[0]", [3, 3, 3, 3, 3, 2, 2])); // minimum | ||
| 3507 | assert(approxEqual(r.map!"a[1]", [3, 4, 7, 11, 11, 11, 11])); // maximum | ||
| 3508 | |||
| 3509 | // Compute sum and sum of squares in one pass | ||
| 3510 | auto r2 = a.cumulativeFold!("a + b", "a + b * b")(tuple(0.0, 0.0)); | ||
| 3511 | assert(approxEqual(r2.map!"a[0]", [3, 7, 14, 25, 28, 30, 35])); // sum | ||
| 3512 | assert(approxEqual(r2.map!"a[1]", [9, 25, 74, 195, 204, 208, 233])); // sum of squares | ||
| 3513 | } | ||
| 3514 | |||
| 3515 | @safe unittest | ||
| 3516 | { | ||
| 3517 | import std.algorithm.comparison : equal, max, min; | ||
| 3518 | import std.conv : to; | ||
| 3519 | import std.range : chain; | ||
| 3520 | import std.typecons : tuple; | ||
| 3521 | |||
| 3522 | double[] a = [3, 4]; | ||
| 3523 | auto r = a.cumulativeFold!("a + b")(0.0); | ||
| 3524 | assert(r.equal([3, 7])); | ||
| 3525 | auto r2 = cumulativeFold!("a + b")(a); | ||
| 3526 | assert(r2.equal([3, 7])); | ||
| 3527 | auto r3 = cumulativeFold!(min)(a); | ||
| 3528 | assert(r3.equal([3, 3])); | ||
| 3529 | double[] b = [100]; | ||
| 3530 | auto r4 = cumulativeFold!("a + b")(chain(a, b)); | ||
| 3531 | assert(r4.equal([3, 7, 107])); | ||
| 3532 | |||
| 3533 | // two funs | ||
| 3534 | auto r5 = cumulativeFold!("a + b", "a - b")(a, tuple(0.0, 0.0)); | ||
| 3535 | assert(r5.equal([tuple(3, -3), tuple(7, -7)])); | ||
| 3536 | auto r6 = cumulativeFold!("a + b", "a - b")(a); | ||
| 3537 | assert(r6.equal([tuple(3, 3), tuple(7, -1)])); | ||
| 3538 | |||
| 3539 | a = [1, 2, 3, 4, 5]; | ||
| 3540 | // Stringize with commas | ||
| 3541 | auto rep = cumulativeFold!("a ~ `, ` ~ to!string(b)")(a, ""); | ||
| 3542 | assert(rep.map!"a[2 .. $]".equal(["1", "1, 2", "1, 2, 3", "1, 2, 3, 4", "1, 2, 3, 4, 5"])); | ||
| 3543 | |||
| 3544 | // Test for empty range | ||
| 3545 | a = []; | ||
| 3546 | assert(a.cumulativeFold!"a + b".empty); | ||
| 3547 | assert(a.cumulativeFold!"a + b"(2.0).empty); | ||
| 3548 | } | ||
| 3549 | |||
| 3550 | @safe unittest | ||
| 3551 | { | ||
| 3552 | import std.algorithm.comparison : max, min; | ||
| 3553 | import std.array : array; | ||
| 3554 | import std.math : approxEqual; | ||
| 3555 | import std.typecons : tuple; | ||
| 3556 | |||
| 3557 | const float a = 0.0; | ||
| 3558 | const float[] b = [1.2, 3, 3.3]; | ||
| 3559 | float[] c = [1.2, 3, 3.3]; | ||
| 3560 | |||
| 3561 | auto r = cumulativeFold!"a + b"(b, a); | ||
| 3562 | assert(approxEqual(r, [1.2, 4.2, 7.5])); | ||
| 3563 | |||
| 3564 | auto r2 = cumulativeFold!"a + b"(c, a); | ||
| 3565 | assert(approxEqual(r2, [1.2, 4.2, 7.5])); | ||
| 3566 | |||
| 3567 | const numbers = [10, 30, 20]; | ||
| 3568 | enum m = numbers.cumulativeFold!(min).array; | ||
| 3569 | assert(m == [10, 10, 10]); | ||
| 3570 | enum minmax = numbers.cumulativeFold!(min, max).array; | ||
| 3571 | assert(minmax == [tuple(10, 10), tuple(10, 30), tuple(10, 30)]); | ||
| 3572 | } | ||
| 3573 | |||
| 3574 | @safe unittest | ||
| 3575 | { | ||
| 3576 | import std.math : approxEqual; | ||
| 3577 | import std.typecons : tuple; | ||
| 3578 | |||
| 3579 | enum foo = "a + 0.5 * b"; | ||
| 3580 | auto r = [0, 1, 2, 3]; | ||
| 3581 | auto r1 = r.cumulativeFold!foo; | ||
| 3582 | auto r2 = r.cumulativeFold!(foo, foo); | ||
| 3583 | assert(approxEqual(r1, [0, 0.5, 1.5, 3])); | ||
| 3584 | assert(approxEqual(r2.map!"a[0]", [0, 0.5, 1.5, 3])); | ||
| 3585 | assert(approxEqual(r2.map!"a[1]", [0, 0.5, 1.5, 3])); | ||
| 3586 | } | ||
| 3587 | |||
| 3588 | @safe unittest | ||
| 3589 | { | ||
| 3590 | import std.algorithm.comparison : equal, max, min; | ||
| 3591 | import std.array : array; | ||
| 3592 | import std.typecons : tuple; | ||
| 3593 | |||
| 3594 | //Seed is tuple of const. | ||
| 3595 | static auto minmaxElement(alias F = min, alias G = max, R)(in R range) | ||
| 3596 | @safe pure nothrow | ||
| 3597 | if (isInputRange!R) | ||
| 3598 | { | ||
| 3599 | return range.cumulativeFold!(F, G)(tuple(ElementType!R.max, ElementType!R.min)); | ||
| 3600 | } | ||
| 3601 | |||
| 3602 | assert(minmaxElement([1, 2, 3]).equal([tuple(1, 1), tuple(1, 2), tuple(1, 3)])); | ||
| 3603 | } | ||
| 3604 | |||
| 3605 | @safe unittest //12569 | ||
| 3606 | { | ||
| 3607 | import std.algorithm.comparison : equal, max, min; | ||
| 3608 | import std.typecons : tuple; | ||
| 3609 | |||
| 3610 | dchar c = 'a'; | ||
| 3611 | |||
| 3612 | assert(cumulativeFold!(min, max)("hello", tuple(c, c)).equal([tuple('a', 'h'), | ||
| 3613 | tuple('a', 'h'), tuple('a', 'l'), tuple('a', 'l'), tuple('a', 'o')])); | ||
| 3614 | static assert(!__traits(compiles, cumulativeFold!(min, max)("hello", tuple(c)))); | ||
| 3615 | static assert(!__traits(compiles, cumulativeFold!(min, max)("hello", tuple(c, c, c)))); | ||
| 3616 | |||
| 3617 | //"Seed dchar should be a Tuple" | ||
| 3618 | static assert(!__traits(compiles, cumulativeFold!(min, max)("hello", c))); | ||
| 3619 | //"Seed (dchar) does not have the correct amount of fields (should be 2)" | ||
| 3620 | static assert(!__traits(compiles, cumulativeFold!(min, max)("hello", tuple(c)))); | ||
| 3621 | //"Seed (dchar, dchar, dchar) does not have the correct amount of fields (should be 2)" | ||
| 3622 | static assert(!__traits(compiles, cumulativeFold!(min, max)("hello", tuple(c, c, c)))); | ||
| 3623 | //"Incompatable function/seed/element: all(alias pred = "a")/int/dchar" | ||
| 3624 | static assert(!__traits(compiles, cumulativeFold!all("hello", 1))); | ||
| 3625 | static assert(!__traits(compiles, cumulativeFold!(all, all)("hello", tuple(1, 1)))); | ||
| 3626 | } | ||
| 3627 | |||
| 3628 | @safe unittest //13304 | ||
| 3629 | { | ||
| 3630 | int[] data; | ||
| 3631 | assert(data.cumulativeFold!((a, b) => a + b).empty); | ||
| 3632 | } | ||
| 3633 | |||
| 3634 | @safe unittest | ||
| 3635 | { | ||
| 3636 | import std.algorithm.comparison : equal; | ||
| 3637 | import std.internal.test.dummyrange : AllDummyRanges, propagatesLength, | ||
| 3638 | propagatesRangeType, RangeType; | ||
| 3639 | |||
| 3640 | foreach (DummyType; AllDummyRanges) | ||
| 3641 | { | ||
| 3642 | DummyType d; | ||
| 3643 | auto m = d.cumulativeFold!"a * b"; | ||
| 3644 | |||
| 3645 | static assert(propagatesLength!(typeof(m), DummyType)); | ||
| 3646 | static if (DummyType.rt <= RangeType.Forward) | ||
| 3647 | static assert(propagatesRangeType!(typeof(m), DummyType)); | ||
| 3648 | |||
| 3649 | assert(m.equal([1, 2, 6, 24, 120, 720, 5040, 40_320, 362_880, 3_628_800])); | ||
| 3650 | } | ||
| 3651 | } | ||
| 3652 | |||
| 3653 | // splitter | ||
| 3654 | /** | ||
| 3655 | Lazily splits a range using an element as a separator. This can be used with | ||
| 3656 | any narrow string type or sliceable range type, but is most popular with string | ||
| 3657 | types. | ||
| 3658 | |||
| 3659 | Two adjacent separators are considered to surround an empty element in | ||
| 3660 | the split range. Use $(D filter!(a => !a.empty)) on the result to compress | ||
| 3661 | empty elements. | ||
| 3662 | |||
| 3663 | The predicate is passed to $(REF binaryFun, std,functional), and can either accept | ||
| 3664 | a string, or any callable that can be executed via $(D pred(element, s)). | ||
| 3665 | |||
| 3666 | If the empty range is given, the result is a range with one empty | ||
| 3667 | element. If a range with one separator is given, the result is a range | ||
| 3668 | with two empty elements. | ||
| 3669 | |||
| 3670 | If splitting a string on whitespace and token compression is desired, | ||
| 3671 | consider using $(D splitter) without specifying a separator (see fourth overload | ||
| 3672 | below). | ||
| 3673 | |||
| 3674 | Params: | ||
| 3675 | pred = The predicate for comparing each element with the separator, | ||
| 3676 | defaulting to $(D "a == b"). | ||
| 3677 | r = The $(REF_ALTTEXT input range, isInputRange, std,range,primitives) to be | ||
| 3678 | split. Must support slicing and $(D .length). | ||
| 3679 | s = The element to be treated as the separator between range segments to be | ||
| 3680 | split. | ||
| 3681 | |||
| 3682 | Constraints: | ||
| 3683 | The predicate $(D pred) needs to accept an element of $(D r) and the | ||
| 3684 | separator $(D s). | ||
| 3685 | |||
| 3686 | Returns: | ||
| 3687 | An input range of the subranges of elements between separators. If $(D r) | ||
| 3688 | is a $(REF_ALTTEXT forward range, isForwardRange, std,range,primitives) | ||
| 3689 | or $(REF_ALTTEXT bidirectional range, isBidirectionalRange, std,range,primitives), | ||
| 3690 | the returned range will be likewise. | ||
| 3691 | |||
| 3692 | See_Also: | ||
| 3693 | $(REF _splitter, std,regex) for a version that splits using a regular | ||
| 3694 | expression defined separator. | ||
| 3695 | */ | ||
| 3696 | auto splitter(alias pred = "a == b", Range, Separator)(Range r, Separator s) | ||
| 3697 | if (is(typeof(binaryFun!pred(r.front, s)) : bool) | ||
| 3698 | && ((hasSlicing!Range && hasLength!Range) || isNarrowString!Range)) | ||
| 3699 | { | ||
| 3700 | import std.algorithm.searching : find; | ||
| 3701 | import std.conv : unsigned; | ||
| 3702 | |||
| 3703 | static struct Result | ||
| 3704 | { | ||
| 3705 | private: | ||
| 3706 | Range _input; | ||
| 3707 | Separator _separator; | ||
| 3708 | // Do we need hasLength!Range? popFront uses _input.length... | ||
| 3709 | enum size_t _unComputed = size_t.max - 1, _atEnd = size_t.max; | ||
| 3710 | size_t _frontLength = _unComputed; | ||
| 3711 | size_t _backLength = _unComputed; | ||
| 3712 | |||
| 3713 | static if (isNarrowString!Range) | ||
| 3714 | { | ||
| 3715 | size_t _separatorLength; | ||
| 3716 | } | ||
| 3717 | else | ||
| 3718 | { | ||
| 3719 | enum _separatorLength = 1; | ||
| 3720 | } | ||
| 3721 | |||
| 3722 | static if (isBidirectionalRange!Range) | ||
| 3723 | { | ||
| 3724 | static size_t lastIndexOf(Range haystack, Separator needle) | ||
| 3725 | { | ||
| 3726 | import std.range : retro; | ||
| 3727 | auto r = haystack.retro().find!pred(needle); | ||
| 3728 | return r.retro().length - 1; | ||
| 3729 | } | ||
| 3730 | } | ||
| 3731 | |||
| 3732 | public: | ||
| 3733 | this(Range input, Separator separator) | ||
| 3734 | { | ||
| 3735 | _input = input; | ||
| 3736 | _separator = separator; | ||
| 3737 | |||
| 3738 | static if (isNarrowString!Range) | ||
| 3739 | { | ||
| 3740 | import std.utf : codeLength; | ||
| 3741 | |||
| 3742 | _separatorLength = codeLength!(ElementEncodingType!Range)(separator); | ||
| 3743 | } | ||
| 3744 | if (_input.empty) | ||
| 3745 | _frontLength = _atEnd; | ||
| 3746 | } | ||
| 3747 | |||
| 3748 | static if (isInfinite!Range) | ||
| 3749 | { | ||
| 3750 | enum bool empty = false; | ||
| 3751 | } | ||
| 3752 | else | ||
| 3753 | { | ||
| 3754 | @property bool empty() | ||
| 3755 | { | ||
| 3756 | return _frontLength == _atEnd; | ||
| 3757 | } | ||
| 3758 | } | ||
| 3759 | |||
| 3760 | @property Range front() | ||
| 3761 | { | ||
| 3762 | assert(!empty, "Attempting to fetch the front of an empty splitter."); | ||
| 3763 | if (_frontLength == _unComputed) | ||
| 3764 | { | ||
| 3765 | auto r = _input.find!pred(_separator); | ||
| 3766 | _frontLength = _input.length - r.length; | ||
| 3767 | } | ||
| 3768 | return _input[0 .. _frontLength]; | ||
| 3769 | } | ||
| 3770 | |||
| 3771 | void popFront() | ||
| 3772 | { | ||
| 3773 | assert(!empty, "Attempting to popFront an empty splitter."); | ||
| 3774 | if (_frontLength == _unComputed) | ||
| 3775 | { | ||
| 3776 | front; | ||
| 3777 | } | ||
| 3778 | assert(_frontLength <= _input.length); | ||
| 3779 | if (_frontLength == _input.length) | ||
| 3780 | { | ||
| 3781 | // no more input and need to fetch => done | ||
| 3782 | _frontLength = _atEnd; | ||
| 3783 | |||
| 3784 | // Probably don't need this, but just for consistency: | ||
| 3785 | _backLength = _atEnd; | ||
| 3786 | } | ||
| 3787 | else | ||
| 3788 | { | ||
| 3789 | _input = _input[_frontLength + _separatorLength .. _input.length]; | ||
| 3790 | _frontLength = _unComputed; | ||
| 3791 | } | ||
| 3792 | } | ||
| 3793 | |||
| 3794 | static if (isForwardRange!Range) | ||
| 3795 | { | ||
| 3796 | @property typeof(this) save() | ||
| 3797 | { | ||
| 3798 | auto ret = this; | ||
| 3799 | ret._input = _input.save; | ||
| 3800 | return ret; | ||
| 3801 | } | ||
| 3802 | } | ||
| 3803 | |||
| 3804 | static if (isBidirectionalRange!Range) | ||
| 3805 | { | ||
| 3806 | @property Range back() | ||
| 3807 | { | ||
| 3808 | assert(!empty, "Attempting to fetch the back of an empty splitter."); | ||
| 3809 | if (_backLength == _unComputed) | ||
| 3810 | { | ||
| 3811 | immutable lastIndex = lastIndexOf(_input, _separator); | ||
| 3812 | if (lastIndex == -1) | ||
| 3813 | { | ||
| 3814 | _backLength = _input.length; | ||
| 3815 | } | ||
| 3816 | else | ||
| 3817 | { | ||
| 3818 | _backLength = _input.length - lastIndex - 1; | ||
| 3819 | } | ||
| 3820 | } | ||
| 3821 | return _input[_input.length - _backLength .. _input.length]; | ||
| 3822 | } | ||
| 3823 | |||
| 3824 | void popBack() | ||
| 3825 | { | ||
| 3826 | assert(!empty, "Attempting to popBack an empty splitter."); | ||
| 3827 | if (_backLength == _unComputed) | ||
| 3828 | { | ||
| 3829 | // evaluate back to make sure it's computed | ||
| 3830 | back; | ||
| 3831 | } | ||
| 3832 | assert(_backLength <= _input.length); | ||
| 3833 | if (_backLength == _input.length) | ||
| 3834 | { | ||
| 3835 | // no more input and need to fetch => done | ||
| 3836 | _frontLength = _atEnd; | ||
| 3837 | _backLength = _atEnd; | ||
| 3838 | } | ||
| 3839 | else | ||
| 3840 | { | ||
| 3841 | _input = _input[0 .. _input.length - _backLength - _separatorLength]; | ||
| 3842 | _backLength = _unComputed; | ||
| 3843 | } | ||
| 3844 | } | ||
| 3845 | } | ||
| 3846 | } | ||
| 3847 | |||
| 3848 | return Result(r, s); | ||
| 3849 | } | ||
| 3850 | |||
| 3851 | /// | ||
| 3852 | @safe unittest | ||
| 3853 | { | ||
| 3854 | import std.algorithm.comparison : equal; | ||
| 3855 | |||
| 3856 | assert(equal(splitter("hello world", ' '), [ "hello", "", "world" ])); | ||
| 3857 | int[] a = [ 1, 2, 0, 0, 3, 0, 4, 5, 0 ]; | ||
| 3858 | int[][] w = [ [1, 2], [], [3], [4, 5], [] ]; | ||
| 3859 | assert(equal(splitter(a, 0), w)); | ||
| 3860 | a = [ 0 ]; | ||
| 3861 | assert(equal(splitter(a, 0), [ (int[]).init, (int[]).init ])); | ||
| 3862 | a = [ 0, 1 ]; | ||
| 3863 | assert(equal(splitter(a, 0), [ [], [1] ])); | ||
| 3864 | w = [ [0], [1], [2] ]; | ||
| 3865 | assert(equal(splitter!"a.front == b"(w, 1), [ [[0]], [[2]] ])); | ||
| 3866 | } | ||
| 3867 | |||
| 3868 | @safe unittest | ||
| 3869 | { | ||
| 3870 | import std.algorithm; | ||
| 3871 | import std.array : array; | ||
| 3872 | import std.internal.test.dummyrange; | ||
| 3873 | import std.range : retro; | ||
| 3874 | |||
| 3875 | assert(equal(splitter("hello world", ' '), [ "hello", "", "world" ])); | ||
| 3876 | assert(equal(splitter("žlutoučkýřkůň", 'ř'), [ "žlutoučký", "kůň" ])); | ||
| 3877 | int[] a = [ 1, 2, 0, 0, 3, 0, 4, 5, 0 ]; | ||
| 3878 | int[][] w = [ [1, 2], [], [3], [4, 5], [] ]; | ||
| 3879 | static assert(isForwardRange!(typeof(splitter(a, 0)))); | ||
| 3880 | |||
| 3881 | assert(equal(splitter(a, 0), w)); | ||
| 3882 | a = null; | ||
| 3883 | assert(equal(splitter(a, 0), (int[][]).init)); | ||
| 3884 | a = [ 0 ]; | ||
| 3885 | assert(equal(splitter(a, 0), [ (int[]).init, (int[]).init ][])); | ||
| 3886 | a = [ 0, 1 ]; | ||
| 3887 | assert(equal(splitter(a, 0), [ [], [1] ][])); | ||
| 3888 | |||
| 3889 | // Thoroughly exercise the bidirectional stuff. | ||
| 3890 | auto str = "abc abcd abcde ab abcdefg abcdefghij ab ac ar an at ada"; | ||
| 3891 | assert(equal( | ||
| 3892 | retro(splitter(str, 'a')), | ||
| 3893 | retro(array(splitter(str, 'a'))) | ||
| 3894 | )); | ||
| 3895 | |||
| 3896 | // Test interleaving front and back. | ||
| 3897 | auto split = splitter(str, 'a'); | ||
| 3898 | assert(split.front == ""); | ||
| 3899 | assert(split.back == ""); | ||
| 3900 | split.popBack(); | ||
| 3901 | assert(split.back == "d"); | ||
| 3902 | split.popFront(); | ||
| 3903 | assert(split.front == "bc "); | ||
| 3904 | assert(split.back == "d"); | ||
| 3905 | split.popFront(); | ||
| 3906 | split.popBack(); | ||
| 3907 | assert(split.back == "t "); | ||
| 3908 | split.popBack(); | ||
| 3909 | split.popBack(); | ||
| 3910 | split.popFront(); | ||
| 3911 | split.popFront(); | ||
| 3912 | assert(split.front == "b "); | ||
| 3913 | assert(split.back == "r "); | ||
| 3914 | |||
| 3915 | foreach (DummyType; AllDummyRanges) { // Bug 4408 | ||
| 3916 | static if (isRandomAccessRange!DummyType) | ||
| 3917 | { | ||
| 3918 | static assert(isBidirectionalRange!DummyType); | ||
| 3919 | DummyType d; | ||
| 3920 | auto s = splitter(d, 5); | ||
| 3921 | assert(equal(s.front, [1,2,3,4])); | ||
| 3922 | assert(equal(s.back, [6,7,8,9,10])); | ||
| 3923 | |||
| 3924 | auto s2 = splitter(d, [4, 5]); | ||
| 3925 | assert(equal(s2.front, [1,2,3])); | ||
| 3926 | } | ||
| 3927 | } | ||
| 3928 | } | ||
| 3929 | @safe unittest | ||
| 3930 | { | ||
| 3931 | import std.algorithm; | ||
| 3932 | import std.range; | ||
| 3933 | auto L = retro(iota(1L, 10L)); | ||
| 3934 | auto s = splitter(L, 5L); | ||
| 3935 | assert(equal(s.front, [9L, 8L, 7L, 6L])); | ||
| 3936 | s.popFront(); | ||
| 3937 | assert(equal(s.front, [4L, 3L, 2L, 1L])); | ||
| 3938 | s.popFront(); | ||
| 3939 | assert(s.empty); | ||
| 3940 | } | ||
| 3941 | |||
| 3942 | /** | ||
| 3943 | Similar to the previous overload of $(D splitter), except this one uses another | ||
| 3944 | range as a separator. This can be used with any narrow string type or sliceable | ||
| 3945 | range type, but is most popular with string types. The predicate is passed to | ||
| 3946 | $(REF binaryFun, std,functional), and can either accept a string, or any callable | ||
| 3947 | that can be executed via $(D pred(r.front, s.front)). | ||
| 3948 | |||
| 3949 | Two adjacent separators are considered to surround an empty element in | ||
| 3950 | the split range. Use $(D filter!(a => !a.empty)) on the result to compress | ||
| 3951 | empty elements. | ||
| 3952 | |||
| 3953 | Params: | ||
| 3954 | pred = The predicate for comparing each element with the separator, | ||
| 3955 | defaulting to $(D "a == b"). | ||
| 3956 | r = The $(REF_ALTTEXT input range, isInputRange, std,range,primitives) to be | ||
| 3957 | split. | ||
| 3958 | s = The $(REF_ALTTEXT forward range, isForwardRange, std,range,primitives) to | ||
| 3959 | be treated as the separator between segments of $(D r) to be split. | ||
| 3960 | |||
| 3961 | Constraints: | ||
| 3962 | The predicate $(D pred) needs to accept an element of $(D r) and an | ||
| 3963 | element of $(D s). | ||
| 3964 | |||
| 3965 | Returns: | ||
| 3966 | An input range of the subranges of elements between separators. If $(D r) | ||
| 3967 | is a forward range or $(REF_ALTTEXT bidirectional range, isBidirectionalRange, std,range,primitives), | ||
| 3968 | the returned range will be likewise. | ||
| 3969 | |||
| 3970 | See_Also: $(REF _splitter, std,regex) for a version that splits using a regular | ||
| 3971 | expression defined separator. | ||
| 3972 | */ | ||
| 3973 | auto splitter(alias pred = "a == b", Range, Separator)(Range r, Separator s) | ||
| 3974 | if (is(typeof(binaryFun!pred(r.front, s.front)) : bool) | ||
| 3975 | && (hasSlicing!Range || isNarrowString!Range) | ||
| 3976 | && isForwardRange!Separator | ||
| 3977 | && (hasLength!Separator || isNarrowString!Separator)) | ||
| 3978 | { | ||
| 3979 | import std.algorithm.searching : find; | ||
| 3980 | import std.conv : unsigned; | ||
| 3981 | |||
| 3982 | static struct Result | ||
| 3983 | { | ||
| 3984 | private: | ||
| 3985 | Range _input; | ||
| 3986 | Separator _separator; | ||
| 3987 | // _frontLength == size_t.max means empty | ||
| 3988 | size_t _frontLength = size_t.max; | ||
| 3989 | static if (isBidirectionalRange!Range) | ||
| 3990 | size_t _backLength = size_t.max; | ||
| 3991 | |||
| 3992 | @property auto separatorLength() { return _separator.length; } | ||
| 3993 | |||
| 3994 | void ensureFrontLength() | ||
| 3995 | { | ||
| 3996 | if (_frontLength != _frontLength.max) return; | ||
| 3997 | assert(!_input.empty); | ||
| 3998 | // compute front length | ||
| 3999 | _frontLength = (_separator.empty) ? 1 : | ||
| 4000 | _input.length - find!pred(_input, _separator).length; | ||
| 4001 | static if (isBidirectionalRange!Range) | ||
| 4002 | if (_frontLength == _input.length) _backLength = _frontLength; | ||
| 4003 | } | ||
| 4004 | |||
| 4005 | void ensureBackLength() | ||
| 4006 | { | ||
| 4007 | static if (isBidirectionalRange!Range) | ||
| 4008 | if (_backLength != _backLength.max) return; | ||
| 4009 | assert(!_input.empty); | ||
| 4010 | // compute back length | ||
| 4011 | static if (isBidirectionalRange!Range && isBidirectionalRange!Separator) | ||
| 4012 | { | ||
| 4013 | import std.range : retro; | ||
| 4014 | _backLength = _input.length - | ||
| 4015 | find!pred(retro(_input), retro(_separator)).source.length; | ||
| 4016 | } | ||
| 4017 | } | ||
| 4018 | |||
| 4019 | public: | ||
| 4020 | this(Range input, Separator separator) | ||
| 4021 | { | ||
| 4022 | _input = input; | ||
| 4023 | _separator = separator; | ||
| 4024 | } | ||
| 4025 | |||
| 4026 | @property Range front() | ||
| 4027 | { | ||
| 4028 | assert(!empty, "Attempting to fetch the front of an empty splitter."); | ||
| 4029 | ensureFrontLength(); | ||
| 4030 | return _input[0 .. _frontLength]; | ||
| 4031 | } | ||
| 4032 | |||
| 4033 | static if (isInfinite!Range) | ||
| 4034 | { | ||
| 4035 | enum bool empty = false; // Propagate infiniteness | ||
| 4036 | } | ||
| 4037 | else | ||
| 4038 | { | ||
| 4039 | @property bool empty() | ||
| 4040 | { | ||
| 4041 | return _frontLength == size_t.max && _input.empty; | ||
| 4042 | } | ||
| 4043 | } | ||
| 4044 | |||
| 4045 | void popFront() | ||
| 4046 | { | ||
| 4047 | assert(!empty, "Attempting to popFront an empty splitter."); | ||
| 4048 | ensureFrontLength(); | ||
| 4049 | if (_frontLength == _input.length) | ||
| 4050 | { | ||
| 4051 | // done, there's no separator in sight | ||
| 4052 | _input = _input[_frontLength .. _frontLength]; | ||
| 4053 | _frontLength = _frontLength.max; | ||
| 4054 | static if (isBidirectionalRange!Range) | ||
| 4055 | _backLength = _backLength.max; | ||
| 4056 | return; | ||
| 4057 | } | ||
| 4058 | if (_frontLength + separatorLength == _input.length) | ||
| 4059 | { | ||
| 4060 | // Special case: popping the first-to-last item; there is | ||
| 4061 | // an empty item right after this. | ||
| 4062 | _input = _input[_input.length .. _input.length]; | ||
| 4063 | _frontLength = 0; | ||
| 4064 | static if (isBidirectionalRange!Range) | ||
| 4065 | _backLength = 0; | ||
| 4066 | return; | ||
| 4067 | } | ||
| 4068 | // Normal case, pop one item and the separator, get ready for | ||
| 4069 | // reading the next item | ||
| 4070 | _input = _input[_frontLength + separatorLength .. _input.length]; | ||
| 4071 | // mark _frontLength as uninitialized | ||
| 4072 | _frontLength = _frontLength.max; | ||
| 4073 | } | ||
| 4074 | |||
| 4075 | static if (isForwardRange!Range) | ||
| 4076 | { | ||
| 4077 | @property typeof(this) save() | ||
| 4078 | { | ||
| 4079 | auto ret = this; | ||
| 4080 | ret._input = _input.save; | ||
| 4081 | return ret; | ||
| 4082 | } | ||
| 4083 | } | ||
| 4084 | } | ||
| 4085 | |||
| 4086 | return Result(r, s); | ||
| 4087 | } | ||
| 4088 | |||
| 4089 | /// | ||
| 4090 | @safe unittest | ||
| 4091 | { | ||
| 4092 | import std.algorithm.comparison : equal; | ||
| 4093 | |||
| 4094 | assert(equal(splitter("hello world", " "), [ "hello", "world" ])); | ||
| 4095 | int[] a = [ 1, 2, 0, 0, 3, 0, 4, 5, 0 ]; | ||
| 4096 | int[][] w = [ [1, 2], [3, 0, 4, 5, 0] ]; | ||
| 4097 | assert(equal(splitter(a, [0, 0]), w)); | ||
| 4098 | a = [ 0, 0 ]; | ||
| 4099 | assert(equal(splitter(a, [0, 0]), [ (int[]).init, (int[]).init ])); | ||
| 4100 | a = [ 0, 0, 1 ]; | ||
| 4101 | assert(equal(splitter(a, [0, 0]), [ [], [1] ])); | ||
| 4102 | } | ||
| 4103 | |||
| 4104 | @safe unittest | ||
| 4105 | { | ||
| 4106 | import std.algorithm.comparison : equal; | ||
| 4107 | import std.typecons : Tuple; | ||
| 4108 | |||
| 4109 | alias C = Tuple!(int, "x", int, "y"); | ||
| 4110 | auto a = [C(1,0), C(2,0), C(3,1), C(4,0)]; | ||
| 4111 | assert(equal(splitter!"a.x == b"(a, [2, 3]), [ [C(1,0)], [C(4,0)] ])); | ||
| 4112 | } | ||
| 4113 | |||
| 4114 | @safe unittest | ||
| 4115 | { | ||
| 4116 | import std.algorithm.comparison : equal; | ||
| 4117 | import std.array : split; | ||
| 4118 | import std.conv : text; | ||
| 4119 | |||
| 4120 | auto s = ",abc, de, fg,hi,"; | ||
| 4121 | auto sp0 = splitter(s, ','); | ||
| 4122 | assert(equal(sp0, ["", "abc", " de", " fg", "hi", ""][])); | ||
| 4123 | |||
| 4124 | auto s1 = ", abc, de, fg, hi, "; | ||
| 4125 | auto sp1 = splitter(s1, ", "); | ||
| 4126 | assert(equal(sp1, ["", "abc", "de", " fg", "hi", ""][])); | ||
| 4127 | static assert(isForwardRange!(typeof(sp1))); | ||
| 4128 | |||
| 4129 | int[] a = [ 1, 2, 0, 3, 0, 4, 5, 0 ]; | ||
| 4130 | int[][] w = [ [1, 2], [3], [4, 5], [] ]; | ||
| 4131 | uint i; | ||
| 4132 | foreach (e; splitter(a, 0)) | ||
| 4133 | { | ||
| 4134 | assert(i < w.length); | ||
| 4135 | assert(e == w[i++]); | ||
| 4136 | } | ||
| 4137 | assert(i == w.length); | ||
| 4138 | // // Now go back | ||
| 4139 | // auto s2 = splitter(a, 0); | ||
| 4140 | |||
| 4141 | // foreach (e; retro(s2)) | ||
| 4142 | // { | ||
| 4143 | // assert(i > 0); | ||
| 4144 | // assert(equal(e, w[--i]), text(e)); | ||
| 4145 | // } | ||
| 4146 | // assert(i == 0); | ||
| 4147 | |||
| 4148 | wstring names = ",peter,paul,jerry,"; | ||
| 4149 | auto words = split(names, ","); | ||
| 4150 | assert(walkLength(words) == 5, text(walkLength(words))); | ||
| 4151 | } | ||
| 4152 | |||
| 4153 | @safe unittest | ||
| 4154 | { | ||
| 4155 | int[][] a = [ [1], [2], [0], [3], [0], [4], [5], [0] ]; | ||
| 4156 | int[][][] w = [ [[1], [2]], [[3]], [[4], [5]], [] ]; | ||
| 4157 | uint i; | ||
| 4158 | foreach (e; splitter!"a.front == 0"(a, 0)) | ||
| 4159 | { | ||
| 4160 | assert(i < w.length); | ||
| 4161 | assert(e == w[i++]); | ||
| 4162 | } | ||
| 4163 | assert(i == w.length); | ||
| 4164 | } | ||
| 4165 | |||
| 4166 | @safe unittest | ||
| 4167 | { | ||
| 4168 | import std.algorithm.comparison : equal; | ||
| 4169 | auto s6 = ","; | ||
| 4170 | auto sp6 = splitter(s6, ','); | ||
| 4171 | foreach (e; sp6) {} | ||
| 4172 | assert(equal(sp6, ["", ""][])); | ||
| 4173 | } | ||
| 4174 | |||
| 4175 | @safe unittest | ||
| 4176 | { | ||
| 4177 | import std.algorithm.comparison : equal; | ||
| 4178 | |||
| 4179 | // Issue 10773 | ||
| 4180 | auto s = splitter("abc", ""); | ||
| 4181 | assert(s.equal(["a", "b", "c"])); | ||
| 4182 | } | ||
| 4183 | |||
| 4184 | @safe unittest | ||
| 4185 | { | ||
| 4186 | import std.algorithm.comparison : equal; | ||
| 4187 | |||
| 4188 | // Test by-reference separator | ||
| 4189 | class RefSep { | ||
| 4190 | @safe: | ||
| 4191 | string _impl; | ||
| 4192 | this(string s) { _impl = s; } | ||
| 4193 | @property empty() { return _impl.empty; } | ||
| 4194 | @property auto front() { return _impl.front; } | ||
| 4195 | void popFront() { _impl = _impl[1..$]; } | ||
| 4196 | @property RefSep save() { return new RefSep(_impl); } | ||
| 4197 | @property auto length() { return _impl.length; } | ||
| 4198 | } | ||
| 4199 | auto sep = new RefSep("->"); | ||
| 4200 | auto data = "i->am->pointing"; | ||
| 4201 | auto words = splitter(data, sep); | ||
| 4202 | assert(words.equal([ "i", "am", "pointing" ])); | ||
| 4203 | } | ||
| 4204 | |||
| 4205 | /** | ||
| 4206 | |||
| 4207 | Similar to the previous overload of $(D splitter), except this one does not use a separator. | ||
| 4208 | Instead, the predicate is an unary function on the input range's element type. | ||
| 4209 | The $(D isTerminator) predicate is passed to $(REF unaryFun, std,functional) and can | ||
| 4210 | either accept a string, or any callable that can be executed via $(D pred(element, s)). | ||
| 4211 | |||
| 4212 | Two adjacent separators are considered to surround an empty element in | ||
| 4213 | the split range. Use $(D filter!(a => !a.empty)) on the result to compress | ||
| 4214 | empty elements. | ||
| 4215 | |||
| 4216 | Params: | ||
| 4217 | isTerminator = The predicate for deciding where to split the range. | ||
| 4218 | input = The $(REF_ALTTEXT input range, isInputRange, std,range,primitives) to | ||
| 4219 | be split. | ||
| 4220 | |||
| 4221 | Constraints: | ||
| 4222 | The predicate $(D isTerminator) needs to accept an element of $(D input). | ||
| 4223 | |||
| 4224 | Returns: | ||
| 4225 | An input range of the subranges of elements between separators. If $(D input) | ||
| 4226 | is a forward range or $(REF_ALTTEXT bidirectional range, isBidirectionalRange, std,range,primitives), | ||
| 4227 | the returned range will be likewise. | ||
| 4228 | |||
| 4229 | See_Also: $(REF _splitter, std,regex) for a version that splits using a regular | ||
| 4230 | expression defined separator. | ||
| 4231 | */ | ||
| 4232 | auto splitter(alias isTerminator, Range)(Range input) | ||
| 4233 | if (isForwardRange!Range && is(typeof(unaryFun!isTerminator(input.front)))) | ||
| 4234 | { | ||
| 4235 | return SplitterResult!(unaryFun!isTerminator, Range)(input); | ||
| 4236 | } | ||
| 4237 | |||
| 4238 | /// | ||
| 4239 | @safe unittest | ||
| 4240 | { | ||
| 4241 | import std.algorithm.comparison : equal; | ||
| 4242 | import std.range.primitives : front; | ||
| 4243 | |||
| 4244 | assert(equal(splitter!(a => a == ' ')("hello world"), [ "hello", "", "world" ])); | ||
| 4245 | int[] a = [ 1, 2, 0, 0, 3, 0, 4, 5, 0 ]; | ||
| 4246 | int[][] w = [ [1, 2], [], [3], [4, 5], [] ]; | ||
| 4247 | assert(equal(splitter!(a => a == 0)(a), w)); | ||
| 4248 | a = [ 0 ]; | ||
| 4249 | assert(equal(splitter!(a => a == 0)(a), [ (int[]).init, (int[]).init ])); | ||
| 4250 | a = [ 0, 1 ]; | ||
| 4251 | assert(equal(splitter!(a => a == 0)(a), [ [], [1] ])); | ||
| 4252 | w = [ [0], [1], [2] ]; | ||
| 4253 | assert(equal(splitter!(a => a.front == 1)(w), [ [[0]], [[2]] ])); | ||
| 4254 | } | ||
| 4255 | |||
| 4256 | private struct SplitterResult(alias isTerminator, Range) | ||
| 4257 | { | ||
| 4258 | import std.algorithm.searching : find; | ||
| 4259 | enum fullSlicing = (hasLength!Range && hasSlicing!Range) || isSomeString!Range; | ||
| 4260 | |||
| 4261 | private Range _input; | ||
| 4262 | private size_t _end = 0; | ||
| 4263 | static if (!fullSlicing) | ||
| 4264 | private Range _next; | ||
| 4265 | |||
| 4266 | private void findTerminator() | ||
| 4267 | { | ||
| 4268 | static if (fullSlicing) | ||
| 4269 | { | ||
| 4270 | auto r = find!isTerminator(_input.save); | ||
| 4271 | _end = _input.length - r.length; | ||
| 4272 | } | ||
| 4273 | else | ||
| 4274 | for ( _end = 0; !_next.empty ; _next.popFront) | ||
| 4275 | { | ||
| 4276 | if (isTerminator(_next.front)) | ||
| 4277 | break; | ||
| 4278 | ++_end; | ||
| 4279 | } | ||
| 4280 | } | ||
| 4281 | |||
| 4282 | this(Range input) | ||
| 4283 | { | ||
| 4284 | _input = input; | ||
| 4285 | static if (!fullSlicing) | ||
| 4286 | _next = _input.save; | ||
| 4287 | |||
| 4288 | if (!_input.empty) | ||
| 4289 | findTerminator(); | ||
| 4290 | else | ||
| 4291 | _end = size_t.max; | ||
| 4292 | } | ||
| 4293 | |||
| 4294 | static if (isInfinite!Range) | ||
| 4295 | { | ||
| 4296 | enum bool empty = false; // Propagate infiniteness. | ||
| 4297 | } | ||
| 4298 | else | ||
| 4299 | { | ||
| 4300 | @property bool empty() | ||
| 4301 | { | ||
| 4302 | return _end == size_t.max; | ||
| 4303 | } | ||
| 4304 | } | ||
| 4305 | |||
| 4306 | @property auto front() | ||
| 4307 | { | ||
| 4308 | version (assert) | ||
| 4309 | { | ||
| 4310 | import core.exception : RangeError; | ||
| 4311 | if (empty) | ||
| 4312 | throw new RangeError(); | ||
| 4313 | } | ||
| 4314 | static if (fullSlicing) | ||
| 4315 | return _input[0 .. _end]; | ||
| 4316 | else | ||
| 4317 | { | ||
| 4318 | import std.range : takeExactly; | ||
| 4319 | return _input.takeExactly(_end); | ||
| 4320 | } | ||
| 4321 | } | ||
| 4322 | |||
| 4323 | void popFront() | ||
| 4324 | { | ||
| 4325 | version (assert) | ||
| 4326 | { | ||
| 4327 | import core.exception : RangeError; | ||
| 4328 | if (empty) | ||
| 4329 | throw new RangeError(); | ||
| 4330 | } | ||
| 4331 | |||
| 4332 | static if (fullSlicing) | ||
| 4333 | { | ||
| 4334 | _input = _input[_end .. _input.length]; | ||
| 4335 | if (_input.empty) | ||
| 4336 | { | ||
| 4337 | _end = size_t.max; | ||
| 4338 | return; | ||
| 4339 | } | ||
| 4340 | _input.popFront(); | ||
| 4341 | } | ||
| 4342 | else | ||
| 4343 | { | ||
| 4344 | if (_next.empty) | ||
| 4345 | { | ||
| 4346 | _input = _next; | ||
| 4347 | _end = size_t.max; | ||
| 4348 | return; | ||
| 4349 | } | ||
| 4350 | _next.popFront(); | ||
| 4351 | _input = _next.save; | ||
| 4352 | } | ||
| 4353 | findTerminator(); | ||
| 4354 | } | ||
| 4355 | |||
| 4356 | @property typeof(this) save() | ||
| 4357 | { | ||
| 4358 | auto ret = this; | ||
| 4359 | ret._input = _input.save; | ||
| 4360 | static if (!fullSlicing) | ||
| 4361 | ret._next = _next.save; | ||
| 4362 | return ret; | ||
| 4363 | } | ||
| 4364 | } | ||
| 4365 | |||
| 4366 | @safe unittest | ||
| 4367 | { | ||
| 4368 | import std.algorithm.comparison : equal; | ||
| 4369 | import std.range : iota; | ||
| 4370 | |||
| 4371 | auto L = iota(1L, 10L); | ||
| 4372 | auto s = splitter(L, [5L, 6L]); | ||
| 4373 | assert(equal(s.front, [1L, 2L, 3L, 4L])); | ||
| 4374 | s.popFront(); | ||
| 4375 | assert(equal(s.front, [7L, 8L, 9L])); | ||
| 4376 | s.popFront(); | ||
| 4377 | assert(s.empty); | ||
| 4378 | } | ||
| 4379 | |||
| 4380 | @safe unittest | ||
| 4381 | { | ||
| 4382 | import std.algorithm.comparison : equal; | ||
| 4383 | import std.algorithm.internal : algoFormat; | ||
| 4384 | import std.internal.test.dummyrange; | ||
| 4385 | |||
| 4386 | void compare(string sentence, string[] witness) | ||
| 4387 | { | ||
| 4388 | auto r = splitter!"a == ' '"(sentence); | ||
| 4389 | assert(equal(r.save, witness), algoFormat("got: %(%s, %) expected: %(%s, %)", r, witness)); | ||
| 4390 | } | ||
| 4391 | |||
| 4392 | compare(" Mary has a little lamb. ", | ||
| 4393 | ["", "Mary", "", "has", "a", "little", "lamb.", "", "", ""]); | ||
| 4394 | compare("Mary has a little lamb. ", | ||
| 4395 | ["Mary", "", "has", "a", "little", "lamb.", "", "", ""]); | ||
| 4396 | compare("Mary has a little lamb.", | ||
| 4397 | ["Mary", "", "has", "a", "little", "lamb."]); | ||
| 4398 | compare("", (string[]).init); | ||
| 4399 | compare(" ", ["", ""]); | ||
| 4400 | |||
| 4401 | static assert(isForwardRange!(typeof(splitter!"a == ' '"("ABC")))); | ||
| 4402 | |||
| 4403 | foreach (DummyType; AllDummyRanges) | ||
| 4404 | { | ||
| 4405 | static if (isRandomAccessRange!DummyType) | ||
| 4406 | { | ||
| 4407 | auto rangeSplit = splitter!"a == 5"(DummyType.init); | ||
| 4408 | assert(equal(rangeSplit.front, [1,2,3,4])); | ||
| 4409 | rangeSplit.popFront(); | ||
| 4410 | assert(equal(rangeSplit.front, [6,7,8,9,10])); | ||
| 4411 | } | ||
| 4412 | } | ||
| 4413 | } | ||
| 4414 | |||
| 4415 | @safe unittest | ||
| 4416 | { | ||
| 4417 | import std.algorithm.comparison : equal; | ||
| 4418 | import std.algorithm.internal : algoFormat; | ||
| 4419 | import std.range; | ||
| 4420 | |||
| 4421 | struct Entry | ||
| 4422 | { | ||
| 4423 | int low; | ||
| 4424 | int high; | ||
| 4425 | int[][] result; | ||
| 4426 | } | ||
| 4427 | Entry[] entries = [ | ||
| 4428 | Entry(0, 0, []), | ||
| 4429 | Entry(0, 1, [[0]]), | ||
| 4430 | Entry(1, 2, [[], []]), | ||
| 4431 | Entry(2, 7, [[2], [4], [6]]), | ||
| 4432 | Entry(1, 8, [[], [2], [4], [6], []]), | ||
| 4433 | ]; | ||
| 4434 | foreach ( entry ; entries ) | ||
| 4435 | { | ||
| 4436 | auto a = iota(entry.low, entry.high).filter!"true"(); | ||
| 4437 | auto b = splitter!"a%2"(a); | ||
| 4438 | assert(equal!equal(b.save, entry.result), algoFormat("got: %(%s, %) expected: %(%s, %)", b, entry.result)); | ||
| 4439 | } | ||
| 4440 | } | ||
| 4441 | |||
| 4442 | @safe unittest | ||
| 4443 | { | ||
| 4444 | import std.algorithm.comparison : equal; | ||
| 4445 | import std.uni : isWhite; | ||
| 4446 | |||
| 4447 | //@@@6791@@@ | ||
| 4448 | assert(equal( | ||
| 4449 | splitter("là dove terminava quella valle"), | ||
| 4450 | ["là", "dove", "terminava", "quella", "valle"] | ||
| 4451 | )); | ||
| 4452 | assert(equal( | ||
| 4453 | splitter!(std.uni.isWhite)("là dove terminava quella valle"), | ||
| 4454 | ["là", "dove", "terminava", "quella", "valle"] | ||
| 4455 | )); | ||
| 4456 | assert(equal(splitter!"a=='本'"("日本語"), ["日", "語"])); | ||
| 4457 | } | ||
| 4458 | |||
| 4459 | /++ | ||
| 4460 | Lazily splits the string $(D s) into words, using whitespace as the delimiter. | ||
| 4461 | |||
| 4462 | This function is string specific and, contrary to | ||
| 4463 | $(D splitter!(std.uni.isWhite)), runs of whitespace will be merged together | ||
| 4464 | (no empty tokens will be produced). | ||
| 4465 | |||
| 4466 | Params: | ||
| 4467 | s = The string to be split. | ||
| 4468 | |||
| 4469 | Returns: | ||
| 4470 | An $(REF_ALTTEXT input range, isInputRange, std,range,primitives) of slices of | ||
| 4471 | the original string split by whitespace. | ||
| 4472 | +/ | ||
| 4473 | auto splitter(C)(C[] s) | ||
| 4474 | if (isSomeChar!C) | ||
| 4475 | { | ||
| 4476 | import std.algorithm.searching : find; | ||
| 4477 | static struct Result | ||
| 4478 | { | ||
| 4479 | private: | ||
| 4480 | import core.exception : RangeError; | ||
| 4481 | C[] _s; | ||
| 4482 | size_t _frontLength; | ||
| 4483 | |||
| 4484 | void getFirst() pure @safe | ||
| 4485 | { | ||
| 4486 | import std.uni : isWhite; | ||
| 4487 | |||
| 4488 | auto r = find!(isWhite)(_s); | ||
| 4489 | _frontLength = _s.length - r.length; | ||
| 4490 | } | ||
| 4491 | |||
| 4492 | public: | ||
| 4493 | this(C[] s) pure @safe | ||
| 4494 | { | ||
| 4495 | import std.string : strip; | ||
| 4496 | _s = s.strip(); | ||
| 4497 | getFirst(); | ||
| 4498 | } | ||
| 4499 | |||
| 4500 | @property C[] front() pure @safe | ||
| 4501 | { | ||
| 4502 | version (assert) if (empty) throw new RangeError(); | ||
| 4503 | return _s[0 .. _frontLength]; | ||
| 4504 | } | ||
| 4505 | |||
| 4506 | void popFront() pure @safe | ||
| 4507 | { | ||
| 4508 | import std.string : stripLeft; | ||
| 4509 | version (assert) if (empty) throw new RangeError(); | ||
| 4510 | _s = _s[_frontLength .. $].stripLeft(); | ||
| 4511 | getFirst(); | ||
| 4512 | } | ||
| 4513 | |||
| 4514 | @property bool empty() const @safe pure nothrow | ||
| 4515 | { | ||
| 4516 | return _s.empty; | ||
| 4517 | } | ||
| 4518 | |||
| 4519 | @property inout(Result) save() inout @safe pure nothrow | ||
| 4520 | { | ||
| 4521 | return this; | ||
| 4522 | } | ||
| 4523 | } | ||
| 4524 | return Result(s); | ||
| 4525 | } | ||
| 4526 | |||
| 4527 | /// | ||
| 4528 | @safe pure unittest | ||
| 4529 | { | ||
| 4530 | import std.algorithm.comparison : equal; | ||
| 4531 | auto a = " a bcd ef gh "; | ||
| 4532 | assert(equal(splitter(a), ["a", "bcd", "ef", "gh"][])); | ||
| 4533 | } | ||
| 4534 | |||
| 4535 | @safe pure unittest | ||
| 4536 | { | ||
| 4537 | import std.algorithm.comparison : equal; | ||
| 4538 | import std.meta : AliasSeq; | ||
| 4539 | foreach (S; AliasSeq!(string, wstring, dstring)) | ||
| 4540 | { | ||
| 4541 | import std.conv : to; | ||
| 4542 | S a = " a bcd ef gh "; | ||
| 4543 | assert(equal(splitter(a), [to!S("a"), to!S("bcd"), to!S("ef"), to!S("gh")])); | ||
| 4544 | a = ""; | ||
| 4545 | assert(splitter(a).empty); | ||
| 4546 | } | ||
| 4547 | |||
| 4548 | immutable string s = " a bcd ef gh "; | ||
| 4549 | assert(equal(splitter(s), ["a", "bcd", "ef", "gh"][])); | ||
| 4550 | } | ||
| 4551 | |||
| 4552 | @safe unittest | ||
| 4553 | { | ||
| 4554 | import std.conv : to; | ||
| 4555 | import std.string : strip; | ||
| 4556 | |||
| 4557 | // TDPL example, page 8 | ||
| 4558 | uint[string] dictionary; | ||
| 4559 | char[][3] lines; | ||
| 4560 | lines[0] = "line one".dup; | ||
| 4561 | lines[1] = "line \ttwo".dup; | ||
| 4562 | lines[2] = "yah last line\ryah".dup; | ||
| 4563 | foreach (line; lines) | ||
| 4564 | { | ||
| 4565 | foreach (word; splitter(strip(line))) | ||
| 4566 | { | ||
| 4567 | if (word in dictionary) continue; // Nothing to do | ||
| 4568 | auto newID = dictionary.length; | ||
| 4569 | dictionary[to!string(word)] = cast(uint) newID; | ||
| 4570 | } | ||
| 4571 | } | ||
| 4572 | assert(dictionary.length == 5); | ||
| 4573 | assert(dictionary["line"]== 0); | ||
| 4574 | assert(dictionary["one"]== 1); | ||
| 4575 | assert(dictionary["two"]== 2); | ||
| 4576 | assert(dictionary["yah"]== 3); | ||
| 4577 | assert(dictionary["last"]== 4); | ||
| 4578 | } | ||
| 4579 | |||
| 4580 | @safe unittest | ||
| 4581 | { | ||
| 4582 | import std.algorithm.comparison : equal; | ||
| 4583 | import std.algorithm.internal : algoFormat; | ||
| 4584 | import std.array : split; | ||
| 4585 | import std.conv : text; | ||
| 4586 | |||
| 4587 | // Check consistency: | ||
| 4588 | // All flavors of split should produce the same results | ||
| 4589 | foreach (input; [(int[]).init, | ||
| 4590 | [0], | ||
| 4591 | [0, 1, 0], | ||
| 4592 | [1, 1, 0, 0, 1, 1], | ||
| 4593 | ]) | ||
| 4594 | { | ||
| 4595 | foreach (s; [0, 1]) | ||
| 4596 | { | ||
| 4597 | auto result = split(input, s); | ||
| 4598 | |||
| 4599 | assert(equal(result, split(input, [s])), algoFormat(`"[%(%s,%)]"`, split(input, [s]))); | ||
| 4600 | //assert(equal(result, split(input, [s].filter!"true"()))); //Not yet implemented | ||
| 4601 | assert(equal(result, split!((a) => a == s)(input)), text(split!((a) => a == s)(input))); | ||
| 4602 | |||
| 4603 | //assert(equal!equal(result, split(input.filter!"true"(), s))); //Not yet implemented | ||
| 4604 | //assert(equal!equal(result, split(input.filter!"true"(), [s]))); //Not yet implemented | ||
| 4605 | //assert(equal!equal(result, split(input.filter!"true"(), [s].filter!"true"()))); //Not yet implemented | ||
| 4606 | assert(equal!equal(result, split!((a) => a == s)(input.filter!"true"()))); | ||
| 4607 | |||
| 4608 | assert(equal(result, splitter(input, s))); | ||
| 4609 | assert(equal(result, splitter(input, [s]))); | ||
| 4610 | //assert(equal(result, splitter(input, [s].filter!"true"()))); //Not yet implemented | ||
| 4611 | assert(equal(result, splitter!((a) => a == s)(input))); | ||
| 4612 | |||
| 4613 | //assert(equal!equal(result, splitter(input.filter!"true"(), s))); //Not yet implemented | ||
| 4614 | //assert(equal!equal(result, splitter(input.filter!"true"(), [s]))); //Not yet implemented | ||
| 4615 | //assert(equal!equal(result, splitter(input.filter!"true"(), [s].filter!"true"()))); //Not yet implemented | ||
| 4616 | assert(equal!equal(result, splitter!((a) => a == s)(input.filter!"true"()))); | ||
| 4617 | } | ||
| 4618 | } | ||
| 4619 | foreach (input; [string.init, | ||
| 4620 | " ", | ||
| 4621 | " hello ", | ||
| 4622 | "hello hello", | ||
| 4623 | " hello what heck this ? " | ||
| 4624 | ]) | ||
| 4625 | { | ||
| 4626 | foreach (s; [' ', 'h']) | ||
| 4627 | { | ||
| 4628 | auto result = split(input, s); | ||
| 4629 | |||
| 4630 | assert(equal(result, split(input, [s]))); | ||
| 4631 | //assert(equal(result, split(input, [s].filter!"true"()))); //Not yet implemented | ||
| 4632 | assert(equal(result, split!((a) => a == s)(input))); | ||
| 4633 | |||
| 4634 | //assert(equal!equal(result, split(input.filter!"true"(), s))); //Not yet implemented | ||
| 4635 | //assert(equal!equal(result, split(input.filter!"true"(), [s]))); //Not yet implemented | ||
| 4636 | //assert(equal!equal(result, split(input.filter!"true"(), [s].filter!"true"()))); //Not yet implemented | ||
| 4637 | assert(equal!equal(result, split!((a) => a == s)(input.filter!"true"()))); | ||
| 4638 | |||
| 4639 | assert(equal(result, splitter(input, s))); | ||
| 4640 | assert(equal(result, splitter(input, [s]))); | ||
| 4641 | //assert(equal(result, splitter(input, [s].filter!"true"()))); //Not yet implemented | ||
| 4642 | assert(equal(result, splitter!((a) => a == s)(input))); | ||
| 4643 | |||
| 4644 | //assert(equal!equal(result, splitter(input.filter!"true"(), s))); //Not yet implemented | ||
| 4645 | //assert(equal!equal(result, splitter(input.filter!"true"(), [s]))); //Not yet implemented | ||
| 4646 | //assert(equal!equal(result, splitter(input.filter!"true"(), [s].filter!"true"()))); //Not yet implemented | ||
| 4647 | assert(equal!equal(result, splitter!((a) => a == s)(input.filter!"true"()))); | ||
| 4648 | } | ||
| 4649 | } | ||
| 4650 | } | ||
| 4651 | |||
| 4652 | // sum | ||
| 4653 | /** | ||
| 4654 | Sums elements of $(D r), which must be a finite | ||
| 4655 | $(REF_ALTTEXT input range, isInputRange, std,range,primitives). Although | ||
| 4656 | conceptually $(D sum(r)) is equivalent to $(LREF fold)!((a, b) => a + | ||
| 4657 | b)(r, 0), $(D sum) uses specialized algorithms to maximize accuracy, | ||
| 4658 | as follows. | ||
| 4659 | |||
| 4660 | $(UL | ||
| 4661 | $(LI If $(D $(REF ElementType, std,range,primitives)!R) is a floating-point | ||
| 4662 | type and $(D R) is a | ||
| 4663 | $(REF_ALTTEXT random-access range, isRandomAccessRange, std,range,primitives) with | ||
| 4664 | length and slicing, then $(D sum) uses the | ||
| 4665 | $(HTTP en.wikipedia.org/wiki/Pairwise_summation, pairwise summation) | ||
| 4666 | algorithm.) | ||
| 4667 | $(LI If $(D ElementType!R) is a floating-point type and $(D R) is a | ||
| 4668 | finite input range (but not a random-access range with slicing), then | ||
| 4669 | $(D sum) uses the $(HTTP en.wikipedia.org/wiki/Kahan_summation, | ||
| 4670 | Kahan summation) algorithm.) | ||
| 4671 | $(LI In all other cases, a simple element by element addition is done.) | ||
| 4672 | ) | ||
| 4673 | |||
| 4674 | For floating point inputs, calculations are made in | ||
| 4675 | $(DDLINK spec/type, Types, $(D real)) | ||
| 4676 | precision for $(D real) inputs and in $(D double) precision otherwise | ||
| 4677 | (Note this is a special case that deviates from $(D fold)'s behavior, | ||
| 4678 | which would have kept $(D float) precision for a $(D float) range). | ||
| 4679 | For all other types, the calculations are done in the same type obtained | ||
| 4680 | from from adding two elements of the range, which may be a different | ||
| 4681 | type from the elements themselves (for example, in case of | ||
| 4682 | $(DDSUBLINK spec/type,integer-promotions, integral promotion)). | ||
| 4683 | |||
| 4684 | A seed may be passed to $(D sum). Not only will this seed be used as an initial | ||
| 4685 | value, but its type will override all the above, and determine the algorithm | ||
| 4686 | and precision used for summation. | ||
| 4687 | |||
| 4688 | Note that these specialized summing algorithms execute more primitive operations | ||
| 4689 | than vanilla summation. Therefore, if in certain cases maximum speed is required | ||
| 4690 | at expense of precision, one can use $(D fold!((a, b) => a + b)(r, 0)), which | ||
| 4691 | is not specialized for summation. | ||
| 4692 | |||
| 4693 | Params: | ||
| 4694 | seed = the initial value of the summation | ||
| 4695 | r = a finite input range | ||
| 4696 | |||
| 4697 | Returns: | ||
| 4698 | The sum of all the elements in the range r. | ||
| 4699 | */ | ||
| 4700 | auto sum(R)(R r) | ||
| 4701 | if (isInputRange!R && !isInfinite!R && is(typeof(r.front + r.front))) | ||
| 4702 | { | ||
| 4703 | alias E = Unqual!(ElementType!R); | ||
| 4704 | static if (isFloatingPoint!E) | ||
| 4705 | alias Seed = typeof(E.init + 0.0); //biggest of double/real | ||
| 4706 | else | ||
| 4707 | alias Seed = typeof(r.front + r.front); | ||
| 4708 | return sum(r, Unqual!Seed(0)); | ||
| 4709 | } | ||
| 4710 | /// ditto | ||
| 4711 | auto sum(R, E)(R r, E seed) | ||
| 4712 | if (isInputRange!R && !isInfinite!R && is(typeof(seed = seed + r.front))) | ||
| 4713 | { | ||
| 4714 | static if (isFloatingPoint!E) | ||
| 4715 | { | ||
| 4716 | static if (hasLength!R && hasSlicing!R) | ||
| 4717 | { | ||
| 4718 | if (r.empty) return seed; | ||
| 4719 | return seed + sumPairwise!E(r); | ||
| 4720 | } | ||
| 4721 | else | ||
| 4722 | return sumKahan!E(seed, r); | ||
| 4723 | } | ||
| 4724 | else | ||
| 4725 | { | ||
| 4726 | return reduce!"a + b"(seed, r); | ||
| 4727 | } | ||
| 4728 | } | ||
| 4729 | |||
| 4730 | // Pairwise summation http://en.wikipedia.org/wiki/Pairwise_summation | ||
| 4731 | private auto sumPairwise(F, R)(R data) | ||
| 4732 | if (isInputRange!R && !isInfinite!R) | ||
| 4733 | { | ||
| 4734 | import core.bitop : bsf; | ||
| 4735 | // Works for r with at least length < 2^^(64 + log2(16)), in keeping with the use of size_t | ||
| 4736 | // elsewhere in std.algorithm and std.range on 64 bit platforms. The 16 in log2(16) comes | ||
| 4737 | // from the manual unrolling in sumPairWise16 | ||
| 4738 | F[64] store = void; | ||
| 4739 | size_t idx = 0; | ||
| 4740 | |||
| 4741 | void collapseStore(T)(T k) | ||
| 4742 | { | ||
| 4743 | auto lastToKeep = idx - cast(uint) bsf(k+1); | ||
| 4744 | while (idx > lastToKeep) | ||
| 4745 | { | ||
| 4746 | store[idx - 1] += store[idx]; | ||
| 4747 | --idx; | ||
| 4748 | } | ||
| 4749 | } | ||
| 4750 | |||
| 4751 | static if (hasLength!R) | ||
| 4752 | { | ||
| 4753 | foreach (k; 0 .. data.length / 16) | ||
| 4754 | { | ||
| 4755 | static if (isRandomAccessRange!R && hasSlicing!R) | ||
| 4756 | { | ||
| 4757 | store[idx] = sumPairwise16!F(data); | ||
| 4758 | data = data[16 .. data.length]; | ||
| 4759 | } | ||
| 4760 | else store[idx] = sumPairwiseN!(16, false, F)(data); | ||
| 4761 | |||
| 4762 | collapseStore(k); | ||
| 4763 | ++idx; | ||
| 4764 | } | ||
| 4765 | |||
| 4766 | size_t i = 0; | ||
| 4767 | foreach (el; data) | ||
| 4768 | { | ||
| 4769 | store[idx] = el; | ||
| 4770 | collapseStore(i); | ||
| 4771 | ++idx; | ||
| 4772 | ++i; | ||
| 4773 | } | ||
| 4774 | } | ||
| 4775 | else | ||
| 4776 | { | ||
| 4777 | size_t k = 0; | ||
| 4778 | while (!data.empty) | ||
| 4779 | { | ||
| 4780 | store[idx] = sumPairwiseN!(16, true, F)(data); | ||
| 4781 | collapseStore(k); | ||
| 4782 | ++idx; | ||
| 4783 | ++k; | ||
| 4784 | } | ||
| 4785 | } | ||
| 4786 | |||
| 4787 | F s = store[idx - 1]; | ||
| 4788 | foreach_reverse (j; 0 .. idx - 1) | ||
| 4789 | s += store[j]; | ||
| 4790 | |||
| 4791 | return s; | ||
| 4792 | } | ||
| 4793 | |||
| 4794 | private auto sumPairwise16(F, R)(R r) | ||
| 4795 | if (isRandomAccessRange!R) | ||
| 4796 | { | ||
| 4797 | return (((cast(F) r[ 0] + r[ 1]) + (cast(F) r[ 2] + r[ 3])) | ||
| 4798 | + ((cast(F) r[ 4] + r[ 5]) + (cast(F) r[ 6] + r[ 7]))) | ||
| 4799 | + (((cast(F) r[ 8] + r[ 9]) + (cast(F) r[10] + r[11])) | ||
| 4800 | + ((cast(F) r[12] + r[13]) + (cast(F) r[14] + r[15]))); | ||
| 4801 | } | ||
| 4802 | |||
| 4803 | private auto sumPair(bool needEmptyChecks, F, R)(ref R r) | ||
| 4804 | if (isForwardRange!R && !isRandomAccessRange!R) | ||
| 4805 | { | ||
| 4806 | static if (needEmptyChecks) if (r.empty) return F(0); | ||
| 4807 | F s0 = r.front; | ||
| 4808 | r.popFront(); | ||
| 4809 | static if (needEmptyChecks) if (r.empty) return s0; | ||
| 4810 | s0 += r.front; | ||
| 4811 | r.popFront(); | ||
| 4812 | return s0; | ||
| 4813 | } | ||
| 4814 | |||
| 4815 | private auto sumPairwiseN(size_t N, bool needEmptyChecks, F, R)(ref R r) | ||
| 4816 | if (isForwardRange!R && !isRandomAccessRange!R) | ||
| 4817 | { | ||
| 4818 | import std.math : isPowerOf2; | ||
| 4819 | static assert(isPowerOf2(N)); | ||
| 4820 | static if (N == 2) return sumPair!(needEmptyChecks, F)(r); | ||
| 4821 | else return sumPairwiseN!(N/2, needEmptyChecks, F)(r) | ||
| 4822 | + sumPairwiseN!(N/2, needEmptyChecks, F)(r); | ||
| 4823 | } | ||
| 4824 | |||
| 4825 | // Kahan algo http://en.wikipedia.org/wiki/Kahan_summation_algorithm | ||
| 4826 | private auto sumKahan(Result, R)(Result result, R r) | ||
| 4827 | { | ||
| 4828 | static assert(isFloatingPoint!Result && isMutable!Result); | ||
| 4829 | Result c = 0; | ||
| 4830 | for (; !r.empty; r.popFront()) | ||
| 4831 | { | ||
| 4832 | immutable y = r.front - c; | ||
| 4833 | immutable t = result + y; | ||
| 4834 | c = (t - result) - y; | ||
| 4835 | result = t; | ||
| 4836 | } | ||
| 4837 | return result; | ||
| 4838 | } | ||
| 4839 | |||
| 4840 | /// Ditto | ||
| 4841 | @safe pure nothrow unittest | ||
| 4842 | { | ||
| 4843 | import std.range; | ||
| 4844 | |||
| 4845 | //simple integral sumation | ||
| 4846 | assert(sum([ 1, 2, 3, 4]) == 10); | ||
| 4847 | |||
| 4848 | //with integral promotion | ||
| 4849 | assert(sum([false, true, true, false, true]) == 3); | ||
| 4850 | assert(sum(ubyte.max.repeat(100)) == 25500); | ||
| 4851 | |||
| 4852 | //The result may overflow | ||
| 4853 | assert(uint.max.repeat(3).sum() == 4294967293U ); | ||
| 4854 | //But a seed can be used to change the sumation primitive | ||
| 4855 | assert(uint.max.repeat(3).sum(ulong.init) == 12884901885UL); | ||
| 4856 | |||
| 4857 | //Floating point sumation | ||
| 4858 | assert(sum([1.0, 2.0, 3.0, 4.0]) == 10); | ||
| 4859 | |||
| 4860 | //Floating point operations have double precision minimum | ||
| 4861 | static assert(is(typeof(sum([1F, 2F, 3F, 4F])) == double)); | ||
| 4862 | assert(sum([1F, 2, 3, 4]) == 10); | ||
| 4863 | |||
| 4864 | //Force pair-wise floating point sumation on large integers | ||
| 4865 | import std.math : approxEqual; | ||
| 4866 | assert(iota(ulong.max / 2, ulong.max / 2 + 4096).sum(0.0) | ||
| 4867 | .approxEqual((ulong.max / 2) * 4096.0 + 4096^^2 / 2)); | ||
| 4868 | } | ||
| 4869 | |||
| 4870 | @safe pure nothrow unittest | ||
| 4871 | { | ||
| 4872 | static assert(is(typeof(sum([cast( byte) 1])) == int)); | ||
| 4873 | static assert(is(typeof(sum([cast(ubyte) 1])) == int)); | ||
| 4874 | static assert(is(typeof(sum([ 1, 2, 3, 4])) == int)); | ||
| 4875 | static assert(is(typeof(sum([ 1U, 2U, 3U, 4U])) == uint)); | ||
| 4876 | static assert(is(typeof(sum([ 1L, 2L, 3L, 4L])) == long)); | ||
| 4877 | static assert(is(typeof(sum([1UL, 2UL, 3UL, 4UL])) == ulong)); | ||
| 4878 | |||
| 4879 | int[] empty; | ||
| 4880 | assert(sum(empty) == 0); | ||
| 4881 | assert(sum([42]) == 42); | ||
| 4882 | assert(sum([42, 43]) == 42 + 43); | ||
| 4883 | assert(sum([42, 43, 44]) == 42 + 43 + 44); | ||
| 4884 | assert(sum([42, 43, 44, 45]) == 42 + 43 + 44 + 45); | ||
| 4885 | } | ||
| 4886 | |||
| 4887 | @safe pure nothrow unittest | ||
| 4888 | { | ||
| 4889 | static assert(is(typeof(sum([1.0, 2.0, 3.0, 4.0])) == double)); | ||
| 4890 | static assert(is(typeof(sum([ 1F, 2F, 3F, 4F])) == double)); | ||
| 4891 | const(float[]) a = [1F, 2F, 3F, 4F]; | ||
| 4892 | assert(sum(a) == 10F); | ||
| 4893 | static assert(is(typeof(sum(a)) == double)); | ||
| 4894 | |||
| 4895 | double[] empty; | ||
| 4896 | assert(sum(empty) == 0); | ||
| 4897 | assert(sum([42.]) == 42); | ||
| 4898 | assert(sum([42., 43.]) == 42 + 43); | ||
| 4899 | assert(sum([42., 43., 44.]) == 42 + 43 + 44); | ||
| 4900 | assert(sum([42., 43., 44., 45.5]) == 42 + 43 + 44 + 45.5); | ||
| 4901 | } | ||
| 4902 | |||
| 4903 | @safe pure nothrow unittest | ||
| 4904 | { | ||
| 4905 | import std.container; | ||
| 4906 | static assert(is(typeof(sum(SList!float()[])) == double)); | ||
| 4907 | static assert(is(typeof(sum(SList!double()[])) == double)); | ||
| 4908 | static assert(is(typeof(sum(SList!real()[])) == real)); | ||
| 4909 | |||
| 4910 | assert(sum(SList!double()[]) == 0); | ||
| 4911 | assert(sum(SList!double(1)[]) == 1); | ||
| 4912 | assert(sum(SList!double(1, 2)[]) == 1 + 2); | ||
| 4913 | assert(sum(SList!double(1, 2, 3)[]) == 1 + 2 + 3); | ||
| 4914 | assert(sum(SList!double(1, 2, 3, 4)[]) == 10); | ||
| 4915 | } | ||
| 4916 | |||
| 4917 | @safe pure nothrow unittest // 12434 | ||
| 4918 | { | ||
| 4919 | immutable a = [10, 20]; | ||
| 4920 | auto s1 = sum(a); | ||
| 4921 | assert(s1 == 30); | ||
| 4922 | auto s2 = a.map!(x => x).sum; | ||
| 4923 | assert(s2 == 30); | ||
| 4924 | } | ||
| 4925 | |||
| 4926 | @system unittest | ||
| 4927 | { | ||
| 4928 | import std.bigint; | ||
| 4929 | import std.range; | ||
| 4930 | |||
| 4931 | immutable BigInt[] a = BigInt("1_000_000_000_000_000_000").repeat(10).array(); | ||
| 4932 | immutable ulong[] b = (ulong.max/2).repeat(10).array(); | ||
| 4933 | auto sa = a.sum(); | ||
| 4934 | auto sb = b.sum(BigInt(0)); //reduce ulongs into bigint | ||
| 4935 | assert(sa == BigInt("10_000_000_000_000_000_000")); | ||
| 4936 | assert(sb == (BigInt(ulong.max/2) * 10)); | ||
| 4937 | } | ||
| 4938 | |||
| 4939 | @safe pure nothrow @nogc unittest | ||
| 4940 | { | ||
| 4941 | import std.range; | ||
| 4942 | foreach (n; iota(50)) | ||
| 4943 | assert(repeat(1.0, n).sum == n); | ||
| 4944 | } | ||
| 4945 | |||
| 4946 | // uniq | ||
| 4947 | /** | ||
| 4948 | Lazily iterates unique consecutive elements of the given range (functionality | ||
| 4949 | akin to the $(HTTP wikipedia.org/wiki/_Uniq, _uniq) system | ||
| 4950 | utility). Equivalence of elements is assessed by using the predicate | ||
| 4951 | $(D pred), by default $(D "a == b"). The predicate is passed to | ||
| 4952 | $(REF binaryFun, std,functional), and can either accept a string, or any callable | ||
| 4953 | that can be executed via $(D pred(element, element)). If the given range is | ||
| 4954 | bidirectional, $(D uniq) also yields a | ||
| 4955 | $(REF_ALTTEXT bidirectional range, isBidirectionalRange, std,range,primitives). | ||
| 4956 | |||
| 4957 | Params: | ||
| 4958 | pred = Predicate for determining equivalence between range elements. | ||
| 4959 | r = An $(REF_ALTTEXT input range, isInputRange, std,range,primitives) of | ||
| 4960 | elements to filter. | ||
| 4961 | |||
| 4962 | Returns: | ||
| 4963 | An $(REF_ALTTEXT input range, isInputRange, std,range,primitives) of | ||
| 4964 | consecutively unique elements in the original range. If $(D r) is also a | ||
| 4965 | forward range or bidirectional range, the returned range will be likewise. | ||
| 4966 | */ | ||
| 4967 | auto uniq(alias pred = "a == b", Range)(Range r) | ||
| 4968 | if (isInputRange!Range && is(typeof(binaryFun!pred(r.front, r.front)) == bool)) | ||
| 4969 | { | ||
| 4970 | return UniqResult!(binaryFun!pred, Range)(r); | ||
| 4971 | } | ||
| 4972 | |||
| 4973 | /// | ||
| 4974 | @safe unittest | ||
| 4975 | { | ||
| 4976 | import std.algorithm.comparison : equal; | ||
| 4977 | import std.algorithm.mutation : copy; | ||
| 4978 | |||
| 4979 | int[] arr = [ 1, 2, 2, 2, 2, 3, 4, 4, 4, 5 ]; | ||
| 4980 | assert(equal(uniq(arr), [ 1, 2, 3, 4, 5 ][])); | ||
| 4981 | |||
| 4982 | // Filter duplicates in-place using copy | ||
| 4983 | arr.length -= arr.uniq().copy(arr).length; | ||
| 4984 | assert(arr == [ 1, 2, 3, 4, 5 ]); | ||
| 4985 | |||
| 4986 | // Note that uniqueness is only determined consecutively; duplicated | ||
| 4987 | // elements separated by an intervening different element will not be | ||
| 4988 | // eliminated: | ||
| 4989 | assert(equal(uniq([ 1, 1, 2, 1, 1, 3, 1]), [1, 2, 1, 3, 1])); | ||
| 4990 | } | ||
| 4991 | |||
| 4992 | private struct UniqResult(alias pred, Range) | ||
| 4993 | { | ||
| 4994 | Range _input; | ||
| 4995 | |||
| 4996 | this(Range input) | ||
| 4997 | { | ||
| 4998 | _input = input; | ||
| 4999 | } | ||
188 more lines aren’t shown here.
| @@ -0,0 +1,2909 @@ | |||
| 1 | // Written in the D programming language. | ||
| 2 | /** | ||
| 3 | This is a submodule of $(MREF std, algorithm). | ||
| 4 | It contains generic _mutation algorithms. | ||
| 5 | |||
| 6 | $(SCRIPT inhibitQuickIndex = 1;) | ||
| 7 | $(BOOKTABLE Cheat Sheet, | ||
| 8 | $(TR $(TH Function Name) $(TH Description)) | ||
| 9 | $(T2 bringToFront, | ||
| 10 | If $(D a = [1, 2, 3]) and $(D b = [4, 5, 6, 7]), | ||
| 11 | $(D bringToFront(a, b)) leaves $(D a = [4, 5, 6]) and | ||
| 12 | $(D b = [7, 1, 2, 3]).) | ||
| 13 | $(T2 copy, | ||
| 14 | Copies a range to another. If | ||
| 15 | $(D a = [1, 2, 3]) and $(D b = new int[5]), then $(D copy(a, b)) | ||
| 16 | leaves $(D b = [1, 2, 3, 0, 0]) and returns $(D b[3 .. $]).) | ||
| 17 | $(T2 fill, | ||
| 18 | Fills a range with a pattern, | ||
| 19 | e.g., if $(D a = new int[3]), then $(D fill(a, 4)) | ||
| 20 | leaves $(D a = [4, 4, 4]) and $(D fill(a, [3, 4])) leaves | ||
| 21 | $(D a = [3, 4, 3]).) | ||
| 22 | $(T2 initializeAll, | ||
| 23 | If $(D a = [1.2, 3.4]), then $(D initializeAll(a)) leaves | ||
| 24 | $(D a = [double.init, double.init]).) | ||
| 25 | $(T2 move, | ||
| 26 | $(D move(a, b)) moves $(D a) into $(D b). $(D move(a)) reads $(D a) | ||
| 27 | destructively when necessary.) | ||
| 28 | $(T2 moveEmplace, | ||
| 29 | Similar to $(D move) but assumes `target` is uninitialized.) | ||
| 30 | $(T2 moveAll, | ||
| 31 | Moves all elements from one range to another.) | ||
| 32 | $(T2 moveEmplaceAll, | ||
| 33 | Similar to $(D moveAll) but assumes all elements in `target` are uninitialized.) | ||
| 34 | $(T2 moveSome, | ||
| 35 | Moves as many elements as possible from one range to another.) | ||
| 36 | $(T2 moveEmplaceSome, | ||
| 37 | Similar to $(D moveSome) but assumes all elements in `target` are uninitialized.) | ||
| 38 | $(T2 remove, | ||
| 39 | Removes elements from a range in-place, and returns the shortened | ||
| 40 | range.) | ||
| 41 | $(T2 reverse, | ||
| 42 | If $(D a = [1, 2, 3]), $(D reverse(a)) changes it to $(D [3, 2, 1]).) | ||
| 43 | $(T2 strip, | ||
| 44 | Strips all leading and trailing elements equal to a value, or that | ||
| 45 | satisfy a predicate. | ||
| 46 | If $(D a = [1, 1, 0, 1, 1]), then $(D strip(a, 1)) and | ||
| 47 | $(D strip!(e => e == 1)(a)) returns $(D [0]).) | ||
| 48 | $(T2 stripLeft, | ||
| 49 | Strips all leading elements equal to a value, or that satisfy a | ||
| 50 | predicate. If $(D a = [1, 1, 0, 1, 1]), then $(D stripLeft(a, 1)) and | ||
| 51 | $(D stripLeft!(e => e == 1)(a)) returns $(D [0, 1, 1]).) | ||
| 52 | $(T2 stripRight, | ||
| 53 | Strips all trailing elements equal to a value, or that satisfy a | ||
| 54 | predicate. | ||
| 55 | If $(D a = [1, 1, 0, 1, 1]), then $(D stripRight(a, 1)) and | ||
| 56 | $(D stripRight!(e => e == 1)(a)) returns $(D [1, 1, 0]).) | ||
| 57 | $(T2 swap, | ||
| 58 | Swaps two values.) | ||
| 59 | $(T2 swapAt, | ||
| 60 | Swaps two values by indices.) | ||
| 61 | $(T2 swapRanges, | ||
| 62 | Swaps all elements of two ranges.) | ||
| 63 | $(T2 uninitializedFill, | ||
| 64 | Fills a range (assumed uninitialized) with a value.) | ||
| 65 | ) | ||
| 66 | |||
| 67 | Copyright: Andrei Alexandrescu 2008-. | ||
| 68 | |||
| 69 | License: $(HTTP boost.org/LICENSE_1_0.txt, Boost License 1.0). | ||
| 70 | |||
| 71 | Authors: $(HTTP erdani.com, Andrei Alexandrescu) | ||
| 72 | |||
| 73 | Source: $(PHOBOSSRC std/algorithm/_mutation.d) | ||
| 74 | |||
| 75 | Macros: | ||
| 76 | T2=$(TR $(TDNW $(LREF $1)) $(TD $+)) | ||
| 77 | */ | ||
| 78 | module std.algorithm.mutation; | ||
| 79 | |||
| 80 | import std.range.primitives; | ||
| 81 | import std.traits : isArray, isBlitAssignable, isNarrowString, Unqual, isSomeChar; | ||
| 82 | // FIXME | ||
| 83 | import std.typecons; // : tuple, Tuple; | ||
| 84 | |||
| 85 | // bringToFront | ||
| 86 | /** | ||
| 87 | The $(D bringToFront) function has considerable flexibility and | ||
| 88 | usefulness. It can rotate elements in one buffer left or right, swap | ||
| 89 | buffers of equal length, and even move elements across disjoint | ||
| 90 | buffers of different types and different lengths. | ||
| 91 | |||
| 92 | $(D bringToFront) takes two ranges $(D front) and $(D back), which may | ||
| 93 | be of different types. Considering the concatenation of $(D front) and | ||
| 94 | $(D back) one unified range, $(D bringToFront) rotates that unified | ||
| 95 | range such that all elements in $(D back) are brought to the beginning | ||
| 96 | of the unified range. The relative ordering of elements in $(D front) | ||
| 97 | and $(D back), respectively, remains unchanged. | ||
| 98 | |||
| 99 | The $(D bringToFront) function treats strings at the code unit | ||
| 100 | level and it is not concerned with Unicode character integrity. | ||
| 101 | $(D bringToFront) is designed as a function for moving elements | ||
| 102 | in ranges, not as a string function. | ||
| 103 | |||
| 104 | Performs $(BIGOH max(front.length, back.length)) evaluations of $(D | ||
| 105 | swap). | ||
| 106 | |||
| 107 | Preconditions: | ||
| 108 | |||
| 109 | Either $(D front) and $(D back) are disjoint, or $(D back) is | ||
| 110 | reachable from $(D front) and $(D front) is not reachable from $(D | ||
| 111 | back). | ||
| 112 | |||
| 113 | Params: | ||
| 114 | front = an $(REF_ALTTEXT input range, isInputRange, std,range,primitives) | ||
| 115 | back = a $(REF_ALTTEXT forward range, isForwardRange, std,range,primitives) | ||
| 116 | |||
| 117 | Returns: | ||
| 118 | The number of elements brought to the front, i.e., the length of $(D back). | ||
| 119 | |||
| 120 | See_Also: | ||
| 121 | $(HTTP sgi.com/tech/stl/_rotate.html, STL's rotate) | ||
| 122 | */ | ||
| 123 | size_t bringToFront(InputRange, ForwardRange)(InputRange front, ForwardRange back) | ||
| 124 | if (isInputRange!InputRange && isForwardRange!ForwardRange) | ||
| 125 | { | ||
| 126 | import std.string : representation; | ||
| 127 | |||
| 128 | static if (isNarrowString!InputRange) | ||
| 129 | { | ||
| 130 | auto frontW = representation(front); | ||
| 131 | } | ||
| 132 | else | ||
| 133 | { | ||
| 134 | alias frontW = front; | ||
| 135 | } | ||
| 136 | static if (isNarrowString!ForwardRange) | ||
| 137 | { | ||
| 138 | auto backW = representation(back); | ||
| 139 | } | ||
| 140 | else | ||
| 141 | { | ||
| 142 | alias backW = back; | ||
| 143 | } | ||
| 144 | |||
| 145 | return bringToFrontImpl(frontW, backW); | ||
| 146 | } | ||
| 147 | |||
| 148 | private size_t bringToFrontImpl(InputRange, ForwardRange)(InputRange front, ForwardRange back) | ||
| 149 | if (isInputRange!InputRange && isForwardRange!ForwardRange) | ||
| 150 | { | ||
| 151 | import std.array : sameHead; | ||
| 152 | import std.range : take, Take; | ||
| 153 | enum bool sameHeadExists = is(typeof(front.sameHead(back))); | ||
| 154 | size_t result; | ||
| 155 | |||
| 156 | for (bool semidone; !front.empty && !back.empty; ) | ||
| 157 | { | ||
| 158 | static if (sameHeadExists) | ||
| 159 | { | ||
| 160 | if (front.sameHead(back)) break; // shortcut | ||
| 161 | } | ||
| 162 | // Swap elements until front and/or back ends. | ||
| 163 | auto back0 = back.save; | ||
| 164 | size_t nswaps; | ||
| 165 | do | ||
| 166 | { | ||
| 167 | static if (sameHeadExists) | ||
| 168 | { | ||
| 169 | // Detect the stepping-over condition. | ||
| 170 | if (front.sameHead(back0)) back0 = back.save; | ||
| 171 | } | ||
| 172 | swapFront(front, back); | ||
| 173 | ++nswaps; | ||
| 174 | front.popFront(); | ||
| 175 | back.popFront(); | ||
| 176 | } | ||
| 177 | while (!front.empty && !back.empty); | ||
| 178 | |||
| 179 | if (!semidone) result += nswaps; | ||
| 180 | |||
| 181 | // Now deal with the remaining elements. | ||
| 182 | if (back.empty) | ||
| 183 | { | ||
| 184 | if (front.empty) break; | ||
| 185 | // Right side was shorter, which means that we've brought | ||
| 186 | // all the back elements to the front. | ||
| 187 | semidone = true; | ||
| 188 | // Next pass: bringToFront(front, back0) to adjust the rest. | ||
| 189 | back = back0; | ||
| 190 | } | ||
| 191 | else | ||
| 192 | { | ||
| 193 | assert(front.empty); | ||
| 194 | // Left side was shorter. Let's step into the back. | ||
| 195 | static if (is(InputRange == Take!ForwardRange)) | ||
| 196 | { | ||
| 197 | front = take(back0, nswaps); | ||
| 198 | } | ||
| 199 | else | ||
| 200 | { | ||
| 201 | immutable subresult = bringToFront(take(back0, nswaps), | ||
| 202 | back); | ||
| 203 | if (!semidone) result += subresult; | ||
| 204 | break; // done | ||
| 205 | } | ||
| 206 | } | ||
| 207 | } | ||
| 208 | return result; | ||
| 209 | } | ||
| 210 | |||
| 211 | /** | ||
| 212 | The simplest use of $(D bringToFront) is for rotating elements in a | ||
| 213 | buffer. For example: | ||
| 214 | */ | ||
| 215 | @safe unittest | ||
| 216 | { | ||
| 217 | auto arr = [4, 5, 6, 7, 1, 2, 3]; | ||
| 218 | auto p = bringToFront(arr[0 .. 4], arr[4 .. $]); | ||
| 219 | assert(p == arr.length - 4); | ||
| 220 | assert(arr == [ 1, 2, 3, 4, 5, 6, 7 ]); | ||
| 221 | } | ||
| 222 | |||
| 223 | /** | ||
| 224 | The $(D front) range may actually "step over" the $(D back) | ||
| 225 | range. This is very useful with forward ranges that cannot compute | ||
| 226 | comfortably right-bounded subranges like $(D arr[0 .. 4]) above. In | ||
| 227 | the example below, $(D r2) is a right subrange of $(D r1). | ||
| 228 | */ | ||
| 229 | @safe unittest | ||
| 230 | { | ||
| 231 | import std.algorithm.comparison : equal; | ||
| 232 | import std.container : SList; | ||
| 233 | import std.range.primitives : popFrontN; | ||
| 234 | |||
| 235 | auto list = SList!(int)(4, 5, 6, 7, 1, 2, 3); | ||
| 236 | auto r1 = list[]; | ||
| 237 | auto r2 = list[]; popFrontN(r2, 4); | ||
| 238 | assert(equal(r2, [ 1, 2, 3 ])); | ||
| 239 | bringToFront(r1, r2); | ||
| 240 | assert(equal(list[], [ 1, 2, 3, 4, 5, 6, 7 ])); | ||
| 241 | } | ||
| 242 | |||
| 243 | /** | ||
| 244 | Elements can be swapped across ranges of different types: | ||
| 245 | */ | ||
| 246 | @safe unittest | ||
| 247 | { | ||
| 248 | import std.algorithm.comparison : equal; | ||
| 249 | import std.container : SList; | ||
| 250 | |||
| 251 | auto list = SList!(int)(4, 5, 6, 7); | ||
| 252 | auto vec = [ 1, 2, 3 ]; | ||
| 253 | bringToFront(list[], vec); | ||
| 254 | assert(equal(list[], [ 1, 2, 3, 4 ])); | ||
| 255 | assert(equal(vec, [ 5, 6, 7 ])); | ||
| 256 | } | ||
| 257 | |||
| 258 | /** | ||
| 259 | Unicode integrity is not preserved: | ||
| 260 | */ | ||
| 261 | @safe unittest | ||
| 262 | { | ||
| 263 | import std.string : representation; | ||
| 264 | auto ar = representation("a".dup); | ||
| 265 | auto br = representation("ç".dup); | ||
| 266 | |||
| 267 | bringToFront(ar, br); | ||
| 268 | |||
| 269 | auto a = cast(char[]) ar; | ||
| 270 | auto b = cast(char[]) br; | ||
| 271 | |||
| 272 | // Illegal UTF-8 | ||
| 273 | assert(a == "\303"); | ||
| 274 | // Illegal UTF-8 | ||
| 275 | assert(b == "\247a"); | ||
| 276 | } | ||
| 277 | |||
| 278 | @safe unittest | ||
| 279 | { | ||
| 280 | import std.algorithm.comparison : equal; | ||
| 281 | import std.conv : text; | ||
| 282 | import std.random : Random, unpredictableSeed, uniform; | ||
| 283 | |||
| 284 | // a more elaborate test | ||
| 285 | { | ||
| 286 | auto rnd = Random(unpredictableSeed); | ||
| 287 | int[] a = new int[uniform(100, 200, rnd)]; | ||
| 288 | int[] b = new int[uniform(100, 200, rnd)]; | ||
| 289 | foreach (ref e; a) e = uniform(-100, 100, rnd); | ||
| 290 | foreach (ref e; b) e = uniform(-100, 100, rnd); | ||
| 291 | int[] c = a ~ b; | ||
| 292 | // writeln("a= ", a); | ||
| 293 | // writeln("b= ", b); | ||
| 294 | auto n = bringToFront(c[0 .. a.length], c[a.length .. $]); | ||
| 295 | //writeln("c= ", c); | ||
| 296 | assert(n == b.length); | ||
| 297 | assert(c == b ~ a, text(c, "\n", a, "\n", b)); | ||
| 298 | } | ||
| 299 | // different types, moveFront, no sameHead | ||
| 300 | { | ||
| 301 | static struct R(T) | ||
| 302 | { | ||
| 303 | T[] data; | ||
| 304 | size_t i; | ||
| 305 | @property | ||
| 306 | { | ||
| 307 | R save() { return this; } | ||
| 308 | bool empty() { return i >= data.length; } | ||
| 309 | T front() { return data[i]; } | ||
| 310 | T front(real e) { return data[i] = cast(T) e; } | ||
| 311 | } | ||
| 312 | void popFront() { ++i; } | ||
| 313 | } | ||
| 314 | auto a = R!int([1, 2, 3, 4, 5]); | ||
| 315 | auto b = R!real([6, 7, 8, 9]); | ||
| 316 | auto n = bringToFront(a, b); | ||
| 317 | assert(n == 4); | ||
| 318 | assert(a.data == [6, 7, 8, 9, 1]); | ||
| 319 | assert(b.data == [2, 3, 4, 5]); | ||
| 320 | } | ||
| 321 | // front steps over back | ||
| 322 | { | ||
| 323 | int[] arr, r1, r2; | ||
| 324 | |||
| 325 | // back is shorter | ||
| 326 | arr = [4, 5, 6, 7, 1, 2, 3]; | ||
| 327 | r1 = arr; | ||
| 328 | r2 = arr[4 .. $]; | ||
| 329 | bringToFront(r1, r2) == 3 || assert(0); | ||
| 330 | assert(equal(arr, [1, 2, 3, 4, 5, 6, 7])); | ||
| 331 | |||
| 332 | // front is shorter | ||
| 333 | arr = [5, 6, 7, 1, 2, 3, 4]; | ||
| 334 | r1 = arr; | ||
| 335 | r2 = arr[3 .. $]; | ||
| 336 | bringToFront(r1, r2) == 4 || assert(0); | ||
| 337 | assert(equal(arr, [1, 2, 3, 4, 5, 6, 7])); | ||
| 338 | } | ||
| 339 | |||
| 340 | // Bugzilla 16959 | ||
| 341 | auto arr = ['4', '5', '6', '7', '1', '2', '3']; | ||
| 342 | auto p = bringToFront(arr[0 .. 4], arr[4 .. $]); | ||
| 343 | |||
| 344 | assert(p == arr.length - 4); | ||
| 345 | assert(arr == ['1', '2', '3', '4', '5', '6', '7']); | ||
| 346 | } | ||
| 347 | |||
| 348 | // Tests if types are arrays and support slice assign. | ||
| 349 | private enum bool areCopyCompatibleArrays(T1, T2) = | ||
| 350 | isArray!T1 && isArray!T2 && is(typeof(T2.init[] = T1.init[])); | ||
| 351 | |||
| 352 | // copy | ||
| 353 | /** | ||
| 354 | Copies the content of $(D source) into $(D target) and returns the | ||
| 355 | remaining (unfilled) part of $(D target). | ||
| 356 | |||
| 357 | Preconditions: $(D target) shall have enough room to accommodate | ||
| 358 | the entirety of $(D source). | ||
| 359 | |||
| 360 | Params: | ||
| 361 | source = an $(REF_ALTTEXT input range, isInputRange, std,range,primitives) | ||
| 362 | target = an output range | ||
| 363 | |||
| 364 | Returns: | ||
| 365 | The unfilled part of target | ||
| 366 | |||
| 367 | See_Also: | ||
| 368 | $(HTTP sgi.com/tech/stl/_copy.html, STL's _copy) | ||
| 369 | */ | ||
| 370 | TargetRange copy(SourceRange, TargetRange)(SourceRange source, TargetRange target) | ||
| 371 | if (areCopyCompatibleArrays!(SourceRange, TargetRange)) | ||
| 372 | { | ||
| 373 | const tlen = target.length; | ||
| 374 | const slen = source.length; | ||
| 375 | assert(tlen >= slen, | ||
| 376 | "Cannot copy a source range into a smaller target range."); | ||
| 377 | |||
| 378 | immutable overlaps = __ctfe || () @trusted { | ||
| 379 | return source.ptr < target.ptr + tlen && | ||
| 380 | target.ptr < source.ptr + slen; }(); | ||
| 381 | |||
| 382 | if (overlaps) | ||
| 383 | { | ||
| 384 | foreach (idx; 0 .. slen) | ||
| 385 | target[idx] = source[idx]; | ||
| 386 | return target[slen .. tlen]; | ||
| 387 | } | ||
| 388 | else | ||
| 389 | { | ||
| 390 | // Array specialization. This uses optimized memory copying | ||
| 391 | // routines under the hood and is about 10-20x faster than the | ||
| 392 | // generic implementation. | ||
| 393 | target[0 .. slen] = source[]; | ||
| 394 | return target[slen .. $]; | ||
| 395 | } | ||
| 396 | } | ||
| 397 | |||
| 398 | /// ditto | ||
| 399 | TargetRange copy(SourceRange, TargetRange)(SourceRange source, TargetRange target) | ||
| 400 | if (!areCopyCompatibleArrays!(SourceRange, TargetRange) && | ||
| 401 | isInputRange!SourceRange && | ||
| 402 | isOutputRange!(TargetRange, ElementType!SourceRange)) | ||
| 403 | { | ||
| 404 | // Specialize for 2 random access ranges. | ||
| 405 | // Typically 2 random access ranges are faster iterated by common | ||
| 406 | // index than by x.popFront(), y.popFront() pair | ||
| 407 | static if (isRandomAccessRange!SourceRange && | ||
| 408 | hasLength!SourceRange && | ||
| 409 | hasSlicing!TargetRange && | ||
| 410 | isRandomAccessRange!TargetRange && | ||
| 411 | hasLength!TargetRange) | ||
| 412 | { | ||
| 413 | auto len = source.length; | ||
| 414 | foreach (idx; 0 .. len) | ||
| 415 | target[idx] = source[idx]; | ||
| 416 | return target[len .. target.length]; | ||
| 417 | } | ||
| 418 | else | ||
| 419 | { | ||
| 420 | put(target, source); | ||
| 421 | return target; | ||
| 422 | } | ||
| 423 | } | ||
| 424 | |||
| 425 | /// | ||
| 426 | @safe unittest | ||
| 427 | { | ||
| 428 | int[] a = [ 1, 5 ]; | ||
| 429 | int[] b = [ 9, 8 ]; | ||
| 430 | int[] buf = new int[](a.length + b.length + 10); | ||
| 431 | auto rem = a.copy(buf); // copy a into buf | ||
| 432 | rem = b.copy(rem); // copy b into remainder of buf | ||
| 433 | assert(buf[0 .. a.length + b.length] == [1, 5, 9, 8]); | ||
| 434 | assert(rem.length == 10); // unused slots in buf | ||
| 435 | } | ||
| 436 | |||
| 437 | /** | ||
| 438 | As long as the target range elements support assignment from source | ||
| 439 | range elements, different types of ranges are accepted: | ||
| 440 | */ | ||
| 441 | @safe unittest | ||
| 442 | { | ||
| 443 | float[] src = [ 1.0f, 5 ]; | ||
| 444 | double[] dest = new double[src.length]; | ||
| 445 | src.copy(dest); | ||
| 446 | } | ||
| 447 | |||
| 448 | /** | ||
| 449 | To _copy at most $(D n) elements from a range, you may want to use | ||
| 450 | $(REF take, std,range): | ||
| 451 | */ | ||
| 452 | @safe unittest | ||
| 453 | { | ||
| 454 | import std.range; | ||
| 455 | int[] src = [ 1, 5, 8, 9, 10 ]; | ||
| 456 | auto dest = new int[](3); | ||
| 457 | src.take(dest.length).copy(dest); | ||
| 458 | assert(dest == [ 1, 5, 8 ]); | ||
| 459 | } | ||
| 460 | |||
| 461 | /** | ||
| 462 | To _copy just those elements from a range that satisfy a predicate, | ||
| 463 | use $(LREF filter): | ||
| 464 | */ | ||
| 465 | @safe unittest | ||
| 466 | { | ||
| 467 | import std.algorithm.iteration : filter; | ||
| 468 | int[] src = [ 1, 5, 8, 9, 10, 1, 2, 0 ]; | ||
| 469 | auto dest = new int[src.length]; | ||
| 470 | auto rem = src | ||
| 471 | .filter!(a => (a & 1) == 1) | ||
| 472 | .copy(dest); | ||
| 473 | assert(dest[0 .. $ - rem.length] == [ 1, 5, 9, 1 ]); | ||
| 474 | } | ||
| 475 | |||
| 476 | /** | ||
| 477 | $(REF retro, std,range) can be used to achieve behavior similar to | ||
| 478 | $(HTTP sgi.com/tech/stl/copy_backward.html, STL's copy_backward'): | ||
| 479 | */ | ||
| 480 | @safe unittest | ||
| 481 | { | ||
| 482 | import std.algorithm, std.range; | ||
| 483 | int[] src = [1, 2, 4]; | ||
| 484 | int[] dest = [0, 0, 0, 0, 0]; | ||
| 485 | src.retro.copy(dest.retro); | ||
| 486 | assert(dest == [0, 0, 1, 2, 4]); | ||
| 487 | } | ||
| 488 | |||
| 489 | // Test CTFE copy. | ||
| 490 | @safe unittest | ||
| 491 | { | ||
| 492 | enum c = copy([1,2,3], [4,5,6,7]); | ||
| 493 | assert(c == [7]); | ||
| 494 | } | ||
| 495 | |||
| 496 | |||
| 497 | @safe unittest | ||
| 498 | { | ||
| 499 | import std.algorithm.iteration : filter; | ||
| 500 | |||
| 501 | { | ||
| 502 | int[] a = [ 1, 5 ]; | ||
| 503 | int[] b = [ 9, 8 ]; | ||
| 504 | auto e = copy(filter!("a > 1")(a), b); | ||
| 505 | assert(b[0] == 5 && e.length == 1); | ||
| 506 | } | ||
| 507 | |||
| 508 | { | ||
| 509 | int[] a = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; | ||
| 510 | copy(a[5 .. 10], a[4 .. 9]); | ||
| 511 | assert(a[4 .. 9] == [6, 7, 8, 9, 10]); | ||
| 512 | } | ||
| 513 | |||
| 514 | { // Test for bug 7898 | ||
| 515 | enum v = | ||
| 516 | { | ||
| 517 | import std.algorithm; | ||
| 518 | int[] arr1 = [10, 20, 30, 40, 50]; | ||
| 519 | int[] arr2 = arr1.dup; | ||
| 520 | copy(arr1, arr2); | ||
| 521 | return 35; | ||
| 522 | }(); | ||
| 523 | assert(v == 35); | ||
| 524 | } | ||
| 525 | } | ||
| 526 | |||
| 527 | @safe unittest | ||
| 528 | { | ||
| 529 | // Issue 13650 | ||
| 530 | import std.meta : AliasSeq; | ||
| 531 | foreach (Char; AliasSeq!(char, wchar, dchar)) | ||
| 532 | { | ||
| 533 | Char[3] a1 = "123"; | ||
| 534 | Char[6] a2 = "456789"; | ||
| 535 | assert(copy(a1[], a2[]) is a2[3..$]); | ||
| 536 | assert(a1[] == "123"); | ||
| 537 | assert(a2[] == "123789"); | ||
| 538 | } | ||
| 539 | } | ||
| 540 | |||
| 541 | /** | ||
| 542 | Assigns $(D value) to each element of input _range $(D range). | ||
| 543 | |||
| 544 | Params: | ||
| 545 | range = An | ||
| 546 | $(REF_ALTTEXT input _range, isInputRange, std,_range,primitives) | ||
| 547 | that exposes references to its elements and has assignable | ||
| 548 | elements | ||
| 549 | value = Assigned to each element of range | ||
| 550 | |||
| 551 | See_Also: | ||
| 552 | $(LREF uninitializedFill) | ||
| 553 | $(LREF initializeAll) | ||
| 554 | */ | ||
| 555 | void fill(Range, Value)(auto ref Range range, auto ref Value value) | ||
| 556 | if ((isInputRange!Range && is(typeof(range.front = value)) || | ||
| 557 | isSomeChar!Value && is(typeof(range[] = value)))) | ||
| 558 | { | ||
| 559 | alias T = ElementType!Range; | ||
| 560 | |||
| 561 | static if (is(typeof(range[] = value))) | ||
| 562 | { | ||
| 563 | range[] = value; | ||
| 564 | } | ||
| 565 | else static if (is(typeof(range[] = T(value)))) | ||
| 566 | { | ||
| 567 | range[] = T(value); | ||
| 568 | } | ||
| 569 | else | ||
| 570 | { | ||
| 571 | for ( ; !range.empty; range.popFront() ) | ||
| 572 | { | ||
| 573 | range.front = value; | ||
| 574 | } | ||
| 575 | } | ||
| 576 | } | ||
| 577 | |||
| 578 | /// | ||
| 579 | @safe unittest | ||
| 580 | { | ||
| 581 | int[] a = [ 1, 2, 3, 4 ]; | ||
| 582 | fill(a, 5); | ||
| 583 | assert(a == [ 5, 5, 5, 5 ]); | ||
| 584 | } | ||
| 585 | |||
| 586 | // issue 16342, test fallback on mutable narrow strings | ||
| 587 | @safe unittest | ||
| 588 | { | ||
| 589 | char[] chars = ['a', 'b']; | ||
| 590 | fill(chars, 'c'); | ||
| 591 | assert(chars == "cc"); | ||
| 592 | |||
| 593 | char[2] chars2 = ['a', 'b']; | ||
| 594 | fill(chars2, 'c'); | ||
| 595 | assert(chars2 == "cc"); | ||
| 596 | |||
| 597 | wchar[] wchars = ['a', 'b']; | ||
| 598 | fill(wchars, wchar('c')); | ||
| 599 | assert(wchars == "cc"w); | ||
| 600 | |||
| 601 | dchar[] dchars = ['a', 'b']; | ||
| 602 | fill(dchars, dchar('c')); | ||
| 603 | assert(dchars == "cc"d); | ||
| 604 | } | ||
| 605 | |||
| 606 | @nogc @safe unittest | ||
| 607 | { | ||
| 608 | const(char)[] chars; | ||
| 609 | assert(chars.length == 0); | ||
| 610 | static assert(!__traits(compiles, fill(chars, 'c'))); | ||
| 611 | wstring wchars; | ||
| 612 | assert(wchars.length == 0); | ||
| 613 | static assert(!__traits(compiles, fill(wchars, wchar('c')))); | ||
| 614 | } | ||
| 615 | |||
| 616 | @nogc @safe unittest | ||
| 617 | { | ||
| 618 | char[] chars; | ||
| 619 | fill(chars, 'c'); | ||
| 620 | assert(chars == ""c); | ||
| 621 | } | ||
| 622 | |||
| 623 | @safe unittest | ||
| 624 | { | ||
| 625 | shared(char)[] chrs = ['r']; | ||
| 626 | fill(chrs, 'c'); | ||
| 627 | assert(chrs == [shared(char)('c')]); | ||
| 628 | } | ||
| 629 | |||
| 630 | @nogc @safe unittest | ||
| 631 | { | ||
| 632 | struct Str(size_t len) | ||
| 633 | { | ||
| 634 | private char[len] _data; | ||
| 635 | void opIndexAssign(char value) @safe @nogc | ||
| 636 | {_data[] = value;} | ||
| 637 | } | ||
| 638 | Str!2 str; | ||
| 639 | str.fill(':'); | ||
| 640 | assert(str._data == "::"); | ||
| 641 | } | ||
| 642 | |||
| 643 | @safe unittest | ||
| 644 | { | ||
| 645 | char[] chars = ['a','b','c','d']; | ||
| 646 | chars[1 .. 3].fill(':'); | ||
| 647 | assert(chars == "a::d"); | ||
| 648 | } | ||
| 649 | // end issue 16342 | ||
| 650 | |||
| 651 | @safe unittest | ||
| 652 | { | ||
| 653 | import std.conv : text; | ||
| 654 | import std.internal.test.dummyrange; | ||
| 655 | |||
| 656 | int[] a = [ 1, 2, 3 ]; | ||
| 657 | fill(a, 6); | ||
| 658 | assert(a == [ 6, 6, 6 ], text(a)); | ||
| 659 | |||
| 660 | void fun0() | ||
| 661 | { | ||
| 662 | foreach (i; 0 .. 1000) | ||
| 663 | { | ||
| 664 | foreach (ref e; a) e = 6; | ||
| 665 | } | ||
| 666 | } | ||
| 667 | void fun1() { foreach (i; 0 .. 1000) fill(a, 6); } | ||
| 668 | |||
| 669 | // fill should accept InputRange | ||
| 670 | alias InputRange = DummyRange!(ReturnBy.Reference, Length.No, RangeType.Input); | ||
| 671 | enum filler = uint.max; | ||
| 672 | InputRange range; | ||
| 673 | fill(range, filler); | ||
| 674 | foreach (value; range.arr) | ||
| 675 | assert(value == filler); | ||
| 676 | } | ||
| 677 | |||
| 678 | @safe unittest | ||
| 679 | { | ||
| 680 | //ER8638_1 IS_NOT self assignable | ||
| 681 | static struct ER8638_1 | ||
| 682 | { | ||
| 683 | void opAssign(int){} | ||
| 684 | } | ||
| 685 | |||
| 686 | //ER8638_1 IS self assignable | ||
| 687 | static struct ER8638_2 | ||
| 688 | { | ||
| 689 | void opAssign(ER8638_2){} | ||
| 690 | void opAssign(int){} | ||
| 691 | } | ||
| 692 | |||
| 693 | auto er8638_1 = new ER8638_1[](10); | ||
| 694 | auto er8638_2 = new ER8638_2[](10); | ||
| 695 | er8638_1.fill(5); //generic case | ||
| 696 | er8638_2.fill(5); //opSlice(T.init) case | ||
| 697 | } | ||
| 698 | |||
| 699 | @safe unittest | ||
| 700 | { | ||
| 701 | { | ||
| 702 | int[] a = [1, 2, 3]; | ||
| 703 | immutable(int) b = 0; | ||
| 704 | a.fill(b); | ||
| 705 | assert(a == [0, 0, 0]); | ||
| 706 | } | ||
| 707 | { | ||
| 708 | double[] a = [1, 2, 3]; | ||
| 709 | immutable(int) b = 0; | ||
| 710 | a.fill(b); | ||
| 711 | assert(a == [0, 0, 0]); | ||
| 712 | } | ||
| 713 | } | ||
| 714 | |||
| 715 | /** | ||
| 716 | Fills $(D range) with a pattern copied from $(D filler). The length of | ||
| 717 | $(D range) does not have to be a multiple of the length of $(D | ||
| 718 | filler). If $(D filler) is empty, an exception is thrown. | ||
| 719 | |||
| 720 | Params: | ||
| 721 | range = An $(REF_ALTTEXT input _range, isInputRange, std,_range,primitives) | ||
| 722 | that exposes references to its elements and has assignable elements. | ||
| 723 | filler = The | ||
| 724 | $(REF_ALTTEXT forward _range, isForwardRange, std,_range,primitives) | ||
| 725 | representing the _fill pattern. | ||
| 726 | */ | ||
| 727 | void fill(InputRange, ForwardRange)(InputRange range, ForwardRange filler) | ||
| 728 | if (isInputRange!InputRange | ||
| 729 | && (isForwardRange!ForwardRange | ||
| 730 | || (isInputRange!ForwardRange && isInfinite!ForwardRange)) | ||
| 731 | && is(typeof(InputRange.init.front = ForwardRange.init.front))) | ||
| 732 | { | ||
| 733 | static if (isInfinite!ForwardRange) | ||
| 734 | { | ||
| 735 | //ForwardRange is infinite, no need for bounds checking or saving | ||
| 736 | static if (hasSlicing!ForwardRange && hasLength!InputRange | ||
| 737 | && is(typeof(filler[0 .. range.length]))) | ||
| 738 | { | ||
| 739 | copy(filler[0 .. range.length], range); | ||
| 740 | } | ||
| 741 | else | ||
| 742 | { | ||
| 743 | //manual feed | ||
| 744 | for ( ; !range.empty; range.popFront(), filler.popFront()) | ||
| 745 | { | ||
| 746 | range.front = filler.front; | ||
| 747 | } | ||
| 748 | } | ||
| 749 | } | ||
| 750 | else | ||
| 751 | { | ||
| 752 | import std.exception : enforce; | ||
| 753 | |||
| 754 | enforce(!filler.empty, "Cannot fill range with an empty filler"); | ||
| 755 | |||
| 756 | static if (hasLength!InputRange && hasLength!ForwardRange | ||
| 757 | && is(typeof(range.length > filler.length))) | ||
| 758 | { | ||
| 759 | //Case we have access to length | ||
| 760 | immutable len = filler.length; | ||
| 761 | //Start by bulk copies | ||
| 762 | while (range.length > len) | ||
| 763 | { | ||
| 764 | range = copy(filler.save, range); | ||
| 765 | } | ||
| 766 | |||
| 767 | //and finally fill the partial range. No need to save here. | ||
| 768 | static if (hasSlicing!ForwardRange && is(typeof(filler[0 .. range.length]))) | ||
| 769 | { | ||
| 770 | //use a quick copy | ||
| 771 | auto len2 = range.length; | ||
| 772 | range = copy(filler[0 .. len2], range); | ||
| 773 | } | ||
| 774 | else | ||
| 775 | { | ||
| 776 | //iterate. No need to check filler, it's length is longer than range's | ||
| 777 | for (; !range.empty; range.popFront(), filler.popFront()) | ||
| 778 | { | ||
| 779 | range.front = filler.front; | ||
| 780 | } | ||
| 781 | } | ||
| 782 | } | ||
| 783 | else | ||
| 784 | { | ||
| 785 | //Most basic case. | ||
| 786 | auto bck = filler.save; | ||
| 787 | for (; !range.empty; range.popFront(), filler.popFront()) | ||
| 788 | { | ||
| 789 | if (filler.empty) filler = bck.save; | ||
| 790 | range.front = filler.front; | ||
| 791 | } | ||
| 792 | } | ||
| 793 | } | ||
| 794 | } | ||
| 795 | |||
| 796 | /// | ||
| 797 | @safe unittest | ||
| 798 | { | ||
| 799 | int[] a = [ 1, 2, 3, 4, 5 ]; | ||
| 800 | int[] b = [ 8, 9 ]; | ||
| 801 | fill(a, b); | ||
| 802 | assert(a == [ 8, 9, 8, 9, 8 ]); | ||
| 803 | } | ||
| 804 | |||
| 805 | @safe unittest | ||
| 806 | { | ||
| 807 | import std.exception : assertThrown; | ||
| 808 | import std.internal.test.dummyrange; | ||
| 809 | |||
| 810 | int[] a = [ 1, 2, 3, 4, 5 ]; | ||
| 811 | int[] b = [1, 2]; | ||
| 812 | fill(a, b); | ||
| 813 | assert(a == [ 1, 2, 1, 2, 1 ]); | ||
| 814 | |||
| 815 | // fill should accept InputRange | ||
| 816 | alias InputRange = DummyRange!(ReturnBy.Reference, Length.No, RangeType.Input); | ||
| 817 | InputRange range; | ||
| 818 | fill(range,[1,2]); | ||
| 819 | foreach (i,value;range.arr) | ||
| 820 | assert(value == (i%2 == 0?1:2)); | ||
| 821 | |||
| 822 | //test with a input being a "reference forward" range | ||
| 823 | fill(a, new ReferenceForwardRange!int([8, 9])); | ||
| 824 | assert(a == [8, 9, 8, 9, 8]); | ||
| 825 | |||
| 826 | //test with a input being an "infinite input" range | ||
| 827 | fill(a, new ReferenceInfiniteInputRange!int()); | ||
| 828 | assert(a == [0, 1, 2, 3, 4]); | ||
| 829 | |||
| 830 | //empty filler test | ||
| 831 | assertThrown(fill(a, a[$..$])); | ||
| 832 | } | ||
| 833 | |||
| 834 | /** | ||
| 835 | Initializes all elements of $(D range) with their $(D .init) value. | ||
| 836 | Assumes that the elements of the range are uninitialized. | ||
| 837 | |||
| 838 | Params: | ||
| 839 | range = An | ||
| 840 | $(REF_ALTTEXT input _range, isInputRange, std,_range,primitives) | ||
| 841 | that exposes references to its elements and has assignable | ||
| 842 | elements | ||
| 843 | |||
| 844 | See_Also: | ||
| 845 | $(LREF fill) | ||
| 846 | $(LREF uninitializeFill) | ||
| 847 | */ | ||
| 848 | void initializeAll(Range)(Range range) | ||
| 849 | if (isInputRange!Range && hasLvalueElements!Range && hasAssignableElements!Range) | ||
| 850 | { | ||
| 851 | import core.stdc.string : memset, memcpy; | ||
| 852 | import std.traits : hasElaborateAssign, isDynamicArray; | ||
| 853 | |||
| 854 | alias T = ElementType!Range; | ||
| 855 | static if (hasElaborateAssign!T) | ||
| 856 | { | ||
| 857 | import std.algorithm.internal : addressOf; | ||
| 858 | //Elaborate opAssign. Must go the memcpy road. | ||
| 859 | //We avoid calling emplace here, because our goal is to initialize to | ||
| 860 | //the static state of T.init, | ||
| 861 | //So we want to avoid any un-necassarilly CC'ing of T.init | ||
| 862 | auto p = typeid(T).initializer(); | ||
| 863 | if (p.ptr) | ||
| 864 | { | ||
| 865 | for ( ; !range.empty ; range.popFront() ) | ||
| 866 | { | ||
| 867 | static if (__traits(isStaticArray, T)) | ||
| 868 | { | ||
| 869 | // static array initializer only contains initialization | ||
| 870 | // for one element of the static array. | ||
| 871 | auto elemp = cast(void *) addressOf(range.front); | ||
| 872 | auto endp = elemp + T.sizeof; | ||
| 873 | while (elemp < endp) | ||
| 874 | { | ||
| 875 | memcpy(elemp, p.ptr, p.length); | ||
| 876 | elemp += p.length; | ||
| 877 | } | ||
| 878 | } | ||
| 879 | else | ||
| 880 | { | ||
| 881 | memcpy(addressOf(range.front), p.ptr, T.sizeof); | ||
| 882 | } | ||
| 883 | } | ||
| 884 | } | ||
| 885 | else | ||
| 886 | static if (isDynamicArray!Range) | ||
| 887 | memset(range.ptr, 0, range.length * T.sizeof); | ||
| 888 | else | ||
| 889 | for ( ; !range.empty ; range.popFront() ) | ||
| 890 | memset(addressOf(range.front), 0, T.sizeof); | ||
| 891 | } | ||
| 892 | else | ||
| 893 | fill(range, T.init); | ||
| 894 | } | ||
| 895 | |||
| 896 | /// ditto | ||
| 897 | void initializeAll(Range)(Range range) | ||
| 898 | if (is(Range == char[]) || is(Range == wchar[])) | ||
| 899 | { | ||
| 900 | alias T = ElementEncodingType!Range; | ||
| 901 | range[] = T.init; | ||
| 902 | } | ||
| 903 | |||
| 904 | /// | ||
| 905 | @system unittest | ||
| 906 | { | ||
| 907 | import core.stdc.stdlib : malloc, free; | ||
| 908 | |||
| 909 | struct S | ||
| 910 | { | ||
| 911 | int a = 10; | ||
| 912 | } | ||
| 913 | |||
| 914 | auto s = (cast(S*) malloc(5 * S.sizeof))[0 .. 5]; | ||
| 915 | initializeAll(s); | ||
| 916 | assert(s == [S(10), S(10), S(10), S(10), S(10)]); | ||
| 917 | |||
| 918 | scope(exit) free(s.ptr); | ||
| 919 | } | ||
| 920 | |||
| 921 | @system unittest | ||
| 922 | { | ||
| 923 | import std.algorithm.iteration : filter; | ||
| 924 | import std.meta : AliasSeq; | ||
| 925 | import std.traits : hasElaborateAssign; | ||
| 926 | |||
| 927 | //Test strings: | ||
| 928 | //Must work on narrow strings. | ||
| 929 | //Must reject const | ||
| 930 | char[3] a = void; | ||
| 931 | a[].initializeAll(); | ||
| 932 | assert(a[] == [char.init, char.init, char.init]); | ||
| 933 | string s; | ||
| 934 | assert(!__traits(compiles, s.initializeAll())); | ||
| 935 | assert(!__traits(compiles, s.initializeAll())); | ||
| 936 | assert(s.empty); | ||
| 937 | |||
| 938 | //Note: Cannot call uninitializedFill on narrow strings | ||
| 939 | |||
| 940 | enum e {e1, e2} | ||
| 941 | e[3] b1 = void; | ||
| 942 | b1[].initializeAll(); | ||
| 943 | assert(b1[] == [e.e1, e.e1, e.e1]); | ||
| 944 | e[3] b2 = void; | ||
| 945 | b2[].uninitializedFill(e.e2); | ||
| 946 | assert(b2[] == [e.e2, e.e2, e.e2]); | ||
| 947 | |||
| 948 | static struct S1 | ||
| 949 | { | ||
| 950 | int i; | ||
| 951 | } | ||
| 952 | static struct S2 | ||
| 953 | { | ||
| 954 | int i = 1; | ||
| 955 | } | ||
| 956 | static struct S3 | ||
| 957 | { | ||
| 958 | int i; | ||
| 959 | this(this){} | ||
| 960 | } | ||
| 961 | static struct S4 | ||
| 962 | { | ||
| 963 | int i = 1; | ||
| 964 | this(this){} | ||
| 965 | } | ||
| 966 | static assert(!hasElaborateAssign!S1); | ||
| 967 | static assert(!hasElaborateAssign!S2); | ||
| 968 | static assert( hasElaborateAssign!S3); | ||
| 969 | static assert( hasElaborateAssign!S4); | ||
| 970 | assert(!typeid(S1).initializer().ptr); | ||
| 971 | assert( typeid(S2).initializer().ptr); | ||
| 972 | assert(!typeid(S3).initializer().ptr); | ||
| 973 | assert( typeid(S4).initializer().ptr); | ||
| 974 | |||
| 975 | foreach (S; AliasSeq!(S1, S2, S3, S4)) | ||
| 976 | { | ||
| 977 | //initializeAll | ||
| 978 | { | ||
| 979 | //Array | ||
| 980 | S[3] ss1 = void; | ||
| 981 | ss1[].initializeAll(); | ||
| 982 | assert(ss1[] == [S.init, S.init, S.init]); | ||
| 983 | |||
| 984 | //Not array | ||
| 985 | S[3] ss2 = void; | ||
| 986 | auto sf = ss2[].filter!"true"(); | ||
| 987 | |||
| 988 | sf.initializeAll(); | ||
| 989 | assert(ss2[] == [S.init, S.init, S.init]); | ||
| 990 | } | ||
| 991 | //uninitializedFill | ||
| 992 | { | ||
| 993 | //Array | ||
| 994 | S[3] ss1 = void; | ||
| 995 | ss1[].uninitializedFill(S(2)); | ||
| 996 | assert(ss1[] == [S(2), S(2), S(2)]); | ||
| 997 | |||
| 998 | //Not array | ||
| 999 | S[3] ss2 = void; | ||
| 1000 | auto sf = ss2[].filter!"true"(); | ||
| 1001 | sf.uninitializedFill(S(2)); | ||
| 1002 | assert(ss2[] == [S(2), S(2), S(2)]); | ||
| 1003 | } | ||
| 1004 | } | ||
| 1005 | } | ||
| 1006 | |||
| 1007 | // test that initializeAll works for arrays of static arrays of structs with | ||
| 1008 | // elaborate assigns. | ||
| 1009 | @system unittest | ||
| 1010 | { | ||
| 1011 | struct Int { | ||
| 1012 | ~this() {} | ||
| 1013 | int x = 3; | ||
| 1014 | } | ||
| 1015 | Int[2] xs = [Int(1), Int(2)]; | ||
| 1016 | struct R { | ||
| 1017 | bool done; | ||
| 1018 | bool empty() { return done; } | ||
| 1019 | ref Int[2] front() { return xs; } | ||
| 1020 | void popFront() { done = true; } | ||
| 1021 | } | ||
| 1022 | initializeAll(R()); | ||
| 1023 | assert(xs[0].x == 3); | ||
| 1024 | assert(xs[1].x == 3); | ||
| 1025 | } | ||
| 1026 | |||
| 1027 | // move | ||
| 1028 | /** | ||
| 1029 | Moves `source` into `target`, via a destructive copy when necessary. | ||
| 1030 | |||
| 1031 | If `T` is a struct with a destructor or postblit defined, source is reset | ||
| 1032 | to its `.init` value after it is moved into target, otherwise it is | ||
| 1033 | left unchanged. | ||
| 1034 | |||
| 1035 | Preconditions: | ||
| 1036 | If source has internal pointers that point to itself, it cannot be moved, and | ||
| 1037 | will trigger an assertion failure. | ||
| 1038 | |||
| 1039 | Params: | ||
| 1040 | source = Data to copy. | ||
| 1041 | target = Where to copy into. The destructor, if any, is invoked before the | ||
| 1042 | copy is performed. | ||
| 1043 | */ | ||
| 1044 | void move(T)(ref T source, ref T target) | ||
| 1045 | { | ||
| 1046 | // test @safe destructible | ||
| 1047 | static if (__traits(compiles, (T t) @safe {})) | ||
| 1048 | trustedMoveImpl(source, target); | ||
| 1049 | else | ||
| 1050 | moveImpl(source, target); | ||
| 1051 | } | ||
| 1052 | |||
| 1053 | /// For non-struct types, `move` just performs `target = source`: | ||
| 1054 | @safe unittest | ||
| 1055 | { | ||
| 1056 | Object obj1 = new Object; | ||
| 1057 | Object obj2 = obj1; | ||
| 1058 | Object obj3; | ||
| 1059 | |||
| 1060 | move(obj2, obj3); | ||
| 1061 | assert(obj3 is obj1); | ||
| 1062 | // obj2 unchanged | ||
| 1063 | assert(obj2 is obj1); | ||
| 1064 | } | ||
| 1065 | |||
| 1066 | /// | ||
| 1067 | pure nothrow @safe @nogc unittest | ||
| 1068 | { | ||
| 1069 | // Structs without destructors are simply copied | ||
| 1070 | struct S1 | ||
| 1071 | { | ||
| 1072 | int a = 1; | ||
| 1073 | int b = 2; | ||
| 1074 | } | ||
| 1075 | S1 s11 = { 10, 11 }; | ||
| 1076 | S1 s12; | ||
| 1077 | |||
| 1078 | move(s11, s12); | ||
| 1079 | |||
| 1080 | assert(s12 == S1(10, 11)); | ||
| 1081 | assert(s11 == s12); | ||
| 1082 | |||
| 1083 | // But structs with destructors or postblits are reset to their .init value | ||
| 1084 | // after copying to the target. | ||
| 1085 | struct S2 | ||
| 1086 | { | ||
| 1087 | int a = 1; | ||
| 1088 | int b = 2; | ||
| 1089 | |||
| 1090 | ~this() pure nothrow @safe @nogc { } | ||
| 1091 | } | ||
| 1092 | S2 s21 = { 3, 4 }; | ||
| 1093 | S2 s22; | ||
| 1094 | |||
| 1095 | move(s21, s22); | ||
| 1096 | |||
| 1097 | assert(s21 == S2(1, 2)); | ||
| 1098 | assert(s22 == S2(3, 4)); | ||
| 1099 | } | ||
| 1100 | |||
| 1101 | @safe unittest | ||
| 1102 | { | ||
| 1103 | import std.exception : assertCTFEable; | ||
| 1104 | import std.traits; | ||
| 1105 | |||
| 1106 | assertCTFEable!((){ | ||
| 1107 | Object obj1 = new Object; | ||
| 1108 | Object obj2 = obj1; | ||
| 1109 | Object obj3; | ||
| 1110 | move(obj2, obj3); | ||
| 1111 | assert(obj3 is obj1); | ||
| 1112 | |||
| 1113 | static struct S1 { int a = 1, b = 2; } | ||
| 1114 | S1 s11 = { 10, 11 }; | ||
| 1115 | S1 s12; | ||
| 1116 | move(s11, s12); | ||
| 1117 | assert(s11.a == 10 && s11.b == 11 && s12.a == 10 && s12.b == 11); | ||
| 1118 | |||
| 1119 | static struct S2 { int a = 1; int * b; } | ||
| 1120 | S2 s21 = { 10, null }; | ||
| 1121 | s21.b = new int; | ||
| 1122 | S2 s22; | ||
| 1123 | move(s21, s22); | ||
| 1124 | assert(s21 == s22); | ||
| 1125 | }); | ||
| 1126 | // Issue 5661 test(1) | ||
| 1127 | static struct S3 | ||
| 1128 | { | ||
| 1129 | static struct X { int n = 0; ~this(){n = 0;} } | ||
| 1130 | X x; | ||
| 1131 | } | ||
| 1132 | static assert(hasElaborateDestructor!S3); | ||
| 1133 | S3 s31, s32; | ||
| 1134 | s31.x.n = 1; | ||
| 1135 | move(s31, s32); | ||
| 1136 | assert(s31.x.n == 0); | ||
| 1137 | assert(s32.x.n == 1); | ||
| 1138 | |||
| 1139 | // Issue 5661 test(2) | ||
| 1140 | static struct S4 | ||
| 1141 | { | ||
| 1142 | static struct X { int n = 0; this(this){n = 0;} } | ||
| 1143 | X x; | ||
| 1144 | } | ||
| 1145 | static assert(hasElaborateCopyConstructor!S4); | ||
| 1146 | S4 s41, s42; | ||
| 1147 | s41.x.n = 1; | ||
| 1148 | move(s41, s42); | ||
| 1149 | assert(s41.x.n == 0); | ||
| 1150 | assert(s42.x.n == 1); | ||
| 1151 | |||
| 1152 | // Issue 13990 test | ||
| 1153 | class S5; | ||
| 1154 | |||
| 1155 | S5 s51; | ||
| 1156 | S5 s52 = s51; | ||
| 1157 | S5 s53; | ||
| 1158 | move(s52, s53); | ||
| 1159 | assert(s53 is s51); | ||
| 1160 | } | ||
| 1161 | |||
| 1162 | /// Ditto | ||
| 1163 | T move(T)(ref T source) | ||
| 1164 | { | ||
| 1165 | // test @safe destructible | ||
| 1166 | static if (__traits(compiles, (T t) @safe {})) | ||
| 1167 | return trustedMoveImpl(source); | ||
| 1168 | else | ||
| 1169 | return moveImpl(source); | ||
| 1170 | } | ||
| 1171 | |||
| 1172 | /// Non-copyable structs can still be moved: | ||
| 1173 | pure nothrow @safe @nogc unittest | ||
| 1174 | { | ||
| 1175 | struct S | ||
| 1176 | { | ||
| 1177 | int a = 1; | ||
| 1178 | @disable this(this); | ||
| 1179 | ~this() pure nothrow @safe @nogc {} | ||
| 1180 | } | ||
| 1181 | S s1; | ||
| 1182 | s1.a = 2; | ||
| 1183 | S s2 = move(s1); | ||
| 1184 | assert(s1.a == 1); | ||
| 1185 | assert(s2.a == 2); | ||
| 1186 | } | ||
| 1187 | |||
| 1188 | private void trustedMoveImpl(T)(ref T source, ref T target) @trusted | ||
| 1189 | { | ||
| 1190 | moveImpl(source, target); | ||
| 1191 | } | ||
| 1192 | |||
| 1193 | private void moveImpl(T)(ref T source, ref T target) | ||
| 1194 | { | ||
| 1195 | import std.traits : hasElaborateDestructor; | ||
| 1196 | |||
| 1197 | static if (is(T == struct)) | ||
| 1198 | { | ||
| 1199 | if (&source == &target) return; | ||
| 1200 | // Destroy target before overwriting it | ||
| 1201 | static if (hasElaborateDestructor!T) target.__xdtor(); | ||
| 1202 | } | ||
| 1203 | // move and emplace source into target | ||
| 1204 | moveEmplace(source, target); | ||
| 1205 | } | ||
| 1206 | |||
| 1207 | private T trustedMoveImpl(T)(ref T source) @trusted | ||
| 1208 | { | ||
| 1209 | return moveImpl(source); | ||
| 1210 | } | ||
| 1211 | |||
| 1212 | private T moveImpl(T)(ref T source) | ||
| 1213 | { | ||
| 1214 | T result = void; | ||
| 1215 | moveEmplace(source, result); | ||
| 1216 | return result; | ||
| 1217 | } | ||
| 1218 | |||
| 1219 | @safe unittest | ||
| 1220 | { | ||
| 1221 | import std.exception : assertCTFEable; | ||
| 1222 | import std.traits; | ||
| 1223 | |||
| 1224 | assertCTFEable!((){ | ||
| 1225 | Object obj1 = new Object; | ||
| 1226 | Object obj2 = obj1; | ||
| 1227 | Object obj3 = move(obj2); | ||
| 1228 | assert(obj3 is obj1); | ||
| 1229 | |||
| 1230 | static struct S1 { int a = 1, b = 2; } | ||
| 1231 | S1 s11 = { 10, 11 }; | ||
| 1232 | S1 s12 = move(s11); | ||
| 1233 | assert(s11.a == 10 && s11.b == 11 && s12.a == 10 && s12.b == 11); | ||
| 1234 | |||
| 1235 | static struct S2 { int a = 1; int * b; } | ||
| 1236 | S2 s21 = { 10, null }; | ||
| 1237 | s21.b = new int; | ||
| 1238 | S2 s22 = move(s21); | ||
| 1239 | assert(s21 == s22); | ||
| 1240 | }); | ||
| 1241 | |||
| 1242 | // Issue 5661 test(1) | ||
| 1243 | static struct S3 | ||
| 1244 | { | ||
| 1245 | static struct X { int n = 0; ~this(){n = 0;} } | ||
| 1246 | X x; | ||
| 1247 | } | ||
| 1248 | static assert(hasElaborateDestructor!S3); | ||
| 1249 | S3 s31; | ||
| 1250 | s31.x.n = 1; | ||
| 1251 | S3 s32 = move(s31); | ||
| 1252 | assert(s31.x.n == 0); | ||
| 1253 | assert(s32.x.n == 1); | ||
| 1254 | |||
| 1255 | // Issue 5661 test(2) | ||
| 1256 | static struct S4 | ||
| 1257 | { | ||
| 1258 | static struct X { int n = 0; this(this){n = 0;} } | ||
| 1259 | X x; | ||
| 1260 | } | ||
| 1261 | static assert(hasElaborateCopyConstructor!S4); | ||
| 1262 | S4 s41; | ||
| 1263 | s41.x.n = 1; | ||
| 1264 | S4 s42 = move(s41); | ||
| 1265 | assert(s41.x.n == 0); | ||
| 1266 | assert(s42.x.n == 1); | ||
| 1267 | |||
| 1268 | // Issue 13990 test | ||
| 1269 | class S5; | ||
| 1270 | |||
| 1271 | S5 s51; | ||
| 1272 | S5 s52 = s51; | ||
| 1273 | S5 s53; | ||
| 1274 | s53 = move(s52); | ||
| 1275 | assert(s53 is s51); | ||
| 1276 | } | ||
| 1277 | |||
| 1278 | @system unittest | ||
| 1279 | { | ||
| 1280 | static struct S { int n = 0; ~this() @system { n = 0; } } | ||
| 1281 | S a, b; | ||
| 1282 | static assert(!__traits(compiles, () @safe { move(a, b); })); | ||
| 1283 | static assert(!__traits(compiles, () @safe { move(a); })); | ||
| 1284 | a.n = 1; | ||
| 1285 | () @trusted { move(a, b); }(); | ||
| 1286 | assert(a.n == 0); | ||
| 1287 | a.n = 1; | ||
| 1288 | () @trusted { move(a); }(); | ||
| 1289 | assert(a.n == 0); | ||
| 1290 | } | ||
| 1291 | |||
| 1292 | @safe unittest//Issue 6217 | ||
| 1293 | { | ||
| 1294 | import std.algorithm.iteration : map; | ||
| 1295 | auto x = map!"a"([1,2,3]); | ||
| 1296 | x = move(x); | ||
| 1297 | } | ||
| 1298 | |||
| 1299 | @safe unittest// Issue 8055 | ||
| 1300 | { | ||
| 1301 | static struct S | ||
| 1302 | { | ||
| 1303 | int x; | ||
| 1304 | ~this() | ||
| 1305 | { | ||
| 1306 | assert(x == 0); | ||
| 1307 | } | ||
| 1308 | } | ||
| 1309 | S foo(S s) | ||
| 1310 | { | ||
| 1311 | return move(s); | ||
| 1312 | } | ||
| 1313 | S a; | ||
| 1314 | a.x = 0; | ||
| 1315 | auto b = foo(a); | ||
| 1316 | assert(b.x == 0); | ||
| 1317 | } | ||
| 1318 | |||
| 1319 | @system unittest// Issue 8057 | ||
| 1320 | { | ||
| 1321 | int n = 10; | ||
| 1322 | struct S | ||
| 1323 | { | ||
| 1324 | int x; | ||
| 1325 | ~this() | ||
| 1326 | { | ||
| 1327 | // Access to enclosing scope | ||
| 1328 | assert(n == 10); | ||
| 1329 | } | ||
| 1330 | } | ||
| 1331 | S foo(S s) | ||
| 1332 | { | ||
| 1333 | // Move nested struct | ||
| 1334 | return move(s); | ||
| 1335 | } | ||
| 1336 | S a; | ||
| 1337 | a.x = 1; | ||
| 1338 | auto b = foo(a); | ||
| 1339 | assert(b.x == 1); | ||
| 1340 | |||
| 1341 | // Regression 8171 | ||
| 1342 | static struct Array(T) | ||
| 1343 | { | ||
| 1344 | // nested struct has no member | ||
| 1345 | struct Payload | ||
| 1346 | { | ||
| 1347 | ~this() {} | ||
| 1348 | } | ||
| 1349 | } | ||
| 1350 | Array!int.Payload x = void; | ||
| 1351 | move(x); | ||
| 1352 | move(x, x); | ||
| 1353 | } | ||
| 1354 | |||
| 1355 | /** | ||
| 1356 | * Similar to $(LREF move) but assumes `target` is uninitialized. This | ||
| 1357 | * is more efficient because `source` can be blitted over `target` | ||
| 1358 | * without destroying or initializing it first. | ||
| 1359 | * | ||
| 1360 | * Params: | ||
| 1361 | * source = value to be moved into target | ||
| 1362 | * target = uninitialized value to be filled by source | ||
| 1363 | */ | ||
| 1364 | void moveEmplace(T)(ref T source, ref T target) @system | ||
| 1365 | { | ||
| 1366 | import core.stdc.string : memcpy, memset; | ||
| 1367 | import std.traits : hasAliasing, hasElaborateAssign, | ||
| 1368 | hasElaborateCopyConstructor, hasElaborateDestructor, | ||
| 1369 | isAssignable; | ||
| 1370 | |||
| 1371 | static if (!is(T == class) && hasAliasing!T) if (!__ctfe) | ||
| 1372 | { | ||
| 1373 | import std.exception : doesPointTo; | ||
| 1374 | assert(!doesPointTo(source, source), "Cannot move object with internal pointer."); | ||
| 1375 | } | ||
| 1376 | |||
| 1377 | static if (is(T == struct)) | ||
| 1378 | { | ||
| 1379 | assert(&source !is &target, "source and target must not be identical"); | ||
| 1380 | |||
| 1381 | static if (hasElaborateAssign!T || !isAssignable!T) | ||
| 1382 | memcpy(&target, &source, T.sizeof); | ||
| 1383 | else | ||
| 1384 | target = source; | ||
| 1385 | |||
| 1386 | // If the source defines a destructor or a postblit hook, we must obliterate the | ||
| 1387 | // object in order to avoid double freeing and undue aliasing | ||
| 1388 | static if (hasElaborateDestructor!T || hasElaborateCopyConstructor!T) | ||
| 1389 | { | ||
| 1390 | // If T is nested struct, keep original context pointer | ||
| 1391 | static if (__traits(isNested, T)) | ||
| 1392 | enum sz = T.sizeof - (void*).sizeof; | ||
| 1393 | else | ||
| 1394 | enum sz = T.sizeof; | ||
| 1395 | |||
| 1396 | auto init = typeid(T).initializer(); | ||
| 1397 | if (init.ptr is null) // null ptr means initialize to 0s | ||
| 1398 | memset(&source, 0, sz); | ||
| 1399 | else | ||
| 1400 | memcpy(&source, init.ptr, sz); | ||
| 1401 | } | ||
| 1402 | } | ||
| 1403 | else | ||
| 1404 | { | ||
| 1405 | // Primitive data (including pointers and arrays) or class - | ||
| 1406 | // assignment works great | ||
| 1407 | target = source; | ||
| 1408 | } | ||
| 1409 | } | ||
| 1410 | |||
| 1411 | /// | ||
| 1412 | pure nothrow @nogc @system unittest | ||
| 1413 | { | ||
| 1414 | static struct Foo | ||
| 1415 | { | ||
| 1416 | pure nothrow @nogc: | ||
| 1417 | this(int* ptr) { _ptr = ptr; } | ||
| 1418 | ~this() { if (_ptr) ++*_ptr; } | ||
| 1419 | int* _ptr; | ||
| 1420 | } | ||
| 1421 | |||
| 1422 | int val; | ||
| 1423 | Foo foo1 = void; // uninitialized | ||
| 1424 | auto foo2 = Foo(&val); // initialized | ||
| 1425 | assert(foo2._ptr is &val); | ||
| 1426 | |||
| 1427 | // Using `move(foo2, foo1)` would have an undefined effect because it would destroy | ||
| 1428 | // the uninitialized foo1. | ||
| 1429 | // moveEmplace directly overwrites foo1 without destroying or initializing it first. | ||
| 1430 | moveEmplace(foo2, foo1); | ||
| 1431 | assert(foo1._ptr is &val); | ||
| 1432 | assert(foo2._ptr is null); | ||
| 1433 | assert(val == 0); | ||
| 1434 | } | ||
| 1435 | |||
| 1436 | // moveAll | ||
| 1437 | /** | ||
| 1438 | Calls `move(a, b)` for each element `a` in `src` and the corresponding | ||
| 1439 | element `b` in `tgt`, in increasing order. | ||
| 1440 | |||
| 1441 | Preconditions: | ||
| 1442 | `walkLength(src) <= walkLength(tgt)`. | ||
| 1443 | This precondition will be asserted. If you cannot ensure there is enough room in | ||
| 1444 | `tgt` to accommodate all of `src` use $(LREF moveSome) instead. | ||
| 1445 | |||
| 1446 | Params: | ||
| 1447 | src = An $(REF_ALTTEXT input range, isInputRange, std,range,primitives) with | ||
| 1448 | movable elements. | ||
| 1449 | tgt = An $(REF_ALTTEXT input range, isInputRange, std,range,primitives) with | ||
| 1450 | elements that elements from $(D src) can be moved into. | ||
| 1451 | |||
| 1452 | Returns: The leftover portion of $(D tgt) after all elements from $(D src) have | ||
| 1453 | been moved. | ||
| 1454 | */ | ||
| 1455 | InputRange2 moveAll(InputRange1, InputRange2)(InputRange1 src, InputRange2 tgt) | ||
| 1456 | if (isInputRange!InputRange1 && isInputRange!InputRange2 | ||
| 1457 | && is(typeof(move(src.front, tgt.front)))) | ||
| 1458 | { | ||
| 1459 | return moveAllImpl!move(src, tgt); | ||
| 1460 | } | ||
| 1461 | |||
| 1462 | /// | ||
| 1463 | pure nothrow @safe @nogc unittest | ||
| 1464 | { | ||
| 1465 | int[3] a = [ 1, 2, 3 ]; | ||
| 1466 | int[5] b; | ||
| 1467 | assert(moveAll(a[], b[]) is b[3 .. $]); | ||
| 1468 | assert(a[] == b[0 .. 3]); | ||
| 1469 | int[3] cmp = [ 1, 2, 3 ]; | ||
| 1470 | assert(a[] == cmp[]); | ||
| 1471 | } | ||
| 1472 | |||
| 1473 | /** | ||
| 1474 | * Similar to $(LREF moveAll) but assumes all elements in `tgt` are | ||
| 1475 | * uninitialized. Uses $(LREF moveEmplace) to move elements from | ||
| 1476 | * `src` over elements from `tgt`. | ||
| 1477 | */ | ||
| 1478 | InputRange2 moveEmplaceAll(InputRange1, InputRange2)(InputRange1 src, InputRange2 tgt) @system | ||
| 1479 | if (isInputRange!InputRange1 && isInputRange!InputRange2 | ||
| 1480 | && is(typeof(moveEmplace(src.front, tgt.front)))) | ||
| 1481 | { | ||
| 1482 | return moveAllImpl!moveEmplace(src, tgt); | ||
| 1483 | } | ||
| 1484 | |||
| 1485 | /// | ||
| 1486 | pure nothrow @nogc @system unittest | ||
| 1487 | { | ||
| 1488 | static struct Foo | ||
| 1489 | { | ||
| 1490 | ~this() pure nothrow @nogc { if (_ptr) ++*_ptr; } | ||
| 1491 | int* _ptr; | ||
| 1492 | } | ||
| 1493 | int[3] refs = [0, 1, 2]; | ||
| 1494 | Foo[3] src = [Foo(&refs[0]), Foo(&refs[1]), Foo(&refs[2])]; | ||
| 1495 | Foo[5] dst = void; | ||
| 1496 | |||
| 1497 | auto tail = moveEmplaceAll(src[], dst[]); // move 3 value from src over dst | ||
| 1498 | assert(tail.length == 2); // returns remaining uninitialized values | ||
| 1499 | initializeAll(tail); | ||
| 1500 | |||
| 1501 | import std.algorithm.searching : all; | ||
| 1502 | assert(src[].all!(e => e._ptr is null)); | ||
| 1503 | assert(dst[0 .. 3].all!(e => e._ptr !is null)); | ||
| 1504 | } | ||
| 1505 | |||
| 1506 | @system unittest | ||
| 1507 | { | ||
| 1508 | struct InputRange | ||
| 1509 | { | ||
| 1510 | ref int front() { return data[0]; } | ||
| 1511 | void popFront() { data.popFront; } | ||
| 1512 | bool empty() { return data.empty; } | ||
| 1513 | int[] data; | ||
| 1514 | } | ||
| 1515 | auto a = InputRange([ 1, 2, 3 ]); | ||
| 1516 | auto b = InputRange(new int[5]); | ||
| 1517 | moveAll(a, b); | ||
| 1518 | assert(a.data == b.data[0 .. 3]); | ||
| 1519 | assert(a.data == [ 1, 2, 3 ]); | ||
| 1520 | } | ||
| 1521 | |||
| 1522 | private InputRange2 moveAllImpl(alias moveOp, InputRange1, InputRange2)( | ||
| 1523 | ref InputRange1 src, ref InputRange2 tgt) | ||
| 1524 | { | ||
| 1525 | import std.exception : enforce; | ||
| 1526 | |||
| 1527 | static if (isRandomAccessRange!InputRange1 && hasLength!InputRange1 && hasLength!InputRange2 | ||
| 1528 | && hasSlicing!InputRange2 && isRandomAccessRange!InputRange2) | ||
| 1529 | { | ||
| 1530 | auto toMove = src.length; | ||
| 1531 | assert(toMove <= tgt.length); | ||
| 1532 | foreach (idx; 0 .. toMove) | ||
| 1533 | moveOp(src[idx], tgt[idx]); | ||
| 1534 | return tgt[toMove .. tgt.length]; | ||
| 1535 | } | ||
| 1536 | else | ||
| 1537 | { | ||
| 1538 | for (; !src.empty; src.popFront(), tgt.popFront()) | ||
| 1539 | { | ||
| 1540 | assert(!tgt.empty); | ||
| 1541 | moveOp(src.front, tgt.front); | ||
| 1542 | } | ||
| 1543 | return tgt; | ||
| 1544 | } | ||
| 1545 | } | ||
| 1546 | |||
| 1547 | // moveSome | ||
| 1548 | /** | ||
| 1549 | Calls `move(a, b)` for each element `a` in `src` and the corresponding | ||
| 1550 | element `b` in `tgt`, in increasing order, stopping when either range has been | ||
| 1551 | exhausted. | ||
| 1552 | |||
| 1553 | Params: | ||
| 1554 | src = An $(REF_ALTTEXT input range, isInputRange, std,range,primitives) with | ||
| 1555 | movable elements. | ||
| 1556 | tgt = An $(REF_ALTTEXT input range, isInputRange, std,range,primitives) with | ||
| 1557 | elements that elements from $(D src) can be moved into. | ||
| 1558 | |||
| 1559 | Returns: The leftover portions of the two ranges after one or the other of the | ||
| 1560 | ranges have been exhausted. | ||
| 1561 | */ | ||
| 1562 | Tuple!(InputRange1, InputRange2) moveSome(InputRange1, InputRange2)(InputRange1 src, InputRange2 tgt) | ||
| 1563 | if (isInputRange!InputRange1 && isInputRange!InputRange2 | ||
| 1564 | && is(typeof(move(src.front, tgt.front)))) | ||
| 1565 | { | ||
| 1566 | return moveSomeImpl!move(src, tgt); | ||
| 1567 | } | ||
| 1568 | |||
| 1569 | /// | ||
| 1570 | pure nothrow @safe @nogc unittest | ||
| 1571 | { | ||
| 1572 | int[5] a = [ 1, 2, 3, 4, 5 ]; | ||
| 1573 | int[3] b; | ||
| 1574 | assert(moveSome(a[], b[])[0] is a[3 .. $]); | ||
| 1575 | assert(a[0 .. 3] == b); | ||
| 1576 | assert(a == [ 1, 2, 3, 4, 5 ]); | ||
| 1577 | } | ||
| 1578 | |||
| 1579 | /** | ||
| 1580 | * Same as $(LREF moveSome) but assumes all elements in `tgt` are | ||
| 1581 | * uninitialized. Uses $(LREF moveEmplace) to move elements from | ||
| 1582 | * `src` over elements from `tgt`. | ||
| 1583 | */ | ||
| 1584 | Tuple!(InputRange1, InputRange2) moveEmplaceSome(InputRange1, InputRange2)(InputRange1 src, InputRange2 tgt) @system | ||
| 1585 | if (isInputRange!InputRange1 && isInputRange!InputRange2 | ||
| 1586 | && is(typeof(move(src.front, tgt.front)))) | ||
| 1587 | { | ||
| 1588 | return moveSomeImpl!moveEmplace(src, tgt); | ||
| 1589 | } | ||
| 1590 | |||
| 1591 | /// | ||
| 1592 | pure nothrow @nogc @system unittest | ||
| 1593 | { | ||
| 1594 | static struct Foo | ||
| 1595 | { | ||
| 1596 | ~this() pure nothrow @nogc { if (_ptr) ++*_ptr; } | ||
| 1597 | int* _ptr; | ||
| 1598 | } | ||
| 1599 | int[4] refs = [0, 1, 2, 3]; | ||
| 1600 | Foo[4] src = [Foo(&refs[0]), Foo(&refs[1]), Foo(&refs[2]), Foo(&refs[3])]; | ||
| 1601 | Foo[3] dst = void; | ||
| 1602 | |||
| 1603 | auto res = moveEmplaceSome(src[], dst[]); | ||
| 1604 | assert(res.length == 2); | ||
| 1605 | |||
| 1606 | import std.algorithm.searching : all; | ||
| 1607 | assert(src[0 .. 3].all!(e => e._ptr is null)); | ||
| 1608 | assert(src[3]._ptr !is null); | ||
| 1609 | assert(dst[].all!(e => e._ptr !is null)); | ||
| 1610 | } | ||
| 1611 | |||
| 1612 | private Tuple!(InputRange1, InputRange2) moveSomeImpl(alias moveOp, InputRange1, InputRange2)( | ||
| 1613 | ref InputRange1 src, ref InputRange2 tgt) | ||
| 1614 | { | ||
| 1615 | for (; !src.empty && !tgt.empty; src.popFront(), tgt.popFront()) | ||
| 1616 | moveOp(src.front, tgt.front); | ||
| 1617 | return tuple(src, tgt); | ||
| 1618 | } | ||
| 1619 | |||
| 1620 | |||
| 1621 | // SwapStrategy | ||
| 1622 | /** | ||
| 1623 | Defines the swapping strategy for algorithms that need to swap | ||
| 1624 | elements in a range (such as partition and sort). The strategy | ||
| 1625 | concerns the swapping of elements that are not the core concern of the | ||
| 1626 | algorithm. For example, consider an algorithm that sorts $(D [ "abc", | ||
| 1627 | "b", "aBc" ]) according to $(D toUpper(a) < toUpper(b)). That | ||
| 1628 | algorithm might choose to swap the two equivalent strings $(D "abc") | ||
| 1629 | and $(D "aBc"). That does not affect the sorting since both $(D [ | ||
| 1630 | "abc", "aBc", "b" ]) and $(D [ "aBc", "abc", "b" ]) are valid | ||
| 1631 | outcomes. | ||
| 1632 | |||
| 1633 | Some situations require that the algorithm must NOT ever change the | ||
| 1634 | relative ordering of equivalent elements (in the example above, only | ||
| 1635 | $(D [ "abc", "aBc", "b" ]) would be the correct result). Such | ||
| 1636 | algorithms are called $(B stable). If the ordering algorithm may swap | ||
| 1637 | equivalent elements discretionarily, the ordering is called $(B | ||
| 1638 | unstable). | ||
| 1639 | |||
| 1640 | Yet another class of algorithms may choose an intermediate tradeoff by | ||
| 1641 | being stable only on a well-defined subrange of the range. There is no | ||
| 1642 | established terminology for such behavior; this library calls it $(B | ||
| 1643 | semistable). | ||
| 1644 | |||
| 1645 | Generally, the $(D stable) ordering strategy may be more costly in | ||
| 1646 | time and/or space than the other two because it imposes additional | ||
| 1647 | constraints. Similarly, $(D semistable) may be costlier than $(D | ||
| 1648 | unstable). As (semi-)stability is not needed very often, the ordering | ||
| 1649 | algorithms in this module parameterized by $(D SwapStrategy) all | ||
| 1650 | choose $(D SwapStrategy.unstable) as the default. | ||
| 1651 | */ | ||
| 1652 | |||
| 1653 | enum SwapStrategy | ||
| 1654 | { | ||
| 1655 | /** | ||
| 1656 | Allows freely swapping of elements as long as the output | ||
| 1657 | satisfies the algorithm's requirements. | ||
| 1658 | */ | ||
| 1659 | unstable, | ||
| 1660 | /** | ||
| 1661 | In algorithms partitioning ranges in two, preserve relative | ||
| 1662 | ordering of elements only to the left of the partition point. | ||
| 1663 | */ | ||
| 1664 | semistable, | ||
| 1665 | /** | ||
| 1666 | Preserve the relative ordering of elements to the largest | ||
| 1667 | extent allowed by the algorithm's requirements. | ||
| 1668 | */ | ||
| 1669 | stable, | ||
| 1670 | } | ||
| 1671 | |||
| 1672 | /// | ||
| 1673 | @safe unittest | ||
| 1674 | { | ||
| 1675 | import std.stdio; | ||
| 1676 | import std.algorithm.sorting : partition; | ||
| 1677 | int[] a = [0, 1, 2, 3]; | ||
| 1678 | assert(remove!(SwapStrategy.stable)(a, 1) == [0, 2, 3]); | ||
| 1679 | a = [0, 1, 2, 3]; | ||
| 1680 | assert(remove!(SwapStrategy.unstable)(a, 1) == [0, 3, 2]); | ||
| 1681 | } | ||
| 1682 | |||
| 1683 | /// | ||
| 1684 | @safe unittest | ||
| 1685 | { | ||
| 1686 | import std.algorithm.sorting : partition; | ||
| 1687 | |||
| 1688 | // Put stuff greater than 3 on the left | ||
| 1689 | auto arr = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; | ||
| 1690 | assert(partition!(a => a > 3, SwapStrategy.stable)(arr) == [1, 2, 3]); | ||
| 1691 | assert(arr == [4, 5, 6, 7, 8, 9, 10, 1, 2, 3]); | ||
| 1692 | |||
| 1693 | arr = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; | ||
| 1694 | assert(partition!(a => a > 3, SwapStrategy.semistable)(arr) == [2, 3, 1]); | ||
| 1695 | assert(arr == [4, 5, 6, 7, 8, 9, 10, 2, 3, 1]); | ||
| 1696 | |||
| 1697 | arr = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; | ||
| 1698 | assert(partition!(a => a > 3, SwapStrategy.unstable)(arr) == [3, 2, 1]); | ||
| 1699 | assert(arr == [10, 9, 8, 4, 5, 6, 7, 3, 2, 1]); | ||
| 1700 | } | ||
| 1701 | |||
| 1702 | /** | ||
| 1703 | Eliminates elements at given offsets from `range` and returns the shortened | ||
| 1704 | range. | ||
| 1705 | |||
| 1706 | For example, here is how to _remove a single element from an array: | ||
| 1707 | |||
| 1708 | ---- | ||
| 1709 | string[] a = [ "a", "b", "c", "d" ]; | ||
| 1710 | a = a.remove(1); // remove element at offset 1 | ||
| 1711 | assert(a == [ "a", "c", "d"]); | ||
| 1712 | ---- | ||
| 1713 | |||
| 1714 | Note that `remove` does not change the length of the original _range directly; | ||
| 1715 | instead, it returns the shortened _range. If its return value is not assigned to | ||
| 1716 | the original _range, the original _range will retain its original length, though | ||
| 1717 | its contents will have changed: | ||
| 1718 | |||
| 1719 | ---- | ||
| 1720 | int[] a = [ 3, 5, 7, 8 ]; | ||
| 1721 | assert(remove(a, 1) == [ 3, 7, 8 ]); | ||
| 1722 | assert(a == [ 3, 7, 8, 8 ]); | ||
| 1723 | ---- | ||
| 1724 | |||
| 1725 | The element at _offset `1` has been removed and the rest of the elements have | ||
| 1726 | shifted up to fill its place, however, the original array remains of the same | ||
| 1727 | length. This is because all functions in `std.algorithm` only change $(I | ||
| 1728 | content), not $(I topology). The value `8` is repeated because $(LREF move) was | ||
| 1729 | invoked to rearrange elements, and on integers `move` simply copies the source | ||
| 1730 | to the destination. To replace `a` with the effect of the removal, simply | ||
| 1731 | assign the slice returned by `remove` to it, as shown in the first example. | ||
| 1732 | |||
| 1733 | Multiple indices can be passed into $(D remove). In that case, | ||
| 1734 | elements at the respective indices are all removed. The indices must | ||
| 1735 | be passed in increasing order, otherwise an exception occurs. | ||
| 1736 | |||
| 1737 | ---- | ||
| 1738 | int[] a = [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ]; | ||
| 1739 | assert(remove(a, 1, 3, 5) == | ||
| 1740 | [ 0, 2, 4, 6, 7, 8, 9, 10 ]); | ||
| 1741 | ---- | ||
| 1742 | |||
| 1743 | (Note that all indices refer to slots in the $(I original) array, not | ||
| 1744 | in the array as it is being progressively shortened.) Finally, any | ||
| 1745 | combination of integral offsets and tuples composed of two integral | ||
| 1746 | offsets can be passed in. | ||
| 1747 | |||
| 1748 | ---- | ||
| 1749 | int[] a = [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ]; | ||
| 1750 | assert(remove(a, 1, tuple(3, 5), 9) == [ 0, 2, 5, 6, 7, 8, 10 ]); | ||
| 1751 | ---- | ||
| 1752 | |||
| 1753 | In this case, the slots at positions 1, 3, 4, and 9 are removed from | ||
| 1754 | the array. The tuple passes in a range closed to the left and open to | ||
| 1755 | the right (consistent with built-in slices), e.g. $(D tuple(3, 5)) | ||
| 1756 | means indices $(D 3) and $(D 4) but not $(D 5). | ||
| 1757 | |||
| 1758 | If the need is to remove some elements in the range but the order of | ||
| 1759 | the remaining elements does not have to be preserved, you may want to | ||
| 1760 | pass $(D SwapStrategy.unstable) to $(D remove). | ||
| 1761 | |||
| 1762 | ---- | ||
| 1763 | int[] a = [ 0, 1, 2, 3 ]; | ||
| 1764 | assert(remove!(SwapStrategy.unstable)(a, 1) == [ 0, 3, 2 ]); | ||
| 1765 | ---- | ||
| 1766 | |||
| 1767 | In the case above, the element at slot $(D 1) is removed, but replaced | ||
| 1768 | with the last element of the range. Taking advantage of the relaxation | ||
| 1769 | of the stability requirement, $(D remove) moved elements from the end | ||
| 1770 | of the array over the slots to be removed. This way there is less data | ||
| 1771 | movement to be done which improves the execution time of the function. | ||
| 1772 | |||
| 1773 | The function $(D remove) works on bidirectional ranges that have assignable | ||
| 1774 | lvalue elements. The moving strategy is (listed from fastest to slowest): | ||
| 1775 | $(UL $(LI If $(D s == SwapStrategy.unstable && isRandomAccessRange!Range && | ||
| 1776 | hasLength!Range && hasLvalueElements!Range), then elements are moved from the | ||
| 1777 | end of the range into the slots to be filled. In this case, the absolute | ||
| 1778 | minimum of moves is performed.) $(LI Otherwise, if $(D s == | ||
| 1779 | SwapStrategy.unstable && isBidirectionalRange!Range && hasLength!Range | ||
| 1780 | && hasLvalueElements!Range), then elements are still moved from the | ||
| 1781 | end of the range, but time is spent on advancing between slots by repeated | ||
| 1782 | calls to $(D range.popFront).) $(LI Otherwise, elements are moved | ||
| 1783 | incrementally towards the front of $(D range); a given element is never | ||
| 1784 | moved several times, but more elements are moved than in the previous | ||
| 1785 | cases.)) | ||
| 1786 | |||
| 1787 | Params: | ||
| 1788 | s = a SwapStrategy to determine if the original order needs to be preserved | ||
| 1789 | range = a $(REF_ALTTEXT bidirectional range, isBidirectionalRange, std,_range,primitives) | ||
| 1790 | with a length member | ||
| 1791 | offset = which element(s) to remove | ||
| 1792 | |||
| 1793 | Returns: | ||
| 1794 | a range containing all of the elements of range with offset removed | ||
| 1795 | */ | ||
| 1796 | Range remove | ||
| 1797 | (SwapStrategy s = SwapStrategy.stable, Range, Offset...) | ||
| 1798 | (Range range, Offset offset) | ||
| 1799 | if (s != SwapStrategy.stable | ||
| 1800 | && isBidirectionalRange!Range | ||
| 1801 | && hasLvalueElements!Range | ||
| 1802 | && hasLength!Range | ||
| 1803 | && Offset.length >= 1) | ||
| 1804 | { | ||
| 1805 | Tuple!(size_t, "pos", size_t, "len")[offset.length] blackouts; | ||
| 1806 | foreach (i, v; offset) | ||
| 1807 | { | ||
| 1808 | static if (is(typeof(v[0]) : size_t) && is(typeof(v[1]) : size_t)) | ||
| 1809 | { | ||
| 1810 | blackouts[i].pos = v[0]; | ||
| 1811 | blackouts[i].len = v[1] - v[0]; | ||
| 1812 | } | ||
| 1813 | else | ||
| 1814 | { | ||
| 1815 | static assert(is(typeof(v) : size_t), typeof(v).stringof); | ||
| 1816 | blackouts[i].pos = v; | ||
| 1817 | blackouts[i].len = 1; | ||
| 1818 | } | ||
| 1819 | static if (i > 0) | ||
| 1820 | { | ||
| 1821 | import std.exception : enforce; | ||
| 1822 | |||
| 1823 | enforce(blackouts[i - 1].pos + blackouts[i - 1].len | ||
| 1824 | <= blackouts[i].pos, | ||
| 1825 | "remove(): incorrect ordering of elements to remove"); | ||
| 1826 | } | ||
| 1827 | } | ||
| 1828 | |||
| 1829 | size_t left = 0, right = offset.length - 1; | ||
| 1830 | auto tgt = range.save; | ||
| 1831 | size_t tgtPos = 0; | ||
| 1832 | |||
| 1833 | while (left <= right) | ||
| 1834 | { | ||
| 1835 | // Look for a blackout on the right | ||
| 1836 | if (blackouts[right].pos + blackouts[right].len >= range.length) | ||
| 1837 | { | ||
| 1838 | range.popBackExactly(blackouts[right].len); | ||
| 1839 | |||
| 1840 | // Since right is unsigned, we must check for this case, otherwise | ||
| 1841 | // we might turn it into size_t.max and the loop condition will not | ||
| 1842 | // fail when it should. | ||
| 1843 | if (right > 0) | ||
| 1844 | { | ||
| 1845 | --right; | ||
| 1846 | continue; | ||
| 1847 | } | ||
| 1848 | else | ||
| 1849 | break; | ||
| 1850 | } | ||
| 1851 | // Advance to next blackout on the left | ||
| 1852 | assert(blackouts[left].pos >= tgtPos); | ||
| 1853 | tgt.popFrontExactly(blackouts[left].pos - tgtPos); | ||
| 1854 | tgtPos = blackouts[left].pos; | ||
| 1855 | |||
| 1856 | // Number of elements to the right of blackouts[right] | ||
| 1857 | immutable tailLen = range.length - (blackouts[right].pos + blackouts[right].len); | ||
| 1858 | size_t toMove = void; | ||
| 1859 | if (tailLen < blackouts[left].len) | ||
| 1860 | { | ||
| 1861 | toMove = tailLen; | ||
| 1862 | blackouts[left].pos += toMove; | ||
| 1863 | blackouts[left].len -= toMove; | ||
| 1864 | } | ||
| 1865 | else | ||
| 1866 | { | ||
| 1867 | toMove = blackouts[left].len; | ||
| 1868 | ++left; | ||
| 1869 | } | ||
| 1870 | tgtPos += toMove; | ||
| 1871 | foreach (i; 0 .. toMove) | ||
| 1872 | { | ||
| 1873 | move(range.back, tgt.front); | ||
| 1874 | range.popBack(); | ||
| 1875 | tgt.popFront(); | ||
| 1876 | } | ||
| 1877 | } | ||
| 1878 | |||
| 1879 | return range; | ||
| 1880 | } | ||
| 1881 | |||
| 1882 | /// Ditto | ||
| 1883 | Range remove | ||
| 1884 | (SwapStrategy s = SwapStrategy.stable, Range, Offset...) | ||
| 1885 | (Range range, Offset offset) | ||
| 1886 | if (s == SwapStrategy.stable | ||
| 1887 | && isBidirectionalRange!Range | ||
| 1888 | && hasLvalueElements!Range | ||
| 1889 | && Offset.length >= 1) | ||
| 1890 | { | ||
| 1891 | auto result = range; | ||
| 1892 | auto src = range, tgt = range; | ||
| 1893 | size_t pos; | ||
| 1894 | foreach (pass, i; offset) | ||
| 1895 | { | ||
| 1896 | static if (is(typeof(i[0])) && is(typeof(i[1]))) | ||
| 1897 | { | ||
| 1898 | auto from = i[0], delta = i[1] - i[0]; | ||
| 1899 | } | ||
| 1900 | else | ||
| 1901 | { | ||
| 1902 | auto from = i; | ||
| 1903 | enum delta = 1; | ||
| 1904 | } | ||
| 1905 | |||
| 1906 | static if (pass > 0) | ||
| 1907 | { | ||
| 1908 | import std.exception : enforce; | ||
| 1909 | enforce(pos <= from, | ||
| 1910 | "remove(): incorrect ordering of elements to remove"); | ||
| 1911 | |||
| 1912 | for (; pos < from; ++pos, src.popFront(), tgt.popFront()) | ||
| 1913 | { | ||
| 1914 | move(src.front, tgt.front); | ||
| 1915 | } | ||
| 1916 | } | ||
| 1917 | else | ||
| 1918 | { | ||
| 1919 | src.popFrontExactly(from); | ||
| 1920 | tgt.popFrontExactly(from); | ||
| 1921 | pos = from; | ||
| 1922 | } | ||
| 1923 | // now skip source to the "to" position | ||
| 1924 | src.popFrontExactly(delta); | ||
| 1925 | result.popBackExactly(delta); | ||
| 1926 | pos += delta; | ||
| 1927 | } | ||
| 1928 | // leftover move | ||
| 1929 | moveAll(src, tgt); | ||
| 1930 | return result; | ||
| 1931 | } | ||
| 1932 | |||
| 1933 | /// | ||
| 1934 | @safe pure unittest | ||
| 1935 | { | ||
| 1936 | import std.typecons : tuple; | ||
| 1937 | |||
| 1938 | auto a = [ 0, 1, 2, 3, 4, 5 ]; | ||
| 1939 | assert(remove!(SwapStrategy.stable)(a, 1) == [ 0, 2, 3, 4, 5 ]); | ||
| 1940 | a = [ 0, 1, 2, 3, 4, 5 ]; | ||
| 1941 | assert(remove!(SwapStrategy.stable)(a, 1, 3) == [ 0, 2, 4, 5] ); | ||
| 1942 | a = [ 0, 1, 2, 3, 4, 5 ]; | ||
| 1943 | assert(remove!(SwapStrategy.stable)(a, 1, tuple(3, 6)) == [ 0, 2 ]); | ||
| 1944 | |||
| 1945 | a = [ 0, 1, 2, 3, 4, 5 ]; | ||
| 1946 | assert(remove!(SwapStrategy.unstable)(a, 1) == [0, 5, 2, 3, 4]); | ||
| 1947 | a = [ 0, 1, 2, 3, 4, 5 ]; | ||
| 1948 | assert(remove!(SwapStrategy.unstable)(a, tuple(1, 4)) == [0, 5, 4]); | ||
| 1949 | } | ||
| 1950 | |||
| 1951 | @safe unittest | ||
| 1952 | { | ||
| 1953 | import std.exception : assertThrown; | ||
| 1954 | import std.range; | ||
| 1955 | |||
| 1956 | // http://d.puremagic.com/issues/show_bug.cgi?id=10173 | ||
| 1957 | int[] test = iota(0, 10).array(); | ||
| 1958 | assertThrown(remove!(SwapStrategy.stable)(test, tuple(2, 4), tuple(1, 3))); | ||
| 1959 | assertThrown(remove!(SwapStrategy.unstable)(test, tuple(2, 4), tuple(1, 3))); | ||
| 1960 | assertThrown(remove!(SwapStrategy.stable)(test, 2, 4, 1, 3)); | ||
| 1961 | assertThrown(remove!(SwapStrategy.unstable)(test, 2, 4, 1, 3)); | ||
| 1962 | } | ||
| 1963 | |||
| 1964 | @safe unittest | ||
| 1965 | { | ||
| 1966 | import std.range; | ||
| 1967 | int[] a = [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ]; | ||
| 1968 | a = [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ]; | ||
| 1969 | assert(remove!(SwapStrategy.stable)(a, 1) == | ||
| 1970 | [ 0, 2, 3, 4, 5, 6, 7, 8, 9, 10 ]); | ||
| 1971 | |||
| 1972 | a = [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ]; | ||
| 1973 | assert(remove!(SwapStrategy.unstable)(a, 0, 10) == | ||
| 1974 | [ 9, 1, 2, 3, 4, 5, 6, 7, 8 ]); | ||
| 1975 | |||
| 1976 | a = [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ]; | ||
| 1977 | assert(remove!(SwapStrategy.unstable)(a, 0, tuple(9, 11)) == | ||
| 1978 | [ 8, 1, 2, 3, 4, 5, 6, 7 ]); | ||
| 1979 | // http://d.puremagic.com/issues/show_bug.cgi?id=5224 | ||
| 1980 | a = [ 1, 2, 3, 4 ]; | ||
| 1981 | assert(remove!(SwapStrategy.unstable)(a, 2) == | ||
| 1982 | [ 1, 2, 4 ]); | ||
| 1983 | |||
| 1984 | a = [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ]; | ||
| 1985 | assert(remove!(SwapStrategy.stable)(a, 1, 5) == | ||
| 1986 | [ 0, 2, 3, 4, 6, 7, 8, 9, 10 ]); | ||
| 1987 | |||
| 1988 | a = [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ]; | ||
| 1989 | assert(remove!(SwapStrategy.stable)(a, 1, 3, 5) | ||
| 1990 | == [ 0, 2, 4, 6, 7, 8, 9, 10]); | ||
| 1991 | a = [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ]; | ||
| 1992 | assert(remove!(SwapStrategy.stable)(a, 1, tuple(3, 5)) | ||
| 1993 | == [ 0, 2, 5, 6, 7, 8, 9, 10]); | ||
| 1994 | |||
| 1995 | a = iota(0, 10).array(); | ||
| 1996 | assert(remove!(SwapStrategy.unstable)(a, tuple(1, 4), tuple(6, 7)) | ||
| 1997 | == [0, 9, 8, 7, 4, 5]); | ||
| 1998 | } | ||
| 1999 | |||
| 2000 | @safe unittest | ||
| 2001 | { | ||
| 2002 | // Issue 11576 | ||
| 2003 | auto arr = [1,2,3]; | ||
| 2004 | arr = arr.remove!(SwapStrategy.unstable)(2); | ||
| 2005 | assert(arr == [1,2]); | ||
| 2006 | |||
| 2007 | } | ||
| 2008 | |||
| 2009 | @safe unittest | ||
| 2010 | { | ||
| 2011 | import std.range; | ||
| 2012 | // Bug# 12889 | ||
| 2013 | int[1][] arr = [[0], [1], [2], [3], [4], [5], [6]]; | ||
| 2014 | auto orig = arr.dup; | ||
| 2015 | foreach (i; iota(arr.length)) | ||
| 2016 | { | ||
| 2017 | assert(orig == arr.remove!(SwapStrategy.unstable)(tuple(i,i))); | ||
| 2018 | assert(orig == arr.remove!(SwapStrategy.stable)(tuple(i,i))); | ||
| 2019 | } | ||
| 2020 | } | ||
| 2021 | |||
| 2022 | /** | ||
| 2023 | Reduces the length of the | ||
| 2024 | $(REF_ALTTEXT bidirectional range, isBidirectionalRange, std,_range,primitives) $(D range) by removing | ||
| 2025 | elements that satisfy $(D pred). If $(D s = SwapStrategy.unstable), | ||
| 2026 | elements are moved from the right end of the range over the elements | ||
| 2027 | to eliminate. If $(D s = SwapStrategy.stable) (the default), | ||
| 2028 | elements are moved progressively to front such that their relative | ||
| 2029 | order is preserved. Returns the filtered range. | ||
| 2030 | |||
| 2031 | Params: | ||
| 2032 | range = a bidirectional ranges with lvalue elements | ||
| 2033 | |||
| 2034 | Returns: | ||
| 2035 | the range with all of the elements where $(D pred) is $(D true) | ||
| 2036 | removed | ||
| 2037 | */ | ||
| 2038 | Range remove(alias pred, SwapStrategy s = SwapStrategy.stable, Range) | ||
| 2039 | (Range range) | ||
| 2040 | if (isBidirectionalRange!Range | ||
| 2041 | && hasLvalueElements!Range) | ||
| 2042 | { | ||
| 2043 | import std.functional : unaryFun; | ||
| 2044 | auto result = range; | ||
| 2045 | static if (s != SwapStrategy.stable) | ||
| 2046 | { | ||
| 2047 | for (;!range.empty;) | ||
| 2048 | { | ||
| 2049 | if (!unaryFun!pred(range.front)) | ||
| 2050 | { | ||
| 2051 | range.popFront(); | ||
| 2052 | continue; | ||
| 2053 | } | ||
| 2054 | move(range.back, range.front); | ||
| 2055 | range.popBack(); | ||
| 2056 | result.popBack(); | ||
| 2057 | } | ||
| 2058 | } | ||
| 2059 | else | ||
| 2060 | { | ||
| 2061 | auto tgt = range; | ||
| 2062 | for (; !range.empty; range.popFront()) | ||
| 2063 | { | ||
| 2064 | if (unaryFun!(pred)(range.front)) | ||
| 2065 | { | ||
| 2066 | // yank this guy | ||
| 2067 | result.popBack(); | ||
| 2068 | continue; | ||
| 2069 | } | ||
| 2070 | // keep this guy | ||
| 2071 | move(range.front, tgt.front); | ||
| 2072 | tgt.popFront(); | ||
| 2073 | } | ||
| 2074 | } | ||
| 2075 | return result; | ||
| 2076 | } | ||
| 2077 | |||
| 2078 | /// | ||
| 2079 | @safe unittest | ||
| 2080 | { | ||
| 2081 | static immutable base = [1, 2, 3, 2, 4, 2, 5, 2]; | ||
| 2082 | |||
| 2083 | int[] arr = base[].dup; | ||
| 2084 | |||
| 2085 | // using a string-based predicate | ||
| 2086 | assert(remove!("a == 2")(arr) == [ 1, 3, 4, 5 ]); | ||
| 2087 | |||
| 2088 | // The original array contents have been modified, | ||
| 2089 | // so we need to reset it to its original state. | ||
| 2090 | // The length is unmodified however. | ||
| 2091 | arr[] = base[]; | ||
| 2092 | |||
| 2093 | // using a lambda predicate | ||
| 2094 | assert(remove!(a => a == 2)(arr) == [ 1, 3, 4, 5 ]); | ||
| 2095 | } | ||
| 2096 | |||
| 2097 | @safe unittest | ||
| 2098 | { | ||
| 2099 | int[] a = [ 1, 2, 3, 2, 3, 4, 5, 2, 5, 6 ]; | ||
| 2100 | assert(remove!("a == 2", SwapStrategy.unstable)(a) == | ||
| 2101 | [ 1, 6, 3, 5, 3, 4, 5 ]); | ||
| 2102 | a = [ 1, 2, 3, 2, 3, 4, 5, 2, 5, 6 ]; | ||
| 2103 | assert(remove!("a == 2", SwapStrategy.stable)(a) == | ||
| 2104 | [ 1, 3, 3, 4, 5, 5, 6 ]); | ||
| 2105 | } | ||
| 2106 | |||
| 2107 | @nogc @system unittest | ||
| 2108 | { | ||
| 2109 | // @nogc test | ||
| 2110 | int[10] arr = [0,1,2,3,4,5,6,7,8,9]; | ||
| 2111 | alias pred = e => e < 5; | ||
| 2112 | |||
| 2113 | auto r = arr[].remove!(SwapStrategy.unstable)(0); | ||
| 2114 | r = r.remove!(SwapStrategy.stable)(0); | ||
| 2115 | r = r.remove!(pred, SwapStrategy.unstable); | ||
| 2116 | r = r.remove!(pred, SwapStrategy.stable); | ||
| 2117 | } | ||
| 2118 | |||
| 2119 | @safe unittest | ||
| 2120 | { | ||
| 2121 | import std.algorithm.comparison : min; | ||
| 2122 | import std.algorithm.searching : all, any; | ||
| 2123 | import std.algorithm.sorting : isStrictlyMonotonic; | ||
| 2124 | import std.array : array; | ||
| 2125 | import std.meta : AliasSeq; | ||
| 2126 | import std.range : iota, only; | ||
| 2127 | import std.typecons : Tuple; | ||
| 2128 | alias S = Tuple!(int[2]); | ||
| 2129 | S[] soffsets; | ||
| 2130 | foreach (start; 0 .. 5) | ||
| 2131 | foreach (end; min(start+1,5) .. 5) | ||
| 2132 | soffsets ~= S([start,end]); | ||
| 2133 | alias D = Tuple!(int[2],int[2]); | ||
| 2134 | D[] doffsets; | ||
| 2135 | foreach (start1; 0 .. 10) | ||
| 2136 | foreach (end1; min(start1+1,10) .. 10) | ||
| 2137 | foreach (start2; end1 .. 10) | ||
| 2138 | foreach (end2; min(start2+1,10) .. 10) | ||
| 2139 | doffsets ~= D([start1,end1],[start2,end2]); | ||
| 2140 | alias T = Tuple!(int[2],int[2],int[2]); | ||
| 2141 | T[] toffsets; | ||
| 2142 | foreach (start1; 0 .. 15) | ||
| 2143 | foreach (end1; min(start1+1,15) .. 15) | ||
| 2144 | foreach (start2; end1 .. 15) | ||
| 2145 | foreach (end2; min(start2+1,15) .. 15) | ||
| 2146 | foreach (start3; end2 .. 15) | ||
| 2147 | foreach (end3; min(start3+1,15) .. 15) | ||
| 2148 | toffsets ~= T([start1,end1],[start2,end2],[start3,end3]); | ||
| 2149 | |||
| 2150 | static void verify(O...)(int[] r, int len, int removed, bool stable, O offsets) | ||
| 2151 | { | ||
| 2152 | assert(r.length == len - removed); | ||
| 2153 | assert(!stable || r.isStrictlyMonotonic); | ||
| 2154 | assert(r.all!(e => all!(o => e < o[0] || e >= o[1])(offsets.only))); | ||
| 2155 | } | ||
| 2156 | |||
| 2157 | foreach (offsets; AliasSeq!(soffsets,doffsets,toffsets)) | ||
| 2158 | foreach (os; offsets) | ||
| 2159 | { | ||
| 2160 | int len = 5*os.length; | ||
| 2161 | auto w = iota(0, len).array; | ||
| 2162 | auto x = w.dup; | ||
| 2163 | auto y = w.dup; | ||
| 2164 | auto z = w.dup; | ||
| 2165 | alias pred = e => any!(o => o[0] <= e && e < o[1])(only(os.expand)); | ||
| 2166 | w = w.remove!(SwapStrategy.unstable)(os.expand); | ||
| 2167 | x = x.remove!(SwapStrategy.stable)(os.expand); | ||
| 2168 | y = y.remove!(pred, SwapStrategy.unstable); | ||
| 2169 | z = z.remove!(pred, SwapStrategy.stable); | ||
| 2170 | int removed; | ||
| 2171 | foreach (o; os) | ||
| 2172 | removed += o[1] - o[0]; | ||
| 2173 | verify(w, len, removed, false, os[]); | ||
| 2174 | verify(x, len, removed, true, os[]); | ||
| 2175 | verify(y, len, removed, false, os[]); | ||
| 2176 | verify(z, len, removed, true, os[]); | ||
| 2177 | assert(w == y); | ||
| 2178 | assert(x == z); | ||
| 2179 | } | ||
| 2180 | } | ||
| 2181 | |||
| 2182 | // reverse | ||
| 2183 | /** | ||
| 2184 | Reverses $(D r) in-place. Performs $(D r.length / 2) evaluations of $(D | ||
| 2185 | swap). | ||
| 2186 | Params: | ||
| 2187 | r = a $(REF_ALTTEXT bidirectional range, isBidirectionalRange, std,range,primitives) | ||
| 2188 | with swappable elements or a random access range with a length member | ||
| 2189 | |||
| 2190 | See_Also: | ||
| 2191 | $(HTTP sgi.com/tech/stl/_reverse.html, STL's _reverse), $(REF retro, std,range) for a lazy reversed range view | ||
| 2192 | */ | ||
| 2193 | void reverse(Range)(Range r) | ||
| 2194 | if (isBidirectionalRange!Range && !isRandomAccessRange!Range | ||
| 2195 | && hasSwappableElements!Range) | ||
| 2196 | { | ||
| 2197 | while (!r.empty) | ||
| 2198 | { | ||
| 2199 | swap(r.front, r.back); | ||
| 2200 | r.popFront(); | ||
| 2201 | if (r.empty) break; | ||
| 2202 | r.popBack(); | ||
| 2203 | } | ||
| 2204 | } | ||
| 2205 | |||
| 2206 | /// | ||
| 2207 | @safe unittest | ||
| 2208 | { | ||
| 2209 | int[] arr = [ 1, 2, 3 ]; | ||
| 2210 | reverse(arr); | ||
| 2211 | assert(arr == [ 3, 2, 1 ]); | ||
| 2212 | } | ||
| 2213 | |||
| 2214 | ///ditto | ||
| 2215 | void reverse(Range)(Range r) | ||
| 2216 | if (isRandomAccessRange!Range && hasLength!Range) | ||
| 2217 | { | ||
| 2218 | //swapAt is in fact the only way to swap non lvalue ranges | ||
| 2219 | immutable last = r.length-1; | ||
| 2220 | immutable steps = r.length/2; | ||
| 2221 | for (size_t i = 0; i < steps; i++) | ||
| 2222 | { | ||
| 2223 | r.swapAt(i, last-i); | ||
| 2224 | } | ||
| 2225 | } | ||
| 2226 | |||
| 2227 | @safe unittest | ||
| 2228 | { | ||
| 2229 | int[] range = null; | ||
| 2230 | reverse(range); | ||
| 2231 | range = [ 1 ]; | ||
| 2232 | reverse(range); | ||
| 2233 | assert(range == [1]); | ||
| 2234 | range = [1, 2]; | ||
| 2235 | reverse(range); | ||
| 2236 | assert(range == [2, 1]); | ||
| 2237 | range = [1, 2, 3]; | ||
| 2238 | reverse(range); | ||
| 2239 | assert(range == [3, 2, 1]); | ||
| 2240 | } | ||
| 2241 | |||
| 2242 | /** | ||
| 2243 | Reverses $(D r) in-place, where $(D r) is a narrow string (having | ||
| 2244 | elements of type $(D char) or $(D wchar)). UTF sequences consisting of | ||
| 2245 | multiple code units are preserved properly. | ||
| 2246 | |||
| 2247 | Params: | ||
| 2248 | s = a narrow string | ||
| 2249 | |||
| 2250 | Bugs: | ||
| 2251 | When passing a sting with unicode modifiers on characters, such as $(D \u0301), | ||
| 2252 | this function will not properly keep the position of the modifier. For example, | ||
| 2253 | reversing $(D ba\u0301d) ("bád") will result in d\u0301ab ("d́ab") instead of | ||
| 2254 | $(D da\u0301b) ("dáb"). | ||
| 2255 | */ | ||
| 2256 | void reverse(Char)(Char[] s) | ||
| 2257 | if (isNarrowString!(Char[]) && !is(Char == const) && !is(Char == immutable)) | ||
| 2258 | { | ||
| 2259 | import std.string : representation; | ||
| 2260 | import std.utf : stride; | ||
| 2261 | |||
| 2262 | auto r = representation(s); | ||
| 2263 | for (size_t i = 0; i < s.length; ) | ||
| 2264 | { | ||
| 2265 | immutable step = stride(s, i); | ||
| 2266 | if (step > 1) | ||
| 2267 | { | ||
| 2268 | .reverse(r[i .. i + step]); | ||
| 2269 | i += step; | ||
| 2270 | } | ||
| 2271 | else | ||
| 2272 | { | ||
| 2273 | ++i; | ||
| 2274 | } | ||
| 2275 | } | ||
| 2276 | reverse(r); | ||
| 2277 | } | ||
| 2278 | |||
| 2279 | /// | ||
| 2280 | @safe unittest | ||
| 2281 | { | ||
| 2282 | char[] arr = "hello\U00010143\u0100\U00010143".dup; | ||
| 2283 | reverse(arr); | ||
| 2284 | assert(arr == "\U00010143\u0100\U00010143olleh"); | ||
| 2285 | } | ||
| 2286 | |||
| 2287 | @safe unittest | ||
| 2288 | { | ||
| 2289 | void test(string a, string b) | ||
| 2290 | { | ||
| 2291 | auto c = a.dup; | ||
| 2292 | reverse(c); | ||
| 2293 | assert(c == b, c ~ " != " ~ b); | ||
| 2294 | } | ||
| 2295 | |||
| 2296 | test("a", "a"); | ||
| 2297 | test(" ", " "); | ||
| 2298 | test("\u2029", "\u2029"); | ||
| 2299 | test("\u0100", "\u0100"); | ||
| 2300 | test("\u0430", "\u0430"); | ||
| 2301 | test("\U00010143", "\U00010143"); | ||
| 2302 | test("abcdefcdef", "fedcfedcba"); | ||
| 2303 | test("hello\U00010143\u0100\U00010143", "\U00010143\u0100\U00010143olleh"); | ||
| 2304 | } | ||
| 2305 | |||
| 2306 | /** | ||
| 2307 | The strip group of functions allow stripping of either leading, trailing, | ||
| 2308 | or both leading and trailing elements. | ||
| 2309 | |||
| 2310 | The $(D stripLeft) function will strip the $(D front) of the range, | ||
| 2311 | the $(D stripRight) function will strip the $(D back) of the range, | ||
| 2312 | while the $(D strip) function will strip both the $(D front) and $(D back) | ||
| 2313 | of the range. | ||
| 2314 | |||
| 2315 | Note that the $(D strip) and $(D stripRight) functions require the range to | ||
| 2316 | be a $(LREF BidirectionalRange) range. | ||
| 2317 | |||
| 2318 | All of these functions come in two varieties: one takes a target element, | ||
| 2319 | where the range will be stripped as long as this element can be found. | ||
| 2320 | The other takes a lambda predicate, where the range will be stripped as | ||
| 2321 | long as the predicate returns true. | ||
| 2322 | |||
| 2323 | Params: | ||
| 2324 | range = a $(REF_ALTTEXT bidirectional range, isBidirectionalRange, std,range,primitives) | ||
| 2325 | or $(REF_ALTTEXT input range, isInputRange, std,range,primitives) | ||
| 2326 | element = the elements to remove | ||
| 2327 | |||
| 2328 | Returns: | ||
| 2329 | a Range with all of range except element at the start and end | ||
| 2330 | */ | ||
| 2331 | Range strip(Range, E)(Range range, E element) | ||
| 2332 | if (isBidirectionalRange!Range && is(typeof(range.front == element) : bool)) | ||
| 2333 | { | ||
| 2334 | return range.stripLeft(element).stripRight(element); | ||
| 2335 | } | ||
| 2336 | |||
| 2337 | /// ditto | ||
| 2338 | Range strip(alias pred, Range)(Range range) | ||
| 2339 | if (isBidirectionalRange!Range && is(typeof(pred(range.back)) : bool)) | ||
| 2340 | { | ||
| 2341 | return range.stripLeft!pred().stripRight!pred(); | ||
| 2342 | } | ||
| 2343 | |||
| 2344 | /// ditto | ||
| 2345 | Range stripLeft(Range, E)(Range range, E element) | ||
| 2346 | if (isInputRange!Range && is(typeof(range.front == element) : bool)) | ||
| 2347 | { | ||
| 2348 | import std.algorithm.searching : find; | ||
| 2349 | return find!((auto ref a) => a != element)(range); | ||
| 2350 | } | ||
| 2351 | |||
| 2352 | /// ditto | ||
| 2353 | Range stripLeft(alias pred, Range)(Range range) | ||
| 2354 | if (isInputRange!Range && is(typeof(pred(range.front)) : bool)) | ||
| 2355 | { | ||
| 2356 | import std.algorithm.searching : find; | ||
| 2357 | import std.functional : not; | ||
| 2358 | |||
| 2359 | return find!(not!pred)(range); | ||
| 2360 | } | ||
| 2361 | |||
| 2362 | /// ditto | ||
| 2363 | Range stripRight(Range, E)(Range range, E element) | ||
| 2364 | if (isBidirectionalRange!Range && is(typeof(range.back == element) : bool)) | ||
| 2365 | { | ||
| 2366 | for (; !range.empty; range.popBack()) | ||
| 2367 | { | ||
| 2368 | if (range.back != element) | ||
| 2369 | break; | ||
| 2370 | } | ||
| 2371 | return range; | ||
| 2372 | } | ||
| 2373 | |||
| 2374 | /// ditto | ||
| 2375 | Range stripRight(alias pred, Range)(Range range) | ||
| 2376 | if (isBidirectionalRange!Range && is(typeof(pred(range.back)) : bool)) | ||
| 2377 | { | ||
| 2378 | for (; !range.empty; range.popBack()) | ||
| 2379 | { | ||
| 2380 | if (!pred(range.back)) | ||
| 2381 | break; | ||
| 2382 | } | ||
| 2383 | return range; | ||
| 2384 | } | ||
| 2385 | |||
| 2386 | /// Strip leading and trailing elements equal to the target element. | ||
| 2387 | @safe pure unittest | ||
| 2388 | { | ||
| 2389 | assert(" foobar ".strip(' ') == "foobar"); | ||
| 2390 | assert("00223.444500".strip('0') == "223.4445"); | ||
| 2391 | assert("ëëêéüŗōpéêëë".strip('ë') == "êéüŗōpéê"); | ||
| 2392 | assert([1, 1, 0, 1, 1].strip(1) == [0]); | ||
| 2393 | assert([0.0, 0.01, 0.01, 0.0].strip(0).length == 2); | ||
| 2394 | } | ||
| 2395 | |||
| 2396 | /// Strip leading and trailing elements while the predicate returns true. | ||
| 2397 | @safe pure unittest | ||
| 2398 | { | ||
| 2399 | assert(" foobar ".strip!(a => a == ' ')() == "foobar"); | ||
| 2400 | assert("00223.444500".strip!(a => a == '0')() == "223.4445"); | ||
| 2401 | assert("ëëêéüŗōpéêëë".strip!(a => a == 'ë')() == "êéüŗōpéê"); | ||
| 2402 | assert([1, 1, 0, 1, 1].strip!(a => a == 1)() == [0]); | ||
| 2403 | assert([0.0, 0.01, 0.5, 0.6, 0.01, 0.0].strip!(a => a < 0.4)().length == 2); | ||
| 2404 | } | ||
| 2405 | |||
| 2406 | /// Strip leading elements equal to the target element. | ||
| 2407 | @safe pure unittest | ||
| 2408 | { | ||
| 2409 | assert(" foobar ".stripLeft(' ') == "foobar "); | ||
| 2410 | assert("00223.444500".stripLeft('0') == "223.444500"); | ||
| 2411 | assert("ůůűniçodêéé".stripLeft('ů') == "űniçodêéé"); | ||
| 2412 | assert([1, 1, 0, 1, 1].stripLeft(1) == [0, 1, 1]); | ||
| 2413 | assert([0.0, 0.01, 0.01, 0.0].stripLeft(0).length == 3); | ||
| 2414 | } | ||
| 2415 | |||
| 2416 | /// Strip leading elements while the predicate returns true. | ||
| 2417 | @safe pure unittest | ||
| 2418 | { | ||
| 2419 | assert(" foobar ".stripLeft!(a => a == ' ')() == "foobar "); | ||
| 2420 | assert("00223.444500".stripLeft!(a => a == '0')() == "223.444500"); | ||
| 2421 | assert("ůůűniçodêéé".stripLeft!(a => a == 'ů')() == "űniçodêéé"); | ||
| 2422 | assert([1, 1, 0, 1, 1].stripLeft!(a => a == 1)() == [0, 1, 1]); | ||
| 2423 | assert([0.0, 0.01, 0.10, 0.5, 0.6].stripLeft!(a => a < 0.4)().length == 2); | ||
| 2424 | } | ||
| 2425 | |||
| 2426 | /// Strip trailing elements equal to the target element. | ||
| 2427 | @safe pure unittest | ||
| 2428 | { | ||
| 2429 | assert(" foobar ".stripRight(' ') == " foobar"); | ||
| 2430 | assert("00223.444500".stripRight('0') == "00223.4445"); | ||
| 2431 | assert("ùniçodêéé".stripRight('é') == "ùniçodê"); | ||
| 2432 | assert([1, 1, 0, 1, 1].stripRight(1) == [1, 1, 0]); | ||
| 2433 | assert([0.0, 0.01, 0.01, 0.0].stripRight(0).length == 3); | ||
| 2434 | } | ||
| 2435 | |||
| 2436 | /// Strip trailing elements while the predicate returns true. | ||
| 2437 | @safe pure unittest | ||
| 2438 | { | ||
| 2439 | assert(" foobar ".stripRight!(a => a == ' ')() == " foobar"); | ||
| 2440 | assert("00223.444500".stripRight!(a => a == '0')() == "00223.4445"); | ||
| 2441 | assert("ùniçodêéé".stripRight!(a => a == 'é')() == "ùniçodê"); | ||
| 2442 | assert([1, 1, 0, 1, 1].stripRight!(a => a == 1)() == [1, 1, 0]); | ||
| 2443 | assert([0.0, 0.01, 0.10, 0.5, 0.6].stripRight!(a => a > 0.4)().length == 3); | ||
| 2444 | } | ||
| 2445 | |||
| 2446 | // swap | ||
| 2447 | /** | ||
| 2448 | Swaps $(D lhs) and $(D rhs). The instances $(D lhs) and $(D rhs) are moved in | ||
| 2449 | memory, without ever calling $(D opAssign), nor any other function. $(D T) | ||
| 2450 | need not be assignable at all to be swapped. | ||
| 2451 | |||
| 2452 | If $(D lhs) and $(D rhs) reference the same instance, then nothing is done. | ||
| 2453 | |||
| 2454 | $(D lhs) and $(D rhs) must be mutable. If $(D T) is a struct or union, then | ||
| 2455 | its fields must also all be (recursively) mutable. | ||
| 2456 | |||
| 2457 | Params: | ||
| 2458 | lhs = Data to be swapped with $(D rhs). | ||
| 2459 | rhs = Data to be swapped with $(D lhs). | ||
| 2460 | */ | ||
| 2461 | void swap(T)(ref T lhs, ref T rhs) @trusted pure nothrow @nogc | ||
| 2462 | if (isBlitAssignable!T && !is(typeof(lhs.proxySwap(rhs)))) | ||
| 2463 | { | ||
| 2464 | import std.traits : hasAliasing, hasElaborateAssign, isAssignable, | ||
| 2465 | isStaticArray; | ||
| 2466 | static if (hasAliasing!T) if (!__ctfe) | ||
| 2467 | { | ||
| 2468 | import std.exception : doesPointTo; | ||
| 2469 | assert(!doesPointTo(lhs, lhs), "Swap: lhs internal pointer."); | ||
| 2470 | assert(!doesPointTo(rhs, rhs), "Swap: rhs internal pointer."); | ||
| 2471 | assert(!doesPointTo(lhs, rhs), "Swap: lhs points to rhs."); | ||
| 2472 | assert(!doesPointTo(rhs, lhs), "Swap: rhs points to lhs."); | ||
| 2473 | } | ||
| 2474 | |||
| 2475 | static if (hasElaborateAssign!T || !isAssignable!T) | ||
| 2476 | { | ||
| 2477 | if (&lhs != &rhs) | ||
| 2478 | { | ||
| 2479 | // For structs with non-trivial assignment, move memory directly | ||
| 2480 | ubyte[T.sizeof] t = void; | ||
| 2481 | auto a = (cast(ubyte*) &lhs)[0 .. T.sizeof]; | ||
| 2482 | auto b = (cast(ubyte*) &rhs)[0 .. T.sizeof]; | ||
| 2483 | t[] = a[]; | ||
| 2484 | a[] = b[]; | ||
| 2485 | b[] = t[]; | ||
| 2486 | } | ||
| 2487 | } | ||
| 2488 | else | ||
| 2489 | { | ||
| 2490 | //Avoid assigning overlapping arrays. Dynamic arrays are fine, because | ||
| 2491 | //it's their ptr and length properties which get assigned rather | ||
| 2492 | //than their elements when assigning them, but static arrays are value | ||
| 2493 | //types and therefore all of their elements get copied as part of | ||
| 2494 | //assigning them, which would be assigning overlapping arrays if lhs | ||
| 2495 | //and rhs were the same array. | ||
| 2496 | static if (isStaticArray!T) | ||
| 2497 | { | ||
| 2498 | if (lhs.ptr == rhs.ptr) | ||
| 2499 | return; | ||
| 2500 | } | ||
| 2501 | |||
| 2502 | // For non-struct types, suffice to do the classic swap | ||
| 2503 | auto tmp = lhs; | ||
| 2504 | lhs = rhs; | ||
| 2505 | rhs = tmp; | ||
| 2506 | } | ||
| 2507 | } | ||
| 2508 | |||
| 2509 | /// | ||
| 2510 | @safe unittest | ||
| 2511 | { | ||
| 2512 | // Swapping POD (plain old data) types: | ||
| 2513 | int a = 42, b = 34; | ||
| 2514 | swap(a, b); | ||
| 2515 | assert(a == 34 && b == 42); | ||
| 2516 | |||
| 2517 | // Swapping structs with indirection: | ||
| 2518 | static struct S { int x; char c; int[] y; } | ||
| 2519 | S s1 = { 0, 'z', [ 1, 2 ] }; | ||
| 2520 | S s2 = { 42, 'a', [ 4, 6 ] }; | ||
| 2521 | swap(s1, s2); | ||
| 2522 | assert(s1.x == 42); | ||
| 2523 | assert(s1.c == 'a'); | ||
| 2524 | assert(s1.y == [ 4, 6 ]); | ||
| 2525 | |||
| 2526 | assert(s2.x == 0); | ||
| 2527 | assert(s2.c == 'z'); | ||
| 2528 | assert(s2.y == [ 1, 2 ]); | ||
| 2529 | |||
| 2530 | // Immutables cannot be swapped: | ||
| 2531 | immutable int imm1 = 1, imm2 = 2; | ||
| 2532 | static assert(!__traits(compiles, swap(imm1, imm2))); | ||
| 2533 | |||
| 2534 | int c = imm1 + 0; | ||
| 2535 | int d = imm2 + 0; | ||
| 2536 | swap(c, d); | ||
| 2537 | assert(c == 2); | ||
| 2538 | assert(d == 1); | ||
| 2539 | } | ||
| 2540 | |||
| 2541 | /// | ||
| 2542 | @safe unittest | ||
| 2543 | { | ||
| 2544 | // Non-copyable types can still be swapped. | ||
| 2545 | static struct NoCopy | ||
| 2546 | { | ||
| 2547 | this(this) { assert(0); } | ||
| 2548 | int n; | ||
| 2549 | string s; | ||
| 2550 | } | ||
| 2551 | NoCopy nc1, nc2; | ||
| 2552 | nc1.n = 127; nc1.s = "abc"; | ||
| 2553 | nc2.n = 513; nc2.s = "uvwxyz"; | ||
| 2554 | |||
| 2555 | swap(nc1, nc2); | ||
| 2556 | assert(nc1.n == 513 && nc1.s == "uvwxyz"); | ||
| 2557 | assert(nc2.n == 127 && nc2.s == "abc"); | ||
| 2558 | |||
| 2559 | swap(nc1, nc1); | ||
| 2560 | swap(nc2, nc2); | ||
| 2561 | assert(nc1.n == 513 && nc1.s == "uvwxyz"); | ||
| 2562 | assert(nc2.n == 127 && nc2.s == "abc"); | ||
| 2563 | |||
| 2564 | // Types containing non-copyable fields can also be swapped. | ||
| 2565 | static struct NoCopyHolder | ||
| 2566 | { | ||
| 2567 | NoCopy noCopy; | ||
| 2568 | } | ||
| 2569 | NoCopyHolder h1, h2; | ||
| 2570 | h1.noCopy.n = 31; h1.noCopy.s = "abc"; | ||
| 2571 | h2.noCopy.n = 65; h2.noCopy.s = null; | ||
| 2572 | |||
| 2573 | swap(h1, h2); | ||
| 2574 | assert(h1.noCopy.n == 65 && h1.noCopy.s == null); | ||
| 2575 | assert(h2.noCopy.n == 31 && h2.noCopy.s == "abc"); | ||
| 2576 | |||
| 2577 | swap(h1, h1); | ||
| 2578 | swap(h2, h2); | ||
| 2579 | assert(h1.noCopy.n == 65 && h1.noCopy.s == null); | ||
| 2580 | assert(h2.noCopy.n == 31 && h2.noCopy.s == "abc"); | ||
| 2581 | |||
| 2582 | // Const types cannot be swapped. | ||
| 2583 | const NoCopy const1, const2; | ||
| 2584 | assert(const1.n == 0 && const2.n == 0); | ||
| 2585 | static assert(!__traits(compiles, swap(const1, const2))); | ||
| 2586 | } | ||
| 2587 | |||
| 2588 | @safe unittest | ||
| 2589 | { | ||
| 2590 | //Bug# 4789 | ||
| 2591 | int[1] s = [1]; | ||
| 2592 | swap(s, s); | ||
| 2593 | |||
| 2594 | int[3] a = [1, 2, 3]; | ||
| 2595 | swap(a[1], a[2]); | ||
| 2596 | assert(a == [1, 3, 2]); | ||
| 2597 | } | ||
| 2598 | |||
| 2599 | @safe unittest | ||
| 2600 | { | ||
| 2601 | static struct NoAssign | ||
| 2602 | { | ||
| 2603 | int i; | ||
| 2604 | void opAssign(NoAssign) @disable; | ||
| 2605 | } | ||
| 2606 | auto s1 = NoAssign(1); | ||
| 2607 | auto s2 = NoAssign(2); | ||
| 2608 | swap(s1, s2); | ||
| 2609 | assert(s1.i == 2); | ||
| 2610 | assert(s2.i == 1); | ||
| 2611 | } | ||
| 2612 | |||
| 2613 | @safe unittest | ||
| 2614 | { | ||
| 2615 | struct S | ||
| 2616 | { | ||
| 2617 | const int i; | ||
| 2618 | int i2 = 2; | ||
| 2619 | int i3 = 3; | ||
| 2620 | } | ||
| 2621 | S s; | ||
| 2622 | static assert(!__traits(compiles, swap(s, s))); | ||
| 2623 | swap(s.i2, s.i3); | ||
| 2624 | assert(s.i2 == 3); | ||
| 2625 | assert(s.i3 == 2); | ||
| 2626 | } | ||
| 2627 | |||
| 2628 | @safe unittest | ||
| 2629 | { | ||
| 2630 | //11853 | ||
| 2631 | import std.traits : isAssignable; | ||
| 2632 | alias T = Tuple!(int, double); | ||
| 2633 | static assert(isAssignable!T); | ||
| 2634 | } | ||
| 2635 | |||
| 2636 | @safe unittest | ||
| 2637 | { | ||
| 2638 | // 12024 | ||
| 2639 | import std.datetime; | ||
| 2640 | SysTime a, b; | ||
| 2641 | swap(a, b); | ||
| 2642 | } | ||
| 2643 | |||
| 2644 | @system unittest // 9975 | ||
| 2645 | { | ||
| 2646 | import std.exception : doesPointTo, mayPointTo; | ||
| 2647 | static struct S2 | ||
| 2648 | { | ||
| 2649 | union | ||
| 2650 | { | ||
| 2651 | size_t sz; | ||
| 2652 | string s; | ||
| 2653 | } | ||
| 2654 | } | ||
| 2655 | S2 a , b; | ||
| 2656 | a.sz = -1; | ||
| 2657 | assert(!doesPointTo(a, b)); | ||
| 2658 | assert( mayPointTo(a, b)); | ||
| 2659 | swap(a, b); | ||
| 2660 | |||
| 2661 | //Note: we can catch an error here, because there is no RAII in this test | ||
| 2662 | import std.exception : assertThrown; | ||
| 2663 | void* p, pp; | ||
| 2664 | p = &p; | ||
| 2665 | assertThrown!Error(move(p)); | ||
| 2666 | assertThrown!Error(move(p, pp)); | ||
| 2667 | assertThrown!Error(swap(p, pp)); | ||
| 2668 | } | ||
| 2669 | |||
| 2670 | @system unittest | ||
| 2671 | { | ||
| 2672 | static struct A | ||
| 2673 | { | ||
| 2674 | int* x; | ||
| 2675 | this(this) { x = new int; } | ||
| 2676 | } | ||
| 2677 | A a1, a2; | ||
| 2678 | swap(a1, a2); | ||
| 2679 | |||
| 2680 | static struct B | ||
| 2681 | { | ||
| 2682 | int* x; | ||
| 2683 | void opAssign(B) { x = new int; } | ||
| 2684 | } | ||
| 2685 | B b1, b2; | ||
| 2686 | swap(b1, b2); | ||
| 2687 | } | ||
| 2688 | |||
| 2689 | /// ditto | ||
| 2690 | void swap(T)(ref T lhs, ref T rhs) | ||
| 2691 | if (is(typeof(lhs.proxySwap(rhs)))) | ||
| 2692 | { | ||
| 2693 | lhs.proxySwap(rhs); | ||
| 2694 | } | ||
| 2695 | |||
| 2696 | /** | ||
| 2697 | Swaps two elements in-place of a range `r`, | ||
| 2698 | specified by their indices `i1` and `i2`. | ||
| 2699 | |||
| 2700 | Params: | ||
| 2701 | r = a range with swappable elements | ||
| 2702 | i1 = first index | ||
| 2703 | i2 = second index | ||
| 2704 | */ | ||
| 2705 | void swapAt(R)(auto ref R r, size_t i1, size_t i2) | ||
| 2706 | { | ||
| 2707 | static if (is(typeof(&r.swapAt))) | ||
| 2708 | { | ||
| 2709 | r.swapAt(i1, i2); | ||
| 2710 | } | ||
| 2711 | else static if (is(typeof(&r[i1]))) | ||
| 2712 | { | ||
| 2713 | swap(r[i1], r[i2]); | ||
| 2714 | } | ||
| 2715 | else | ||
| 2716 | { | ||
| 2717 | if (i1 == i2) return; | ||
| 2718 | auto t1 = r.moveAt(i1); | ||
| 2719 | auto t2 = r.moveAt(i2); | ||
| 2720 | r[i2] = t1; | ||
| 2721 | r[i1] = t2; | ||
| 2722 | } | ||
| 2723 | } | ||
| 2724 | |||
| 2725 | /// | ||
| 2726 | pure @safe nothrow unittest | ||
| 2727 | { | ||
| 2728 | import std.algorithm.comparison : equal; | ||
| 2729 | auto a = [1, 2, 3]; | ||
| 2730 | a.swapAt(1, 2); | ||
| 2731 | assert(a.equal([1, 3, 2])); | ||
| 2732 | } | ||
| 2733 | |||
| 2734 | pure @safe nothrow unittest | ||
| 2735 | { | ||
| 2736 | import std.algorithm.comparison : equal; | ||
| 2737 | auto a = [4, 5, 6]; | ||
| 2738 | a.swapAt(1, 1); | ||
| 2739 | assert(a.equal([4, 5, 6])); | ||
| 2740 | } | ||
| 2741 | |||
| 2742 | pure @safe nothrow unittest | ||
| 2743 | { | ||
| 2744 | // test non random access ranges | ||
| 2745 | import std.algorithm.comparison : equal; | ||
| 2746 | import std.array : array; | ||
| 2747 | |||
| 2748 | char[] b = ['a', 'b', 'c']; | ||
| 2749 | b.swapAt(1, 2); | ||
| 2750 | assert(b.equal(['a', 'c', 'b'])); | ||
| 2751 | |||
| 2752 | int[3] c = [1, 2, 3]; | ||
| 2753 | c.swapAt(1, 2); | ||
| 2754 | assert(c.array.equal([1, 3, 2])); | ||
| 2755 | |||
| 2756 | // opIndex returns lvalue | ||
| 2757 | struct RandomIndexType(T) | ||
| 2758 | { | ||
| 2759 | T payload; | ||
| 2760 | |||
| 2761 | @property ref auto opIndex(size_t i) | ||
| 2762 | { | ||
| 2763 | return payload[i]; | ||
| 2764 | } | ||
| 2765 | |||
| 2766 | } | ||
| 2767 | auto d = RandomIndexType!(int[])([4, 5, 6]); | ||
| 2768 | d.swapAt(1, 2); | ||
| 2769 | assert(d.payload.equal([4, 6, 5])); | ||
| 2770 | |||
| 2771 | // custom moveAt and opIndexAssign | ||
| 2772 | struct RandomMoveAtType(T) | ||
| 2773 | { | ||
| 2774 | T payload; | ||
| 2775 | |||
| 2776 | ElementType!T moveAt(size_t i) | ||
| 2777 | { | ||
| 2778 | return payload.moveAt(i); | ||
| 2779 | } | ||
| 2780 | |||
| 2781 | void opIndexAssign(ElementType!T val, size_t idx) | ||
| 2782 | { | ||
| 2783 | payload[idx] = val; | ||
| 2784 | } | ||
| 2785 | } | ||
| 2786 | auto e = RandomMoveAtType!(int[])([7, 8, 9]); | ||
| 2787 | e.swapAt(1, 2); | ||
| 2788 | assert(e.payload.equal([7, 9, 8])); | ||
| 2789 | |||
| 2790 | |||
| 2791 | // custom swapAt | ||
| 2792 | struct RandomSwapAtType(T) | ||
| 2793 | { | ||
| 2794 | T payload; | ||
| 2795 | |||
| 2796 | void swapAt(size_t i) | ||
| 2797 | { | ||
| 2798 | return payload.swapAt(i); | ||
| 2799 | } | ||
| 2800 | } | ||
| 2801 | auto f = RandomMoveAtType!(int[])([10, 11, 12]); | ||
| 2802 | swapAt(f, 1, 2); | ||
| 2803 | assert(f.payload.equal([10, 12, 11])); | ||
| 2804 | } | ||
| 2805 | |||
| 2806 | private void swapFront(R1, R2)(R1 r1, R2 r2) | ||
| 2807 | if (isInputRange!R1 && isInputRange!R2) | ||
| 2808 | { | ||
| 2809 | static if (is(typeof(swap(r1.front, r2.front)))) | ||
| 2810 | { | ||
| 2811 | swap(r1.front, r2.front); | ||
| 2812 | } | ||
| 2813 | else | ||
| 2814 | { | ||
| 2815 | auto t1 = moveFront(r1), t2 = moveFront(r2); | ||
| 2816 | r1.front = move(t2); | ||
| 2817 | r2.front = move(t1); | ||
| 2818 | } | ||
| 2819 | } | ||
| 2820 | |||
| 2821 | // swapRanges | ||
| 2822 | /** | ||
| 2823 | Swaps all elements of $(D r1) with successive elements in $(D r2). | ||
| 2824 | Returns a tuple containing the remainder portions of $(D r1) and $(D | ||
| 2825 | r2) that were not swapped (one of them will be empty). The ranges may | ||
| 2826 | be of different types but must have the same element type and support | ||
| 2827 | swapping. | ||
| 2828 | |||
| 2829 | Params: | ||
| 2830 | r1 = an $(REF_ALTTEXT input _range, isInputRange, std,_range,primitives) | ||
| 2831 | with swappable elements | ||
| 2832 | r2 = an $(REF_ALTTEXT input _range, isInputRange, std,_range,primitives) | ||
| 2833 | with swappable elements | ||
| 2834 | |||
| 2835 | Returns: | ||
| 2836 | Tuple containing the remainder portions of r1 and r2 that were not swapped | ||
| 2837 | */ | ||
| 2838 | Tuple!(InputRange1, InputRange2) | ||
| 2839 | swapRanges(InputRange1, InputRange2)(InputRange1 r1, InputRange2 r2) | ||
| 2840 | if (hasSwappableElements!InputRange1 && hasSwappableElements!InputRange2 | ||
| 2841 | && is(ElementType!InputRange1 == ElementType!InputRange2)) | ||
| 2842 | { | ||
| 2843 | for (; !r1.empty && !r2.empty; r1.popFront(), r2.popFront()) | ||
| 2844 | { | ||
| 2845 | swap(r1.front, r2.front); | ||
| 2846 | } | ||
| 2847 | return tuple(r1, r2); | ||
| 2848 | } | ||
| 2849 | |||
| 2850 | /// | ||
| 2851 | @safe unittest | ||
| 2852 | { | ||
| 2853 | import std.range : empty; | ||
| 2854 | int[] a = [ 100, 101, 102, 103 ]; | ||
| 2855 | int[] b = [ 0, 1, 2, 3 ]; | ||
| 2856 | auto c = swapRanges(a[1 .. 3], b[2 .. 4]); | ||
| 2857 | assert(c[0].empty && c[1].empty); | ||
| 2858 | assert(a == [ 100, 2, 3, 103 ]); | ||
| 2859 | assert(b == [ 0, 1, 101, 102 ]); | ||
| 2860 | } | ||
| 2861 | |||
| 2862 | /** | ||
| 2863 | Initializes each element of $(D range) with $(D value). | ||
| 2864 | Assumes that the elements of the range are uninitialized. | ||
| 2865 | This is of interest for structs that | ||
| 2866 | define copy constructors (for all other types, $(LREF fill) and | ||
| 2867 | uninitializedFill are equivalent). | ||
| 2868 | |||
| 2869 | Params: | ||
| 2870 | range = An | ||
| 2871 | $(REF_ALTTEXT input _range, isInputRange, std,_range,primitives) | ||
| 2872 | that exposes references to its elements and has assignable | ||
| 2873 | elements | ||
| 2874 | value = Assigned to each element of range | ||
| 2875 | |||
| 2876 | See_Also: | ||
| 2877 | $(LREF fill) | ||
| 2878 | $(LREF initializeAll) | ||
| 2879 | */ | ||
| 2880 | void uninitializedFill(Range, Value)(Range range, Value value) | ||
| 2881 | if (isInputRange!Range && hasLvalueElements!Range && is(typeof(range.front = value))) | ||
| 2882 | { | ||
| 2883 | import std.traits : hasElaborateAssign; | ||
| 2884 | |||
| 2885 | alias T = ElementType!Range; | ||
| 2886 | static if (hasElaborateAssign!T) | ||
| 2887 | { | ||
| 2888 | import std.conv : emplaceRef; | ||
| 2889 | |||
| 2890 | // Must construct stuff by the book | ||
| 2891 | for (; !range.empty; range.popFront()) | ||
| 2892 | emplaceRef!T(range.front, value); | ||
| 2893 | } | ||
| 2894 | else | ||
| 2895 | // Doesn't matter whether fill is initialized or not | ||
| 2896 | return fill(range, value); | ||
| 2897 | } | ||
| 2898 | |||
| 2899 | /// | ||
| 2900 | nothrow @system unittest | ||
| 2901 | { | ||
| 2902 | import core.stdc.stdlib : malloc, free; | ||
| 2903 | |||
| 2904 | auto s = (cast(int*) malloc(5 * int.sizeof))[0 .. 5]; | ||
| 2905 | uninitializedFill(s, 42); | ||
| 2906 | assert(s == [ 42, 42, 42, 42, 42 ]); | ||
| 2907 | |||
| 2908 | scope(exit) free(s.ptr); | ||
| 2909 | } | ||
| @@ -0,0 +1,198 @@ | |||
| 1 | // Written in the D programming language. | ||
| 2 | |||
| 3 | /** | ||
| 4 | This package implements generic algorithms oriented towards the processing of | ||
| 5 | sequences. Sequences processed by these functions define range-based | ||
| 6 | interfaces. See also $(MREF_ALTTEXT Reference on ranges, std, range) and | ||
| 7 | $(HTTP ddili.org/ders/d.en/ranges.html, tutorial on ranges). | ||
| 8 | |||
| 9 | $(SCRIPT inhibitQuickIndex = 1;) | ||
| 10 | |||
| 11 | Algorithms are categorized into the following submodules: | ||
| 12 | |||
| 13 | $(DIVC quickindex, | ||
| 14 | $(BOOKTABLE , | ||
| 15 | $(TR $(TH Submodule) $(TH Functions) | ||
| 16 | ) | ||
| 17 | $(TR | ||
| 18 | $(TDNW $(SUBMODULE Searching, searching)) | ||
| 19 | $(TD | ||
| 20 | $(SUBREF searching, all) | ||
| 21 | $(SUBREF searching, any) | ||
| 22 | $(SUBREF searching, balancedParens) | ||
| 23 | $(SUBREF searching, boyerMooreFinder) | ||
| 24 | $(SUBREF searching, canFind) | ||
| 25 | $(SUBREF searching, commonPrefix) | ||
| 26 | $(SUBREF searching, count) | ||
| 27 | $(SUBREF searching, countUntil) | ||
| 28 | $(SUBREF searching, endsWith) | ||
| 29 | $(SUBREF searching, find) | ||
| 30 | $(SUBREF searching, findAdjacent) | ||
| 31 | $(SUBREF searching, findAmong) | ||
| 32 | $(SUBREF searching, findSkip) | ||
| 33 | $(SUBREF searching, findSplit) | ||
| 34 | $(SUBREF searching, findSplitAfter) | ||
| 35 | $(SUBREF searching, findSplitBefore) | ||
| 36 | $(SUBREF searching, minCount) | ||
| 37 | $(SUBREF searching, maxCount) | ||
| 38 | $(SUBREF searching, minElement) | ||
| 39 | $(SUBREF searching, maxElement) | ||
| 40 | $(SUBREF searching, minIndex) | ||
| 41 | $(SUBREF searching, maxIndex) | ||
| 42 | $(SUBREF searching, minPos) | ||
| 43 | $(SUBREF searching, maxPos) | ||
| 44 | $(SUBREF searching, skipOver) | ||
| 45 | $(SUBREF searching, startsWith) | ||
| 46 | $(SUBREF searching, until) | ||
| 47 | ) | ||
| 48 | ) | ||
| 49 | $(TR | ||
| 50 | $(TDNW $(SUBMODULE Comparison, comparison)) | ||
| 51 | $(TD | ||
| 52 | $(SUBREF comparison, among) | ||
| 53 | $(SUBREF comparison, castSwitch) | ||
| 54 | $(SUBREF comparison, clamp) | ||
| 55 | $(SUBREF comparison, cmp) | ||
| 56 | $(SUBREF comparison, either) | ||
| 57 | $(SUBREF comparison, equal) | ||
| 58 | $(SUBREF comparison, isPermutation) | ||
| 59 | $(SUBREF comparison, isSameLength) | ||
| 60 | $(SUBREF comparison, levenshteinDistance) | ||
| 61 | $(SUBREF comparison, levenshteinDistanceAndPath) | ||
| 62 | $(SUBREF comparison, max) | ||
| 63 | $(SUBREF comparison, min) | ||
| 64 | $(SUBREF comparison, mismatch) | ||
| 65 | $(SUBREF comparison, predSwitch) | ||
| 66 | ) | ||
| 67 | ) | ||
| 68 | $(TR | ||
| 69 | $(TDNW $(SUBMODULE Iteration, iteration)) | ||
| 70 | $(TD | ||
| 71 | $(SUBREF iteration, cache) | ||
| 72 | $(SUBREF iteration, cacheBidirectional) | ||
| 73 | $(SUBREF iteration, chunkBy) | ||
| 74 | $(SUBREF iteration, cumulativeFold) | ||
| 75 | $(SUBREF iteration, each) | ||
| 76 | $(SUBREF iteration, filter) | ||
| 77 | $(SUBREF iteration, filterBidirectional) | ||
| 78 | $(SUBREF iteration, fold) | ||
| 79 | $(SUBREF iteration, group) | ||
| 80 | $(SUBREF iteration, joiner) | ||
| 81 | $(SUBREF iteration, map) | ||
| 82 | $(SUBREF iteration, permutations) | ||
| 83 | $(SUBREF iteration, reduce) | ||
| 84 | $(SUBREF iteration, splitter) | ||
| 85 | $(SUBREF iteration, sum) | ||
| 86 | $(SUBREF iteration, uniq) | ||
| 87 | ) | ||
| 88 | ) | ||
| 89 | $(TR | ||
| 90 | $(TDNW $(SUBMODULE Sorting, sorting)) | ||
| 91 | $(TD | ||
| 92 | $(SUBREF sorting, completeSort) | ||
| 93 | $(SUBREF sorting, isPartitioned) | ||
| 94 | $(SUBREF sorting, isSorted) | ||
| 95 | $(SUBREF sorting, isStrictlyMonotonic) | ||
| 96 | $(SUBREF sorting, ordered) | ||
| 97 | $(SUBREF sorting, strictlyOrdered) | ||
| 98 | $(SUBREF sorting, makeIndex) | ||
| 99 | $(SUBREF sorting, merge) | ||
| 100 | $(SUBREF sorting, multiSort) | ||
| 101 | $(SUBREF sorting, nextEvenPermutation) | ||
| 102 | $(SUBREF sorting, nextPermutation) | ||
| 103 | $(SUBREF sorting, partialSort) | ||
| 104 | $(SUBREF sorting, partition) | ||
| 105 | $(SUBREF sorting, partition3) | ||
| 106 | $(SUBREF sorting, schwartzSort) | ||
| 107 | $(SUBREF sorting, sort) | ||
| 108 | $(SUBREF sorting, topN) | ||
| 109 | $(SUBREF sorting, topNCopy) | ||
| 110 | $(SUBREF sorting, topNIndex) | ||
| 111 | ) | ||
| 112 | ) | ||
| 113 | $(TR | ||
| 114 | $(TDNW Set operations $(BR)($(SUBMODULE setops, setops))) | ||
| 115 | $(TD | ||
| 116 | $(SUBREF setops, cartesianProduct) | ||
| 117 | $(SUBREF setops, largestPartialIntersection) | ||
| 118 | $(SUBREF setops, largestPartialIntersectionWeighted) | ||
| 119 | $(SUBREF setops, multiwayMerge) | ||
| 120 | $(SUBREF setops, multiwayUnion) | ||
| 121 | $(SUBREF setops, setDifference) | ||
| 122 | $(SUBREF setops, setIntersection) | ||
| 123 | $(SUBREF setops, setSymmetricDifference) | ||
| 124 | ) | ||
| 125 | ) | ||
| 126 | $(TR | ||
| 127 | $(TDNW $(SUBMODULE Mutation, mutation)) | ||
| 128 | $(TD | ||
| 129 | $(SUBREF mutation, bringToFront) | ||
| 130 | $(SUBREF mutation, copy) | ||
| 131 | $(SUBREF mutation, fill) | ||
| 132 | $(SUBREF mutation, initializeAll) | ||
| 133 | $(SUBREF mutation, move) | ||
| 134 | $(SUBREF mutation, moveAll) | ||
| 135 | $(SUBREF mutation, moveSome) | ||
| 136 | $(SUBREF mutation, moveEmplace) | ||
| 137 | $(SUBREF mutation, moveEmplaceAll) | ||
| 138 | $(SUBREF mutation, moveEmplaceSome) | ||
| 139 | $(SUBREF mutation, remove) | ||
| 140 | $(SUBREF mutation, reverse) | ||
| 141 | $(SUBREF mutation, strip) | ||
| 142 | $(SUBREF mutation, stripLeft) | ||
| 143 | $(SUBREF mutation, stripRight) | ||
| 144 | $(SUBREF mutation, swap) | ||
| 145 | $(SUBREF mutation, swapRanges) | ||
| 146 | $(SUBREF mutation, uninitializedFill) | ||
| 147 | ) | ||
| 148 | ) | ||
| 149 | )) | ||
| 150 | |||
| 151 | Many functions in this package are parameterized with a $(GLOSSARY predicate). | ||
| 152 | The predicate may be any suitable callable type | ||
| 153 | (a function, a delegate, a $(GLOSSARY functor), or a lambda), or a | ||
| 154 | compile-time string. The string may consist of $(B any) legal D | ||
| 155 | expression that uses the symbol $(D a) (for unary functions) or the | ||
| 156 | symbols $(D a) and $(D b) (for binary functions). These names will NOT | ||
| 157 | interfere with other homonym symbols in user code because they are | ||
| 158 | evaluated in a different context. The default for all binary | ||
| 159 | comparison predicates is $(D "a == b") for unordered operations and | ||
| 160 | $(D "a < b") for ordered operations. | ||
| 161 | |||
| 162 | Example: | ||
| 163 | |||
| 164 | ---- | ||
| 165 | int[] a = ...; | ||
| 166 | static bool greater(int a, int b) | ||
| 167 | { | ||
| 168 | return a > b; | ||
| 169 | } | ||
| 170 | sort!greater(a); // predicate as alias | ||
| 171 | sort!((a, b) => a > b)(a); // predicate as a lambda. | ||
| 172 | sort!"a > b"(a); // predicate as string | ||
| 173 | // (no ambiguity with array name) | ||
| 174 | sort(a); // no predicate, "a < b" is implicit | ||
| 175 | ---- | ||
| 176 | |||
| 177 | Macros: | ||
| 178 | SUBMODULE = $(MREF_ALTTEXT $1, std, algorithm, $2) | ||
| 179 | SUBREF = $(REF_ALTTEXT $(TT $2), $2, std, algorithm, $1)$(NBSP) | ||
| 180 | |||
| 181 | Copyright: Andrei Alexandrescu 2008-. | ||
| 182 | |||
| 183 | License: $(HTTP boost.org/LICENSE_1_0.txt, Boost License 1.0). | ||
| 184 | |||
| 185 | Authors: $(HTTP erdani.com, Andrei Alexandrescu) | ||
| 186 | |||
| 187 | Source: $(PHOBOSSRC std/_algorithm/package.d) | ||
| 188 | */ | ||
| 189 | module std.algorithm; | ||
| 190 | |||
| 191 | public import std.algorithm.comparison; | ||
| 192 | public import std.algorithm.iteration; | ||
| 193 | public import std.algorithm.mutation; | ||
| 194 | public import std.algorithm.searching; | ||
| 195 | public import std.algorithm.setops; | ||
| 196 | public import std.algorithm.sorting; | ||
| 197 | |||
| 198 | static import std.functional; | ||
| @@ -0,0 +1,4600 @@ | |||
| 1 | // Written in the D programming language. | ||
| 2 | /** | ||
| 3 | This is a submodule of $(MREF std, algorithm). | ||
| 4 | It contains generic _searching algorithms. | ||
| 5 | |||
| 6 | $(SCRIPT inhibitQuickIndex = 1;) | ||
| 7 | $(BOOKTABLE Cheat Sheet, | ||
| 8 | $(TR $(TH Function Name) $(TH Description)) | ||
| 9 | $(T2 all, | ||
| 10 | $(D all!"a > 0"([1, 2, 3, 4])) returns $(D true) because all elements | ||
| 11 | are positive) | ||
| 12 | $(T2 any, | ||
| 13 | $(D any!"a > 0"([1, 2, -3, -4])) returns $(D true) because at least one | ||
| 14 | element is positive) | ||
| 15 | $(T2 balancedParens, | ||
| 16 | $(D balancedParens("((1 + 1) / 2)")) returns $(D true) because the | ||
| 17 | string has balanced parentheses.) | ||
| 18 | $(T2 boyerMooreFinder, | ||
| 19 | $(D find("hello world", boyerMooreFinder("or"))) returns $(D "orld") | ||
| 20 | using the $(LINK2 https://en.wikipedia.org/wiki/Boyer%E2%80%93Moore_string_search_algorithm, | ||
| 21 | Boyer-Moore _algorithm).) | ||
| 22 | $(T2 canFind, | ||
| 23 | $(D canFind("hello world", "or")) returns $(D true).) | ||
| 24 | $(T2 count, | ||
| 25 | Counts elements that are equal to a specified value or satisfy a | ||
| 26 | predicate. $(D count([1, 2, 1], 1)) returns $(D 2) and | ||
| 27 | $(D count!"a < 0"([1, -3, 0])) returns $(D 1).) | ||
| 28 | $(T2 countUntil, | ||
| 29 | $(D countUntil(a, b)) returns the number of steps taken in $(D a) to | ||
| 30 | reach $(D b); for example, $(D countUntil("hello!", "o")) returns | ||
| 31 | $(D 4).) | ||
| 32 | $(T2 commonPrefix, | ||
| 33 | $(D commonPrefix("parakeet", "parachute")) returns $(D "para").) | ||
| 34 | $(T2 endsWith, | ||
| 35 | $(D endsWith("rocks", "ks")) returns $(D true).) | ||
| 36 | $(T2 find, | ||
| 37 | $(D find("hello world", "or")) returns $(D "orld") using linear search. | ||
| 38 | (For binary search refer to $(REF sortedRange, std,range).)) | ||
| 39 | $(T2 findAdjacent, | ||
| 40 | $(D findAdjacent([1, 2, 3, 3, 4])) returns the subrange starting with | ||
| 41 | two equal adjacent elements, i.e. $(D [3, 3, 4]).) | ||
| 42 | $(T2 findAmong, | ||
| 43 | $(D findAmong("abcd", "qcx")) returns $(D "cd") because $(D 'c') is | ||
| 44 | among $(D "qcx").) | ||
| 45 | $(T2 findSkip, | ||
| 46 | If $(D a = "abcde"), then $(D findSkip(a, "x")) returns $(D false) and | ||
| 47 | leaves $(D a) unchanged, whereas $(D findSkip(a, "c")) advances $(D a) | ||
| 48 | to $(D "de") and returns $(D true).) | ||
| 49 | $(T2 findSplit, | ||
| 50 | $(D findSplit("abcdefg", "de")) returns the three ranges $(D "abc"), | ||
| 51 | $(D "de"), and $(D "fg").) | ||
| 52 | $(T2 findSplitAfter, | ||
| 53 | $(D findSplitAfter("abcdefg", "de")) returns the two ranges | ||
| 54 | $(D "abcde") and $(D "fg").) | ||
| 55 | $(T2 findSplitBefore, | ||
| 56 | $(D findSplitBefore("abcdefg", "de")) returns the two ranges $(D "abc") | ||
| 57 | and $(D "defg").) | ||
| 58 | $(T2 minCount, | ||
| 59 | $(D minCount([2, 1, 1, 4, 1])) returns $(D tuple(1, 3)).) | ||
| 60 | $(T2 maxCount, | ||
| 61 | $(D maxCount([2, 4, 1, 4, 1])) returns $(D tuple(4, 2)).) | ||
| 62 | $(T2 minElement, | ||
| 63 | Selects the minimal element of a range. | ||
| 64 | `minElement([3, 4, 1, 2])` returns `1`.) | ||
| 65 | $(T2 maxElement, | ||
| 66 | Selects the maximal element of a range. | ||
| 67 | `maxElement([3, 4, 1, 2])` returns `4`.) | ||
| 68 | $(T2 minIndex, | ||
| 69 | Index of the minimal element of a range. | ||
| 70 | `minElement([3, 4, 1, 2])` returns `2`.) | ||
| 71 | $(T2 maxIndex, | ||
| 72 | Index of the maximal element of a range. | ||
| 73 | `maxElement([3, 4, 1, 2])` returns `1`.) | ||
| 74 | $(T2 minPos, | ||
| 75 | $(D minPos([2, 3, 1, 3, 4, 1])) returns the subrange $(D [1, 3, 4, 1]), | ||
| 76 | i.e., positions the range at the first occurrence of its minimal | ||
| 77 | element.) | ||
| 78 | $(T2 maxPos, | ||
| 79 | $(D maxPos([2, 3, 1, 3, 4, 1])) returns the subrange $(D [4, 1]), | ||
| 80 | i.e., positions the range at the first occurrence of its maximal | ||
| 81 | element.) | ||
| 82 | $(T2 mismatch, | ||
| 83 | $(D mismatch("parakeet", "parachute")) returns the two ranges | ||
| 84 | $(D "keet") and $(D "chute").) | ||
| 85 | $(T2 skipOver, | ||
| 86 | Assume $(D a = "blah"). Then $(D skipOver(a, "bi")) leaves $(D a) | ||
| 87 | unchanged and returns $(D false), whereas $(D skipOver(a, "bl")) | ||
| 88 | advances $(D a) to refer to $(D "ah") and returns $(D true).) | ||
| 89 | $(T2 startsWith, | ||
| 90 | $(D startsWith("hello, world", "hello")) returns $(D true).) | ||
| 91 | $(T2 until, | ||
| 92 | Lazily iterates a range until a specific value is found.) | ||
| 93 | ) | ||
| 94 | |||
| 95 | Copyright: Andrei Alexandrescu 2008-. | ||
| 96 | |||
| 97 | License: $(HTTP boost.org/LICENSE_1_0.txt, Boost License 1.0). | ||
| 98 | |||
| 99 | Authors: $(HTTP erdani.com, Andrei Alexandrescu) | ||
| 100 | |||
| 101 | Source: $(PHOBOSSRC std/algorithm/_searching.d) | ||
| 102 | |||
| 103 | Macros: | ||
| 104 | T2=$(TR $(TDNW $(LREF $1)) $(TD $+)) | ||
| 105 | */ | ||
| 106 | module std.algorithm.searching; | ||
| 107 | |||
| 108 | // FIXME | ||
| 109 | import std.functional; // : unaryFun, binaryFun; | ||
| 110 | import std.range.primitives; | ||
| 111 | import std.traits; | ||
| 112 | // FIXME | ||
| 113 | import std.typecons; // : Tuple, Flag, Yes, No; | ||
| 114 | |||
| 115 | /++ | ||
| 116 | Checks if $(I _all) of the elements verify $(D pred). | ||
| 117 | +/ | ||
| 118 | template all(alias pred = "a") | ||
| 119 | { | ||
| 120 | /++ | ||
| 121 | Returns $(D true) if and only if $(I _all) values $(D v) found in the | ||
| 122 | input _range $(D range) satisfy the predicate $(D pred). | ||
| 123 | Performs (at most) $(BIGOH range.length) evaluations of $(D pred). | ||
| 124 | +/ | ||
| 125 | bool all(Range)(Range range) | ||
| 126 | if (isInputRange!Range && is(typeof(unaryFun!pred(range.front)))) | ||
| 127 | { | ||
| 128 | import std.functional : not; | ||
| 129 | |||
| 130 | return find!(not!(unaryFun!pred))(range).empty; | ||
| 131 | } | ||
| 132 | } | ||
| 133 | |||
| 134 | /// | ||
| 135 | @safe unittest | ||
| 136 | { | ||
| 137 | assert( all!"a & 1"([1, 3, 5, 7, 9])); | ||
| 138 | assert(!all!"a & 1"([1, 2, 3, 5, 7, 9])); | ||
| 139 | } | ||
| 140 | |||
| 141 | /++ | ||
| 142 | $(D all) can also be used without a predicate, if its items can be | ||
| 143 | evaluated to true or false in a conditional statement. This can be a | ||
| 144 | convenient way to quickly evaluate that $(I _all) of the elements of a range | ||
| 145 | are true. | ||
| 146 | +/ | ||
| 147 | @safe unittest | ||
| 148 | { | ||
| 149 | int[3] vals = [5, 3, 18]; | ||
| 150 | assert( all(vals[])); | ||
| 151 | } | ||
| 152 | |||
| 153 | @safe unittest | ||
| 154 | { | ||
| 155 | int x = 1; | ||
| 156 | assert(all!(a => a > x)([2, 3])); | ||
| 157 | } | ||
| 158 | |||
| 159 | /++ | ||
| 160 | Checks if $(I _any) of the elements verifies $(D pred). | ||
| 161 | $(D !any) can be used to verify that $(I none) of the elements verify | ||
| 162 | $(D pred). | ||
| 163 | This is sometimes called `exists` in other languages. | ||
| 164 | +/ | ||
| 165 | template any(alias pred = "a") | ||
| 166 | { | ||
| 167 | /++ | ||
| 168 | Returns $(D true) if and only if $(I _any) value $(D v) found in the | ||
| 169 | input _range $(D range) satisfies the predicate $(D pred). | ||
| 170 | Performs (at most) $(BIGOH range.length) evaluations of $(D pred). | ||
| 171 | +/ | ||
| 172 | bool any(Range)(Range range) | ||
| 173 | if (isInputRange!Range && is(typeof(unaryFun!pred(range.front)))) | ||
| 174 | { | ||
| 175 | return !find!pred(range).empty; | ||
| 176 | } | ||
| 177 | } | ||
| 178 | |||
| 179 | /// | ||
| 180 | @safe unittest | ||
| 181 | { | ||
| 182 | import std.ascii : isWhite; | ||
| 183 | assert( all!(any!isWhite)(["a a", "b b"])); | ||
| 184 | assert(!any!(all!isWhite)(["a a", "b b"])); | ||
| 185 | } | ||
| 186 | |||
| 187 | /++ | ||
| 188 | $(D any) can also be used without a predicate, if its items can be | ||
| 189 | evaluated to true or false in a conditional statement. $(D !any) can be a | ||
| 190 | convenient way to quickly test that $(I none) of the elements of a range | ||
| 191 | evaluate to true. | ||
| 192 | +/ | ||
| 193 | @safe unittest | ||
| 194 | { | ||
| 195 | int[3] vals1 = [0, 0, 0]; | ||
| 196 | assert(!any(vals1[])); //none of vals1 evaluate to true | ||
| 197 | |||
| 198 | int[3] vals2 = [2, 0, 2]; | ||
| 199 | assert( any(vals2[])); | ||
| 200 | assert(!all(vals2[])); | ||
| 201 | |||
| 202 | int[3] vals3 = [3, 3, 3]; | ||
| 203 | assert( any(vals3[])); | ||
| 204 | assert( all(vals3[])); | ||
| 205 | } | ||
| 206 | |||
| 207 | @safe unittest | ||
| 208 | { | ||
| 209 | auto a = [ 1, 2, 0, 4 ]; | ||
| 210 | assert(any!"a == 2"(a)); | ||
| 211 | } | ||
| 212 | |||
| 213 | // balancedParens | ||
| 214 | /** | ||
| 215 | Checks whether $(D r) has "balanced parentheses", i.e. all instances | ||
| 216 | of $(D lPar) are closed by corresponding instances of $(D rPar). The | ||
| 217 | parameter $(D maxNestingLevel) controls the nesting level allowed. The | ||
| 218 | most common uses are the default or $(D 0). In the latter case, no | ||
| 219 | nesting is allowed. | ||
| 220 | |||
| 221 | Params: | ||
| 222 | r = The range to check. | ||
| 223 | lPar = The element corresponding with a left (opening) parenthesis. | ||
| 224 | rPar = The element corresponding with a right (closing) parenthesis. | ||
| 225 | maxNestingLevel = The maximum allowed nesting level. | ||
| 226 | |||
| 227 | Returns: | ||
| 228 | true if the given range has balanced parenthesis within the given maximum | ||
| 229 | nesting level; false otherwise. | ||
| 230 | */ | ||
| 231 | bool balancedParens(Range, E)(Range r, E lPar, E rPar, | ||
| 232 | size_t maxNestingLevel = size_t.max) | ||
| 233 | if (isInputRange!(Range) && is(typeof(r.front == lPar))) | ||
| 234 | { | ||
| 235 | size_t count; | ||
| 236 | for (; !r.empty; r.popFront()) | ||
| 237 | { | ||
| 238 | if (r.front == lPar) | ||
| 239 | { | ||
| 240 | if (count > maxNestingLevel) return false; | ||
| 241 | ++count; | ||
| 242 | } | ||
| 243 | else if (r.front == rPar) | ||
| 244 | { | ||
| 245 | if (!count) return false; | ||
| 246 | --count; | ||
| 247 | } | ||
| 248 | } | ||
| 249 | return count == 0; | ||
| 250 | } | ||
| 251 | |||
| 252 | /// | ||
| 253 | @safe unittest | ||
| 254 | { | ||
| 255 | auto s = "1 + (2 * (3 + 1 / 2)"; | ||
| 256 | assert(!balancedParens(s, '(', ')')); | ||
| 257 | s = "1 + (2 * (3 + 1) / 2)"; | ||
| 258 | assert(balancedParens(s, '(', ')')); | ||
| 259 | s = "1 + (2 * (3 + 1) / 2)"; | ||
| 260 | assert(!balancedParens(s, '(', ')', 0)); | ||
| 261 | s = "1 + (2 * 3 + 1) / (2 - 5)"; | ||
| 262 | assert(balancedParens(s, '(', ')', 0)); | ||
| 263 | } | ||
| 264 | |||
| 265 | /** | ||
| 266 | * Sets up Boyer-Moore matching for use with $(D find) below. | ||
| 267 | * By default, elements are compared for equality. | ||
| 268 | * | ||
| 269 | * $(D BoyerMooreFinder) allocates GC memory. | ||
| 270 | * | ||
| 271 | * Params: | ||
| 272 | * pred = Predicate used to compare elements. | ||
| 273 | * needle = A random-access range with length and slicing. | ||
| 274 | * | ||
| 275 | * Returns: | ||
| 276 | * An instance of $(D BoyerMooreFinder) that can be used with $(D find()) to | ||
| 277 | * invoke the Boyer-Moore matching algorithm for finding of $(D needle) in a | ||
| 278 | * given haystack. | ||
| 279 | */ | ||
| 280 | struct BoyerMooreFinder(alias pred, Range) | ||
| 281 | { | ||
| 282 | private: | ||
| 283 | size_t[] skip; // GC allocated | ||
| 284 | ptrdiff_t[ElementType!(Range)] occ; // GC allocated | ||
| 285 | Range needle; | ||
| 286 | |||
| 287 | ptrdiff_t occurrence(ElementType!(Range) c) | ||
| 288 | { | ||
| 289 | auto p = c in occ; | ||
| 290 | return p ? *p : -1; | ||
| 291 | } | ||
| 292 | |||
| 293 | /* | ||
| 294 | This helper function checks whether the last "portion" bytes of | ||
| 295 | "needle" (which is "nlen" bytes long) exist within the "needle" at | ||
| 296 | offset "offset" (counted from the end of the string), and whether the | ||
| 297 | character preceding "offset" is not a match. Notice that the range | ||
| 298 | being checked may reach beyond the beginning of the string. Such range | ||
| 299 | is ignored. | ||
| 300 | */ | ||
| 301 | static bool needlematch(R)(R needle, | ||
| 302 | size_t portion, size_t offset) | ||
| 303 | { | ||
| 304 | import std.algorithm.comparison : equal; | ||
| 305 | ptrdiff_t virtual_begin = needle.length - offset - portion; | ||
| 306 | ptrdiff_t ignore = 0; | ||
| 307 | if (virtual_begin < 0) | ||
| 308 | { | ||
| 309 | ignore = -virtual_begin; | ||
| 310 | virtual_begin = 0; | ||
| 311 | } | ||
| 312 | if (virtual_begin > 0 | ||
| 313 | && needle[virtual_begin - 1] == needle[$ - portion - 1]) | ||
| 314 | return 0; | ||
| 315 | |||
| 316 | immutable delta = portion - ignore; | ||
| 317 | return equal(needle[needle.length - delta .. needle.length], | ||
| 318 | needle[virtual_begin .. virtual_begin + delta]); | ||
| 319 | } | ||
| 320 | |||
| 321 | public: | ||
| 322 | /// | ||
| 323 | this(Range needle) | ||
| 324 | { | ||
| 325 | if (!needle.length) return; | ||
| 326 | this.needle = needle; | ||
| 327 | /* Populate table with the analysis of the needle */ | ||
| 328 | /* But ignoring the last letter */ | ||
| 329 | foreach (i, n ; needle[0 .. $ - 1]) | ||
| 330 | { | ||
| 331 | this.occ[n] = i; | ||
| 332 | } | ||
| 333 | /* Preprocess #2: init skip[] */ | ||
| 334 | /* Note: This step could be made a lot faster. | ||
| 335 | * A simple implementation is shown here. */ | ||
| 336 | this.skip = new size_t[needle.length]; | ||
| 337 | foreach (a; 0 .. needle.length) | ||
| 338 | { | ||
| 339 | size_t value = 0; | ||
| 340 | while (value < needle.length | ||
| 341 | && !needlematch(needle, a, value)) | ||
| 342 | { | ||
| 343 | ++value; | ||
| 344 | } | ||
| 345 | this.skip[needle.length - a - 1] = value; | ||
| 346 | } | ||
| 347 | } | ||
| 348 | |||
| 349 | /// | ||
| 350 | Range beFound(Range haystack) | ||
| 351 | { | ||
| 352 | import std.algorithm.comparison : max; | ||
| 353 | |||
| 354 | if (!needle.length) return haystack; | ||
| 355 | if (needle.length > haystack.length) return haystack[$ .. $]; | ||
| 356 | /* Search: */ | ||
| 357 | immutable limit = haystack.length - needle.length; | ||
| 358 | for (size_t hpos = 0; hpos <= limit; ) | ||
| 359 | { | ||
| 360 | size_t npos = needle.length - 1; | ||
| 361 | while (pred(needle[npos], haystack[npos+hpos])) | ||
| 362 | { | ||
| 363 | if (npos == 0) return haystack[hpos .. $]; | ||
| 364 | --npos; | ||
| 365 | } | ||
| 366 | hpos += max(skip[npos], cast(sizediff_t) npos - occurrence(haystack[npos+hpos])); | ||
| 367 | } | ||
| 368 | return haystack[$ .. $]; | ||
| 369 | } | ||
| 370 | |||
| 371 | /// | ||
| 372 | @property size_t length() | ||
| 373 | { | ||
| 374 | return needle.length; | ||
| 375 | } | ||
| 376 | |||
| 377 | /// | ||
| 378 | alias opDollar = length; | ||
| 379 | } | ||
| 380 | |||
| 381 | /// Ditto | ||
| 382 | BoyerMooreFinder!(binaryFun!(pred), Range) boyerMooreFinder | ||
| 383 | (alias pred = "a == b", Range) | ||
| 384 | (Range needle) | ||
| 385 | if ((isRandomAccessRange!(Range) && hasSlicing!Range) || isSomeString!Range) | ||
| 386 | { | ||
| 387 | return typeof(return)(needle); | ||
| 388 | } | ||
| 389 | |||
| 390 | /// | ||
| 391 | @safe pure nothrow unittest | ||
| 392 | { | ||
| 393 | auto bmFinder = boyerMooreFinder("TG"); | ||
| 394 | |||
| 395 | string r = "TAGTGCCTGA"; | ||
| 396 | // search for the first match in the haystack r | ||
| 397 | r = bmFinder.beFound(r); | ||
| 398 | assert(r == "TGCCTGA"); | ||
| 399 | |||
| 400 | // continue search in haystack | ||
| 401 | r = bmFinder.beFound(r[2 .. $]); | ||
| 402 | assert(r == "TGA"); | ||
| 403 | } | ||
| 404 | |||
| 405 | /** | ||
| 406 | Returns the common prefix of two ranges. | ||
| 407 | |||
| 408 | Params: | ||
| 409 | pred = The predicate to use in comparing elements for commonality. Defaults | ||
| 410 | to equality $(D "a == b"). | ||
| 411 | |||
| 412 | r1 = A $(REF_ALTTEXT forward range, isForwardRange, std,range,primitives) of | ||
| 413 | elements. | ||
| 414 | |||
| 415 | r2 = An $(REF_ALTTEXT input range, isInputRange, std,range,primitives) of | ||
| 416 | elements. | ||
| 417 | |||
| 418 | Returns: | ||
| 419 | A slice of $(D r1) which contains the characters that both ranges start with, | ||
| 420 | if the first argument is a string; otherwise, the same as the result of | ||
| 421 | $(D takeExactly(r1, n)), where $(D n) is the number of elements in the common | ||
| 422 | prefix of both ranges. | ||
| 423 | |||
| 424 | See_Also: | ||
| 425 | $(REF takeExactly, std,range) | ||
| 426 | */ | ||
| 427 | auto commonPrefix(alias pred = "a == b", R1, R2)(R1 r1, R2 r2) | ||
| 428 | if (isForwardRange!R1 && isInputRange!R2 && | ||
| 429 | !isNarrowString!R1 && | ||
| 430 | is(typeof(binaryFun!pred(r1.front, r2.front)))) | ||
| 431 | { | ||
| 432 | import std.algorithm.comparison : min; | ||
| 433 | static if (isRandomAccessRange!R1 && isRandomAccessRange!R2 && | ||
| 434 | hasLength!R1 && hasLength!R2 && | ||
| 435 | hasSlicing!R1) | ||
| 436 | { | ||
| 437 | immutable limit = min(r1.length, r2.length); | ||
| 438 | foreach (i; 0 .. limit) | ||
| 439 | { | ||
| 440 | if (!binaryFun!pred(r1[i], r2[i])) | ||
| 441 | { | ||
| 442 | return r1[0 .. i]; | ||
| 443 | } | ||
| 444 | } | ||
| 445 | return r1[0 .. limit]; | ||
| 446 | } | ||
| 447 | else | ||
| 448 | { | ||
| 449 | import std.range : takeExactly; | ||
| 450 | auto result = r1.save; | ||
| 451 | size_t i = 0; | ||
| 452 | for (; | ||
| 453 | !r1.empty && !r2.empty && binaryFun!pred(r1.front, r2.front); | ||
| 454 | ++i, r1.popFront(), r2.popFront()) | ||
| 455 | {} | ||
| 456 | return takeExactly(result, i); | ||
| 457 | } | ||
| 458 | } | ||
| 459 | |||
| 460 | /// | ||
| 461 | @safe unittest | ||
| 462 | { | ||
| 463 | assert(commonPrefix("hello, world", "hello, there") == "hello, "); | ||
| 464 | } | ||
| 465 | |||
| 466 | /// ditto | ||
| 467 | auto commonPrefix(alias pred, R1, R2)(R1 r1, R2 r2) | ||
| 468 | if (isNarrowString!R1 && isInputRange!R2 && | ||
| 469 | is(typeof(binaryFun!pred(r1.front, r2.front)))) | ||
| 470 | { | ||
| 471 | import std.utf : decode; | ||
| 472 | |||
| 473 | auto result = r1.save; | ||
| 474 | immutable len = r1.length; | ||
| 475 | size_t i = 0; | ||
| 476 | |||
| 477 | for (size_t j = 0; i < len && !r2.empty; r2.popFront(), i = j) | ||
| 478 | { | ||
| 479 | immutable f = decode(r1, j); | ||
| 480 | if (!binaryFun!pred(f, r2.front)) | ||
| 481 | break; | ||
| 482 | } | ||
| 483 | |||
| 484 | return result[0 .. i]; | ||
| 485 | } | ||
| 486 | |||
| 487 | /// ditto | ||
| 488 | auto commonPrefix(R1, R2)(R1 r1, R2 r2) | ||
| 489 | if (isNarrowString!R1 && isInputRange!R2 && !isNarrowString!R2 && | ||
| 490 | is(typeof(r1.front == r2.front))) | ||
| 491 | { | ||
| 492 | return commonPrefix!"a == b"(r1, r2); | ||
| 493 | } | ||
| 494 | |||
| 495 | /// ditto | ||
| 496 | auto commonPrefix(R1, R2)(R1 r1, R2 r2) | ||
| 497 | if (isNarrowString!R1 && isNarrowString!R2) | ||
| 498 | { | ||
| 499 | import std.algorithm.comparison : min; | ||
| 500 | |||
| 501 | static if (ElementEncodingType!R1.sizeof == ElementEncodingType!R2.sizeof) | ||
| 502 | { | ||
| 503 | import std.utf : stride, UTFException; | ||
| 504 | |||
| 505 | immutable limit = min(r1.length, r2.length); | ||
| 506 | for (size_t i = 0; i < limit;) | ||
| 507 | { | ||
| 508 | immutable codeLen = stride(r1, i); | ||
| 509 | size_t j = 0; | ||
| 510 | |||
| 511 | for (; j < codeLen && i < limit; ++i, ++j) | ||
| 512 | { | ||
| 513 | if (r1[i] != r2[i]) | ||
| 514 | return r1[0 .. i - j]; | ||
| 515 | } | ||
| 516 | |||
| 517 | if (i == limit && j < codeLen) | ||
| 518 | throw new UTFException("Invalid UTF-8 sequence", i); | ||
| 519 | } | ||
| 520 | return r1[0 .. limit]; | ||
| 521 | } | ||
| 522 | else | ||
| 523 | return commonPrefix!"a == b"(r1, r2); | ||
| 524 | } | ||
| 525 | |||
| 526 | @safe unittest | ||
| 527 | { | ||
| 528 | import std.algorithm.comparison : equal; | ||
| 529 | import std.algorithm.iteration : filter; | ||
| 530 | import std.conv : to; | ||
| 531 | import std.exception : assertThrown; | ||
| 532 | import std.meta : AliasSeq; | ||
| 533 | import std.range; | ||
| 534 | import std.utf : UTFException; | ||
| 535 | |||
| 536 | assert(commonPrefix([1, 2, 3], [1, 2, 3, 4, 5]) == [1, 2, 3]); | ||
| 537 | assert(commonPrefix([1, 2, 3, 4, 5], [1, 2, 3]) == [1, 2, 3]); | ||
| 538 | assert(commonPrefix([1, 2, 3, 4], [1, 2, 3, 4]) == [1, 2, 3, 4]); | ||
| 539 | assert(commonPrefix([1, 2, 3], [7, 2, 3, 4, 5]).empty); | ||
| 540 | assert(commonPrefix([7, 2, 3, 4, 5], [1, 2, 3]).empty); | ||
| 541 | assert(commonPrefix([1, 2, 3], cast(int[]) null).empty); | ||
| 542 | assert(commonPrefix(cast(int[]) null, [1, 2, 3]).empty); | ||
| 543 | assert(commonPrefix(cast(int[]) null, cast(int[]) null).empty); | ||
| 544 | |||
| 545 | foreach (S; AliasSeq!(char[], const(char)[], string, | ||
| 546 | wchar[], const(wchar)[], wstring, | ||
| 547 | dchar[], const(dchar)[], dstring)) | ||
| 548 | { | ||
| 549 | foreach (T; AliasSeq!(string, wstring, dstring)) | ||
| 550 | (){ // avoid slow optimizations for large functions @@@BUG@@@ 2396 | ||
| 551 | assert(commonPrefix(to!S(""), to!T("")).empty); | ||
| 552 | assert(commonPrefix(to!S(""), to!T("hello")).empty); | ||
| 553 | assert(commonPrefix(to!S("hello"), to!T("")).empty); | ||
| 554 | assert(commonPrefix(to!S("hello, world"), to!T("hello, there")) == to!S("hello, ")); | ||
| 555 | assert(commonPrefix(to!S("hello, there"), to!T("hello, world")) == to!S("hello, ")); | ||
| 556 | assert(commonPrefix(to!S("hello, "), to!T("hello, world")) == to!S("hello, ")); | ||
| 557 | assert(commonPrefix(to!S("hello, world"), to!T("hello, ")) == to!S("hello, ")); | ||
| 558 | assert(commonPrefix(to!S("hello, world"), to!T("hello, world")) == to!S("hello, world")); | ||
| 559 | |||
| 560 | //Bug# 8890 | ||
| 561 | assert(commonPrefix(to!S("Пиво"), to!T("Пони"))== to!S("П")); | ||
| 562 | assert(commonPrefix(to!S("Пони"), to!T("Пиво"))== to!S("П")); | ||
| 563 | assert(commonPrefix(to!S("Пиво"), to!T("Пиво"))== to!S("Пиво")); | ||
| 564 | assert(commonPrefix(to!S("\U0010FFFF\U0010FFFB\U0010FFFE"), | ||
| 565 | to!T("\U0010FFFF\U0010FFFB\U0010FFFC")) == to!S("\U0010FFFF\U0010FFFB")); | ||
| 566 | assert(commonPrefix(to!S("\U0010FFFF\U0010FFFB\U0010FFFC"), | ||
| 567 | to!T("\U0010FFFF\U0010FFFB\U0010FFFE")) == to!S("\U0010FFFF\U0010FFFB")); | ||
| 568 | assert(commonPrefix!"a != b"(to!S("Пиво"), to!T("онво")) == to!S("Пи")); | ||
| 569 | assert(commonPrefix!"a != b"(to!S("онво"), to!T("Пиво")) == to!S("он")); | ||
| 570 | }(); | ||
| 571 | |||
| 572 | static assert(is(typeof(commonPrefix(to!S("Пиво"), filter!"true"("Пони"))) == S)); | ||
| 573 | assert(equal(commonPrefix(to!S("Пиво"), filter!"true"("Пони")), to!S("П"))); | ||
| 574 | |||
| 575 | static assert(is(typeof(commonPrefix(filter!"true"("Пиво"), to!S("Пони"))) == | ||
| 576 | typeof(takeExactly(filter!"true"("П"), 1)))); | ||
| 577 | assert(equal(commonPrefix(filter!"true"("Пиво"), to!S("Пони")), takeExactly(filter!"true"("П"), 1))); | ||
| 578 | } | ||
| 579 | |||
| 580 | assertThrown!UTFException(commonPrefix("\U0010FFFF\U0010FFFB", "\U0010FFFF\U0010FFFB"[0 .. $ - 1])); | ||
| 581 | |||
| 582 | assert(commonPrefix("12345"d, [49, 50, 51, 60, 60]) == "123"d); | ||
| 583 | assert(commonPrefix([49, 50, 51, 60, 60], "12345" ) == [49, 50, 51]); | ||
| 584 | assert(commonPrefix([49, 50, 51, 60, 60], "12345"d) == [49, 50, 51]); | ||
| 585 | |||
| 586 | assert(commonPrefix!"a == ('0' + b)"("12345" , [1, 2, 3, 9, 9]) == "123"); | ||
| 587 | assert(commonPrefix!"a == ('0' + b)"("12345"d, [1, 2, 3, 9, 9]) == "123"d); | ||
| 588 | assert(commonPrefix!"('0' + a) == b"([1, 2, 3, 9, 9], "12345" ) == [1, 2, 3]); | ||
| 589 | assert(commonPrefix!"('0' + a) == b"([1, 2, 3, 9, 9], "12345"d) == [1, 2, 3]); | ||
| 590 | } | ||
| 591 | |||
| 592 | // count | ||
| 593 | /** | ||
| 594 | The first version counts the number of elements $(D x) in $(D r) for | ||
| 595 | which $(D pred(x, value)) is $(D true). $(D pred) defaults to | ||
| 596 | equality. Performs $(BIGOH haystack.length) evaluations of $(D pred). | ||
| 597 | |||
| 598 | The second version returns the number of times $(D needle) occurs in | ||
| 599 | $(D haystack). Throws an exception if $(D needle.empty), as the _count | ||
| 600 | of the empty range in any range would be infinite. Overlapped counts | ||
| 601 | are not considered, for example $(D count("aaa", "aa")) is $(D 1), not | ||
| 602 | $(D 2). | ||
| 603 | |||
| 604 | The third version counts the elements for which $(D pred(x)) is $(D | ||
| 605 | true). Performs $(BIGOH haystack.length) evaluations of $(D pred). | ||
| 606 | |||
| 607 | The fourth version counts the number of elements in a range. It is | ||
| 608 | an optimization for the third version: if the given range has the | ||
| 609 | `length` property the count is returned right away, otherwise | ||
| 610 | performs $(BIGOH haystack.length) to walk the range. | ||
| 611 | |||
| 612 | Note: Regardless of the overload, $(D count) will not accept | ||
| 613 | infinite ranges for $(D haystack). | ||
| 614 | |||
| 615 | Params: | ||
| 616 | pred = The predicate to evaluate. | ||
| 617 | haystack = The range to _count. | ||
| 618 | needle = The element or sub-range to _count in the `haystack`. | ||
| 619 | |||
| 620 | Returns: | ||
| 621 | The number of positions in the `haystack` for which `pred` returned true. | ||
| 622 | */ | ||
| 623 | size_t count(alias pred = "a == b", Range, E)(Range haystack, E needle) | ||
| 624 | if (isInputRange!Range && !isInfinite!Range && | ||
| 625 | is(typeof(binaryFun!pred(haystack.front, needle)) : bool)) | ||
| 626 | { | ||
| 627 | bool pred2(ElementType!Range a) { return binaryFun!pred(a, needle); } | ||
| 628 | return count!pred2(haystack); | ||
| 629 | } | ||
| 630 | |||
| 631 | /// | ||
| 632 | @safe unittest | ||
| 633 | { | ||
| 634 | import std.uni : toLower; | ||
| 635 | |||
| 636 | // count elements in range | ||
| 637 | int[] a = [ 1, 2, 4, 3, 2, 5, 3, 2, 4 ]; | ||
| 638 | assert(count(a) == 9); | ||
| 639 | assert(count(a, 2) == 3); | ||
| 640 | assert(count!("a > b")(a, 2) == 5); | ||
| 641 | // count range in range | ||
| 642 | assert(count("abcadfabf", "ab") == 2); | ||
| 643 | assert(count("ababab", "abab") == 1); | ||
| 644 | assert(count("ababab", "abx") == 0); | ||
| 645 | // fuzzy count range in range | ||
| 646 | assert(count!((a, b) => toLower(a) == toLower(b))("AbcAdFaBf", "ab") == 2); | ||
| 647 | // count predicate in range | ||
| 648 | assert(count!("a > 1")(a) == 8); | ||
| 649 | } | ||
| 650 | |||
| 651 | @safe unittest | ||
| 652 | { | ||
| 653 | import std.conv : text; | ||
| 654 | |||
| 655 | int[] a = [ 1, 2, 4, 3, 2, 5, 3, 2, 4 ]; | ||
| 656 | assert(count(a, 2) == 3, text(count(a, 2))); | ||
| 657 | assert(count!("a > b")(a, 2) == 5, text(count!("a > b")(a, 2))); | ||
| 658 | |||
| 659 | // check strings | ||
| 660 | assert(count("日本語") == 3); | ||
| 661 | assert(count("日本語"w) == 3); | ||
| 662 | assert(count("日本語"d) == 3); | ||
| 663 | |||
| 664 | assert(count!("a == '日'")("日本語") == 1); | ||
| 665 | assert(count!("a == '本'")("日本語"w) == 1); | ||
| 666 | assert(count!("a == '語'")("日本語"d) == 1); | ||
| 667 | } | ||
| 668 | |||
| 669 | @safe unittest | ||
| 670 | { | ||
| 671 | string s = "This is a fofofof list"; | ||
| 672 | string sub = "fof"; | ||
| 673 | assert(count(s, sub) == 2); | ||
| 674 | } | ||
| 675 | |||
| 676 | /// Ditto | ||
| 677 | size_t count(alias pred = "a == b", R1, R2)(R1 haystack, R2 needle) | ||
| 678 | if (isForwardRange!R1 && !isInfinite!R1 && | ||
| 679 | isForwardRange!R2 && | ||
| 680 | is(typeof(binaryFun!pred(haystack.front, needle.front)) : bool)) | ||
| 681 | { | ||
| 682 | assert(!needle.empty, "Cannot count occurrences of an empty range"); | ||
| 683 | |||
| 684 | static if (isInfinite!R2) | ||
| 685 | { | ||
| 686 | //Note: This is the special case of looking for an infinite inside a finite... | ||
| 687 | //"How many instances of the Fibonacci sequence can you count in [1, 2, 3]?" - "None." | ||
| 688 | return 0; | ||
| 689 | } | ||
| 690 | else | ||
| 691 | { | ||
| 692 | size_t result; | ||
| 693 | //Note: haystack is not saved, because findskip is designed to modify it | ||
| 694 | for ( ; findSkip!pred(haystack, needle.save) ; ++result) | ||
| 695 | {} | ||
| 696 | return result; | ||
| 697 | } | ||
| 698 | } | ||
| 699 | |||
| 700 | /// Ditto | ||
| 701 | size_t count(alias pred, R)(R haystack) | ||
| 702 | if (isInputRange!R && !isInfinite!R && | ||
| 703 | is(typeof(unaryFun!pred(haystack.front)) : bool)) | ||
| 704 | { | ||
| 705 | size_t result; | ||
| 706 | alias T = ElementType!R; //For narrow strings forces dchar iteration | ||
| 707 | foreach (T elem; haystack) | ||
| 708 | if (unaryFun!pred(elem)) ++result; | ||
| 709 | return result; | ||
| 710 | } | ||
| 711 | |||
| 712 | /// Ditto | ||
| 713 | size_t count(R)(R haystack) | ||
| 714 | if (isInputRange!R && !isInfinite!R) | ||
| 715 | { | ||
| 716 | return walkLength(haystack); | ||
| 717 | } | ||
| 718 | |||
| 719 | @safe unittest | ||
| 720 | { | ||
| 721 | int[] a = [ 1, 2, 4, 3, 2, 5, 3, 2, 4 ]; | ||
| 722 | assert(count!("a == 3")(a) == 2); | ||
| 723 | assert(count("日本語") == 3); | ||
| 724 | } | ||
| 725 | |||
| 726 | // Issue 11253 | ||
| 727 | @safe nothrow unittest | ||
| 728 | { | ||
| 729 | assert([1, 2, 3].count([2, 3]) == 1); | ||
| 730 | } | ||
| 731 | |||
| 732 | /++ | ||
| 733 | Counts elements in the given | ||
| 734 | $(REF_ALTTEXT forward range, isForwardRange, std,range,primitives) | ||
| 735 | until the given predicate is true for one of the given $(D needles). | ||
| 736 | |||
| 737 | Params: | ||
| 738 | pred = The predicate for determining when to stop counting. | ||
| 739 | haystack = The | ||
| 740 | $(REF_ALTTEXT input range, isInputRange, std,range,primitives) to be | ||
| 741 | counted. | ||
| 742 | needles = Either a single element, or a | ||
| 743 | $(REF_ALTTEXT forward range, isForwardRange, std,range,primitives) | ||
| 744 | of elements, to be evaluated in turn against each | ||
| 745 | element in $(D haystack) under the given predicate. | ||
| 746 | |||
| 747 | Returns: The number of elements which must be popped from the front of | ||
| 748 | $(D haystack) before reaching an element for which | ||
| 749 | $(D startsWith!pred(haystack, needles)) is $(D true). If | ||
| 750 | $(D startsWith!pred(haystack, needles)) is not $(D true) for any element in | ||
| 751 | $(D haystack), then $(D -1) is returned. | ||
| 752 | |||
| 753 | See_Also: $(REF indexOf, std,string) | ||
| 754 | +/ | ||
| 755 | ptrdiff_t countUntil(alias pred = "a == b", R, Rs...)(R haystack, Rs needles) | ||
| 756 | if (isForwardRange!R | ||
| 757 | && Rs.length > 0 | ||
| 758 | && isForwardRange!(Rs[0]) == isInputRange!(Rs[0]) | ||
| 759 | && is(typeof(startsWith!pred(haystack, needles[0]))) | ||
| 760 | && (Rs.length == 1 | ||
| 761 | || is(typeof(countUntil!pred(haystack, needles[1 .. $]))))) | ||
| 762 | { | ||
| 763 | typeof(return) result; | ||
| 764 | |||
| 765 | static if (needles.length == 1) | ||
| 766 | { | ||
| 767 | static if (hasLength!R) //Note: Narrow strings don't have length. | ||
| 768 | { | ||
| 769 | //We delegate to find because find is very efficient. | ||
| 770 | //We store the length of the haystack so we don't have to save it. | ||
| 771 | auto len = haystack.length; | ||
| 772 | auto r2 = find!pred(haystack, needles[0]); | ||
| 773 | if (!r2.empty) | ||
| 774 | return cast(typeof(return)) (len - r2.length); | ||
| 775 | } | ||
| 776 | else | ||
| 777 | { | ||
| 778 | import std.range : dropOne; | ||
| 779 | |||
| 780 | if (needles[0].empty) | ||
| 781 | return 0; | ||
| 782 | |||
| 783 | //Default case, slower route doing startsWith iteration | ||
| 784 | for ( ; !haystack.empty ; ++result ) | ||
| 785 | { | ||
| 786 | //We compare the first elements of the ranges here before | ||
| 787 | //forwarding to startsWith. This avoids making useless saves to | ||
| 788 | //haystack/needle if they aren't even going to be mutated anyways. | ||
| 789 | //It also cuts down on the amount of pops on haystack. | ||
| 790 | if (binaryFun!pred(haystack.front, needles[0].front)) | ||
| 791 | { | ||
| 792 | //Here, we need to save the needle before popping it. | ||
| 793 | //haystack we pop in all paths, so we do that, and then save. | ||
| 794 | haystack.popFront(); | ||
| 795 | if (startsWith!pred(haystack.save, needles[0].save.dropOne())) | ||
| 796 | return result; | ||
| 797 | } | ||
| 798 | else | ||
| 799 | haystack.popFront(); | ||
| 800 | } | ||
| 801 | } | ||
| 802 | } | ||
| 803 | else | ||
| 804 | { | ||
| 805 | foreach (i, Ri; Rs) | ||
| 806 | { | ||
| 807 | static if (isForwardRange!Ri) | ||
| 808 | { | ||
| 809 | if (needles[i].empty) | ||
| 810 | return 0; | ||
| 811 | } | ||
| 812 | } | ||
| 813 | Tuple!Rs t; | ||
| 814 | foreach (i, Ri; Rs) | ||
| 815 | { | ||
| 816 | static if (!isForwardRange!Ri) | ||
| 817 | { | ||
| 818 | t[i] = needles[i]; | ||
| 819 | } | ||
| 820 | } | ||
| 821 | for (; !haystack.empty ; ++result, haystack.popFront()) | ||
| 822 | { | ||
| 823 | foreach (i, Ri; Rs) | ||
| 824 | { | ||
| 825 | static if (isForwardRange!Ri) | ||
| 826 | { | ||
| 827 | t[i] = needles[i].save; | ||
| 828 | } | ||
| 829 | } | ||
| 830 | if (startsWith!pred(haystack.save, t.expand)) | ||
| 831 | { | ||
| 832 | return result; | ||
| 833 | } | ||
| 834 | } | ||
| 835 | } | ||
| 836 | |||
| 837 | //Because of @@@8804@@@: Avoids both "unreachable code" or "no return statement" | ||
| 838 | static if (isInfinite!R) assert(0); | ||
| 839 | else return -1; | ||
| 840 | } | ||
| 841 | |||
| 842 | /// ditto | ||
| 843 | ptrdiff_t countUntil(alias pred = "a == b", R, N)(R haystack, N needle) | ||
| 844 | if (isInputRange!R && | ||
| 845 | is(typeof(binaryFun!pred(haystack.front, needle)) : bool)) | ||
| 846 | { | ||
| 847 | bool pred2(ElementType!R a) { return binaryFun!pred(a, needle); } | ||
| 848 | return countUntil!pred2(haystack); | ||
| 849 | } | ||
| 850 | |||
| 851 | /// | ||
| 852 | @safe unittest | ||
| 853 | { | ||
| 854 | assert(countUntil("hello world", "world") == 6); | ||
| 855 | assert(countUntil("hello world", 'r') == 8); | ||
| 856 | assert(countUntil("hello world", "programming") == -1); | ||
| 857 | assert(countUntil("日本語", "本語") == 1); | ||
| 858 | assert(countUntil("日本語", '語') == 2); | ||
| 859 | assert(countUntil("日本語", "五") == -1); | ||
| 860 | assert(countUntil("日本語", '五') == -1); | ||
| 861 | assert(countUntil([0, 7, 12, 22, 9], [12, 22]) == 2); | ||
| 862 | assert(countUntil([0, 7, 12, 22, 9], 9) == 4); | ||
| 863 | assert(countUntil!"a > b"([0, 7, 12, 22, 9], 20) == 3); | ||
| 864 | } | ||
| 865 | |||
| 866 | @safe unittest | ||
| 867 | { | ||
| 868 | import std.algorithm.iteration : filter; | ||
| 869 | import std.internal.test.dummyrange; | ||
| 870 | |||
| 871 | assert(countUntil("日本語", "") == 0); | ||
| 872 | assert(countUntil("日本語"d, "") == 0); | ||
| 873 | |||
| 874 | assert(countUntil("", "") == 0); | ||
| 875 | assert(countUntil("".filter!"true"(), "") == 0); | ||
| 876 | |||
| 877 | auto rf = [0, 20, 12, 22, 9].filter!"true"(); | ||
| 878 | assert(rf.countUntil!"a > b"((int[]).init) == 0); | ||
| 879 | assert(rf.countUntil!"a > b"(20) == 3); | ||
| 880 | assert(rf.countUntil!"a > b"([20, 8]) == 3); | ||
| 881 | assert(rf.countUntil!"a > b"([20, 10]) == -1); | ||
| 882 | assert(rf.countUntil!"a > b"([20, 8, 0]) == -1); | ||
| 883 | |||
| 884 | auto r = new ReferenceForwardRange!int([0, 1, 2, 3, 4, 5, 6]); | ||
| 885 | auto r2 = new ReferenceForwardRange!int([3, 4]); | ||
| 886 | auto r3 = new ReferenceForwardRange!int([3, 5]); | ||
| 887 | assert(r.save.countUntil(3) == 3); | ||
| 888 | assert(r.save.countUntil(r2) == 3); | ||
| 889 | assert(r.save.countUntil(7) == -1); | ||
| 890 | assert(r.save.countUntil(r3) == -1); | ||
| 891 | } | ||
| 892 | |||
| 893 | @safe unittest | ||
| 894 | { | ||
| 895 | assert(countUntil("hello world", "world", "asd") == 6); | ||
| 896 | assert(countUntil("hello world", "world", "ello") == 1); | ||
| 897 | assert(countUntil("hello world", "world", "") == 0); | ||
| 898 | assert(countUntil("hello world", "world", 'l') == 2); | ||
| 899 | } | ||
| 900 | |||
| 901 | /++ | ||
| 902 | Similar to the previous overload of $(D countUntil), except that this one | ||
| 903 | evaluates only the predicate $(D pred). | ||
| 904 | |||
| 905 | Params: | ||
| 906 | pred = Predicate to when to stop counting. | ||
| 907 | haystack = An | ||
| 908 | $(REF_ALTTEXT input range, isInputRange, std,range,primitives) of | ||
| 909 | elements to be counted. | ||
| 910 | Returns: The number of elements which must be popped from $(D haystack) | ||
| 911 | before $(D pred(haystack.front)) is $(D true). | ||
| 912 | +/ | ||
| 913 | ptrdiff_t countUntil(alias pred, R)(R haystack) | ||
| 914 | if (isInputRange!R && | ||
| 915 | is(typeof(unaryFun!pred(haystack.front)) : bool)) | ||
| 916 | { | ||
| 917 | typeof(return) i; | ||
| 918 | static if (isRandomAccessRange!R) | ||
| 919 | { | ||
| 920 | //Optimized RA implementation. Since we want to count *and* iterate at | ||
| 921 | //the same time, it is more efficient this way. | ||
| 922 | static if (hasLength!R) | ||
| 923 | { | ||
| 924 | immutable len = cast(typeof(return)) haystack.length; | ||
| 925 | for ( ; i < len ; ++i ) | ||
| 926 | if (unaryFun!pred(haystack[i])) return i; | ||
| 927 | } | ||
| 928 | else //if (isInfinite!R) | ||
| 929 | { | ||
| 930 | for ( ; ; ++i ) | ||
| 931 | if (unaryFun!pred(haystack[i])) return i; | ||
| 932 | } | ||
| 933 | } | ||
| 934 | else static if (hasLength!R) | ||
| 935 | { | ||
| 936 | //For those odd ranges that have a length, but aren't RA. | ||
| 937 | //It is faster to quick find, and then compare the lengths | ||
| 938 | auto r2 = find!pred(haystack.save); | ||
| 939 | if (!r2.empty) return cast(typeof(return)) (haystack.length - r2.length); | ||
| 940 | } | ||
| 941 | else //Everything else | ||
| 942 | { | ||
| 943 | alias T = ElementType!R; //For narrow strings forces dchar iteration | ||
| 944 | foreach (T elem; haystack) | ||
| 945 | { | ||
| 946 | if (unaryFun!pred(elem)) return i; | ||
| 947 | ++i; | ||
| 948 | } | ||
| 949 | } | ||
| 950 | |||
| 951 | //Because of @@@8804@@@: Avoids both "unreachable code" or "no return statement" | ||
| 952 | static if (isInfinite!R) assert(0); | ||
| 953 | else return -1; | ||
| 954 | } | ||
| 955 | |||
| 956 | /// | ||
| 957 | @safe unittest | ||
| 958 | { | ||
| 959 | import std.ascii : isDigit; | ||
| 960 | import std.uni : isWhite; | ||
| 961 | |||
| 962 | assert(countUntil!(std.uni.isWhite)("hello world") == 5); | ||
| 963 | assert(countUntil!(std.ascii.isDigit)("hello world") == -1); | ||
| 964 | assert(countUntil!"a > 20"([0, 7, 12, 22, 9]) == 3); | ||
| 965 | } | ||
| 966 | |||
| 967 | @safe unittest | ||
| 968 | { | ||
| 969 | import std.internal.test.dummyrange; | ||
| 970 | |||
| 971 | // References | ||
| 972 | { | ||
| 973 | // input | ||
| 974 | ReferenceInputRange!int r; | ||
| 975 | r = new ReferenceInputRange!int([0, 1, 2, 3, 4, 5, 6]); | ||
| 976 | assert(r.countUntil(3) == 3); | ||
| 977 | r = new ReferenceInputRange!int([0, 1, 2, 3, 4, 5, 6]); | ||
| 978 | assert(r.countUntil(7) == -1); | ||
| 979 | } | ||
| 980 | { | ||
| 981 | // forward | ||
| 982 | auto r = new ReferenceForwardRange!int([0, 1, 2, 3, 4, 5, 6]); | ||
| 983 | assert(r.save.countUntil([3, 4]) == 3); | ||
| 984 | assert(r.save.countUntil(3) == 3); | ||
| 985 | assert(r.save.countUntil([3, 7]) == -1); | ||
| 986 | assert(r.save.countUntil(7) == -1); | ||
| 987 | } | ||
| 988 | { | ||
| 989 | // infinite forward | ||
| 990 | auto r = new ReferenceInfiniteForwardRange!int(0); | ||
| 991 | assert(r.save.countUntil([3, 4]) == 3); | ||
| 992 | assert(r.save.countUntil(3) == 3); | ||
| 993 | } | ||
| 994 | } | ||
| 995 | |||
| 996 | /** | ||
| 997 | Checks if the given range ends with (one of) the given needle(s). | ||
| 998 | The reciprocal of $(D startsWith). | ||
| 999 | |||
| 1000 | Params: | ||
| 1001 | pred = The predicate to use for comparing elements between the range and | ||
| 1002 | the needle(s). | ||
| 1003 | |||
| 1004 | doesThisEnd = The | ||
| 1005 | $(REF_ALTTEXT bidirectional range, isBidirectionalRange, std,range,primitives) | ||
| 1006 | to check. | ||
| 1007 | |||
| 1008 | withOneOfThese = The needles to check against, which may be single | ||
| 1009 | elements, or bidirectional ranges of elements. | ||
| 1010 | |||
| 1011 | withThis = The single element to check. | ||
| 1012 | |||
| 1013 | Returns: | ||
| 1014 | 0 if the needle(s) do not occur at the end of the given range; | ||
| 1015 | otherwise the position of the matching needle, that is, 1 if the range ends | ||
| 1016 | with $(D withOneOfThese[0]), 2 if it ends with $(D withOneOfThese[1]), and so | ||
| 1017 | on. | ||
| 1018 | |||
| 1019 | In the case when no needle parameters are given, return $(D true) iff back of | ||
| 1020 | $(D doesThisStart) fulfils predicate $(D pred). | ||
| 1021 | */ | ||
| 1022 | uint endsWith(alias pred = "a == b", Range, Needles...)(Range doesThisEnd, Needles withOneOfThese) | ||
| 1023 | if (isBidirectionalRange!Range && Needles.length > 1 && | ||
| 1024 | is(typeof(.endsWith!pred(doesThisEnd, withOneOfThese[0])) : bool) && | ||
| 1025 | is(typeof(.endsWith!pred(doesThisEnd, withOneOfThese[1 .. $])) : uint)) | ||
| 1026 | { | ||
| 1027 | alias haystack = doesThisEnd; | ||
| 1028 | alias needles = withOneOfThese; | ||
| 1029 | |||
| 1030 | // Make one pass looking for empty ranges in needles | ||
| 1031 | foreach (i, Unused; Needles) | ||
| 1032 | { | ||
| 1033 | // Empty range matches everything | ||
| 1034 | static if (!is(typeof(binaryFun!pred(haystack.back, needles[i])) : bool)) | ||
| 1035 | { | ||
| 1036 | if (needles[i].empty) return i + 1; | ||
| 1037 | } | ||
| 1038 | } | ||
| 1039 | |||
| 1040 | for (; !haystack.empty; haystack.popBack()) | ||
| 1041 | { | ||
| 1042 | foreach (i, Unused; Needles) | ||
| 1043 | { | ||
| 1044 | static if (is(typeof(binaryFun!pred(haystack.back, needles[i])) : bool)) | ||
| 1045 | { | ||
| 1046 | // Single-element | ||
| 1047 | if (binaryFun!pred(haystack.back, needles[i])) | ||
| 1048 | { | ||
| 1049 | // found, but continue to account for one-element | ||
| 1050 | // range matches (consider endsWith("ab", "b", | ||
| 1051 | // 'b') should return 1, not 2). | ||
| 1052 | continue; | ||
| 1053 | } | ||
| 1054 | } | ||
| 1055 | else | ||
| 1056 | { | ||
| 1057 | if (binaryFun!pred(haystack.back, needles[i].back)) | ||
| 1058 | continue; | ||
| 1059 | } | ||
| 1060 | |||
| 1061 | // This code executed on failure to match | ||
| 1062 | // Out with this guy, check for the others | ||
| 1063 | uint result = endsWith!pred(haystack, needles[0 .. i], needles[i + 1 .. $]); | ||
| 1064 | if (result > i) ++result; | ||
| 1065 | return result; | ||
| 1066 | } | ||
| 1067 | |||
| 1068 | // If execution reaches this point, then the back matches for all | ||
| 1069 | // needles ranges. What we need to do now is to lop off the back of | ||
| 1070 | // all ranges involved and recurse. | ||
| 1071 | foreach (i, Unused; Needles) | ||
| 1072 | { | ||
| 1073 | static if (is(typeof(binaryFun!pred(haystack.back, needles[i])) : bool)) | ||
| 1074 | { | ||
| 1075 | // Test has passed in the previous loop | ||
| 1076 | return i + 1; | ||
| 1077 | } | ||
| 1078 | else | ||
| 1079 | { | ||
| 1080 | needles[i].popBack(); | ||
| 1081 | if (needles[i].empty) return i + 1; | ||
| 1082 | } | ||
| 1083 | } | ||
| 1084 | } | ||
| 1085 | return 0; | ||
| 1086 | } | ||
| 1087 | |||
| 1088 | /// Ditto | ||
| 1089 | bool endsWith(alias pred = "a == b", R1, R2)(R1 doesThisEnd, R2 withThis) | ||
| 1090 | if (isBidirectionalRange!R1 && | ||
| 1091 | isBidirectionalRange!R2 && | ||
| 1092 | is(typeof(binaryFun!pred(doesThisEnd.back, withThis.back)) : bool)) | ||
| 1093 | { | ||
| 1094 | alias haystack = doesThisEnd; | ||
| 1095 | alias needle = withThis; | ||
| 1096 | |||
| 1097 | static if (is(typeof(pred) : string)) | ||
| 1098 | enum isDefaultPred = pred == "a == b"; | ||
| 1099 | else | ||
| 1100 | enum isDefaultPred = false; | ||
| 1101 | |||
| 1102 | static if (isDefaultPred && isArray!R1 && isArray!R2 && | ||
| 1103 | is(Unqual!(ElementEncodingType!R1) == Unqual!(ElementEncodingType!R2))) | ||
| 1104 | { | ||
| 1105 | if (haystack.length < needle.length) return false; | ||
| 1106 | |||
| 1107 | return haystack[$ - needle.length .. $] == needle; | ||
| 1108 | } | ||
| 1109 | else | ||
| 1110 | { | ||
| 1111 | import std.range : retro; | ||
| 1112 | return startsWith!pred(retro(doesThisEnd), retro(withThis)); | ||
| 1113 | } | ||
| 1114 | } | ||
| 1115 | |||
| 1116 | /// Ditto | ||
| 1117 | bool endsWith(alias pred = "a == b", R, E)(R doesThisEnd, E withThis) | ||
| 1118 | if (isBidirectionalRange!R && | ||
| 1119 | is(typeof(binaryFun!pred(doesThisEnd.back, withThis)) : bool)) | ||
| 1120 | { | ||
| 1121 | if (doesThisEnd.empty) | ||
| 1122 | return false; | ||
| 1123 | |||
| 1124 | alias predFunc = binaryFun!pred; | ||
| 1125 | |||
| 1126 | // auto-decoding special case | ||
| 1127 | static if (isNarrowString!R) | ||
| 1128 | { | ||
| 1129 | // specialize for ASCII as to not change previous behavior | ||
| 1130 | if (withThis <= 0x7F) | ||
| 1131 | return predFunc(doesThisEnd[$ - 1], withThis); | ||
| 1132 | else | ||
| 1133 | return predFunc(doesThisEnd.back, withThis); | ||
| 1134 | } | ||
| 1135 | else | ||
| 1136 | { | ||
| 1137 | return predFunc(doesThisEnd.back, withThis); | ||
| 1138 | } | ||
| 1139 | } | ||
| 1140 | |||
| 1141 | /// Ditto | ||
| 1142 | bool endsWith(alias pred, R)(R doesThisEnd) | ||
| 1143 | if (isInputRange!R && | ||
| 1144 | ifTestable!(typeof(doesThisEnd.front), unaryFun!pred)) | ||
| 1145 | { | ||
| 1146 | return !doesThisEnd.empty && unaryFun!pred(doesThisEnd.back); | ||
| 1147 | } | ||
| 1148 | |||
| 1149 | /// | ||
| 1150 | @safe unittest | ||
| 1151 | { | ||
| 1152 | import std.ascii : isAlpha; | ||
| 1153 | assert("abc".endsWith!(a => a.isAlpha)); | ||
| 1154 | assert("abc".endsWith!isAlpha); | ||
| 1155 | |||
| 1156 | assert(!"ab1".endsWith!(a => a.isAlpha)); | ||
| 1157 | |||
| 1158 | assert(!"ab1".endsWith!isAlpha); | ||
| 1159 | assert(!"".endsWith!(a => a.isAlpha)); | ||
| 1160 | |||
| 1161 | import std.algorithm.comparison : among; | ||
| 1162 | assert("abc".endsWith!(a => a.among('c', 'd') != 0)); | ||
| 1163 | assert(!"abc".endsWith!(a => a.among('a', 'b') != 0)); | ||
| 1164 | |||
| 1165 | assert(endsWith("abc", "")); | ||
| 1166 | assert(!endsWith("abc", "b")); | ||
| 1167 | assert(endsWith("abc", "a", 'c') == 2); | ||
| 1168 | assert(endsWith("abc", "c", "a") == 1); | ||
| 1169 | assert(endsWith("abc", "c", "c") == 1); | ||
| 1170 | assert(endsWith("abc", "bc", "c") == 2); | ||
| 1171 | assert(endsWith("abc", "x", "c", "b") == 2); | ||
| 1172 | assert(endsWith("abc", "x", "aa", "bc") == 3); | ||
| 1173 | assert(endsWith("abc", "x", "aaa", "sab") == 0); | ||
| 1174 | assert(endsWith("abc", "x", "aaa", 'c', "sab") == 3); | ||
| 1175 | } | ||
| 1176 | |||
| 1177 | @safe unittest | ||
| 1178 | { | ||
| 1179 | import std.algorithm.iteration : filterBidirectional; | ||
| 1180 | import std.conv : to; | ||
| 1181 | import std.meta : AliasSeq; | ||
| 1182 | |||
| 1183 | foreach (S; AliasSeq!(char[], wchar[], dchar[], string, wstring, dstring)) | ||
| 1184 | { | ||
| 1185 | assert(!endsWith(to!S("abc"), 'a')); | ||
| 1186 | assert(endsWith(to!S("abc"), 'a', 'c') == 2); | ||
| 1187 | assert(!endsWith(to!S("abc"), 'x', 'n', 'b')); | ||
| 1188 | assert(endsWith(to!S("abc"), 'x', 'n', 'c') == 3); | ||
| 1189 | assert(endsWith(to!S("abc\uFF28"), 'a', '\uFF28', 'c') == 2); | ||
| 1190 | |||
| 1191 | foreach (T; AliasSeq!(char[], wchar[], dchar[], string, wstring, dstring)) | ||
| 1192 | (){ // avoid slow optimizations for large functions @@@BUG@@@ 2396 | ||
| 1193 | //Lots of strings | ||
| 1194 | assert(endsWith(to!S("abc"), to!T(""))); | ||
| 1195 | assert(!endsWith(to!S("abc"), to!T("a"))); | ||
| 1196 | assert(!endsWith(to!S("abc"), to!T("b"))); | ||
| 1197 | assert(endsWith(to!S("abc"), to!T("bc"), 'c') == 2); | ||
| 1198 | assert(endsWith(to!S("abc"), to!T("a"), "c") == 2); | ||
| 1199 | assert(endsWith(to!S("abc"), to!T("c"), "a") == 1); | ||
| 1200 | assert(endsWith(to!S("abc"), to!T("c"), "c") == 1); | ||
| 1201 | assert(endsWith(to!S("abc"), to!T("x"), 'c', "b") == 2); | ||
| 1202 | assert(endsWith(to!S("abc"), 'x', to!T("aa"), "bc") == 3); | ||
| 1203 | assert(endsWith(to!S("abc"), to!T("x"), "aaa", "sab") == 0); | ||
| 1204 | assert(endsWith(to!S("abc"), to!T("x"), "aaa", "c", "sab") == 3); | ||
| 1205 | assert(endsWith(to!S("\uFF28el\uFF4co"), to!T("l\uFF4co"))); | ||
| 1206 | assert(endsWith(to!S("\uFF28el\uFF4co"), to!T("lo"), to!T("l\uFF4co")) == 2); | ||
| 1207 | |||
| 1208 | //Unicode | ||
| 1209 | assert(endsWith(to!S("\uFF28el\uFF4co"), to!T("l\uFF4co"))); | ||
| 1210 | assert(endsWith(to!S("\uFF28el\uFF4co"), to!T("lo"), to!T("l\uFF4co")) == 2); | ||
| 1211 | assert(endsWith(to!S("日本語"), to!T("本語"))); | ||
| 1212 | assert(endsWith(to!S("日本語"), to!T("日本語"))); | ||
| 1213 | assert(!endsWith(to!S("本語"), to!T("日本語"))); | ||
| 1214 | |||
| 1215 | //Empty | ||
| 1216 | assert(endsWith(to!S(""), T.init)); | ||
| 1217 | assert(!endsWith(to!S(""), 'a')); | ||
| 1218 | assert(endsWith(to!S("a"), T.init)); | ||
| 1219 | assert(endsWith(to!S("a"), T.init, "") == 1); | ||
| 1220 | assert(endsWith(to!S("a"), T.init, 'a') == 1); | ||
| 1221 | assert(endsWith(to!S("a"), 'a', T.init) == 2); | ||
| 1222 | }(); | ||
| 1223 | } | ||
| 1224 | |||
| 1225 | foreach (T; AliasSeq!(int, short)) | ||
| 1226 | { | ||
| 1227 | immutable arr = cast(T[])[0, 1, 2, 3, 4, 5]; | ||
| 1228 | |||
| 1229 | //RA range | ||
| 1230 | assert(endsWith(arr, cast(int[]) null)); | ||
| 1231 | assert(!endsWith(arr, 0)); | ||
| 1232 | assert(!endsWith(arr, 4)); | ||
| 1233 | assert(endsWith(arr, 5)); | ||
| 1234 | assert(endsWith(arr, 0, 4, 5) == 3); | ||
| 1235 | assert(endsWith(arr, [5])); | ||
| 1236 | assert(endsWith(arr, [4, 5])); | ||
| 1237 | assert(endsWith(arr, [4, 5], 7) == 1); | ||
| 1238 | assert(!endsWith(arr, [2, 4, 5])); | ||
| 1239 | assert(endsWith(arr, [2, 4, 5], [3, 4, 5]) == 2); | ||
| 1240 | |||
| 1241 | //Normal input range | ||
| 1242 | assert(!endsWith(filterBidirectional!"true"(arr), 4)); | ||
| 1243 | assert(endsWith(filterBidirectional!"true"(arr), 5)); | ||
| 1244 | assert(endsWith(filterBidirectional!"true"(arr), [5])); | ||
| 1245 | assert(endsWith(filterBidirectional!"true"(arr), [4, 5])); | ||
| 1246 | assert(endsWith(filterBidirectional!"true"(arr), [4, 5], 7) == 1); | ||
| 1247 | assert(!endsWith(filterBidirectional!"true"(arr), [2, 4, 5])); | ||
| 1248 | assert(endsWith(filterBidirectional!"true"(arr), [2, 4, 5], [3, 4, 5]) == 2); | ||
| 1249 | assert(endsWith(arr, filterBidirectional!"true"([4, 5]))); | ||
| 1250 | assert(endsWith(arr, filterBidirectional!"true"([4, 5]), 7) == 1); | ||
| 1251 | assert(!endsWith(arr, filterBidirectional!"true"([2, 4, 5]))); | ||
| 1252 | assert(endsWith(arr, [2, 4, 5], filterBidirectional!"true"([3, 4, 5])) == 2); | ||
| 1253 | |||
| 1254 | //Non-default pred | ||
| 1255 | assert(endsWith!("a%10 == b%10")(arr, [14, 15])); | ||
| 1256 | assert(!endsWith!("a%10 == b%10")(arr, [15, 14])); | ||
| 1257 | } | ||
| 1258 | } | ||
| 1259 | |||
| 1260 | /** | ||
| 1261 | Iterates the passed range and selects the extreme element with `less`. | ||
| 1262 | If the extreme element occurs multiple time, the first occurrence will be | ||
| 1263 | returned. | ||
| 1264 | |||
| 1265 | Params: | ||
| 1266 | map = custom accessor for the comparison key | ||
| 1267 | selector = custom mapping for the extrema selection | ||
| 1268 | seed = custom seed to use as initial element | ||
| 1269 | r = Range from which the extreme value will be selected | ||
| 1270 | |||
| 1271 | Returns: | ||
| 1272 | The extreme value according to `map` and `selector` of the passed-in values. | ||
| 1273 | */ | ||
| 1274 | private auto extremum(alias map, alias selector = "a < b", Range)(Range r) | ||
| 1275 | if (isInputRange!Range && !isInfinite!Range && | ||
| 1276 | is(typeof(unaryFun!map(ElementType!(Range).init)))) | ||
| 1277 | in | ||
| 1278 | { | ||
| 1279 | assert(!r.empty, "r is an empty range"); | ||
| 1280 | } | ||
| 1281 | body | ||
| 1282 | { | ||
| 1283 | alias Element = ElementType!Range; | ||
| 1284 | Unqual!Element seed = r.front; | ||
| 1285 | r.popFront(); | ||
| 1286 | return extremum!(map, selector)(r, seed); | ||
| 1287 | } | ||
| 1288 | |||
| 1289 | private auto extremum(alias map, alias selector = "a < b", Range, | ||
| 1290 | RangeElementType = ElementType!Range) | ||
| 1291 | (Range r, RangeElementType seedElement) | ||
| 1292 | if (isInputRange!Range && !isInfinite!Range && | ||
| 1293 | !is(CommonType!(ElementType!Range, RangeElementType) == void) && | ||
| 1294 | is(typeof(unaryFun!map(ElementType!(Range).init)))) | ||
| 1295 | { | ||
| 1296 | alias mapFun = unaryFun!map; | ||
| 1297 | alias selectorFun = binaryFun!selector; | ||
| 1298 | |||
| 1299 | alias Element = ElementType!Range; | ||
| 1300 | alias CommonElement = CommonType!(Element, RangeElementType); | ||
| 1301 | Unqual!CommonElement extremeElement = seedElement; | ||
| 1302 | |||
| 1303 | alias MapType = Unqual!(typeof(mapFun(CommonElement.init))); | ||
| 1304 | MapType extremeElementMapped = mapFun(extremeElement); | ||
| 1305 | |||
| 1306 | // direct access via a random access range is faster | ||
| 1307 | static if (isRandomAccessRange!Range) | ||
| 1308 | { | ||
| 1309 | foreach (const i; 0 .. r.length) | ||
| 1310 | { | ||
| 1311 | MapType mapElement = mapFun(r[i]); | ||
| 1312 | if (selectorFun(mapElement, extremeElementMapped)) | ||
| 1313 | { | ||
| 1314 | extremeElement = r[i]; | ||
| 1315 | extremeElementMapped = mapElement; | ||
| 1316 | } | ||
| 1317 | } | ||
| 1318 | } | ||
| 1319 | else | ||
| 1320 | { | ||
| 1321 | while (!r.empty) | ||
| 1322 | { | ||
| 1323 | MapType mapElement = mapFun(r.front); | ||
| 1324 | if (selectorFun(mapElement, extremeElementMapped)) | ||
| 1325 | { | ||
| 1326 | extremeElement = r.front; | ||
| 1327 | extremeElementMapped = mapElement; | ||
| 1328 | } | ||
| 1329 | r.popFront(); | ||
| 1330 | } | ||
| 1331 | } | ||
| 1332 | return extremeElement; | ||
| 1333 | } | ||
| 1334 | |||
| 1335 | private auto extremum(alias selector = "a < b", Range)(Range r) | ||
| 1336 | if (isInputRange!Range && !isInfinite!Range && | ||
| 1337 | !is(typeof(unaryFun!selector(ElementType!(Range).init)))) | ||
| 1338 | { | ||
| 1339 | alias Element = ElementType!Range; | ||
| 1340 | Unqual!Element seed = r.front; | ||
| 1341 | r.popFront(); | ||
| 1342 | return extremum!selector(r, seed); | ||
| 1343 | } | ||
| 1344 | |||
| 1345 | // if we only have one statement in the loop it can be optimized a lot better | ||
| 1346 | private auto extremum(alias selector = "a < b", Range, | ||
| 1347 | RangeElementType = ElementType!Range) | ||
| 1348 | (Range r, RangeElementType seedElement) | ||
| 1349 | if (isInputRange!Range && !isInfinite!Range && | ||
| 1350 | !is(CommonType!(ElementType!Range, RangeElementType) == void) && | ||
| 1351 | !is(typeof(unaryFun!selector(ElementType!(Range).init)))) | ||
| 1352 | { | ||
| 1353 | alias Element = ElementType!Range; | ||
| 1354 | alias CommonElement = CommonType!(Element, RangeElementType); | ||
| 1355 | Unqual!CommonElement extremeElement = seedElement; | ||
| 1356 | alias selectorFun = binaryFun!selector; | ||
| 1357 | |||
| 1358 | // direct access via a random access range is faster | ||
| 1359 | static if (isRandomAccessRange!Range) | ||
| 1360 | { | ||
| 1361 | foreach (const i; 0 .. r.length) | ||
| 1362 | { | ||
| 1363 | if (selectorFun(r[i], extremeElement)) | ||
| 1364 | { | ||
| 1365 | extremeElement = r[i]; | ||
| 1366 | } | ||
| 1367 | } | ||
| 1368 | } | ||
| 1369 | else | ||
| 1370 | { | ||
| 1371 | while (!r.empty) | ||
| 1372 | { | ||
| 1373 | if (selectorFun(r.front, extremeElement)) | ||
| 1374 | { | ||
| 1375 | extremeElement = r.front; | ||
| 1376 | } | ||
| 1377 | r.popFront(); | ||
| 1378 | } | ||
| 1379 | } | ||
| 1380 | return extremeElement; | ||
| 1381 | } | ||
| 1382 | |||
| 1383 | @safe pure unittest | ||
| 1384 | { | ||
| 1385 | // allows a custom map to select the extremum | ||
| 1386 | assert([[0, 4], [1, 2]].extremum!"a[0]" == [0, 4]); | ||
| 1387 | assert([[0, 4], [1, 2]].extremum!"a[1]" == [1, 2]); | ||
| 1388 | |||
| 1389 | // allows a custom selector for comparison | ||
| 1390 | assert([[0, 4], [1, 2]].extremum!("a[0]", "a > b") == [1, 2]); | ||
| 1391 | assert([[0, 4], [1, 2]].extremum!("a[1]", "a > b") == [0, 4]); | ||
| 1392 | |||
| 1393 | // use a custom comparator | ||
| 1394 | import std.math : cmp; | ||
| 1395 | assert([-2., 0, 5].extremum!cmp == 5.0); | ||
| 1396 | assert([-2., 0, 2].extremum!`cmp(a, b) < 0` == -2.0); | ||
| 1397 | |||
| 1398 | // combine with map | ||
| 1399 | import std.range : enumerate; | ||
| 1400 | assert([-3., 0, 5].enumerate.extremum!(`a.value`, cmp) == tuple(2, 5.0)); | ||
| 1401 | assert([-2., 0, 2].enumerate.extremum!(`a.value`, `cmp(a, b) < 0`) == tuple(0, -2.0)); | ||
| 1402 | |||
| 1403 | // seed with a custom value | ||
| 1404 | int[] arr; | ||
| 1405 | assert(arr.extremum(1) == 1); | ||
| 1406 | } | ||
| 1407 | |||
| 1408 | @safe pure nothrow unittest | ||
| 1409 | { | ||
| 1410 | // 2d seeds | ||
| 1411 | int[][] arr2d; | ||
| 1412 | assert(arr2d.extremum([1]) == [1]); | ||
| 1413 | |||
| 1414 | // allow seeds of different types (implicit casting) | ||
| 1415 | assert(extremum([2, 3, 4], 1.5) == 1.5); | ||
| 1416 | } | ||
| 1417 | |||
| 1418 | @safe pure unittest | ||
| 1419 | { | ||
| 1420 | import std.range : enumerate, iota; | ||
| 1421 | |||
| 1422 | // forward ranges | ||
| 1423 | assert(iota(1, 5).extremum() == 1); | ||
| 1424 | assert(iota(2, 5).enumerate.extremum!"a.value" == tuple(0, 2)); | ||
| 1425 | |||
| 1426 | // should work with const | ||
| 1427 | const(int)[] immArr = [2, 1, 3]; | ||
| 1428 | assert(immArr.extremum == 1); | ||
| 1429 | |||
| 1430 | // should work with immutable | ||
| 1431 | immutable(int)[] immArr2 = [2, 1, 3]; | ||
| 1432 | assert(immArr2.extremum == 1); | ||
| 1433 | |||
| 1434 | // with strings | ||
| 1435 | assert(["b", "a", "c"].extremum == "a"); | ||
| 1436 | |||
| 1437 | // with all dummy ranges | ||
| 1438 | import std.internal.test.dummyrange; | ||
| 1439 | foreach (DummyType; AllDummyRanges) | ||
| 1440 | { | ||
| 1441 | DummyType d; | ||
| 1442 | assert(d.extremum == 1); | ||
| 1443 | assert(d.extremum!(a => a) == 1); | ||
| 1444 | assert(d.extremum!`a > b` == 10); | ||
| 1445 | assert(d.extremum!(a => a, `a > b`) == 10); | ||
| 1446 | } | ||
| 1447 | } | ||
| 1448 | |||
| 1449 | @nogc @safe nothrow pure unittest | ||
| 1450 | { | ||
| 1451 | static immutable arr = [7, 3, 4, 2, 1, 8]; | ||
| 1452 | assert(arr.extremum == 1); | ||
| 1453 | |||
| 1454 | static immutable arr2d = [[1, 9], [3, 1], [4, 2]]; | ||
| 1455 | assert(arr2d.extremum!"a[1]" == arr2d[1]); | ||
| 1456 | } | ||
| 1457 | |||
| 1458 | // find | ||
| 1459 | /** | ||
| 1460 | Finds an individual element in an input range. Elements of $(D | ||
| 1461 | haystack) are compared with $(D needle) by using predicate $(D | ||
| 1462 | pred). Performs $(BIGOH walkLength(haystack)) evaluations of $(D | ||
| 1463 | pred). | ||
| 1464 | |||
| 1465 | To _find the last occurrence of $(D needle) in $(D haystack), call $(D | ||
| 1466 | find(retro(haystack), needle)). See $(REF retro, std,range). | ||
| 1467 | |||
| 1468 | Params: | ||
| 1469 | |||
| 1470 | pred = The predicate for comparing each element with the needle, defaulting to | ||
| 1471 | $(D "a == b"). | ||
| 1472 | The negated predicate $(D "a != b") can be used to search instead for the first | ||
| 1473 | element $(I not) matching the needle. | ||
| 1474 | |||
| 1475 | haystack = The $(REF_ALTTEXT input range, isInputRange, std,range,primitives) | ||
| 1476 | searched in. | ||
| 1477 | |||
| 1478 | needle = The element searched for. | ||
| 1479 | |||
| 1480 | Constraints: | ||
| 1481 | |||
| 1482 | $(D isInputRange!InputRange && is(typeof(binaryFun!pred(haystack.front, needle) | ||
| 1483 | : bool))) | ||
| 1484 | |||
| 1485 | Returns: | ||
| 1486 | |||
| 1487 | $(D haystack) advanced such that the front element is the one searched for; | ||
| 1488 | that is, until $(D binaryFun!pred(haystack.front, needle)) is $(D true). If no | ||
| 1489 | such position exists, returns an empty $(D haystack). | ||
| 1490 | |||
| 1491 | See_Also: | ||
| 1492 | $(HTTP sgi.com/tech/stl/_find.html, STL's _find) | ||
| 1493 | */ | ||
| 1494 | InputRange find(alias pred = "a == b", InputRange, Element)(InputRange haystack, scope Element needle) | ||
| 1495 | if (isInputRange!InputRange && | ||
| 1496 | is (typeof(binaryFun!pred(haystack.front, needle)) : bool)) | ||
| 1497 | { | ||
| 1498 | alias R = InputRange; | ||
| 1499 | alias E = Element; | ||
| 1500 | alias predFun = binaryFun!pred; | ||
| 1501 | static if (is(typeof(pred == "a == b"))) | ||
| 1502 | enum isDefaultPred = pred == "a == b"; | ||
| 1503 | else | ||
| 1504 | enum isDefaultPred = false; | ||
| 1505 | enum isIntegralNeedle = isSomeChar!E || isIntegral!E || isBoolean!E; | ||
| 1506 | |||
| 1507 | alias EType = ElementType!R; | ||
| 1508 | |||
| 1509 | // If the haystack is a SortedRange we can use binary search to find the needle. | ||
| 1510 | // Works only for the default find predicate and any SortedRange predicate. | ||
| 1511 | // 8829 enhancement | ||
| 1512 | import std.range : SortedRange; | ||
| 1513 | static if (is(InputRange : SortedRange!TT, TT) && isDefaultPred) | ||
| 1514 | { | ||
| 1515 | auto lb = haystack.lowerBound(needle); | ||
| 1516 | if (lb.length == haystack.length || haystack[lb.length] != needle) | ||
| 1517 | return haystack[$ .. $]; | ||
| 1518 | |||
| 1519 | return haystack[lb.length .. $]; | ||
| 1520 | } | ||
| 1521 | else static if (isNarrowString!R) | ||
| 1522 | { | ||
| 1523 | alias EEType = ElementEncodingType!R; | ||
| 1524 | alias UEEType = Unqual!EEType; | ||
| 1525 | |||
| 1526 | //These are two special cases which can search without decoding the UTF stream. | ||
| 1527 | static if (isDefaultPred && isIntegralNeedle) | ||
| 1528 | { | ||
| 1529 | import std.utf : canSearchInCodeUnits; | ||
| 1530 | |||
| 1531 | //This special case deals with UTF8 search, when the needle | ||
| 1532 | //is represented by a single code point. | ||
| 1533 | //Note: "needle <= 0x7F" properly handles sign via unsigned promotion | ||
| 1534 | static if (is(UEEType == char)) | ||
| 1535 | { | ||
| 1536 | if (!__ctfe && canSearchInCodeUnits!char(needle)) | ||
| 1537 | { | ||
| 1538 | static R trustedMemchr(ref R haystack, ref E needle) @trusted nothrow pure | ||
| 1539 | { | ||
| 1540 | import core.stdc.string : memchr; | ||
| 1541 | auto ptr = memchr(haystack.ptr, needle, haystack.length); | ||
| 1542 | return ptr ? | ||
| 1543 | haystack[cast(char*) ptr - haystack.ptr .. $] : | ||
| 1544 | haystack[$ .. $]; | ||
| 1545 | } | ||
| 1546 | return trustedMemchr(haystack, needle); | ||
| 1547 | } | ||
| 1548 | } | ||
| 1549 | |||
| 1550 | //Ditto, but for UTF16 | ||
| 1551 | static if (is(UEEType == wchar)) | ||
| 1552 | { | ||
| 1553 | if (canSearchInCodeUnits!wchar(needle)) | ||
| 1554 | { | ||
| 1555 | foreach (i, ref EEType e; haystack) | ||
| 1556 | { | ||
| 1557 | if (e == needle) | ||
| 1558 | return haystack[i .. $]; | ||
| 1559 | } | ||
| 1560 | return haystack[$ .. $]; | ||
| 1561 | } | ||
| 1562 | } | ||
| 1563 | } | ||
| 1564 | |||
| 1565 | //Previous conditonal optimizations did not succeed. Fallback to | ||
| 1566 | //unconditional implementations | ||
| 1567 | static if (isDefaultPred) | ||
| 1568 | { | ||
| 1569 | import std.utf : encode; | ||
| 1570 | |||
| 1571 | //In case of default pred, it is faster to do string/string search. | ||
| 1572 | UEEType[is(UEEType == char) ? 4 : 2] buf; | ||
| 1573 | |||
| 1574 | size_t len = encode(buf, needle); | ||
| 1575 | return find(haystack, buf[0 .. len]); | ||
| 1576 | } | ||
| 1577 | else | ||
| 1578 | { | ||
| 1579 | import std.utf : decode; | ||
| 1580 | |||
| 1581 | //Explicit pred: we must test each character by the book. | ||
| 1582 | //We choose a manual decoding approach, because it is faster than | ||
| 1583 | //the built-in foreach, or doing a front/popFront for-loop. | ||
| 1584 | immutable len = haystack.length; | ||
| 1585 | size_t i = 0, next = 0; | ||
| 1586 | while (next < len) | ||
| 1587 | { | ||
| 1588 | if (predFun(decode(haystack, next), needle)) | ||
| 1589 | return haystack[i .. $]; | ||
| 1590 | i = next; | ||
| 1591 | } | ||
| 1592 | return haystack[$ .. $]; | ||
| 1593 | } | ||
| 1594 | } | ||
| 1595 | else static if (isArray!R) | ||
| 1596 | { | ||
| 1597 | //10403 optimization | ||
| 1598 | static if (isDefaultPred && isIntegral!EType && EType.sizeof == 1 && isIntegralNeedle) | ||
| 1599 | { | ||
| 1600 | import std.algorithm.comparison : max, min; | ||
| 1601 | |||
| 1602 | R findHelper(ref R haystack, ref E needle) @trusted nothrow pure | ||
| 1603 | { | ||
| 1604 | import core.stdc.string : memchr; | ||
| 1605 | |||
| 1606 | EType* ptr = null; | ||
| 1607 | //Note: we use "min/max" to handle sign mismatch. | ||
| 1608 | if (min(EType.min, needle) == EType.min && | ||
| 1609 | max(EType.max, needle) == EType.max) | ||
| 1610 | { | ||
| 1611 | ptr = cast(EType*) memchr(haystack.ptr, needle, | ||
| 1612 | haystack.length); | ||
| 1613 | } | ||
| 1614 | |||
| 1615 | return ptr ? | ||
| 1616 | haystack[ptr - haystack.ptr .. $] : | ||
| 1617 | haystack[$ .. $]; | ||
| 1618 | } | ||
| 1619 | |||
| 1620 | if (!__ctfe) | ||
| 1621 | return findHelper(haystack, needle); | ||
| 1622 | } | ||
| 1623 | |||
| 1624 | //Default implementation. | ||
| 1625 | foreach (i, ref e; haystack) | ||
| 1626 | if (predFun(e, needle)) | ||
| 1627 | return haystack[i .. $]; | ||
| 1628 | return haystack[$ .. $]; | ||
| 1629 | } | ||
| 1630 | else | ||
| 1631 | { | ||
| 1632 | //Everything else. Walk. | ||
| 1633 | for ( ; !haystack.empty; haystack.popFront() ) | ||
| 1634 | { | ||
| 1635 | if (predFun(haystack.front, needle)) | ||
| 1636 | break; | ||
| 1637 | } | ||
| 1638 | return haystack; | ||
| 1639 | } | ||
| 1640 | } | ||
| 1641 | |||
| 1642 | /// | ||
| 1643 | @safe unittest | ||
| 1644 | { | ||
| 1645 | import std.algorithm.comparison : equal; | ||
| 1646 | import std.container : SList; | ||
| 1647 | import std.range; | ||
| 1648 | import std.range.primitives : empty; | ||
| 1649 | |||
| 1650 | auto arr = assumeSorted!"a < b"([1, 2, 4, 4, 4, 4, 5, 6, 9]); | ||
| 1651 | assert(find(arr, 4) == assumeSorted!"a < b"([4, 4, 4, 4, 5, 6, 9])); | ||
| 1652 | assert(find(arr, 1) == arr); | ||
| 1653 | assert(find(arr, 9) == assumeSorted!"a < b"([9])); | ||
| 1654 | assert(find!"a > b"(arr, 4) == assumeSorted!"a < b"([5, 6, 9])); | ||
| 1655 | assert(find!"a < b"(arr, 4) == arr); | ||
| 1656 | assert(find(arr, 0).empty()); | ||
| 1657 | assert(find(arr, 10).empty()); | ||
| 1658 | assert(find(arr, 8).empty()); | ||
| 1659 | |||
| 1660 | auto r = assumeSorted!"a > b"([10, 7, 3, 1, 0, 0]); | ||
| 1661 | assert(find(r, 3) == assumeSorted!"a > b"([3, 1, 0, 0])); | ||
| 1662 | assert(find!"a > b"(r, 8) == r); | ||
| 1663 | assert(find!"a < b"(r, 5) == assumeSorted!"a > b"([3, 1, 0, 0])); | ||
| 1664 | |||
| 1665 | assert(find("hello, world", ',') == ", world"); | ||
| 1666 | assert(find([1, 2, 3, 5], 4) == []); | ||
| 1667 | assert(equal(find(SList!int(1, 2, 3, 4, 5)[], 4), SList!int(4, 5)[])); | ||
| 1668 | assert(find!"a > b"([1, 2, 3, 5], 2) == [3, 5]); | ||
| 1669 | |||
| 1670 | auto a = [ 1, 2, 3 ]; | ||
| 1671 | assert(find(a, 5).empty); // not found | ||
| 1672 | assert(!find(a, 2).empty); // found | ||
| 1673 | |||
| 1674 | // Case-insensitive find of a string | ||
| 1675 | string[] s = [ "Hello", "world", "!" ]; | ||
| 1676 | assert(!find!("toLower(a) == b")(s, "hello").empty); | ||
| 1677 | } | ||
| 1678 | |||
| 1679 | @safe unittest | ||
| 1680 | { | ||
| 1681 | import std.algorithm.comparison : equal; | ||
| 1682 | import std.container : SList; | ||
| 1683 | |||
| 1684 | auto lst = SList!int(1, 2, 5, 7, 3); | ||
| 1685 | assert(lst.front == 1); | ||
| 1686 | auto r = find(lst[], 5); | ||
| 1687 | assert(equal(r, SList!int(5, 7, 3)[])); | ||
| 1688 | assert(find([1, 2, 3, 5], 4).empty); | ||
| 1689 | assert(equal(find!"a > b"("hello", 'k'), "llo")); | ||
| 1690 | } | ||
| 1691 | |||
| 1692 | @safe pure nothrow unittest | ||
| 1693 | { | ||
| 1694 | assert(!find ([1, 2, 3], 2).empty); | ||
| 1695 | assert(!find!((a,b)=>a == b)([1, 2, 3], 2).empty); | ||
| 1696 | assert(!find ([1, 2, 3], 2).empty); | ||
| 1697 | assert(!find!((a,b)=>a == b)([1, 2, 3], 2).empty); | ||
| 1698 | } | ||
| 1699 | |||
| 1700 | @safe pure unittest | ||
| 1701 | { | ||
| 1702 | import std.meta : AliasSeq; | ||
| 1703 | foreach (R; AliasSeq!(string, wstring, dstring)) | ||
| 1704 | { | ||
| 1705 | foreach (E; AliasSeq!(char, wchar, dchar)) | ||
| 1706 | { | ||
| 1707 | assert(find ("hello world", 'w') == "world"); | ||
| 1708 | assert(find!((a,b)=>a == b)("hello world", 'w') == "world"); | ||
| 1709 | assert(find ("日c語", 'c') == "c語"); | ||
| 1710 | assert(find!((a,b)=>a == b)("日c語", 'c') == "c語"); | ||
| 1711 | assert(find ("0123456789", 'A').empty); | ||
| 1712 | static if (E.sizeof >= 2) | ||
| 1713 | { | ||
| 1714 | assert(find ("日本語", '本') == "本語"); | ||
| 1715 | assert(find!((a,b)=>a == b)("日本語", '本') == "本語"); | ||
| 1716 | } | ||
| 1717 | } | ||
| 1718 | } | ||
| 1719 | } | ||
| 1720 | |||
| 1721 | @safe unittest | ||
| 1722 | { | ||
| 1723 | //CTFE | ||
| 1724 | static assert(find("abc", 'b') == "bc"); | ||
| 1725 | static assert(find("日b語", 'b') == "b語"); | ||
| 1726 | static assert(find("日本語", '本') == "本語"); | ||
| 1727 | static assert(find([1, 2, 3], 2) == [2, 3]); | ||
| 1728 | |||
| 1729 | static assert(find ([1, 2, 3], 2)); | ||
| 1730 | static assert(find!((a,b)=>a == b)([1, 2, 3], 2)); | ||
| 1731 | static assert(find ([1, 2, 3], 2)); | ||
| 1732 | static assert(find!((a,b)=>a == b)([1, 2, 3], 2)); | ||
| 1733 | } | ||
| 1734 | |||
| 1735 | @safe unittest | ||
| 1736 | { | ||
| 1737 | import std.exception : assertCTFEable; | ||
| 1738 | import std.meta : AliasSeq; | ||
| 1739 | |||
| 1740 | void dg() @safe pure nothrow | ||
| 1741 | { | ||
| 1742 | byte[] sarr = [1, 2, 3, 4]; | ||
| 1743 | ubyte[] uarr = [1, 2, 3, 4]; | ||
| 1744 | foreach (arr; AliasSeq!(sarr, uarr)) | ||
| 1745 | { | ||
| 1746 | foreach (T; AliasSeq!(byte, ubyte, int, uint)) | ||
| 1747 | { | ||
| 1748 | assert(find(arr, cast(T) 3) == arr[2 .. $]); | ||
| 1749 | assert(find(arr, cast(T) 9) == arr[$ .. $]); | ||
| 1750 | } | ||
| 1751 | assert(find(arr, 256) == arr[$ .. $]); | ||
| 1752 | } | ||
| 1753 | } | ||
| 1754 | dg(); | ||
| 1755 | assertCTFEable!dg; | ||
| 1756 | } | ||
| 1757 | |||
| 1758 | @safe unittest | ||
| 1759 | { | ||
| 1760 | // Bugzilla 11603 | ||
| 1761 | enum Foo : ubyte { A } | ||
| 1762 | assert([Foo.A].find(Foo.A).empty == false); | ||
| 1763 | |||
| 1764 | ubyte x = 0; | ||
| 1765 | assert([x].find(x).empty == false); | ||
| 1766 | } | ||
| 1767 | |||
| 1768 | /** | ||
| 1769 | Advances the input range $(D haystack) by calling $(D haystack.popFront) | ||
| 1770 | until either $(D pred(haystack.front)), or $(D | ||
| 1771 | haystack.empty). Performs $(BIGOH haystack.length) evaluations of $(D | ||
| 1772 | pred). | ||
| 1773 | |||
| 1774 | To _find the last element of a | ||
| 1775 | $(REF_ALTTEXT bidirectional, isBidirectionalRange, std,range,primitives) $(D haystack) satisfying | ||
| 1776 | $(D pred), call $(D find!(pred)(retro(haystack))). See $(REF retro, std,range). | ||
| 1777 | |||
| 1778 | `find` behaves similar to `dropWhile` in other languages. | ||
| 1779 | |||
| 1780 | Params: | ||
| 1781 | |||
| 1782 | pred = The predicate for determining if a given element is the one being | ||
| 1783 | searched for. | ||
| 1784 | |||
| 1785 | haystack = The $(REF_ALTTEXT input range, isInputRange, std,range,primitives) to | ||
| 1786 | search in. | ||
| 1787 | |||
| 1788 | Returns: | ||
| 1789 | |||
| 1790 | $(D haystack) advanced such that the front element is the one searched for; | ||
| 1791 | that is, until $(D binaryFun!pred(haystack.front, needle)) is $(D true). If no | ||
| 1792 | such position exists, returns an empty $(D haystack). | ||
| 1793 | |||
| 1794 | See_Also: | ||
| 1795 | $(HTTP sgi.com/tech/stl/find_if.html, STL's find_if) | ||
| 1796 | */ | ||
| 1797 | InputRange find(alias pred, InputRange)(InputRange haystack) | ||
| 1798 | if (isInputRange!InputRange) | ||
| 1799 | { | ||
| 1800 | alias R = InputRange; | ||
| 1801 | alias predFun = unaryFun!pred; | ||
| 1802 | static if (isNarrowString!R) | ||
| 1803 | { | ||
| 1804 | import std.utf : decode; | ||
| 1805 | |||
| 1806 | immutable len = haystack.length; | ||
| 1807 | size_t i = 0, next = 0; | ||
| 1808 | while (next < len) | ||
| 1809 | { | ||
| 1810 | if (predFun(decode(haystack, next))) | ||
| 1811 | return haystack[i .. $]; | ||
| 1812 | i = next; | ||
| 1813 | } | ||
| 1814 | return haystack[$ .. $]; | ||
| 1815 | } | ||
| 1816 | else | ||
| 1817 | { | ||
| 1818 | //standard range | ||
| 1819 | for ( ; !haystack.empty; haystack.popFront() ) | ||
| 1820 | { | ||
| 1821 | if (predFun(haystack.front)) | ||
| 1822 | break; | ||
| 1823 | } | ||
| 1824 | return haystack; | ||
| 1825 | } | ||
| 1826 | } | ||
| 1827 | |||
| 1828 | /// | ||
| 1829 | @safe unittest | ||
| 1830 | { | ||
| 1831 | auto arr = [ 1, 2, 3, 4, 1 ]; | ||
| 1832 | assert(find!("a > 2")(arr) == [ 3, 4, 1 ]); | ||
| 1833 | |||
| 1834 | // with predicate alias | ||
| 1835 | bool pred(int x) { return x + 1 > 1.5; } | ||
| 1836 | assert(find!(pred)(arr) == arr); | ||
| 1837 | } | ||
| 1838 | |||
| 1839 | @safe pure unittest | ||
| 1840 | { | ||
| 1841 | int[] r = [ 1, 2, 3 ]; | ||
| 1842 | assert(find!(a=>a > 2)(r) == [3]); | ||
| 1843 | bool pred(int x) { return x + 1 > 1.5; } | ||
| 1844 | assert(find!(pred)(r) == r); | ||
| 1845 | |||
| 1846 | assert(find!(a=>a > 'v')("hello world") == "world"); | ||
| 1847 | assert(find!(a=>a%4 == 0)("日本語") == "本語"); | ||
| 1848 | } | ||
| 1849 | |||
| 1850 | /** | ||
| 1851 | Finds the first occurrence of a forward range in another forward range. | ||
| 1852 | |||
| 1853 | Performs $(BIGOH walkLength(haystack) * walkLength(needle)) comparisons in the | ||
| 1854 | worst case. There are specializations that improve performance by taking | ||
| 1855 | advantage of $(REF_ALTTEXT bidirectional range, isBidirectionalRange, std,range,primitives) | ||
| 1856 | or random access in the given ranges (where possible), depending on the statistics | ||
| 1857 | of the two ranges' content. | ||
| 1858 | |||
| 1859 | Params: | ||
| 1860 | |||
| 1861 | pred = The predicate to use for comparing respective elements from the haystack | ||
| 1862 | and the needle. Defaults to simple equality $(D "a == b"). | ||
| 1863 | |||
| 1864 | haystack = The $(REF_ALTTEXT forward range, isForwardRange, std,range,primitives) | ||
| 1865 | searched in. | ||
| 1866 | |||
| 1867 | needle = The $(REF_ALTTEXT forward range, isForwardRange, std,range,primitives) | ||
| 1868 | searched for. | ||
| 1869 | |||
| 1870 | Returns: | ||
| 1871 | |||
| 1872 | $(D haystack) advanced such that $(D needle) is a prefix of it (if no | ||
| 1873 | such position exists, returns $(D haystack) advanced to termination). | ||
| 1874 | */ | ||
| 1875 | R1 find(alias pred = "a == b", R1, R2)(R1 haystack, scope R2 needle) | ||
| 1876 | if (isForwardRange!R1 && isForwardRange!R2 | ||
| 1877 | && is(typeof(binaryFun!pred(haystack.front, needle.front)) : bool)) | ||
| 1878 | { | ||
| 1879 | static if (!isRandomAccessRange!R1) | ||
| 1880 | { | ||
| 1881 | static if (is(typeof(pred == "a == b")) && pred == "a == b" && isSomeString!R1 && isSomeString!R2 | ||
| 1882 | && haystack[0].sizeof == needle[0].sizeof) | ||
| 1883 | { | ||
| 1884 | // return cast(R1) find(representation(haystack), representation(needle)); | ||
| 1885 | // Specialization for simple string search | ||
| 1886 | alias Representation = | ||
| 1887 | Select!(haystack[0].sizeof == 1, ubyte[], | ||
| 1888 | Select!(haystack[0].sizeof == 2, ushort[], uint[])); | ||
| 1889 | // Will use the array specialization | ||
| 1890 | static TO force(TO, T)(T r) @trusted { return cast(TO) r; } | ||
| 1891 | return force!R1(.find!(pred, Representation, Representation) | ||
| 1892 | (force!Representation(haystack), force!Representation(needle))); | ||
| 1893 | } | ||
| 1894 | else | ||
| 1895 | { | ||
| 1896 | return simpleMindedFind!pred(haystack, needle); | ||
| 1897 | } | ||
| 1898 | } | ||
| 1899 | else static if (!isBidirectionalRange!R2 || !hasSlicing!R1) | ||
| 1900 | { | ||
| 1901 | static if (!is(ElementType!R1 == ElementType!R2)) | ||
| 1902 | { | ||
| 1903 | return simpleMindedFind!pred(haystack, needle); | ||
| 1904 | } | ||
| 1905 | else | ||
| 1906 | { | ||
| 1907 | // Prepare the search with needle's first element | ||
| 1908 | if (needle.empty) | ||
| 1909 | return haystack; | ||
| 1910 | |||
| 1911 | haystack = .find!pred(haystack, needle.front); | ||
| 1912 | |||
| 1913 | static if (hasLength!R1 && hasLength!R2 && is(typeof(takeNone(haystack)) == R1)) | ||
| 1914 | { | ||
| 1915 | if (needle.length > haystack.length) | ||
| 1916 | return takeNone(haystack); | ||
| 1917 | } | ||
| 1918 | else | ||
| 1919 | { | ||
| 1920 | if (haystack.empty) | ||
| 1921 | return haystack; | ||
| 1922 | } | ||
| 1923 | |||
| 1924 | needle.popFront(); | ||
| 1925 | size_t matchLen = 1; | ||
| 1926 | |||
| 1927 | // Loop invariant: haystack[0 .. matchLen] matches everything in | ||
| 1928 | // the initial needle that was popped out of needle. | ||
| 1929 | for (;;) | ||
| 1930 | { | ||
| 1931 | // Extend matchLength as much as possible | ||
| 1932 | for (;;) | ||
| 1933 | { | ||
| 1934 | import std.range : takeNone; | ||
| 1935 | |||
| 1936 | if (needle.empty || haystack.empty) | ||
| 1937 | return haystack; | ||
| 1938 | |||
| 1939 | static if (hasLength!R1 && is(typeof(takeNone(haystack)) == R1)) | ||
| 1940 | { | ||
| 1941 | if (matchLen == haystack.length) | ||
| 1942 | return takeNone(haystack); | ||
| 1943 | } | ||
| 1944 | |||
| 1945 | if (!binaryFun!pred(haystack[matchLen], needle.front)) | ||
| 1946 | break; | ||
| 1947 | |||
| 1948 | ++matchLen; | ||
| 1949 | needle.popFront(); | ||
| 1950 | } | ||
| 1951 | |||
| 1952 | auto bestMatch = haystack[0 .. matchLen]; | ||
| 1953 | haystack.popFront(); | ||
| 1954 | haystack = .find!pred(haystack, bestMatch); | ||
| 1955 | } | ||
| 1956 | } | ||
| 1957 | } | ||
| 1958 | else // static if (hasSlicing!R1 && isBidirectionalRange!R2) | ||
| 1959 | { | ||
| 1960 | if (needle.empty) return haystack; | ||
| 1961 | |||
| 1962 | static if (hasLength!R2) | ||
| 1963 | { | ||
| 1964 | immutable needleLength = needle.length; | ||
| 1965 | } | ||
| 1966 | else | ||
| 1967 | { | ||
| 1968 | immutable needleLength = walkLength(needle.save); | ||
| 1969 | } | ||
| 1970 | if (needleLength > haystack.length) | ||
| 1971 | { | ||
| 1972 | return haystack[haystack.length .. haystack.length]; | ||
| 1973 | } | ||
| 1974 | // Optimization in case the ranges are both SortedRanges. | ||
| 1975 | // Binary search can be used to find the first occurence | ||
| 1976 | // of the first element of the needle in haystack. | ||
| 1977 | // When it is found O(walklength(needle)) steps are performed. | ||
| 1978 | // 8829 enhancement | ||
| 1979 | import std.algorithm.comparison : mismatch; | ||
| 1980 | import std.range : SortedRange; | ||
| 1981 | static if (is(R1 == R2) | ||
| 1982 | && is(R1 : SortedRange!TT, TT) | ||
| 1983 | && pred == "a == b") | ||
| 1984 | { | ||
| 1985 | auto needleFirstElem = needle[0]; | ||
| 1986 | auto partitions = haystack.trisect(needleFirstElem); | ||
| 1987 | auto firstElemLen = partitions[1].length; | ||
| 1988 | size_t count = 0; | ||
| 1989 | |||
| 1990 | if (firstElemLen == 0) | ||
| 1991 | return haystack[$ .. $]; | ||
| 1992 | |||
| 1993 | while (needle.front() == needleFirstElem) | ||
| 1994 | { | ||
| 1995 | needle.popFront(); | ||
| 1996 | ++count; | ||
| 1997 | |||
| 1998 | if (count > firstElemLen) | ||
| 1999 | return haystack[$ .. $]; | ||
| 2000 | } | ||
| 2001 | |||
| 2002 | auto m = mismatch(partitions[2], needle); | ||
| 2003 | |||
| 2004 | if (m[1].empty) | ||
| 2005 | return haystack[partitions[0].length + partitions[1].length - count .. $]; | ||
| 2006 | } | ||
| 2007 | else static if (isRandomAccessRange!R2) | ||
| 2008 | { | ||
| 2009 | immutable lastIndex = needleLength - 1; | ||
| 2010 | auto last = needle[lastIndex]; | ||
| 2011 | size_t j = lastIndex, skip = 0; | ||
| 2012 | for (; j < haystack.length;) | ||
| 2013 | { | ||
| 2014 | if (!binaryFun!pred(haystack[j], last)) | ||
| 2015 | { | ||
| 2016 | ++j; | ||
| 2017 | continue; | ||
| 2018 | } | ||
| 2019 | immutable k = j - lastIndex; | ||
| 2020 | // last elements match | ||
| 2021 | for (size_t i = 0;; ++i) | ||
| 2022 | { | ||
| 2023 | if (i == lastIndex) | ||
| 2024 | return haystack[k .. haystack.length]; | ||
| 2025 | if (!binaryFun!pred(haystack[k + i], needle[i])) | ||
| 2026 | break; | ||
| 2027 | } | ||
| 2028 | if (skip == 0) | ||
| 2029 | { | ||
| 2030 | skip = 1; | ||
| 2031 | while (skip < needleLength && needle[needleLength - 1 - skip] != needle[needleLength - 1]) | ||
| 2032 | { | ||
| 2033 | ++skip; | ||
| 2034 | } | ||
| 2035 | } | ||
| 2036 | j += skip; | ||
| 2037 | } | ||
| 2038 | } | ||
| 2039 | else | ||
| 2040 | { | ||
| 2041 | // @@@BUG@@@ | ||
| 2042 | // auto needleBack = moveBack(needle); | ||
| 2043 | // Stage 1: find the step | ||
| 2044 | size_t step = 1; | ||
| 2045 | auto needleBack = needle.back; | ||
| 2046 | needle.popBack(); | ||
| 2047 | for (auto i = needle.save; !i.empty && i.back != needleBack; | ||
| 2048 | i.popBack(), ++step) | ||
| 2049 | { | ||
| 2050 | } | ||
| 2051 | // Stage 2: linear find | ||
| 2052 | size_t scout = needleLength - 1; | ||
| 2053 | for (;;) | ||
| 2054 | { | ||
| 2055 | if (scout >= haystack.length) | ||
| 2056 | break; | ||
| 2057 | if (!binaryFun!pred(haystack[scout], needleBack)) | ||
| 2058 | { | ||
| 2059 | ++scout; | ||
| 2060 | continue; | ||
| 2061 | } | ||
| 2062 | // Found a match with the last element in the needle | ||
| 2063 | auto cand = haystack[scout + 1 - needleLength .. haystack.length]; | ||
| 2064 | if (startsWith!pred(cand, needle)) | ||
| 2065 | { | ||
| 2066 | // found | ||
| 2067 | return cand; | ||
| 2068 | } | ||
| 2069 | scout += step; | ||
| 2070 | } | ||
| 2071 | } | ||
| 2072 | return haystack[haystack.length .. haystack.length]; | ||
| 2073 | } | ||
| 2074 | } | ||
| 2075 | |||
| 2076 | /// | ||
| 2077 | @safe unittest | ||
| 2078 | { | ||
| 2079 | import std.container : SList; | ||
| 2080 | import std.range.primitives : empty; | ||
| 2081 | import std.typecons : Tuple; | ||
| 2082 | |||
| 2083 | assert(find("hello, world", "World").empty); | ||
| 2084 | assert(find("hello, world", "wo") == "world"); | ||
| 2085 | assert([1, 2, 3, 4].find(SList!int(2, 3)[]) == [2, 3, 4]); | ||
| 2086 | alias C = Tuple!(int, "x", int, "y"); | ||
| 2087 | auto a = [C(1,0), C(2,0), C(3,1), C(4,0)]; | ||
| 2088 | assert(a.find!"a.x == b"([2, 3]) == [C(2,0), C(3,1), C(4,0)]); | ||
| 2089 | assert(a[1 .. $].find!"a.x == b"([2, 3]) == [C(2,0), C(3,1), C(4,0)]); | ||
| 2090 | } | ||
| 2091 | |||
| 2092 | @safe unittest | ||
| 2093 | { | ||
| 2094 | import std.container : SList; | ||
| 2095 | alias C = Tuple!(int, "x", int, "y"); | ||
| 2096 | assert([C(1,0), C(2,0), C(3,1), C(4,0)].find!"a.x == b"(SList!int(2, 3)[]) == [C(2,0), C(3,1), C(4,0)]); | ||
| 2097 | } | ||
| 2098 | |||
| 2099 | @safe unittest | ||
| 2100 | { | ||
| 2101 | import std.algorithm.comparison : equal; | ||
| 2102 | import std.container : SList; | ||
| 2103 | |||
| 2104 | auto lst = SList!int(1, 2, 5, 7, 3); | ||
| 2105 | static assert(isForwardRange!(int[])); | ||
| 2106 | static assert(isForwardRange!(typeof(lst[]))); | ||
| 2107 | auto r = find(lst[], [2, 5]); | ||
| 2108 | assert(equal(r, SList!int(2, 5, 7, 3)[])); | ||
| 2109 | } | ||
| 2110 | |||
| 2111 | @safe unittest | ||
| 2112 | { | ||
| 2113 | import std.range; | ||
| 2114 | import std.stdio; | ||
| 2115 | |||
| 2116 | auto r1 = assumeSorted([1, 2, 3, 3, 3, 4, 5, 6, 7, 8, 8, 8, 10]); | ||
| 2117 | auto r2 = assumeSorted([3, 3, 4, 5, 6, 7, 8, 8]); | ||
| 2118 | auto r3 = assumeSorted([3, 4, 5, 6, 7, 8]); | ||
| 2119 | auto r4 = assumeSorted([4, 5, 6]); | ||
| 2120 | auto r5 = assumeSorted([12, 13]); | ||
| 2121 | auto r6 = assumeSorted([8, 8, 10, 11]); | ||
| 2122 | auto r7 = assumeSorted([3, 3, 3, 3, 3, 3, 3]); | ||
| 2123 | |||
| 2124 | assert(find(r1, r2) == assumeSorted([3, 3, 4, 5, 6, 7, 8, 8, 8, 10])); | ||
| 2125 | assert(find(r1, r3) == assumeSorted([3, 4, 5, 6, 7, 8, 8, 8, 10])); | ||
| 2126 | assert(find(r1, r4) == assumeSorted([4, 5, 6, 7, 8, 8, 8, 10])); | ||
| 2127 | assert(find(r1, r5).empty()); | ||
| 2128 | assert(find(r1, r6).empty()); | ||
| 2129 | assert(find(r1, r7).empty()); | ||
| 2130 | } | ||
| 2131 | |||
| 2132 | @safe unittest | ||
| 2133 | { | ||
| 2134 | import std.algorithm.comparison : equal; | ||
| 2135 | // @@@BUG@@@ removing static below makes unittest fail | ||
| 2136 | static struct BiRange | ||
| 2137 | { | ||
| 2138 | int[] payload; | ||
| 2139 | @property bool empty() { return payload.empty; } | ||
| 2140 | @property BiRange save() { return this; } | ||
| 2141 | @property ref int front() { return payload[0]; } | ||
| 2142 | @property ref int back() { return payload[$ - 1]; } | ||
| 2143 | void popFront() { return payload.popFront(); } | ||
| 2144 | void popBack() { return payload.popBack(); } | ||
| 2145 | } | ||
| 2146 | auto r = BiRange([1, 2, 3, 10, 11, 4]); | ||
| 2147 | assert(equal(find(r, [10, 11]), [10, 11, 4])); | ||
| 2148 | } | ||
| 2149 | |||
| 2150 | @safe unittest | ||
| 2151 | { | ||
| 2152 | import std.container : SList; | ||
| 2153 | |||
| 2154 | assert(find([ 1, 2, 3 ], SList!int(2, 3)[]) == [ 2, 3 ]); | ||
| 2155 | assert(find([ 1, 2, 1, 2, 3, 3 ], SList!int(2, 3)[]) == [ 2, 3, 3 ]); | ||
| 2156 | } | ||
| 2157 | |||
| 2158 | //Bug# 8334 | ||
| 2159 | @safe unittest | ||
| 2160 | { | ||
| 2161 | import std.algorithm.iteration : filter; | ||
| 2162 | import std.range; | ||
| 2163 | |||
| 2164 | auto haystack = [1, 2, 3, 4, 1, 9, 12, 42]; | ||
| 2165 | auto needle = [12, 42, 27]; | ||
| 2166 | |||
| 2167 | //different overload of find, but it's the base case. | ||
| 2168 | assert(find(haystack, needle).empty); | ||
| 2169 | |||
| 2170 | assert(find(haystack, takeExactly(filter!"true"(needle), 3)).empty); | ||
| 2171 | assert(find(haystack, filter!"true"(needle)).empty); | ||
| 2172 | } | ||
| 2173 | |||
| 2174 | // Internally used by some find() overloads above | ||
| 2175 | private R1 simpleMindedFind(alias pred, R1, R2)(R1 haystack, scope R2 needle) | ||
| 2176 | { | ||
| 2177 | enum estimateNeedleLength = hasLength!R1 && !hasLength!R2; | ||
| 2178 | |||
| 2179 | static if (hasLength!R1) | ||
| 2180 | { | ||
| 2181 | static if (!hasLength!R2) | ||
| 2182 | size_t estimatedNeedleLength = 0; | ||
| 2183 | else | ||
| 2184 | immutable size_t estimatedNeedleLength = needle.length; | ||
| 2185 | } | ||
| 2186 | |||
| 2187 | bool haystackTooShort() | ||
| 2188 | { | ||
| 2189 | static if (estimateNeedleLength) | ||
| 2190 | { | ||
| 2191 | return haystack.length < estimatedNeedleLength; | ||
| 2192 | } | ||
| 2193 | else | ||
| 2194 | { | ||
| 2195 | return haystack.empty; | ||
| 2196 | } | ||
| 2197 | } | ||
| 2198 | |||
| 2199 | searching: | ||
| 2200 | for (;; haystack.popFront()) | ||
| 2201 | { | ||
| 2202 | if (haystackTooShort()) | ||
| 2203 | { | ||
| 2204 | // Failed search | ||
| 2205 | static if (hasLength!R1) | ||
| 2206 | { | ||
| 2207 | static if (is(typeof(haystack[haystack.length .. | ||
| 2208 | haystack.length]) : R1)) | ||
| 2209 | return haystack[haystack.length .. haystack.length]; | ||
| 2210 | else | ||
| 2211 | return R1.init; | ||
| 2212 | } | ||
| 2213 | else | ||
| 2214 | { | ||
| 2215 | assert(haystack.empty); | ||
| 2216 | return haystack; | ||
| 2217 | } | ||
| 2218 | } | ||
| 2219 | static if (estimateNeedleLength) | ||
| 2220 | size_t matchLength = 0; | ||
| 2221 | for (auto h = haystack.save, n = needle.save; | ||
| 2222 | !n.empty; | ||
| 2223 | h.popFront(), n.popFront()) | ||
| 2224 | { | ||
| 2225 | if (h.empty || !binaryFun!pred(h.front, n.front)) | ||
| 2226 | { | ||
| 2227 | // Failed searching n in h | ||
| 2228 | static if (estimateNeedleLength) | ||
| 2229 | { | ||
| 2230 | if (estimatedNeedleLength < matchLength) | ||
| 2231 | estimatedNeedleLength = matchLength; | ||
| 2232 | } | ||
| 2233 | continue searching; | ||
| 2234 | } | ||
| 2235 | static if (estimateNeedleLength) | ||
| 2236 | ++matchLength; | ||
| 2237 | } | ||
| 2238 | break; | ||
| 2239 | } | ||
| 2240 | return haystack; | ||
| 2241 | } | ||
| 2242 | |||
| 2243 | @safe unittest | ||
| 2244 | { | ||
| 2245 | // Test simpleMindedFind for the case where both haystack and needle have | ||
| 2246 | // length. | ||
| 2247 | struct CustomString | ||
| 2248 | { | ||
| 2249 | @safe: | ||
| 2250 | string _impl; | ||
| 2251 | |||
| 2252 | // This is what triggers issue 7992. | ||
| 2253 | @property size_t length() const { return _impl.length; } | ||
| 2254 | @property void length(size_t len) { _impl.length = len; } | ||
| 2255 | |||
| 2256 | // This is for conformance to the forward range API (we deliberately | ||
| 2257 | // make it non-random access so that we will end up in | ||
| 2258 | // simpleMindedFind). | ||
| 2259 | @property bool empty() const { return _impl.empty; } | ||
| 2260 | @property dchar front() const { return _impl.front; } | ||
| 2261 | void popFront() { _impl.popFront(); } | ||
| 2262 | @property CustomString save() { return this; } | ||
| 2263 | } | ||
| 2264 | |||
| 2265 | // If issue 7992 occurs, this will throw an exception from calling | ||
| 2266 | // popFront() on an empty range. | ||
| 2267 | auto r = find(CustomString("a"), CustomString("b")); | ||
| 2268 | assert(r.empty); | ||
| 2269 | } | ||
| 2270 | |||
| 2271 | /** | ||
| 2272 | Finds two or more $(D needles) into a $(D haystack). The predicate $(D | ||
| 2273 | pred) is used throughout to compare elements. By default, elements are | ||
| 2274 | compared for equality. | ||
| 2275 | |||
| 2276 | Params: | ||
| 2277 | |||
| 2278 | pred = The predicate to use for comparing elements. | ||
| 2279 | |||
| 2280 | haystack = The target of the search. Must be an input range. | ||
| 2281 | If any of $(D needles) is a range with elements comparable to | ||
| 2282 | elements in $(D haystack), then $(D haystack) must be a | ||
| 2283 | $(REF_ALTTEXT forward range, isForwardRange, std,range,primitives) | ||
| 2284 | such that the search can backtrack. | ||
| 2285 | |||
| 2286 | needles = One or more items to search for. Each of $(D needles) must | ||
| 2287 | be either comparable to one element in $(D haystack), or be itself a | ||
| 2288 | forward range with elements comparable with elements in | ||
| 2289 | $(D haystack). | ||
| 2290 | |||
| 2291 | Returns: | ||
| 2292 | |||
| 2293 | A tuple containing $(D haystack) positioned to match one of the | ||
| 2294 | needles and also the 1-based index of the matching element in $(D | ||
| 2295 | needles) (0 if none of $(D needles) matched, 1 if $(D needles[0]) | ||
| 2296 | matched, 2 if $(D needles[1]) matched...). The first needle to be found | ||
| 2297 | will be the one that matches. If multiple needles are found at the | ||
| 2298 | same spot in the range, then the shortest one is the one which matches | ||
| 2299 | (if multiple needles of the same length are found at the same spot (e.g | ||
| 2300 | $(D "a") and $(D 'a')), then the left-most of them in the argument list | ||
| 2301 | matches). | ||
| 2302 | |||
| 2303 | The relationship between $(D haystack) and $(D needles) simply means | ||
| 2304 | that one can e.g. search for individual $(D int)s or arrays of $(D | ||
| 2305 | int)s in an array of $(D int)s. In addition, if elements are | ||
| 2306 | individually comparable, searches of heterogeneous types are allowed | ||
| 2307 | as well: a $(D double[]) can be searched for an $(D int) or a $(D | ||
| 2308 | short[]), and conversely a $(D long) can be searched for a $(D float) | ||
| 2309 | or a $(D double[]). This makes for efficient searches without the need | ||
| 2310 | to coerce one side of the comparison into the other's side type. | ||
| 2311 | |||
| 2312 | The complexity of the search is $(BIGOH haystack.length * | ||
| 2313 | max(needles.length)). (For needles that are individual items, length | ||
| 2314 | is considered to be 1.) The strategy used in searching several | ||
| 2315 | subranges at once maximizes cache usage by moving in $(D haystack) as | ||
| 2316 | few times as possible. | ||
| 2317 | */ | ||
| 2318 | Tuple!(Range, size_t) find(alias pred = "a == b", Range, Ranges...) | ||
| 2319 | (Range haystack, Ranges needles) | ||
| 2320 | if (Ranges.length > 1 && is(typeof(startsWith!pred(haystack, needles)))) | ||
| 2321 | { | ||
| 2322 | for (;; haystack.popFront()) | ||
| 2323 | { | ||
| 2324 | size_t r = startsWith!pred(haystack, needles); | ||
| 2325 | if (r || haystack.empty) | ||
| 2326 | { | ||
| 2327 | return tuple(haystack, r); | ||
| 2328 | } | ||
| 2329 | } | ||
| 2330 | } | ||
| 2331 | |||
| 2332 | /// | ||
| 2333 | @safe unittest | ||
| 2334 | { | ||
| 2335 | import std.typecons : tuple; | ||
| 2336 | int[] a = [ 1, 4, 2, 3 ]; | ||
| 2337 | assert(find(a, 4) == [ 4, 2, 3 ]); | ||
| 2338 | assert(find(a, [ 1, 4 ]) == [ 1, 4, 2, 3 ]); | ||
| 2339 | assert(find(a, [ 1, 3 ], 4) == tuple([ 4, 2, 3 ], 2)); | ||
| 2340 | // Mixed types allowed if comparable | ||
| 2341 | assert(find(a, 5, [ 1.2, 3.5 ], 2.0) == tuple([ 2, 3 ], 3)); | ||
| 2342 | } | ||
| 2343 | |||
| 2344 | @safe unittest | ||
| 2345 | { | ||
| 2346 | auto s1 = "Mary has a little lamb"; | ||
| 2347 | assert(find(s1, "has a", "has an") == tuple("has a little lamb", 1)); | ||
| 2348 | assert(find(s1, 't', "has a", "has an") == tuple("has a little lamb", 2)); | ||
| 2349 | assert(find(s1, 't', "has a", 'y', "has an") == tuple("y has a little lamb", 3)); | ||
| 2350 | assert(find("abc", "bc").length == 2); | ||
| 2351 | } | ||
| 2352 | |||
| 2353 | @safe unittest | ||
| 2354 | { | ||
| 2355 | import std.algorithm.internal : rndstuff; | ||
| 2356 | import std.meta : AliasSeq; | ||
| 2357 | import std.uni : toUpper; | ||
| 2358 | |||
| 2359 | int[] a = [ 1, 2, 3 ]; | ||
| 2360 | assert(find(a, 5).empty); | ||
| 2361 | assert(find(a, 2) == [2, 3]); | ||
| 2362 | |||
| 2363 | foreach (T; AliasSeq!(int, double)) | ||
| 2364 | { | ||
| 2365 | auto b = rndstuff!(T)(); | ||
| 2366 | if (!b.length) continue; | ||
| 2367 | b[$ / 2] = 200; | ||
| 2368 | b[$ / 4] = 200; | ||
| 2369 | assert(find(b, 200).length == b.length - b.length / 4); | ||
| 2370 | } | ||
| 2371 | |||
| 2372 | // Case-insensitive find of a string | ||
| 2373 | string[] s = [ "Hello", "world", "!" ]; | ||
| 2374 | assert(find!("toUpper(a) == toUpper(b)")(s, "hello").length == 3); | ||
| 2375 | |||
| 2376 | static bool f(string a, string b) { return toUpper(a) == toUpper(b); } | ||
| 2377 | assert(find!(f)(s, "hello").length == 3); | ||
| 2378 | } | ||
| 2379 | |||
| 2380 | @safe unittest | ||
| 2381 | { | ||
| 2382 | import std.algorithm.comparison : equal; | ||
| 2383 | import std.algorithm.internal : rndstuff; | ||
| 2384 | import std.meta : AliasSeq; | ||
| 2385 | import std.range : retro; | ||
| 2386 | |||
| 2387 | int[] a = [ 1, 2, 3, 2, 6 ]; | ||
| 2388 | assert(find(retro(a), 5).empty); | ||
| 2389 | assert(equal(find(retro(a), 2), [ 2, 3, 2, 1 ][])); | ||
| 2390 | |||
| 2391 | foreach (T; AliasSeq!(int, double)) | ||
| 2392 | { | ||
| 2393 | auto b = rndstuff!(T)(); | ||
| 2394 | if (!b.length) continue; | ||
| 2395 | b[$ / 2] = 200; | ||
| 2396 | b[$ / 4] = 200; | ||
| 2397 | assert(find(retro(b), 200).length == | ||
| 2398 | b.length - (b.length - 1) / 2); | ||
| 2399 | } | ||
| 2400 | } | ||
| 2401 | |||
| 2402 | @safe unittest | ||
| 2403 | { | ||
| 2404 | import std.algorithm.comparison : equal; | ||
| 2405 | import std.internal.test.dummyrange; | ||
| 2406 | |||
| 2407 | int[] a = [ -1, 0, 1, 2, 3, 4, 5 ]; | ||
| 2408 | int[] b = [ 1, 2, 3 ]; | ||
| 2409 | assert(find(a, b) == [ 1, 2, 3, 4, 5 ]); | ||
| 2410 | assert(find(b, a).empty); | ||
| 2411 | |||
| 2412 | foreach (DummyType; AllDummyRanges) | ||
| 2413 | { | ||
| 2414 | DummyType d; | ||
| 2415 | auto findRes = find(d, 5); | ||
| 2416 | assert(equal(findRes, [5,6,7,8,9,10])); | ||
| 2417 | } | ||
| 2418 | } | ||
| 2419 | |||
| 2420 | /** | ||
| 2421 | * Finds $(D needle) in $(D haystack) efficiently using the | ||
| 2422 | * $(LINK2 https://en.wikipedia.org/wiki/Boyer%E2%80%93Moore_string_search_algorithm, | ||
| 2423 | * Boyer-Moore) method. | ||
| 2424 | * | ||
| 2425 | * Params: | ||
| 2426 | * haystack = A random-access range with length and slicing. | ||
| 2427 | * needle = A $(LREF BoyerMooreFinder). | ||
| 2428 | * | ||
| 2429 | * Returns: | ||
| 2430 | * $(D haystack) advanced such that $(D needle) is a prefix of it (if no | ||
| 2431 | * such position exists, returns $(D haystack) advanced to termination). | ||
| 2432 | */ | ||
| 2433 | RandomAccessRange find(RandomAccessRange, alias pred, InputRange)( | ||
| 2434 | RandomAccessRange haystack, scope BoyerMooreFinder!(pred, InputRange) needle) | ||
| 2435 | { | ||
| 2436 | return needle.beFound(haystack); | ||
| 2437 | } | ||
| 2438 | |||
| 2439 | @safe unittest | ||
| 2440 | { | ||
| 2441 | string h = "/homes/aalexand/d/dmd/bin/../lib/libphobos.a(dmain2.o)"~ | ||
| 2442 | "(.gnu.linkonce.tmain+0x74): In function `main' undefined reference"~ | ||
| 2443 | " to `_Dmain':"; | ||
| 2444 | string[] ns = ["libphobos", "function", " undefined", "`", ":"]; | ||
| 2445 | foreach (n ; ns) | ||
| 2446 | { | ||
| 2447 | auto p = find(h, boyerMooreFinder(n)); | ||
| 2448 | assert(!p.empty); | ||
| 2449 | } | ||
| 2450 | } | ||
| 2451 | |||
| 2452 | /// | ||
| 2453 | @safe unittest | ||
| 2454 | { | ||
| 2455 | import std.range.primitives : empty; | ||
| 2456 | int[] a = [ -1, 0, 1, 2, 3, 4, 5 ]; | ||
| 2457 | int[] b = [ 1, 2, 3 ]; | ||
| 2458 | |||
| 2459 | assert(find(a, boyerMooreFinder(b)) == [ 1, 2, 3, 4, 5 ]); | ||
| 2460 | assert(find(b, boyerMooreFinder(a)).empty); | ||
| 2461 | } | ||
| 2462 | |||
| 2463 | @safe unittest | ||
| 2464 | { | ||
| 2465 | auto bm = boyerMooreFinder("for"); | ||
| 2466 | auto match = find("Moor", bm); | ||
| 2467 | assert(match.empty); | ||
| 2468 | } | ||
| 2469 | |||
| 2470 | // canFind | ||
| 2471 | /++ | ||
| 2472 | Convenience function. Like find, but only returns whether or not the search | ||
| 2473 | was successful. | ||
| 2474 | |||
| 2475 | See_Also: | ||
| 2476 | $(LREF among) for checking a value against multiple possibilities. | ||
| 2477 | +/ | ||
| 2478 | template canFind(alias pred="a == b") | ||
| 2479 | { | ||
| 2480 | import std.meta : allSatisfy; | ||
| 2481 | |||
| 2482 | /++ | ||
| 2483 | Returns $(D true) if and only if any value $(D v) found in the | ||
| 2484 | input range $(D range) satisfies the predicate $(D pred). | ||
| 2485 | Performs (at most) $(BIGOH haystack.length) evaluations of $(D pred). | ||
| 2486 | +/ | ||
| 2487 | bool canFind(Range)(Range haystack) | ||
| 2488 | if (is(typeof(find!pred(haystack)))) | ||
| 2489 | { | ||
| 2490 | return any!pred(haystack); | ||
| 2491 | } | ||
| 2492 | |||
| 2493 | /++ | ||
| 2494 | Returns $(D true) if and only if $(D needle) can be found in $(D | ||
| 2495 | range). Performs $(BIGOH haystack.length) evaluations of $(D pred). | ||
| 2496 | +/ | ||
| 2497 | bool canFind(Range, Element)(Range haystack, scope Element needle) | ||
| 2498 | if (is(typeof(find!pred(haystack, needle)))) | ||
| 2499 | { | ||
| 2500 | return !find!pred(haystack, needle).empty; | ||
| 2501 | } | ||
| 2502 | |||
| 2503 | /++ | ||
| 2504 | Returns the 1-based index of the first needle found in $(D haystack). If no | ||
| 2505 | needle is found, then $(D 0) is returned. | ||
| 2506 | |||
| 2507 | So, if used directly in the condition of an if statement or loop, the result | ||
| 2508 | will be $(D true) if one of the needles is found and $(D false) if none are | ||
| 2509 | found, whereas if the result is used elsewhere, it can either be cast to | ||
| 2510 | $(D bool) for the same effect or used to get which needle was found first | ||
| 2511 | without having to deal with the tuple that $(D LREF find) returns for the | ||
| 2512 | same operation. | ||
| 2513 | +/ | ||
| 2514 | size_t canFind(Range, Ranges...)(Range haystack, scope Ranges needles) | ||
| 2515 | if (Ranges.length > 1 && | ||
| 2516 | allSatisfy!(isForwardRange, Ranges) && | ||
| 2517 | is(typeof(find!pred(haystack, needles)))) | ||
| 2518 | { | ||
| 2519 | return find!pred(haystack, needles)[1]; | ||
| 2520 | } | ||
| 2521 | } | ||
| 2522 | |||
| 2523 | /// | ||
| 2524 | @safe unittest | ||
| 2525 | { | ||
| 2526 | assert(canFind([0, 1, 2, 3], 2) == true); | ||
| 2527 | assert(canFind([0, 1, 2, 3], [1, 2], [2, 3])); | ||
| 2528 | assert(canFind([0, 1, 2, 3], [1, 2], [2, 3]) == 1); | ||
| 2529 | assert(canFind([0, 1, 2, 3], [1, 7], [2, 3])); | ||
| 2530 | assert(canFind([0, 1, 2, 3], [1, 7], [2, 3]) == 2); | ||
| 2531 | |||
| 2532 | assert(canFind([0, 1, 2, 3], 4) == false); | ||
| 2533 | assert(!canFind([0, 1, 2, 3], [1, 3], [2, 4])); | ||
| 2534 | assert(canFind([0, 1, 2, 3], [1, 3], [2, 4]) == 0); | ||
| 2535 | } | ||
| 2536 | |||
| 2537 | /** | ||
| 2538 | * Example using a custom predicate. | ||
| 2539 | * Note that the needle appears as the second argument of the predicate. | ||
| 2540 | */ | ||
| 2541 | @safe unittest | ||
| 2542 | { | ||
| 2543 | auto words = [ | ||
| 2544 | "apple", | ||
| 2545 | "beeswax", | ||
| 2546 | "cardboard" | ||
| 2547 | ]; | ||
| 2548 | assert(!canFind(words, "bees")); | ||
| 2549 | assert( canFind!((string a, string b) => a.startsWith(b))(words, "bees")); | ||
| 2550 | } | ||
| 2551 | |||
| 2552 | @safe unittest | ||
| 2553 | { | ||
| 2554 | import std.algorithm.internal : rndstuff; | ||
| 2555 | |||
| 2556 | auto a = rndstuff!(int)(); | ||
| 2557 | if (a.length) | ||
| 2558 | { | ||
| 2559 | auto b = a[a.length / 2]; | ||
| 2560 | assert(canFind(a, b)); | ||
| 2561 | } | ||
| 2562 | } | ||
| 2563 | |||
| 2564 | @safe unittest | ||
| 2565 | { | ||
| 2566 | import std.algorithm.comparison : equal; | ||
| 2567 | assert(equal!(canFind!"a < b")([[1, 2, 3], [7, 8, 9]], [2, 8])); | ||
| 2568 | } | ||
| 2569 | |||
| 2570 | // findAdjacent | ||
| 2571 | /** | ||
| 2572 | Advances $(D r) until it finds the first two adjacent elements $(D a), | ||
| 2573 | $(D b) that satisfy $(D pred(a, b)). Performs $(BIGOH r.length) | ||
| 2574 | evaluations of $(D pred). | ||
| 2575 | |||
| 2576 | Params: | ||
| 2577 | pred = The predicate to satisfy. | ||
| 2578 | r = A $(REF_ALTTEXT forward range, isForwardRange, std,range,primitives) to | ||
| 2579 | search in. | ||
| 2580 | |||
| 2581 | Returns: | ||
| 2582 | $(D r) advanced to the first occurrence of two adjacent elements that satisfy | ||
| 2583 | the given predicate. If there are no such two elements, returns $(D r) advanced | ||
| 2584 | until empty. | ||
| 2585 | |||
| 2586 | See_Also: | ||
| 2587 | $(HTTP sgi.com/tech/stl/adjacent_find.html, STL's adjacent_find) | ||
| 2588 | */ | ||
| 2589 | Range findAdjacent(alias pred = "a == b", Range)(Range r) | ||
| 2590 | if (isForwardRange!(Range)) | ||
| 2591 | { | ||
| 2592 | auto ahead = r.save; | ||
| 2593 | if (!ahead.empty) | ||
| 2594 | { | ||
| 2595 | for (ahead.popFront(); !ahead.empty; r.popFront(), ahead.popFront()) | ||
| 2596 | { | ||
| 2597 | if (binaryFun!(pred)(r.front, ahead.front)) return r; | ||
| 2598 | } | ||
| 2599 | } | ||
| 2600 | static if (!isInfinite!Range) | ||
| 2601 | return ahead; | ||
| 2602 | } | ||
| 2603 | |||
| 2604 | /// | ||
| 2605 | @safe unittest | ||
| 2606 | { | ||
| 2607 | int[] a = [ 11, 10, 10, 9, 8, 8, 7, 8, 9 ]; | ||
| 2608 | auto r = findAdjacent(a); | ||
| 2609 | assert(r == [ 10, 10, 9, 8, 8, 7, 8, 9 ]); | ||
| 2610 | auto p = findAdjacent!("a < b")(a); | ||
| 2611 | assert(p == [ 7, 8, 9 ]); | ||
| 2612 | |||
| 2613 | } | ||
| 2614 | |||
| 2615 | @safe unittest | ||
| 2616 | { | ||
| 2617 | import std.algorithm.comparison : equal; | ||
| 2618 | import std.internal.test.dummyrange; | ||
| 2619 | import std.range; | ||
| 2620 | |||
| 2621 | int[] a = [ 11, 10, 10, 9, 8, 8, 7, 8, 9 ]; | ||
| 2622 | auto p = findAdjacent(a); | ||
| 2623 | assert(p == [10, 10, 9, 8, 8, 7, 8, 9 ]); | ||
| 2624 | p = findAdjacent!("a < b")(a); | ||
| 2625 | assert(p == [7, 8, 9]); | ||
| 2626 | // empty | ||
| 2627 | a = []; | ||
| 2628 | p = findAdjacent(a); | ||
| 2629 | assert(p.empty); | ||
| 2630 | // not found | ||
| 2631 | a = [ 1, 2, 3, 4, 5 ]; | ||
| 2632 | p = findAdjacent(a); | ||
| 2633 | assert(p.empty); | ||
| 2634 | p = findAdjacent!"a > b"(a); | ||
| 2635 | assert(p.empty); | ||
| 2636 | ReferenceForwardRange!int rfr = new ReferenceForwardRange!int([1, 2, 3, 2, 2, 3]); | ||
| 2637 | assert(equal(findAdjacent(rfr), [2, 2, 3])); | ||
| 2638 | |||
| 2639 | // Issue 9350 | ||
| 2640 | assert(!repeat(1).findAdjacent().empty); | ||
| 2641 | } | ||
| 2642 | |||
| 2643 | // findAmong | ||
| 2644 | /** | ||
| 2645 | Searches the given range for an element that matches one of the given choices. | ||
| 2646 | |||
| 2647 | Advances $(D seq) by calling $(D seq.popFront) until either | ||
| 2648 | $(D find!(pred)(choices, seq.front)) is $(D true), or $(D seq) becomes empty. | ||
| 2649 | Performs $(BIGOH seq.length * choices.length) evaluations of $(D pred). | ||
| 2650 | |||
| 2651 | Params: | ||
| 2652 | pred = The predicate to use for determining a match. | ||
| 2653 | seq = The $(REF_ALTTEXT input range, isInputRange, std,range,primitives) to | ||
| 2654 | search. | ||
| 2655 | choices = A $(REF_ALTTEXT forward range, isForwardRange, std,range,primitives) | ||
| 2656 | of possible choices. | ||
| 2657 | |||
| 2658 | Returns: | ||
| 2659 | $(D seq) advanced to the first matching element, or until empty if there are no | ||
| 2660 | matching elements. | ||
| 2661 | |||
| 2662 | See_Also: | ||
| 2663 | $(HTTP sgi.com/tech/stl/find_first_of.html, STL's find_first_of) | ||
| 2664 | */ | ||
| 2665 | InputRange findAmong(alias pred = "a == b", InputRange, ForwardRange)( | ||
| 2666 | InputRange seq, ForwardRange choices) | ||
| 2667 | if (isInputRange!InputRange && isForwardRange!ForwardRange) | ||
| 2668 | { | ||
| 2669 | for (; !seq.empty && find!pred(choices, seq.front).empty; seq.popFront()) | ||
| 2670 | { | ||
| 2671 | } | ||
| 2672 | return seq; | ||
| 2673 | } | ||
| 2674 | |||
| 2675 | /// | ||
| 2676 | @safe unittest | ||
| 2677 | { | ||
| 2678 | int[] a = [ -1, 0, 1, 2, 3, 4, 5 ]; | ||
| 2679 | int[] b = [ 3, 1, 2 ]; | ||
| 2680 | assert(findAmong(a, b) == a[2 .. $]); | ||
| 2681 | } | ||
| 2682 | |||
| 2683 | @safe unittest | ||
| 2684 | { | ||
| 2685 | int[] a = [ -1, 0, 2, 1, 2, 3, 4, 5 ]; | ||
| 2686 | int[] b = [ 1, 2, 3 ]; | ||
| 2687 | assert(findAmong(a, b) == [2, 1, 2, 3, 4, 5 ]); | ||
| 2688 | assert(findAmong(b, [ 4, 6, 7 ][]).empty); | ||
| 2689 | assert(findAmong!("a == b")(a, b).length == a.length - 2); | ||
| 2690 | assert(findAmong!("a == b")(b, [ 4, 6, 7 ][]).empty); | ||
| 2691 | } | ||
| 2692 | |||
| 2693 | // findSkip | ||
| 2694 | /** | ||
| 2695 | * Finds $(D needle) in $(D haystack) and positions $(D haystack) | ||
| 2696 | * right after the first occurrence of $(D needle). | ||
| 2697 | * | ||
| 2698 | * Params: | ||
| 2699 | * haystack = The | ||
| 2700 | * $(REF_ALTTEXT forward range, isForwardRange, std,range,primitives) to search | ||
| 2701 | * in. | ||
| 2702 | * needle = The | ||
| 2703 | * $(REF_ALTTEXT forward range, isForwardRange, std,range,primitives) to search | ||
| 2704 | * for. | ||
| 2705 | * | ||
| 2706 | * Returns: $(D true) if the needle was found, in which case $(D haystack) is | ||
| 2707 | * positioned after the end of the first occurrence of $(D needle); otherwise | ||
| 2708 | * $(D false), leaving $(D haystack) untouched. | ||
| 2709 | */ | ||
| 2710 | bool findSkip(alias pred = "a == b", R1, R2)(ref R1 haystack, R2 needle) | ||
| 2711 | if (isForwardRange!R1 && isForwardRange!R2 | ||
| 2712 | && is(typeof(binaryFun!pred(haystack.front, needle.front)))) | ||
| 2713 | { | ||
| 2714 | auto parts = findSplit!pred(haystack, needle); | ||
| 2715 | if (parts[1].empty) return false; | ||
| 2716 | // found | ||
| 2717 | haystack = parts[2]; | ||
| 2718 | return true; | ||
| 2719 | } | ||
| 2720 | |||
| 2721 | /// | ||
| 2722 | @safe unittest | ||
| 2723 | { | ||
| 2724 | import std.range.primitives : empty; | ||
| 2725 | // Needle is found; s is replaced by the substring following the first | ||
| 2726 | // occurrence of the needle. | ||
| 2727 | string s = "abcdef"; | ||
| 2728 | assert(findSkip(s, "cd") && s == "ef"); | ||
| 2729 | |||
| 2730 | // Needle is not found; s is left untouched. | ||
| 2731 | s = "abcdef"; | ||
| 2732 | assert(!findSkip(s, "cxd") && s == "abcdef"); | ||
| 2733 | |||
| 2734 | // If the needle occurs at the end of the range, the range is left empty. | ||
| 2735 | s = "abcdef"; | ||
| 2736 | assert(findSkip(s, "def") && s.empty); | ||
| 2737 | } | ||
| 2738 | |||
| 2739 | /** | ||
| 2740 | These functions find the first occurrence of `needle` in `haystack` and then | ||
| 2741 | split `haystack` as follows. | ||
| 2742 | |||
| 2743 | `findSplit` returns a tuple `result` containing $(I three) ranges. `result[0]` | ||
| 2744 | is the portion of `haystack` before `needle`, `result[1]` is the portion of | ||
| 2745 | `haystack` that matches `needle`, and `result[2]` is the portion of `haystack` | ||
| 2746 | after the match. If `needle` was not found, `result[0]` comprehends `haystack` | ||
| 2747 | entirely and `result[1]` and `result[2]` are empty. | ||
| 2748 | |||
| 2749 | `findSplitBefore` returns a tuple `result` containing two ranges. `result[0]` is | ||
| 2750 | the portion of `haystack` before `needle`, and `result[1]` is the balance of | ||
| 2751 | `haystack` starting with the match. If `needle` was not found, `result[0]` | ||
| 2752 | comprehends `haystack` entirely and `result[1]` is empty. | ||
| 2753 | |||
| 2754 | `findSplitAfter` returns a tuple `result` containing two ranges. | ||
| 2755 | `result[0]` is the portion of `haystack` up to and including the | ||
| 2756 | match, and `result[1]` is the balance of `haystack` starting | ||
| 2757 | after the match. If `needle` was not found, `result[0]` is empty | ||
| 2758 | and `result[1]` is `haystack`. | ||
| 2759 | |||
| 2760 | In all cases, the concatenation of the returned ranges spans the | ||
| 2761 | entire `haystack`. | ||
| 2762 | |||
| 2763 | If `haystack` is a random-access range, all three components of the tuple have | ||
| 2764 | the same type as `haystack`. Otherwise, `haystack` must be a | ||
| 2765 | $(REF_ALTTEXT forward range, isForwardRange, std,range,primitives) and | ||
| 2766 | the type of `result[0]` and `result[1]` is the same as $(REF takeExactly, | ||
| 2767 | std,range). | ||
| 2768 | |||
| 2769 | Params: | ||
| 2770 | pred = Predicate to use for comparing needle against haystack. | ||
| 2771 | haystack = The range to search. | ||
| 2772 | needle = What to look for. | ||
| 2773 | |||
| 2774 | Returns: | ||
| 2775 | |||
| 2776 | A sub-type of `Tuple!()` of the split portions of `haystack` (see above for | ||
| 2777 | details). This sub-type of `Tuple!()` has `opCast` defined for `bool`. This | ||
| 2778 | `opCast` returns `true` when the separating `needle` was found | ||
| 2779 | (`!result[1].empty`) and `false` otherwise. This enables the convenient idiom | ||
| 2780 | shown in the following example. | ||
| 2781 | |||
| 2782 | Example: | ||
| 2783 | --- | ||
| 2784 | if (const split = haystack.findSplit(needle)) | ||
| 2785 | { | ||
| 2786 | doSomethingWithSplit(split); | ||
| 2787 | } | ||
| 2788 | --- | ||
| 2789 | */ | ||
| 2790 | auto findSplit(alias pred = "a == b", R1, R2)(R1 haystack, R2 needle) | ||
| 2791 | if (isForwardRange!R1 && isForwardRange!R2) | ||
| 2792 | { | ||
| 2793 | static struct Result(S1, S2) if (isForwardRange!S1 && | ||
| 2794 | isForwardRange!S2) | ||
| 2795 | { | ||
| 2796 | this(S1 pre, S1 separator, S2 post) | ||
| 2797 | { | ||
| 2798 | asTuple = typeof(asTuple)(pre, separator, post); | ||
| 2799 | } | ||
| 2800 | void opAssign(typeof(asTuple) rhs) | ||
| 2801 | { | ||
| 2802 | asTuple = rhs; | ||
| 2803 | } | ||
| 2804 | Tuple!(S1, S1, S2) asTuple; | ||
| 2805 | bool opCast(T : bool)() | ||
| 2806 | { | ||
| 2807 | return !asTuple[1].empty; | ||
| 2808 | } | ||
| 2809 | alias asTuple this; | ||
| 2810 | } | ||
| 2811 | |||
| 2812 | static if (isSomeString!R1 && isSomeString!R2 | ||
| 2813 | || (isRandomAccessRange!R1 && hasSlicing!R1 && hasLength!R1 && hasLength!R2)) | ||
| 2814 | { | ||
| 2815 | auto balance = find!pred(haystack, needle); | ||
| 2816 | immutable pos1 = haystack.length - balance.length; | ||
| 2817 | immutable pos2 = balance.empty ? pos1 : pos1 + needle.length; | ||
| 2818 | return Result!(typeof(haystack[0 .. pos1]), | ||
| 2819 | typeof(haystack[pos2 .. haystack.length]))(haystack[0 .. pos1], | ||
| 2820 | haystack[pos1 .. pos2], | ||
| 2821 | haystack[pos2 .. haystack.length]); | ||
| 2822 | } | ||
| 2823 | else | ||
| 2824 | { | ||
| 2825 | import std.range : takeExactly; | ||
| 2826 | auto original = haystack.save; | ||
| 2827 | auto h = haystack.save; | ||
| 2828 | auto n = needle.save; | ||
| 2829 | size_t pos1, pos2; | ||
| 2830 | while (!n.empty && !h.empty) | ||
| 2831 | { | ||
| 2832 | if (binaryFun!pred(h.front, n.front)) | ||
| 2833 | { | ||
| 2834 | h.popFront(); | ||
| 2835 | n.popFront(); | ||
| 2836 | ++pos2; | ||
| 2837 | } | ||
| 2838 | else | ||
| 2839 | { | ||
| 2840 | haystack.popFront(); | ||
| 2841 | n = needle.save; | ||
| 2842 | h = haystack.save; | ||
| 2843 | pos2 = ++pos1; | ||
| 2844 | } | ||
| 2845 | } | ||
| 2846 | return Result!(typeof(takeExactly(original, pos1)), | ||
| 2847 | typeof(h))(takeExactly(original, pos1), | ||
| 2848 | takeExactly(haystack, pos2 - pos1), | ||
| 2849 | h); | ||
| 2850 | } | ||
| 2851 | } | ||
| 2852 | |||
| 2853 | /// Ditto | ||
| 2854 | auto findSplitBefore(alias pred = "a == b", R1, R2)(R1 haystack, R2 needle) | ||
| 2855 | if (isForwardRange!R1 && isForwardRange!R2) | ||
| 2856 | { | ||
| 2857 | static struct Result(S1, S2) if (isForwardRange!S1 && | ||
| 2858 | isForwardRange!S2) | ||
| 2859 | { | ||
| 2860 | this(S1 pre, S2 post) | ||
| 2861 | { | ||
| 2862 | asTuple = typeof(asTuple)(pre, post); | ||
| 2863 | } | ||
| 2864 | void opAssign(typeof(asTuple) rhs) | ||
| 2865 | { | ||
| 2866 | asTuple = rhs; | ||
| 2867 | } | ||
| 2868 | Tuple!(S1, S2) asTuple; | ||
| 2869 | bool opCast(T : bool)() | ||
| 2870 | { | ||
| 2871 | return !asTuple[0].empty; | ||
| 2872 | } | ||
| 2873 | alias asTuple this; | ||
| 2874 | } | ||
| 2875 | |||
| 2876 | static if (isSomeString!R1 && isSomeString!R2 | ||
| 2877 | || (isRandomAccessRange!R1 && hasLength!R1 && hasSlicing!R1 && hasLength!R2)) | ||
| 2878 | { | ||
| 2879 | auto balance = find!pred(haystack, needle); | ||
| 2880 | immutable pos = haystack.length - balance.length; | ||
| 2881 | return Result!(typeof(haystack[0 .. pos]), | ||
| 2882 | typeof(haystack[pos .. haystack.length]))(haystack[0 .. pos], | ||
| 2883 | haystack[pos .. haystack.length]); | ||
| 2884 | } | ||
| 2885 | else | ||
| 2886 | { | ||
| 2887 | import std.range : takeExactly; | ||
| 2888 | auto original = haystack.save; | ||
| 2889 | auto h = haystack.save; | ||
| 2890 | auto n = needle.save; | ||
| 2891 | size_t pos; | ||
| 2892 | while (!n.empty && !h.empty) | ||
| 2893 | { | ||
| 2894 | if (binaryFun!pred(h.front, n.front)) | ||
| 2895 | { | ||
| 2896 | h.popFront(); | ||
| 2897 | n.popFront(); | ||
| 2898 | } | ||
| 2899 | else | ||
| 2900 | { | ||
| 2901 | haystack.popFront(); | ||
| 2902 | n = needle.save; | ||
| 2903 | h = haystack.save; | ||
| 2904 | ++pos; | ||
| 2905 | } | ||
| 2906 | } | ||
| 2907 | return Result!(typeof(takeExactly(original, pos)), | ||
| 2908 | typeof(haystack))(takeExactly(original, pos), | ||
| 2909 | haystack); | ||
| 2910 | } | ||
| 2911 | } | ||
| 2912 | |||
| 2913 | /// Ditto | ||
| 2914 | auto findSplitAfter(alias pred = "a == b", R1, R2)(R1 haystack, R2 needle) | ||
| 2915 | if (isForwardRange!R1 && isForwardRange!R2) | ||
| 2916 | { | ||
| 2917 | static struct Result(S1, S2) if (isForwardRange!S1 && | ||
| 2918 | isForwardRange!S2) | ||
| 2919 | { | ||
| 2920 | this(S1 pre, S2 post) | ||
| 2921 | { | ||
| 2922 | asTuple = typeof(asTuple)(pre, post); | ||
| 2923 | } | ||
| 2924 | void opAssign(typeof(asTuple) rhs) | ||
| 2925 | { | ||
| 2926 | asTuple = rhs; | ||
| 2927 | } | ||
| 2928 | Tuple!(S1, S2) asTuple; | ||
| 2929 | bool opCast(T : bool)() | ||
| 2930 | { | ||
| 2931 | return !asTuple[1].empty; | ||
| 2932 | } | ||
| 2933 | alias asTuple this; | ||
| 2934 | } | ||
| 2935 | |||
| 2936 | static if (isSomeString!R1 && isSomeString!R2 | ||
| 2937 | || isRandomAccessRange!R1 && hasLength!R1 && hasSlicing!R1 && hasLength!R2) | ||
| 2938 | { | ||
| 2939 | auto balance = find!pred(haystack, needle); | ||
| 2940 | immutable pos = balance.empty ? 0 : haystack.length - balance.length + needle.length; | ||
| 2941 | return Result!(typeof(haystack[0 .. pos]), | ||
| 2942 | typeof(haystack[pos .. haystack.length]))(haystack[0 .. pos], | ||
| 2943 | haystack[pos .. haystack.length]); | ||
| 2944 | } | ||
| 2945 | else | ||
| 2946 | { | ||
| 2947 | import std.range : takeExactly; | ||
| 2948 | auto original = haystack.save; | ||
| 2949 | auto h = haystack.save; | ||
| 2950 | auto n = needle.save; | ||
| 2951 | size_t pos1, pos2; | ||
| 2952 | while (!n.empty) | ||
| 2953 | { | ||
| 2954 | if (h.empty) | ||
| 2955 | { | ||
| 2956 | // Failed search | ||
| 2957 | return Result!(typeof(takeExactly(original, 0)), | ||
| 2958 | typeof(original))(takeExactly(original, 0), | ||
| 2959 | original); | ||
| 2960 | } | ||
| 2961 | if (binaryFun!pred(h.front, n.front)) | ||
| 2962 | { | ||
| 2963 | h.popFront(); | ||
| 2964 | n.popFront(); | ||
| 2965 | ++pos2; | ||
| 2966 | } | ||
| 2967 | else | ||
| 2968 | { | ||
| 2969 | haystack.popFront(); | ||
| 2970 | n = needle.save; | ||
| 2971 | h = haystack.save; | ||
| 2972 | pos2 = ++pos1; | ||
| 2973 | } | ||
| 2974 | } | ||
| 2975 | return Result!(typeof(takeExactly(original, pos2)), | ||
| 2976 | typeof(h))(takeExactly(original, pos2), | ||
| 2977 | h); | ||
| 2978 | } | ||
| 2979 | } | ||
| 2980 | |||
| 2981 | /// | ||
| 2982 | @safe pure nothrow unittest | ||
| 2983 | { | ||
| 2984 | import std.range.primitives : empty; | ||
| 2985 | |||
| 2986 | auto a = "Carl Sagan Memorial Station"; | ||
| 2987 | auto r = findSplit(a, "Velikovsky"); | ||
| 2988 | import std.typecons : isTuple; | ||
| 2989 | static assert(isTuple!(typeof(r.asTuple))); | ||
| 2990 | static assert(isTuple!(typeof(r))); | ||
| 2991 | assert(!r); | ||
| 2992 | assert(r[0] == a); | ||
| 2993 | assert(r[1].empty); | ||
| 2994 | assert(r[2].empty); | ||
| 2995 | r = findSplit(a, " "); | ||
| 2996 | assert(r[0] == "Carl"); | ||
| 2997 | assert(r[1] == " "); | ||
| 2998 | assert(r[2] == "Sagan Memorial Station"); | ||
| 2999 | auto r1 = findSplitBefore(a, "Sagan"); | ||
| 3000 | assert(r1); | ||
| 3001 | assert(r1[0] == "Carl "); | ||
| 3002 | assert(r1[1] == "Sagan Memorial Station"); | ||
| 3003 | auto r2 = findSplitAfter(a, "Sagan"); | ||
| 3004 | assert(r2); | ||
| 3005 | assert(r2[0] == "Carl Sagan"); | ||
| 3006 | assert(r2[1] == " Memorial Station"); | ||
| 3007 | } | ||
| 3008 | |||
| 3009 | /// Use $(REF only, std,range) to find single elements: | ||
| 3010 | @safe pure nothrow unittest | ||
| 3011 | { | ||
| 3012 | import std.range : only; | ||
| 3013 | assert([1, 2, 3, 4].findSplitBefore(only(3))[0] == [1, 2]); | ||
| 3014 | } | ||
| 3015 | |||
| 3016 | @safe pure nothrow unittest | ||
| 3017 | { | ||
| 3018 | import std.range.primitives : empty; | ||
| 3019 | |||
| 3020 | auto a = [ 1, 2, 3, 4, 5, 6, 7, 8 ]; | ||
| 3021 | auto r = findSplit(a, [9, 1]); | ||
| 3022 | assert(!r); | ||
| 3023 | assert(r[0] == a); | ||
| 3024 | assert(r[1].empty); | ||
| 3025 | assert(r[2].empty); | ||
| 3026 | r = findSplit(a, [3]); | ||
| 3027 | assert(r); | ||
| 3028 | assert(r[0] == a[0 .. 2]); | ||
| 3029 | assert(r[1] == a[2 .. 3]); | ||
| 3030 | assert(r[2] == a[3 .. $]); | ||
| 3031 | |||
| 3032 | auto r1 = findSplitBefore(a, [9, 1]); | ||
| 3033 | assert(r1); | ||
| 3034 | assert(r1[0] == a); | ||
| 3035 | assert(r1[1].empty); | ||
| 3036 | r1 = findSplitBefore(a, [3, 4]); | ||
| 3037 | assert(r1); | ||
| 3038 | assert(r1[0] == a[0 .. 2]); | ||
| 3039 | assert(r1[1] == a[2 .. $]); | ||
| 3040 | |||
| 3041 | auto r2 = findSplitAfter(a, [9, 1]); | ||
| 3042 | assert(r2); | ||
| 3043 | assert(r2[0].empty); | ||
| 3044 | assert(r2[1] == a); | ||
| 3045 | r2 = findSplitAfter(a, [3, 4]); | ||
| 3046 | assert(r2); | ||
| 3047 | assert(r2[0] == a[0 .. 4]); | ||
| 3048 | assert(r2[1] == a[4 .. $]); | ||
| 3049 | } | ||
| 3050 | |||
| 3051 | @safe pure nothrow unittest | ||
| 3052 | { | ||
| 3053 | import std.algorithm.comparison : equal; | ||
| 3054 | import std.algorithm.iteration : filter; | ||
| 3055 | |||
| 3056 | auto a = [ 1, 2, 3, 4, 5, 6, 7, 8 ]; | ||
| 3057 | auto fwd = filter!"a > 0"(a); | ||
| 3058 | auto r = findSplit(fwd, [9, 1]); | ||
| 3059 | assert(!r); | ||
| 3060 | assert(equal(r[0], a)); | ||
| 3061 | assert(r[1].empty); | ||
| 3062 | assert(r[2].empty); | ||
| 3063 | r = findSplit(fwd, [3]); | ||
| 3064 | assert(r); | ||
| 3065 | assert(equal(r[0], a[0 .. 2])); | ||
| 3066 | assert(equal(r[1], a[2 .. 3])); | ||
| 3067 | assert(equal(r[2], a[3 .. $])); | ||
| 3068 | |||
| 3069 | auto r1 = findSplitBefore(fwd, [9, 1]); | ||
| 3070 | assert(r1); | ||
| 3071 | assert(equal(r1[0], a)); | ||
| 3072 | assert(r1[1].empty); | ||
| 3073 | r1 = findSplitBefore(fwd, [3, 4]); | ||
| 3074 | assert(r1); | ||
| 3075 | assert(equal(r1[0], a[0 .. 2])); | ||
| 3076 | assert(equal(r1[1], a[2 .. $])); | ||
| 3077 | |||
| 3078 | auto r2 = findSplitAfter(fwd, [9, 1]); | ||
| 3079 | assert(r2); | ||
| 3080 | assert(r2[0].empty); | ||
| 3081 | assert(equal(r2[1], a)); | ||
| 3082 | r2 = findSplitAfter(fwd, [3, 4]); | ||
| 3083 | assert(r2); | ||
| 3084 | assert(equal(r2[0], a[0 .. 4])); | ||
| 3085 | assert(equal(r2[1], a[4 .. $])); | ||
| 3086 | } | ||
| 3087 | |||
| 3088 | @safe pure nothrow @nogc unittest | ||
| 3089 | { | ||
| 3090 | auto str = "sep,one,sep,two"; | ||
| 3091 | |||
| 3092 | auto split = str.findSplitAfter(","); | ||
| 3093 | assert(split[0] == "sep,"); | ||
| 3094 | |||
| 3095 | split = split[1].findSplitAfter(","); | ||
| 3096 | assert(split[0] == "one,"); | ||
| 3097 | |||
| 3098 | split = split[1].findSplitBefore(","); | ||
| 3099 | assert(split[0] == "sep"); | ||
| 3100 | } | ||
| 3101 | |||
| 3102 | @safe pure nothrow @nogc unittest | ||
| 3103 | { | ||
| 3104 | auto str = "sep,one,sep,two"; | ||
| 3105 | |||
| 3106 | auto split = str.findSplitBefore(",two"); | ||
| 3107 | assert(split[0] == "sep,one,sep"); | ||
| 3108 | assert(split[1] == ",two"); | ||
| 3109 | |||
| 3110 | split = split[0].findSplitBefore(",sep"); | ||
| 3111 | assert(split[0] == "sep,one"); | ||
| 3112 | assert(split[1] == ",sep"); | ||
| 3113 | |||
| 3114 | split = split[0].findSplitAfter(","); | ||
| 3115 | assert(split[0] == "sep,"); | ||
| 3116 | assert(split[1] == "one"); | ||
| 3117 | } | ||
| 3118 | |||
| 3119 | // minCount | ||
| 3120 | /** | ||
| 3121 | |||
| 3122 | Computes the minimum (respectively maximum) of `range` along with its number of | ||
| 3123 | occurrences. Formally, the minimum is a value `x` in `range` such that $(D | ||
| 3124 | pred(a, x)) is `false` for all values `a` in `range`. Conversely, the maximum is | ||
| 3125 | a value `x` in `range` such that $(D pred(x, a)) is `false` for all values `a` | ||
| 3126 | in `range` (note the swapped arguments to `pred`). | ||
| 3127 | |||
| 3128 | These functions may be used for computing arbitrary extrema by choosing `pred` | ||
| 3129 | appropriately. For corrrect functioning, `pred` must be a strict partial order, | ||
| 3130 | i.e. transitive (if $(D pred(a, b) && pred(b, c)) then $(D pred(a, c))) and | ||
| 3131 | irreflexive ($(D pred(a, a)) is `false`). The $(LUCKY trichotomy property of | ||
| 3132 | inequality) is not required: these algoritms consider elements `a` and `b` equal | ||
| 3133 | (for the purpose of counting) if `pred` puts them in the same equivalence class, | ||
| 3134 | i.e. $(D !pred(a, b) && !pred(b, a)). | ||
| 3135 | |||
| 3136 | Params: | ||
| 3137 | pred = The ordering predicate to use to determine the extremum (minimum | ||
| 3138 | or maximum). | ||
| 3139 | range = The $(REF_ALTTEXT input range, isInputRange, std,range,primitives) to count. | ||
| 3140 | |||
| 3141 | Returns: The minimum, respectively maximum element of a range together with the | ||
| 3142 | number it occurs in the range. | ||
| 3143 | |||
| 3144 | Throws: `Exception` if `range.empty`. | ||
| 3145 | */ | ||
| 3146 | Tuple!(ElementType!Range, size_t) | ||
| 3147 | minCount(alias pred = "a < b", Range)(Range range) | ||
| 3148 | if (isInputRange!Range && !isInfinite!Range && | ||
| 3149 | is(typeof(binaryFun!pred(range.front, range.front)))) | ||
| 3150 | { | ||
| 3151 | import std.algorithm.internal : algoFormat; | ||
| 3152 | import std.exception : enforce; | ||
| 3153 | |||
| 3154 | alias T = ElementType!Range; | ||
| 3155 | alias UT = Unqual!T; | ||
| 3156 | alias RetType = Tuple!(T, size_t); | ||
| 3157 | |||
| 3158 | static assert(is(typeof(RetType(range.front, 1))), | ||
| 3159 | algoFormat("Error: Cannot call minCount on a %s, because it is not possible "~ | ||
| 3160 | "to copy the result value (a %s) into a Tuple.", Range.stringof, T.stringof)); | ||
| 3161 | |||
| 3162 | enforce(!range.empty, "Can't count elements from an empty range"); | ||
| 3163 | size_t occurrences = 1; | ||
| 3164 | |||
| 3165 | static if (isForwardRange!Range) | ||
| 3166 | { | ||
| 3167 | Range least = range.save; | ||
| 3168 | for (range.popFront(); !range.empty; range.popFront()) | ||
| 3169 | { | ||
| 3170 | if (binaryFun!pred(least.front, range.front)) | ||
| 3171 | { | ||
| 3172 | assert(!binaryFun!pred(range.front, least.front), | ||
| 3173 | "min/maxPos: predicate must be a strict partial order."); | ||
| 3174 | continue; | ||
| 3175 | } | ||
| 3176 | if (binaryFun!pred(range.front, least.front)) | ||
| 3177 | { | ||
| 3178 | // change the min | ||
| 3179 | least = range.save; | ||
| 3180 | occurrences = 1; | ||
| 3181 | } | ||
| 3182 | else | ||
| 3183 | ++occurrences; | ||
| 3184 | } | ||
| 3185 | return RetType(least.front, occurrences); | ||
| 3186 | } | ||
| 3187 | else static if (isAssignable!(UT, T) || (!hasElaborateAssign!UT && isAssignable!UT)) | ||
| 3188 | { | ||
| 3189 | UT v = UT.init; | ||
| 3190 | static if (isAssignable!(UT, T)) v = range.front; | ||
| 3191 | else v = cast(UT) range.front; | ||
| 3192 | |||
| 3193 | for (range.popFront(); !range.empty; range.popFront()) | ||
| 3194 | { | ||
| 3195 | if (binaryFun!pred(*cast(T*)&v, range.front)) continue; | ||
| 3196 | if (binaryFun!pred(range.front, *cast(T*)&v)) | ||
| 3197 | { | ||
| 3198 | // change the min | ||
| 3199 | static if (isAssignable!(UT, T)) v = range.front; | ||
| 3200 | else v = cast(UT) range.front; //Safe because !hasElaborateAssign!UT | ||
| 3201 | occurrences = 1; | ||
| 3202 | } | ||
| 3203 | else | ||
| 3204 | ++occurrences; | ||
| 3205 | } | ||
| 3206 | return RetType(*cast(T*)&v, occurrences); | ||
| 3207 | } | ||
| 3208 | else static if (hasLvalueElements!Range) | ||
| 3209 | { | ||
| 3210 | import std.algorithm.internal : addressOf; | ||
| 3211 | T* p = addressOf(range.front); | ||
| 3212 | for (range.popFront(); !range.empty; range.popFront()) | ||
| 3213 | { | ||
| 3214 | if (binaryFun!pred(*p, range.front)) continue; | ||
| 3215 | if (binaryFun!pred(range.front, *p)) | ||
| 3216 | { | ||
| 3217 | // change the min | ||
| 3218 | p = addressOf(range.front); | ||
| 3219 | occurrences = 1; | ||
| 3220 | } | ||
| 3221 | else | ||
| 3222 | ++occurrences; | ||
| 3223 | } | ||
| 3224 | return RetType(*p, occurrences); | ||
| 3225 | } | ||
| 3226 | else | ||
| 3227 | static assert(false, | ||
| 3228 | algoFormat("Sorry, can't find the minCount of a %s: Don't know how "~ | ||
| 3229 | "to keep track of the smallest %s element.", Range.stringof, T.stringof)); | ||
| 3230 | } | ||
| 3231 | |||
| 3232 | /// Ditto | ||
| 3233 | Tuple!(ElementType!Range, size_t) | ||
| 3234 | maxCount(alias pred = "a < b", Range)(Range range) | ||
| 3235 | if (isInputRange!Range && !isInfinite!Range && | ||
| 3236 | is(typeof(binaryFun!pred(range.front, range.front)))) | ||
| 3237 | { | ||
| 3238 | return range.minCount!((a, b) => binaryFun!pred(b, a)); | ||
| 3239 | } | ||
| 3240 | |||
| 3241 | /// | ||
| 3242 | @safe unittest | ||
| 3243 | { | ||
| 3244 | import std.conv : text; | ||
| 3245 | import std.typecons : tuple; | ||
| 3246 | |||
| 3247 | int[] a = [ 2, 3, 4, 1, 2, 4, 1, 1, 2 ]; | ||
| 3248 | // Minimum is 1 and occurs 3 times | ||
| 3249 | assert(a.minCount == tuple(1, 3)); | ||
| 3250 | // Maximum is 4 and occurs 2 times | ||
| 3251 | assert(a.maxCount == tuple(4, 2)); | ||
| 3252 | } | ||
| 3253 | |||
| 3254 | @system unittest | ||
| 3255 | { | ||
| 3256 | import std.conv : text; | ||
| 3257 | import std.exception : assertThrown; | ||
| 3258 | import std.internal.test.dummyrange; | ||
| 3259 | |||
| 3260 | int[][] b = [ [4], [2, 4], [4], [4] ]; | ||
| 3261 | auto c = minCount!("a[0] < b[0]")(b); | ||
| 3262 | assert(c == tuple([2, 4], 1), text(c[0])); | ||
| 3263 | |||
| 3264 | //Test empty range | ||
| 3265 | assertThrown(minCount(b[$..$])); | ||
| 3266 | |||
| 3267 | //test with reference ranges. Test both input and forward. | ||
| 3268 | assert(minCount(new ReferenceInputRange!int([1, 2, 1, 0, 2, 0])) == tuple(0, 2)); | ||
| 3269 | assert(minCount(new ReferenceForwardRange!int([1, 2, 1, 0, 2, 0])) == tuple(0, 2)); | ||
| 3270 | } | ||
| 3271 | |||
| 3272 | @system unittest | ||
| 3273 | { | ||
| 3274 | import std.conv : text; | ||
| 3275 | import std.meta : AliasSeq; | ||
| 3276 | |||
| 3277 | static struct R(T) //input range | ||
| 3278 | { | ||
| 3279 | T[] arr; | ||
| 3280 | alias arr this; | ||
| 3281 | } | ||
| 3282 | |||
| 3283 | immutable a = [ 2, 3, 4, 1, 2, 4, 1, 1, 2 ]; | ||
| 3284 | R!(immutable int) b = R!(immutable int)(a); | ||
| 3285 | |||
| 3286 | assert(minCount(a) == tuple(1, 3)); | ||
| 3287 | assert(minCount(b) == tuple(1, 3)); | ||
| 3288 | assert(minCount!((ref immutable int a, ref immutable int b) => (a > b))(a) == tuple(4, 2)); | ||
| 3289 | assert(minCount!((ref immutable int a, ref immutable int b) => (a > b))(b) == tuple(4, 2)); | ||
| 3290 | |||
| 3291 | immutable(int[])[] c = [ [4], [2, 4], [4], [4] ]; | ||
| 3292 | assert(minCount!("a[0] < b[0]")(c) == tuple([2, 4], 1), text(c[0])); | ||
| 3293 | |||
| 3294 | static struct S1 | ||
| 3295 | { | ||
| 3296 | int i; | ||
| 3297 | } | ||
| 3298 | alias IS1 = immutable(S1); | ||
| 3299 | static assert( isAssignable!S1); | ||
| 3300 | static assert( isAssignable!(S1, IS1)); | ||
| 3301 | |||
| 3302 | static struct S2 | ||
| 3303 | { | ||
| 3304 | int* p; | ||
| 3305 | this(ref immutable int i) immutable {p = &i;} | ||
| 3306 | this(ref int i) {p = &i;} | ||
| 3307 | @property ref inout(int) i() inout {return *p;} | ||
| 3308 | bool opEquals(const S2 other) const {return i == other.i;} | ||
| 3309 | } | ||
| 3310 | alias IS2 = immutable(S2); | ||
| 3311 | static assert( isAssignable!S2); | ||
| 3312 | static assert(!isAssignable!(S2, IS2)); | ||
| 3313 | static assert(!hasElaborateAssign!S2); | ||
| 3314 | |||
| 3315 | static struct S3 | ||
| 3316 | { | ||
| 3317 | int i; | ||
| 3318 | void opAssign(ref S3 other) @disable; | ||
| 3319 | } | ||
| 3320 | static assert(!isAssignable!S3); | ||
| 3321 | |||
| 3322 | foreach (Type; AliasSeq!(S1, IS1, S2, IS2, S3)) | ||
| 3323 | { | ||
| 3324 | static if (is(Type == immutable)) alias V = immutable int; | ||
| 3325 | else alias V = int; | ||
| 3326 | V one = 1, two = 2; | ||
| 3327 | auto r1 = [Type(two), Type(one), Type(one)]; | ||
| 3328 | auto r2 = R!Type(r1); | ||
| 3329 | assert(minCount!"a.i < b.i"(r1) == tuple(Type(one), 2)); | ||
| 3330 | assert(minCount!"a.i < b.i"(r2) == tuple(Type(one), 2)); | ||
| 3331 | assert(one == 1 && two == 2); | ||
| 3332 | } | ||
| 3333 | } | ||
| 3334 | |||
| 3335 | /** | ||
| 3336 | Iterates the passed range and returns the minimal element. | ||
| 3337 | A custom mapping function can be passed to `map`. | ||
| 3338 | In other languages this is sometimes called `argmin`. | ||
| 3339 | |||
| 3340 | Complexity: O(n) | ||
| 3341 | Exactly `n - 1` comparisons are needed. | ||
| 3342 | |||
| 3343 | Params: | ||
| 3344 | map = custom accessor for the comparison key | ||
| 3345 | r = range from which the minimal element will be selected | ||
| 3346 | seed = custom seed to use as initial element | ||
| 3347 | |||
| 3348 | Returns: The minimal element of the passed-in range. | ||
| 3349 | |||
| 3350 | See_Also: | ||
| 3351 | $(REF min, std,algorithm,comparison) | ||
| 3352 | */ | ||
| 3353 | auto minElement(alias map, Range)(Range r) | ||
| 3354 | if (isInputRange!Range && !isInfinite!Range) | ||
| 3355 | { | ||
| 3356 | return extremum!map(r); | ||
| 3357 | } | ||
| 3358 | |||
| 3359 | /// ditto | ||
| 3360 | auto minElement(Range)(Range r) | ||
| 3361 | if (isInputRange!Range && !isInfinite!Range) | ||
| 3362 | { | ||
| 3363 | return extremum(r); | ||
| 3364 | } | ||
| 3365 | |||
| 3366 | /// ditto | ||
| 3367 | auto minElement(alias map, Range, RangeElementType = ElementType!Range) | ||
| 3368 | (Range r, RangeElementType seed) | ||
| 3369 | if (isInputRange!Range && !isInfinite!Range && | ||
| 3370 | !is(CommonType!(ElementType!Range, RangeElementType) == void)) | ||
| 3371 | { | ||
| 3372 | return extremum!map(r, seed); | ||
| 3373 | } | ||
| 3374 | |||
| 3375 | /// ditto | ||
| 3376 | auto minElement(Range, RangeElementType = ElementType!Range) | ||
| 3377 | (Range r, RangeElementType seed) | ||
| 3378 | if (isInputRange!Range && !isInfinite!Range && | ||
| 3379 | !is(CommonType!(ElementType!Range, RangeElementType) == void)) | ||
| 3380 | { | ||
| 3381 | return extremum(r, seed); | ||
| 3382 | } | ||
| 3383 | |||
| 3384 | /// | ||
| 3385 | @safe pure unittest | ||
| 3386 | { | ||
| 3387 | import std.range : enumerate; | ||
| 3388 | import std.typecons : tuple; | ||
| 3389 | |||
| 3390 | assert([2, 1, 4, 3].minElement == 1); | ||
| 3391 | |||
| 3392 | // allows to get the index of an element too | ||
| 3393 | assert([5, 3, 7, 9].enumerate.minElement!"a.value" == tuple(1, 3)); | ||
| 3394 | |||
| 3395 | // any custom accessor can be passed | ||
| 3396 | assert([[0, 4], [1, 2]].minElement!"a[1]" == [1, 2]); | ||
| 3397 | |||
| 3398 | // can be seeded | ||
| 3399 | int[] arr; | ||
| 3400 | assert(arr.minElement(1) == 1); | ||
| 3401 | } | ||
| 3402 | |||
| 3403 | @safe pure unittest | ||
| 3404 | { | ||
| 3405 | import std.range : enumerate, iota; | ||
| 3406 | // supports mapping | ||
| 3407 | assert([3, 4, 5, 1, 2].enumerate.minElement!"a.value" == tuple(3, 1)); | ||
| 3408 | assert([5, 2, 4].enumerate.minElement!"a.value" == tuple(1, 2)); | ||
| 3409 | |||
| 3410 | // forward ranges | ||
| 3411 | assert(iota(1, 5).minElement() == 1); | ||
| 3412 | assert(iota(2, 5).enumerate.minElement!"a.value" == tuple(0, 2)); | ||
| 3413 | |||
| 3414 | // should work with const | ||
| 3415 | const(int)[] immArr = [2, 1, 3]; | ||
| 3416 | assert(immArr.minElement == 1); | ||
| 3417 | |||
| 3418 | // should work with immutable | ||
| 3419 | immutable(int)[] immArr2 = [2, 1, 3]; | ||
| 3420 | assert(immArr2.minElement == 1); | ||
| 3421 | |||
| 3422 | // with strings | ||
| 3423 | assert(["b", "a", "c"].minElement == "a"); | ||
| 3424 | |||
| 3425 | // with all dummy ranges | ||
| 3426 | import std.internal.test.dummyrange; | ||
| 3427 | foreach (DummyType; AllDummyRanges) | ||
| 3428 | { | ||
| 3429 | DummyType d; | ||
| 3430 | assert(d.minElement == 1); | ||
| 3431 | assert(d.minElement!(a => a) == 1); | ||
| 3432 | } | ||
| 3433 | |||
| 3434 | // with empty, but seeded ranges | ||
| 3435 | int[] arr; | ||
| 3436 | assert(arr.minElement(42) == 42); | ||
| 3437 | assert(arr.minElement!(a => a)(42) == 42); | ||
| 3438 | } | ||
| 3439 | |||
| 3440 | @nogc @safe nothrow pure unittest | ||
| 3441 | { | ||
| 3442 | static immutable arr = [7, 3, 4, 2, 1, 8]; | ||
| 3443 | assert(arr.minElement == 1); | ||
| 3444 | |||
| 3445 | static immutable arr2d = [[1, 9], [3, 1], [4, 2]]; | ||
| 3446 | assert(arr2d.minElement!"a[1]" == arr2d[1]); | ||
| 3447 | } | ||
| 3448 | |||
| 3449 | /** | ||
| 3450 | Iterates the passed range and returns the maximal element. | ||
| 3451 | A custom mapping function can be passed to `map`. | ||
| 3452 | In other languages this is sometimes called `argmax`. | ||
| 3453 | |||
| 3454 | Complexity: | ||
| 3455 | Exactly `n - 1` comparisons are needed. | ||
| 3456 | |||
| 3457 | Params: | ||
| 3458 | map = custom accessor for the comparison key | ||
| 3459 | r = range from which the maximum will be selected | ||
| 3460 | seed = custom seed to use as initial element | ||
| 3461 | |||
| 3462 | Returns: The maximal element of the passed-in range. | ||
| 3463 | |||
| 3464 | See_Also: | ||
| 3465 | $(REF max, std,algorithm,comparison) | ||
| 3466 | */ | ||
| 3467 | auto maxElement(alias map, Range)(Range r) | ||
| 3468 | if (isInputRange!Range && !isInfinite!Range) | ||
| 3469 | { | ||
| 3470 | return extremum!(map, "a > b")(r); | ||
| 3471 | } | ||
| 3472 | |||
| 3473 | /// ditto | ||
| 3474 | auto maxElement(Range)(Range r) | ||
| 3475 | if (isInputRange!Range && !isInfinite!Range) | ||
| 3476 | { | ||
| 3477 | return extremum!`a > b`(r); | ||
| 3478 | } | ||
| 3479 | |||
| 3480 | /// ditto | ||
| 3481 | auto maxElement(alias map, Range, RangeElementType = ElementType!Range) | ||
| 3482 | (Range r, RangeElementType seed) | ||
| 3483 | if (isInputRange!Range && !isInfinite!Range && | ||
| 3484 | !is(CommonType!(ElementType!Range, RangeElementType) == void)) | ||
| 3485 | { | ||
| 3486 | return extremum!(map, "a > b")(r, seed); | ||
| 3487 | } | ||
| 3488 | |||
| 3489 | /// ditto | ||
| 3490 | auto maxElement(Range, RangeElementType = ElementType!Range) | ||
| 3491 | (Range r, RangeElementType seed) | ||
| 3492 | if (isInputRange!Range && !isInfinite!Range && | ||
| 3493 | !is(CommonType!(ElementType!Range, RangeElementType) == void)) | ||
| 3494 | { | ||
| 3495 | return extremum!`a > b`(r, seed); | ||
| 3496 | } | ||
| 3497 | |||
| 3498 | /// | ||
| 3499 | @safe pure unittest | ||
| 3500 | { | ||
| 3501 | import std.range : enumerate; | ||
| 3502 | import std.typecons : tuple; | ||
| 3503 | assert([2, 1, 4, 3].maxElement == 4); | ||
| 3504 | |||
| 3505 | // allows to get the index of an element too | ||
| 3506 | assert([2, 1, 4, 3].enumerate.maxElement!"a.value" == tuple(2, 4)); | ||
| 3507 | |||
| 3508 | // any custom accessor can be passed | ||
| 3509 | assert([[0, 4], [1, 2]].maxElement!"a[1]" == [0, 4]); | ||
| 3510 | |||
| 3511 | // can be seeded | ||
| 3512 | int[] arr; | ||
| 3513 | assert(arr.minElement(1) == 1); | ||
| 3514 | } | ||
| 3515 | |||
| 3516 | @safe pure unittest | ||
| 3517 | { | ||
| 3518 | import std.range : enumerate, iota; | ||
| 3519 | |||
| 3520 | // supports mapping | ||
| 3521 | assert([3, 4, 5, 1, 2].enumerate.maxElement!"a.value" == tuple(2, 5)); | ||
| 3522 | assert([5, 2, 4].enumerate.maxElement!"a.value" == tuple(0, 5)); | ||
| 3523 | |||
| 3524 | // forward ranges | ||
| 3525 | assert(iota(1, 5).maxElement() == 4); | ||
| 3526 | assert(iota(2, 5).enumerate.maxElement!"a.value" == tuple(2, 4)); | ||
| 3527 | assert(iota(4, 14).enumerate.maxElement!"a.value" == tuple(9, 13)); | ||
| 3528 | |||
| 3529 | // should work with const | ||
| 3530 | const(int)[] immArr = [2, 3, 1]; | ||
| 3531 | assert(immArr.maxElement == 3); | ||
| 3532 | |||
| 3533 | // should work with immutable | ||
| 3534 | immutable(int)[] immArr2 = [2, 3, 1]; | ||
| 3535 | assert(immArr2.maxElement == 3); | ||
| 3536 | |||
| 3537 | // with strings | ||
| 3538 | assert(["a", "c", "b"].maxElement == "c"); | ||
| 3539 | |||
| 3540 | // with all dummy ranges | ||
| 3541 | import std.internal.test.dummyrange; | ||
| 3542 | foreach (DummyType; AllDummyRanges) | ||
| 3543 | { | ||
| 3544 | DummyType d; | ||
| 3545 | assert(d.maxElement == 10); | ||
| 3546 | assert(d.maxElement!(a => a) == 10); | ||
| 3547 | } | ||
| 3548 | |||
| 3549 | // with empty, but seeded ranges | ||
| 3550 | int[] arr; | ||
| 3551 | assert(arr.maxElement(42) == 42); | ||
| 3552 | assert(arr.maxElement!(a => a)(42) == 42); | ||
| 3553 | |||
| 3554 | } | ||
| 3555 | |||
| 3556 | @nogc @safe nothrow pure unittest | ||
| 3557 | { | ||
| 3558 | static immutable arr = [7, 3, 8, 2, 1, 4]; | ||
| 3559 | assert(arr.maxElement == 8); | ||
| 3560 | |||
| 3561 | static immutable arr2d = [[1, 3], [3, 9], [4, 2]]; | ||
| 3562 | assert(arr2d.maxElement!"a[1]" == arr2d[1]); | ||
| 3563 | } | ||
| 3564 | |||
| 3565 | // minPos | ||
| 3566 | /** | ||
| 3567 | Computes a subrange of `range` starting at the first occurrence of `range`'s | ||
| 3568 | minimum (respectively maximum) and with the same ending as `range`, or the | ||
| 3569 | empty range if `range` itself is empty. | ||
| 3570 | |||
| 3571 | Formally, the minimum is a value `x` in `range` such that $(D pred(a, x)) is | ||
| 3572 | `false` for all values `a` in `range`. Conversely, the maximum is a value `x` in | ||
| 3573 | `range` such that $(D pred(x, a)) is `false` for all values `a` in `range` (note | ||
| 3574 | the swapped arguments to `pred`). | ||
| 3575 | |||
| 3576 | These functions may be used for computing arbitrary extrema by choosing `pred` | ||
| 3577 | appropriately. For corrrect functioning, `pred` must be a strict partial order, | ||
| 3578 | i.e. transitive (if $(D pred(a, b) && pred(b, c)) then $(D pred(a, c))) and | ||
| 3579 | irreflexive ($(D pred(a, a)) is `false`). | ||
| 3580 | |||
| 3581 | Params: | ||
| 3582 | pred = The ordering predicate to use to determine the extremum (minimum or | ||
| 3583 | maximum) element. | ||
| 3584 | range = The $(REF_ALTTEXT input range, isInputRange, std,range,primitives) to search. | ||
| 3585 | |||
| 3586 | Returns: The position of the minimum (respectively maximum) element of forward | ||
| 3587 | range `range`, i.e. a subrange of `range` starting at the position of its | ||
| 3588 | smallest (respectively largest) element and with the same ending as `range`. | ||
| 3589 | |||
| 3590 | */ | ||
| 3591 | Range minPos(alias pred = "a < b", Range)(Range range) | ||
| 3592 | if (isForwardRange!Range && !isInfinite!Range && | ||
| 3593 | is(typeof(binaryFun!pred(range.front, range.front)))) | ||
| 3594 | { | ||
| 3595 | static if (hasSlicing!Range && isRandomAccessRange!Range && hasLength!Range) | ||
| 3596 | { | ||
| 3597 | // Prefer index-based access | ||
| 3598 | size_t pos = 0; | ||
| 3599 | foreach (i; 1 .. range.length) | ||
| 3600 | { | ||
| 3601 | if (binaryFun!pred(range[i], range[pos])) | ||
| 3602 | { | ||
| 3603 | pos = i; | ||
| 3604 | } | ||
| 3605 | } | ||
| 3606 | return range[pos .. range.length]; | ||
| 3607 | } | ||
| 3608 | else | ||
| 3609 | { | ||
| 3610 | auto result = range.save; | ||
| 3611 | if (range.empty) return result; | ||
| 3612 | for (range.popFront(); !range.empty; range.popFront()) | ||
| 3613 | { | ||
| 3614 | // Note: Unlike minCount, we do not care to find equivalence, so a | ||
| 3615 | // single pred call is enough. | ||
| 3616 | if (binaryFun!pred(range.front, result.front)) | ||
| 3617 | { | ||
| 3618 | // change the min | ||
| 3619 | result = range.save; | ||
| 3620 | } | ||
| 3621 | } | ||
| 3622 | return result; | ||
| 3623 | } | ||
| 3624 | } | ||
| 3625 | |||
| 3626 | /// Ditto | ||
| 3627 | Range maxPos(alias pred = "a < b", Range)(Range range) | ||
| 3628 | if (isForwardRange!Range && !isInfinite!Range && | ||
| 3629 | is(typeof(binaryFun!pred(range.front, range.front)))) | ||
| 3630 | { | ||
| 3631 | return range.minPos!((a, b) => binaryFun!pred(b, a)); | ||
| 3632 | } | ||
| 3633 | |||
| 3634 | /// | ||
| 3635 | @safe unittest | ||
| 3636 | { | ||
| 3637 | int[] a = [ 2, 3, 4, 1, 2, 4, 1, 1, 2 ]; | ||
| 3638 | // Minimum is 1 and first occurs in position 3 | ||
| 3639 | assert(a.minPos == [ 1, 2, 4, 1, 1, 2 ]); | ||
| 3640 | // Maximum is 4 and first occurs in position 2 | ||
| 3641 | assert(a.maxPos == [ 4, 1, 2, 4, 1, 1, 2 ]); | ||
| 3642 | } | ||
| 3643 | |||
| 3644 | @safe unittest | ||
| 3645 | { | ||
| 3646 | import std.algorithm.comparison : equal; | ||
| 3647 | import std.internal.test.dummyrange; | ||
| 3648 | |||
| 3649 | int[] a = [ 2, 3, 4, 1, 2, 4, 1, 1, 2 ]; | ||
| 3650 | //Test that an empty range works | ||
| 3651 | int[] b = a[$..$]; | ||
| 3652 | assert(equal(minPos(b), b)); | ||
| 3653 | |||
| 3654 | //test with reference range. | ||
| 3655 | assert( equal( minPos(new ReferenceForwardRange!int([1, 2, 1, 0, 2, 0])), [0, 2, 0] ) ); | ||
| 3656 | } | ||
| 3657 | |||
| 3658 | @system unittest | ||
| 3659 | { | ||
| 3660 | //Rvalue range | ||
| 3661 | import std.algorithm.comparison : equal; | ||
| 3662 | import std.container : Array; | ||
| 3663 | |||
| 3664 | assert(Array!int(2, 3, 4, 1, 2, 4, 1, 1, 2) | ||
| 3665 | [] | ||
| 3666 | .minPos() | ||
| 3667 | .equal([ 1, 2, 4, 1, 1, 2 ])); | ||
| 3668 | } | ||
| 3669 | |||
| 3670 | @safe unittest | ||
| 3671 | { | ||
| 3672 | //BUG 9299 | ||
| 3673 | immutable a = [ 2, 3, 4, 1, 2, 4, 1, 1, 2 ]; | ||
| 3674 | // Minimum is 1 and first occurs in position 3 | ||
| 3675 | assert(minPos(a) == [ 1, 2, 4, 1, 1, 2 ]); | ||
| 3676 | // Maximum is 4 and first occurs in position 5 | ||
| 3677 | assert(minPos!("a > b")(a) == [ 4, 1, 2, 4, 1, 1, 2 ]); | ||
| 3678 | |||
| 3679 | immutable(int[])[] b = [ [4], [2, 4], [4], [4] ]; | ||
| 3680 | assert(minPos!("a[0] < b[0]")(b) == [ [2, 4], [4], [4] ]); | ||
| 3681 | } | ||
| 3682 | |||
| 3683 | /** | ||
| 3684 | Computes the index of the first occurrence of `range`'s minimum element. | ||
| 3685 | |||
| 3686 | Params: | ||
| 3687 | pred = The ordering predicate to use to determine the minimum element. | ||
| 3688 | range = The $(REF_ALTTEXT input range, isInputRange, std,range,primitives) | ||
| 3689 | to search. | ||
| 3690 | |||
| 3691 | Complexity: O(n) | ||
| 3692 | Exactly `n - 1` comparisons are needed. | ||
| 3693 | |||
| 3694 | Returns: | ||
| 3695 | The index of the first encounter of the minimum element in `range`. If the | ||
| 3696 | `range` is empty, -1 is returned. | ||
| 3697 | |||
| 3698 | See_Also: | ||
| 3699 | $(REF min, std,algorithm,comparison), $(LREF minCount), $(LREF minElement), $(LREF minPos) | ||
| 3700 | */ | ||
| 3701 | sizediff_t minIndex(alias pred = "a < b", Range)(Range range) | ||
| 3702 | if (isForwardRange!Range && !isInfinite!Range && | ||
| 3703 | is(typeof(binaryFun!pred(range.front, range.front)))) | ||
| 3704 | { | ||
| 3705 | if (range.empty) return -1; | ||
| 3706 | |||
| 3707 | sizediff_t minPos = 0; | ||
| 3708 | |||
| 3709 | static if (isRandomAccessRange!Range && hasLength!Range) | ||
| 3710 | { | ||
| 3711 | foreach (i; 1 .. range.length) | ||
| 3712 | { | ||
| 3713 | if (binaryFun!pred(range[i], range[minPos])) | ||
| 3714 | { | ||
| 3715 | minPos = i; | ||
| 3716 | } | ||
| 3717 | } | ||
| 3718 | } | ||
| 3719 | else | ||
| 3720 | { | ||
| 3721 | sizediff_t curPos = 0; | ||
| 3722 | Unqual!(typeof(range.front)) min = range.front; | ||
| 3723 | for (range.popFront(); !range.empty; range.popFront()) | ||
| 3724 | { | ||
| 3725 | ++curPos; | ||
| 3726 | if (binaryFun!pred(range.front, min)) | ||
| 3727 | { | ||
| 3728 | min = range.front; | ||
| 3729 | minPos = curPos; | ||
| 3730 | } | ||
| 3731 | } | ||
| 3732 | } | ||
| 3733 | return minPos; | ||
| 3734 | } | ||
| 3735 | |||
| 3736 | /// | ||
| 3737 | @safe pure nothrow unittest | ||
| 3738 | { | ||
| 3739 | int[] a = [2, 3, 4, 1, 2, 4, 1, 1, 2]; | ||
| 3740 | |||
| 3741 | // Minimum is 1 and first occurs in position 3 | ||
| 3742 | assert(a.minIndex == 3); | ||
| 3743 | // Get maximum index with minIndex | ||
| 3744 | assert(a.minIndex!"a > b" == 2); | ||
| 3745 | |||
| 3746 | // Range is empty, so return value is -1 | ||
| 3747 | int[] b; | ||
| 3748 | assert(b.minIndex == -1); | ||
| 3749 | |||
| 3750 | // Works with more custom types | ||
| 3751 | struct Dog { int age; } | ||
| 3752 | Dog[] dogs = [Dog(10), Dog(5), Dog(15)]; | ||
| 3753 | assert(dogs.minIndex!"a.age < b.age" == 1); | ||
| 3754 | } | ||
| 3755 | |||
| 3756 | @safe pure unittest | ||
| 3757 | { | ||
| 3758 | // should work with const | ||
| 3759 | const(int)[] immArr = [2, 1, 3]; | ||
| 3760 | assert(immArr.minIndex == 1); | ||
| 3761 | |||
| 3762 | // Works for const ranges too | ||
| 3763 | const int[] c = [2, 5, 4, 1, 2, 3]; | ||
| 3764 | assert(c.minIndex == 3); | ||
| 3765 | |||
| 3766 | // should work with immutable | ||
| 3767 | immutable(int)[] immArr2 = [2, 1, 3]; | ||
| 3768 | assert(immArr2.minIndex == 1); | ||
| 3769 | |||
| 3770 | // with strings | ||
| 3771 | assert(["b", "a", "c"].minIndex == 1); | ||
| 3772 | |||
| 3773 | // infinite range | ||
| 3774 | import std.range : cycle; | ||
| 3775 | static assert(!__traits(compiles, cycle([1]).minIndex)); | ||
| 3776 | |||
| 3777 | // with all dummy ranges | ||
| 3778 | import std.internal.test.dummyrange : AllDummyRanges; | ||
| 3779 | foreach (DummyType; AllDummyRanges) | ||
| 3780 | { | ||
| 3781 | static if (isForwardRange!DummyType && !isInfinite!DummyType) | ||
| 3782 | { | ||
| 3783 | DummyType d; | ||
| 3784 | d.arr = [5, 3, 7, 2, 1, 4]; | ||
| 3785 | assert(d.minIndex == 4); | ||
| 3786 | |||
| 3787 | d.arr = []; | ||
| 3788 | assert(d.minIndex == -1); | ||
| 3789 | } | ||
| 3790 | } | ||
| 3791 | } | ||
| 3792 | |||
| 3793 | @nogc @safe nothrow pure unittest | ||
| 3794 | { | ||
| 3795 | static immutable arr = [7, 3, 8, 2, 1, 4]; | ||
| 3796 | assert(arr.minIndex == 4); | ||
| 3797 | |||
| 3798 | static immutable arr2d = [[1, 3], [3, 9], [4, 2]]; | ||
| 3799 | assert(arr2d.minIndex!"a[1] < b[1]" == 2); | ||
| 3800 | } | ||
| 3801 | |||
| 3802 | /** | ||
| 3803 | Computes the index of the first occurrence of `range`'s maximum element. | ||
| 3804 | |||
| 3805 | Complexity: O(n) | ||
| 3806 | Exactly `n - 1` comparisons are needed. | ||
| 3807 | |||
| 3808 | Params: | ||
| 3809 | pred = The ordering predicate to use to determine the maximum element. | ||
| 3810 | range = The $(REF_ALTTEXT input range, isInputRange, std,range,primitives) to search. | ||
| 3811 | |||
| 3812 | Returns: | ||
| 3813 | The index of the first encounter of the maximum in `range`. If the | ||
| 3814 | `range` is empty, -1 is returned. | ||
| 3815 | |||
| 3816 | See_Also: | ||
| 3817 | $(REF max, std,algorithm,comparison), $(LREF maxCount), $(LREF maxElement), $(LREF maxPos) | ||
| 3818 | */ | ||
| 3819 | sizediff_t maxIndex(alias pred = "a < b", Range)(Range range) | ||
| 3820 | if (isInputRange!Range && !isInfinite!Range && | ||
| 3821 | is(typeof(binaryFun!pred(range.front, range.front)))) | ||
| 3822 | { | ||
| 3823 | return range.minIndex!((a, b) => binaryFun!pred(b, a)); | ||
| 3824 | } | ||
| 3825 | |||
| 3826 | /// | ||
| 3827 | @safe pure nothrow unittest | ||
| 3828 | { | ||
| 3829 | // Maximum is 4 and first occurs in position 2 | ||
| 3830 | int[] a = [2, 3, 4, 1, 2, 4, 1, 1, 2]; | ||
| 3831 | assert(a.maxIndex == 2); | ||
| 3832 | |||
| 3833 | // Empty range | ||
| 3834 | int[] b; | ||
| 3835 | assert(b.maxIndex == -1); | ||
| 3836 | |||
| 3837 | // Works with more custom types | ||
| 3838 | struct Dog { int age; } | ||
| 3839 | Dog[] dogs = [Dog(10), Dog(15), Dog(5)]; | ||
| 3840 | assert(dogs.maxIndex!"a.age < b.age" == 1); | ||
| 3841 | } | ||
| 3842 | |||
| 3843 | @safe pure unittest | ||
| 3844 | { | ||
| 3845 | // should work with const | ||
| 3846 | const(int)[] immArr = [5, 1, 3]; | ||
| 3847 | assert(immArr.maxIndex == 0); | ||
| 3848 | |||
| 3849 | // Works for const ranges too | ||
| 3850 | const int[] c = [2, 5, 4, 1, 2, 3]; | ||
| 3851 | assert(c.maxIndex == 1); | ||
| 3852 | |||
| 3853 | |||
| 3854 | // should work with immutable | ||
| 3855 | immutable(int)[] immArr2 = [2, 1, 3]; | ||
| 3856 | assert(immArr2.maxIndex == 2); | ||
| 3857 | |||
| 3858 | // with strings | ||
| 3859 | assert(["b", "a", "c"].maxIndex == 2); | ||
| 3860 | |||
| 3861 | // infinite range | ||
| 3862 | import std.range : cycle; | ||
| 3863 | static assert(!__traits(compiles, cycle([1]).maxIndex)); | ||
| 3864 | |||
| 3865 | // with all dummy ranges | ||
| 3866 | import std.internal.test.dummyrange : AllDummyRanges; | ||
| 3867 | foreach (DummyType; AllDummyRanges) | ||
| 3868 | { | ||
| 3869 | static if (isForwardRange!DummyType && !isInfinite!DummyType) | ||
| 3870 | { | ||
| 3871 | DummyType d; | ||
| 3872 | |||
| 3873 | d.arr = [5, 3, 7, 2, 1, 4]; | ||
| 3874 | assert(d.maxIndex == 2); | ||
| 3875 | |||
| 3876 | d.arr = []; | ||
| 3877 | assert(d.maxIndex == -1); | ||
| 3878 | } | ||
| 3879 | } | ||
| 3880 | } | ||
| 3881 | |||
| 3882 | @nogc @safe nothrow pure unittest | ||
| 3883 | { | ||
| 3884 | static immutable arr = [7, 3, 8, 2, 1, 4]; | ||
| 3885 | assert(arr.maxIndex == 2); | ||
| 3886 | |||
| 3887 | static immutable arr2d = [[1, 3], [3, 9], [4, 2]]; | ||
| 3888 | assert(arr2d.maxIndex!"a[1] < b[1]" == 1); | ||
| 3889 | } | ||
| 3890 | |||
| 3891 | /** | ||
| 3892 | Skip over the initial portion of the first given range that matches the second | ||
| 3893 | range, or if no second range is given skip over the elements that fullfil pred. | ||
| 3894 | Do nothing if there is no match. | ||
| 3895 | |||
| 3896 | Params: | ||
| 3897 | pred = The predicate that determines whether elements from each respective | ||
| 3898 | range match. Defaults to equality $(D "a == b"). | ||
| 3899 | r1 = The $(REF_ALTTEXT forward range, isForwardRange, std,range,primitives) to | ||
| 3900 | move forward. | ||
| 3901 | r2 = The $(REF_ALTTEXT input range, isInputRange, std,range,primitives) | ||
| 3902 | representing the initial segment of $(D r1) to skip over. | ||
| 3903 | |||
| 3904 | Returns: | ||
| 3905 | true if the initial segment of $(D r1) matches $(D r2) or $(D pred) evaluates to true, | ||
| 3906 | and $(D r1) has been advanced to the point past this segment; otherwise false, and | ||
| 3907 | $(D r1) is left in its original position. | ||
| 3908 | */ | ||
| 3909 | bool skipOver(R1, R2)(ref R1 r1, R2 r2) | ||
| 3910 | if (isForwardRange!R1 && isInputRange!R2 | ||
| 3911 | && is(typeof(r1.front == r2.front))) | ||
| 3912 | { | ||
| 3913 | static if (is(typeof(r1[0 .. $] == r2) : bool) | ||
| 3914 | && is(typeof(r2.length > r1.length) : bool) | ||
| 3915 | && is(typeof(r1 = r1[r2.length .. $]))) | ||
| 3916 | { | ||
| 3917 | if (r2.length > r1.length || r1[0 .. r2.length] != r2) | ||
| 3918 | { | ||
| 3919 | return false; | ||
| 3920 | } | ||
| 3921 | r1 = r1[r2.length .. $]; | ||
| 3922 | return true; | ||
| 3923 | } | ||
| 3924 | else | ||
| 3925 | { | ||
| 3926 | return skipOver!((a, b) => a == b)(r1, r2); | ||
| 3927 | } | ||
| 3928 | } | ||
| 3929 | |||
| 3930 | /// Ditto | ||
| 3931 | bool skipOver(alias pred, R1, R2)(ref R1 r1, R2 r2) | ||
| 3932 | if (is(typeof(binaryFun!pred(r1.front, r2.front))) && | ||
| 3933 | isForwardRange!R1 && | ||
| 3934 | isInputRange!R2) | ||
| 3935 | { | ||
| 3936 | static if (hasLength!R1 && hasLength!R2) | ||
| 3937 | { | ||
| 3938 | // Shortcut opportunity! | ||
| 3939 | if (r2.length > r1.length) | ||
| 3940 | return false; | ||
| 3941 | } | ||
| 3942 | auto r = r1.save; | ||
| 3943 | while (!r2.empty && !r.empty && binaryFun!pred(r.front, r2.front)) | ||
| 3944 | { | ||
| 3945 | r.popFront(); | ||
| 3946 | r2.popFront(); | ||
| 3947 | } | ||
| 3948 | if (r2.empty) | ||
| 3949 | r1 = r; | ||
| 3950 | return r2.empty; | ||
| 3951 | } | ||
| 3952 | |||
| 3953 | /// Ditto | ||
| 3954 | bool skipOver(alias pred, R)(ref R r1) | ||
| 3955 | if (isForwardRange!R && | ||
| 3956 | ifTestable!(typeof(r1.front), unaryFun!pred)) | ||
| 3957 | { | ||
| 3958 | if (r1.empty || !unaryFun!pred(r1.front)) | ||
| 3959 | return false; | ||
| 3960 | |||
| 3961 | do | ||
| 3962 | r1.popFront(); | ||
| 3963 | while (!r1.empty && unaryFun!pred(r1.front)); | ||
| 3964 | return true; | ||
| 3965 | } | ||
| 3966 | |||
| 3967 | /// | ||
| 3968 | @safe unittest | ||
| 3969 | { | ||
| 3970 | import std.algorithm.comparison : equal; | ||
| 3971 | |||
| 3972 | auto s1 = "Hello world"; | ||
| 3973 | assert(!skipOver(s1, "Ha")); | ||
| 3974 | assert(s1 == "Hello world"); | ||
| 3975 | assert(skipOver(s1, "Hell") && s1 == "o world"); | ||
| 3976 | |||
| 3977 | string[] r1 = ["abc", "def", "hij"]; | ||
| 3978 | dstring[] r2 = ["abc"d]; | ||
| 3979 | assert(!skipOver!((a, b) => a.equal(b))(r1, ["def"d])); | ||
| 3980 | assert(r1 == ["abc", "def", "hij"]); | ||
| 3981 | assert(skipOver!((a, b) => a.equal(b))(r1, r2)); | ||
| 3982 | assert(r1 == ["def", "hij"]); | ||
| 3983 | |||
| 3984 | import std.ascii : isWhite; | ||
| 3985 | import std.range.primitives : empty; | ||
| 3986 | |||
| 3987 | auto s2 = "\t\tvalue"; | ||
| 3988 | auto s3 = ""; | ||
| 3989 | auto s4 = "\t\t\t"; | ||
| 3990 | assert(s2.skipOver!isWhite && s2 == "value"); | ||
| 3991 | assert(!s3.skipOver!isWhite); | ||
| 3992 | assert(s4.skipOver!isWhite && s3.empty); | ||
| 3993 | } | ||
| 3994 | |||
| 3995 | /** | ||
| 3996 | Skip over the first element of the given range if it matches the given element, | ||
| 3997 | otherwise do nothing. | ||
| 3998 | |||
| 3999 | Params: | ||
| 4000 | pred = The predicate that determines whether an element from the range | ||
| 4001 | matches the given element. | ||
| 4002 | |||
| 4003 | r = The $(REF_ALTTEXT input range, isInputRange, std,range,primitives) to skip | ||
| 4004 | over. | ||
| 4005 | |||
| 4006 | e = The element to match. | ||
| 4007 | |||
| 4008 | Returns: | ||
| 4009 | true if the first element matches the given element according to the given | ||
| 4010 | predicate, and the range has been advanced by one element; otherwise false, and | ||
| 4011 | the range is left untouched. | ||
| 4012 | */ | ||
| 4013 | bool skipOver(R, E)(ref R r, E e) | ||
| 4014 | if (isInputRange!R && is(typeof(r.front == e) : bool)) | ||
| 4015 | { | ||
| 4016 | return skipOver!((a, b) => a == b)(r, e); | ||
| 4017 | } | ||
| 4018 | |||
| 4019 | /// Ditto | ||
| 4020 | bool skipOver(alias pred, R, E)(ref R r, E e) | ||
| 4021 | if (is(typeof(binaryFun!pred(r.front, e))) && isInputRange!R) | ||
| 4022 | { | ||
| 4023 | if (r.empty || !binaryFun!pred(r.front, e)) | ||
| 4024 | return false; | ||
| 4025 | r.popFront(); | ||
| 4026 | return true; | ||
| 4027 | } | ||
| 4028 | |||
| 4029 | /// | ||
| 4030 | @safe unittest | ||
| 4031 | { | ||
| 4032 | import std.algorithm.comparison : equal; | ||
| 4033 | |||
| 4034 | auto s1 = "Hello world"; | ||
| 4035 | assert(!skipOver(s1, 'a')); | ||
| 4036 | assert(s1 == "Hello world"); | ||
| 4037 | assert(skipOver(s1, 'H') && s1 == "ello world"); | ||
| 4038 | |||
| 4039 | string[] r = ["abc", "def", "hij"]; | ||
| 4040 | dstring e = "abc"d; | ||
| 4041 | assert(!skipOver!((a, b) => a.equal(b))(r, "def"d)); | ||
| 4042 | assert(r == ["abc", "def", "hij"]); | ||
| 4043 | assert(skipOver!((a, b) => a.equal(b))(r, e)); | ||
| 4044 | assert(r == ["def", "hij"]); | ||
| 4045 | |||
| 4046 | auto s2 = ""; | ||
| 4047 | assert(!s2.skipOver('a')); | ||
| 4048 | } | ||
| 4049 | |||
| 4050 | /** | ||
| 4051 | Checks whether the given | ||
| 4052 | $(REF_ALTTEXT input range, isInputRange, std,range,primitives) starts with (one | ||
| 4053 | of) the given needle(s) or, if no needles are given, | ||
| 4054 | if its front element fulfils predicate $(D pred). | ||
| 4055 | |||
| 4056 | Params: | ||
| 4057 | |||
| 4058 | pred = Predicate to use in comparing the elements of the haystack and the | ||
| 4059 | needle(s). Mandatory if no needles are given. | ||
| 4060 | |||
| 4061 | doesThisStart = The input range to check. | ||
| 4062 | |||
| 4063 | withOneOfThese = The needles against which the range is to be checked, | ||
| 4064 | which may be individual elements or input ranges of elements. | ||
| 4065 | |||
| 4066 | withThis = The single needle to check, which may be either a single element | ||
| 4067 | or an input range of elements. | ||
| 4068 | |||
| 4069 | Returns: | ||
| 4070 | |||
| 4071 | 0 if the needle(s) do not occur at the beginning of the given range; | ||
| 4072 | otherwise the position of the matching needle, that is, 1 if the range starts | ||
| 4073 | with $(D withOneOfThese[0]), 2 if it starts with $(D withOneOfThese[1]), and so | ||
| 4074 | on. | ||
| 4075 | |||
| 4076 | In the case where $(D doesThisStart) starts with multiple of the ranges or | ||
| 4077 | elements in $(D withOneOfThese), then the shortest one matches (if there are | ||
| 4078 | two which match which are of the same length (e.g. $(D "a") and $(D 'a')), then | ||
| 4079 | the left-most of them in the argument | ||
| 4080 | list matches). | ||
| 4081 | |||
| 4082 | In the case when no needle parameters are given, return $(D true) iff front of | ||
| 4083 | $(D doesThisStart) fulfils predicate $(D pred). | ||
| 4084 | */ | ||
| 4085 | uint startsWith(alias pred = "a == b", Range, Needles...)(Range doesThisStart, Needles withOneOfThese) | ||
| 4086 | if (isInputRange!Range && Needles.length > 1 && | ||
| 4087 | is(typeof(.startsWith!pred(doesThisStart, withOneOfThese[0])) : bool ) && | ||
| 4088 | is(typeof(.startsWith!pred(doesThisStart, withOneOfThese[1 .. $])) : uint)) | ||
| 4089 | { | ||
| 4090 | alias haystack = doesThisStart; | ||
| 4091 | alias needles = withOneOfThese; | ||
| 4092 | |||
| 4093 | // Make one pass looking for empty ranges in needles | ||
| 4094 | foreach (i, Unused; Needles) | ||
| 4095 | { | ||
| 4096 | // Empty range matches everything | ||
| 4097 | static if (!is(typeof(binaryFun!pred(haystack.front, needles[i])) : bool)) | ||
| 4098 | { | ||
| 4099 | if (needles[i].empty) return i + 1; | ||
| 4100 | } | ||
| 4101 | } | ||
| 4102 | |||
| 4103 | for (; !haystack.empty; haystack.popFront()) | ||
| 4104 | { | ||
| 4105 | foreach (i, Unused; Needles) | ||
| 4106 | { | ||
| 4107 | static if (is(typeof(binaryFun!pred(haystack.front, needles[i])) : bool)) | ||
| 4108 | { | ||
| 4109 | // Single-element | ||
| 4110 | if (binaryFun!pred(haystack.front, needles[i])) | ||
| 4111 | { | ||
| 4112 | // found, but instead of returning, we just stop searching. | ||
| 4113 | // This is to account for one-element | ||
| 4114 | // range matches (consider startsWith("ab", "a", | ||
| 4115 | // 'a') should return 1, not 2). | ||
| 4116 | break; | ||
| 4117 | } | ||
| 4118 | } | ||
| 4119 | else | ||
| 4120 | { | ||
| 4121 | if (binaryFun!pred(haystack.front, needles[i].front)) | ||
| 4122 | { | ||
| 4123 | continue; | ||
| 4124 | } | ||
| 4125 | } | ||
| 4126 | |||
| 4127 | // This code executed on failure to match | ||
| 4128 | // Out with this guy, check for the others | ||
| 4129 | uint result = startsWith!pred(haystack, needles[0 .. i], needles[i + 1 .. $]); | ||
| 4130 | if (result > i) ++result; | ||
| 4131 | return result; | ||
| 4132 | } | ||
| 4133 | |||
| 4134 | // If execution reaches this point, then the front matches for all | ||
| 4135 | // needle ranges, or a needle element has been matched. | ||
| 4136 | // What we need to do now is iterate, lopping off the front of | ||
| 4137 | // the range and checking if the result is empty, or finding an | ||
| 4138 | // element needle and returning. | ||
| 4139 | // If neither happens, we drop to the end and loop. | ||
| 4140 | foreach (i, Unused; Needles) | ||
| 4141 | { | ||
| 4142 | static if (is(typeof(binaryFun!pred(haystack.front, needles[i])) : bool)) | ||
| 4143 | { | ||
| 4144 | // Test has passed in the previous loop | ||
| 4145 | return i + 1; | ||
| 4146 | } | ||
| 4147 | else | ||
| 4148 | { | ||
| 4149 | needles[i].popFront(); | ||
| 4150 | if (needles[i].empty) return i + 1; | ||
| 4151 | } | ||
| 4152 | } | ||
| 4153 | } | ||
| 4154 | return 0; | ||
| 4155 | } | ||
| 4156 | |||
| 4157 | /// Ditto | ||
| 4158 | bool startsWith(alias pred = "a == b", R1, R2)(R1 doesThisStart, R2 withThis) | ||
| 4159 | if (isInputRange!R1 && | ||
| 4160 | isInputRange!R2 && | ||
| 4161 | is(typeof(binaryFun!pred(doesThisStart.front, withThis.front)) : bool)) | ||
| 4162 | { | ||
| 4163 | alias haystack = doesThisStart; | ||
| 4164 | alias needle = withThis; | ||
| 4165 | |||
| 4166 | static if (is(typeof(pred) : string)) | ||
| 4167 | enum isDefaultPred = pred == "a == b"; | ||
| 4168 | else | ||
| 4169 | enum isDefaultPred = false; | ||
| 4170 | |||
| 4171 | //Note: While narrow strings don't have a "true" length, for a narrow string to start with another | ||
| 4172 | //narrow string *of the same type*, it must have *at least* as many code units. | ||
| 4173 | static if ((hasLength!R1 && hasLength!R2) || | ||
| 4174 | (isNarrowString!R1 && isNarrowString!R2 && ElementEncodingType!R1.sizeof == ElementEncodingType!R2.sizeof)) | ||
| 4175 | { | ||
| 4176 | if (haystack.length < needle.length) | ||
| 4177 | return false; | ||
| 4178 | } | ||
| 4179 | |||
| 4180 | static if (isDefaultPred && isArray!R1 && isArray!R2 && | ||
| 4181 | is(Unqual!(ElementEncodingType!R1) == Unqual!(ElementEncodingType!R2))) | ||
| 4182 | { | ||
| 4183 | //Array slice comparison mode | ||
| 4184 | return haystack[0 .. needle.length] == needle; | ||
| 4185 | } | ||
| 4186 | else static if (isRandomAccessRange!R1 && isRandomAccessRange!R2 && hasLength!R2) | ||
| 4187 | { | ||
| 4188 | //RA dual indexing mode | ||
| 4189 | foreach (j; 0 .. needle.length) | ||
| 4190 | { | ||
| 4191 | if (!binaryFun!pred(haystack[j], needle[j])) | ||
| 4192 | // not found | ||
| 4193 | return false; | ||
| 4194 | } | ||
| 4195 | // found! | ||
| 4196 | return true; | ||
| 4197 | } | ||
| 4198 | else | ||
| 4199 | { | ||
| 4200 | //Standard input range mode | ||
| 4201 | if (needle.empty) return true; | ||
| 4202 | static if (hasLength!R1 && hasLength!R2) | ||
| 4203 | { | ||
| 4204 | //We have previously checked that haystack.length > needle.length, | ||
| 4205 | //So no need to check haystack.empty during iteration | ||
| 4206 | for ( ; ; haystack.popFront() ) | ||
| 4207 | { | ||
| 4208 | if (!binaryFun!pred(haystack.front, needle.front)) break; | ||
| 4209 | needle.popFront(); | ||
| 4210 | if (needle.empty) return true; | ||
| 4211 | } | ||
| 4212 | } | ||
| 4213 | else | ||
| 4214 | { | ||
| 4215 | for ( ; !haystack.empty ; haystack.popFront() ) | ||
| 4216 | { | ||
| 4217 | if (!binaryFun!pred(haystack.front, needle.front)) break; | ||
| 4218 | needle.popFront(); | ||
| 4219 | if (needle.empty) return true; | ||
| 4220 | } | ||
| 4221 | } | ||
| 4222 | return false; | ||
| 4223 | } | ||
| 4224 | } | ||
| 4225 | |||
| 4226 | /// Ditto | ||
| 4227 | bool startsWith(alias pred = "a == b", R, E)(R doesThisStart, E withThis) | ||
| 4228 | if (isInputRange!R && | ||
| 4229 | is(typeof(binaryFun!pred(doesThisStart.front, withThis)) : bool)) | ||
| 4230 | { | ||
| 4231 | if (doesThisStart.empty) | ||
| 4232 | return false; | ||
| 4233 | |||
| 4234 | alias predFunc = binaryFun!pred; | ||
| 4235 | |||
| 4236 | // auto-decoding special case | ||
| 4237 | static if (isNarrowString!R) | ||
| 4238 | { | ||
| 4239 | // specialize for ASCII as to not change previous behavior | ||
| 4240 | if (withThis <= 0x7F) | ||
| 4241 | return predFunc(doesThisStart[0], withThis); | ||
| 4242 | else | ||
| 4243 | return predFunc(doesThisStart.front, withThis); | ||
| 4244 | } | ||
| 4245 | else | ||
| 4246 | { | ||
| 4247 | return predFunc(doesThisStart.front, withThis); | ||
| 4248 | } | ||
| 4249 | } | ||
| 4250 | |||
| 4251 | /// Ditto | ||
| 4252 | bool startsWith(alias pred, R)(R doesThisStart) | ||
| 4253 | if (isInputRange!R && | ||
| 4254 | ifTestable!(typeof(doesThisStart.front), unaryFun!pred)) | ||
| 4255 | { | ||
| 4256 | return !doesThisStart.empty && unaryFun!pred(doesThisStart.front); | ||
| 4257 | } | ||
| 4258 | |||
| 4259 | /// | ||
| 4260 | @safe unittest | ||
| 4261 | { | ||
| 4262 | import std.ascii : isAlpha; | ||
| 4263 | |||
| 4264 | assert("abc".startsWith!(a => a.isAlpha)); | ||
| 4265 | assert("abc".startsWith!isAlpha); | ||
| 4266 | assert(!"1ab".startsWith!(a => a.isAlpha)); | ||
| 4267 | assert(!"".startsWith!(a => a.isAlpha)); | ||
| 4268 | |||
| 4269 | import std.algorithm.comparison : among; | ||
| 4270 | assert("abc".startsWith!(a => a.among('a', 'b') != 0)); | ||
| 4271 | assert(!"abc".startsWith!(a => a.among('b', 'c') != 0)); | ||
| 4272 | |||
| 4273 | assert(startsWith("abc", "")); | ||
| 4274 | assert(startsWith("abc", "a")); | ||
| 4275 | assert(!startsWith("abc", "b")); | ||
| 4276 | assert(startsWith("abc", 'a', "b") == 1); | ||
| 4277 | assert(startsWith("abc", "b", "a") == 2); | ||
| 4278 | assert(startsWith("abc", "a", "a") == 1); | ||
| 4279 | assert(startsWith("abc", "ab", "a") == 2); | ||
| 4280 | assert(startsWith("abc", "x", "a", "b") == 2); | ||
| 4281 | assert(startsWith("abc", "x", "aa", "ab") == 3); | ||
| 4282 | assert(startsWith("abc", "x", "aaa", "sab") == 0); | ||
| 4283 | assert(startsWith("abc", "x", "aaa", "a", "sab") == 3); | ||
| 4284 | |||
| 4285 | import std.typecons : Tuple; | ||
| 4286 | alias C = Tuple!(int, "x", int, "y"); | ||
| 4287 | assert(startsWith!"a.x == b"([ C(1,1), C(1,2), C(2,2) ], [1, 1])); | ||
| 4288 | assert(startsWith!"a.x == b"([ C(1,1), C(2,1), C(2,2) ], [1, 1], [1, 2], [1, 3]) == 2); | ||
| 4289 | } | ||
| 4290 | |||
| 4291 | @safe unittest | ||
| 4292 | { | ||
| 4293 | import std.algorithm.iteration : filter; | ||
| 4294 | import std.conv : to; | ||
| 4295 | import std.meta : AliasSeq; | ||
| 4296 | import std.range; | ||
| 4297 | |||
| 4298 | foreach (S; AliasSeq!(char[], wchar[], dchar[], string, wstring, dstring)) | ||
| 4299 | { | ||
| 4300 | assert(!startsWith(to!S("abc"), 'c')); | ||
| 4301 | assert(startsWith(to!S("abc"), 'a', 'c') == 1); | ||
| 4302 | assert(!startsWith(to!S("abc"), 'x', 'n', 'b')); | ||
| 4303 | assert(startsWith(to!S("abc"), 'x', 'n', 'a') == 3); | ||
| 4304 | assert(startsWith(to!S("\uFF28abc"), 'a', '\uFF28', 'c') == 2); | ||
| 4305 | |||
| 4306 | foreach (T; AliasSeq!(char[], wchar[], dchar[], string, wstring, dstring)) | ||
| 4307 | (){ // avoid slow optimizations for large functions @@@BUG@@@ 2396 | ||
| 4308 | //Lots of strings | ||
| 4309 | assert(startsWith(to!S("abc"), to!T(""))); | ||
| 4310 | assert(startsWith(to!S("ab"), to!T("a"))); | ||
| 4311 | assert(startsWith(to!S("abc"), to!T("a"))); | ||
| 4312 | assert(!startsWith(to!S("abc"), to!T("b"))); | ||
| 4313 | assert(!startsWith(to!S("abc"), to!T("b"), "bc", "abcd", "xyz")); | ||
| 4314 | assert(startsWith(to!S("abc"), to!T("ab"), 'a') == 2); | ||
| 4315 | assert(startsWith(to!S("abc"), to!T("a"), "b") == 1); | ||
| 4316 | assert(startsWith(to!S("abc"), to!T("b"), "a") == 2); | ||
| 4317 | assert(startsWith(to!S("abc"), to!T("a"), 'a') == 1); | ||
| 4318 | assert(startsWith(to!S("abc"), 'a', to!T("a")) == 1); | ||
| 4319 | assert(startsWith(to!S("abc"), to!T("x"), "a", "b") == 2); | ||
| 4320 | assert(startsWith(to!S("abc"), to!T("x"), "aa", "ab") == 3); | ||
| 4321 | assert(startsWith(to!S("abc"), to!T("x"), "aaa", "sab") == 0); | ||
| 4322 | assert(startsWith(to!S("abc"), 'a')); | ||
| 4323 | assert(!startsWith(to!S("abc"), to!T("sab"))); | ||
| 4324 | assert(startsWith(to!S("abc"), 'x', to!T("aaa"), 'a', "sab") == 3); | ||
| 4325 | |||
| 4326 | //Unicode | ||
| 4327 | assert(startsWith(to!S("\uFF28el\uFF4co"), to!T("\uFF28el"))); | ||
| 4328 | assert(startsWith(to!S("\uFF28el\uFF4co"), to!T("Hel"), to!T("\uFF28el")) == 2); | ||
| 4329 | assert(startsWith(to!S("日本語"), to!T("日本"))); | ||
| 4330 | assert(startsWith(to!S("日本語"), to!T("日本語"))); | ||
| 4331 | assert(!startsWith(to!S("日本"), to!T("日本語"))); | ||
| 4332 | |||
| 4333 | //Empty | ||
| 4334 | assert(startsWith(to!S(""), T.init)); | ||
| 4335 | assert(!startsWith(to!S(""), 'a')); | ||
| 4336 | assert(startsWith(to!S("a"), T.init)); | ||
| 4337 | assert(startsWith(to!S("a"), T.init, "") == 1); | ||
| 4338 | assert(startsWith(to!S("a"), T.init, 'a') == 1); | ||
| 4339 | assert(startsWith(to!S("a"), 'a', T.init) == 2); | ||
| 4340 | }(); | ||
| 4341 | } | ||
| 4342 | |||
| 4343 | //Length but no RA | ||
| 4344 | assert(!startsWith("abc".takeExactly(3), "abcd".takeExactly(4))); | ||
| 4345 | assert(startsWith("abc".takeExactly(3), "abcd".takeExactly(3))); | ||
| 4346 | assert(startsWith("abc".takeExactly(3), "abcd".takeExactly(1))); | ||
| 4347 | |||
| 4348 | foreach (T; AliasSeq!(int, short)) | ||
| 4349 | { | ||
| 4350 | immutable arr = cast(T[])[0, 1, 2, 3, 4, 5]; | ||
| 4351 | |||
| 4352 | //RA range | ||
| 4353 | assert(startsWith(arr, cast(int[]) null)); | ||
| 4354 | assert(!startsWith(arr, 5)); | ||
| 4355 | assert(!startsWith(arr, 1)); | ||
| 4356 | assert(startsWith(arr, 0)); | ||
| 4357 | assert(startsWith(arr, 5, 0, 1) == 2); | ||
| 4358 | assert(startsWith(arr, [0])); | ||
| 4359 | assert(startsWith(arr, [0, 1])); | ||
| 4360 | assert(startsWith(arr, [0, 1], 7) == 1); | ||
| 4361 | assert(!startsWith(arr, [0, 1, 7])); | ||
| 4362 | assert(startsWith(arr, [0, 1, 7], [0, 1, 2]) == 2); | ||
| 4363 | |||
| 4364 | //Normal input range | ||
| 4365 | assert(!startsWith(filter!"true"(arr), 1)); | ||
| 4366 | assert(startsWith(filter!"true"(arr), 0)); | ||
| 4367 | assert(startsWith(filter!"true"(arr), [0])); | ||
| 4368 | assert(startsWith(filter!"true"(arr), [0, 1])); | ||
| 4369 | assert(startsWith(filter!"true"(arr), [0, 1], 7) == 1); | ||
| 4370 | assert(!startsWith(filter!"true"(arr), [0, 1, 7])); | ||
| 4371 | assert(startsWith(filter!"true"(arr), [0, 1, 7], [0, 1, 2]) == 2); | ||
| 4372 | assert(startsWith(arr, filter!"true"([0, 1]))); | ||
| 4373 | assert(startsWith(arr, filter!"true"([0, 1]), 7) == 1); | ||
| 4374 | assert(!startsWith(arr, filter!"true"([0, 1, 7]))); | ||
| 4375 | assert(startsWith(arr, [0, 1, 7], filter!"true"([0, 1, 2])) == 2); | ||
| 4376 | |||
| 4377 | //Non-default pred | ||
| 4378 | assert(startsWith!("a%10 == b%10")(arr, [10, 11])); | ||
| 4379 | assert(!startsWith!("a%10 == b%10")(arr, [10, 12])); | ||
| 4380 | } | ||
| 4381 | } | ||
| 4382 | |||
| 4383 | /* (Not yet documented.) | ||
| 4384 | Consume all elements from $(D r) that are equal to one of the elements | ||
| 4385 | $(D es). | ||
| 4386 | */ | ||
| 4387 | private void skipAll(alias pred = "a == b", R, Es...)(ref R r, Es es) | ||
| 4388 | //if (is(typeof(binaryFun!pred(r1.front, es[0])))) | ||
| 4389 | { | ||
| 4390 | loop: | ||
| 4391 | for (; !r.empty; r.popFront()) | ||
| 4392 | { | ||
| 4393 | foreach (i, E; Es) | ||
| 4394 | { | ||
| 4395 | if (binaryFun!pred(r.front, es[i])) | ||
| 4396 | { | ||
| 4397 | continue loop; | ||
| 4398 | } | ||
| 4399 | } | ||
| 4400 | break; | ||
| 4401 | } | ||
| 4402 | } | ||
| 4403 | |||
| 4404 | @safe unittest | ||
| 4405 | { | ||
| 4406 | auto s1 = "Hello world"; | ||
| 4407 | skipAll(s1, 'H', 'e'); | ||
| 4408 | assert(s1 == "llo world"); | ||
| 4409 | } | ||
| 4410 | |||
| 4411 | /** | ||
| 4412 | Interval option specifier for `until` (below) and others. | ||
| 4413 | |||
| 4414 | If set to $(D OpenRight.yes), then the interval is open to the right | ||
| 4415 | (last element is not included). | ||
| 4416 | |||
| 4417 | Otherwise if set to $(D OpenRight.no), then the interval is closed to the right | ||
| 4418 | (last element included). | ||
| 4419 | */ | ||
| 4420 | alias OpenRight = Flag!"openRight"; | ||
| 4421 | |||
| 4422 | /** | ||
| 4423 | Lazily iterates $(D range) _until the element $(D e) for which | ||
| 4424 | $(D pred(e, sentinel)) is true. | ||
| 4425 | |||
| 4426 | This is similar to `takeWhile` in other languages. | ||
| 4427 | |||
| 4428 | Params: | ||
| 4429 | pred = Predicate to determine when to stop. | ||
| 4430 | range = The $(REF_ALTTEXT input _range, isInputRange, std,_range,primitives) | ||
| 4431 | to iterate over. | ||
| 4432 | sentinel = The element to stop at. | ||
| 4433 | openRight = Determines whether the element for which the given predicate is | ||
| 4434 | true should be included in the resulting range ($(D No.openRight)), or | ||
| 4435 | not ($(D Yes.openRight)). | ||
| 4436 | |||
| 4437 | Returns: | ||
| 4438 | An $(REF_ALTTEXT input _range, isInputRange, std,_range,primitives) that | ||
| 4439 | iterates over the original range's elements, but ends when the specified | ||
| 4440 | predicate becomes true. If the original range is a | ||
| 4441 | $(REF_ALTTEXT forward _range, isForwardRange, std,_range,primitives) or | ||
| 4442 | higher, this range will be a forward range. | ||
| 4443 | */ | ||
| 4444 | Until!(pred, Range, Sentinel) | ||
| 4445 | until(alias pred = "a == b", Range, Sentinel) | ||
| 4446 | (Range range, Sentinel sentinel, OpenRight openRight = Yes.openRight) | ||
| 4447 | if (!is(Sentinel == OpenRight)) | ||
| 4448 | { | ||
| 4449 | return typeof(return)(range, sentinel, openRight); | ||
| 4450 | } | ||
| 4451 | |||
| 4452 | /// Ditto | ||
| 4453 | Until!(pred, Range, void) | ||
| 4454 | until(alias pred, Range) | ||
| 4455 | (Range range, OpenRight openRight = Yes.openRight) | ||
| 4456 | { | ||
| 4457 | return typeof(return)(range, openRight); | ||
| 4458 | } | ||
| 4459 | |||
| 4460 | /// ditto | ||
| 4461 | struct Until(alias pred, Range, Sentinel) | ||
| 4462 | if (isInputRange!Range) | ||
| 4463 | { | ||
| 4464 | private Range _input; | ||
| 4465 | static if (!is(Sentinel == void)) | ||
| 4466 | private Sentinel _sentinel; | ||
| 4467 | private OpenRight _openRight; | ||
| 4468 | private bool _done; | ||
| 4469 | |||
| 4470 | static if (!is(Sentinel == void)) | ||
| 4471 | /// | ||
| 4472 | this(Range input, Sentinel sentinel, | ||
| 4473 | OpenRight openRight = Yes.openRight) | ||
| 4474 | { | ||
| 4475 | _input = input; | ||
| 4476 | _sentinel = sentinel; | ||
| 4477 | _openRight = openRight; | ||
| 4478 | _done = _input.empty || openRight && predSatisfied(); | ||
| 4479 | } | ||
| 4480 | else | ||
| 4481 | /// | ||
| 4482 | this(Range input, OpenRight openRight = Yes.openRight) | ||
| 4483 | { | ||
| 4484 | _input = input; | ||
| 4485 | _openRight = openRight; | ||
| 4486 | _done = _input.empty || openRight && predSatisfied(); | ||
| 4487 | } | ||
| 4488 | |||
| 4489 | /// | ||
| 4490 | @property bool empty() | ||
| 4491 | { | ||
| 4492 | return _done; | ||
| 4493 | } | ||
| 4494 | |||
| 4495 | /// | ||
| 4496 | @property auto ref front() | ||
| 4497 | { | ||
| 4498 | assert(!empty); | ||
| 4499 | return _input.front; | ||
| 4500 | } | ||
| 4501 | |||
| 4502 | private bool predSatisfied() | ||
| 4503 | { | ||
| 4504 | static if (is(Sentinel == void)) | ||
| 4505 | return cast(bool) unaryFun!pred(_input.front); | ||
| 4506 | else | ||
| 4507 | return cast(bool) startsWith!pred(_input, _sentinel); | ||
| 4508 | } | ||
| 4509 | |||
| 4510 | /// | ||
| 4511 | void popFront() | ||
| 4512 | { | ||
| 4513 | assert(!empty); | ||
| 4514 | if (!_openRight) | ||
| 4515 | { | ||
| 4516 | _done = predSatisfied(); | ||
| 4517 | _input.popFront(); | ||
| 4518 | _done = _done || _input.empty; | ||
| 4519 | } | ||
| 4520 | else | ||
| 4521 | { | ||
| 4522 | _input.popFront(); | ||
| 4523 | _done = _input.empty || predSatisfied(); | ||
| 4524 | } | ||
| 4525 | } | ||
| 4526 | |||
| 4527 | static if (isForwardRange!Range) | ||
| 4528 | { | ||
| 4529 | static if (!is(Sentinel == void)) | ||
| 4530 | /// | ||
| 4531 | @property Until save() | ||
| 4532 | { | ||
| 4533 | Until result = this; | ||
| 4534 | result._input = _input.save; | ||
| 4535 | result._sentinel = _sentinel; | ||
| 4536 | result._openRight = _openRight; | ||
| 4537 | result._done = _done; | ||
| 4538 | return result; | ||
| 4539 | } | ||
| 4540 | else | ||
| 4541 | /// | ||
| 4542 | @property Until save() | ||
| 4543 | { | ||
| 4544 | Until result = this; | ||
| 4545 | result._input = _input.save; | ||
| 4546 | result._openRight = _openRight; | ||
| 4547 | result._done = _done; | ||
| 4548 | return result; | ||
| 4549 | } | ||
| 4550 | } | ||
| 4551 | } | ||
| 4552 | |||
| 4553 | /// | ||
| 4554 | @safe unittest | ||
| 4555 | { | ||
| 4556 | import std.algorithm.comparison : equal; | ||
| 4557 | import std.typecons : No; | ||
| 4558 | int[] a = [ 1, 2, 4, 7, 7, 2, 4, 7, 3, 5]; | ||
| 4559 | assert(equal(a.until(7), [1, 2, 4])); | ||
| 4560 | assert(equal(a.until(7, No.openRight), [1, 2, 4, 7])); | ||
| 4561 | } | ||
| 4562 | |||
| 4563 | @safe unittest | ||
| 4564 | { | ||
| 4565 | import std.algorithm.comparison : equal; | ||
| 4566 | int[] a = [ 1, 2, 4, 7, 7, 2, 4, 7, 3, 5]; | ||
| 4567 | |||
| 4568 | static assert(isForwardRange!(typeof(a.until(7)))); | ||
| 4569 | static assert(isForwardRange!(typeof(until!"a == 2"(a, No.openRight)))); | ||
| 4570 | |||
| 4571 | assert(equal(a.until(7), [1, 2, 4])); | ||
| 4572 | assert(equal(a.until([7, 2]), [1, 2, 4, 7])); | ||
| 4573 | assert(equal(a.until(7, No.openRight), [1, 2, 4, 7])); | ||
| 4574 | assert(equal(until!"a == 2"(a, No.openRight), [1, 2])); | ||
| 4575 | } | ||
| 4576 | |||
| 4577 | @system unittest // bugzilla 13171 | ||
| 4578 | { | ||
| 4579 | import std.algorithm.comparison : equal; | ||
| 4580 | import std.range; | ||
| 4581 | auto a = [1, 2, 3, 4]; | ||
| 4582 | assert(equal(refRange(&a).until(3, No.openRight), [1, 2, 3])); | ||
| 4583 | assert(a == [4]); | ||
| 4584 | } | ||
| 4585 | |||
| 4586 | @safe unittest // Issue 10460 | ||
| 4587 | { | ||
| 4588 | import std.algorithm.comparison : equal; | ||
| 4589 | auto a = [1, 2, 3, 4]; | ||
| 4590 | foreach (ref e; a.until(3)) | ||
| 4591 | e = 0; | ||
| 4592 | assert(equal(a, [0, 0, 3, 4])); | ||
| 4593 | } | ||
| 4594 | |||
| 4595 | @safe unittest // Issue 13124 | ||
| 4596 | { | ||
| 4597 | import std.algorithm.comparison : among, equal; | ||
| 4598 | auto s = "hello how\nare you"; | ||
| 4599 | assert(equal(s.until!(c => c.among!('\n', '\r')), "hello how")); | ||
| 4600 | } | ||
| @@ -0,0 +1,1521 @@ | |||
| 1 | // Written in the D programming language. | ||
| 2 | /** | ||
| 3 | This is a submodule of $(MREF std, algorithm). | ||
| 4 | It contains generic algorithms that implement set operations. | ||
| 5 | |||
| 6 | The functions $(LREF multiwayMerge), $(LREF multiwayUnion), $(LREF setDifference), | ||
| 7 | $(LREF setIntersection), $(LREF setSymmetricDifference) expect a range of sorted | ||
| 8 | ranges as input. | ||
| 9 | |||
| 10 | All algorithms are generalized to accept as input not only sets but also | ||
| 11 | $(HTTP https://en.wikipedia.org/wiki/Multiset, multisets). Each algorithm | ||
| 12 | documents behaviour in the presence of duplicated inputs. | ||
| 13 | |||
| 14 | $(SCRIPT inhibitQuickIndex = 1;) | ||
| 15 | $(BOOKTABLE Cheat Sheet, | ||
| 16 | $(TR $(TH Function Name) $(TH Description)) | ||
| 17 | $(T2 cartesianProduct, | ||
| 18 | Computes Cartesian product of two ranges.) | ||
| 19 | $(T2 largestPartialIntersection, | ||
| 20 | Copies out the values that occur most frequently in a range of ranges.) | ||
| 21 | $(T2 largestPartialIntersectionWeighted, | ||
| 22 | Copies out the values that occur most frequently (multiplied by | ||
| 23 | per-value weights) in a range of ranges.) | ||
| 24 | $(T2 multiwayMerge, | ||
| 25 | Merges a range of sorted ranges.) | ||
| 26 | $(T2 multiwayUnion, | ||
| 27 | Computes the union of a range of sorted ranges.) | ||
| 28 | $(T2 setDifference, | ||
| 29 | Lazily computes the set difference of two or more sorted ranges.) | ||
| 30 | $(T2 setIntersection, | ||
| 31 | Lazily computes the intersection of two or more sorted ranges.) | ||
| 32 | $(T2 setSymmetricDifference, | ||
| 33 | Lazily computes the symmetric set difference of two or more sorted | ||
| 34 | ranges.) | ||
| 35 | ) | ||
| 36 | |||
| 37 | Copyright: Andrei Alexandrescu 2008-. | ||
| 38 | |||
| 39 | License: $(HTTP boost.org/LICENSE_1_0.txt, Boost License 1.0). | ||
| 40 | |||
| 41 | Authors: $(HTTP erdani.com, Andrei Alexandrescu) | ||
| 42 | |||
| 43 | Source: $(PHOBOSSRC std/algorithm/_setops.d) | ||
| 44 | |||
| 45 | Macros: | ||
| 46 | T2=$(TR $(TDNW $(LREF $1)) $(TD $+)) | ||
| 47 | */ | ||
| 48 | module std.algorithm.setops; | ||
| 49 | |||
| 50 | import std.range.primitives; | ||
| 51 | |||
| 52 | // FIXME | ||
| 53 | import std.functional; // : unaryFun, binaryFun; | ||
| 54 | import std.traits; | ||
| 55 | // FIXME | ||
| 56 | import std.meta; // : AliasSeq, staticMap, allSatisfy, anySatisfy; | ||
| 57 | |||
| 58 | import std.algorithm.sorting; // : Merge; | ||
| 59 | import std.typecons : No; | ||
| 60 | |||
| 61 | // cartesianProduct | ||
| 62 | /** | ||
| 63 | Lazily computes the Cartesian product of two or more ranges. The product is a | ||
| 64 | _range of tuples of elements from each respective range. | ||
| 65 | |||
| 66 | The conditions for the two-range case are as follows: | ||
| 67 | |||
| 68 | If both ranges are finite, then one must be (at least) a | ||
| 69 | $(REF_ALTTEXT forward range, isForwardRange, std,range,primitives) and the | ||
| 70 | other an $(REF_ALTTEXT input range, isInputRange, std,range,primitives). | ||
| 71 | |||
| 72 | If one _range is infinite and the other finite, then the finite _range must | ||
| 73 | be a forward _range, and the infinite range can be an input _range. | ||
| 74 | |||
| 75 | If both ranges are infinite, then both must be forward ranges. | ||
| 76 | |||
| 77 | When there are more than two ranges, the above conditions apply to each | ||
| 78 | adjacent pair of ranges. | ||
| 79 | |||
| 80 | Params: | ||
| 81 | range1 = The first range | ||
| 82 | range2 = The second range | ||
| 83 | ranges = Two or more non-infinite forward ranges | ||
| 84 | otherRanges = Zero or more non-infinite forward ranges | ||
| 85 | |||
| 86 | Returns: | ||
| 87 | A forward range of $(REF Tuple, std,typecons) representing elements of the | ||
| 88 | cartesian product of the given ranges. | ||
| 89 | */ | ||
| 90 | auto cartesianProduct(R1, R2)(R1 range1, R2 range2) | ||
| 91 | if (!allSatisfy!(isForwardRange, R1, R2) || | ||
| 92 | anySatisfy!(isInfinite, R1, R2)) | ||
| 93 | { | ||
| 94 | import std.algorithm.iteration : map, joiner; | ||
| 95 | |||
| 96 | static if (isInfinite!R1 && isInfinite!R2) | ||
| 97 | { | ||
| 98 | static if (isForwardRange!R1 && isForwardRange!R2) | ||
| 99 | { | ||
| 100 | import std.range : zip, repeat, take, chain, sequence; | ||
| 101 | |||
| 102 | // This algorithm traverses the cartesian product by alternately | ||
| 103 | // covering the right and bottom edges of an increasing square area | ||
| 104 | // over the infinite table of combinations. This schedule allows us | ||
| 105 | // to require only forward ranges. | ||
| 106 | return zip(sequence!"n"(cast(size_t) 0), range1.save, range2.save, | ||
| 107 | repeat(range1), repeat(range2)) | ||
| 108 | .map!(function(a) => chain( | ||
| 109 | zip(repeat(a[1]), take(a[4].save, a[0])), | ||
| 110 | zip(take(a[3].save, a[0]+1), repeat(a[2])) | ||
| 111 | ))() | ||
| 112 | .joiner(); | ||
| 113 | } | ||
| 114 | else static assert(0, "cartesianProduct of infinite ranges requires "~ | ||
| 115 | "forward ranges"); | ||
| 116 | } | ||
| 117 | else static if (isInputRange!R1 && isForwardRange!R2 && !isInfinite!R2) | ||
| 118 | { | ||
| 119 | import std.range : zip, repeat; | ||
| 120 | return joiner(map!((ElementType!R1 a) => zip(repeat(a), range2.save)) | ||
| 121 | (range1)); | ||
| 122 | } | ||
| 123 | else static if (isInputRange!R2 && isForwardRange!R1 && !isInfinite!R1) | ||
| 124 | { | ||
| 125 | import std.range : zip, repeat; | ||
| 126 | return joiner(map!((ElementType!R2 a) => zip(range1.save, repeat(a))) | ||
| 127 | (range2)); | ||
| 128 | } | ||
| 129 | else static assert(0, "cartesianProduct involving finite ranges must "~ | ||
| 130 | "have at least one finite forward range"); | ||
| 131 | } | ||
| 132 | |||
| 133 | /// | ||
| 134 | @safe unittest | ||
| 135 | { | ||
| 136 | import std.algorithm.searching : canFind; | ||
| 137 | import std.range; | ||
| 138 | import std.typecons : tuple; | ||
| 139 | |||
| 140 | auto N = sequence!"n"(0); // the range of natural numbers | ||
| 141 | auto N2 = cartesianProduct(N, N); // the range of all pairs of natural numbers | ||
| 142 | |||
| 143 | // Various arbitrary number pairs can be found in the range in finite time. | ||
| 144 | assert(canFind(N2, tuple(0, 0))); | ||
| 145 | assert(canFind(N2, tuple(123, 321))); | ||
| 146 | assert(canFind(N2, tuple(11, 35))); | ||
| 147 | assert(canFind(N2, tuple(279, 172))); | ||
| 148 | } | ||
| 149 | |||
| 150 | /// | ||
| 151 | @safe unittest | ||
| 152 | { | ||
| 153 | import std.algorithm.searching : canFind; | ||
| 154 | import std.typecons : tuple; | ||
| 155 | |||
| 156 | auto B = [ 1, 2, 3 ]; | ||
| 157 | auto C = [ 4, 5, 6 ]; | ||
| 158 | auto BC = cartesianProduct(B, C); | ||
| 159 | |||
| 160 | foreach (n; [[1, 4], [2, 4], [3, 4], [1, 5], [2, 5], [3, 5], [1, 6], | ||
| 161 | [2, 6], [3, 6]]) | ||
| 162 | { | ||
| 163 | assert(canFind(BC, tuple(n[0], n[1]))); | ||
| 164 | } | ||
| 165 | } | ||
| 166 | |||
| 167 | @safe unittest | ||
| 168 | { | ||
| 169 | // Test cartesian product of two infinite ranges | ||
| 170 | import std.algorithm.searching : canFind; | ||
| 171 | import std.range; | ||
| 172 | import std.typecons : tuple; | ||
| 173 | |||
| 174 | auto Even = sequence!"2*n"(0); | ||
| 175 | auto Odd = sequence!"2*n+1"(0); | ||
| 176 | auto EvenOdd = cartesianProduct(Even, Odd); | ||
| 177 | |||
| 178 | foreach (pair; [[0, 1], [2, 1], [0, 3], [2, 3], [4, 1], [4, 3], [0, 5], | ||
| 179 | [2, 5], [4, 5], [6, 1], [6, 3], [6, 5]]) | ||
| 180 | { | ||
| 181 | assert(canFind(EvenOdd, tuple(pair[0], pair[1]))); | ||
| 182 | } | ||
| 183 | |||
| 184 | // This should terminate in finite time | ||
| 185 | assert(canFind(EvenOdd, tuple(124, 73))); | ||
| 186 | assert(canFind(EvenOdd, tuple(0, 97))); | ||
| 187 | assert(canFind(EvenOdd, tuple(42, 1))); | ||
| 188 | } | ||
| 189 | |||
| 190 | @safe unittest | ||
| 191 | { | ||
| 192 | // Test cartesian product of an infinite input range and a finite forward | ||
| 193 | // range. | ||
| 194 | import std.algorithm.searching : canFind; | ||
| 195 | import std.range; | ||
| 196 | import std.typecons : tuple; | ||
| 197 | |||
| 198 | auto N = sequence!"n"(0); | ||
| 199 | auto M = [100, 200, 300]; | ||
| 200 | auto NM = cartesianProduct(N,M); | ||
| 201 | |||
| 202 | foreach (pair; [[0, 100], [0, 200], [0, 300], [1, 100], [1, 200], [1, 300], | ||
| 203 | [2, 100], [2, 200], [2, 300], [3, 100], [3, 200], | ||
| 204 | [3, 300]]) | ||
| 205 | { | ||
| 206 | assert(canFind(NM, tuple(pair[0], pair[1]))); | ||
| 207 | } | ||
| 208 | |||
| 209 | // We can't solve the halting problem, so we can only check a finite | ||
| 210 | // initial segment here. | ||
| 211 | assert(!canFind(NM.take(100), tuple(100, 0))); | ||
| 212 | assert(!canFind(NM.take(100), tuple(1, 1))); | ||
| 213 | assert(!canFind(NM.take(100), tuple(100, 200))); | ||
| 214 | |||
| 215 | auto MN = cartesianProduct(M,N); | ||
| 216 | foreach (pair; [[100, 0], [200, 0], [300, 0], [100, 1], [200, 1], [300, 1], | ||
| 217 | [100, 2], [200, 2], [300, 2], [100, 3], [200, 3], | ||
| 218 | [300, 3]]) | ||
| 219 | { | ||
| 220 | assert(canFind(MN, tuple(pair[0], pair[1]))); | ||
| 221 | } | ||
| 222 | |||
| 223 | // We can't solve the halting problem, so we can only check a finite | ||
| 224 | // initial segment here. | ||
| 225 | assert(!canFind(MN.take(100), tuple(0, 100))); | ||
| 226 | assert(!canFind(MN.take(100), tuple(0, 1))); | ||
| 227 | assert(!canFind(MN.take(100), tuple(100, 200))); | ||
| 228 | } | ||
| 229 | |||
| 230 | @safe unittest | ||
| 231 | { | ||
| 232 | import std.algorithm.searching : canFind; | ||
| 233 | import std.typecons : tuple; | ||
| 234 | |||
| 235 | // Test cartesian product of two finite ranges. | ||
| 236 | auto X = [1, 2, 3]; | ||
| 237 | auto Y = [4, 5, 6]; | ||
| 238 | auto XY = cartesianProduct(X, Y); | ||
| 239 | auto Expected = [[1, 4], [1, 5], [1, 6], [2, 4], [2, 5], [2, 6], [3, 4], | ||
| 240 | [3, 5], [3, 6]]; | ||
| 241 | |||
| 242 | // Verify Expected ⊆ XY | ||
| 243 | foreach (pair; Expected) | ||
| 244 | { | ||
| 245 | assert(canFind(XY, tuple(pair[0], pair[1]))); | ||
| 246 | } | ||
| 247 | |||
| 248 | // Verify XY ⊆ Expected | ||
| 249 | foreach (pair; XY) | ||
| 250 | { | ||
| 251 | assert(canFind(Expected, [pair[0], pair[1]])); | ||
| 252 | } | ||
| 253 | |||
| 254 | // And therefore, by set comprehension, XY == Expected | ||
| 255 | } | ||
| 256 | |||
| 257 | @safe unittest | ||
| 258 | { | ||
| 259 | import std.algorithm.comparison : equal; | ||
| 260 | import std.algorithm.iteration : map; | ||
| 261 | import std.algorithm.searching : canFind; | ||
| 262 | import std.typecons : tuple; | ||
| 263 | |||
| 264 | import std.range; | ||
| 265 | auto N = sequence!"n"(0); | ||
| 266 | |||
| 267 | // To force the template to fall to the second case, we wrap N in a struct | ||
| 268 | // that doesn't allow bidirectional access. | ||
| 269 | struct FwdRangeWrapper(R) | ||
| 270 | { | ||
| 271 | R impl; | ||
| 272 | |||
| 273 | // Input range API | ||
| 274 | @property auto front() { return impl.front; } | ||
| 275 | void popFront() { impl.popFront(); } | ||
| 276 | static if (isInfinite!R) | ||
| 277 | enum empty = false; | ||
| 278 | else | ||
| 279 | @property bool empty() { return impl.empty; } | ||
| 280 | |||
| 281 | // Forward range API | ||
| 282 | @property auto save() { return typeof(this)(impl.save); } | ||
| 283 | } | ||
| 284 | auto fwdWrap(R)(R range) { return FwdRangeWrapper!R(range); } | ||
| 285 | |||
| 286 | // General test: two infinite bidirectional ranges | ||
| 287 | auto N2 = cartesianProduct(N, N); | ||
| 288 | |||
| 289 | assert(canFind(N2, tuple(0, 0))); | ||
| 290 | assert(canFind(N2, tuple(123, 321))); | ||
| 291 | assert(canFind(N2, tuple(11, 35))); | ||
| 292 | assert(canFind(N2, tuple(279, 172))); | ||
| 293 | |||
| 294 | // Test first case: forward range with bidirectional range | ||
| 295 | auto fwdN = fwdWrap(N); | ||
| 296 | auto N2_a = cartesianProduct(fwdN, N); | ||
| 297 | |||
| 298 | assert(canFind(N2_a, tuple(0, 0))); | ||
| 299 | assert(canFind(N2_a, tuple(123, 321))); | ||
| 300 | assert(canFind(N2_a, tuple(11, 35))); | ||
| 301 | assert(canFind(N2_a, tuple(279, 172))); | ||
| 302 | |||
| 303 | // Test second case: bidirectional range with forward range | ||
| 304 | auto N2_b = cartesianProduct(N, fwdN); | ||
| 305 | |||
| 306 | assert(canFind(N2_b, tuple(0, 0))); | ||
| 307 | assert(canFind(N2_b, tuple(123, 321))); | ||
| 308 | assert(canFind(N2_b, tuple(11, 35))); | ||
| 309 | assert(canFind(N2_b, tuple(279, 172))); | ||
| 310 | |||
| 311 | // Test third case: finite forward range with (infinite) input range | ||
| 312 | static struct InpRangeWrapper(R) | ||
| 313 | { | ||
| 314 | R impl; | ||
| 315 | |||
| 316 | // Input range API | ||
| 317 | @property auto front() { return impl.front; } | ||
| 318 | void popFront() { impl.popFront(); } | ||
| 319 | static if (isInfinite!R) | ||
| 320 | enum empty = false; | ||
| 321 | else | ||
| 322 | @property bool empty() { return impl.empty; } | ||
| 323 | } | ||
| 324 | auto inpWrap(R)(R r) { return InpRangeWrapper!R(r); } | ||
| 325 | |||
| 326 | auto inpN = inpWrap(N); | ||
| 327 | auto B = [ 1, 2, 3 ]; | ||
| 328 | auto fwdB = fwdWrap(B); | ||
| 329 | auto BN = cartesianProduct(fwdB, inpN); | ||
| 330 | |||
| 331 | assert(equal(map!"[a[0],a[1]]"(BN.take(10)), [[1, 0], [2, 0], [3, 0], | ||
| 332 | [1, 1], [2, 1], [3, 1], [1, 2], [2, 2], [3, 2], [1, 3]])); | ||
| 333 | |||
| 334 | // Test fourth case: (infinite) input range with finite forward range | ||
| 335 | auto NB = cartesianProduct(inpN, fwdB); | ||
| 336 | |||
| 337 | assert(equal(map!"[a[0],a[1]]"(NB.take(10)), [[0, 1], [0, 2], [0, 3], | ||
| 338 | [1, 1], [1, 2], [1, 3], [2, 1], [2, 2], [2, 3], [3, 1]])); | ||
| 339 | |||
| 340 | // General finite range case | ||
| 341 | auto C = [ 4, 5, 6 ]; | ||
| 342 | auto BC = cartesianProduct(B, C); | ||
| 343 | |||
| 344 | foreach (n; [[1, 4], [2, 4], [3, 4], [1, 5], [2, 5], [3, 5], [1, 6], | ||
| 345 | [2, 6], [3, 6]]) | ||
| 346 | { | ||
| 347 | assert(canFind(BC, tuple(n[0], n[1]))); | ||
| 348 | } | ||
| 349 | } | ||
| 350 | |||
| 351 | // Issue 13091 | ||
| 352 | pure nothrow @safe @nogc unittest | ||
| 353 | { | ||
| 354 | int[1] a = [1]; | ||
| 355 | foreach (t; cartesianProduct(a[], a[])) {} | ||
| 356 | } | ||
| 357 | |||
| 358 | /// ditto | ||
| 359 | auto cartesianProduct(RR...)(RR ranges) | ||
| 360 | if (ranges.length >= 2 && | ||
| 361 | allSatisfy!(isForwardRange, RR) && | ||
| 362 | !anySatisfy!(isInfinite, RR)) | ||
| 363 | { | ||
| 364 | // This overload uses a much less template-heavy implementation when | ||
| 365 | // all ranges are finite forward ranges, which is the most common use | ||
| 366 | // case, so that we don't run out of resources too quickly. | ||
| 367 | // | ||
| 368 | // For infinite ranges or non-forward ranges, we fall back to the old | ||
| 369 | // implementation which expands an exponential number of templates. | ||
| 370 | import std.typecons : tuple; | ||
| 371 | |||
| 372 | static struct Result | ||
| 373 | { | ||
| 374 | RR ranges; | ||
| 375 | RR current; | ||
| 376 | bool empty = true; | ||
| 377 | |||
| 378 | this(RR _ranges) | ||
| 379 | { | ||
| 380 | ranges = _ranges; | ||
| 381 | empty = false; | ||
| 382 | foreach (i, r; ranges) | ||
| 383 | { | ||
| 384 | current[i] = r.save; | ||
| 385 | if (current[i].empty) | ||
| 386 | empty = true; | ||
| 387 | } | ||
| 388 | } | ||
| 389 | @property auto front() | ||
| 390 | { | ||
| 391 | import std.algorithm.internal : algoFormat; | ||
| 392 | import std.range : iota; | ||
| 393 | return mixin(algoFormat("tuple(%(current[%d].front%|,%))", | ||
| 394 | iota(0, current.length))); | ||
| 395 | } | ||
| 396 | void popFront() | ||
| 397 | { | ||
| 398 | foreach_reverse (i, ref r; current) | ||
| 399 | { | ||
| 400 | r.popFront(); | ||
| 401 | if (!r.empty) break; | ||
| 402 | |||
| 403 | static if (i == 0) | ||
| 404 | empty = true; | ||
| 405 | else | ||
| 406 | r = ranges[i].save; // rollover | ||
| 407 | } | ||
| 408 | } | ||
| 409 | @property Result save() | ||
| 410 | { | ||
| 411 | Result copy = this; | ||
| 412 | foreach (i, r; ranges) | ||
| 413 | { | ||
| 414 | copy.ranges[i] = r.save; | ||
| 415 | copy.current[i] = current[i].save; | ||
| 416 | } | ||
| 417 | return copy; | ||
| 418 | } | ||
| 419 | } | ||
| 420 | static assert(isForwardRange!Result); | ||
| 421 | |||
| 422 | return Result(ranges); | ||
| 423 | } | ||
| 424 | |||
| 425 | @safe unittest | ||
| 426 | { | ||
| 427 | // Issue 10693: cartesian product of empty ranges should be empty. | ||
| 428 | int[] a, b, c, d, e; | ||
| 429 | auto cprod = cartesianProduct(a,b,c,d,e); | ||
| 430 | assert(cprod.empty); | ||
| 431 | foreach (_; cprod) {} // should not crash | ||
| 432 | |||
| 433 | // Test case where only one of the ranges is empty: the result should still | ||
| 434 | // be empty. | ||
| 435 | int[] p=[1], q=[]; | ||
| 436 | auto cprod2 = cartesianProduct(p,p,p,q,p); | ||
| 437 | assert(cprod2.empty); | ||
| 438 | foreach (_; cprod2) {} // should not crash | ||
| 439 | } | ||
| 440 | |||
| 441 | @safe unittest | ||
| 442 | { | ||
| 443 | // .init value of cartesianProduct should be empty | ||
| 444 | auto cprod = cartesianProduct([0,0], [1,1], [2,2]); | ||
| 445 | assert(!cprod.empty); | ||
| 446 | assert(cprod.init.empty); | ||
| 447 | } | ||
| 448 | |||
| 449 | @safe unittest | ||
| 450 | { | ||
| 451 | // Issue 13393 | ||
| 452 | assert(!cartesianProduct([0],[0],[0]).save.empty); | ||
| 453 | } | ||
| 454 | |||
| 455 | /// ditto | ||
| 456 | auto cartesianProduct(R1, R2, RR...)(R1 range1, R2 range2, RR otherRanges) | ||
| 457 | if (!allSatisfy!(isForwardRange, R1, R2, RR) || | ||
| 458 | anySatisfy!(isInfinite, R1, R2, RR)) | ||
| 459 | { | ||
| 460 | /* We implement the n-ary cartesian product by recursively invoking the | ||
| 461 | * binary cartesian product. To make the resulting range nicer, we denest | ||
| 462 | * one level of tuples so that a ternary cartesian product, for example, | ||
| 463 | * returns 3-element tuples instead of nested 2-element tuples. | ||
| 464 | */ | ||
| 465 | import std.algorithm.internal : algoFormat; | ||
| 466 | import std.algorithm.iteration : map; | ||
| 467 | import std.range : iota; | ||
| 468 | |||
| 469 | enum string denest = algoFormat("tuple(a[0], %(a[1][%d]%|,%))", | ||
| 470 | iota(0, otherRanges.length+1)); | ||
| 471 | return map!denest( | ||
| 472 | cartesianProduct(range1, cartesianProduct(range2, otherRanges)) | ||
| 473 | ); | ||
| 474 | } | ||
| 475 | |||
| 476 | @safe unittest | ||
| 477 | { | ||
| 478 | import std.algorithm.searching : canFind; | ||
| 479 | import std.range; | ||
| 480 | import std.typecons : tuple, Tuple; | ||
| 481 | |||
| 482 | auto N = sequence!"n"(0); | ||
| 483 | auto N3 = cartesianProduct(N, N, N); | ||
| 484 | |||
| 485 | // Check that tuples are properly denested | ||
| 486 | assert(is(ElementType!(typeof(N3)) == Tuple!(size_t,size_t,size_t))); | ||
| 487 | |||
| 488 | assert(canFind(N3, tuple(0, 27, 7))); | ||
| 489 | assert(canFind(N3, tuple(50, 23, 71))); | ||
| 490 | assert(canFind(N3, tuple(9, 3, 0))); | ||
| 491 | } | ||
| 492 | |||
| 493 | @safe unittest | ||
| 494 | { | ||
| 495 | import std.algorithm.searching : canFind; | ||
| 496 | import std.range; | ||
| 497 | import std.typecons : tuple, Tuple; | ||
| 498 | |||
| 499 | auto N = sequence!"n"(0); | ||
| 500 | auto N4 = cartesianProduct(N, N, N, N); | ||
| 501 | |||
| 502 | // Check that tuples are properly denested | ||
| 503 | assert(is(ElementType!(typeof(N4)) == Tuple!(size_t,size_t,size_t,size_t))); | ||
| 504 | |||
| 505 | assert(canFind(N4, tuple(1, 2, 3, 4))); | ||
| 506 | assert(canFind(N4, tuple(4, 3, 2, 1))); | ||
| 507 | assert(canFind(N4, tuple(10, 31, 7, 12))); | ||
| 508 | } | ||
| 509 | |||
| 510 | // Issue 9878 | ||
| 511 | /// | ||
| 512 | @safe unittest | ||
| 513 | { | ||
| 514 | import std.algorithm.comparison : equal; | ||
| 515 | import std.typecons : tuple; | ||
| 516 | |||
| 517 | auto A = [ 1, 2, 3 ]; | ||
| 518 | auto B = [ 'a', 'b', 'c' ]; | ||
| 519 | auto C = [ "x", "y", "z" ]; | ||
| 520 | auto ABC = cartesianProduct(A, B, C); | ||
| 521 | |||
| 522 | assert(ABC.equal([ | ||
| 523 | tuple(1, 'a', "x"), tuple(1, 'a', "y"), tuple(1, 'a', "z"), | ||
| 524 | tuple(1, 'b', "x"), tuple(1, 'b', "y"), tuple(1, 'b', "z"), | ||
| 525 | tuple(1, 'c', "x"), tuple(1, 'c', "y"), tuple(1, 'c', "z"), | ||
| 526 | tuple(2, 'a', "x"), tuple(2, 'a', "y"), tuple(2, 'a', "z"), | ||
| 527 | tuple(2, 'b', "x"), tuple(2, 'b', "y"), tuple(2, 'b', "z"), | ||
| 528 | tuple(2, 'c', "x"), tuple(2, 'c', "y"), tuple(2, 'c', "z"), | ||
| 529 | tuple(3, 'a', "x"), tuple(3, 'a', "y"), tuple(3, 'a', "z"), | ||
| 530 | tuple(3, 'b', "x"), tuple(3, 'b', "y"), tuple(3, 'b', "z"), | ||
| 531 | tuple(3, 'c', "x"), tuple(3, 'c', "y"), tuple(3, 'c', "z") | ||
| 532 | ])); | ||
| 533 | } | ||
| 534 | |||
| 535 | pure @safe nothrow @nogc unittest | ||
| 536 | { | ||
| 537 | import std.range.primitives : isForwardRange; | ||
| 538 | int[2] A = [1,2]; | ||
| 539 | auto C = cartesianProduct(A[], A[], A[]); | ||
| 540 | assert(isForwardRange!(typeof(C))); | ||
| 541 | |||
| 542 | C.popFront(); | ||
| 543 | auto front1 = C.front; | ||
| 544 | auto D = C.save; | ||
| 545 | C.popFront(); | ||
| 546 | assert(D.front == front1); | ||
| 547 | } | ||
| 548 | |||
| 549 | // Issue 13935 | ||
| 550 | @safe unittest | ||
| 551 | { | ||
| 552 | import std.algorithm.iteration : map; | ||
| 553 | auto seq = [1, 2].map!(x => x); | ||
| 554 | foreach (pair; cartesianProduct(seq, seq)) {} | ||
| 555 | } | ||
| 556 | |||
| 557 | // largestPartialIntersection | ||
| 558 | /** | ||
| 559 | Given a range of sorted $(REF_ALTTEXT forward ranges, isForwardRange, std,range,primitives) | ||
| 560 | $(D ror), copies to $(D tgt) the elements that are common to most ranges, along with their number | ||
| 561 | of occurrences. All ranges in $(D ror) are assumed to be sorted by $(D | ||
| 562 | less). Only the most frequent $(D tgt.length) elements are returned. | ||
| 563 | |||
| 564 | Params: | ||
| 565 | less = The predicate the ranges are sorted by. | ||
| 566 | ror = A range of forward ranges sorted by `less`. | ||
| 567 | tgt = The target range to copy common elements to. | ||
| 568 | sorted = Whether the elements copied should be in sorted order. | ||
| 569 | |||
| 570 | The function $(D largestPartialIntersection) is useful for | ||
| 571 | e.g. searching an $(LINK2 https://en.wikipedia.org/wiki/Inverted_index, | ||
| 572 | inverted index) for the documents most | ||
| 573 | likely to contain some terms of interest. The complexity of the search | ||
| 574 | is $(BIGOH n * log(tgt.length)), where $(D n) is the sum of lengths of | ||
| 575 | all input ranges. This approach is faster than keeping an associative | ||
| 576 | array of the occurrences and then selecting its top items, and also | ||
| 577 | requires less memory ($(D largestPartialIntersection) builds its | ||
| 578 | result directly in $(D tgt) and requires no extra memory). | ||
| 579 | |||
| 580 | If at least one of the ranges is a multiset, then all occurences | ||
| 581 | of a duplicate element are taken into account. The result is | ||
| 582 | equivalent to merging all ranges and picking the most frequent | ||
| 583 | $(D tgt.length) elements. | ||
| 584 | |||
| 585 | Warning: Because $(D largestPartialIntersection) does not allocate | ||
| 586 | extra memory, it will leave $(D ror) modified. Namely, $(D | ||
| 587 | largestPartialIntersection) assumes ownership of $(D ror) and | ||
| 588 | discretionarily swaps and advances elements of it. If you want $(D | ||
| 589 | ror) to preserve its contents after the call, you may want to pass a | ||
| 590 | duplicate to $(D largestPartialIntersection) (and perhaps cache the | ||
| 591 | duplicate in between calls). | ||
| 592 | */ | ||
| 593 | void largestPartialIntersection | ||
| 594 | (alias less = "a < b", RangeOfRanges, Range) | ||
| 595 | (RangeOfRanges ror, Range tgt, SortOutput sorted = No.sortOutput) | ||
| 596 | { | ||
| 597 | struct UnitWeights | ||
| 598 | { | ||
| 599 | static int opIndex(ElementType!(ElementType!RangeOfRanges)) { return 1; } | ||
| 600 | } | ||
| 601 | return largestPartialIntersectionWeighted!less(ror, tgt, UnitWeights(), | ||
| 602 | sorted); | ||
| 603 | } | ||
| 604 | |||
| 605 | /// | ||
| 606 | @system unittest | ||
| 607 | { | ||
| 608 | import std.typecons : tuple, Tuple; | ||
| 609 | |||
| 610 | // Figure which number can be found in most arrays of the set of | ||
| 611 | // arrays below. | ||
| 612 | double[][] a = | ||
| 613 | [ | ||
| 614 | [ 1, 4, 7, 8 ], | ||
| 615 | [ 1, 7 ], | ||
| 616 | [ 1, 7, 8], | ||
| 617 | [ 4 ], | ||
| 618 | [ 7 ], | ||
| 619 | ]; | ||
| 620 | auto b = new Tuple!(double, uint)[1]; | ||
| 621 | // it will modify the input range, hence we need to create a duplicate | ||
| 622 | largestPartialIntersection(a.dup, b); | ||
| 623 | // First member is the item, second is the occurrence count | ||
| 624 | assert(b[0] == tuple(7.0, 4u)); | ||
| 625 | // 7.0 occurs in 4 out of 5 inputs, more than any other number | ||
| 626 | |||
| 627 | // If more of the top-frequent numbers are needed, just create a larger | ||
| 628 | // tgt range | ||
| 629 | auto c = new Tuple!(double, uint)[2]; | ||
| 630 | largestPartialIntersection(a, c); | ||
| 631 | assert(c[0] == tuple(1.0, 3u)); | ||
| 632 | // 1.0 occurs in 3 inputs | ||
| 633 | |||
| 634 | // multiset | ||
| 635 | double[][] x = | ||
| 636 | [ | ||
| 637 | [1, 1, 1, 1, 4, 7, 8], | ||
| 638 | [1, 7], | ||
| 639 | [1, 7, 8], | ||
| 640 | [4, 7], | ||
| 641 | [7] | ||
| 642 | ]; | ||
| 643 | auto y = new Tuple!(double, uint)[2]; | ||
| 644 | largestPartialIntersection(x.dup, y); | ||
| 645 | // 7.0 occurs 5 times | ||
| 646 | assert(y[0] == tuple(7.0, 5u)); | ||
| 647 | // 1.0 occurs 6 times | ||
| 648 | assert(y[1] == tuple(1.0, 6u)); | ||
| 649 | } | ||
| 650 | |||
| 651 | import std.algorithm.sorting : SortOutput; // FIXME | ||
| 652 | |||
| 653 | // largestPartialIntersectionWeighted | ||
| 654 | /** | ||
| 655 | Similar to $(D largestPartialIntersection), but associates a weight | ||
| 656 | with each distinct element in the intersection. | ||
| 657 | |||
| 658 | If at least one of the ranges is a multiset, then all occurences | ||
| 659 | of a duplicate element are taken into account. The result | ||
| 660 | is equivalent to merging all input ranges and picking the highest | ||
| 661 | $(D tgt.length), weight-based ranking elements. | ||
| 662 | |||
| 663 | Params: | ||
| 664 | less = The predicate the ranges are sorted by. | ||
| 665 | ror = A range of $(REF_ALTTEXT forward ranges, isForwardRange, std,range,primitives) | ||
| 666 | sorted by `less`. | ||
| 667 | tgt = The target range to copy common elements to. | ||
| 668 | weights = An associative array mapping elements to weights. | ||
| 669 | sorted = Whether the elements copied should be in sorted order. | ||
| 670 | |||
| 671 | */ | ||
| 672 | void largestPartialIntersectionWeighted | ||
| 673 | (alias less = "a < b", RangeOfRanges, Range, WeightsAA) | ||
| 674 | (RangeOfRanges ror, Range tgt, WeightsAA weights, SortOutput sorted = No.sortOutput) | ||
| 675 | { | ||
| 676 | import std.algorithm.iteration : group; | ||
| 677 | import std.algorithm.sorting : topNCopy; | ||
| 678 | |||
| 679 | if (tgt.empty) return; | ||
| 680 | alias InfoType = ElementType!Range; | ||
| 681 | bool heapComp(InfoType a, InfoType b) | ||
| 682 | { | ||
| 683 | return weights[a[0]] * a[1] > weights[b[0]] * b[1]; | ||
| 684 | } | ||
| 685 | topNCopy!heapComp(group(multiwayMerge!less(ror)), tgt, sorted); | ||
| 686 | } | ||
| 687 | |||
| 688 | /// | ||
| 689 | @system unittest | ||
| 690 | { | ||
| 691 | import std.typecons : tuple, Tuple; | ||
| 692 | |||
| 693 | // Figure which number can be found in most arrays of the set of | ||
| 694 | // arrays below, with specific per-element weights | ||
| 695 | double[][] a = | ||
| 696 | [ | ||
| 697 | [ 1, 4, 7, 8 ], | ||
| 698 | [ 1, 7 ], | ||
| 699 | [ 1, 7, 8], | ||
| 700 | [ 4 ], | ||
| 701 | [ 7 ], | ||
| 702 | ]; | ||
| 703 | auto b = new Tuple!(double, uint)[1]; | ||
| 704 | double[double] weights = [ 1:1.2, 4:2.3, 7:1.1, 8:1.1 ]; | ||
| 705 | largestPartialIntersectionWeighted(a, b, weights); | ||
| 706 | // First member is the item, second is the occurrence count | ||
| 707 | assert(b[0] == tuple(4.0, 2u)); | ||
| 708 | // 4.0 occurs 2 times -> 4.6 (2 * 2.3) | ||
| 709 | // 7.0 occurs 3 times -> 4.4 (3 * 1.1) | ||
| 710 | |||
| 711 | // multiset | ||
| 712 | double[][] x = | ||
| 713 | [ | ||
| 714 | [ 1, 1, 1, 4, 7, 8 ], | ||
| 715 | [ 1, 7 ], | ||
| 716 | [ 1, 7, 8], | ||
| 717 | [ 4 ], | ||
| 718 | [ 7 ], | ||
| 719 | ]; | ||
| 720 | auto y = new Tuple!(double, uint)[1]; | ||
| 721 | largestPartialIntersectionWeighted(x, y, weights); | ||
| 722 | assert(y[0] == tuple(1.0, 5u)); | ||
| 723 | // 1.0 occurs 5 times -> 1.2 * 5 = 6 | ||
| 724 | } | ||
| 725 | |||
| 726 | @system unittest | ||
| 727 | { | ||
| 728 | import std.conv : text; | ||
| 729 | import std.typecons : tuple, Tuple, Yes; | ||
| 730 | |||
| 731 | double[][] a = | ||
| 732 | [ | ||
| 733 | [ 1, 4, 7, 8 ], | ||
| 734 | [ 1, 7 ], | ||
| 735 | [ 1, 7, 8], | ||
| 736 | [ 4 ], | ||
| 737 | [ 7 ], | ||
| 738 | ]; | ||
| 739 | auto b = new Tuple!(double, uint)[2]; | ||
| 740 | largestPartialIntersection(a, b, Yes.sortOutput); | ||
| 741 | assert(b == [ tuple(7.0, 4u), tuple(1.0, 3u) ][], text(b)); | ||
| 742 | assert(a[0].empty); | ||
| 743 | } | ||
| 744 | |||
| 745 | @system unittest | ||
| 746 | { | ||
| 747 | import std.conv : text; | ||
| 748 | import std.typecons : tuple, Tuple, Yes; | ||
| 749 | |||
| 750 | string[][] a = | ||
| 751 | [ | ||
| 752 | [ "1", "4", "7", "8" ], | ||
| 753 | [ "1", "7" ], | ||
| 754 | [ "1", "7", "8"], | ||
| 755 | [ "4" ], | ||
| 756 | [ "7" ], | ||
| 757 | ]; | ||
| 758 | auto b = new Tuple!(string, uint)[2]; | ||
| 759 | largestPartialIntersection(a, b, Yes.sortOutput); | ||
| 760 | assert(b == [ tuple("7", 4u), tuple("1", 3u) ][], text(b)); | ||
| 761 | } | ||
| 762 | |||
| 763 | @system unittest | ||
| 764 | { | ||
| 765 | import std.typecons : tuple, Tuple; | ||
| 766 | |||
| 767 | // Figure which number can be found in most arrays of the set of | ||
| 768 | // arrays below, with specific per-element weights | ||
| 769 | double[][] a = | ||
| 770 | [ | ||
| 771 | [ 1, 4, 7, 8 ], | ||
| 772 | [ 1, 7 ], | ||
| 773 | [ 1, 7, 8], | ||
| 774 | [ 4 ], | ||
| 775 | [ 7 ], | ||
| 776 | ]; | ||
| 777 | auto b = new Tuple!(double, uint)[1]; | ||
| 778 | double[double] weights = [ 1:1.2, 4:2.3, 7:1.1, 8:1.1 ]; | ||
| 779 | largestPartialIntersectionWeighted(a, b, weights); | ||
| 780 | // First member is the item, second is the occurrence count | ||
| 781 | assert(b[0] == tuple(4.0, 2u)); | ||
| 782 | } | ||
| 783 | |||
| 784 | @system unittest | ||
| 785 | { | ||
| 786 | import std.container : Array; | ||
| 787 | import std.typecons : Tuple; | ||
| 788 | |||
| 789 | alias T = Tuple!(uint, uint); | ||
| 790 | const Array!T arrayOne = Array!T( [ T(1,2), T(3,4) ] ); | ||
| 791 | const Array!T arrayTwo = Array!T([ T(1,2), T(3,4) ] ); | ||
| 792 | |||
| 793 | assert(arrayOne == arrayTwo); | ||
| 794 | } | ||
| 795 | |||
| 796 | // MultiwayMerge | ||
| 797 | /** | ||
| 798 | Merges multiple sets. The input sets are passed as a | ||
| 799 | range of ranges and each is assumed to be sorted by $(D | ||
| 800 | less). Computation is done lazily, one union element at a time. The | ||
| 801 | complexity of one $(D popFront) operation is $(BIGOH | ||
| 802 | log(ror.length)). However, the length of $(D ror) decreases as ranges | ||
| 803 | in it are exhausted, so the complexity of a full pass through $(D | ||
| 804 | MultiwayMerge) is dependent on the distribution of the lengths of ranges | ||
| 805 | contained within $(D ror). If all ranges have the same length $(D n) | ||
| 806 | (worst case scenario), the complexity of a full pass through $(D | ||
| 807 | MultiwayMerge) is $(BIGOH n * ror.length * log(ror.length)), i.e., $(D | ||
| 808 | log(ror.length)) times worse than just spanning all ranges in | ||
| 809 | turn. The output comes sorted (unstably) by $(D less). | ||
| 810 | |||
| 811 | The length of the resulting range is the sum of all lengths of | ||
| 812 | the ranges passed as input. This means that all elements (duplicates | ||
| 813 | included) are transferred to the resulting range. | ||
| 814 | |||
| 815 | For backward compatibility, `multiwayMerge` is available under | ||
| 816 | the name `nWayUnion` and `MultiwayMerge` under the name of `NWayUnion` . | ||
| 817 | Future code should use `multiwayMerge` and `MultiwayMerge` as `nWayUnion` | ||
| 818 | and `NWayUnion` will be deprecated. | ||
| 819 | |||
| 820 | Params: | ||
| 821 | less = Predicate the given ranges are sorted by. | ||
| 822 | ror = A range of ranges sorted by `less` to compute the union for. | ||
| 823 | |||
| 824 | Returns: | ||
| 825 | A range of the union of the ranges in `ror`. | ||
| 826 | |||
| 827 | Warning: Because $(D MultiwayMerge) does not allocate extra memory, it | ||
| 828 | will leave $(D ror) modified. Namely, $(D MultiwayMerge) assumes ownership | ||
| 829 | of $(D ror) and discretionarily swaps and advances elements of it. If | ||
| 830 | you want $(D ror) to preserve its contents after the call, you may | ||
| 831 | want to pass a duplicate to $(D MultiwayMerge) (and perhaps cache the | ||
| 832 | duplicate in between calls). | ||
| 833 | */ | ||
| 834 | struct MultiwayMerge(alias less, RangeOfRanges) | ||
| 835 | { | ||
| 836 | import std.container : BinaryHeap; | ||
| 837 | |||
| 838 | private alias ElementType = .ElementType!(.ElementType!RangeOfRanges); | ||
| 839 | private alias comp = binaryFun!less; | ||
| 840 | private RangeOfRanges _ror; | ||
| 841 | |||
| 842 | /// | ||
| 843 | static bool compFront(.ElementType!RangeOfRanges a, | ||
| 844 | .ElementType!RangeOfRanges b) | ||
| 845 | { | ||
| 846 | // revert comparison order so we get the smallest elements first | ||
| 847 | return comp(b.front, a.front); | ||
| 848 | } | ||
| 849 | private BinaryHeap!(RangeOfRanges, compFront) _heap; | ||
| 850 | |||
| 851 | /// | ||
| 852 | this(RangeOfRanges ror) | ||
| 853 | { | ||
| 854 | import std.algorithm.mutation : remove, SwapStrategy; | ||
| 855 | |||
| 856 | // Preemptively get rid of all empty ranges in the input | ||
| 857 | // No need for stability either | ||
| 858 | _ror = remove!("a.empty", SwapStrategy.unstable)(ror); | ||
| 859 | //Build the heap across the range | ||
| 860 | _heap.acquire(_ror); | ||
| 861 | } | ||
| 862 | |||
| 863 | /// | ||
| 864 | @property bool empty() { return _ror.empty; } | ||
| 865 | |||
| 866 | /// | ||
| 867 | @property auto ref front() | ||
| 868 | { | ||
| 869 | return _heap.front.front; | ||
| 870 | } | ||
| 871 | |||
| 872 | /// | ||
| 873 | void popFront() | ||
| 874 | { | ||
| 875 | _heap.removeFront(); | ||
| 876 | // let's look at the guy just popped | ||
| 877 | _ror.back.popFront(); | ||
| 878 | if (_ror.back.empty) | ||
| 879 | { | ||
| 880 | _ror.popBack(); | ||
| 881 | // nothing else to do: the empty range is not in the | ||
| 882 | // heap and not in _ror | ||
| 883 | return; | ||
| 884 | } | ||
| 885 | // Put the popped range back in the heap | ||
| 886 | _heap.conditionalInsert(_ror.back) || assert(false); | ||
| 887 | } | ||
| 888 | } | ||
| 889 | |||
| 890 | /// Ditto | ||
| 891 | MultiwayMerge!(less, RangeOfRanges) multiwayMerge | ||
| 892 | (alias less = "a < b", RangeOfRanges) | ||
| 893 | (RangeOfRanges ror) | ||
| 894 | { | ||
| 895 | return typeof(return)(ror); | ||
| 896 | } | ||
| 897 | |||
| 898 | /// | ||
| 899 | @system unittest | ||
| 900 | { | ||
| 901 | import std.algorithm.comparison : equal; | ||
| 902 | |||
| 903 | double[][] a = | ||
| 904 | [ | ||
| 905 | [ 1, 4, 7, 8 ], | ||
| 906 | [ 1, 7 ], | ||
| 907 | [ 1, 7, 8], | ||
| 908 | [ 4 ], | ||
| 909 | [ 7 ], | ||
| 910 | ]; | ||
| 911 | auto witness = [ | ||
| 912 | 1, 1, 1, 4, 4, 7, 7, 7, 7, 8, 8 | ||
| 913 | ]; | ||
| 914 | assert(equal(multiwayMerge(a), witness)); | ||
| 915 | |||
| 916 | double[][] b = | ||
| 917 | [ | ||
| 918 | // range with duplicates | ||
| 919 | [ 1, 1, 4, 7, 8 ], | ||
| 920 | [ 7 ], | ||
| 921 | [ 1, 7, 8], | ||
| 922 | [ 4 ], | ||
| 923 | [ 7 ], | ||
| 924 | ]; | ||
| 925 | // duplicates are propagated to the resulting range | ||
| 926 | assert(equal(multiwayMerge(b), witness)); | ||
| 927 | } | ||
| 928 | |||
| 929 | alias nWayUnion = multiwayMerge; | ||
| 930 | alias NWayUnion = MultiwayMerge; | ||
| 931 | |||
| 932 | /** | ||
| 933 | Computes the union of multiple ranges. The input ranges are passed | ||
| 934 | as a range of ranges and each is assumed to be sorted by $(D | ||
| 935 | less). Computation is done lazily, one union element at a time. | ||
| 936 | `multiwayUnion(ror)` is functionally equivalent to `multiwayMerge(ror).uniq`. | ||
| 937 | |||
| 938 | "The output of multiwayUnion has no duplicates even when its inputs contain duplicates." | ||
| 939 | |||
| 940 | Params: | ||
| 941 | less = Predicate the given ranges are sorted by. | ||
| 942 | ror = A range of ranges sorted by `less` to compute the intersection for. | ||
| 943 | |||
| 944 | Returns: | ||
| 945 | A range of the union of the ranges in `ror`. | ||
| 946 | |||
| 947 | See also: $(LREF multiwayMerge) | ||
| 948 | */ | ||
| 949 | auto multiwayUnion(alias less = "a < b", RangeOfRanges)(RangeOfRanges ror) | ||
| 950 | { | ||
| 951 | import std.algorithm.iteration : uniq; | ||
| 952 | return ror.multiwayMerge.uniq; | ||
| 953 | } | ||
| 954 | |||
| 955 | /// | ||
| 956 | @system unittest | ||
| 957 | { | ||
| 958 | import std.algorithm.comparison : equal; | ||
| 959 | |||
| 960 | // sets | ||
| 961 | double[][] a = | ||
| 962 | [ | ||
| 963 | [ 1, 4, 7, 8 ], | ||
| 964 | [ 1, 7 ], | ||
| 965 | [ 1, 7, 8], | ||
| 966 | [ 4 ], | ||
| 967 | [ 7 ], | ||
| 968 | ]; | ||
| 969 | |||
| 970 | auto witness = [1, 4, 7, 8]; | ||
| 971 | assert(equal(multiwayUnion(a), witness)); | ||
| 972 | |||
| 973 | // multisets | ||
| 974 | double[][] b = | ||
| 975 | [ | ||
| 976 | [ 1, 1, 1, 4, 7, 8 ], | ||
| 977 | [ 1, 7 ], | ||
| 978 | [ 1, 7, 7, 8], | ||
| 979 | [ 4 ], | ||
| 980 | [ 7 ], | ||
| 981 | ]; | ||
| 982 | assert(equal(multiwayUnion(b), witness)); | ||
| 983 | } | ||
| 984 | |||
| 985 | /** | ||
| 986 | Lazily computes the difference of $(D r1) and $(D r2). The two ranges | ||
| 987 | are assumed to be sorted by $(D less). The element types of the two | ||
| 988 | ranges must have a common type. | ||
| 989 | |||
| 990 | |||
| 991 | In the case of multisets, considering that element `a` appears `x` | ||
| 992 | times in $(D r1) and `y` times and $(D r2), the number of occurences | ||
| 993 | of `a` in the resulting range is going to be `x-y` if x > y or 0 othwerise. | ||
| 994 | |||
| 995 | Params: | ||
| 996 | less = Predicate the given ranges are sorted by. | ||
| 997 | r1 = The first range. | ||
| 998 | r2 = The range to subtract from `r1`. | ||
| 999 | |||
| 1000 | Returns: | ||
| 1001 | A range of the difference of `r1` and `r2`. | ||
| 1002 | |||
| 1003 | See_also: $(LREF setSymmetricDifference) | ||
| 1004 | */ | ||
| 1005 | struct SetDifference(alias less = "a < b", R1, R2) | ||
| 1006 | if (isInputRange!(R1) && isInputRange!(R2)) | ||
| 1007 | { | ||
| 1008 | private: | ||
| 1009 | R1 r1; | ||
| 1010 | R2 r2; | ||
| 1011 | alias comp = binaryFun!(less); | ||
| 1012 | |||
| 1013 | void adjustPosition() | ||
| 1014 | { | ||
| 1015 | while (!r1.empty) | ||
| 1016 | { | ||
| 1017 | if (r2.empty || comp(r1.front, r2.front)) break; | ||
| 1018 | if (comp(r2.front, r1.front)) | ||
| 1019 | { | ||
| 1020 | r2.popFront(); | ||
| 1021 | } | ||
| 1022 | else | ||
| 1023 | { | ||
| 1024 | // both are equal | ||
| 1025 | r1.popFront(); | ||
| 1026 | r2.popFront(); | ||
| 1027 | } | ||
| 1028 | } | ||
| 1029 | } | ||
| 1030 | |||
| 1031 | public: | ||
| 1032 | /// | ||
| 1033 | this(R1 r1, R2 r2) | ||
| 1034 | { | ||
| 1035 | this.r1 = r1; | ||
| 1036 | this.r2 = r2; | ||
| 1037 | // position to the first element | ||
| 1038 | adjustPosition(); | ||
| 1039 | } | ||
| 1040 | |||
| 1041 | /// | ||
| 1042 | void popFront() | ||
| 1043 | { | ||
| 1044 | r1.popFront(); | ||
| 1045 | adjustPosition(); | ||
| 1046 | } | ||
| 1047 | |||
| 1048 | /// | ||
| 1049 | @property auto ref front() | ||
| 1050 | { | ||
| 1051 | assert(!empty); | ||
| 1052 | return r1.front; | ||
| 1053 | } | ||
| 1054 | |||
| 1055 | static if (isForwardRange!R1 && isForwardRange!R2) | ||
| 1056 | { | ||
| 1057 | /// | ||
| 1058 | @property typeof(this) save() | ||
| 1059 | { | ||
| 1060 | auto ret = this; | ||
| 1061 | ret.r1 = r1.save; | ||
| 1062 | ret.r2 = r2.save; | ||
| 1063 | return ret; | ||
| 1064 | } | ||
| 1065 | } | ||
| 1066 | |||
| 1067 | /// | ||
| 1068 | @property bool empty() { return r1.empty; } | ||
| 1069 | } | ||
| 1070 | |||
| 1071 | /// Ditto | ||
| 1072 | SetDifference!(less, R1, R2) setDifference(alias less = "a < b", R1, R2) | ||
| 1073 | (R1 r1, R2 r2) | ||
| 1074 | { | ||
| 1075 | return typeof(return)(r1, r2); | ||
| 1076 | } | ||
| 1077 | |||
| 1078 | /// | ||
| 1079 | @safe unittest | ||
| 1080 | { | ||
| 1081 | import std.algorithm.comparison : equal; | ||
| 1082 | import std.range.primitives : isForwardRange; | ||
| 1083 | |||
| 1084 | //sets | ||
| 1085 | int[] a = [ 1, 2, 4, 5, 7, 9 ]; | ||
| 1086 | int[] b = [ 0, 1, 2, 4, 7, 8 ]; | ||
| 1087 | assert(equal(setDifference(a, b), [5, 9])); | ||
| 1088 | static assert(isForwardRange!(typeof(setDifference(a, b)))); | ||
| 1089 | |||
| 1090 | // multisets | ||
| 1091 | int[] x = [1, 1, 1, 2, 3]; | ||
| 1092 | int[] y = [1, 1, 2, 4, 5]; | ||
| 1093 | auto r = setDifference(x, y); | ||
| 1094 | assert(equal(r, [1, 3])); | ||
| 1095 | assert(setDifference(r, x).empty); | ||
| 1096 | } | ||
| 1097 | |||
| 1098 | @safe unittest // Issue 10460 | ||
| 1099 | { | ||
| 1100 | import std.algorithm.comparison : equal; | ||
| 1101 | |||
| 1102 | int[] a = [1, 2, 3, 4, 5]; | ||
| 1103 | int[] b = [2, 4]; | ||
| 1104 | foreach (ref e; setDifference(a, b)) | ||
| 1105 | e = 0; | ||
| 1106 | assert(equal(a, [0, 2, 0, 4, 0])); | ||
| 1107 | } | ||
| 1108 | |||
| 1109 | /** | ||
| 1110 | Lazily computes the intersection of two or more input ranges $(D | ||
| 1111 | ranges). The ranges are assumed to be sorted by $(D less). The element | ||
| 1112 | types of the ranges must have a common type. | ||
| 1113 | |||
| 1114 | In the case of multisets, the range with the minimum number of | ||
| 1115 | occurences of a given element, propagates the number of | ||
| 1116 | occurences of this element to the resulting range. | ||
| 1117 | |||
| 1118 | Params: | ||
| 1119 | less = Predicate the given ranges are sorted by. | ||
| 1120 | ranges = The ranges to compute the intersection for. | ||
| 1121 | |||
| 1122 | Returns: | ||
| 1123 | A range containing the intersection of the given ranges. | ||
| 1124 | */ | ||
| 1125 | struct SetIntersection(alias less = "a < b", Rs...) | ||
| 1126 | if (Rs.length >= 2 && allSatisfy!(isInputRange, Rs) && | ||
| 1127 | !is(CommonType!(staticMap!(ElementType, Rs)) == void)) | ||
| 1128 | { | ||
| 1129 | private: | ||
| 1130 | Rs _input; | ||
| 1131 | alias comp = binaryFun!less; | ||
| 1132 | alias ElementType = CommonType!(staticMap!(.ElementType, Rs)); | ||
| 1133 | |||
| 1134 | // Positions to the first elements that are all equal | ||
| 1135 | void adjustPosition() | ||
| 1136 | { | ||
| 1137 | if (empty) return; | ||
| 1138 | |||
| 1139 | size_t done = Rs.length; | ||
| 1140 | static if (Rs.length > 1) while (true) | ||
| 1141 | { | ||
| 1142 | foreach (i, ref r; _input) | ||
| 1143 | { | ||
| 1144 | alias next = _input[(i + 1) % Rs.length]; | ||
| 1145 | |||
| 1146 | if (comp(next.front, r.front)) | ||
| 1147 | { | ||
| 1148 | do | ||
| 1149 | { | ||
| 1150 | next.popFront(); | ||
| 1151 | if (next.empty) return; | ||
| 1152 | } while (comp(next.front, r.front)); | ||
| 1153 | done = Rs.length; | ||
| 1154 | } | ||
| 1155 | if (--done == 0) return; | ||
| 1156 | } | ||
| 1157 | } | ||
| 1158 | } | ||
| 1159 | |||
| 1160 | public: | ||
| 1161 | /// | ||
| 1162 | this(Rs input) | ||
| 1163 | { | ||
| 1164 | this._input = input; | ||
| 1165 | // position to the first element | ||
| 1166 | adjustPosition(); | ||
| 1167 | } | ||
| 1168 | |||
| 1169 | /// | ||
| 1170 | @property bool empty() | ||
| 1171 | { | ||
| 1172 | foreach (ref r; _input) | ||
| 1173 | { | ||
| 1174 | if (r.empty) return true; | ||
| 1175 | } | ||
| 1176 | return false; | ||
| 1177 | } | ||
| 1178 | |||
| 1179 | /// | ||
| 1180 | void popFront() | ||
| 1181 | { | ||
| 1182 | assert(!empty); | ||
| 1183 | static if (Rs.length > 1) foreach (i, ref r; _input) | ||
| 1184 | { | ||
| 1185 | alias next = _input[(i + 1) % Rs.length]; | ||
| 1186 | assert(!comp(r.front, next.front)); | ||
| 1187 | } | ||
| 1188 | |||
| 1189 | foreach (ref r; _input) | ||
| 1190 | { | ||
| 1191 | r.popFront(); | ||
| 1192 | } | ||
| 1193 | adjustPosition(); | ||
| 1194 | } | ||
| 1195 | |||
| 1196 | /// | ||
| 1197 | @property ElementType front() | ||
| 1198 | { | ||
| 1199 | assert(!empty); | ||
| 1200 | return _input[0].front; | ||
| 1201 | } | ||
| 1202 | |||
| 1203 | static if (allSatisfy!(isForwardRange, Rs)) | ||
| 1204 | { | ||
| 1205 | /// | ||
| 1206 | @property SetIntersection save() | ||
| 1207 | { | ||
| 1208 | auto ret = this; | ||
| 1209 | foreach (i, ref r; _input) | ||
| 1210 | { | ||
| 1211 | ret._input[i] = r.save; | ||
| 1212 | } | ||
| 1213 | return ret; | ||
| 1214 | } | ||
| 1215 | } | ||
| 1216 | } | ||
| 1217 | |||
| 1218 | /// Ditto | ||
| 1219 | SetIntersection!(less, Rs) setIntersection(alias less = "a < b", Rs...)(Rs ranges) | ||
| 1220 | if (Rs.length >= 2 && allSatisfy!(isInputRange, Rs) && | ||
| 1221 | !is(CommonType!(staticMap!(ElementType, Rs)) == void)) | ||
| 1222 | { | ||
| 1223 | return typeof(return)(ranges); | ||
| 1224 | } | ||
| 1225 | |||
| 1226 | /// | ||
| 1227 | @safe unittest | ||
| 1228 | { | ||
| 1229 | import std.algorithm.comparison : equal; | ||
| 1230 | |||
| 1231 | // sets | ||
| 1232 | int[] a = [ 1, 2, 4, 5, 7, 9 ]; | ||
| 1233 | int[] b = [ 0, 1, 2, 4, 7, 8 ]; | ||
| 1234 | int[] c = [ 0, 1, 4, 5, 7, 8 ]; | ||
| 1235 | assert(equal(setIntersection(a, a), a)); | ||
| 1236 | assert(equal(setIntersection(a, b), [1, 2, 4, 7])); | ||
| 1237 | assert(equal(setIntersection(a, b, c), [1, 4, 7])); | ||
| 1238 | |||
| 1239 | // multisets | ||
| 1240 | int[] d = [ 1, 1, 2, 2, 7, 7 ]; | ||
| 1241 | int[] e = [ 1, 1, 1, 7]; | ||
| 1242 | assert(equal(setIntersection(a, d), [1, 2, 7])); | ||
| 1243 | assert(equal(setIntersection(d, e), [1, 1, 7])); | ||
| 1244 | } | ||
| 1245 | |||
| 1246 | @safe unittest | ||
| 1247 | { | ||
| 1248 | import std.algorithm.comparison : equal; | ||
| 1249 | import std.algorithm.iteration : filter; | ||
| 1250 | |||
| 1251 | int[] a = [ 1, 2, 4, 5, 7, 9 ]; | ||
| 1252 | int[] b = [ 0, 1, 2, 4, 7, 8 ]; | ||
| 1253 | int[] c = [ 0, 1, 4, 5, 7, 8 ]; | ||
| 1254 | int[] d = [ 1, 3, 4 ]; | ||
| 1255 | int[] e = [ 4, 5 ]; | ||
| 1256 | |||
| 1257 | assert(equal(setIntersection(a, a), a)); | ||
| 1258 | assert(equal(setIntersection(a, a, a), a)); | ||
| 1259 | assert(equal(setIntersection(a, b), [1, 2, 4, 7])); | ||
| 1260 | assert(equal(setIntersection(a, b, c), [1, 4, 7])); | ||
| 1261 | assert(equal(setIntersection(a, b, c, d), [1, 4])); | ||
| 1262 | assert(equal(setIntersection(a, b, c, d, e), [4])); | ||
| 1263 | |||
| 1264 | auto inpA = a.filter!(_ => true), inpB = b.filter!(_ => true); | ||
| 1265 | auto inpC = c.filter!(_ => true), inpD = d.filter!(_ => true); | ||
| 1266 | assert(equal(setIntersection(inpA, inpB, inpC, inpD), [1, 4])); | ||
| 1267 | |||
| 1268 | assert(equal(setIntersection(a, b, b, a), [1, 2, 4, 7])); | ||
| 1269 | assert(equal(setIntersection(a, c, b), [1, 4, 7])); | ||
| 1270 | assert(equal(setIntersection(b, a, c), [1, 4, 7])); | ||
| 1271 | assert(equal(setIntersection(b, c, a), [1, 4, 7])); | ||
| 1272 | assert(equal(setIntersection(c, a, b), [1, 4, 7])); | ||
| 1273 | assert(equal(setIntersection(c, b, a), [1, 4, 7])); | ||
| 1274 | } | ||
| 1275 | |||
| 1276 | /** | ||
| 1277 | Lazily computes the symmetric difference of $(D r1) and $(D r2), | ||
| 1278 | i.e. the elements that are present in exactly one of $(D r1) and $(D | ||
| 1279 | r2). The two ranges are assumed to be sorted by $(D less), and the | ||
| 1280 | output is also sorted by $(D less). The element types of the two | ||
| 1281 | ranges must have a common type. | ||
| 1282 | |||
| 1283 | If both ranges are sets (without duplicated elements), the resulting | ||
| 1284 | range is going to be a set. If at least one of the ranges is a multiset, | ||
| 1285 | the number of occurences of an element `x` in the resulting range is `abs(a-b)` | ||
| 1286 | where `a` is the number of occurences of `x` in $(D r1), `b` is the number of | ||
| 1287 | occurences of `x` in $(D r2), and `abs` is the absolute value. | ||
| 1288 | |||
| 1289 | If both arguments are ranges of L-values of the same type then | ||
| 1290 | $(D SetSymmetricDifference) will also be a range of L-values of | ||
| 1291 | that type. | ||
| 1292 | |||
| 1293 | Params: | ||
| 1294 | less = Predicate the given ranges are sorted by. | ||
| 1295 | r1 = The first range. | ||
| 1296 | r2 = The second range. | ||
| 1297 | |||
| 1298 | Returns: | ||
| 1299 | A range of the symmetric difference between `r1` and `r2`. | ||
| 1300 | |||
| 1301 | See_also: $(LREF setDifference) | ||
| 1302 | */ | ||
| 1303 | struct SetSymmetricDifference(alias less = "a < b", R1, R2) | ||
| 1304 | if (isInputRange!(R1) && isInputRange!(R2)) | ||
| 1305 | { | ||
| 1306 | private: | ||
| 1307 | R1 r1; | ||
| 1308 | R2 r2; | ||
| 1309 | //bool usingR2; | ||
| 1310 | alias comp = binaryFun!(less); | ||
| 1311 | |||
| 1312 | void adjustPosition() | ||
| 1313 | { | ||
| 1314 | while (!r1.empty && !r2.empty) | ||
| 1315 | { | ||
| 1316 | if (comp(r1.front, r2.front) || comp(r2.front, r1.front)) | ||
| 1317 | { | ||
| 1318 | break; | ||
| 1319 | } | ||
| 1320 | // equal, pop both | ||
| 1321 | r1.popFront(); | ||
| 1322 | r2.popFront(); | ||
| 1323 | } | ||
| 1324 | } | ||
| 1325 | |||
| 1326 | public: | ||
| 1327 | /// | ||
| 1328 | this(R1 r1, R2 r2) | ||
| 1329 | { | ||
| 1330 | this.r1 = r1; | ||
| 1331 | this.r2 = r2; | ||
| 1332 | // position to the first element | ||
| 1333 | adjustPosition(); | ||
| 1334 | } | ||
| 1335 | |||
| 1336 | /// | ||
| 1337 | void popFront() | ||
| 1338 | { | ||
| 1339 | assert(!empty); | ||
| 1340 | if (r1.empty) r2.popFront(); | ||
| 1341 | else if (r2.empty) r1.popFront(); | ||
| 1342 | else | ||
| 1343 | { | ||
| 1344 | // neither is empty | ||
| 1345 | if (comp(r1.front, r2.front)) | ||
| 1346 | { | ||
| 1347 | r1.popFront(); | ||
| 1348 | } | ||
| 1349 | else | ||
| 1350 | { | ||
| 1351 | assert(comp(r2.front, r1.front)); | ||
| 1352 | r2.popFront(); | ||
| 1353 | } | ||
| 1354 | } | ||
| 1355 | adjustPosition(); | ||
| 1356 | } | ||
| 1357 | |||
| 1358 | /// | ||
| 1359 | @property auto ref front() | ||
| 1360 | { | ||
| 1361 | assert(!empty); | ||
| 1362 | immutable chooseR1 = r2.empty || !r1.empty && comp(r1.front, r2.front); | ||
| 1363 | assert(chooseR1 || r1.empty || comp(r2.front, r1.front)); | ||
| 1364 | return chooseR1 ? r1.front : r2.front; | ||
| 1365 | } | ||
| 1366 | |||
| 1367 | static if (isForwardRange!R1 && isForwardRange!R2) | ||
| 1368 | { | ||
| 1369 | /// | ||
| 1370 | @property typeof(this) save() | ||
| 1371 | { | ||
| 1372 | auto ret = this; | ||
| 1373 | ret.r1 = r1.save; | ||
| 1374 | ret.r2 = r2.save; | ||
| 1375 | return ret; | ||
| 1376 | } | ||
| 1377 | } | ||
| 1378 | |||
| 1379 | /// | ||
| 1380 | ref auto opSlice() { return this; } | ||
| 1381 | |||
| 1382 | /// | ||
| 1383 | @property bool empty() { return r1.empty && r2.empty; } | ||
| 1384 | } | ||
| 1385 | |||
| 1386 | /// Ditto | ||
| 1387 | SetSymmetricDifference!(less, R1, R2) | ||
| 1388 | setSymmetricDifference(alias less = "a < b", R1, R2) | ||
| 1389 | (R1 r1, R2 r2) | ||
| 1390 | { | ||
| 1391 | return typeof(return)(r1, r2); | ||
| 1392 | } | ||
| 1393 | |||
| 1394 | /// | ||
| 1395 | @safe unittest | ||
| 1396 | { | ||
| 1397 | import std.algorithm.comparison : equal; | ||
| 1398 | import std.range.primitives : isForwardRange; | ||
| 1399 | |||
| 1400 | // sets | ||
| 1401 | int[] a = [ 1, 2, 4, 5, 7, 9 ]; | ||
| 1402 | int[] b = [ 0, 1, 2, 4, 7, 8 ]; | ||
| 1403 | assert(equal(setSymmetricDifference(a, b), [0, 5, 8, 9][])); | ||
| 1404 | static assert(isForwardRange!(typeof(setSymmetricDifference(a, b)))); | ||
| 1405 | |||
| 1406 | //mutisets | ||
| 1407 | int[] c = [1, 1, 1, 1, 2, 2, 2, 4, 5, 6]; | ||
| 1408 | int[] d = [1, 1, 2, 2, 2, 2, 4, 7, 9]; | ||
| 1409 | assert(equal(setSymmetricDifference(c, d), setSymmetricDifference(d, c))); | ||
| 1410 | assert(equal(setSymmetricDifference(c, d), [1, 1, 2, 5, 6, 7, 9])); | ||
| 1411 | } | ||
| 1412 | |||
| 1413 | @safe unittest // Issue 10460 | ||
| 1414 | { | ||
| 1415 | import std.algorithm.comparison : equal; | ||
| 1416 | |||
| 1417 | int[] a = [1, 2]; | ||
| 1418 | double[] b = [2.0, 3.0]; | ||
| 1419 | int[] c = [2, 3]; | ||
| 1420 | |||
| 1421 | alias R1 = typeof(setSymmetricDifference(a, b)); | ||
| 1422 | static assert(is(ElementType!R1 == double)); | ||
| 1423 | static assert(!hasLvalueElements!R1); | ||
| 1424 | |||
| 1425 | alias R2 = typeof(setSymmetricDifference(a, c)); | ||
| 1426 | static assert(is(ElementType!R2 == int)); | ||
| 1427 | static assert(hasLvalueElements!R2); | ||
| 1428 | |||
| 1429 | assert(equal(setSymmetricDifference(a, b), [1.0, 3.0])); | ||
| 1430 | assert(equal(setSymmetricDifference(a, c), [1, 3])); | ||
| 1431 | } | ||
| 1432 | |||
| 1433 | /++ | ||
| 1434 | TODO: once SetUnion got deprecated we can provide the usual definition | ||
| 1435 | (= merge + filter after uniqs) | ||
| 1436 | See: https://github.com/dlang/phobos/pull/4249 | ||
| 1437 | /** | ||
| 1438 | Lazily computes the union of two or more ranges $(D rs). The ranges | ||
| 1439 | are assumed to be sorted by $(D less). Elements in the output are | ||
| 1440 | unique. The element types of all ranges must have a common type. | ||
| 1441 | |||
| 1442 | Params: | ||
| 1443 | less = Predicate the given ranges are sorted by. | ||
| 1444 | rs = The ranges to compute the union for. | ||
| 1445 | |||
| 1446 | Returns: | ||
| 1447 | A range containing the unique union of the given ranges. | ||
| 1448 | |||
| 1449 | See_Also: | ||
| 1450 | $(REF merge, std,algorithm,sorting) | ||
| 1451 | */ | ||
| 1452 | auto setUnion(alias less = "a < b", Rs...) | ||
| 1453 | (Rs rs) | ||
| 1454 | { | ||
| 1455 | import std.algorithm.iteration : uniq; | ||
| 1456 | import std.algorithm.sorting : merge; | ||
| 1457 | return merge!(less, Rs)(rs).uniq; | ||
| 1458 | } | ||
| 1459 | |||
| 1460 | /// | ||
| 1461 | @safe pure nothrow unittest | ||
| 1462 | /// | ||
| 1463 | { | ||
| 1464 | import std.algorithm.comparison : equal; | ||
| 1465 | |||
| 1466 | int[] a = [1, 3, 5]; | ||
| 1467 | int[] b = [2, 3, 4]; | ||
| 1468 | assert(a.setUnion(b).equal([1, 2, 3, 4, 5])); | ||
| 1469 | } | ||
| 1470 | |||
| 1471 | @safe pure nothrow unittest | ||
| 1472 | { | ||
| 1473 | import std.algorithm.comparison : equal; | ||
| 1474 | |||
| 1475 | int[] a = [ 1, 2, 4, 5, 7, 9 ]; | ||
| 1476 | int[] b = [ 0, 1, 2, 4, 7, 8 ]; | ||
| 1477 | double[] c = [ 10.5 ]; | ||
| 1478 | |||
| 1479 | assert(equal(setUnion(a, b), [0, 1, 2, 4, 5, 7, 8, 9][])); | ||
| 1480 | assert(equal(setUnion(a, c, b), | ||
| 1481 | [0, 1, 2, 4, 5, 7, 8, 9, 10.5][])); | ||
| 1482 | } | ||
| 1483 | |||
| 1484 | @safe unittest | ||
| 1485 | { | ||
| 1486 | // save | ||
| 1487 | import std.range : dropOne; | ||
| 1488 | int[] a = [0, 1, 2]; | ||
| 1489 | int[] b = [0, 3]; | ||
| 1490 | auto arr = a.setUnion(b); | ||
| 1491 | assert(arr.front == 0); | ||
| 1492 | assert(arr.save.dropOne.front == 1); | ||
| 1493 | assert(arr.front == 0); | ||
| 1494 | } | ||
| 1495 | |||
| 1496 | @nogc @safe pure nothrow unittest | ||
| 1497 | { | ||
| 1498 | import std.algorithm.comparison : equal; | ||
| 1499 | |||
| 1500 | static immutable a = [1, 3, 5]; | ||
| 1501 | static immutable b = [2, 4]; | ||
| 1502 | static immutable r = [1, 2, 3, 4, 5]; | ||
| 1503 | assert(a.setUnion(b).equal(r)); | ||
| 1504 | } | ||
| 1505 | |||
| 1506 | @safe pure nothrow unittest | ||
| 1507 | { | ||
| 1508 | import std.algorithm.comparison : equal; | ||
| 1509 | import std.internal.test.dummyrange; | ||
| 1510 | import std.range : iota; | ||
| 1511 | |||
| 1512 | auto dummyResult1 = [1, 1.5, 2, 3, 4, 5, 5.5, 6, 7, 8, 9, 10]; | ||
| 1513 | auto dummyResult2 = iota(1, 11); | ||
| 1514 | foreach (DummyType; AllDummyRanges) | ||
| 1515 | { | ||
| 1516 | DummyType d; | ||
| 1517 | assert(d.setUnion([1, 1.5, 5.5]).equal(dummyResult1)); | ||
| 1518 | assert(d.setUnion(d).equal(dummyResult2)); | ||
| 1519 | } | ||
| 1520 | } | ||
| 1521 | ++/ | ||
| @@ -0,0 +1,4468 @@ | |||
| 1 | // Written in the D programming language. | ||
| 2 | /** | ||
| 3 | This is a submodule of $(MREF std, algorithm). | ||
| 4 | It contains generic _sorting algorithms. | ||
| 5 | |||
| 6 | $(SCRIPT inhibitQuickIndex = 1;) | ||
| 7 | $(BOOKTABLE Cheat Sheet, | ||
| 8 | $(TR $(TH Function Name) $(TH Description)) | ||
| 9 | $(T2 completeSort, | ||
| 10 | If $(D a = [10, 20, 30]) and $(D b = [40, 6, 15]), then | ||
| 11 | $(D completeSort(a, b)) leaves $(D a = [6, 10, 15]) and $(D b = [20, | ||
| 12 | 30, 40]). | ||
| 13 | The range $(D a) must be sorted prior to the call, and as a result the | ||
| 14 | combination $(D $(REF chain, std,range)(a, b)) is sorted.) | ||
| 15 | $(T2 isPartitioned, | ||
| 16 | $(D isPartitioned!"a < 0"([-1, -2, 1, 0, 2])) returns $(D true) because | ||
| 17 | the predicate is $(D true) for a portion of the range and $(D false) | ||
| 18 | afterwards.) | ||
| 19 | $(T2 isSorted, | ||
| 20 | $(D isSorted([1, 1, 2, 3])) returns $(D true).) | ||
| 21 | $(T2 isStrictlyMonotonic, | ||
| 22 | $(D isStrictlyMonotonic([1, 1, 2, 3])) returns $(D false).) | ||
| 23 | $(T2 ordered, | ||
| 24 | $(D ordered(1, 1, 2, 3)) returns $(D true).) | ||
| 25 | $(T2 strictlyOrdered, | ||
| 26 | $(D strictlyOrdered(1, 1, 2, 3)) returns $(D false).) | ||
| 27 | $(T2 makeIndex, | ||
| 28 | Creates a separate index for a range.) | ||
| 29 | $(T2 merge, | ||
| 30 | Lazily merges two or more sorted ranges.) | ||
| 31 | $(T2 multiSort, | ||
| 32 | Sorts by multiple keys.) | ||
| 33 | $(T2 nextEvenPermutation, | ||
| 34 | Computes the next lexicographically greater even permutation of a range | ||
| 35 | in-place.) | ||
| 36 | $(T2 nextPermutation, | ||
| 37 | Computes the next lexicographically greater permutation of a range | ||
| 38 | in-place.) | ||
| 39 | $(T2 partialSort, | ||
| 40 | If $(D a = [5, 4, 3, 2, 1]), then $(D partialSort(a, 3)) leaves | ||
| 41 | $(D a[0 .. 3] = [1, 2, 3]). | ||
| 42 | The other elements of $(D a) are left in an unspecified order.) | ||
| 43 | $(T2 partition, | ||
| 44 | Partitions a range according to a unary predicate.) | ||
| 45 | $(T2 partition3, | ||
| 46 | Partitions a range according to a binary predicate in three parts (less | ||
| 47 | than, equal, greater than the given pivot). Pivot is not given as an | ||
| 48 | index, but instead as an element independent from the range's content.) | ||
| 49 | $(T2 pivotPartition, | ||
| 50 | Partitions a range according to a binary predicate in two parts: less | ||
| 51 | than or equal, and greater than or equal to the given pivot, passed as | ||
| 52 | an index in the range.) | ||
| 53 | $(T2 schwartzSort, | ||
| 54 | Sorts with the help of the $(LINK2 https://en.wikipedia.org/wiki/Schwartzian_transform, Schwartzian transform).) | ||
| 55 | $(T2 sort, | ||
| 56 | Sorts.) | ||
| 57 | $(T2 topN, | ||
| 58 | Separates the top elements in a range.) | ||
| 59 | $(T2 topNCopy, | ||
| 60 | Copies out the top elements of a range.) | ||
| 61 | $(T2 topNIndex, | ||
| 62 | Builds an index of the top elements of a range.) | ||
| 63 | ) | ||
| 64 | |||
| 65 | Copyright: Andrei Alexandrescu 2008-. | ||
| 66 | |||
| 67 | License: $(HTTP boost.org/LICENSE_1_0.txt, Boost License 1.0). | ||
| 68 | |||
| 69 | Authors: $(HTTP erdani.com, Andrei Alexandrescu) | ||
| 70 | |||
| 71 | Source: $(PHOBOSSRC std/algorithm/_sorting.d) | ||
| 72 | |||
| 73 | Macros: | ||
| 74 | T2=$(TR $(TDNW $(LREF $1)) $(TD $+)) | ||
| 75 | */ | ||
| 76 | module std.algorithm.sorting; | ||
| 77 | |||
| 78 | import std.algorithm.mutation : SwapStrategy; | ||
| 79 | import std.functional; // : unaryFun, binaryFun; | ||
| 80 | import std.range.primitives; | ||
| 81 | import std.typecons : Flag; | ||
| 82 | // FIXME | ||
| 83 | import std.meta; // : allSatisfy; | ||
| 84 | import std.range; // : SortedRange; | ||
| 85 | import std.traits; | ||
| 86 | |||
| 87 | /** | ||
| 88 | Specifies whether the output of certain algorithm is desired in sorted | ||
| 89 | format. | ||
| 90 | |||
| 91 | If set to $(D SortOutput.no), the output should not be sorted. | ||
| 92 | |||
| 93 | Otherwise if set to $(D SortOutput.yes), the output should be sorted. | ||
| 94 | */ | ||
| 95 | alias SortOutput = Flag!"sortOutput"; | ||
| 96 | |||
| 97 | // completeSort | ||
| 98 | /** | ||
| 99 | Sorts the random-access range $(D chain(lhs, rhs)) according to | ||
| 100 | predicate $(D less). The left-hand side of the range $(D lhs) is | ||
| 101 | assumed to be already sorted; $(D rhs) is assumed to be unsorted. The | ||
| 102 | exact strategy chosen depends on the relative sizes of $(D lhs) and | ||
| 103 | $(D rhs). Performs $(BIGOH lhs.length + rhs.length * log(rhs.length)) | ||
| 104 | (best case) to $(BIGOH (lhs.length + rhs.length) * log(lhs.length + | ||
| 105 | rhs.length)) (worst-case) evaluations of $(D swap). | ||
| 106 | |||
| 107 | Params: | ||
| 108 | less = The predicate to sort by. | ||
| 109 | ss = The swapping strategy to use. | ||
| 110 | lhs = The sorted, left-hand side of the random access range to be sorted. | ||
| 111 | rhs = The unsorted, right-hand side of the random access range to be | ||
| 112 | sorted. | ||
| 113 | */ | ||
| 114 | void completeSort(alias less = "a < b", SwapStrategy ss = SwapStrategy.unstable, | ||
| 115 | RandomAccessRange1, RandomAccessRange2)(SortedRange!(RandomAccessRange1, less) lhs, RandomAccessRange2 rhs) | ||
| 116 | if (hasLength!(RandomAccessRange2) && hasSlicing!(RandomAccessRange2)) | ||
| 117 | { | ||
| 118 | import std.algorithm.mutation : bringToFront; | ||
| 119 | import std.range : chain, assumeSorted; | ||
| 120 | // Probably this algorithm can be optimized by using in-place | ||
| 121 | // merge | ||
| 122 | auto lhsOriginal = lhs.release(); | ||
| 123 | foreach (i; 0 .. rhs.length) | ||
| 124 | { | ||
| 125 | auto sortedSoFar = chain(lhsOriginal, rhs[0 .. i]); | ||
| 126 | auto ub = assumeSorted!less(sortedSoFar).upperBound(rhs[i]); | ||
| 127 | if (!ub.length) continue; | ||
| 128 | bringToFront(ub.release(), rhs[i .. i + 1]); | ||
| 129 | } | ||
| 130 | } | ||
| 131 | |||
| 132 | /// | ||
| 133 | @safe unittest | ||
| 134 | { | ||
| 135 | import std.range : assumeSorted; | ||
| 136 | int[] a = [ 1, 2, 3 ]; | ||
| 137 | int[] b = [ 4, 0, 6, 5 ]; | ||
| 138 | completeSort(assumeSorted(a), b); | ||
| 139 | assert(a == [ 0, 1, 2 ]); | ||
| 140 | assert(b == [ 3, 4, 5, 6 ]); | ||
| 141 | } | ||
| 142 | |||
| 143 | // isSorted | ||
| 144 | /** | ||
| 145 | Checks whether a $(REF_ALTTEXT forward range, isForwardRange, std,range,primitives) | ||
| 146 | is sorted according to the comparison operation $(D less). Performs $(BIGOH r.length) | ||
| 147 | evaluations of $(D less). | ||
| 148 | |||
| 149 | Unlike `isSorted`, `isStrictlyMonotonic` does not allow for equal values, | ||
| 150 | i.e. values for which both `less(a, b)` and `less(b, a)` are false. | ||
| 151 | |||
| 152 | With either function, the predicate must be a strict ordering just like with | ||
| 153 | `isSorted`. For example, using `"a <= b"` instead of `"a < b"` is | ||
| 154 | incorrect and will cause failed assertions. | ||
| 155 | |||
| 156 | Params: | ||
| 157 | less = Predicate the range should be sorted by. | ||
| 158 | r = Forward range to check for sortedness. | ||
| 159 | |||
| 160 | Returns: | ||
| 161 | `true` if the range is sorted, false otherwise. `isSorted` allows | ||
| 162 | duplicates, `isStrictlyMonotonic` not. | ||
| 163 | */ | ||
| 164 | bool isSorted(alias less = "a < b", Range)(Range r) | ||
| 165 | if (isForwardRange!(Range)) | ||
| 166 | { | ||
| 167 | if (r.empty) return true; | ||
| 168 | |||
| 169 | static if (isRandomAccessRange!Range && hasLength!Range) | ||
| 170 | { | ||
| 171 | immutable limit = r.length - 1; | ||
| 172 | foreach (i; 0 .. limit) | ||
| 173 | { | ||
| 174 | if (!binaryFun!less(r[i + 1], r[i])) continue; | ||
| 175 | assert( | ||
| 176 | !binaryFun!less(r[i], r[i + 1]), | ||
| 177 | "Predicate for isSorted is not antisymmetric. Both" ~ | ||
| 178 | " pred(a, b) and pred(b, a) are true for certain values."); | ||
| 179 | return false; | ||
| 180 | } | ||
| 181 | } | ||
| 182 | else | ||
| 183 | { | ||
| 184 | auto ahead = r.save; | ||
| 185 | ahead.popFront(); | ||
| 186 | size_t i; | ||
| 187 | |||
| 188 | for (; !ahead.empty; ahead.popFront(), r.popFront(), ++i) | ||
| 189 | { | ||
| 190 | if (!binaryFun!less(ahead.front, r.front)) continue; | ||
| 191 | // Check for antisymmetric predicate | ||
| 192 | assert( | ||
| 193 | !binaryFun!less(r.front, ahead.front), | ||
| 194 | "Predicate for isSorted is not antisymmetric. Both" ~ | ||
| 195 | " pred(a, b) and pred(b, a) are true for certain values."); | ||
| 196 | return false; | ||
| 197 | } | ||
| 198 | } | ||
| 199 | return true; | ||
| 200 | } | ||
| 201 | |||
| 202 | /// | ||
| 203 | @safe unittest | ||
| 204 | { | ||
| 205 | assert([1, 1, 2].isSorted); | ||
| 206 | // strictly monotonic doesn't allow duplicates | ||
| 207 | assert(![1, 1, 2].isStrictlyMonotonic); | ||
| 208 | |||
| 209 | int[] arr = [4, 3, 2, 1]; | ||
| 210 | assert(!isSorted(arr)); | ||
| 211 | assert(!isStrictlyMonotonic(arr)); | ||
| 212 | |||
| 213 | assert(isSorted!"a > b"(arr)); | ||
| 214 | assert(isStrictlyMonotonic!"a > b"(arr)); | ||
| 215 | |||
| 216 | sort(arr); | ||
| 217 | assert(isSorted(arr)); | ||
| 218 | assert(isStrictlyMonotonic(arr)); | ||
| 219 | } | ||
| 220 | |||
| 221 | @safe unittest | ||
| 222 | { | ||
| 223 | import std.conv : to; | ||
| 224 | |||
| 225 | // Issue 9457 | ||
| 226 | auto x = "abcd"; | ||
| 227 | assert(isSorted(x)); | ||
| 228 | auto y = "acbd"; | ||
| 229 | assert(!isSorted(y)); | ||
| 230 | |||
| 231 | int[] a = [1, 2, 3]; | ||
| 232 | assert(isSorted(a)); | ||
| 233 | int[] b = [1, 3, 2]; | ||
| 234 | assert(!isSorted(b)); | ||
| 235 | |||
| 236 | // ignores duplicates | ||
| 237 | int[] c = [1, 1, 2]; | ||
| 238 | assert(isSorted(c)); | ||
| 239 | |||
| 240 | dchar[] ds = "コーヒーが好きです"d.dup; | ||
| 241 | sort(ds); | ||
| 242 | string s = to!string(ds); | ||
| 243 | assert(isSorted(ds)); // random-access | ||
| 244 | assert(isSorted(s)); // bidirectional | ||
| 245 | } | ||
| 246 | |||
| 247 | @nogc @safe nothrow pure unittest | ||
| 248 | { | ||
| 249 | static immutable a = [1, 2, 3]; | ||
| 250 | assert(a.isSorted); | ||
| 251 | } | ||
| 252 | |||
| 253 | /// ditto | ||
| 254 | bool isStrictlyMonotonic(alias less = "a < b", Range)(Range r) | ||
| 255 | if (isForwardRange!Range) | ||
| 256 | { | ||
| 257 | import std.algorithm.searching : findAdjacent; | ||
| 258 | return findAdjacent!((a,b) => !binaryFun!less(a,b))(r).empty; | ||
| 259 | } | ||
| 260 | |||
| 261 | @safe unittest | ||
| 262 | { | ||
| 263 | import std.conv : to; | ||
| 264 | |||
| 265 | assert("abcd".isStrictlyMonotonic); | ||
| 266 | assert(!"aacd".isStrictlyMonotonic); | ||
| 267 | assert(!"acb".isStrictlyMonotonic); | ||
| 268 | |||
| 269 | assert([1, 2, 3].isStrictlyMonotonic); | ||
| 270 | assert(![1, 3, 2].isStrictlyMonotonic); | ||
| 271 | assert(![1, 1, 2].isStrictlyMonotonic); | ||
| 272 | |||
| 273 | // ー occurs twice -> can't be strict | ||
| 274 | dchar[] ds = "コーヒーが好きです"d.dup; | ||
| 275 | sort(ds); | ||
| 276 | string s = to!string(ds); | ||
| 277 | assert(!isStrictlyMonotonic(ds)); // random-access | ||
| 278 | assert(!isStrictlyMonotonic(s)); // bidirectional | ||
| 279 | |||
| 280 | dchar[] ds2 = "コーヒが好きです"d.dup; | ||
| 281 | sort(ds2); | ||
| 282 | string s2 = to!string(ds2); | ||
| 283 | assert(isStrictlyMonotonic(ds2)); // random-access | ||
| 284 | assert(isStrictlyMonotonic(s2)); // bidirectional | ||
| 285 | } | ||
| 286 | |||
| 287 | @nogc @safe nothrow pure unittest | ||
| 288 | { | ||
| 289 | static immutable a = [1, 2, 3]; | ||
| 290 | assert(a.isStrictlyMonotonic); | ||
| 291 | } | ||
| 292 | |||
| 293 | /** | ||
| 294 | Like $(D isSorted), returns $(D true) if the given $(D values) are ordered | ||
| 295 | according to the comparison operation $(D less). Unlike $(D isSorted), takes values | ||
| 296 | directly instead of structured in a range. | ||
| 297 | |||
| 298 | $(D ordered) allows repeated values, e.g. $(D ordered(1, 1, 2)) is $(D true). To verify | ||
| 299 | that the values are ordered strictly monotonically, use $(D strictlyOrdered); | ||
| 300 | $(D strictlyOrdered(1, 1, 2)) is $(D false). | ||
| 301 | |||
| 302 | With either function, the predicate must be a strict ordering. For example, | ||
| 303 | using $(D "a <= b") instead of $(D "a < b") is incorrect and will cause failed | ||
| 304 | assertions. | ||
| 305 | |||
| 306 | Params: | ||
| 307 | values = The tested value | ||
| 308 | less = The comparison predicate | ||
| 309 | |||
| 310 | Returns: | ||
| 311 | $(D true) if the values are ordered; $(D ordered) allows for duplicates, | ||
| 312 | $(D strictlyOrdered) does not. | ||
| 313 | */ | ||
| 314 | |||
| 315 | bool ordered(alias less = "a < b", T...)(T values) | ||
| 316 | if ((T.length == 2 && is(typeof(binaryFun!less(values[1], values[0])) : bool)) | ||
| 317 | || | ||
| 318 | (T.length > 2 && is(typeof(ordered!less(values[0 .. 1 + $ / 2]))) | ||
| 319 | && is(typeof(ordered!less(values[$ / 2 .. $])))) | ||
| 320 | ) | ||
| 321 | { | ||
| 322 | foreach (i, _; T[0 .. $ - 1]) | ||
| 323 | { | ||
| 324 | if (binaryFun!less(values[i + 1], values[i])) | ||
| 325 | { | ||
| 326 | assert(!binaryFun!less(values[i], values[i + 1]), | ||
| 327 | __FUNCTION__ ~ ": incorrect non-strict predicate."); | ||
| 328 | return false; | ||
| 329 | } | ||
| 330 | } | ||
| 331 | return true; | ||
| 332 | } | ||
| 333 | |||
| 334 | /// ditto | ||
| 335 | bool strictlyOrdered(alias less = "a < b", T...)(T values) | ||
| 336 | if (is(typeof(ordered!less(values)))) | ||
| 337 | { | ||
| 338 | foreach (i, _; T[0 .. $ - 1]) | ||
| 339 | { | ||
| 340 | if (!binaryFun!less(values[i], values[i + 1])) | ||
| 341 | { | ||
| 342 | return false; | ||
| 343 | } | ||
| 344 | assert(!binaryFun!less(values[i + 1], values[i]), | ||
| 345 | __FUNCTION__ ~ ": incorrect non-strict predicate."); | ||
| 346 | } | ||
| 347 | return true; | ||
| 348 | } | ||
| 349 | |||
| 350 | /// | ||
| 351 | @safe unittest | ||
| 352 | { | ||
| 353 | assert(ordered(42, 42, 43)); | ||
| 354 | assert(!strictlyOrdered(43, 42, 45)); | ||
| 355 | assert(ordered(42, 42, 43)); | ||
| 356 | assert(!strictlyOrdered(42, 42, 43)); | ||
| 357 | assert(!ordered(43, 42, 45)); | ||
| 358 | // Ordered lexicographically | ||
| 359 | assert(ordered("Jane", "Jim", "Joe")); | ||
| 360 | assert(strictlyOrdered("Jane", "Jim", "Joe")); | ||
| 361 | // Incidentally also ordered by length decreasing | ||
| 362 | assert(ordered!((a, b) => a.length > b.length)("Jane", "Jim", "Joe")); | ||
| 363 | // ... but not strictly so: "Jim" and "Joe" have the same length | ||
| 364 | assert(!strictlyOrdered!((a, b) => a.length > b.length)("Jane", "Jim", "Joe")); | ||
| 365 | } | ||
| 366 | |||
| 367 | // partition | ||
| 368 | /** | ||
| 369 | Partitions a range in two using the given $(D predicate). | ||
| 370 | Specifically, reorders the range $(D r = [left, right$(RPAREN)) using $(D swap) | ||
| 371 | such that all elements $(D i) for which $(D predicate(i)) is $(D true) come | ||
| 372 | before all elements $(D j) for which $(D predicate(j)) returns $(D false). | ||
| 373 | |||
| 374 | Performs $(BIGOH r.length) (if unstable or semistable) or $(BIGOH | ||
| 375 | r.length * log(r.length)) (if stable) evaluations of $(D less) and $(D | ||
| 376 | swap). The unstable version computes the minimum possible evaluations | ||
| 377 | of $(D swap) (roughly half of those performed by the semistable | ||
| 378 | version). | ||
| 379 | |||
| 380 | Params: | ||
| 381 | predicate = The predicate to partition by. | ||
| 382 | ss = The swapping strategy to employ. | ||
| 383 | r = The random-access range to partition. | ||
| 384 | |||
| 385 | Returns: | ||
| 386 | |||
| 387 | The right part of $(D r) after partitioning. | ||
| 388 | |||
| 389 | If $(D ss == SwapStrategy.stable), $(D partition) preserves the relative | ||
| 390 | ordering of all elements $(D a), $(D b) in $(D r) for which $(D predicate(a) == | ||
| 391 | predicate(b)). If $(D ss == SwapStrategy.semistable), $(D partition) preserves | ||
| 392 | the relative ordering of all elements $(D a), $(D b) in the left part of $(D r) | ||
| 393 | for which $(D predicate(a) == predicate(b)). | ||
| 394 | |||
| 395 | See_Also: | ||
| 396 | STL's $(HTTP sgi.com/tech/stl/_partition.html, _partition)$(BR) | ||
| 397 | STL's $(HTTP sgi.com/tech/stl/stable_partition.html, stable_partition) | ||
| 398 | */ | ||
| 399 | Range partition(alias predicate, SwapStrategy ss, Range)(Range r) | ||
| 400 | if (ss == SwapStrategy.stable && isRandomAccessRange!(Range) && hasLength!Range && hasSlicing!Range) | ||
| 401 | { | ||
| 402 | import std.algorithm.mutation : bringToFront; | ||
| 403 | |||
| 404 | alias pred = unaryFun!(predicate); | ||
| 405 | if (r.empty) return r; | ||
| 406 | |||
| 407 | if (r.length == 1) | ||
| 408 | { | ||
| 409 | if (pred(r.front)) r.popFront(); | ||
| 410 | return r; | ||
| 411 | } | ||
| 412 | const middle = r.length / 2; | ||
| 413 | alias recurse = .partition!(pred, ss, Range); | ||
| 414 | auto lower = recurse(r[0 .. middle]); | ||
| 415 | auto upper = recurse(r[middle .. r.length]); | ||
| 416 | bringToFront(lower, r[middle .. r.length - upper.length]); | ||
| 417 | return r[r.length - lower.length - upper.length .. r.length]; | ||
| 418 | } | ||
| 419 | |||
| 420 | ///ditto | ||
| 421 | Range partition(alias predicate, SwapStrategy ss = SwapStrategy.unstable, Range)(Range r) | ||
| 422 | if (ss != SwapStrategy.stable && isInputRange!Range && hasSwappableElements!Range) | ||
| 423 | { | ||
| 424 | import std.algorithm.mutation : swap; | ||
| 425 | alias pred = unaryFun!(predicate); | ||
| 426 | |||
| 427 | static if (ss == SwapStrategy.semistable) | ||
| 428 | { | ||
| 429 | if (r.empty) return r; | ||
| 430 | |||
| 431 | for (; !r.empty; r.popFront()) | ||
| 432 | { | ||
| 433 | // skip the initial portion of "correct" elements | ||
| 434 | if (pred(r.front)) continue; | ||
| 435 | // hit the first "bad" element | ||
| 436 | auto result = r; | ||
| 437 | for (r.popFront(); !r.empty; r.popFront()) | ||
| 438 | { | ||
| 439 | if (!pred(r.front)) continue; | ||
| 440 | swap(result.front, r.front); | ||
| 441 | result.popFront(); | ||
| 442 | } | ||
| 443 | return result; | ||
| 444 | } | ||
| 445 | |||
| 446 | return r; | ||
| 447 | } | ||
| 448 | else | ||
| 449 | { | ||
| 450 | // Inspired from www.stepanovpapers.com/PAM3-partition_notes.pdf, | ||
| 451 | // section "Bidirectional Partition Algorithm (Hoare)" | ||
| 452 | static if (isDynamicArray!Range) | ||
| 453 | { | ||
| 454 | import std.algorithm.mutation : swapAt; | ||
| 455 | // For dynamic arrays prefer index-based manipulation | ||
| 456 | if (!r.length) return r; | ||
| 457 | size_t lo = 0, hi = r.length - 1; | ||
| 458 | for (;;) | ||
| 459 | { | ||
| 460 | for (;;) | ||
| 461 | { | ||
| 462 | if (lo > hi) return r[lo .. r.length]; | ||
| 463 | if (!pred(r[lo])) break; | ||
| 464 | ++lo; | ||
| 465 | } | ||
| 466 | // found the left bound | ||
| 467 | assert(lo <= hi); | ||
| 468 | for (;;) | ||
| 469 | { | ||
| 470 | if (lo == hi) return r[lo .. r.length]; | ||
| 471 | if (pred(r[hi])) break; | ||
| 472 | --hi; | ||
| 473 | } | ||
| 474 | // found the right bound, swap & make progress | ||
| 475 | r.swapAt(lo++, hi--); | ||
| 476 | } | ||
| 477 | } | ||
| 478 | else | ||
| 479 | { | ||
| 480 | import std.algorithm.mutation : swap; | ||
| 481 | auto result = r; | ||
| 482 | for (;;) | ||
| 483 | { | ||
| 484 | for (;;) | ||
| 485 | { | ||
| 486 | if (r.empty) return result; | ||
| 487 | if (!pred(r.front)) break; | ||
| 488 | r.popFront(); | ||
| 489 | result.popFront(); | ||
| 490 | } | ||
| 491 | // found the left bound | ||
| 492 | assert(!r.empty); | ||
| 493 | for (;;) | ||
| 494 | { | ||
| 495 | if (pred(r.back)) break; | ||
| 496 | r.popBack(); | ||
| 497 | if (r.empty) return result; | ||
| 498 | } | ||
| 499 | // found the right bound, swap & make progress | ||
| 500 | static if (is(typeof(swap(r.front, r.back)))) | ||
| 501 | { | ||
| 502 | swap(r.front, r.back); | ||
| 503 | } | ||
| 504 | else | ||
| 505 | { | ||
| 506 | auto t1 = r.moveFront(), t2 = r.moveBack(); | ||
| 507 | r.front = t2; | ||
| 508 | r.back = t1; | ||
| 509 | } | ||
| 510 | r.popFront(); | ||
| 511 | result.popFront(); | ||
| 512 | r.popBack(); | ||
| 513 | } | ||
| 514 | } | ||
| 515 | } | ||
| 516 | } | ||
| 517 | |||
| 518 | /// | ||
| 519 | @safe unittest | ||
| 520 | { | ||
| 521 | import std.algorithm.mutation : SwapStrategy; | ||
| 522 | import std.algorithm.searching : count, find; | ||
| 523 | import std.conv : text; | ||
| 524 | import std.range.primitives : empty; | ||
| 525 | |||
| 526 | auto Arr = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; | ||
| 527 | auto arr = Arr.dup; | ||
| 528 | static bool even(int a) { return (a & 1) == 0; } | ||
| 529 | // Partition arr such that even numbers come first | ||
| 530 | auto r = partition!(even)(arr); | ||
| 531 | // Now arr is separated in evens and odds. | ||
| 532 | // Numbers may have become shuffled due to instability | ||
| 533 | assert(r == arr[5 .. $]); | ||
| 534 | assert(count!(even)(arr[0 .. 5]) == 5); | ||
| 535 | assert(find!(even)(r).empty); | ||
| 536 | |||
| 537 | // Can also specify the predicate as a string. | ||
| 538 | // Use 'a' as the predicate argument name | ||
| 539 | arr[] = Arr[]; | ||
| 540 | r = partition!(q{(a & 1) == 0})(arr); | ||
| 541 | assert(r == arr[5 .. $]); | ||
| 542 | |||
| 543 | // Now for a stable partition: | ||
| 544 | arr[] = Arr[]; | ||
| 545 | r = partition!(q{(a & 1) == 0}, SwapStrategy.stable)(arr); | ||
| 546 | // Now arr is [2 4 6 8 10 1 3 5 7 9], and r points to 1 | ||
| 547 | assert(arr == [2, 4, 6, 8, 10, 1, 3, 5, 7, 9] && r == arr[5 .. $]); | ||
| 548 | |||
| 549 | // In case the predicate needs to hold its own state, use a delegate: | ||
| 550 | arr[] = Arr[]; | ||
| 551 | int x = 3; | ||
| 552 | // Put stuff greater than 3 on the left | ||
| 553 | bool fun(int a) { return a > x; } | ||
| 554 | r = partition!(fun, SwapStrategy.semistable)(arr); | ||
| 555 | // Now arr is [4 5 6 7 8 9 10 2 3 1] and r points to 2 | ||
| 556 | assert(arr == [4, 5, 6, 7, 8, 9, 10, 2, 3, 1] && r == arr[7 .. $]); | ||
| 557 | } | ||
| 558 | |||
| 559 | @safe unittest | ||
| 560 | { | ||
| 561 | import std.algorithm.internal : rndstuff; | ||
| 562 | static bool even(int a) { return (a & 1) == 0; } | ||
| 563 | |||
| 564 | // test with random data | ||
| 565 | auto a = rndstuff!int(); | ||
| 566 | partition!even(a); | ||
| 567 | assert(isPartitioned!even(a)); | ||
| 568 | auto b = rndstuff!string(); | ||
| 569 | partition!`a.length < 5`(b); | ||
| 570 | assert(isPartitioned!`a.length < 5`(b)); | ||
| 571 | } | ||
| 572 | |||
| 573 | // pivotPartition | ||
| 574 | /** | ||
| 575 | |||
| 576 | Partitions `r` around `pivot` using comparison function `less`, algorithm akin | ||
| 577 | to $(LINK2 https://en.wikipedia.org/wiki/Quicksort#Hoare_partition_scheme, | ||
| 578 | Hoare partition). Specifically, permutes elements of `r` and returns | ||
| 579 | an index $(D k < r.length) such that: | ||
| 580 | |||
| 581 | $(UL | ||
| 582 | |||
| 583 | $(LI `r[pivot]` is swapped to `r[k]`) | ||
| 584 | |||
| 585 | $(LI All elements `e` in subrange $(D r[0 .. k]) satisfy $(D !less(r[k], e)) | ||
| 586 | (i.e. `r[k]` is greater than or equal to each element to its left according to | ||
| 587 | predicate `less`)) | ||
| 588 | |||
| 589 | $(LI All elements `e` in subrange $(D r[0 .. k]) satisfy $(D !less(e, | ||
| 590 | r[k])) (i.e. `r[k]` is less than or equal to each element to its right | ||
| 591 | according to predicate `less`))) | ||
| 592 | |||
| 593 | If `r` contains equivalent elements, multiple permutations of `r` satisfy these | ||
| 594 | constraints. In such cases, `pivotPartition` attempts to distribute equivalent | ||
| 595 | elements fairly to the left and right of `k` such that `k` stays close to $(D | ||
| 596 | r.length / 2). | ||
| 597 | |||
| 598 | Params: | ||
| 599 | less = The predicate used for comparison, modeled as a | ||
| 600 | $(LINK2 https://en.wikipedia.org/wiki/Weak_ordering#Strict_weak_orderings, | ||
| 601 | strict weak ordering) (irreflexive, antisymmetric, transitive, and implying a transitive | ||
| 602 | equivalence) | ||
| 603 | r = The range being partitioned | ||
| 604 | pivot = The index of the pivot for partitioning, must be less than `r.length` or | ||
| 605 | `0` is `r.length` is `0` | ||
| 606 | |||
| 607 | Returns: | ||
| 608 | The new position of the pivot | ||
| 609 | |||
| 610 | See_Also: | ||
| 611 | $(HTTP jgrcs.info/index.php/jgrcs/article/view/142, Engineering of a Quicksort | ||
| 612 | Partitioning Algorithm), D. Abhyankar, Journal of Global Research in Computer | ||
| 613 | Science, February 2011. $(HTTPS youtube.com/watch?v=AxnotgLql0k, ACCU 2016 | ||
| 614 | Keynote), Andrei Alexandrescu. | ||
| 615 | */ | ||
| 616 | size_t pivotPartition(alias less = "a < b", Range) | ||
| 617 | (Range r, size_t pivot) | ||
| 618 | if (isRandomAccessRange!Range && hasLength!Range && hasSlicing!Range) | ||
| 619 | { | ||
| 620 | assert(pivot < r.length || r.length == 0 && pivot == 0); | ||
| 621 | if (r.length <= 1) return 0; | ||
| 622 | import std.algorithm.mutation : swapAt, move; | ||
| 623 | alias lt = binaryFun!less; | ||
| 624 | |||
| 625 | // Pivot at the front | ||
| 626 | r.swapAt(pivot, 0); | ||
| 627 | |||
| 628 | // Fork implementation depending on nothrow copy, assignment, and | ||
| 629 | // comparison. If all of these are nothrow, use the specialized | ||
| 630 | // implementation discussed at https://youtube.com/watch?v=AxnotgLql0k. | ||
| 631 | static if (is(typeof( | ||
| 632 | () nothrow { auto x = r.front; x = r.front; return lt(x, x); } | ||
| 633 | ))) | ||
| 634 | { | ||
| 635 | auto p = r[0]; | ||
| 636 | // Plant the pivot in the end as well as a sentinel | ||
| 637 | size_t lo = 0, hi = r.length - 1; | ||
| 638 | auto save = move(r[hi]); | ||
| 639 | r[hi] = p; // Vacancy is in r[$ - 1] now | ||
| 640 | // Start process | ||
| 641 | for (;;) | ||
| 642 | { | ||
| 643 | // Loop invariant | ||
| 644 | version (unittest) | ||
| 645 | { | ||
| 646 | import std.algorithm.searching : all; | ||
| 647 | assert(r[0 .. lo].all!(x => !lt(p, x))); | ||
| 648 | assert(r[hi + 1 .. r.length].all!(x => !lt(x, p))); | ||
| 649 | } | ||
| 650 | do ++lo; while (lt(r[lo], p)); | ||
| 651 | r[hi] = r[lo]; | ||
| 652 | // Vacancy is now in r[lo] | ||
| 653 | do --hi; while (lt(p, r[hi])); | ||
| 654 | if (lo >= hi) break; | ||
| 655 | r[lo] = r[hi]; | ||
| 656 | // Vacancy is not in r[hi] | ||
| 657 | } | ||
| 658 | // Fixup | ||
| 659 | assert(lo - hi <= 2); | ||
| 660 | assert(!lt(p, r[hi])); | ||
| 661 | if (lo == hi + 2) | ||
| 662 | { | ||
| 663 | assert(!lt(r[hi + 1], p)); | ||
| 664 | r[lo] = r[hi + 1]; | ||
| 665 | --lo; | ||
| 666 | } | ||
| 667 | r[lo] = save; | ||
| 668 | if (lt(p, save)) --lo; | ||
| 669 | assert(!lt(p, r[lo])); | ||
| 670 | } | ||
| 671 | else | ||
| 672 | { | ||
| 673 | size_t lo = 1, hi = r.length - 1; | ||
| 674 | loop: for (;; lo++, hi--) | ||
| 675 | { | ||
| 676 | for (;; ++lo) | ||
| 677 | { | ||
| 678 | if (lo > hi) break loop; | ||
| 679 | if (!lt(r[lo], r[0])) break; | ||
| 680 | } | ||
| 681 | // found the left bound: r[lo] >= r[0] | ||
| 682 | assert(lo <= hi); | ||
| 683 | for (;; --hi) | ||
| 684 | { | ||
| 685 | if (lo >= hi) break loop; | ||
| 686 | if (!lt(r[0], r[hi])) break; | ||
| 687 | } | ||
| 688 | // found the right bound: r[hi] <= r[0], swap & make progress | ||
| 689 | assert(!lt(r[lo], r[hi])); | ||
| 690 | r.swapAt(lo, hi); | ||
| 691 | } | ||
| 692 | --lo; | ||
| 693 | } | ||
| 694 | r.swapAt(lo, 0); | ||
| 695 | return lo; | ||
| 696 | } | ||
| 697 | |||
| 698 | /// | ||
| 699 | @safe nothrow unittest | ||
| 700 | { | ||
| 701 | int[] a = [5, 3, 2, 6, 4, 1, 3, 7]; | ||
| 702 | size_t pivot = pivotPartition(a, a.length / 2); | ||
| 703 | import std.algorithm.searching : all; | ||
| 704 | assert(a[0 .. pivot].all!(x => x <= a[pivot])); | ||
| 705 | assert(a[pivot .. $].all!(x => x >= a[pivot])); | ||
| 706 | } | ||
| 707 | |||
| 708 | @safe unittest | ||
| 709 | { | ||
| 710 | void test(alias less)() | ||
| 711 | { | ||
| 712 | int[] a; | ||
| 713 | size_t pivot; | ||
| 714 | |||
| 715 | a = [-9, -4, -2, -2, 9]; | ||
| 716 | pivot = pivotPartition!less(a, a.length / 2); | ||
| 717 | import std.algorithm.searching : all; | ||
| 718 | assert(a[0 .. pivot].all!(x => x <= a[pivot])); | ||
| 719 | assert(a[pivot .. $].all!(x => x >= a[pivot])); | ||
| 720 | |||
| 721 | a = [9, 2, 8, -5, 5, 4, -8, -4, 9]; | ||
| 722 | pivot = pivotPartition!less(a, a.length / 2); | ||
| 723 | assert(a[0 .. pivot].all!(x => x <= a[pivot])); | ||
| 724 | assert(a[pivot .. $].all!(x => x >= a[pivot])); | ||
| 725 | |||
| 726 | a = [ 42 ]; | ||
| 727 | pivot = pivotPartition!less(a, a.length / 2); | ||
| 728 | assert(pivot == 0); | ||
| 729 | assert(a == [ 42 ]); | ||
| 730 | |||
| 731 | a = [ 43, 42 ]; | ||
| 732 | pivot = pivotPartition!less(a, 0); | ||
| 733 | assert(pivot == 1); | ||
| 734 | assert(a == [ 42, 43 ]); | ||
| 735 | |||
| 736 | a = [ 43, 42 ]; | ||
| 737 | pivot = pivotPartition!less(a, 1); | ||
| 738 | assert(pivot == 0); | ||
| 739 | assert(a == [ 42, 43 ]); | ||
| 740 | |||
| 741 | a = [ 42, 42 ]; | ||
| 742 | pivot = pivotPartition!less(a, 0); | ||
| 743 | assert(pivot == 0 || pivot == 1); | ||
| 744 | assert(a == [ 42, 42 ]); | ||
| 745 | pivot = pivotPartition!less(a, 1); | ||
| 746 | assert(pivot == 0 || pivot == 1); | ||
| 747 | assert(a == [ 42, 42 ]); | ||
| 748 | |||
| 749 | import std.algorithm.iteration : map; | ||
| 750 | import std.random; | ||
| 751 | import std.stdio; | ||
| 752 | auto s = unpredictableSeed; | ||
| 753 | auto g = Random(s); | ||
| 754 | a = iota(0, uniform(1, 1000, g)) | ||
| 755 | .map!(_ => uniform(-1000, 1000, g)) | ||
| 756 | .array; | ||
| 757 | scope(failure) writeln("RNG seed was ", s); | ||
| 758 | pivot = pivotPartition!less(a, a.length / 2); | ||
| 759 | assert(a[0 .. pivot].all!(x => x <= a[pivot])); | ||
| 760 | assert(a[pivot .. $].all!(x => x >= a[pivot])); | ||
| 761 | } | ||
| 762 | test!"a < b"; | ||
| 763 | static bool myLess(int a, int b) | ||
| 764 | { | ||
| 765 | static bool bogus; | ||
| 766 | if (bogus) throw new Exception(""); // just to make it no-nothrow | ||
| 767 | return a < b; | ||
| 768 | } | ||
| 769 | test!myLess; | ||
| 770 | } | ||
| 771 | |||
| 772 | /** | ||
| 773 | Params: | ||
| 774 | pred = The predicate that the range should be partitioned by. | ||
| 775 | r = The range to check. | ||
| 776 | Returns: $(D true) if $(D r) is partitioned according to predicate $(D pred). | ||
| 777 | */ | ||
| 778 | bool isPartitioned(alias pred, Range)(Range r) | ||
| 779 | if (isForwardRange!(Range)) | ||
| 780 | { | ||
| 781 | for (; !r.empty; r.popFront()) | ||
| 782 | { | ||
| 783 | if (unaryFun!(pred)(r.front)) continue; | ||
| 784 | for (r.popFront(); !r.empty; r.popFront()) | ||
| 785 | { | ||
| 786 | if (unaryFun!(pred)(r.front)) return false; | ||
| 787 | } | ||
| 788 | break; | ||
| 789 | } | ||
| 790 | return true; | ||
| 791 | } | ||
| 792 | |||
| 793 | /// | ||
| 794 | @safe unittest | ||
| 795 | { | ||
| 796 | int[] r = [ 1, 3, 5, 7, 8, 2, 4, ]; | ||
| 797 | assert(isPartitioned!"a & 1"(r)); | ||
| 798 | } | ||
| 799 | |||
| 800 | // partition3 | ||
| 801 | /** | ||
| 802 | Rearranges elements in $(D r) in three adjacent ranges and returns | ||
| 803 | them. The first and leftmost range only contains elements in $(D r) | ||
| 804 | less than $(D pivot). The second and middle range only contains | ||
| 805 | elements in $(D r) that are equal to $(D pivot). Finally, the third | ||
| 806 | and rightmost range only contains elements in $(D r) that are greater | ||
| 807 | than $(D pivot). The less-than test is defined by the binary function | ||
| 808 | $(D less). | ||
| 809 | |||
| 810 | Params: | ||
| 811 | less = The predicate to use for the rearrangement. | ||
| 812 | ss = The swapping strategy to use. | ||
| 813 | r = The random-access range to rearrange. | ||
| 814 | pivot = The pivot element. | ||
| 815 | |||
| 816 | Returns: | ||
| 817 | A $(REF Tuple, std,typecons) of the three resulting ranges. These ranges are | ||
| 818 | slices of the original range. | ||
| 819 | |||
| 820 | BUGS: stable $(D partition3) has not been implemented yet. | ||
| 821 | */ | ||
| 822 | auto partition3(alias less = "a < b", SwapStrategy ss = SwapStrategy.unstable, Range, E) | ||
| 823 | (Range r, E pivot) | ||
| 824 | if (ss == SwapStrategy.unstable && isRandomAccessRange!Range | ||
| 825 | && hasSwappableElements!Range && hasLength!Range && hasSlicing!Range | ||
| 826 | && is(typeof(binaryFun!less(r.front, pivot)) == bool) | ||
| 827 | && is(typeof(binaryFun!less(pivot, r.front)) == bool) | ||
| 828 | && is(typeof(binaryFun!less(r.front, r.front)) == bool)) | ||
| 829 | { | ||
| 830 | // The algorithm is described in "Engineering a sort function" by | ||
| 831 | // Jon Bentley et al, pp 1257. | ||
| 832 | |||
| 833 | import std.algorithm.comparison : min; | ||
| 834 | import std.algorithm.mutation : swap, swapAt, swapRanges; | ||
| 835 | import std.typecons : tuple; | ||
| 836 | |||
| 837 | alias lessFun = binaryFun!less; | ||
| 838 | size_t i, j, k = r.length, l = k; | ||
| 839 | |||
| 840 | bigloop: | ||
| 841 | for (;;) | ||
| 842 | { | ||
| 843 | for (;; ++j) | ||
| 844 | { | ||
| 845 | if (j == k) break bigloop; | ||
| 846 | assert(j < r.length); | ||
| 847 | if (lessFun(r[j], pivot)) continue; | ||
| 848 | if (lessFun(pivot, r[j])) break; | ||
| 849 | r.swapAt(i++, j); | ||
| 850 | } | ||
| 851 | assert(j < k); | ||
| 852 | for (;;) | ||
| 853 | { | ||
| 854 | assert(k > 0); | ||
| 855 | if (!lessFun(pivot, r[--k])) | ||
| 856 | { | ||
| 857 | if (lessFun(r[k], pivot)) break; | ||
| 858 | r.swapAt(k, --l); | ||
| 859 | } | ||
| 860 | if (j == k) break bigloop; | ||
| 861 | } | ||
| 862 | // Here we know r[j] > pivot && r[k] < pivot | ||
| 863 | r.swapAt(j++, k); | ||
| 864 | } | ||
| 865 | |||
| 866 | // Swap the equal ranges from the extremes into the middle | ||
| 867 | auto strictlyLess = j - i, strictlyGreater = l - k; | ||
| 868 | auto swapLen = min(i, strictlyLess); | ||
| 869 | swapRanges(r[0 .. swapLen], r[j - swapLen .. j]); | ||
| 870 | swapLen = min(r.length - l, strictlyGreater); | ||
| 871 | swapRanges(r[k .. k + swapLen], r[r.length - swapLen .. r.length]); | ||
| 872 | return tuple(r[0 .. strictlyLess], | ||
| 873 | r[strictlyLess .. r.length - strictlyGreater], | ||
| 874 | r[r.length - strictlyGreater .. r.length]); | ||
| 875 | } | ||
| 876 | |||
| 877 | /// | ||
| 878 | @safe unittest | ||
| 879 | { | ||
| 880 | auto a = [ 8, 3, 4, 1, 4, 7, 4 ]; | ||
| 881 | auto pieces = partition3(a, 4); | ||
| 882 | assert(pieces[0] == [ 1, 3 ]); | ||
| 883 | assert(pieces[1] == [ 4, 4, 4 ]); | ||
| 884 | assert(pieces[2] == [ 8, 7 ]); | ||
| 885 | } | ||
| 886 | |||
| 887 | @safe unittest | ||
| 888 | { | ||
| 889 | import std.random : Random, uniform, unpredictableSeed; | ||
| 890 | |||
| 891 | immutable uint[] seeds = [3923355730, 1927035882, unpredictableSeed]; | ||
| 892 | foreach (s; seeds) | ||
| 893 | { | ||
| 894 | auto r = Random(s); | ||
| 895 | auto a = new int[](uniform(0, 100, r)); | ||
| 896 | foreach (ref e; a) | ||
| 897 | { | ||
| 898 | e = uniform(0, 50, r); | ||
| 899 | } | ||
| 900 | auto pieces = partition3(a, 25); | ||
| 901 | assert(pieces[0].length + pieces[1].length + pieces[2].length == a.length); | ||
| 902 | foreach (e; pieces[0]) | ||
| 903 | { | ||
| 904 | assert(e < 25); | ||
| 905 | } | ||
| 906 | foreach (e; pieces[1]) | ||
| 907 | { | ||
| 908 | assert(e == 25); | ||
| 909 | } | ||
| 910 | foreach (e; pieces[2]) | ||
| 911 | { | ||
| 912 | assert(e > 25); | ||
| 913 | } | ||
| 914 | } | ||
| 915 | } | ||
| 916 | |||
| 917 | // makeIndex | ||
| 918 | /** | ||
| 919 | Computes an index for $(D r) based on the comparison $(D less). The | ||
| 920 | index is a sorted array of pointers or indices into the original | ||
| 921 | range. This technique is similar to sorting, but it is more flexible | ||
| 922 | because (1) it allows "sorting" of immutable collections, (2) allows | ||
| 923 | binary search even if the original collection does not offer random | ||
| 924 | access, (3) allows multiple indexes, each on a different predicate, | ||
| 925 | and (4) may be faster when dealing with large objects. However, using | ||
| 926 | an index may also be slower under certain circumstances due to the | ||
| 927 | extra indirection, and is always larger than a sorting-based solution | ||
| 928 | because it needs space for the index in addition to the original | ||
| 929 | collection. The complexity is the same as $(D sort)'s. | ||
| 930 | |||
| 931 | The first overload of $(D makeIndex) writes to a range containing | ||
| 932 | pointers, and the second writes to a range containing offsets. The | ||
| 933 | first overload requires $(D Range) to be a | ||
| 934 | $(REF_ALTTEXT forward range, isForwardRange, std,range,primitives), and the | ||
| 935 | latter requires it to be a random-access range. | ||
| 936 | |||
| 937 | $(D makeIndex) overwrites its second argument with the result, but | ||
| 938 | never reallocates it. | ||
| 939 | |||
| 940 | Params: | ||
| 941 | less = The comparison to use. | ||
| 942 | ss = The swapping strategy. | ||
| 943 | r = The range to index. | ||
| 944 | index = The resulting index. | ||
| 945 | |||
| 946 | Returns: The pointer-based version returns a $(D SortedRange) wrapper | ||
| 947 | over index, of type $(D SortedRange!(RangeIndex, (a, b) => | ||
| 948 | binaryFun!less(*a, *b))) thus reflecting the ordering of the | ||
| 949 | index. The index-based version returns $(D void) because the ordering | ||
| 950 | relation involves not only $(D index) but also $(D r). | ||
| 951 | |||
| 952 | Throws: If the second argument's length is less than that of the range | ||
| 953 | indexed, an exception is thrown. | ||
| 954 | */ | ||
| 955 | SortedRange!(RangeIndex, (a, b) => binaryFun!less(*a, *b)) | ||
| 956 | makeIndex( | ||
| 957 | alias less = "a < b", | ||
| 958 | SwapStrategy ss = SwapStrategy.unstable, | ||
| 959 | Range, | ||
| 960 | RangeIndex) | ||
| 961 | (Range r, RangeIndex index) | ||
| 962 | if (isForwardRange!(Range) && isRandomAccessRange!(RangeIndex) | ||
| 963 | && is(ElementType!(RangeIndex) : ElementType!(Range)*)) | ||
| 964 | { | ||
| 965 | import std.algorithm.internal : addressOf; | ||
| 966 | import std.exception : enforce; | ||
| 967 | |||
| 968 | // assume collection already ordered | ||
| 969 | size_t i; | ||
| 970 | for (; !r.empty; r.popFront(), ++i) | ||
| 971 | index[i] = addressOf(r.front); | ||
| 972 | enforce(index.length == i); | ||
| 973 | // sort the index | ||
| 974 | sort!((a, b) => binaryFun!less(*a, *b), ss)(index); | ||
| 975 | return typeof(return)(index); | ||
| 976 | } | ||
| 977 | |||
| 978 | /// Ditto | ||
| 979 | void makeIndex( | ||
| 980 | alias less = "a < b", | ||
| 981 | SwapStrategy ss = SwapStrategy.unstable, | ||
| 982 | Range, | ||
| 983 | RangeIndex) | ||
| 984 | (Range r, RangeIndex index) | ||
| 985 | if (isRandomAccessRange!Range && !isInfinite!Range && | ||
| 986 | isRandomAccessRange!RangeIndex && !isInfinite!RangeIndex && | ||
| 987 | isIntegral!(ElementType!RangeIndex)) | ||
| 988 | { | ||
| 989 | import std.conv : to; | ||
| 990 | import std.exception : enforce; | ||
| 991 | |||
| 992 | alias IndexType = Unqual!(ElementType!RangeIndex); | ||
| 993 | enforce(r.length == index.length, | ||
| 994 | "r and index must be same length for makeIndex."); | ||
| 995 | static if (IndexType.sizeof < size_t.sizeof) | ||
| 996 | { | ||
| 997 | enforce(r.length <= size_t(1) + IndexType.max, "Cannot create an index with " ~ | ||
| 998 | "element type " ~ IndexType.stringof ~ " with length " ~ | ||
| 999 | to!string(r.length) ~ "."); | ||
| 1000 | } | ||
| 1001 | |||
| 1002 | // Use size_t as loop index to avoid overflow on ++i, | ||
| 1003 | // e.g. when squeezing 256 elements into a ubyte index. | ||
| 1004 | foreach (size_t i; 0 .. r.length) | ||
| 1005 | index[i] = cast(IndexType) i; | ||
| 1006 | |||
| 1007 | // sort the index | ||
| 1008 | sort!((a, b) => binaryFun!less(r[cast(size_t) a], r[cast(size_t) b]), ss) | ||
| 1009 | (index); | ||
| 1010 | } | ||
| 1011 | |||
| 1012 | /// | ||
| 1013 | @system unittest | ||
| 1014 | { | ||
| 1015 | immutable(int[]) arr = [ 2, 3, 1, 5, 0 ]; | ||
| 1016 | // index using pointers | ||
| 1017 | auto index1 = new immutable(int)*[arr.length]; | ||
| 1018 | makeIndex!("a < b")(arr, index1); | ||
| 1019 | assert(isSorted!("*a < *b")(index1)); | ||
| 1020 | // index using offsets | ||
| 1021 | auto index2 = new size_t[arr.length]; | ||
| 1022 | makeIndex!("a < b")(arr, index2); | ||
| 1023 | assert(isSorted! | ||
| 1024 | ((size_t a, size_t b){ return arr[a] < arr[b];}) | ||
| 1025 | (index2)); | ||
| 1026 | } | ||
| 1027 | |||
| 1028 | @system unittest | ||
| 1029 | { | ||
| 1030 | immutable(int)[] arr = [ 2, 3, 1, 5, 0 ]; | ||
| 1031 | // index using pointers | ||
| 1032 | auto index1 = new immutable(int)*[arr.length]; | ||
| 1033 | alias ImmRange = typeof(arr); | ||
| 1034 | alias ImmIndex = typeof(index1); | ||
| 1035 | static assert(isForwardRange!(ImmRange)); | ||
| 1036 | static assert(isRandomAccessRange!(ImmIndex)); | ||
| 1037 | static assert(!isIntegral!(ElementType!(ImmIndex))); | ||
| 1038 | static assert(is(ElementType!(ImmIndex) : ElementType!(ImmRange)*)); | ||
| 1039 | makeIndex!("a < b")(arr, index1); | ||
| 1040 | assert(isSorted!("*a < *b")(index1)); | ||
| 1041 | |||
| 1042 | // index using offsets | ||
| 1043 | auto index2 = new long[arr.length]; | ||
| 1044 | makeIndex(arr, index2); | ||
| 1045 | assert(isSorted! | ||
| 1046 | ((long a, long b){ | ||
| 1047 | return arr[cast(size_t) a] < arr[cast(size_t) b]; | ||
| 1048 | })(index2)); | ||
| 1049 | |||
| 1050 | // index strings using offsets | ||
| 1051 | string[] arr1 = ["I", "have", "no", "chocolate"]; | ||
| 1052 | auto index3 = new byte[arr1.length]; | ||
| 1053 | makeIndex(arr1, index3); | ||
| 1054 | assert(isSorted! | ||
| 1055 | ((byte a, byte b){ return arr1[a] < arr1[b];}) | ||
| 1056 | (index3)); | ||
| 1057 | } | ||
| 1058 | |||
| 1059 | @safe unittest | ||
| 1060 | { | ||
| 1061 | import std.algorithm.comparison : equal; | ||
| 1062 | |||
| 1063 | ubyte[256] index = void; | ||
| 1064 | iota(256).makeIndex(index[]); | ||
| 1065 | assert(index[].equal(iota(256))); | ||
| 1066 | byte[128] sindex = void; | ||
| 1067 | iota(128).makeIndex(sindex[]); | ||
| 1068 | assert(sindex[].equal(iota(128))); | ||
| 1069 | |||
| 1070 | auto index2 = new uint[10]; | ||
| 1071 | 10.iota.makeIndex(index2); | ||
| 1072 | assert(index2.equal(10.iota)); | ||
| 1073 | } | ||
| 1074 | |||
| 1075 | struct Merge(alias less = "a < b", Rs...) | ||
| 1076 | if (Rs.length >= 2 && | ||
| 1077 | allSatisfy!(isInputRange, Rs) && | ||
| 1078 | !is(CommonType!(staticMap!(ElementType, Rs)) == void)) | ||
| 1079 | { | ||
| 1080 | public Rs source; | ||
| 1081 | private size_t _lastFrontIndex = size_t.max; | ||
| 1082 | static if (isBidirectional) | ||
| 1083 | { | ||
| 1084 | private size_t _lastBackIndex = size_t.max; // `size_t.max` means uninitialized, | ||
| 1085 | } | ||
| 1086 | |||
| 1087 | import std.functional : binaryFun; | ||
| 1088 | import std.traits : isCopyable; | ||
| 1089 | import std.typetuple : anySatisfy; | ||
| 1090 | |||
| 1091 | private alias comp = binaryFun!less; | ||
| 1092 | private alias ElementType = CommonType!(staticMap!(.ElementType, Rs)); | ||
| 1093 | private enum isBidirectional = allSatisfy!(isBidirectionalRange, staticMap!(Unqual, Rs)); | ||
| 1094 | |||
| 1095 | debug private enum canCheckSortedness = isCopyable!ElementType && !hasAliasing!ElementType; | ||
| 1096 | |||
| 1097 | this(Rs source) | ||
| 1098 | { | ||
| 1099 | this.source = source; | ||
| 1100 | this._lastFrontIndex = frontIndex; | ||
| 1101 | } | ||
| 1102 | |||
| 1103 | static if (anySatisfy!(isInfinite, Rs)) | ||
| 1104 | { | ||
| 1105 | enum bool empty = false; // propagate infiniteness | ||
| 1106 | } | ||
| 1107 | else | ||
| 1108 | { | ||
| 1109 | @property bool empty() | ||
| 1110 | { | ||
| 1111 | return _lastFrontIndex == size_t.max; | ||
| 1112 | } | ||
| 1113 | } | ||
| 1114 | |||
| 1115 | @property auto ref front() | ||
| 1116 | { | ||
| 1117 | final switch (_lastFrontIndex) | ||
| 1118 | { | ||
| 1119 | foreach (i, _; Rs) | ||
| 1120 | { | ||
| 1121 | case i: | ||
| 1122 | assert(!source[i].empty); | ||
| 1123 | return source[i].front; | ||
| 1124 | } | ||
| 1125 | } | ||
| 1126 | } | ||
| 1127 | |||
| 1128 | private size_t frontIndex() | ||
| 1129 | { | ||
| 1130 | size_t bestIndex = size_t.max; // indicate undefined | ||
| 1131 | Unqual!ElementType bestElement; | ||
| 1132 | foreach (i, _; Rs) | ||
| 1133 | { | ||
| 1134 | if (source[i].empty) continue; | ||
| 1135 | if (bestIndex == size_t.max || // either this is the first or | ||
| 1136 | comp(source[i].front, bestElement)) | ||
| 1137 | { | ||
| 1138 | bestIndex = i; | ||
| 1139 | bestElement = source[i].front; | ||
| 1140 | } | ||
| 1141 | } | ||
| 1142 | return bestIndex; | ||
| 1143 | } | ||
| 1144 | |||
| 1145 | void popFront() | ||
| 1146 | { | ||
| 1147 | sw: final switch (_lastFrontIndex) | ||
| 1148 | { | ||
| 1149 | foreach (i, R; Rs) | ||
| 1150 | { | ||
| 1151 | case i: | ||
| 1152 | debug static if (canCheckSortedness) | ||
| 1153 | { | ||
| 1154 | ElementType previousFront = source[i].front(); | ||
| 1155 | } | ||
| 1156 | source[i].popFront(); | ||
| 1157 | debug static if (canCheckSortedness) | ||
| 1158 | { | ||
| 1159 | if (!source[i].empty) | ||
| 1160 | { | ||
| 1161 | assert(previousFront == source[i].front || | ||
| 1162 | comp(previousFront, source[i].front), | ||
| 1163 | "Input " ~ i.stringof ~ " is unsorted"); // @nogc | ||
| 1164 | } | ||
| 1165 | } | ||
| 1166 | break sw; | ||
| 1167 | } | ||
| 1168 | } | ||
| 1169 | _lastFrontIndex = frontIndex; | ||
| 1170 | } | ||
| 1171 | |||
| 1172 | static if (isBidirectional) | ||
| 1173 | { | ||
| 1174 | @property auto ref back() | ||
| 1175 | { | ||
| 1176 | if (_lastBackIndex == size_t.max) | ||
| 1177 | { | ||
| 1178 | this._lastBackIndex = backIndex; // lazy initialization | ||
| 1179 | } | ||
| 1180 | final switch (_lastBackIndex) | ||
| 1181 | { | ||
| 1182 | foreach (i, _; Rs) | ||
| 1183 | { | ||
| 1184 | case i: | ||
| 1185 | assert(!source[i].empty); | ||
| 1186 | return source[i].back; | ||
| 1187 | } | ||
| 1188 | } | ||
| 1189 | } | ||
| 1190 | |||
| 1191 | private size_t backIndex() | ||
| 1192 | { | ||
| 1193 | size_t bestIndex = size_t.max; // indicate undefined | ||
| 1194 | Unqual!ElementType bestElement; | ||
| 1195 | foreach (i, _; Rs) | ||
| 1196 | { | ||
| 1197 | if (source[i].empty) continue; | ||
| 1198 | if (bestIndex == size_t.max || // either this is the first or | ||
| 1199 | comp(bestElement, source[i].back)) | ||
| 1200 | { | ||
| 1201 | bestIndex = i; | ||
| 1202 | bestElement = source[i].back; | ||
| 1203 | } | ||
| 1204 | } | ||
| 1205 | return bestIndex; | ||
| 1206 | } | ||
| 1207 | |||
| 1208 | void popBack() | ||
| 1209 | { | ||
| 1210 | if (_lastBackIndex == size_t.max) | ||
| 1211 | { | ||
| 1212 | this._lastBackIndex = backIndex; // lazy initialization | ||
| 1213 | } | ||
| 1214 | sw: final switch (_lastBackIndex) | ||
| 1215 | { | ||
| 1216 | foreach (i, R; Rs) | ||
| 1217 | { | ||
| 1218 | case i: | ||
| 1219 | debug static if (canCheckSortedness) | ||
| 1220 | { | ||
| 1221 | ElementType previousBack = source[i].back(); | ||
| 1222 | } | ||
| 1223 | source[i].popBack(); | ||
| 1224 | debug static if (canCheckSortedness) | ||
| 1225 | { | ||
| 1226 | if (!source[i].empty) | ||
| 1227 | { | ||
| 1228 | assert(previousBack == source[i].back || | ||
| 1229 | comp(source[i].back, previousBack), | ||
| 1230 | "Input " ~ i.stringof ~ " is unsorted"); // @nogc | ||
| 1231 | } | ||
| 1232 | } | ||
| 1233 | break sw; | ||
| 1234 | } | ||
| 1235 | } | ||
| 1236 | _lastBackIndex = backIndex; | ||
| 1237 | if (_lastBackIndex == size_t.max) // if emptied | ||
| 1238 | { | ||
| 1239 | _lastFrontIndex = size_t.max; | ||
| 1240 | } | ||
| 1241 | } | ||
| 1242 | } | ||
| 1243 | |||
| 1244 | static if (allSatisfy!(isForwardRange, staticMap!(Unqual, Rs))) | ||
| 1245 | { | ||
| 1246 | @property auto save() | ||
| 1247 | { | ||
| 1248 | auto result = this; | ||
| 1249 | foreach (i, _; Rs) | ||
| 1250 | { | ||
| 1251 | result.source[i] = result.source[i].save; | ||
| 1252 | } | ||
| 1253 | return result; | ||
| 1254 | } | ||
| 1255 | } | ||
| 1256 | |||
| 1257 | static if (allSatisfy!(hasLength, Rs)) | ||
| 1258 | { | ||
| 1259 | @property size_t length() | ||
| 1260 | { | ||
| 1261 | size_t result; | ||
| 1262 | foreach (i, _; Rs) | ||
| 1263 | { | ||
| 1264 | result += source[i].length; | ||
| 1265 | } | ||
| 1266 | return result; | ||
| 1267 | } | ||
| 1268 | |||
| 1269 | alias opDollar = length; | ||
| 1270 | } | ||
| 1271 | } | ||
| 1272 | |||
| 1273 | /** | ||
| 1274 | Merge multiple sorted ranges `rs` with less-than predicate function `pred` | ||
| 1275 | into one single sorted output range containing the sorted union of the | ||
| 1276 | elements of inputs. Duplicates are not eliminated, meaning that the total | ||
| 1277 | number of elements in the output is the sum of all elements in the ranges | ||
| 1278 | passed to it; the `length` member is offered if all inputs also have | ||
| 1279 | `length`. The element types of all the inputs must have a common type | ||
| 1280 | `CommonType`. | ||
| 1281 | |||
| 1282 | Params: | ||
| 1283 | less = Predicate the given ranges are sorted by. | ||
| 1284 | rs = The ranges to compute the union for. | ||
| 1285 | |||
| 1286 | Returns: | ||
| 1287 | A range containing the union of the given ranges. | ||
| 1288 | |||
| 1289 | Details: | ||
| 1290 | |||
| 1291 | All of its inputs are assumed to be sorted. This can mean that inputs are | ||
| 1292 | instances of $(REF SortedRange, std,range). Use the result of $(REF sort, | ||
| 1293 | std,algorithm,sorting), or $(REF assumeSorted, std,range) to merge ranges | ||
| 1294 | known to be sorted (show in the example below). Note that there is currently | ||
| 1295 | no way of ensuring that two or more instances of $(REF SortedRange, | ||
| 1296 | std,range) are sorted using a specific comparison function `pred`. Therefore | ||
| 1297 | no checking is done here to assure that all inputs `rs` are instances of | ||
| 1298 | $(REF SortedRange, std,range). | ||
| 1299 | |||
| 1300 | This algorithm is lazy, doing work progressively as elements are pulled off | ||
| 1301 | the result. | ||
| 1302 | |||
| 1303 | Time complexity is proportional to the sum of element counts over all inputs. | ||
| 1304 | |||
| 1305 | If all inputs have the same element type and offer it by `ref`, output | ||
| 1306 | becomes a range with mutable `front` (and `back` where appropriate) that | ||
| 1307 | reflects in the original inputs. | ||
| 1308 | |||
| 1309 | If any of the inputs `rs` is infinite so is the result (`empty` being always | ||
| 1310 | `false`). | ||
| 1311 | */ | ||
| 1312 | Merge!(less, Rs) merge(alias less = "a < b", Rs...)(Rs rs) | ||
| 1313 | if (Rs.length >= 2 && | ||
| 1314 | allSatisfy!(isInputRange, Rs) && | ||
| 1315 | !is(CommonType!(staticMap!(ElementType, Rs)) == void)) | ||
| 1316 | { | ||
| 1317 | return typeof(return)(rs); | ||
| 1318 | } | ||
| 1319 | |||
| 1320 | /// | ||
| 1321 | @safe pure nothrow unittest | ||
| 1322 | { | ||
| 1323 | import std.algorithm.comparison : equal; | ||
| 1324 | import std.range : retro; | ||
| 1325 | |||
| 1326 | int[] a = [1, 3, 5]; | ||
| 1327 | int[] b = [2, 3, 4]; | ||
| 1328 | |||
| 1329 | assert(a.merge(b).equal([1, 2, 3, 3, 4, 5])); | ||
| 1330 | assert(a.merge(b).retro.equal([5, 4, 3, 3, 2, 1])); | ||
| 1331 | } | ||
| 1332 | |||
| 1333 | @safe pure nothrow unittest | ||
| 1334 | { | ||
| 1335 | import std.algorithm.comparison : equal; | ||
| 1336 | |||
| 1337 | int[] a = [ 1, 2, 4, 5, 7, 9 ]; | ||
| 1338 | int[] b = [ 0, 1, 2, 4, 7, 8 ]; | ||
| 1339 | double[] c = [ 10.5 ]; | ||
| 1340 | |||
| 1341 | assert(merge(a, b).length == a.length + b.length); | ||
| 1342 | assert(equal(merge(a, b), [0, 1, 1, 2, 2, 4, 4, 5, 7, 7, 8, 9][])); | ||
| 1343 | assert(equal(merge(a, c, b), | ||
| 1344 | [0, 1, 1, 2, 2, 4, 4, 5, 7, 7, 8, 9, 10.5][])); | ||
| 1345 | auto u = merge(a, b); | ||
| 1346 | u.front--; | ||
| 1347 | assert(equal(u, [-1, 1, 1, 2, 2, 4, 4, 5, 7, 7, 8, 9][])); | ||
| 1348 | } | ||
| 1349 | |||
| 1350 | @safe pure nothrow unittest | ||
| 1351 | { | ||
| 1352 | // save | ||
| 1353 | import std.range : dropOne; | ||
| 1354 | int[] a = [1, 2]; | ||
| 1355 | int[] b = [0, 3]; | ||
| 1356 | auto arr = a.merge(b); | ||
| 1357 | assert(arr.front == 0); | ||
| 1358 | assert(arr.save.dropOne.front == 1); | ||
| 1359 | assert(arr.front == 0); | ||
| 1360 | } | ||
| 1361 | |||
| 1362 | @safe pure nothrow unittest | ||
| 1363 | { | ||
| 1364 | import std.algorithm.comparison : equal; | ||
| 1365 | import std.internal.test.dummyrange; | ||
| 1366 | |||
| 1367 | auto dummyResult1 = [1, 1, 1.5, 2, 3, 4, 5, 5.5, 6, 7, 8, 9, 10]; | ||
| 1368 | auto dummyResult2 = [1, 1, 2, 2, 3, 3, 4, 4, 5, 5, | ||
| 1369 | 6, 6, 7, 7, 8, 8, 9, 9, 10, 10]; | ||
| 1370 | foreach (DummyType; AllDummyRanges) | ||
| 1371 | { | ||
| 1372 | DummyType d; | ||
| 1373 | assert(d.merge([1, 1.5, 5.5]).equal(dummyResult1)); | ||
| 1374 | assert(d.merge(d).equal(dummyResult2)); | ||
| 1375 | } | ||
| 1376 | } | ||
| 1377 | |||
| 1378 | @nogc @safe pure nothrow unittest | ||
| 1379 | { | ||
| 1380 | import std.algorithm.comparison : equal; | ||
| 1381 | |||
| 1382 | static immutable a = [1, 3, 5]; | ||
| 1383 | static immutable b = [2, 3, 4]; | ||
| 1384 | static immutable r = [1, 2, 3, 3, 4, 5]; | ||
| 1385 | assert(a.merge(b).equal(r)); | ||
| 1386 | } | ||
| 1387 | |||
| 1388 | /// test bi-directional access and common type | ||
| 1389 | @safe pure nothrow unittest | ||
| 1390 | { | ||
| 1391 | import std.algorithm.comparison : equal; | ||
| 1392 | import std.range : retro; | ||
| 1393 | import std.traits : CommonType; | ||
| 1394 | |||
| 1395 | alias S = short; | ||
| 1396 | alias I = int; | ||
| 1397 | alias D = double; | ||
| 1398 | |||
| 1399 | S[] a = [1, 2, 3]; | ||
| 1400 | I[] b = [50, 60]; | ||
| 1401 | D[] c = [10, 20, 30, 40]; | ||
| 1402 | |||
| 1403 | auto m = merge(a, b, c); | ||
| 1404 | |||
| 1405 | static assert(is(typeof(m.front) == CommonType!(S, I, D))); | ||
| 1406 | |||
| 1407 | assert(equal(m, [1, 2, 3, 10, 20, 30, 40, 50, 60])); | ||
| 1408 | assert(equal(m.retro, [60, 50, 40, 30, 20, 10, 3, 2, 1])); | ||
| 1409 | |||
| 1410 | m.popFront(); | ||
| 1411 | assert(equal(m, [2, 3, 10, 20, 30, 40, 50, 60])); | ||
| 1412 | m.popBack(); | ||
| 1413 | assert(equal(m, [2, 3, 10, 20, 30, 40, 50])); | ||
| 1414 | m.popFront(); | ||
| 1415 | assert(equal(m, [3, 10, 20, 30, 40, 50])); | ||
| 1416 | m.popBack(); | ||
| 1417 | assert(equal(m, [3, 10, 20, 30, 40])); | ||
| 1418 | m.popFront(); | ||
| 1419 | assert(equal(m, [10, 20, 30, 40])); | ||
| 1420 | m.popBack(); | ||
| 1421 | assert(equal(m, [10, 20, 30])); | ||
| 1422 | m.popFront(); | ||
| 1423 | assert(equal(m, [20, 30])); | ||
| 1424 | m.popBack(); | ||
| 1425 | assert(equal(m, [20])); | ||
| 1426 | m.popFront(); | ||
| 1427 | assert(m.empty); | ||
| 1428 | } | ||
| 1429 | |||
| 1430 | private template validPredicates(E, less...) | ||
| 1431 | { | ||
| 1432 | static if (less.length == 0) | ||
| 1433 | enum validPredicates = true; | ||
| 1434 | else static if (less.length == 1 && is(typeof(less[0]) == SwapStrategy)) | ||
| 1435 | enum validPredicates = true; | ||
| 1436 | else | ||
| 1437 | enum validPredicates = | ||
| 1438 | is(typeof((E a, E b){ bool r = binaryFun!(less[0])(a, b); })) | ||
| 1439 | && validPredicates!(E, less[1 .. $]); | ||
| 1440 | } | ||
| 1441 | |||
| 1442 | /** | ||
| 1443 | $(D auto multiSort(Range)(Range r) | ||
| 1444 | if (validPredicates!(ElementType!Range, less));) | ||
| 1445 | |||
| 1446 | Sorts a range by multiple keys. The call $(D multiSort!("a.id < b.id", | ||
| 1447 | "a.date > b.date")(r)) sorts the range $(D r) by $(D id) ascending, | ||
| 1448 | and sorts elements that have the same $(D id) by $(D date) | ||
| 1449 | descending. Such a call is equivalent to $(D sort!"a.id != b.id ? a.id | ||
| 1450 | < b.id : a.date > b.date"(r)), but $(D multiSort) is faster because it | ||
| 1451 | does fewer comparisons (in addition to being more convenient). | ||
| 1452 | |||
| 1453 | Returns: | ||
| 1454 | The initial range wrapped as a $(D SortedRange) with its predicates | ||
| 1455 | converted to an equivalent single predicate. | ||
| 1456 | */ | ||
| 1457 | template multiSort(less...) //if (less.length > 1) | ||
| 1458 | { | ||
| 1459 | auto multiSort(Range)(Range r) | ||
| 1460 | if (validPredicates!(ElementType!Range, less)) | ||
| 1461 | { | ||
| 1462 | import std.meta : AliasSeq; | ||
| 1463 | import std.range : assumeSorted; | ||
| 1464 | static if (is(typeof(less[$ - 1]) == SwapStrategy)) | ||
| 1465 | { | ||
| 1466 | enum ss = less[$ - 1]; | ||
| 1467 | alias funs = less[0 .. $ - 1]; | ||
| 1468 | } | ||
| 1469 | else | ||
| 1470 | { | ||
| 1471 | enum ss = SwapStrategy.unstable; | ||
| 1472 | alias funs = less; | ||
| 1473 | } | ||
| 1474 | |||
| 1475 | static if (funs.length == 0) | ||
| 1476 | static assert(false, "No sorting predicate provided for multiSort"); | ||
| 1477 | else | ||
| 1478 | static if (funs.length == 1) | ||
| 1479 | return sort!(funs[0], ss, Range)(r); | ||
| 1480 | else | ||
| 1481 | { | ||
| 1482 | multiSortImpl!(Range, ss, funs)(r); | ||
| 1483 | return assumeSorted!(multiSortPredFun!(Range, funs))(r); | ||
| 1484 | } | ||
| 1485 | } | ||
| 1486 | } | ||
| 1487 | |||
| 1488 | private bool multiSortPredFun(Range, funs...)(ElementType!Range a, ElementType!Range b) | ||
| 1489 | { | ||
| 1490 | foreach (f; funs) | ||
| 1491 | { | ||
| 1492 | alias lessFun = binaryFun!(f); | ||
| 1493 | if (lessFun(a, b)) return true; | ||
| 1494 | if (lessFun(b, a)) return false; | ||
| 1495 | } | ||
| 1496 | return false; | ||
| 1497 | } | ||
| 1498 | |||
| 1499 | private void multiSortImpl(Range, SwapStrategy ss, funs...)(Range r) | ||
| 1500 | { | ||
| 1501 | alias lessFun = binaryFun!(funs[0]); | ||
| 1502 | |||
| 1503 | static if (funs.length > 1) | ||
| 1504 | { | ||
| 1505 | while (r.length > 1) | ||
| 1506 | { | ||
| 1507 | auto p = getPivot!lessFun(r); | ||
| 1508 | auto t = partition3!(funs[0], ss)(r, r[p]); | ||
| 1509 | if (t[0].length <= t[2].length) | ||
| 1510 | { | ||
| 1511 | multiSortImpl!(Range, ss, funs)(t[0]); | ||
| 1512 | multiSortImpl!(Range, ss, funs[1 .. $])(t[1]); | ||
| 1513 | r = t[2]; | ||
| 1514 | } | ||
| 1515 | else | ||
| 1516 | { | ||
| 1517 | multiSortImpl!(Range, ss, funs[1 .. $])(t[1]); | ||
| 1518 | multiSortImpl!(Range, ss, funs)(t[2]); | ||
| 1519 | r = t[0]; | ||
| 1520 | } | ||
| 1521 | } | ||
| 1522 | } | ||
| 1523 | else | ||
| 1524 | { | ||
| 1525 | sort!(lessFun, ss)(r); | ||
| 1526 | } | ||
| 1527 | } | ||
| 1528 | |||
| 1529 | /// | ||
| 1530 | @safe unittest | ||
| 1531 | { | ||
| 1532 | import std.algorithm.mutation : SwapStrategy; | ||
| 1533 | static struct Point { int x, y; } | ||
| 1534 | auto pts1 = [ Point(0, 0), Point(5, 5), Point(0, 1), Point(0, 2) ]; | ||
| 1535 | auto pts2 = [ Point(0, 0), Point(0, 1), Point(0, 2), Point(5, 5) ]; | ||
| 1536 | multiSort!("a.x < b.x", "a.y < b.y", SwapStrategy.unstable)(pts1); | ||
| 1537 | assert(pts1 == pts2); | ||
| 1538 | } | ||
| 1539 | |||
| 1540 | @safe unittest | ||
| 1541 | { | ||
| 1542 | import std.algorithm.comparison : equal; | ||
| 1543 | import std.range; | ||
| 1544 | |||
| 1545 | static struct Point { int x, y; } | ||
| 1546 | auto pts1 = [ Point(5, 6), Point(1, 0), Point(5, 7), Point(1, 1), Point(1, 2), Point(0, 1) ]; | ||
| 1547 | auto pts2 = [ Point(0, 1), Point(1, 0), Point(1, 1), Point(1, 2), Point(5, 6), Point(5, 7) ]; | ||
| 1548 | static assert(validPredicates!(Point, "a.x < b.x", "a.y < b.y")); | ||
| 1549 | multiSort!("a.x < b.x", "a.y < b.y", SwapStrategy.unstable)(pts1); | ||
| 1550 | assert(pts1 == pts2); | ||
| 1551 | |||
| 1552 | auto pts3 = indexed(pts1, iota(pts1.length)); | ||
| 1553 | assert(pts3.multiSort!("a.x < b.x", "a.y < b.y", SwapStrategy.unstable).release.equal(pts2)); | ||
| 1554 | |||
| 1555 | auto pts4 = iota(10).array; | ||
| 1556 | assert(pts4.multiSort!("a > b").release.equal(iota(10).retro)); | ||
| 1557 | } | ||
| 1558 | |||
| 1559 | @safe unittest //issue 9160 (L-value only comparators) | ||
| 1560 | { | ||
| 1561 | static struct A | ||
| 1562 | { | ||
| 1563 | int x; | ||
| 1564 | int y; | ||
| 1565 | } | ||
| 1566 | |||
| 1567 | static bool byX(const ref A lhs, const ref A rhs) | ||
| 1568 | { | ||
| 1569 | return lhs.x < rhs.x; | ||
| 1570 | } | ||
| 1571 | |||
| 1572 | static bool byY(const ref A lhs, const ref A rhs) | ||
| 1573 | { | ||
| 1574 | return lhs.y < rhs.y; | ||
| 1575 | } | ||
| 1576 | |||
| 1577 | auto points = [ A(4, 1), A(2, 4)]; | ||
| 1578 | multiSort!(byX, byY)(points); | ||
| 1579 | assert(points[0] == A(2, 4)); | ||
| 1580 | assert(points[1] == A(4, 1)); | ||
| 1581 | } | ||
| 1582 | |||
| 1583 | @safe unittest // issue 16179 (cannot access frame of function) | ||
| 1584 | { | ||
| 1585 | auto arr = [[1, 2], [2, 0], [1, 0], [1, 1]]; | ||
| 1586 | int c = 3; | ||
| 1587 | |||
| 1588 | arr.multiSort!( | ||
| 1589 | (a, b) => a[0] < b[0], | ||
| 1590 | (a, b) => c*a[1] < c*b[1] | ||
| 1591 | ); | ||
| 1592 | assert(arr == [[1, 0], [1, 1], [1, 2], [2, 0]]); | ||
| 1593 | } | ||
| 1594 | |||
| 1595 | @safe unittest //Issue 16413 - @system comparison function | ||
| 1596 | { | ||
| 1597 | bool lt(int a, int b) { return a < b; } static @system | ||
| 1598 | auto a = [2, 1]; | ||
| 1599 | a.multiSort!(lt, lt); | ||
| 1600 | assert(a == [1, 2]); | ||
| 1601 | } | ||
| 1602 | |||
| 1603 | private size_t getPivot(alias less, Range)(Range r) | ||
| 1604 | { | ||
| 1605 | auto mid = r.length / 2; | ||
| 1606 | if (r.length < 512) | ||
| 1607 | { | ||
| 1608 | if (r.length >= 32) | ||
| 1609 | medianOf!less(r, size_t(0), mid, r.length - 1); | ||
| 1610 | return mid; | ||
| 1611 | } | ||
| 1612 | |||
| 1613 | // The plan here is to take the median of five by taking five elements in | ||
| 1614 | // the array, segregate around their median, and return the position of the | ||
| 1615 | // third. We choose first, mid, last, and two more in between those. | ||
| 1616 | |||
| 1617 | auto quarter = r.length / 4; | ||
| 1618 | medianOf!less(r, | ||
| 1619 | size_t(0), mid - quarter, mid, mid + quarter, r.length - 1); | ||
| 1620 | return mid; | ||
| 1621 | } | ||
| 1622 | |||
| 1623 | /* | ||
| 1624 | Sorting routine that is optimized for short ranges. Note: uses insertion sort | ||
| 1625 | going downward. Benchmarked a similar routine that goes upward, for some reason | ||
| 1626 | it's slower. | ||
| 1627 | */ | ||
| 1628 | private void shortSort(alias less, Range)(Range r) | ||
| 1629 | { | ||
| 1630 | import std.algorithm.mutation : swapAt; | ||
| 1631 | alias pred = binaryFun!(less); | ||
| 1632 | |||
| 1633 | switch (r.length) | ||
| 1634 | { | ||
| 1635 | case 0: case 1: | ||
| 1636 | return; | ||
| 1637 | case 2: | ||
| 1638 | if (pred(r[1], r[0])) r.swapAt(0, 1); | ||
| 1639 | return; | ||
| 1640 | case 3: | ||
| 1641 | if (pred(r[2], r[0])) | ||
| 1642 | { | ||
| 1643 | if (pred(r[0], r[1])) | ||
| 1644 | { | ||
| 1645 | r.swapAt(0, 1); | ||
| 1646 | r.swapAt(0, 2); | ||
| 1647 | } | ||
| 1648 | else | ||
| 1649 | { | ||
| 1650 | r.swapAt(0, 2); | ||
| 1651 | if (pred(r[1], r[0])) r.swapAt(0, 1); | ||
| 1652 | } | ||
| 1653 | } | ||
| 1654 | else | ||
| 1655 | { | ||
| 1656 | if (pred(r[1], r[0])) | ||
| 1657 | { | ||
| 1658 | r.swapAt(0, 1); | ||
| 1659 | } | ||
| 1660 | else | ||
| 1661 | { | ||
| 1662 | if (pred(r[2], r[1])) r.swapAt(1, 2); | ||
| 1663 | } | ||
| 1664 | } | ||
| 1665 | return; | ||
| 1666 | case 4: | ||
| 1667 | if (pred(r[1], r[0])) r.swapAt(0, 1); | ||
| 1668 | if (pred(r[3], r[2])) r.swapAt(2, 3); | ||
| 1669 | if (pred(r[2], r[0])) r.swapAt(0, 2); | ||
| 1670 | if (pred(r[3], r[1])) r.swapAt(1, 3); | ||
| 1671 | if (pred(r[2], r[1])) r.swapAt(1, 2); | ||
| 1672 | return; | ||
| 1673 | default: | ||
| 1674 | sort5!pred(r[r.length - 5 .. r.length]); | ||
| 1675 | if (r.length == 5) return; | ||
| 1676 | break; | ||
| 1677 | } | ||
| 1678 | |||
| 1679 | assert(r.length >= 6); | ||
| 1680 | /* The last 5 elements of the range are sorted. Proceed with expanding the | ||
| 1681 | sorted portion downward. */ | ||
| 1682 | immutable maxJ = r.length - 2; | ||
| 1683 | for (size_t i = r.length - 6; ; --i) | ||
| 1684 | { | ||
| 1685 | static if (is(typeof(() nothrow | ||
| 1686 | { | ||
| 1687 | auto t = r[0]; if (pred(t, r[0])) r[0] = r[0]; | ||
| 1688 | }))) // Can we afford to temporarily invalidate the array? | ||
| 1689 | { | ||
| 1690 | size_t j = i + 1; | ||
| 1691 | auto temp = r[i]; | ||
| 1692 | if (pred(r[j], temp)) | ||
| 1693 | { | ||
| 1694 | do | ||
| 1695 | { | ||
| 1696 | r[j - 1] = r[j]; | ||
| 1697 | ++j; | ||
| 1698 | } | ||
| 1699 | while (j < r.length && pred(r[j], temp)); | ||
| 1700 | r[j - 1] = temp; | ||
| 1701 | } | ||
| 1702 | } | ||
| 1703 | else | ||
| 1704 | { | ||
| 1705 | size_t j = i; | ||
| 1706 | while (pred(r[j + 1], r[j])) | ||
| 1707 | { | ||
| 1708 | r.swapAt(j, j + 1); | ||
| 1709 | if (j == maxJ) break; | ||
| 1710 | ++j; | ||
| 1711 | } | ||
| 1712 | } | ||
| 1713 | if (i == 0) break; | ||
| 1714 | } | ||
| 1715 | } | ||
| 1716 | |||
| 1717 | @safe unittest | ||
| 1718 | { | ||
| 1719 | import std.random : Random, uniform; | ||
| 1720 | |||
| 1721 | auto rnd = Random(1); | ||
| 1722 | auto a = new int[uniform(100, 200, rnd)]; | ||
| 1723 | foreach (ref e; a) | ||
| 1724 | { | ||
| 1725 | e = uniform(-100, 100, rnd); | ||
| 1726 | } | ||
| 1727 | |||
| 1728 | shortSort!(binaryFun!("a < b"), int[])(a); | ||
| 1729 | assert(isSorted(a)); | ||
| 1730 | } | ||
| 1731 | |||
| 1732 | /* | ||
| 1733 | Sorts the first 5 elements exactly of range r. | ||
| 1734 | */ | ||
| 1735 | private void sort5(alias lt, Range)(Range r) | ||
| 1736 | { | ||
| 1737 | assert(r.length >= 5); | ||
| 1738 | |||
| 1739 | import std.algorithm.mutation : swapAt; | ||
| 1740 | |||
| 1741 | // 1. Sort first two pairs | ||
| 1742 | if (lt(r[1], r[0])) r.swapAt(0, 1); | ||
| 1743 | if (lt(r[3], r[2])) r.swapAt(2, 3); | ||
| 1744 | |||
| 1745 | // 2. Arrange first two pairs by the largest element | ||
| 1746 | if (lt(r[3], r[1])) | ||
| 1747 | { | ||
| 1748 | r.swapAt(0, 2); | ||
| 1749 | r.swapAt(1, 3); | ||
| 1750 | } | ||
| 1751 | assert(!lt(r[1], r[0]) && !lt(r[3], r[1]) && !lt(r[3], r[2])); | ||
| 1752 | |||
| 1753 | // 3. Insert 4 into [0, 1, 3] | ||
| 1754 | if (lt(r[4], r[1])) | ||
| 1755 | { | ||
| 1756 | r.swapAt(3, 4); | ||
| 1757 | r.swapAt(1, 3); | ||
| 1758 | if (lt(r[1], r[0])) | ||
| 1759 | { | ||
| 1760 | r.swapAt(0, 1); | ||
| 1761 | } | ||
| 1762 | } | ||
| 1763 | else if (lt(r[4], r[3])) | ||
| 1764 | { | ||
| 1765 | r.swapAt(3, 4); | ||
| 1766 | } | ||
| 1767 | assert(!lt(r[1], r[0]) && !lt(r[3], r[1]) && !lt(r[4], r[3])); | ||
| 1768 | |||
| 1769 | // 4. Insert 2 into [0, 1, 3, 4] (note: we already know the last is greater) | ||
| 1770 | assert(!lt(r[4], r[2])); | ||
| 1771 | if (lt(r[2], r[1])) | ||
| 1772 | { | ||
| 1773 | r.swapAt(1, 2); | ||
| 1774 | if (lt(r[1], r[0])) | ||
| 1775 | { | ||
| 1776 | r.swapAt(0, 1); | ||
| 1777 | } | ||
| 1778 | } | ||
| 1779 | else if (lt(r[3], r[2])) | ||
| 1780 | { | ||
| 1781 | r.swapAt(2, 3); | ||
| 1782 | } | ||
| 1783 | // 7 comparisons, 0-9 swaps | ||
| 1784 | } | ||
| 1785 | |||
| 1786 | @safe unittest | ||
| 1787 | { | ||
| 1788 | import std.algorithm.iteration : permutations; | ||
| 1789 | import std.algorithm.mutation : copy; | ||
| 1790 | |||
| 1791 | int[5] buf; | ||
| 1792 | foreach (per; iota(5).permutations) | ||
| 1793 | { | ||
| 1794 | per.copy(buf[]); | ||
| 1795 | sort5!((a, b) => a < b)(buf[]); | ||
| 1796 | assert(buf[].isSorted); | ||
| 1797 | } | ||
| 1798 | } | ||
| 1799 | |||
| 1800 | // sort | ||
| 1801 | /** | ||
| 1802 | Sorts a random-access range according to the predicate $(D less). Performs | ||
| 1803 | $(BIGOH r.length * log(r.length)) evaluations of $(D less). If `less` involves | ||
| 1804 | expensive computations on the _sort key, it may be worthwhile to use | ||
| 1805 | $(LREF schwartzSort) instead. | ||
| 1806 | |||
| 1807 | Stable sorting requires $(D hasAssignableElements!Range) to be true. | ||
| 1808 | |||
| 1809 | $(D sort) returns a $(REF SortedRange, std,range) over the original range, | ||
| 1810 | allowing functions that can take advantage of sorted data to know that the | ||
| 1811 | range is sorted and adjust accordingly. The $(REF SortedRange, std,range) is a | ||
| 1812 | wrapper around the original range, so both it and the original range are sorted. | ||
| 1813 | Other functions can't know that the original range has been sorted, but | ||
| 1814 | they $(I can) know that $(REF SortedRange, std,range) has been sorted. | ||
| 1815 | |||
| 1816 | Preconditions: | ||
| 1817 | |||
| 1818 | The predicate is expected to satisfy certain rules in order for $(D sort) to | ||
| 1819 | behave as expected - otherwise, the program may fail on certain inputs (but not | ||
| 1820 | others) when not compiled in release mode, due to the cursory $(D assumeSorted) | ||
| 1821 | check. Specifically, $(D sort) expects $(D less(a,b) && less(b,c)) to imply | ||
| 1822 | $(D less(a,c)) (transitivity), and, conversely, $(D !less(a,b) && !less(b,c)) to | ||
| 1823 | imply $(D !less(a,c)). Note that the default predicate ($(D "a < b")) does not | ||
| 1824 | always satisfy these conditions for floating point types, because the expression | ||
| 1825 | will always be $(D false) when either $(D a) or $(D b) is NaN. | ||
| 1826 | Use $(REF cmp, std,math) instead. | ||
| 1827 | |||
| 1828 | Params: | ||
| 1829 | less = The predicate to sort by. | ||
| 1830 | ss = The swapping strategy to use. | ||
| 1831 | r = The range to sort. | ||
| 1832 | |||
| 1833 | Returns: The initial range wrapped as a $(D SortedRange) with the predicate | ||
| 1834 | $(D binaryFun!less). | ||
| 1835 | |||
| 1836 | Algorithms: $(HTTP en.wikipedia.org/wiki/Introsort, Introsort) is used for unstable sorting and | ||
| 1837 | $(HTTP en.wikipedia.org/wiki/Timsort, Timsort) is used for stable sorting. | ||
| 1838 | Each algorithm has benefits beyond stability. Introsort is generally faster but | ||
| 1839 | Timsort may achieve greater speeds on data with low entropy or if predicate calls | ||
| 1840 | are expensive. Introsort performs no allocations whereas Timsort will perform one | ||
| 1841 | or more allocations per call. Both algorithms have $(BIGOH n log n) worst-case | ||
| 1842 | time complexity. | ||
| 1843 | |||
| 1844 | See_Also: | ||
| 1845 | $(REF assumeSorted, std,range)$(BR) | ||
| 1846 | $(REF SortedRange, std,range)$(BR) | ||
| 1847 | $(REF SwapStrategy, std,algorithm,mutation)$(BR) | ||
| 1848 | $(REF binaryFun, std,functional) | ||
| 1849 | */ | ||
| 1850 | SortedRange!(Range, less) | ||
| 1851 | sort(alias less = "a < b", SwapStrategy ss = SwapStrategy.unstable, | ||
| 1852 | Range)(Range r) | ||
| 1853 | if (((ss == SwapStrategy.unstable && (hasSwappableElements!Range || | ||
| 1854 | hasAssignableElements!Range)) || | ||
| 1855 | (ss != SwapStrategy.unstable && hasAssignableElements!Range)) && | ||
| 1856 | isRandomAccessRange!Range && | ||
| 1857 | hasSlicing!Range && | ||
| 1858 | hasLength!Range) | ||
| 1859 | /+ Unstable sorting uses the quicksort algorithm, which uses swapAt, | ||
| 1860 | which either uses swap(...), requiring swappable elements, or just | ||
| 1861 | swaps using assignment. | ||
| 1862 | Stable sorting uses TimSort, which needs to copy elements into a buffer, | ||
| 1863 | requiring assignable elements. +/ | ||
| 1864 | { | ||
| 1865 | import std.range : assumeSorted; | ||
| 1866 | alias lessFun = binaryFun!(less); | ||
| 1867 | alias LessRet = typeof(lessFun(r.front, r.front)); // instantiate lessFun | ||
| 1868 | static if (is(LessRet == bool)) | ||
| 1869 | { | ||
| 1870 | static if (ss == SwapStrategy.unstable) | ||
| 1871 | quickSortImpl!(lessFun)(r, r.length); | ||
| 1872 | else //use Tim Sort for semistable & stable | ||
| 1873 | TimSortImpl!(lessFun, Range).sort(r, null); | ||
| 1874 | |||
| 1875 | assert(isSorted!lessFun(r), "Failed to sort range of type " ~ Range.stringof); | ||
| 1876 | } | ||
| 1877 | else | ||
| 1878 | { | ||
| 1879 | static assert(false, "Invalid predicate passed to sort: " ~ less.stringof); | ||
| 1880 | } | ||
| 1881 | return assumeSorted!less(r); | ||
| 1882 | } | ||
| 1883 | |||
| 1884 | /// | ||
| 1885 | @safe pure nothrow unittest | ||
| 1886 | { | ||
| 1887 | int[] array = [ 1, 2, 3, 4 ]; | ||
| 1888 | |||
| 1889 | // sort in descending order | ||
| 1890 | array.sort!("a > b"); | ||
| 1891 | assert(array == [ 4, 3, 2, 1 ]); | ||
| 1892 | |||
| 1893 | // sort in ascending order | ||
| 1894 | array.sort(); | ||
| 1895 | assert(array == [ 1, 2, 3, 4 ]); | ||
| 1896 | |||
| 1897 | // sort with reusable comparator and chain | ||
| 1898 | alias myComp = (x, y) => x > y; | ||
| 1899 | assert(array.sort!(myComp).release == [ 4, 3, 2, 1 ]); | ||
| 1900 | } | ||
| 1901 | |||
| 1902 | /// | ||
| 1903 | @safe unittest | ||
| 1904 | { | ||
| 1905 | // Showcase stable sorting | ||
| 1906 | import std.algorithm.mutation : SwapStrategy; | ||
| 1907 | string[] words = [ "aBc", "a", "abc", "b", "ABC", "c" ]; | ||
| 1908 | sort!("toUpper(a) < toUpper(b)", SwapStrategy.stable)(words); | ||
| 1909 | assert(words == [ "a", "aBc", "abc", "ABC", "b", "c" ]); | ||
| 1910 | } | ||
| 1911 | |||
| 1912 | /// | ||
| 1913 | @safe unittest | ||
| 1914 | { | ||
| 1915 | // Sorting floating-point numbers in presence of NaN | ||
| 1916 | double[] numbers = [-0.0, 3.0, -2.0, double.nan, 0.0, -double.nan]; | ||
| 1917 | |||
| 1918 | import std.algorithm.comparison : equal; | ||
| 1919 | import std.math : cmp, isIdentical; | ||
| 1920 | |||
| 1921 | sort!((a, b) => cmp(a, b) < 0)(numbers); | ||
| 1922 | |||
| 1923 | double[] sorted = [-double.nan, -2.0, -0.0, 0.0, 3.0, double.nan]; | ||
| 1924 | assert(numbers.equal!isIdentical(sorted)); | ||
| 1925 | } | ||
| 1926 | |||
| 1927 | @safe unittest | ||
| 1928 | { | ||
| 1929 | // Simple regression benchmark | ||
| 1930 | import std.algorithm.iteration, std.algorithm.mutation, std.random; | ||
| 1931 | Random rng; | ||
| 1932 | int[] a = iota(20148).map!(_ => uniform(-1000, 1000, rng)).array; | ||
| 1933 | static uint comps; | ||
| 1934 | static bool less(int a, int b) { ++comps; return a < b; } | ||
| 1935 | sort!less(a); // random numbers | ||
| 1936 | sort!less(a); // sorted ascending | ||
| 1937 | a.reverse(); | ||
| 1938 | sort!less(a); // sorted descending | ||
| 1939 | a[] = 0; | ||
| 1940 | sort!less(a); // all equal | ||
| 1941 | |||
| 1942 | // This should get smaller with time. On occasion it may go larger, but only | ||
| 1943 | // if there's thorough justification. | ||
| 1944 | debug enum uint watermark = 1676280; | ||
| 1945 | else enum uint watermark = 1676220; | ||
| 1946 | |||
| 1947 | import std.conv; | ||
| 1948 | assert(comps <= watermark, text("You seem to have pessimized sort! ", | ||
| 1949 | watermark, " < ", comps)); | ||
| 1950 | assert(comps >= watermark, text("You seem to have improved sort!", | ||
| 1951 | " Please update watermark from ", watermark, " to ", comps)); | ||
| 1952 | } | ||
| 1953 | |||
| 1954 | @safe unittest | ||
| 1955 | { | ||
| 1956 | import std.algorithm.internal : rndstuff; | ||
| 1957 | import std.algorithm.mutation : swapRanges; | ||
| 1958 | import std.random : Random, unpredictableSeed, uniform; | ||
| 1959 | import std.uni : toUpper; | ||
| 1960 | |||
| 1961 | // sort using delegate | ||
| 1962 | auto a = new int[100]; | ||
| 1963 | auto rnd = Random(unpredictableSeed); | ||
| 1964 | foreach (ref e; a) | ||
| 1965 | { | ||
| 1966 | e = uniform(-100, 100, rnd); | ||
| 1967 | } | ||
| 1968 | |||
| 1969 | int i = 0; | ||
| 1970 | bool greater2(int a, int b) @safe { return a + i > b + i; } | ||
| 1971 | auto greater = &greater2; | ||
| 1972 | sort!(greater)(a); | ||
| 1973 | assert(isSorted!(greater)(a)); | ||
| 1974 | |||
| 1975 | // sort using string | ||
| 1976 | sort!("a < b")(a); | ||
| 1977 | assert(isSorted!("a < b")(a)); | ||
| 1978 | |||
| 1979 | // sort using function; all elements equal | ||
| 1980 | foreach (ref e; a) | ||
| 1981 | { | ||
| 1982 | e = 5; | ||
| 1983 | } | ||
| 1984 | static bool less(int a, int b) { return a < b; } | ||
| 1985 | sort!(less)(a); | ||
| 1986 | assert(isSorted!(less)(a)); | ||
| 1987 | |||
| 1988 | string[] words = [ "aBc", "a", "abc", "b", "ABC", "c" ]; | ||
| 1989 | bool lessi(string a, string b) { return toUpper(a) < toUpper(b); } | ||
| 1990 | sort!(lessi, SwapStrategy.stable)(words); | ||
| 1991 | assert(words == [ "a", "aBc", "abc", "ABC", "b", "c" ]); | ||
| 1992 | |||
| 1993 | // sort using ternary predicate | ||
| 1994 | //sort!("b - a")(a); | ||
| 1995 | //assert(isSorted!(less)(a)); | ||
| 1996 | |||
| 1997 | a = rndstuff!(int)(); | ||
| 1998 | sort(a); | ||
| 1999 | assert(isSorted(a)); | ||
| 2000 | auto b = rndstuff!(string)(); | ||
| 2001 | sort!("toLower(a) < toLower(b)")(b); | ||
| 2002 | assert(isSorted!("toUpper(a) < toUpper(b)")(b)); | ||
| 2003 | |||
| 2004 | { | ||
| 2005 | // Issue 10317 | ||
| 2006 | enum E_10317 { a, b } | ||
| 2007 | auto a_10317 = new E_10317[10]; | ||
| 2008 | sort(a_10317); | ||
| 2009 | } | ||
| 2010 | |||
| 2011 | { | ||
| 2012 | // Issue 7767 | ||
| 2013 | // Unstable sort should complete without an excessive number of predicate calls | ||
| 2014 | // This would suggest it's running in quadratic time | ||
| 2015 | |||
| 2016 | // Compilation error if predicate is not static, i.e. a nested function | ||
| 2017 | static uint comp; | ||
| 2018 | static bool pred(size_t a, size_t b) | ||
| 2019 | { | ||
| 2020 | ++comp; | ||
| 2021 | return a < b; | ||
| 2022 | } | ||
| 2023 | |||
| 2024 | size_t[] arr; | ||
| 2025 | arr.length = 1024; | ||
| 2026 | |||
| 2027 | foreach (k; 0 .. arr.length) arr[k] = k; | ||
| 2028 | swapRanges(arr[0..$/2], arr[$/2..$]); | ||
| 2029 | |||
| 2030 | sort!(pred, SwapStrategy.unstable)(arr); | ||
| 2031 | assert(comp < 25_000); | ||
| 2032 | } | ||
| 2033 | |||
| 2034 | { | ||
| 2035 | import std.algorithm.mutation : swap; | ||
| 2036 | |||
| 2037 | bool proxySwapCalled; | ||
| 2038 | struct S | ||
| 2039 | { | ||
| 2040 | int i; | ||
| 2041 | alias i this; | ||
| 2042 | void proxySwap(ref S other) { swap(i, other.i); proxySwapCalled = true; } | ||
| 2043 | @disable void opAssign(S value); | ||
| 2044 | } | ||
| 2045 | |||
| 2046 | alias R = S[]; | ||
| 2047 | R r = [S(3), S(2), S(1)]; | ||
| 2048 | static assert(hasSwappableElements!R); | ||
| 2049 | static assert(!hasAssignableElements!R); | ||
| 2050 | r.sort(); | ||
| 2051 | assert(proxySwapCalled); | ||
| 2052 | } | ||
| 2053 | } | ||
| 2054 | |||
| 2055 | private void quickSortImpl(alias less, Range)(Range r, size_t depth) | ||
| 2056 | { | ||
| 2057 | import std.algorithm.comparison : min, max; | ||
| 2058 | import std.algorithm.mutation : swap, swapAt; | ||
| 2059 | |||
| 2060 | alias Elem = ElementType!(Range); | ||
| 2061 | enum size_t shortSortGetsBetter = max(32, 1024 / Elem.sizeof); | ||
| 2062 | static assert(shortSortGetsBetter >= 1); | ||
| 2063 | |||
| 2064 | // partition | ||
| 2065 | while (r.length > shortSortGetsBetter) | ||
| 2066 | { | ||
| 2067 | if (depth == 0) | ||
| 2068 | { | ||
| 2069 | HeapOps!(less, Range).heapSort(r); | ||
| 2070 | return; | ||
| 2071 | } | ||
| 2072 | depth = depth >= depth.max / 2 ? (depth / 3) * 2 : (depth * 2) / 3; | ||
| 2073 | |||
| 2074 | const pivotIdx = getPivot!(less)(r); | ||
| 2075 | auto pivot = r[pivotIdx]; | ||
| 2076 | |||
| 2077 | // partition | ||
| 2078 | r.swapAt(pivotIdx, r.length - 1); | ||
| 2079 | size_t lessI = size_t.max, greaterI = r.length - 1; | ||
| 2080 | |||
| 2081 | outer: for (;;) | ||
| 2082 | { | ||
| 2083 | alias pred = binaryFun!less; | ||
| 2084 | while (pred(r[++lessI], pivot)) {} | ||
| 2085 | assert(lessI <= greaterI, "sort: invalid comparison function."); | ||
| 2086 | for (;;) | ||
| 2087 | { | ||
| 2088 | if (greaterI == lessI) break outer; | ||
| 2089 | if (!pred(pivot, r[--greaterI])) break; | ||
| 2090 | } | ||
| 2091 | assert(lessI <= greaterI, "sort: invalid comparison function."); | ||
| 2092 | if (lessI == greaterI) break; | ||
| 2093 | r.swapAt(lessI, greaterI); | ||
| 2094 | } | ||
| 2095 | |||
| 2096 | r.swapAt(r.length - 1, lessI); | ||
| 2097 | auto left = r[0 .. lessI], right = r[lessI + 1 .. r.length]; | ||
| 2098 | if (right.length > left.length) | ||
| 2099 | { | ||
| 2100 | swap(left, right); | ||
| 2101 | } | ||
| 2102 | .quickSortImpl!(less, Range)(right, depth); | ||
| 2103 | r = left; | ||
| 2104 | } | ||
| 2105 | // residual sort | ||
| 2106 | static if (shortSortGetsBetter > 1) | ||
| 2107 | { | ||
| 2108 | shortSort!(less, Range)(r); | ||
| 2109 | } | ||
| 2110 | } | ||
| 2111 | |||
| 2112 | // Heap operations for random-access ranges | ||
| 2113 | package(std) template HeapOps(alias less, Range) | ||
| 2114 | { | ||
| 2115 | import std.algorithm.mutation : swapAt; | ||
| 2116 | |||
| 2117 | static assert(isRandomAccessRange!Range); | ||
| 2118 | static assert(hasLength!Range); | ||
| 2119 | static assert(hasSwappableElements!Range || hasAssignableElements!Range); | ||
| 2120 | |||
| 2121 | alias lessFun = binaryFun!less; | ||
| 2122 | |||
| 2123 | //template because of @@@12410@@@ | ||
| 2124 | void heapSort()(Range r) | ||
| 2125 | { | ||
| 2126 | // If true, there is nothing to do | ||
| 2127 | if (r.length < 2) return; | ||
| 2128 | // Build Heap | ||
| 2129 | buildHeap(r); | ||
| 2130 | // Sort | ||
| 2131 | for (size_t i = r.length - 1; i > 0; --i) | ||
| 2132 | { | ||
| 2133 | r.swapAt(0, i); | ||
| 2134 | percolate(r, 0, i); | ||
| 2135 | } | ||
| 2136 | } | ||
| 2137 | |||
| 2138 | //template because of @@@12410@@@ | ||
| 2139 | void buildHeap()(Range r) | ||
| 2140 | { | ||
| 2141 | immutable n = r.length; | ||
| 2142 | for (size_t i = n / 2; i-- > 0; ) | ||
| 2143 | { | ||
| 2144 | siftDown(r, i, n); | ||
| 2145 | } | ||
| 2146 | assert(isHeap(r)); | ||
| 2147 | } | ||
| 2148 | |||
| 2149 | bool isHeap()(Range r) | ||
| 2150 | { | ||
| 2151 | size_t parent = 0; | ||
| 2152 | foreach (child; 1 .. r.length) | ||
| 2153 | { | ||
| 2154 | if (lessFun(r[parent], r[child])) return false; | ||
| 2155 | // Increment parent every other pass | ||
| 2156 | parent += !(child & 1); | ||
| 2157 | } | ||
| 2158 | return true; | ||
| 2159 | } | ||
| 2160 | |||
| 2161 | // Sifts down r[parent] (which is initially assumed to be messed up) so the | ||
| 2162 | // heap property is restored for r[parent .. end]. | ||
| 2163 | // template because of @@@12410@@@ | ||
| 2164 | void siftDown()(Range r, size_t parent, immutable size_t end) | ||
| 2165 | { | ||
| 2166 | for (;;) | ||
| 2167 | { | ||
| 2168 | auto child = (parent + 1) * 2; | ||
| 2169 | if (child >= end) | ||
| 2170 | { | ||
| 2171 | // Leftover left child? | ||
| 2172 | if (child == end && lessFun(r[parent], r[--child])) | ||
| 2173 | r.swapAt(parent, child); | ||
| 2174 | break; | ||
| 2175 | } | ||
| 2176 | auto leftChild = child - 1; | ||
| 2177 | if (lessFun(r[child], r[leftChild])) child = leftChild; | ||
| 2178 | if (!lessFun(r[parent], r[child])) break; | ||
| 2179 | r.swapAt(parent, child); | ||
| 2180 | parent = child; | ||
| 2181 | } | ||
| 2182 | } | ||
| 2183 | |||
| 2184 | // Alternate version of siftDown that performs fewer comparisons, see | ||
| 2185 | // https://en.wikipedia.org/wiki/Heapsort#Bottom-up_heapsort. The percolate | ||
| 2186 | // process first sifts the parent all the way down (without comparing it | ||
| 2187 | // against the leaves), and then a bit up until the heap property is | ||
| 2188 | // restored. So there are more swaps but fewer comparisons. Gains are made | ||
| 2189 | // when the final position is likely to end toward the bottom of the heap, | ||
| 2190 | // so not a lot of sifts back are performed. | ||
| 2191 | //template because of @@@12410@@@ | ||
| 2192 | void percolate()(Range r, size_t parent, immutable size_t end) | ||
| 2193 | { | ||
| 2194 | immutable root = parent; | ||
| 2195 | |||
| 2196 | // Sift down | ||
| 2197 | for (;;) | ||
| 2198 | { | ||
| 2199 | auto child = (parent + 1) * 2; | ||
| 2200 | |||
| 2201 | if (child >= end) | ||
| 2202 | { | ||
| 2203 | if (child == end) | ||
| 2204 | { | ||
| 2205 | // Leftover left node. | ||
| 2206 | --child; | ||
| 2207 | r.swapAt(parent, child); | ||
| 2208 | parent = child; | ||
| 2209 | } | ||
| 2210 | break; | ||
| 2211 | } | ||
| 2212 | |||
| 2213 | auto leftChild = child - 1; | ||
| 2214 | if (lessFun(r[child], r[leftChild])) child = leftChild; | ||
| 2215 | r.swapAt(parent, child); | ||
| 2216 | parent = child; | ||
| 2217 | } | ||
| 2218 | |||
| 2219 | // Sift up | ||
| 2220 | for (auto child = parent; child > root; child = parent) | ||
| 2221 | { | ||
| 2222 | parent = (child - 1) / 2; | ||
| 2223 | if (!lessFun(r[parent], r[child])) break; | ||
| 2224 | r.swapAt(parent, child); | ||
| 2225 | } | ||
| 2226 | } | ||
| 2227 | } | ||
| 2228 | |||
| 2229 | // Tim Sort implementation | ||
| 2230 | private template TimSortImpl(alias pred, R) | ||
| 2231 | { | ||
| 2232 | import core.bitop : bsr; | ||
| 2233 | import std.array : uninitializedArray; | ||
| 2234 | |||
| 2235 | static assert(isRandomAccessRange!R); | ||
| 2236 | static assert(hasLength!R); | ||
| 2237 | static assert(hasSlicing!R); | ||
| 2238 | static assert(hasAssignableElements!R); | ||
| 2239 | |||
| 2240 | alias T = ElementType!R; | ||
| 2241 | |||
| 2242 | alias less = binaryFun!pred; | ||
| 2243 | alias greater = (a, b) => less(b, a); | ||
| 2244 | alias greaterEqual = (a, b) => !less(a, b); | ||
| 2245 | alias lessEqual = (a, b) => !less(b, a); | ||
| 2246 | |||
| 2247 | enum minimalMerge = 128; | ||
| 2248 | enum minimalGallop = 7; | ||
| 2249 | enum minimalStorage = 256; | ||
| 2250 | enum stackSize = 40; | ||
| 2251 | |||
| 2252 | struct Slice{ size_t base, length; } | ||
| 2253 | |||
| 2254 | // Entry point for tim sort | ||
| 2255 | void sort()(R range, T[] temp) | ||
| 2256 | { | ||
| 2257 | import std.algorithm.comparison : min; | ||
| 2258 | |||
| 2259 | // Do insertion sort on small range | ||
| 2260 | if (range.length <= minimalMerge) | ||
| 2261 | { | ||
| 2262 | binaryInsertionSort(range); | ||
| 2263 | return; | ||
| 2264 | } | ||
| 2265 | |||
| 2266 | immutable minRun = minRunLength(range.length); | ||
| 2267 | immutable minTemp = min(range.length / 2, minimalStorage); | ||
| 2268 | size_t minGallop = minimalGallop; | ||
| 2269 | Slice[stackSize] stack = void; | ||
| 2270 | size_t stackLen = 0; | ||
| 2271 | |||
| 2272 | // Allocate temporary memory if not provided by user | ||
| 2273 | if (temp.length < minTemp) temp = () @trusted { return uninitializedArray!(T[])(minTemp); }(); | ||
| 2274 | |||
| 2275 | for (size_t i = 0; i < range.length; ) | ||
| 2276 | { | ||
| 2277 | // Find length of first run in list | ||
| 2278 | size_t runLen = firstRun(range[i .. range.length]); | ||
| 2279 | |||
| 2280 | // If run has less than minRun elements, extend using insertion sort | ||
| 2281 | if (runLen < minRun) | ||
| 2282 | { | ||
| 2283 | // Do not run farther than the length of the range | ||
| 2284 | immutable force = range.length - i > minRun ? minRun : range.length - i; | ||
| 2285 | binaryInsertionSort(range[i .. i + force], runLen); | ||
| 2286 | runLen = force; | ||
| 2287 | } | ||
| 2288 | |||
| 2289 | // Push run onto stack | ||
| 2290 | stack[stackLen++] = Slice(i, runLen); | ||
| 2291 | i += runLen; | ||
| 2292 | |||
| 2293 | // Collapse stack so that (e1 > e2 + e3 && e2 > e3) | ||
| 2294 | // STACK is | ... e1 e2 e3 > | ||
| 2295 | while (stackLen > 1) | ||
| 2296 | { | ||
| 2297 | immutable run4 = stackLen - 1; | ||
| 2298 | immutable run3 = stackLen - 2; | ||
| 2299 | immutable run2 = stackLen - 3; | ||
| 2300 | immutable run1 = stackLen - 4; | ||
| 2301 | |||
| 2302 | if ( (stackLen > 2 && stack[run2].length <= stack[run3].length + stack[run4].length) || | ||
| 2303 | (stackLen > 3 && stack[run1].length <= stack[run3].length + stack[run2].length) ) | ||
| 2304 | { | ||
| 2305 | immutable at = stack[run2].length < stack[run4].length ? run2 : run3; | ||
| 2306 | mergeAt(range, stack[0 .. stackLen], at, minGallop, temp); | ||
| 2307 | } | ||
| 2308 | else if (stack[run3].length > stack[run4].length) break; | ||
| 2309 | else mergeAt(range, stack[0 .. stackLen], run3, minGallop, temp); | ||
| 2310 | |||
| 2311 | stackLen -= 1; | ||
| 2312 | } | ||
| 2313 | |||
| 2314 | // Assert that the code above established the invariant correctly | ||
| 2315 | version (assert) | ||
| 2316 | { | ||
| 2317 | if (stackLen == 2) assert(stack[0].length > stack[1].length); | ||
| 2318 | else if (stackLen > 2) | ||
| 2319 | { | ||
| 2320 | foreach (k; 2 .. stackLen) | ||
| 2321 | { | ||
| 2322 | assert(stack[k - 2].length > stack[k - 1].length + stack[k].length); | ||
| 2323 | assert(stack[k - 1].length > stack[k].length); | ||
| 2324 | } | ||
| 2325 | } | ||
| 2326 | } | ||
| 2327 | } | ||
| 2328 | |||
| 2329 | // Force collapse stack until there is only one run left | ||
| 2330 | while (stackLen > 1) | ||
| 2331 | { | ||
| 2332 | immutable run3 = stackLen - 1; | ||
| 2333 | immutable run2 = stackLen - 2; | ||
| 2334 | immutable run1 = stackLen - 3; | ||
| 2335 | immutable at = stackLen >= 3 && stack[run1].length <= stack[run3].length | ||
| 2336 | ? run1 : run2; | ||
| 2337 | mergeAt(range, stack[0 .. stackLen], at, minGallop, temp); | ||
| 2338 | --stackLen; | ||
| 2339 | } | ||
| 2340 | } | ||
| 2341 | |||
| 2342 | // Calculates optimal value for minRun: | ||
| 2343 | // take first 6 bits of n and add 1 if any lower bits are set | ||
| 2344 | size_t minRunLength()(size_t n) | ||
| 2345 | { | ||
| 2346 | immutable shift = bsr(n)-5; | ||
| 2347 | auto result = (n >> shift) + !!(n & ~((1 << shift)-1)); | ||
| 2348 | return result; | ||
| 2349 | } | ||
| 2350 | |||
| 2351 | // Returns length of first run in range | ||
| 2352 | size_t firstRun()(R range) | ||
| 2353 | out(ret) | ||
| 2354 | { | ||
| 2355 | assert(ret <= range.length); | ||
| 2356 | } | ||
| 2357 | body | ||
| 2358 | { | ||
| 2359 | import std.algorithm.mutation : reverse; | ||
| 2360 | |||
| 2361 | if (range.length < 2) return range.length; | ||
| 2362 | |||
| 2363 | size_t i = 2; | ||
| 2364 | if (lessEqual(range[0], range[1])) | ||
| 2365 | { | ||
| 2366 | while (i < range.length && lessEqual(range[i-1], range[i])) ++i; | ||
| 2367 | } | ||
| 2368 | else | ||
| 2369 | { | ||
| 2370 | while (i < range.length && greater(range[i-1], range[i])) ++i; | ||
| 2371 | reverse(range[0 .. i]); | ||
| 2372 | } | ||
| 2373 | return i; | ||
| 2374 | } | ||
| 2375 | |||
| 2376 | // A binary insertion sort for building runs up to minRun length | ||
| 2377 | void binaryInsertionSort()(R range, size_t sortedLen = 1) | ||
| 2378 | out | ||
| 2379 | { | ||
| 2380 | if (!__ctfe) assert(isSorted!pred(range)); | ||
| 2381 | } | ||
| 2382 | body | ||
| 2383 | { | ||
| 2384 | import std.algorithm.mutation : move; | ||
| 2385 | |||
| 2386 | for (; sortedLen < range.length; ++sortedLen) | ||
| 2387 | { | ||
| 2388 | T item = range.moveAt(sortedLen); | ||
| 2389 | size_t lower = 0; | ||
| 2390 | size_t upper = sortedLen; | ||
| 2391 | while (upper != lower) | ||
| 2392 | { | ||
| 2393 | size_t center = (lower + upper) / 2; | ||
| 2394 | if (less(item, range[center])) upper = center; | ||
| 2395 | else lower = center + 1; | ||
| 2396 | } | ||
| 2397 | //Currently (DMD 2.061) moveAll+retro is slightly less | ||
| 2398 | //efficient then stright 'for' loop | ||
| 2399 | //11 instructions vs 7 in the innermost loop [checked on Win32] | ||
| 2400 | //moveAll(retro(range[lower .. sortedLen]), | ||
| 2401 | // retro(range[lower+1 .. sortedLen+1])); | ||
| 2402 | for (upper=sortedLen; upper > lower; upper--) | ||
| 2403 | range[upper] = range.moveAt(upper - 1); | ||
| 2404 | range[lower] = move(item); | ||
| 2405 | } | ||
| 2406 | } | ||
| 2407 | |||
| 2408 | // Merge two runs in stack (at, at + 1) | ||
| 2409 | void mergeAt()(R range, Slice[] stack, immutable size_t at, ref size_t minGallop, ref T[] temp) | ||
| 2410 | in | ||
| 2411 | { | ||
| 2412 | assert(stack.length >= 2); | ||
| 2413 | assert(stack.length - at == 2 || stack.length - at == 3); | ||
| 2414 | } | ||
| 2415 | body | ||
| 2416 | { | ||
| 2417 | immutable base = stack[at].base; | ||
| 2418 | immutable mid = stack[at].length; | ||
| 2419 | immutable len = stack[at + 1].length + mid; | ||
| 2420 | |||
| 2421 | // Pop run from stack | ||
| 2422 | stack[at] = Slice(base, len); | ||
| 2423 | if (stack.length - at == 3) stack[$ - 2] = stack[$ - 1]; | ||
| 2424 | |||
| 2425 | // Merge runs (at, at + 1) | ||
| 2426 | return merge(range[base .. base + len], mid, minGallop, temp); | ||
| 2427 | } | ||
| 2428 | |||
| 2429 | // Merge two runs in a range. Mid is the starting index of the second run. | ||
| 2430 | // minGallop and temp are references; The calling function must receive the updated values. | ||
| 2431 | void merge()(R range, size_t mid, ref size_t minGallop, ref T[] temp) | ||
| 2432 | in | ||
| 2433 | { | ||
| 2434 | if (!__ctfe) | ||
| 2435 | { | ||
| 2436 | assert(isSorted!pred(range[0 .. mid])); | ||
| 2437 | assert(isSorted!pred(range[mid .. range.length])); | ||
| 2438 | } | ||
| 2439 | } | ||
| 2440 | body | ||
| 2441 | { | ||
| 2442 | assert(mid < range.length); | ||
| 2443 | |||
| 2444 | // Reduce range of elements | ||
| 2445 | immutable firstElement = gallopForwardUpper(range[0 .. mid], range[mid]); | ||
| 2446 | immutable lastElement = gallopReverseLower(range[mid .. range.length], range[mid - 1]) + mid; | ||
| 2447 | range = range[firstElement .. lastElement]; | ||
| 2448 | mid -= firstElement; | ||
| 2449 | |||
| 2450 | if (mid == 0 || mid == range.length) return; | ||
| 2451 | |||
| 2452 | // Call function which will copy smaller run into temporary memory | ||
| 2453 | if (mid <= range.length / 2) | ||
| 2454 | { | ||
| 2455 | temp = ensureCapacity(mid, temp); | ||
| 2456 | minGallop = mergeLo(range, mid, minGallop, temp); | ||
| 2457 | } | ||
| 2458 | else | ||
| 2459 | { | ||
| 2460 | temp = ensureCapacity(range.length - mid, temp); | ||
| 2461 | minGallop = mergeHi(range, mid, minGallop, temp); | ||
| 2462 | } | ||
| 2463 | } | ||
| 2464 | |||
| 2465 | // Enlarge size of temporary memory if needed | ||
| 2466 | T[] ensureCapacity()(size_t minCapacity, T[] temp) | ||
| 2467 | out(ret) | ||
| 2468 | { | ||
| 2469 | assert(ret.length >= minCapacity); | ||
| 2470 | } | ||
| 2471 | body | ||
| 2472 | { | ||
| 2473 | if (temp.length < minCapacity) | ||
| 2474 | { | ||
| 2475 | size_t newSize = 1<<(bsr(minCapacity)+1); | ||
| 2476 | //Test for overflow | ||
| 2477 | if (newSize < minCapacity) newSize = minCapacity; | ||
| 2478 | |||
| 2479 | if (__ctfe) temp.length = newSize; | ||
| 2480 | else temp = () @trusted { return uninitializedArray!(T[])(newSize); }(); | ||
| 2481 | } | ||
| 2482 | return temp; | ||
| 2483 | } | ||
| 2484 | |||
| 2485 | // Merge front to back. Returns new value of minGallop. | ||
| 2486 | // temp must be large enough to store range[0 .. mid] | ||
| 2487 | size_t mergeLo()(R range, immutable size_t mid, size_t minGallop, T[] temp) | ||
| 2488 | out | ||
| 2489 | { | ||
| 2490 | if (!__ctfe) assert(isSorted!pred(range)); | ||
| 2491 | } | ||
| 2492 | body | ||
| 2493 | { | ||
| 2494 | import std.algorithm.mutation : copy; | ||
| 2495 | |||
| 2496 | assert(mid <= range.length); | ||
| 2497 | assert(temp.length >= mid); | ||
| 2498 | |||
| 2499 | // Copy run into temporary memory | ||
| 2500 | temp = temp[0 .. mid]; | ||
| 2501 | copy(range[0 .. mid], temp); | ||
| 2502 | |||
| 2503 | // Move first element into place | ||
| 2504 | range[0] = range[mid]; | ||
| 2505 | |||
| 2506 | size_t i = 1, lef = 0, rig = mid + 1; | ||
| 2507 | size_t count_lef, count_rig; | ||
| 2508 | immutable lef_end = temp.length - 1; | ||
| 2509 | |||
| 2510 | if (lef < lef_end && rig < range.length) | ||
| 2511 | outer: while (true) | ||
| 2512 | { | ||
| 2513 | count_lef = 0; | ||
| 2514 | count_rig = 0; | ||
| 2515 | |||
| 2516 | // Linear merge | ||
| 2517 | while ((count_lef | count_rig) < minGallop) | ||
| 2518 | { | ||
| 2519 | if (lessEqual(temp[lef], range[rig])) | ||
| 2520 | { | ||
| 2521 | range[i++] = temp[lef++]; | ||
| 2522 | if (lef >= lef_end) break outer; | ||
| 2523 | ++count_lef; | ||
| 2524 | count_rig = 0; | ||
| 2525 | } | ||
| 2526 | else | ||
| 2527 | { | ||
| 2528 | range[i++] = range[rig++]; | ||
| 2529 | if (rig >= range.length) break outer; | ||
| 2530 | count_lef = 0; | ||
| 2531 | ++count_rig; | ||
| 2532 | } | ||
| 2533 | } | ||
| 2534 | |||
| 2535 | // Gallop merge | ||
| 2536 | do | ||
| 2537 | { | ||
| 2538 | count_lef = gallopForwardUpper(temp[lef .. $], range[rig]); | ||
| 2539 | foreach (j; 0 .. count_lef) range[i++] = temp[lef++]; | ||
| 2540 | if (lef >= temp.length) break outer; | ||
| 2541 | |||
| 2542 | count_rig = gallopForwardLower(range[rig .. range.length], temp[lef]); | ||
| 2543 | foreach (j; 0 .. count_rig) range[i++] = range[rig++]; | ||
| 2544 | if (rig >= range.length) while (true) | ||
| 2545 | { | ||
| 2546 | range[i++] = temp[lef++]; | ||
| 2547 | if (lef >= temp.length) break outer; | ||
| 2548 | } | ||
| 2549 | |||
| 2550 | if (minGallop > 0) --minGallop; | ||
| 2551 | } | ||
| 2552 | while (count_lef >= minimalGallop || count_rig >= minimalGallop); | ||
| 2553 | |||
| 2554 | minGallop += 2; | ||
| 2555 | } | ||
| 2556 | |||
| 2557 | // Move remaining elements from right | ||
| 2558 | while (rig < range.length) | ||
| 2559 | range[i++] = range[rig++]; | ||
| 2560 | |||
| 2561 | // Move remaining elements from left | ||
| 2562 | while (lef < temp.length) | ||
| 2563 | range[i++] = temp[lef++]; | ||
| 2564 | |||
| 2565 | return minGallop > 0 ? minGallop : 1; | ||
| 2566 | } | ||
| 2567 | |||
| 2568 | // Merge back to front. Returns new value of minGallop. | ||
| 2569 | // temp must be large enough to store range[mid .. range.length] | ||
| 2570 | size_t mergeHi()(R range, immutable size_t mid, size_t minGallop, T[] temp) | ||
| 2571 | out | ||
| 2572 | { | ||
| 2573 | if (!__ctfe) assert(isSorted!pred(range)); | ||
| 2574 | } | ||
| 2575 | body | ||
| 2576 | { | ||
| 2577 | import std.algorithm.mutation : copy; | ||
| 2578 | |||
| 2579 | assert(mid <= range.length); | ||
| 2580 | assert(temp.length >= range.length - mid); | ||
| 2581 | |||
| 2582 | // Copy run into temporary memory | ||
| 2583 | temp = temp[0 .. range.length - mid]; | ||
| 2584 | copy(range[mid .. range.length], temp); | ||
| 2585 | |||
| 2586 | // Move first element into place | ||
| 2587 | range[range.length - 1] = range[mid - 1]; | ||
| 2588 | |||
| 2589 | size_t i = range.length - 2, lef = mid - 2, rig = temp.length - 1; | ||
| 2590 | size_t count_lef, count_rig; | ||
| 2591 | |||
| 2592 | outer: | ||
| 2593 | while (true) | ||
| 2594 | { | ||
| 2595 | count_lef = 0; | ||
| 2596 | count_rig = 0; | ||
| 2597 | |||
| 2598 | // Linear merge | ||
| 2599 | while ((count_lef | count_rig) < minGallop) | ||
| 2600 | { | ||
| 2601 | if (greaterEqual(temp[rig], range[lef])) | ||
| 2602 | { | ||
| 2603 | range[i--] = temp[rig]; | ||
| 2604 | if (rig == 1) | ||
| 2605 | { | ||
| 2606 | // Move remaining elements from left | ||
| 2607 | while (true) | ||
| 2608 | { | ||
| 2609 | range[i--] = range[lef]; | ||
| 2610 | if (lef == 0) break; | ||
| 2611 | --lef; | ||
| 2612 | } | ||
| 2613 | |||
| 2614 | // Move last element into place | ||
| 2615 | range[i] = temp[0]; | ||
| 2616 | |||
| 2617 | break outer; | ||
| 2618 | } | ||
| 2619 | --rig; | ||
| 2620 | count_lef = 0; | ||
| 2621 | ++count_rig; | ||
| 2622 | } | ||
| 2623 | else | ||
| 2624 | { | ||
| 2625 | range[i--] = range[lef]; | ||
| 2626 | if (lef == 0) while (true) | ||
| 2627 | { | ||
| 2628 | range[i--] = temp[rig]; | ||
| 2629 | if (rig == 0) break outer; | ||
| 2630 | --rig; | ||
| 2631 | } | ||
| 2632 | --lef; | ||
| 2633 | ++count_lef; | ||
| 2634 | count_rig = 0; | ||
| 2635 | } | ||
| 2636 | } | ||
| 2637 | |||
| 2638 | // Gallop merge | ||
| 2639 | do | ||
| 2640 | { | ||
| 2641 | count_rig = rig - gallopReverseLower(temp[0 .. rig], range[lef]); | ||
| 2642 | foreach (j; 0 .. count_rig) | ||
| 2643 | { | ||
| 2644 | range[i--] = temp[rig]; | ||
| 2645 | if (rig == 0) break outer; | ||
| 2646 | --rig; | ||
| 2647 | } | ||
| 2648 | |||
| 2649 | count_lef = lef - gallopReverseUpper(range[0 .. lef], temp[rig]); | ||
| 2650 | foreach (j; 0 .. count_lef) | ||
| 2651 | { | ||
| 2652 | range[i--] = range[lef]; | ||
| 2653 | if (lef == 0) while (true) | ||
| 2654 | { | ||
| 2655 | range[i--] = temp[rig]; | ||
| 2656 | if (rig == 0) break outer; | ||
| 2657 | --rig; | ||
| 2658 | } | ||
| 2659 | --lef; | ||
| 2660 | } | ||
| 2661 | |||
| 2662 | if (minGallop > 0) --minGallop; | ||
| 2663 | } | ||
| 2664 | while (count_lef >= minimalGallop || count_rig >= minimalGallop); | ||
| 2665 | |||
| 2666 | minGallop += 2; | ||
| 2667 | } | ||
| 2668 | |||
| 2669 | return minGallop > 0 ? minGallop : 1; | ||
| 2670 | } | ||
| 2671 | |||
| 2672 | // false = forward / lower, true = reverse / upper | ||
| 2673 | template gallopSearch(bool forwardReverse, bool lowerUpper) | ||
| 2674 | { | ||
| 2675 | // Gallop search on range according to attributes forwardReverse and lowerUpper | ||
| 2676 | size_t gallopSearch(R)(R range, T value) | ||
| 2677 | out(ret) | ||
| 2678 | { | ||
| 2679 | assert(ret <= range.length); | ||
| 2680 | } | ||
| 2681 | body | ||
| 2682 | { | ||
| 2683 | size_t lower = 0, center = 1, upper = range.length; | ||
| 2684 | alias gap = center; | ||
| 2685 | |||
| 2686 | static if (forwardReverse) | ||
| 2687 | { | ||
| 2688 | static if (!lowerUpper) alias comp = lessEqual; // reverse lower | ||
| 2689 | static if (lowerUpper) alias comp = less; // reverse upper | ||
| 2690 | |||
| 2691 | // Gallop Search Reverse | ||
| 2692 | while (gap <= upper) | ||
| 2693 | { | ||
| 2694 | if (comp(value, range[upper - gap])) | ||
| 2695 | { | ||
| 2696 | upper -= gap; | ||
| 2697 | gap *= 2; | ||
| 2698 | } | ||
| 2699 | else | ||
| 2700 | { | ||
| 2701 | lower = upper - gap; | ||
| 2702 | break; | ||
| 2703 | } | ||
| 2704 | } | ||
| 2705 | |||
| 2706 | // Binary Search Reverse | ||
| 2707 | while (upper != lower) | ||
| 2708 | { | ||
| 2709 | center = lower + (upper - lower) / 2; | ||
| 2710 | if (comp(value, range[center])) upper = center; | ||
| 2711 | else lower = center + 1; | ||
| 2712 | } | ||
| 2713 | } | ||
| 2714 | else | ||
| 2715 | { | ||
| 2716 | static if (!lowerUpper) alias comp = greater; // forward lower | ||
| 2717 | static if (lowerUpper) alias comp = greaterEqual; // forward upper | ||
| 2718 | |||
| 2719 | // Gallop Search Forward | ||
| 2720 | while (lower + gap < upper) | ||
| 2721 | { | ||
| 2722 | if (comp(value, range[lower + gap])) | ||
| 2723 | { | ||
| 2724 | lower += gap; | ||
| 2725 | gap *= 2; | ||
| 2726 | } | ||
| 2727 | else | ||
| 2728 | { | ||
| 2729 | upper = lower + gap; | ||
| 2730 | break; | ||
| 2731 | } | ||
| 2732 | } | ||
| 2733 | |||
| 2734 | // Binary Search Forward | ||
| 2735 | while (lower != upper) | ||
| 2736 | { | ||
| 2737 | center = lower + (upper - lower) / 2; | ||
| 2738 | if (comp(value, range[center])) lower = center + 1; | ||
| 2739 | else upper = center; | ||
| 2740 | } | ||
| 2741 | } | ||
| 2742 | |||
| 2743 | return lower; | ||
| 2744 | } | ||
| 2745 | } | ||
| 2746 | |||
| 2747 | alias gallopForwardLower = gallopSearch!(false, false); | ||
| 2748 | alias gallopForwardUpper = gallopSearch!(false, true); | ||
| 2749 | alias gallopReverseLower = gallopSearch!( true, false); | ||
| 2750 | alias gallopReverseUpper = gallopSearch!( true, true); | ||
| 2751 | } | ||
| 2752 | |||
| 2753 | @safe unittest | ||
| 2754 | { | ||
| 2755 | import std.random : Random, uniform, randomShuffle; | ||
| 2756 | |||
| 2757 | // Element type with two fields | ||
| 2758 | static struct E | ||
| 2759 | { | ||
| 2760 | size_t value, index; | ||
| 2761 | } | ||
| 2762 | |||
| 2763 | // Generates data especially for testing sorting with Timsort | ||
| 2764 | static E[] genSampleData(uint seed) @safe | ||
| 2765 | { | ||
| 2766 | import std.algorithm.mutation : swap, swapRanges; | ||
| 2767 | |||
| 2768 | auto rnd = Random(seed); | ||
| 2769 | |||
| 2770 | E[] arr; | ||
| 2771 | arr.length = 64 * 64; | ||
| 2772 | |||
| 2773 | // We want duplicate values for testing stability | ||
| 2774 | foreach (i, ref v; arr) v.value = i / 64; | ||
| 2775 | |||
| 2776 | // Swap ranges at random middle point (test large merge operation) | ||
| 2777 | immutable mid = uniform(arr.length / 4, arr.length / 4 * 3, rnd); | ||
| 2778 | swapRanges(arr[0 .. mid], arr[mid .. $]); | ||
| 2779 | |||
| 2780 | // Shuffle last 1/8 of the array (test insertion sort and linear merge) | ||
| 2781 | randomShuffle(arr[$ / 8 * 7 .. $], rnd); | ||
| 2782 | |||
| 2783 | // Swap few random elements (test galloping mode) | ||
| 2784 | foreach (i; 0 .. arr.length / 64) | ||
| 2785 | { | ||
| 2786 | immutable a = uniform(0, arr.length, rnd), b = uniform(0, arr.length, rnd); | ||
| 2787 | swap(arr[a], arr[b]); | ||
| 2788 | } | ||
| 2789 | |||
| 2790 | // Now that our test array is prepped, store original index value | ||
| 2791 | // This will allow us to confirm the array was sorted stably | ||
| 2792 | foreach (i, ref v; arr) v.index = i; | ||
| 2793 | |||
| 2794 | return arr; | ||
| 2795 | } | ||
| 2796 | |||
| 2797 | // Tests the Timsort function for correctness and stability | ||
| 2798 | static bool testSort(uint seed) | ||
| 2799 | { | ||
| 2800 | auto arr = genSampleData(seed); | ||
| 2801 | |||
| 2802 | // Now sort the array! | ||
| 2803 | static bool comp(E a, E b) | ||
| 2804 | { | ||
| 2805 | return a.value < b.value; | ||
| 2806 | } | ||
| 2807 | |||
| 2808 | sort!(comp, SwapStrategy.stable)(arr); | ||
| 2809 | |||
| 2810 | // Test that the array was sorted correctly | ||
| 2811 | assert(isSorted!comp(arr)); | ||
| 2812 | |||
| 2813 | // Test that the array was sorted stably | ||
| 2814 | foreach (i; 0 .. arr.length - 1) | ||
| 2815 | { | ||
| 2816 | if (arr[i].value == arr[i + 1].value) assert(arr[i].index < arr[i + 1].index); | ||
| 2817 | } | ||
| 2818 | |||
| 2819 | return true; | ||
| 2820 | } | ||
| 2821 | |||
| 2822 | enum seed = 310614065; | ||
| 2823 | testSort(seed); | ||
| 2824 | |||
| 2825 | enum result = testSort(seed); | ||
| 2826 | assert(result == true); | ||
| 2827 | } | ||
| 2828 | |||
| 2829 | @safe unittest | ||
| 2830 | {//bugzilla 4584 | ||
| 2831 | assert(isSorted!"a < b"(sort!("a < b", SwapStrategy.stable)( | ||
| 2832 | [83, 42, 85, 86, 87, 22, 89, 30, 91, 46, 93, 94, 95, 6, | ||
| 2833 | 97, 14, 33, 10, 101, 102, 103, 26, 105, 106, 107, 6] | ||
| 2834 | ))); | ||
| 2835 | |||
| 2836 | } | ||
| 2837 | |||
| 2838 | @safe unittest | ||
| 2839 | { | ||
| 2840 | //test stable sort + zip | ||
| 2841 | import std.range; | ||
| 2842 | auto x = [10, 50, 60, 60, 20]; | ||
| 2843 | dchar[] y = "abcde"d.dup; | ||
| 2844 | |||
| 2845 | sort!("a[0] < b[0]", SwapStrategy.stable)(zip(x, y)); | ||
| 2846 | assert(x == [10, 20, 50, 60, 60]); | ||
| 2847 | assert(y == "aebcd"d); | ||
| 2848 | } | ||
| 2849 | |||
| 2850 | @safe unittest | ||
| 2851 | { | ||
| 2852 | // Issue 14223 | ||
| 2853 | import std.array, std.range; | ||
| 2854 | auto arr = chain(iota(0, 384), iota(0, 256), iota(0, 80), iota(0, 64), iota(0, 96)).array; | ||
| 2855 | sort!("a < b", SwapStrategy.stable)(arr); | ||
| 2856 | } | ||
| 2857 | |||
| 2858 | // schwartzSort | ||
| 2859 | /** | ||
| 2860 | Alternative sorting method that should be used when comparing keys involves an | ||
| 2861 | expensive computation. Instead of using `less(a, b)` for comparing elements, | ||
| 2862 | `schwartzSort` uses `less(transform(a), transform(b))`. The values of the | ||
| 2863 | `transform` function are precomputed in a temporary array, thus saving on | ||
| 2864 | repeatedly computing it. Conversely, if the cost of `transform` is small | ||
| 2865 | compared to the cost of allocating and filling the precomputed array, `sort` | ||
| 2866 | may be faster and therefore preferable. | ||
| 2867 | |||
| 2868 | This approach to sorting is akin to the $(HTTP | ||
| 2869 | wikipedia.org/wiki/Schwartzian_transform, Schwartzian transform), also known as | ||
| 2870 | the decorate-sort-undecorate pattern in Python and Lisp. The complexity is the | ||
| 2871 | same as that of the corresponding `sort`, but `schwartzSort` evaluates | ||
| 2872 | `transform` only `r.length` times (less than half when compared to regular | ||
| 2873 | sorting). The usage can be best illustrated with an example. | ||
| 2874 | |||
| 2875 | Example: | ||
| 2876 | ---- | ||
| 2877 | uint hashFun(string) { ... expensive computation ... } | ||
| 2878 | string[] array = ...; | ||
| 2879 | // Sort strings by hash, slow | ||
| 2880 | sort!((a, b) => hashFun(a) < hashFun(b))(array); | ||
| 2881 | // Sort strings by hash, fast (only computes arr.length hashes): | ||
| 2882 | schwartzSort!(hashFun, "a < b")(array); | ||
| 2883 | ---- | ||
| 2884 | |||
| 2885 | The $(D schwartzSort) function might require less temporary data and | ||
| 2886 | be faster than the Perl idiom or the decorate-sort-undecorate idiom | ||
| 2887 | present in Python and Lisp. This is because sorting is done in-place | ||
| 2888 | and only minimal extra data (one array of transformed elements) is | ||
| 2889 | created. | ||
| 2890 | |||
| 2891 | To check whether an array was sorted and benefit of the speedup of | ||
| 2892 | Schwartz sorting, a function $(D schwartzIsSorted) is not provided | ||
| 2893 | because the effect can be achieved by calling $(D | ||
| 2894 | isSorted!less(map!transform(r))). | ||
| 2895 | |||
| 2896 | Params: | ||
| 2897 | transform = The transformation to apply. | ||
| 2898 | less = The predicate to sort by. | ||
| 2899 | ss = The swapping strategy to use. | ||
| 2900 | r = The range to sort. | ||
| 2901 | |||
| 2902 | Returns: The initial range wrapped as a $(D SortedRange) with the | ||
| 2903 | predicate $(D (a, b) => binaryFun!less(transform(a), | ||
| 2904 | transform(b))). | ||
| 2905 | */ | ||
| 2906 | SortedRange!(R, ((a, b) => binaryFun!less(unaryFun!transform(a), | ||
| 2907 | unaryFun!transform(b)))) | ||
| 2908 | schwartzSort(alias transform, alias less = "a < b", | ||
| 2909 | SwapStrategy ss = SwapStrategy.unstable, R)(R r) | ||
| 2910 | if (isRandomAccessRange!R && hasLength!R) | ||
| 2911 | { | ||
| 2912 | import std.conv : emplace; | ||
| 2913 | import std.range : zip, SortedRange; | ||
| 2914 | import std.string : representation; | ||
| 2915 | |||
| 2916 | alias T = typeof(unaryFun!transform(r.front)); | ||
| 2917 | static trustedMalloc(size_t len) @trusted | ||
| 2918 | { | ||
| 2919 | import core.checkedint : mulu; | ||
| 2920 | import core.stdc.stdlib : malloc; | ||
| 2921 | bool overflow; | ||
| 2922 | const nbytes = mulu(len, T.sizeof, overflow); | ||
| 2923 | if (overflow) assert(0); | ||
| 2924 | return (cast(T*) malloc(nbytes))[0 .. len]; | ||
| 2925 | } | ||
| 2926 | auto xform1 = trustedMalloc(r.length); | ||
| 2927 | |||
| 2928 | size_t length; | ||
| 2929 | scope(exit) | ||
| 2930 | { | ||
| 2931 | static if (hasElaborateDestructor!T) | ||
| 2932 | { | ||
| 2933 | foreach (i; 0 .. length) collectException(destroy(xform1[i])); | ||
| 2934 | } | ||
| 2935 | static void trustedFree(T[] p) @trusted | ||
| 2936 | { | ||
| 2937 | import core.stdc.stdlib : free; | ||
| 2938 | free(p.ptr); | ||
| 2939 | } | ||
| 2940 | trustedFree(xform1); | ||
| 2941 | } | ||
| 2942 | for (; length != r.length; ++length) | ||
| 2943 | { | ||
| 2944 | emplace(&xform1[length], unaryFun!transform(r[length])); | ||
| 2945 | } | ||
| 2946 | // Make sure we use ubyte[] and ushort[], not char[] and wchar[] | ||
| 2947 | // for the intermediate array, lest zip gets confused. | ||
| 2948 | static if (isNarrowString!(typeof(xform1))) | ||
| 2949 | { | ||
| 2950 | auto xform = xform1.representation(); | ||
| 2951 | } | ||
| 2952 | else | ||
| 2953 | { | ||
| 2954 | alias xform = xform1; | ||
| 2955 | } | ||
| 2956 | zip(xform, r).sort!((a, b) => binaryFun!less(a[0], b[0]), ss)(); | ||
| 2957 | return typeof(return)(r); | ||
| 2958 | } | ||
| 2959 | |||
| 2960 | /// | ||
| 2961 | @safe unittest | ||
| 2962 | { | ||
| 2963 | import std.algorithm.iteration : map; | ||
| 2964 | import std.numeric : entropy; | ||
| 2965 | |||
| 2966 | auto lowEnt = [ 1.0, 0, 0 ], | ||
| 2967 | midEnt = [ 0.1, 0.1, 0.8 ], | ||
| 2968 | highEnt = [ 0.31, 0.29, 0.4 ]; | ||
| 2969 | auto arr = new double[][3]; | ||
| 2970 | arr[0] = midEnt; | ||
| 2971 | arr[1] = lowEnt; | ||
| 2972 | arr[2] = highEnt; | ||
| 2973 | |||
| 2974 | schwartzSort!(entropy, "a > b")(arr); | ||
| 2975 | |||
| 2976 | assert(arr[0] == highEnt); | ||
| 2977 | assert(arr[1] == midEnt); | ||
| 2978 | assert(arr[2] == lowEnt); | ||
| 2979 | assert(isSorted!("a > b")(map!(entropy)(arr))); | ||
| 2980 | } | ||
| 2981 | |||
| 2982 | @safe unittest | ||
| 2983 | { | ||
| 2984 | import std.algorithm.iteration : map; | ||
| 2985 | import std.numeric : entropy; | ||
| 2986 | |||
| 2987 | auto lowEnt = [ 1.0, 0, 0 ], | ||
| 2988 | midEnt = [ 0.1, 0.1, 0.8 ], | ||
| 2989 | highEnt = [ 0.31, 0.29, 0.4 ]; | ||
| 2990 | auto arr = new double[][3]; | ||
| 2991 | arr[0] = midEnt; | ||
| 2992 | arr[1] = lowEnt; | ||
| 2993 | arr[2] = highEnt; | ||
| 2994 | |||
| 2995 | schwartzSort!(entropy, "a < b")(arr); | ||
| 2996 | |||
| 2997 | assert(arr[0] == lowEnt); | ||
| 2998 | assert(arr[1] == midEnt); | ||
| 2999 | assert(arr[2] == highEnt); | ||
| 3000 | assert(isSorted!("a < b")(map!(entropy)(arr))); | ||
| 3001 | } | ||
| 3002 | |||
| 3003 | @safe unittest | ||
| 3004 | { | ||
| 3005 | // issue 4909 | ||
| 3006 | import std.typecons : Tuple; | ||
| 3007 | Tuple!(char)[] chars; | ||
| 3008 | schwartzSort!"a[0]"(chars); | ||
| 3009 | } | ||
| 3010 | |||
| 3011 | @safe unittest | ||
| 3012 | { | ||
| 3013 | // issue 5924 | ||
| 3014 | import std.typecons : Tuple; | ||
| 3015 | Tuple!(char)[] chars; | ||
| 3016 | schwartzSort!((Tuple!(char) c){ return c[0]; })(chars); | ||
| 3017 | } | ||
| 3018 | |||
| 3019 | // partialSort | ||
| 3020 | /** | ||
| 3021 | Reorders the random-access range $(D r) such that the range $(D r[0 | ||
| 3022 | .. mid]) is the same as if the entire $(D r) were sorted, and leaves | ||
| 3023 | the range $(D r[mid .. r.length]) in no particular order. Performs | ||
| 3024 | $(BIGOH r.length * log(mid)) evaluations of $(D pred). The | ||
| 3025 | implementation simply calls $(D topN!(less, ss)(r, n)) and then $(D | ||
| 3026 | sort!(less, ss)(r[0 .. n])). | ||
| 3027 | |||
| 3028 | Params: | ||
| 3029 | less = The predicate to sort by. | ||
| 3030 | ss = The swapping strategy to use. | ||
| 3031 | r = The random-access range to reorder. | ||
| 3032 | n = The length of the initial segment of `r` to sort. | ||
| 3033 | */ | ||
| 3034 | void partialSort(alias less = "a < b", SwapStrategy ss = SwapStrategy.unstable, | ||
| 3035 | Range)(Range r, size_t n) | ||
| 3036 | if (isRandomAccessRange!(Range) && hasLength!(Range) && hasSlicing!(Range)) | ||
| 3037 | { | ||
| 3038 | partialSort!(less, ss)(r[0 .. n], r[n .. $]); | ||
| 3039 | } | ||
| 3040 | |||
| 3041 | /// | ||
| 3042 | @system unittest | ||
| 3043 | { | ||
| 3044 | int[] a = [ 9, 8, 7, 6, 5, 4, 3, 2, 1, 0 ]; | ||
| 3045 | partialSort(a, 5); | ||
| 3046 | assert(a[0 .. 5] == [ 0, 1, 2, 3, 4 ]); | ||
| 3047 | } | ||
| 3048 | |||
| 3049 | /** | ||
| 3050 | Stores the smallest elements of the two ranges in the left-hand range in sorted order. | ||
| 3051 | |||
| 3052 | Params: | ||
| 3053 | less = The predicate to sort by. | ||
| 3054 | ss = The swapping strategy to use. | ||
| 3055 | r1 = The first range. | ||
| 3056 | r2 = The second range. | ||
| 3057 | */ | ||
| 3058 | |||
| 3059 | void partialSort(alias less = "a < b", SwapStrategy ss = SwapStrategy.unstable, | ||
| 3060 | Range1, Range2)(Range1 r1, Range2 r2) | ||
| 3061 | if (isRandomAccessRange!(Range1) && hasLength!Range1 && | ||
| 3062 | isInputRange!Range2 && is(ElementType!Range1 == ElementType!Range2) && | ||
| 3063 | hasLvalueElements!Range1 && hasLvalueElements!Range2) | ||
| 3064 | { | ||
| 3065 | topN!(less, ss)(r1, r2); | ||
| 3066 | sort!(less, ss)(r1); | ||
| 3067 | } | ||
| 3068 | /// | ||
| 3069 | @system unittest | ||
| 3070 | { | ||
| 3071 | int[] a = [5, 7, 2, 6, 7]; | ||
| 3072 | int[] b = [2, 1, 5, 6, 7, 3, 0]; | ||
| 3073 | |||
| 3074 | partialSort(a, b); | ||
| 3075 | assert(a == [0, 1, 2, 2, 3]); | ||
| 3076 | } | ||
| 3077 | |||
| 3078 | // topN | ||
| 3079 | /** | ||
| 3080 | Reorders the range $(D r) using $(D swap) such that $(D r[nth]) refers | ||
| 3081 | to the element that would fall there if the range were fully | ||
| 3082 | sorted. In addition, it also partitions $(D r) such that all elements | ||
| 3083 | $(D e1) from $(D r[0]) to $(D r[nth]) satisfy $(D !less(r[nth], e1)), | ||
| 3084 | and all elements $(D e2) from $(D r[nth]) to $(D r[r.length]) satisfy | ||
| 3085 | $(D !less(e2, r[nth])). Effectively, it finds the nth smallest | ||
| 3086 | (according to $(D less)) elements in $(D r). Performs an expected | ||
| 3087 | $(BIGOH r.length) (if unstable) or $(BIGOH r.length * log(r.length)) | ||
| 3088 | (if stable) evaluations of $(D less) and $(D swap). | ||
| 3089 | |||
| 3090 | If $(D n >= r.length), the algorithm has no effect and returns | ||
| 3091 | `r[0 .. r.length]`. | ||
| 3092 | |||
| 3093 | Params: | ||
| 3094 | less = The predicate to sort by. | ||
| 3095 | ss = The swapping strategy to use. | ||
| 3096 | r = The random-access range to reorder. | ||
| 3097 | nth = The index of the element that should be in sorted position after the | ||
| 3098 | function is done. | ||
| 3099 | |||
| 3100 | See_Also: | ||
| 3101 | $(LREF topNIndex), | ||
| 3102 | $(HTTP sgi.com/tech/stl/nth_element.html, STL's nth_element) | ||
| 3103 | |||
| 3104 | BUGS: | ||
| 3105 | |||
| 3106 | Stable topN has not been implemented yet. | ||
| 3107 | */ | ||
| 3108 | auto topN(alias less = "a < b", | ||
| 3109 | SwapStrategy ss = SwapStrategy.unstable, | ||
| 3110 | Range)(Range r, size_t nth) | ||
| 3111 | if (isRandomAccessRange!(Range) && hasLength!Range && hasSlicing!Range) | ||
| 3112 | { | ||
| 3113 | static assert(ss == SwapStrategy.unstable, | ||
| 3114 | "Stable topN not yet implemented"); | ||
| 3115 | if (nth >= r.length) return r[0 .. r.length]; | ||
| 3116 | auto ret = r[0 .. nth]; | ||
| 3117 | if (false) | ||
| 3118 | { | ||
| 3119 | // Workaround for https://issues.dlang.org/show_bug.cgi?id=16528 | ||
| 3120 | // Safety checks: enumerate all potentially unsafe generic primitives | ||
| 3121 | // then use a @trusted implementation. | ||
| 3122 | binaryFun!less(r[0], r[r.length - 1]); | ||
| 3123 | import std.algorithm.mutation : swapAt; | ||
| 3124 | r.swapAt(size_t(0), size_t(0)); | ||
| 3125 | static assert(is(typeof(r.length) == size_t)); | ||
| 3126 | pivotPartition!less(r, 0); | ||
| 3127 | } | ||
| 3128 | bool useSampling = true; | ||
| 3129 | topNImpl!(binaryFun!less)(r, nth, useSampling); | ||
| 3130 | return ret; | ||
| 3131 | } | ||
| 3132 | |||
| 3133 | private @trusted | ||
| 3134 | void topNImpl(alias less, R)(R r, size_t n, ref bool useSampling) | ||
| 3135 | { | ||
| 3136 | for (;;) | ||
| 3137 | { | ||
| 3138 | import std.algorithm.mutation : swapAt; | ||
| 3139 | assert(n < r.length); | ||
| 3140 | size_t pivot = void; | ||
| 3141 | |||
| 3142 | // Decide strategy for partitioning | ||
| 3143 | if (n == 0) | ||
| 3144 | { | ||
| 3145 | pivot = 0; | ||
| 3146 | foreach (i; 1 .. r.length) | ||
| 3147 | if (less(r[i], r[pivot])) pivot = i; | ||
| 3148 | r.swapAt(n, pivot); | ||
| 3149 | return; | ||
| 3150 | } | ||
| 3151 | if (n + 1 == r.length) | ||
| 3152 | { | ||
| 3153 | pivot = 0; | ||
| 3154 | foreach (i; 1 .. r.length) | ||
| 3155 | if (less(r[pivot], r[i])) pivot = i; | ||
| 3156 | r.swapAt(n, pivot); | ||
| 3157 | return; | ||
| 3158 | } | ||
| 3159 | if (r.length <= 12) | ||
| 3160 | { | ||
| 3161 | pivot = pivotPartition!less(r, r.length / 2); | ||
| 3162 | } | ||
| 3163 | else if (n * 16 <= (r.length - 1) * 7) | ||
| 3164 | { | ||
| 3165 | pivot = topNPartitionOffMedian!(less, No.leanRight) | ||
| 3166 | (r, n, useSampling); | ||
| 3167 | // Quality check | ||
| 3168 | if (useSampling) | ||
| 3169 | { | ||
| 3170 | if (pivot < n) | ||
| 3171 | { | ||
| 3172 | if (pivot * 4 < r.length) | ||
| 3173 | { | ||
| 3174 | useSampling = false; | ||
| 3175 | } | ||
| 3176 | } | ||
| 3177 | else if ((r.length - pivot) * 8 < r.length * 3) | ||
| 3178 | { | ||
| 3179 | useSampling = false; | ||
| 3180 | } | ||
| 3181 | } | ||
| 3182 | } | ||
| 3183 | else if (n * 16 >= (r.length - 1) * 9) | ||
| 3184 | { | ||
| 3185 | pivot = topNPartitionOffMedian!(less, Yes.leanRight) | ||
| 3186 | (r, n, useSampling); | ||
| 3187 | // Quality check | ||
| 3188 | if (useSampling) | ||
| 3189 | { | ||
| 3190 | if (pivot < n) | ||
| 3191 | { | ||
| 3192 | if (pivot * 8 < r.length * 3) | ||
| 3193 | { | ||
| 3194 | useSampling = false; | ||
| 3195 | } | ||
| 3196 | } | ||
| 3197 | else if ((r.length - pivot) * 4 < r.length) | ||
| 3198 | { | ||
| 3199 | useSampling = false; | ||
| 3200 | } | ||
| 3201 | } | ||
| 3202 | } | ||
| 3203 | else | ||
| 3204 | { | ||
| 3205 | pivot = topNPartition!less(r, n, useSampling); | ||
| 3206 | // Quality check | ||
| 3207 | if (useSampling && | ||
| 3208 | (pivot * 9 < r.length * 2 || pivot * 9 > r.length * 7)) | ||
| 3209 | { | ||
| 3210 | // Failed - abort sampling going forward | ||
| 3211 | useSampling = false; | ||
| 3212 | } | ||
| 3213 | } | ||
| 3214 | |||
| 3215 | assert(pivot != size_t.max); | ||
| 3216 | // See how the pivot fares | ||
| 3217 | if (pivot == n) | ||
| 3218 | { | ||
| 3219 | return; | ||
| 3220 | } | ||
| 3221 | if (pivot > n) | ||
| 3222 | { | ||
| 3223 | r = r[0 .. pivot]; | ||
| 3224 | } | ||
| 3225 | else | ||
| 3226 | { | ||
| 3227 | n -= pivot + 1; | ||
| 3228 | r = r[pivot + 1 .. r.length]; | ||
| 3229 | } | ||
| 3230 | } | ||
| 3231 | } | ||
| 3232 | |||
| 3233 | /// | ||
| 3234 | @safe unittest | ||
| 3235 | { | ||
| 3236 | int[] v = [ 25, 7, 9, 2, 0, 5, 21 ]; | ||
| 3237 | topN!"a < b"(v, 100); | ||
| 3238 | assert(v == [ 25, 7, 9, 2, 0, 5, 21 ]); | ||
| 3239 | auto n = 4; | ||
| 3240 | topN!"a < b"(v, n); | ||
| 3241 | assert(v[n] == 9); | ||
| 3242 | } | ||
| 3243 | |||
| 3244 | private size_t topNPartition(alias lp, R)(R r, size_t n, bool useSampling) | ||
| 3245 | { | ||
| 3246 | assert(r.length >= 9 && n < r.length); | ||
| 3247 | immutable ninth = r.length / 9; | ||
| 3248 | auto pivot = ninth / 2; | ||
| 3249 | // Position subrange r[lo .. hi] to have length equal to ninth and its upper | ||
| 3250 | // median r[lo .. hi][$ / 2] in exactly the same place as the upper median | ||
| 3251 | // of the entire range r[$ / 2]. This is to improve behavior for searching | ||
| 3252 | // the median in already sorted ranges. | ||
| 3253 | immutable lo = r.length / 2 - pivot, hi = lo + ninth; | ||
| 3254 | // We have either one straggler on the left, one on the right, or none. | ||
| 3255 | assert(lo - (r.length - hi) <= 1 || (r.length - hi) - lo <= 1); | ||
| 3256 | assert(lo >= ninth * 4); | ||
| 3257 | assert(r.length - hi >= ninth * 4); | ||
| 3258 | |||
| 3259 | // Partition in groups of 3, and the mid tertile again in groups of 3 | ||
| 3260 | if (!useSampling) | ||
| 3261 | p3!lp(r, lo - ninth, hi + ninth); | ||
| 3262 | p3!lp(r, lo, hi); | ||
| 3263 | |||
| 3264 | // Get the median of medians of medians | ||
| 3265 | // Map the full interval of n to the full interval of the ninth | ||
| 3266 | pivot = (n * (ninth - 1)) / (r.length - 1); | ||
| 3267 | topNImpl!lp(r[lo .. hi], pivot, useSampling); | ||
| 3268 | return expandPartition!lp(r, lo, pivot + lo, hi); | ||
| 3269 | } | ||
| 3270 | |||
| 3271 | private void p3(alias less, Range)(Range r, size_t lo, immutable size_t hi) | ||
| 3272 | { | ||
| 3273 | assert(lo <= hi && hi < r.length); | ||
| 3274 | immutable ln = hi - lo; | ||
| 3275 | for (; lo < hi; ++lo) | ||
| 3276 | { | ||
| 3277 | assert(lo >= ln); | ||
| 3278 | assert(lo + ln < r.length); | ||
| 3279 | medianOf!less(r, lo - ln, lo, lo + ln); | ||
| 3280 | } | ||
| 3281 | } | ||
| 3282 | |||
| 3283 | private void p4(alias less, Flag!"leanRight" f, Range) | ||
| 3284 | (Range r, size_t lo, immutable size_t hi) | ||
| 3285 | { | ||
| 3286 | assert(lo <= hi && hi < r.length); | ||
| 3287 | immutable ln = hi - lo, _2ln = ln * 2; | ||
| 3288 | for (; lo < hi; ++lo) | ||
| 3289 | { | ||
| 3290 | assert(lo >= ln); | ||
| 3291 | assert(lo + ln < r.length); | ||
| 3292 | static if (f == Yes.leanRight) | ||
| 3293 | medianOf!(less, f)(r, lo - _2ln, lo - ln, lo, lo + ln); | ||
| 3294 | else | ||
| 3295 | medianOf!(less, f)(r, lo - ln, lo, lo + ln, lo + _2ln); | ||
| 3296 | } | ||
| 3297 | } | ||
| 3298 | |||
| 3299 | private size_t topNPartitionOffMedian(alias lp, Flag!"leanRight" f, R) | ||
| 3300 | (R r, size_t n, bool useSampling) | ||
| 3301 | { | ||
| 3302 | assert(r.length >= 12); | ||
| 3303 | assert(n < r.length); | ||
| 3304 | immutable _4 = r.length / 4; | ||
| 3305 | static if (f == Yes.leanRight) | ||
| 3306 | immutable leftLimit = 2 * _4; | ||
| 3307 | else | ||
| 3308 | immutable leftLimit = _4; | ||
| 3309 | // Partition in groups of 4, and the left quartile again in groups of 3 | ||
| 3310 | if (!useSampling) | ||
| 3311 | { | ||
| 3312 | p4!(lp, f)(r, leftLimit, leftLimit + _4); | ||
| 3313 | } | ||
| 3314 | immutable _12 = _4 / 3; | ||
| 3315 | immutable lo = leftLimit + _12, hi = lo + _12; | ||
| 3316 | p3!lp(r, lo, hi); | ||
| 3317 | |||
| 3318 | // Get the median of medians of medians | ||
| 3319 | // Map the full interval of n to the full interval of the ninth | ||
| 3320 | immutable pivot = (n * (_12 - 1)) / (r.length - 1); | ||
| 3321 | topNImpl!lp(r[lo .. hi], pivot, useSampling); | ||
| 3322 | return expandPartition!lp(r, lo, pivot + lo, hi); | ||
| 3323 | } | ||
| 3324 | |||
| 3325 | /* | ||
| 3326 | Params: | ||
| 3327 | less = predicate | ||
| 3328 | r = range to partition | ||
| 3329 | pivot = pivot to partition around | ||
| 3330 | lo = value such that r[lo .. pivot] already less than r[pivot] | ||
| 3331 | hi = value such that r[pivot .. hi] already greater than r[pivot] | ||
| 3332 | |||
| 3333 | Returns: new position of pivot | ||
| 3334 | */ | ||
| 3335 | private | ||
| 3336 | size_t expandPartition(alias lp, R)(R r, size_t lo, size_t pivot, size_t hi) | ||
| 3337 | in | ||
| 3338 | { | ||
| 3339 | import std.algorithm.searching : all; | ||
| 3340 | assert(lo <= pivot); | ||
| 3341 | assert(pivot < hi); | ||
| 3342 | assert(hi <= r.length); | ||
| 3343 | assert(r[lo .. pivot + 1].all!(x => !lp(r[pivot], x))); | ||
| 3344 | assert(r[pivot + 1 .. hi].all!(x => !lp(x, r[pivot]))); | ||
| 3345 | } | ||
| 3346 | out | ||
| 3347 | { | ||
| 3348 | import std.algorithm.searching : all; | ||
| 3349 | assert(r[0 .. pivot + 1].all!(x => !lp(r[pivot], x))); | ||
| 3350 | assert(r[pivot + 1 .. r.length].all!(x => !lp(x, r[pivot]))); | ||
| 3351 | } | ||
| 3352 | body | ||
| 3353 | { | ||
| 3354 | import std.algorithm.mutation : swapAt; | ||
| 3355 | import std.algorithm.searching : all; | ||
| 3356 | // We work with closed intervals! | ||
| 3357 | --hi; | ||
| 3358 | |||
| 3359 | size_t left = 0, rite = r.length - 1; | ||
| 3360 | loop: for (;; ++left, --rite) | ||
| 3361 | { | ||
| 3362 | for (;; ++left) | ||
| 3363 | { | ||
| 3364 | if (left == lo) break loop; | ||
| 3365 | if (!lp(r[left], r[pivot])) break; | ||
| 3366 | } | ||
| 3367 | for (;; --rite) | ||
| 3368 | { | ||
| 3369 | if (rite == hi) break loop; | ||
| 3370 | if (!lp(r[pivot], r[rite])) break; | ||
| 3371 | } | ||
| 3372 | r.swapAt(left, rite); | ||
| 3373 | } | ||
| 3374 | |||
| 3375 | assert(r[lo .. pivot + 1].all!(x => !lp(r[pivot], x))); | ||
| 3376 | assert(r[pivot + 1 .. hi + 1].all!(x => !lp(x, r[pivot]))); | ||
| 3377 | assert(r[0 .. left].all!(x => !lp(r[pivot], x))); | ||
| 3378 | assert(r[rite + 1 .. r.length].all!(x => !lp(x, r[pivot]))); | ||
| 3379 | |||
| 3380 | immutable oldPivot = pivot; | ||
| 3381 | |||
| 3382 | if (left < lo) | ||
| 3383 | { | ||
| 3384 | // First loop: spend r[lo .. pivot] | ||
| 3385 | for (; lo < pivot; ++left) | ||
| 3386 | { | ||
| 3387 | if (left == lo) goto done; | ||
| 3388 | if (!lp(r[oldPivot], r[left])) continue; | ||
| 3389 | --pivot; | ||
| 3390 | assert(!lp(r[oldPivot], r[pivot])); | ||
| 3391 | r.swapAt(left, pivot); | ||
| 3392 | } | ||
| 3393 | // Second loop: make left and pivot meet | ||
| 3394 | for (;; ++left) | ||
| 3395 | { | ||
| 3396 | if (left == pivot) goto done; | ||
| 3397 | if (!lp(r[oldPivot], r[left])) continue; | ||
| 3398 | for (;;) | ||
| 3399 | { | ||
| 3400 | if (left == pivot) goto done; | ||
| 3401 | --pivot; | ||
| 3402 | if (lp(r[pivot], r[oldPivot])) | ||
| 3403 | { | ||
| 3404 | r.swapAt(left, pivot); | ||
| 3405 | break; | ||
| 3406 | } | ||
| 3407 | } | ||
| 3408 | } | ||
| 3409 | } | ||
| 3410 | |||
| 3411 | // First loop: spend r[lo .. pivot] | ||
| 3412 | for (; hi != pivot; --rite) | ||
| 3413 | { | ||
| 3414 | if (rite == hi) goto done; | ||
| 3415 | if (!lp(r[rite], r[oldPivot])) continue; | ||
| 3416 | ++pivot; | ||
| 3417 | assert(!lp(r[pivot], r[oldPivot])); | ||
| 3418 | r.swapAt(rite, pivot); | ||
| 3419 | } | ||
| 3420 | // Second loop: make left and pivot meet | ||
| 3421 | for (; rite > pivot; --rite) | ||
| 3422 | { | ||
| 3423 | if (!lp(r[rite], r[oldPivot])) continue; | ||
| 3424 | while (rite > pivot) | ||
| 3425 | { | ||
| 3426 | ++pivot; | ||
| 3427 | if (lp(r[oldPivot], r[pivot])) | ||
| 3428 | { | ||
| 3429 | r.swapAt(rite, pivot); | ||
| 3430 | break; | ||
| 3431 | } | ||
| 3432 | } | ||
| 3433 | } | ||
| 3434 | |||
| 3435 | done: | ||
| 3436 | r.swapAt(oldPivot, pivot); | ||
| 3437 | return pivot; | ||
| 3438 | } | ||
| 3439 | |||
| 3440 | @safe unittest | ||
| 3441 | { | ||
| 3442 | auto a = [ 10, 5, 3, 4, 8, 11, 13, 3, 9, 4, 10 ]; | ||
| 3443 | assert(expandPartition!((a, b) => a < b)(a, 4, 5, 6) == 9); | ||
| 3444 | a = randomArray; | ||
| 3445 | if (a.length == 0) return; | ||
| 3446 | expandPartition!((a, b) => a < b)(a, a.length / 2, a.length / 2, | ||
| 3447 | a.length / 2 + 1); | ||
| 3448 | } | ||
| 3449 | |||
| 3450 | version (unittest) | ||
| 3451 | private T[] randomArray(Flag!"exactSize" flag = No.exactSize, T = int)( | ||
| 3452 | size_t maxSize = 1000, | ||
| 3453 | T minValue = 0, T maxValue = 255) | ||
| 3454 | { | ||
| 3455 | import std.algorithm.iteration : map; | ||
| 3456 | import std.random : unpredictableSeed, Random, uniform; | ||
| 3457 | auto size = flag == Yes.exactSize ? maxSize : uniform(1, maxSize); | ||
| 3458 | return iota(0, size).map!(_ => uniform(minValue, maxValue)).array; | ||
| 3459 | } | ||
| 3460 | |||
| 3461 | @safe unittest | ||
| 3462 | { | ||
| 3463 | import std.algorithm.comparison : max, min; | ||
| 3464 | import std.algorithm.iteration : reduce; | ||
| 3465 | |||
| 3466 | int[] v = [ 7, 6, 5, 4, 3, 2, 1, 0 ]; | ||
| 3467 | ptrdiff_t n = 3; | ||
| 3468 | topN!("a < b")(v, n); | ||
| 3469 | assert(reduce!max(v[0 .. n]) <= v[n]); | ||
| 3470 | assert(reduce!min(v[n + 1 .. $]) >= v[n]); | ||
| 3471 | // | ||
| 3472 | v = [3, 4, 5, 6, 7, 2, 3, 4, 5, 6, 1, 2, 3, 4, 5]; | ||
| 3473 | n = 3; | ||
| 3474 | topN(v, n); | ||
| 3475 | assert(reduce!max(v[0 .. n]) <= v[n]); | ||
| 3476 | assert(reduce!min(v[n + 1 .. $]) >= v[n]); | ||
| 3477 | // | ||
| 3478 | v = [3, 4, 5, 6, 7, 2, 3, 4, 5, 6, 1, 2, 3, 4, 5]; | ||
| 3479 | n = 1; | ||
| 3480 | topN(v, n); | ||
| 3481 | assert(reduce!max(v[0 .. n]) <= v[n]); | ||
| 3482 | assert(reduce!min(v[n + 1 .. $]) >= v[n]); | ||
| 3483 | // | ||
| 3484 | v = [3, 4, 5, 6, 7, 2, 3, 4, 5, 6, 1, 2, 3, 4, 5]; | ||
| 3485 | n = v.length - 1; | ||
| 3486 | topN(v, n); | ||
| 3487 | assert(v[n] == 7); | ||
| 3488 | // | ||
| 3489 | v = [3, 4, 5, 6, 7, 2, 3, 4, 5, 6, 1, 2, 3, 4, 5]; | ||
| 3490 | n = 0; | ||
| 3491 | topN(v, n); | ||
| 3492 | assert(v[n] == 1); | ||
| 3493 | |||
| 3494 | double[][] v1 = [[-10, -5], [-10, -3], [-10, -5], [-10, -4], | ||
| 3495 | [-10, -5], [-9, -5], [-9, -3], [-9, -5],]; | ||
| 3496 | |||
| 3497 | // double[][] v1 = [ [-10, -5], [-10, -4], [-9, -5], [-9, -5], | ||
| 3498 | // [-10, -5], [-10, -3], [-10, -5], [-9, -3],]; | ||
| 3499 | double[]*[] idx = [ &v1[0], &v1[1], &v1[2], &v1[3], &v1[4], &v1[5], &v1[6], | ||
| 3500 | &v1[7], ]; | ||
| 3501 | |||
| 3502 | auto mid = v1.length / 2; | ||
| 3503 | topN!((a, b){ return (*a)[1] < (*b)[1]; })(idx, mid); | ||
| 3504 | foreach (e; idx[0 .. mid]) assert((*e)[1] <= (*idx[mid])[1]); | ||
| 3505 | foreach (e; idx[mid .. $]) assert((*e)[1] >= (*idx[mid])[1]); | ||
| 3506 | } | ||
| 3507 | |||
| 3508 | @safe unittest | ||
| 3509 | { | ||
| 3510 | import std.algorithm.comparison : max, min; | ||
| 3511 | import std.algorithm.iteration : reduce; | ||
| 3512 | import std.random : Random, uniform, unpredictableSeed; | ||
| 3513 | |||
| 3514 | immutable uint[] seeds = [90027751, 2709791795, 1374631933, 995751648, 3541495258, 984840953, unpredictableSeed]; | ||
| 3515 | foreach (s; seeds) | ||
| 3516 | { | ||
| 3517 | auto r = Random(s); | ||
| 3518 | |||
| 3519 | int[] a = new int[uniform(1, 10000, r)]; | ||
| 3520 | foreach (ref e; a) e = uniform(-1000, 1000, r); | ||
| 3521 | |||
| 3522 | auto k = uniform(0, a.length, r); | ||
| 3523 | topN(a, k); | ||
| 3524 | if (k > 0) | ||
| 3525 | { | ||
| 3526 | auto left = reduce!max(a[0 .. k]); | ||
| 3527 | assert(left <= a[k]); | ||
| 3528 | } | ||
| 3529 | if (k + 1 < a.length) | ||
939 more lines aren’t shown here.