1 /******************************************************************************* 2 3 Based upon Doug Lea's Java collection package 4 5 Copyright: 6 Copyright (c) 2008 Kris Bell. 7 Some parts copyright (c) 2009-2016 dunnhumby Germany GmbH. 8 All rights reserved. 9 10 License: 11 Tango Dual License: 3-Clause BSD License / Academic Free License v3.0. 12 See LICENSE_TANGO.txt for details. 13 14 Version: Apr 2008: Initial release 15 16 Authors: Kris 17 18 *******************************************************************************/ 19 20 module ocean.util.container.CircularList; 21 22 import ocean.util.container.Clink; 23 24 public import ocean.util.container.Container; 25 26 import ocean.util.container.model.IContainer; 27 28 version (unittest) import ocean.core.Test; 29 30 /******************************************************************************* 31 32 Circular linked list 33 34 --- 35 Iterator iterator () 36 int opApply (int delegate(ref V value) dg) 37 38 CircularList add (V element) 39 CircularList addAt (size_t index, V element) 40 CircularList append (V element) 41 CircularList prepend (V element) 42 size_t addAt (size_t index, IContainer!(V) e) 43 size_t append (IContainer!(V) e) 44 size_t prepend (IContainer!(V) e) 45 46 bool take (ref V v) 47 bool contains (V element) 48 V get (size_t index) 49 size_t first (V element, size_t startingIndex = 0) 50 size_t last (V element, size_t startingIndex = 0) 51 52 V head () 53 V tail () 54 V head (V element) 55 V tail (V element) 56 V removeHead () 57 V removeTail () 58 59 bool removeAt (size_t index) 60 size_t remove (V element, bool all) 61 size_t removeRange (size_t fromIndex, size_t toIndex) 62 63 size_t replace (V oldElement, V newElement, bool all) 64 bool replaceAt (size_t index, V element) 65 66 size_t size () 67 bool isEmpty () 68 V[] toArray (V[] dst) 69 CircularList dup () 70 CircularList subset (size_t from, size_t length) 71 CircularList clear () 72 CircularList reset () 73 CircularList check () 74 --- 75 76 *******************************************************************************/ 77 78 class CircularList (V, alias Reap = Container.reap, 79 alias Heap = Container.DefaultCollect) 80 : IContainer!(V) 81 { 82 import ocean.core.Verify; 83 84 // use this type for Allocator configuration 85 public alias Clink!(V) Type; 86 87 private alias Type *Ref; 88 89 private alias Heap!(Type) Alloc; 90 91 // number of elements contained 92 private size_t count; 93 94 // configured heap manager 95 private Alloc heap; 96 97 // mutation tag updates on each change 98 private size_t mutation; 99 100 // head of the list. Null if empty 101 private Ref list; 102 103 104 /*********************************************************************** 105 106 Make an empty list 107 108 ***********************************************************************/ 109 110 this () 111 { 112 this (null, 0); 113 } 114 115 /*********************************************************************** 116 117 Make an configured list 118 119 ***********************************************************************/ 120 121 protected this (Ref h, size_t c) 122 { 123 list = h; 124 count = c; 125 } 126 127 /*********************************************************************** 128 129 Clean up when deleted 130 131 ***********************************************************************/ 132 133 ~this () 134 { 135 reset; 136 } 137 138 /*********************************************************************** 139 140 Return a generic iterator for contained elements 141 142 ***********************************************************************/ 143 144 final Iterator iterator () 145 { 146 // used to be Iterator i = void, but that doesn't initialize 147 // fields that are not initialized here. 148 Iterator i; 149 i.owner = this; 150 i.mutation = mutation; 151 i.cell = i.head = list; 152 i.count = count; 153 i.index = 0; 154 return i; 155 } 156 157 /*********************************************************************** 158 159 Configure the assigned allocator with the size of each 160 allocation block (number of nodes allocated at one time) 161 and the number of nodes to pre-populate the cache with. 162 163 Time complexity: O(n) 164 165 ***********************************************************************/ 166 167 final CircularList cache (size_t chunk, size_t count=0) 168 { 169 heap.config (chunk, count); 170 return this; 171 } 172 173 /*********************************************************************** 174 175 176 ***********************************************************************/ 177 178 final int opApply (scope int delegate(ref V value) dg) 179 { 180 return iterator.opApply (dg); 181 } 182 183 /*********************************************************************** 184 185 Return the number of elements contained 186 187 ***********************************************************************/ 188 189 final size_t size () 190 { 191 return count; 192 } 193 194 /*********************************************************************** 195 196 Make an independent copy of the list. Elements themselves 197 are not cloned 198 199 ***********************************************************************/ 200 201 final CircularList dup () 202 { 203 return new CircularList!(V, Reap, Heap) (list ? list.copyList(&heap.allocate) : null, count); 204 } 205 206 /*********************************************************************** 207 208 Time complexity: O(n) 209 210 ***********************************************************************/ 211 212 final bool contains (V element) 213 { 214 if (list) 215 return list.find (element) !is null; 216 return false; 217 } 218 219 /*********************************************************************** 220 221 Time complexity: O(1) 222 223 ***********************************************************************/ 224 225 final V head () 226 { 227 return firstCell.value; 228 } 229 230 /*********************************************************************** 231 232 Time complexity: O(1) 233 234 ***********************************************************************/ 235 236 final V tail () 237 { 238 return lastCell.value; 239 } 240 241 /*********************************************************************** 242 243 Time complexity: O(n) 244 245 ***********************************************************************/ 246 247 final V get (size_t index) 248 { 249 return cellAt(index).value; 250 } 251 252 /*********************************************************************** 253 254 Time complexity: O(n) 255 Returns size_t.max if no element found. 256 257 ***********************************************************************/ 258 259 final size_t first (V element, size_t startingIndex = 0) 260 { 261 if (startingIndex < 0) 262 startingIndex = 0; 263 264 auto p = list; 265 if (p is null) 266 return size_t.max; 267 268 for (size_t i = 0; true; ++i) 269 { 270 if (i >= startingIndex && element == p.value) 271 return i; 272 273 p = p.next; 274 if (p is list) 275 break; 276 } 277 return size_t.max; 278 } 279 280 /*********************************************************************** 281 282 Time complexity: O(n) 283 Returns size_t.max if no element found. 284 285 ***********************************************************************/ 286 287 final size_t last (V element, size_t startingIndex = 0) 288 { 289 if (count is 0) 290 return size_t.max; 291 292 if (startingIndex >= count) 293 startingIndex = count - 1; 294 295 if (startingIndex < 0) 296 startingIndex = 0; 297 298 auto p = cellAt (startingIndex); 299 size_t i = startingIndex; 300 for (;;) 301 { 302 if (element == p.value) 303 return i; 304 else 305 if (p is list) 306 break; 307 else 308 { 309 p = p.prev; 310 --i; 311 } 312 } 313 return size_t.max; 314 } 315 316 /*********************************************************************** 317 318 Time complexity: O(length) 319 320 ***********************************************************************/ 321 322 final CircularList subset (size_t from, size_t length) 323 { 324 Ref newlist = null; 325 326 if (length > 0) 327 { 328 checkIndex (from); 329 auto p = cellAt (from); 330 auto current = newlist = heap.allocate.set (p.value); 331 332 for (size_t i = 1; i < length; ++i) 333 { 334 p = p.next; 335 if (p is null) 336 length = i; 337 else 338 { 339 current.addNext (p.value, &heap.allocate); 340 current = current.next; 341 } 342 } 343 } 344 345 return new CircularList (newlist, length); 346 } 347 348 /*********************************************************************** 349 350 Time complexity: O(1) 351 352 ***********************************************************************/ 353 354 final CircularList clear () 355 { 356 return clear (false); 357 } 358 359 /*********************************************************************** 360 361 Reset the HashMap contents and optionally configure a new 362 heap manager. This releases more memory than clear() does 363 364 Time complexity: O(n) 365 366 ***********************************************************************/ 367 368 final CircularList reset () 369 { 370 return clear (true); 371 } 372 373 /*********************************************************************** 374 375 Time complexity: O(n) 376 377 Takes the last element on the list 378 379 ***********************************************************************/ 380 381 final bool take (ref V v) 382 { 383 if (count) 384 { 385 v = tail; 386 removeTail (); 387 return true; 388 } 389 return false; 390 } 391 392 /*********************************************************************** 393 394 Time complexity: O(1) 395 396 ***********************************************************************/ 397 398 final CircularList prepend (V element) 399 { 400 if (list is null) 401 list = heap.allocate.set (element); 402 else 403 list = list.addPrev (element, &heap.allocate); 404 increment; 405 return this; 406 } 407 408 /*********************************************************************** 409 410 Time complexity: O(1) 411 412 ***********************************************************************/ 413 414 final V head (V element) 415 { 416 auto p = firstCell; 417 auto v = p.value; 418 p.value = element; 419 mutate; 420 return v; 421 } 422 423 /*********************************************************************** 424 425 Time complexity: O(1) 426 427 ***********************************************************************/ 428 429 final V removeHead () 430 { 431 auto p = firstCell; 432 if (p.singleton) 433 list = null; 434 else 435 { 436 auto n = p.next; 437 p.unlink; 438 list = n; 439 } 440 441 auto v = p.value; 442 decrement (p); 443 return v; 444 } 445 446 /*********************************************************************** 447 448 Time complexity: O(1) 449 450 ***********************************************************************/ 451 452 final CircularList add (V element) 453 { 454 return append (element); 455 } 456 457 /*********************************************************************** 458 459 Time complexity: O(1) 460 461 ***********************************************************************/ 462 463 final CircularList append (V element) 464 { 465 if (list is null) 466 prepend (element); 467 else 468 { 469 list.prev.addNext (element, &heap.allocate); 470 increment; 471 } 472 return this; 473 } 474 475 /*********************************************************************** 476 477 Time complexity: O(1) 478 479 ***********************************************************************/ 480 481 final V tail (V element) 482 { 483 auto p = lastCell; 484 auto v = p.value; 485 p.value = element; 486 mutate; 487 return v; 488 } 489 490 /*********************************************************************** 491 492 Time complexity: O(1) 493 494 ***********************************************************************/ 495 496 final V removeTail () 497 { 498 auto p = lastCell; 499 if (p is list) 500 list = null; 501 else 502 p.unlink; 503 504 auto v = p.value; 505 decrement (p); 506 return v; 507 } 508 509 /*********************************************************************** 510 511 Time complexity: O(n) 512 513 ***********************************************************************/ 514 515 final CircularList addAt (size_t index, V element) 516 { 517 if (index is 0) 518 prepend (element); 519 else 520 { 521 cellAt(index - 1).addNext(element, &heap.allocate); 522 increment; 523 } 524 return this; 525 } 526 527 /*********************************************************************** 528 529 Time complexity: O(n) 530 531 ***********************************************************************/ 532 533 final CircularList replaceAt (size_t index, V element) 534 { 535 cellAt(index).value = element; 536 mutate; 537 return this; 538 } 539 540 /*********************************************************************** 541 542 Time complexity: O(n) 543 544 ***********************************************************************/ 545 546 final CircularList removeAt (size_t index) 547 { 548 if (index is 0) 549 removeHead; 550 else 551 { 552 auto p = cellAt(index); 553 p.unlink; 554 decrement (p); 555 } 556 return this; 557 } 558 559 /*********************************************************************** 560 561 Time complexity: O(n) 562 563 ***********************************************************************/ 564 565 final size_t remove (V element, bool all) 566 { 567 auto c = count; 568 if (list) 569 { 570 auto p = list; 571 for (;;) 572 { 573 auto n = p.next; 574 if (element == p.value) 575 { 576 p.unlink; 577 decrement (p); 578 if (p is list) 579 { 580 if (p is n) 581 { 582 list = null; 583 break; 584 } 585 else 586 list = n; 587 } 588 589 if (! all) 590 break; 591 else 592 p = n; 593 } 594 else 595 if (n is list) 596 break; 597 else 598 p = n; 599 } 600 } 601 return c - count; 602 } 603 604 /*********************************************************************** 605 606 Time complexity: O(n) 607 608 ***********************************************************************/ 609 610 final size_t replace (V oldElement, V newElement, bool all) 611 { 612 size_t c; 613 if (list) 614 { 615 auto p = list; 616 do { 617 if (oldElement == p.value) 618 { 619 ++c; 620 mutate; 621 p.value = newElement; 622 if (! all) 623 break; 624 } 625 p = p.next; 626 } while (p !is list); 627 } 628 return c; 629 } 630 631 /*********************************************************************** 632 633 Time complexity: O(number of elements in e) 634 635 ***********************************************************************/ 636 637 final size_t prepend (IContainer!(V) e) 638 { 639 Ref hd = null; 640 Ref current = null; 641 auto c = count; 642 643 foreach (element; e) 644 { 645 increment; 646 647 if (hd is null) 648 { 649 hd = heap.allocate.set(element); 650 current = hd; 651 } 652 else 653 { 654 current.addNext (element, &heap.allocate); 655 current = current.next; 656 } 657 } 658 659 if (list is null) 660 list = hd; 661 else 662 if (hd) 663 { 664 auto tl = list.prev; 665 current.next = list; 666 list.prev = current; 667 tl.next = hd; 668 hd.prev = tl; 669 list = hd; 670 } 671 return count - c; 672 } 673 674 /*********************************************************************** 675 676 Time complexity: O(number of elements in e) 677 678 ***********************************************************************/ 679 680 final size_t append (IContainer!(V) e) 681 { 682 auto c = count; 683 if (list is null) 684 prepend (e); 685 else 686 { 687 auto current = list.prev; 688 foreach (element; e) 689 { 690 increment; 691 current.addNext (element, &heap.allocate); 692 current = current.next; 693 } 694 } 695 return count - c; 696 } 697 698 /*********************************************************************** 699 700 Time complexity: O(size() + number of elements in e) 701 702 ***********************************************************************/ 703 704 final size_t addAt (size_t index, IContainer!(V) e) 705 { 706 auto c = count; 707 if (list is null || index is 0) 708 prepend (e); 709 else 710 { 711 auto current = cellAt (index - 1); 712 foreach (element; e) 713 { 714 increment; 715 current.addNext (element, &heap.allocate); 716 current = current.next; 717 } 718 } 719 return count - c; 720 } 721 722 /*********************************************************************** 723 724 Time complexity: O(n) 725 726 ***********************************************************************/ 727 728 final size_t removeRange (size_t fromIndex, size_t toIndex) 729 { 730 auto p = cellAt (fromIndex); 731 auto last = list.prev; 732 auto c = count; 733 for (size_t i = fromIndex; i <= toIndex; ++i) 734 { 735 auto n = p.next; 736 p.unlink; 737 decrement (p); 738 if (p is list) 739 { 740 if (p is last) 741 { 742 list = null; 743 break; 744 } 745 else 746 list = n; 747 } 748 p = n; 749 } 750 return c - count; 751 } 752 753 /*********************************************************************** 754 755 Copy and return the contained set of values in an array, 756 using the optional dst as a recipient (which is resized 757 as necessary). 758 759 Returns a slice of dst representing the container values. 760 761 Time complexity: O(n) 762 763 ***********************************************************************/ 764 765 final V[] toArray (V[] dst = null) 766 { 767 if (dst.length < count) 768 dst.length = count; 769 770 size_t i = 0; 771 foreach (v; this) 772 dst[i++] = v; 773 return dst [0 .. count]; 774 } 775 776 /*********************************************************************** 777 778 Is this container empty? 779 780 Time complexity: O(1) 781 782 ***********************************************************************/ 783 784 final bool isEmpty () 785 { 786 return count is 0; 787 } 788 789 /*********************************************************************** 790 791 792 ***********************************************************************/ 793 794 final CircularList check() 795 { 796 verify(((count is 0) is (list is null))); 797 verify((list is null || list.size is count)); 798 799 if (list) 800 { 801 size_t c = 0; 802 auto p = list; 803 do { 804 verify(p.prev.next is p); 805 verify(p.next.prev is p); 806 verify(instances(p.value) > 0); 807 verify(contains(p.value)); 808 p = p.next; 809 ++c; 810 } while (p !is list); 811 verify(c is size); 812 } 813 return this; 814 } 815 816 /*********************************************************************** 817 818 Time complexity: O(n) 819 820 ***********************************************************************/ 821 822 private size_t instances (V element) 823 { 824 if (list) 825 return list.count (element); 826 return 0; 827 } 828 829 /*********************************************************************** 830 831 832 ***********************************************************************/ 833 834 private void checkIndex (size_t i) 835 { 836 if (i >= count) 837 throw new Exception ("out of range"); 838 } 839 840 /*********************************************************************** 841 842 return the first cell, or throw exception if empty 843 844 ***********************************************************************/ 845 846 private Ref firstCell () 847 { 848 checkIndex (0); 849 return list; 850 } 851 852 /*********************************************************************** 853 854 return the last cell, or throw exception if empty 855 856 ***********************************************************************/ 857 858 private Ref lastCell () 859 { 860 checkIndex (0); 861 return list.prev; 862 } 863 864 /*********************************************************************** 865 866 return the index'th cell, or throw exception if bad index 867 868 ***********************************************************************/ 869 870 private Ref cellAt (size_t index) 871 { 872 checkIndex (index); 873 return list.nth (index); 874 } 875 876 /*********************************************************************** 877 878 Time complexity: O(1) 879 880 ***********************************************************************/ 881 882 private CircularList clear (bool all) 883 { 884 mutate; 885 886 // collect each node if we can't collect all at once 887 if (heap.collect(all) is false && count) 888 { 889 auto p = list; 890 do { 891 auto n = p.next; 892 decrement (p); 893 p = n; 894 } while (p != list); 895 } 896 897 list = null; 898 count = 0; 899 return this; 900 } 901 902 /*********************************************************************** 903 904 new element was added 905 906 ***********************************************************************/ 907 908 private void increment () 909 { 910 ++mutation; 911 ++count; 912 } 913 914 /*********************************************************************** 915 916 element was removed 917 918 ***********************************************************************/ 919 920 private void decrement (Ref p) 921 { 922 Reap (p.value); 923 heap.collect (p); 924 ++mutation; 925 --count; 926 } 927 928 /*********************************************************************** 929 930 set was changed 931 932 ***********************************************************************/ 933 934 private void mutate () 935 { 936 ++mutation; 937 } 938 939 /*********************************************************************** 940 941 Iterator with no filtering 942 943 ***********************************************************************/ 944 945 private struct Iterator 946 { 947 bool rev; 948 Ref cell, 949 head, 950 prior; 951 CircularList owner; 952 size_t index, 953 count; 954 size_t mutation; 955 956 /*************************************************************** 957 958 Did the container change underneath us? 959 960 ***************************************************************/ 961 962 bool valid () 963 { 964 return owner.mutation is mutation; 965 } 966 967 /*************************************************************** 968 969 Accesses the next value, and returns false when 970 there are no further values to traverse 971 972 ***************************************************************/ 973 974 bool next (ref V v) 975 { 976 if (auto n = next) 977 { 978 v = *n; 979 return true; 980 } 981 return false; 982 } 983 984 /*************************************************************** 985 986 Return a pointer to the next value, or null when 987 there are no further values to traverse 988 989 ***************************************************************/ 990 991 V* next () 992 { 993 V* r; 994 995 if (index < count) 996 { 997 ++index; 998 prior = cell; 999 r = &cell.value; 1000 cell = (rev ? cell.prev : cell.next); 1001 } 1002 else 1003 cell = null; 1004 return r; 1005 } 1006 1007 /*************************************************************** 1008 1009 Foreach support 1010 1011 ***************************************************************/ 1012 1013 int opApply (scope int delegate(ref V value) dg) 1014 { 1015 int result; 1016 auto c = cell; 1017 1018 while (index < count) 1019 { 1020 ++index; 1021 prior = c; 1022 c = (rev ? c.prev : c.next); 1023 if ((result = dg(prior.value)) != 0) 1024 break; 1025 } 1026 cell = null; 1027 return result; 1028 } 1029 1030 /*************************************************************** 1031 1032 Remove value that was just iterated. 1033 1034 ***************************************************************/ 1035 1036 bool remove () 1037 { 1038 if (prior) 1039 { 1040 auto next = (rev ? prior.prev : prior.next); 1041 if (prior is head) 1042 { 1043 if (prior is next) 1044 owner.list = null; 1045 else 1046 head = owner.list = next; 1047 } 1048 1049 prior.unlink; 1050 owner.decrement (prior); 1051 prior = null; 1052 1053 --count; 1054 // ignore this change 1055 ++mutation; 1056 return true; 1057 } 1058 return false; 1059 } 1060 1061 /*************************************************************** 1062 1063 Insert a new value before the node about to be 1064 iterated (or after the node that was just iterated). 1065 1066 Returns: a copy of this iterator for chaining. 1067 1068 ***************************************************************/ 1069 1070 Iterator insert (V value) 1071 { 1072 // Note: this needs some attention, not sure how 1073 // to handle when iterator is in reverse. 1074 if (cell is null) 1075 prior.addNext (value, &owner.heap.allocate); 1076 else 1077 cell.addPrev (value, &owner.heap.allocate); 1078 owner.increment; 1079 1080 ++count; 1081 // ignore this change 1082 ++mutation; 1083 return this; 1084 } 1085 1086 /*************************************************************** 1087 1088 Flip the direction of next() and opApply(), and 1089 reset the termination point such that we can do 1090 another full traversal. 1091 1092 ***************************************************************/ 1093 1094 Iterator reverse () 1095 { 1096 rev ^= true; 1097 next; 1098 index = 0; 1099 return this; 1100 } 1101 } 1102 } 1103 1104 /******************************************************************************* 1105 1106 *******************************************************************************/ 1107 1108 unittest 1109 { 1110 auto list = new CircularList!(int); 1111 list.add(1); 1112 list.add(2); 1113 list.add(3); 1114 1115 int i = 1; 1116 foreach(v; list) 1117 { 1118 test(v == i); 1119 i++; 1120 } 1121 1122 auto iter = list.iterator; 1123 iter.next(); 1124 iter.remove(); // delete the first item 1125 1126 i = 2; 1127 foreach(v; list) 1128 { 1129 test(v == i); 1130 i++; 1131 } 1132 1133 // test insert functionality 1134 iter = list.iterator; 1135 iter.next; 1136 iter.insert(4); 1137 1138 int[] compareto = [2, 4, 3]; 1139 i = 0; 1140 foreach(v; list) 1141 { 1142 test(v == compareto[i++]); 1143 } 1144 } 1145 1146 /******************************************************************************* 1147 1148 *******************************************************************************/ 1149 1150 debug (CircularList) 1151 { 1152 import ocean.io.Stdout; 1153 import ocean.core.Thread; 1154 import ocean.time.StopWatch; 1155 1156 void main() 1157 { 1158 // usage examples ... 1159 auto list = new CircularList!(char[]); 1160 foreach (value; list) 1161 Stdout (value).newline; 1162 1163 list.add ("foo"); 1164 list.add ("bar"); 1165 list.add ("wumpus"); 1166 1167 // implicit generic iteration 1168 foreach (value; list) 1169 Stdout (value).newline; 1170 1171 // explicit generic iteration 1172 foreach (value; list.iterator.reverse) 1173 Stdout.formatln ("> {}", value); 1174 1175 // generic iteration with optional remove 1176 auto s = list.iterator; 1177 foreach (value; s) 1178 {} //s.remove; 1179 1180 // incremental iteration, with optional remove 1181 char[] v; 1182 auto iterator = list.iterator; 1183 while (iterator.next(v)) 1184 {}//iterator.remove; 1185 1186 // incremental iteration, with optional failfast 1187 auto it = list.iterator; 1188 while (it.valid && it.next(v)) 1189 {} 1190 1191 // remove specific element 1192 list.remove ("wumpus", false); 1193 1194 // remove first element ... 1195 while (list.take(v)) 1196 Stdout.formatln ("taking {}, {} left", v, list.size); 1197 1198 1199 // setup for benchmark, with a set of integers. We 1200 // use a chunk allocator, and presize the bucket[] 1201 auto test = new CircularList!(uint, Container.reap, Container.Chunk); 1202 test.cache (1000, 1_000_000); 1203 static immutable count = 1_000_000; 1204 StopWatch w; 1205 1206 // benchmark adding 1207 w.start; 1208 for (uint i=count; i--;) 1209 test.add(i); 1210 Stdout.formatln ("{} adds: {}/s", test.size, test.size/w.stop); 1211 1212 // benchmark adding without allocation overhead 1213 test.clear; 1214 w.start; 1215 for (uint i=count; i--;) 1216 test.add(i); 1217 Stdout.formatln ("{} adds (after clear): {}/s", test.size, test.size/w.stop); 1218 1219 // benchmark duplication 1220 w.start; 1221 auto dup = test.dup; 1222 Stdout.formatln ("{} element dup: {}/s", dup.size, dup.size/w.stop); 1223 1224 // benchmark iteration 1225 w.start; 1226 foreach (value; test) {} 1227 Stdout.formatln ("{} element iteration: {}/s", test.size, test.size/w.stop); 1228 1229 test.check; 1230 } 1231 }