deprecate SCViewHolder-layRight
[supercollider.git] / SCClassLibrary / Common / Collections / SequenceableCollection.sc
blobd151c0e46abe8d698c7a45f2f8c1291525d019eb
1 SequenceableCollection : Collection {
2         // synonyms
3         |@| { arg index; ^this.clipAt(index) }
4         @@ { arg index; ^this.wrapAt(index) }
5         @|@ { arg index; ^this.foldAt(index) }
7         // fill with ramp of values
8         *series { arg size, start=0, step=1;
9                 var obj = this.new(size);
10                 size.do {|i|
11                         obj.add(start + (step * i));
12                 };
13                 ^obj
14         }
16         // fill with geometric series
17         *geom { arg size, start, grow;
18                 var i=0;
19                 var obj = this.new(size);
20                 while ({ i < size },{
21                         obj.add(start);
22                         start = start * grow;
23                         i = i + 1;
24                 });
25                 ^obj
26         }
27         // fill with fibonacci series
28         *fib { arg size, a=0.0, b=1.0;
29                 var i=0, temp;
30                 var obj = this.new(size);
31                 while { i < size }{
32                         obj.add(b);
33                         temp = b;
34                         b = a + b;
35                         a = temp;
36                         i = i + 1;
37                 };
38                 ^obj
39         }
40         *rand { arg size, minVal, maxVal;
41                 var i=0;
42                 var obj = this.new(size);
43                 while ({ i < size },{
44                         obj.add(rrand(minVal, maxVal));
45                         i = i + 1;
46                 });
47                 ^obj
48         }
49         *exprand { arg size, minVal, maxVal;
50                 var i=0;
51                 var obj = this.new(size);
52                 while ({ i < size },{
53                         obj.add(exprand(minVal, maxVal));
54                         i = i + 1;
55                 });
56                 ^obj
57         }
58         *rand2 { arg size, val;
59                 var i=0;
60                 var obj = this.new(size);
61                 while ({ i < size },{
62                         obj.add(val.rand2);
63                         i = i + 1;
64                 });
65                 ^obj
66         }
67         *linrand { arg size, minVal, maxVal;
68                 var i=0;
69                 var range = maxVal - minVal;
70                 var obj = this.new(size);
71                 while ({ i < size },{
72                         obj.add(minVal + range.linrand);
73                         i = i + 1;
74                 });
75                 ^obj
76         }
78         // fill with interpolation of values between start and end
79          *interpolation { arg size, start=0.0, end=1.0;
80                 var obj = this.new(size), step;
81                 if(size == 1) { ^obj.add(start) };
82                 step = (end - start) / (size - 1);
83                 size.do {|i|
84                         obj.add(start + (i * step));
85                 };
86                 ^obj
87         }
90         ++ { arg aSequenceableCollection;
91                 var newlist = this.species.new(this.size + aSequenceableCollection.size);
92                 newlist = newlist.addAll(this).addAll(aSequenceableCollection);
93                 ^newlist
94         }
95         +++ { arg aSequenceableCollection;
96                 aSequenceableCollection = aSequenceableCollection.asSequenceableCollection;
97                 ^this.collect {|item, i|
98                         item.asSequenceableCollection ++ aSequenceableCollection.wrapAt(i)
99                 }
100         }
101         asSequenceableCollection { ^this }
103         // select an element at random
104         choose {
105                 ^this.at(this.size.rand)
106         }
107         // select an element at random using an array of weights
108         wchoose { arg weights;
109                 ^this.at(weights.windex)
110         }
112         == { | aCollection |
113                 if (aCollection.class != this.class) { ^false };
114                 if (this.size != aCollection.size) { ^false };
115                 this.do { | item, i |
116                         if (item != aCollection[i]) { ^false };
117                 };
118                 ^true
119         }
121         copyRange { arg start, end;
122                 var newColl;
123                 var i = start;
124                 newColl = this.species.new(end - start);
125                 while ({ i < end },{
126                         newColl.add(this.at(i));
127                         i = i + 1;
128                 });
129                 ^newColl
130         }
131         keep { arg n;
132                 var size;
133                 if (n>=0) {
134                         ^this.copyRange(0, n-1)
135                 }{
136                         size = this.size;
137                         ^this.copyRange(size+n, size-1)
138                 }
139         }
140         drop { arg n;
141                 var size = this.size;
142                 if (n>=0) {
143                         ^this.copyRange(n, size-1)
144                 }{
145                         ^this.copyRange(0, size+n-1)
146                 }
147         }
148         copyToEnd { arg start;
149                 ^this.copyRange(start, this.size - 1)
150         }
151         copyFromStart { arg end;
152                 ^this.copyRange(0, end)
153         }
155         indexOf { arg item;
156                 this.do({ arg elem, i;
157                         if ( item === elem, { ^i })
158                 });
159                 ^nil
160         }
162         indexOfEqual { arg item, offset=0;
163                 (this.size - offset).do ({ arg i;
164                         i = i + offset;
165                         if ( item == this[i], { ^i })
166                 });
167                 ^nil
168         }
170         indicesOfEqual { |item|
171                 var indices;
172                 this.do { arg val, i;
173                         if (item == val) { indices = indices.add(i) }
174                 };
175                 ^indices
176         }
179         find { |sublist, offset=0|
180                 var subSize_1 = sublist.size - 1, first = sublist.first, index;
181                 (this.size - offset).do { |i|
182                         index = i + offset;
183                         if (this[index] == first) {
184                                 if (this.copyRange(index, index + subSize_1) == sublist) { ^index }
185                         };
186                 };
187                 ^nil
188         }
190         findAll { arg arr, offset=0;
191                 var indices, i=0;
192                 while {
193                         i = this.find(arr, offset);
194                         i.notNil
195                 }{
196                         indices = indices.add(i);
197                         offset = i + 1;
198                 }
199                 ^indices
200         }
202         indexOfGreaterThan { arg val;
203                 ^this.detectIndex { |item| item > val };
204         }
206         indexIn { arg val; // collection is sorted, returns closest index
207                 var i, a, b;
208                 var j = this.indexOfGreaterThan(val);
209                 if(j.isNil) { ^this.size - 1 };
210                 if(j == 0) { ^j };
211                 i = j - 1;
212                 ^if((val - this[i]) < (this[j] - val)) { i } { j }
213         }
215         indexInBetween { arg val; // collection is sorted, returns linearly interpolated index
216                 var a, b, div, i;
217                 if(this.isEmpty) { ^nil };
218                 i = this.indexOfGreaterThan(val);
219                 if(i.isNil) { ^this.size - 1 };
220                 if(i == 0) { ^i };
221                 a = this[i-1]; b = this[i];
222                 div = b - a;
223                 if(div == 0) { ^i };
224                 ^((val - a) / div) + i - 1
225         }
227         resamp0 { arg newSize;
228                 var factor = this.size - 1 / (newSize - 1);
229                 ^this.species.fill(newSize, { |i| this.at((i * factor).round(1.0).asInteger) })
230         }
232         resamp1 { arg newSize;
233                 var factor = this.size - 1 / (newSize - 1);
234                 ^this.species.fill(newSize, { |i| this.blendAt(i * factor) })
235         }
237         remove { arg item;
238                 var index = this.indexOf(item);
239                 ^if ( index.notNil, {
240                         this.removeAt(index);
241                 },{
242                         nil
243                 });
244         }
245         removing { arg item;
246                 var coll = this.copy;
247                 coll.remove(item);
248                 ^coll
249         }
250         take { arg item;
251                 var index = this.indexOf(item);
252                 ^if ( index.notNil, {
253                         this.takeAt(index);
254                 },{
255                         nil
256                 });
257         }
259         // accessing
260         lastIndex { ^if(this.size > 0) { this.size - 1 } { nil } }
261         middleIndex { ^if(this.size > 0) { (this.size - 1) div: 2 } { nil } }
263         first { if (this.size > 0, { ^this.at(0) }, { ^nil }) }
264         last { if (this.size > 0, { ^this.at(this.size - 1) }, { ^nil }) }
265         middle { if (this.size > 0, { ^this.at((this.size - 1) div: 2) }, { ^nil }) }
267         top { ^this.last }
268         putFirst { arg obj; if (this.size > 0, { ^this.put(0, obj) }) }
269         putLast { arg obj; if (this.size > 0, { ^this.put(this.size - 1, obj) }) }
272         // compatibility with isolated objects
274         obtain { arg index, default; ^this[index] ? default }
276         instill { arg index, item, default;
277                 var res = if (index >= this.size) {
278                         this.extend(index + 1, default)
279                 }{
280                         this.copy
281                 };
282                 ^res.put(index, item)
283         }
285         // ordering
286         pairsDo { arg function;
287                 forBy(0, this.size-2, 2) {|i|
288                         function.value(this[i], this[i+1], i);
289                 }
290         }
291         keysValuesDo { arg function;
292                 ^this.pairsDo(function)
293         }
295         doAdjacentPairs { arg function;
296                 (this.size - 1).do({ arg i;
297                         function.value(this.at(i), this.at(i+1), i);
298                 })
299         }
300         separate { arg function = true;
301                 var list = Array.new;
302                 var sublist = this.species.new;
303                 this.doAdjacentPairs({ arg a, b, i;
304                         sublist = sublist.add(a);
305                         if ( function.value(a, b, i), {
306                                 list = list.add(sublist);
307                                 sublist = this.species.new;
308                         });
309                 });
310                 if(this.notEmpty) { sublist = sublist.add(this.last) };
311                 list = list.add(sublist);
312                 ^list
313         }
314         delimit { arg function;
315                 var list, sublist;
316                 list = Array.new;
317                 sublist = this.species.new;
318                 this.do({ arg item, i;
319                         if ( function.value(item, i), {
320                                 list = list.add(sublist);
321                                 sublist = this.species.new;
322                         },{
323                                 sublist = sublist.add(item);
324                         });
325                 });
326                 list = list.add(sublist);
327                 ^list
328         }
329         clump { arg groupSize;
330                 var list = Array.new((this.size / groupSize).roundUp.asInteger);
331                 var sublist = this.species.new(groupSize);
332                 this.do({ arg item;
333                         sublist.add(item);
334                         if (sublist.size >= groupSize, {
335                                 list.add(sublist);
336                                 sublist = this.species.new(groupSize);
337                         });
338                 });
339                 if (sublist.size > 0, { list = list.add(sublist); });
340                 ^list
341         }
342         clumps { arg groupSizeList;
343                 var i = 0;
344                 var list = Array.new(groupSizeList.size); // still better estimate than default
345                 var subSize = groupSizeList.at(0);
346                 var sublist = this.species.new(subSize);
347                 this.do({ arg item;
348                         sublist = sublist.add(item);
349                         if (sublist.size >= subSize, {
350                                 i = i + 1;
351                                 list = list.add(sublist);
352                                 subSize = groupSizeList.wrapAt(i);
353                                 sublist = this.species.new(subSize);
354                         });
355                 });
356                 if (sublist.size > 0, { list = list.add(sublist); });
357                 ^list
358         }
359         curdle { arg probability;
360                 ^this.separate({ probability.coin });
361         }
362         flatten { arg numLevels=1;
363                 var list;
365                 if (numLevels <= 0, { ^this });
366                 numLevels = numLevels - 1;
368                 list = this.species.new;
369                 this.do({ arg item;
370                         if (item.respondsTo('flatten'), {
371                                 list = list.addAll(item.flatten(numLevels));
372                         },{
373                                 list = list.add(item);
374                         });
375                 });
376                 ^list
377         }
378         
379         flat {
380                 ^this.prFlat(this.species.new(this.flatSize))
381         }
382         
383         prFlat { |list|
384                 this.do({ arg item, i;
385                         if (item.respondsTo('prFlat'), {
386                                 list = item.prFlat(list);
387                         },{
388                                 list = list.add(item);
389                         });
390                 });
391                 ^list
392         }
394         flatIf { |func|
395                 var list = this.species.new(this.size); // as we don't know the size, just guess
396                 this.do({ arg item, i;
397                         if (item.respondsTo('flatIf') and: { func.value(item, i) }, {
398                                 list = list.addAll(item.flatIf(func));
399                         },{
400                                 list = list.add(item);
401                         });
402                 });
403                 ^list
404         }
405         
406         flop {
407                 var list, size, maxsize;
409                 size = this.size;
410                 maxsize = 0;
411                 this.do({ arg sublist;
412                         var sz;
413                         sz = if (sublist.isSequenceableCollection, { sublist.size },{ 1 });
414                         if (sz > maxsize, { maxsize = sz });
415                 });
417                 list = this.species.fill(maxsize, { this.species.new(size) });
418                 this.do({ arg isublist, i;
419                         if (isublist.isSequenceableCollection, {
420                                 list.do({ arg jsublist, j;
421                                         jsublist.add( isublist.wrapAt(j); );
422                                 });
423                         },{
424                                 list.do({ arg jsublist, j;
425                                         jsublist.add( isublist );
426                                 });
427                         });
428                 });
429                 ^list
430         }
432         flopTogether { arg ... moreArrays;
433                 var standIn, maxSize = 0, array;
434                 array = [this] ++ moreArrays;
435                 array.do { |sublist|
436                         sublist.do { |each|
437                                 maxSize = max(maxSize, each.size)
438                         }
439                 };
440                 standIn = 0.dup(maxSize);
441                 array = array.collect { |sublist| sublist.add(standIn) };
442                 ^array.collect { |sublist|
443                         sublist.flop.collect { |each| each.drop(-1) } // remove stand-in
444                 };
445         }
447         unlace { arg numlists, clumpSize=1, clip=false;
448                 var size, list, sublist, self;
450                 size = (this.size + numlists - 1) div: numlists;
451                 list = this.species.fill(numlists, { this.species.new(size) });
452                 self = if(clip) { this.keep(this.size.trunc(clumpSize * numlists).postln)} { this };
453                 self.do({ arg item, i;
454                         sublist = list.at(i div: clumpSize % numlists);
455                         sublist.add(item);
456                 });
457                 ^list
458         }
460         integrate {
461                 var list, sum = 0;
462                 list = this.class.new(this.size);
463                 this.do {|item|
464                         sum = sum + item;
465                         list.add( sum );
466                 };
467                 ^list
468         }
469         differentiate {
470                 var list, prev = 0;
471                 list = this.class.new(this.size);
472                 this.do {|item|
473                         list.add( item - prev );
474                         prev = item;
475                 };
476                 ^list
477         }
478         // complement to Integer:asDigits
479         convertDigits { arg base=10;
480                 var lastIndex = this.lastIndex;
481                 ^this.sum { |x, i|
482                         if(x >= base) { Error("digit too large for base").throw };
483                         base ** (lastIndex - i) * x
484                 }.asInteger
485         }
487         hammingDistance { |that|
488                         // if this is shorter than that, size difference should be included
489                         // (if this is longer, the do loop will take care of it)
490                 var     count = (that.size - this.size).max(0);
491                 this.do({ |elem, i|
492                         if(elem != that[i]) { count = count + 1 };
493                 });
494                 ^count
495         }
497         // pitch operations
498         degreeToKey { arg scale, stepsPerOctave=12;
499                 ^this.collect({ arg scaleDegree;
500                         scaleDegree.degreeToKey(scale, stepsPerOctave);
501                 });
502         }
503         keyToDegree { arg scale, stepsPerOctave=12;
504                 ^this.collect { arg val; val.keyToDegree(scale, stepsPerOctave) }
505         }
507         nearestInScale { arg scale, stepsPerOctave=12; // collection is sorted
508                 var key, root;
509                 root = this.trunc(stepsPerOctave);
510                 key = this % stepsPerOctave;
511                 ^key.nearestInList(scale) + root
512         }
513         nearestInList { arg list;  // collection is sorted
514                 ^this.collect({ arg item; list.at(list.indexIn(item)) })
515         }
517         transposeKey { arg amount, octave=12;
518                 ^((this + amount) % octave).sort
519         }
520         mode { arg degree, octave=12;
521                 ^(rotate(this, degree.neg) - this.wrapAt(degree)) % octave
522         }
524         performDegreeToKey { arg scaleDegree, stepsPerOctave = 12, accidental = 0;
525                 var baseKey = (stepsPerOctave * (scaleDegree div: this.size)) + this.wrapAt(scaleDegree);
526                 ^if(accidental == 0) { baseKey } { baseKey + (accidental * (stepsPerOctave / 12.0)) }
527         }
529         performKeyToDegree { | degree, stepsPerOctave = 12 |
530                 var n = degree div: stepsPerOctave * this.size;
531                 var key = degree % stepsPerOctave;
532                 ^this.indexInBetween(key) + n
533         }
535         performNearestInList { | degree |
536                 ^this.at(this.indexIn(degree))
537         }
539         performNearestInScale { arg degree, stepsPerOctave=12; // collection is sorted
540                 var root = degree.trunc(stepsPerOctave);
541                 var key = degree % stepsPerOctave;
542                 ^key.nearestInList(this) + root
543         }
545         // supports a variation of Mikael Laurson's rhythm list RTM-notation.
546         convertRhythm {
547                 var list, tie;
548                 list = List.new;
549                 tie = this.convertOneRhythm(list);
550                 if (tie > 0.0, { list.add(tie) });  // check for tie at end of rhythm
551                 ^list
552         }
553         sumRhythmDivisions {
554                 var sum = 0;
555                 this.do {|beats|
556                         sum = sum + abs(if (beats.isSequenceableCollection) {
557                                 beats[0];
558                         }{
559                                 beats
560                         });
561                 };
562                 ^sum
563         }
564         convertOneRhythm { arg list, tie = 0.0, stretch = 1.0;
565                 var beats, divisions, repeats;
566                 #beats, divisions, repeats = this;
567                 repeats = repeats ? 1;
568                 stretch = stretch * beats / divisions.sumRhythmDivisions;
569                 repeats.do({
570                         divisions.do { |val|
571                                 if (val.isSequenceableCollection) {
572                                         tie = val.convertOneRhythm(list, tie, stretch)
573                                 }{
574                                         val = val * stretch;
575                                         if (val > 0.0) {
576                                                 list.add(val + tie);
577                                                 tie = 0.0;
578                                         }{
579                                                 tie = tie - val
580                                         };
581                                 };
582                         };
583                 });
584                 ^tie
585         }
587         isSequenceableCollection { ^true }
588         containsSeqColl { ^this.any(_.isSequenceableCollection) }
590         // unary math ops
591         neg { ^this.performUnaryOp('neg') }
592         bitNot { ^this.performUnaryOp('bitNot') }
593         abs { ^this.performUnaryOp('abs') }
594         ceil { ^this.performUnaryOp('ceil') }
595         floor { ^this.performUnaryOp('floor') }
596         frac { ^this.performUnaryOp('frac') }
597         sign { ^this.performUnaryOp('sign') }
598         squared { ^this.performUnaryOp('squared') }
599         cubed { ^this.performUnaryOp('cubed') }
600         sqrt { ^this.performUnaryOp('sqrt') }
601         exp { ^this.performUnaryOp('exp') }
602         reciprocal { ^this.performUnaryOp('reciprocal') }
603         midicps { ^this.performUnaryOp('midicps') }
604         cpsmidi { ^this.performUnaryOp('cpsmidi') }
605         midiratio { ^this.performUnaryOp('midiratio') }
606         ratiomidi { ^this.performUnaryOp('ratiomidi') }
607         ampdb { ^this.performUnaryOp('ampdb') }
608         dbamp { ^this.performUnaryOp('dbamp') }
609         octcps { ^this.performUnaryOp('octcps') }
610         cpsoct { ^this.performUnaryOp('cpsoct') }
611         log { ^this.performUnaryOp('log') }
612         log2 { ^this.performUnaryOp('log2') }
613         log10 { ^this.performUnaryOp('log10') }
614         sin { ^this.performUnaryOp('sin') }
615         cos { ^this.performUnaryOp('cos') }
616         tan { ^this.performUnaryOp('tan') }
617         asin { ^this.performUnaryOp('asin') }
618         acos { ^this.performUnaryOp('acos') }
619         atan { ^this.performUnaryOp('atan') }
620         sinh { ^this.performUnaryOp('sinh') }
621         cosh { ^this.performUnaryOp('cosh') }
622         tanh { ^this.performUnaryOp('tanh') }
623         rand { ^this.performUnaryOp('rand') }
624         rand2 { ^this.performUnaryOp('rand2') }
625         linrand { ^this.performUnaryOp('linrand') }
626         bilinrand { ^this.performUnaryOp('bilinrand') }
627         sum3rand { ^this.performUnaryOp('sum3rand') }
629         distort { ^this.performUnaryOp('distort') }
630         softclip { ^this.performUnaryOp('softclip') }
631         coin { ^this.performUnaryOp('coin') }
632         even { ^this.performUnaryOp('even') }
633         odd { ^this.performUnaryOp('odd') }
634         isPositive { ^this.performUnaryOp('isPositive') }
635         isNegative { ^this.performUnaryOp('isNegative') }
636         isStrictlyPositive { ^this.performUnaryOp('isStrictlyPositive') }
638         rectWindow { ^this.performUnaryOp('rectWindow') }
639         hanWindow { ^this.performUnaryOp('hanWindow') }
640         welWindow { ^this.performUnaryOp('welWindow') }
641         triWindow { ^this.performUnaryOp('triWindow') }
643         scurve { ^this.performUnaryOp('scurve') }
644         ramp { ^this.performUnaryOp('ramp') }
646         asFloat { ^this.performUnaryOp('asFloat') }
647         asInteger { ^this.performUnaryOp('asInteger') }
649         nthPrime { ^this.performUnaryOp('nthPrime') }
650         prevPrime { ^this.performUnaryOp('prevPrime') }
651         nextPrime { ^this.performUnaryOp('nextPrime') }
652         indexOfPrime { ^this.performUnaryOp('indexOfPrime') }
654         real { ^this.performUnaryOp('real') }
655         imag { ^this.performUnaryOp('imag') }
657         magnitude { ^this.performUnaryOp('magnitude') }
658         magnitudeApx { ^this.performUnaryOp('magnitudeApx') }
659         phase { ^this.performUnaryOp('phase') }
660         angle { ^this.performUnaryOp('angle') }
662         rho { ^this.performUnaryOp('rho') }
663         theta { ^this.performUnaryOp('theta') }
665         degrad { ^this.performUnaryOp('degrad') }
666         raddeg { ^this.performUnaryOp('raddeg') }
668         // binary math ops
669         + { arg aNumber, adverb; ^this.performBinaryOp('+', aNumber, adverb) }
670         - { arg aNumber, adverb; ^this.performBinaryOp('-', aNumber, adverb) }
671         * { arg aNumber, adverb; ^this.performBinaryOp('*', aNumber, adverb) }
672         / { arg aNumber, adverb; ^this.performBinaryOp('/', aNumber, adverb) }
673         div { arg aNumber, adverb; ^this.performBinaryOp('div', aNumber, adverb) }
674         mod { arg aNumber, adverb; ^this.performBinaryOp('mod', aNumber, adverb) }
675         pow { arg aNumber, adverb; ^this.performBinaryOp('pow', aNumber, adverb) }
676         min { arg aNumber, adverb; ^this.performBinaryOp('min', aNumber, adverb) }
677         max { arg aNumber=0, adverb; ^this.performBinaryOp('max', aNumber, adverb) }
680         <  { arg aNumber, adverb; ^this.performBinaryOp('<', aNumber, adverb) }
681         <= { arg aNumber, adverb; ^this.performBinaryOp('<=', aNumber, adverb) }
682         >  { arg aNumber, adverb; ^this.performBinaryOp('>', aNumber, adverb) }
683         >= { arg aNumber, adverb; ^this.performBinaryOp('>=', aNumber, adverb) }
685         bitAnd { arg aNumber, adverb; ^this.performBinaryOp('bitAnd', aNumber, adverb) }
686         bitOr { arg aNumber, adverb; ^this.performBinaryOp('bitOr', aNumber, adverb) }
687         bitXor { arg aNumber, adverb; ^this.performBinaryOp('bitXor', aNumber, adverb) }
688         bitHammingDistance { arg aNumber, adverb;
689                 ^this.performBinaryOp('hammingDistance', aNumber, adverb)
690         }
692         lcm { arg aNumber, adverb; ^this.performBinaryOp('lcm', aNumber, adverb) }
693         gcd { arg aNumber, adverb; ^this.performBinaryOp('gcd', aNumber, adverb) }
694         round { arg aNumber=1, adverb; ^this.performBinaryOp('round', aNumber, adverb) }
695         roundUp { arg aNumber=1, adverb; ^this.performBinaryOp('roundUp', aNumber, adverb) }
696         trunc { arg aNumber=1, adverb; ^this.performBinaryOp('trunc', aNumber, adverb) }
697         atan2 { arg aNumber, adverb; ^this.performBinaryOp('atan2', aNumber, adverb) }
698         hypot { arg aNumber, adverb; ^this.performBinaryOp('hypot', aNumber, adverb) }
699         hypotApx { arg aNumber, adverb; ^this.performBinaryOp('hypotApx', aNumber, adverb) }
700         leftShift { arg aNumber, adverb; ^this.performBinaryOp('leftShift', aNumber, adverb) }
701         rightShift { arg aNumber, adverb; ^this.performBinaryOp('rightShift', aNumber, adverb) }
702         unsignedRightShift { arg aNumber, adverb; ^this.performBinaryOp('unsignedRightShift', aNumber, adverb) }
703         ring1 { arg aNumber, adverb; ^this.performBinaryOp('ring1', aNumber, adverb) }
704         ring2 { arg aNumber, adverb; ^this.performBinaryOp('ring2', aNumber, adverb) }
705         ring3 { arg aNumber, adverb; ^this.performBinaryOp('ring3', aNumber, adverb) }
706         ring4 { arg aNumber, adverb; ^this.performBinaryOp('ring4', aNumber, adverb) }
707         difsqr { arg aNumber, adverb; ^this.performBinaryOp('difsqr', aNumber, adverb) }
708         sumsqr { arg aNumber, adverb; ^this.performBinaryOp('sumsqr', aNumber, adverb) }
709         sqrsum { arg aNumber, adverb; ^this.performBinaryOp('sqrsum', aNumber, adverb) }
710         sqrdif { arg aNumber, adverb; ^this.performBinaryOp('sqrdif', aNumber, adverb) }
711         absdif { arg aNumber, adverb; ^this.performBinaryOp('absdif', aNumber, adverb) }
712         thresh { arg aNumber, adverb; ^this.performBinaryOp('thresh', aNumber, adverb) }
713         amclip { arg aNumber, adverb; ^this.performBinaryOp('amclip', aNumber, adverb) }
714         scaleneg { arg aNumber, adverb; ^this.performBinaryOp('scaleneg', aNumber, adverb) }
715         clip2 { arg aNumber=1, adverb; ^this.performBinaryOp('clip2', aNumber, adverb) }
716         fold2 { arg aNumber, adverb; ^this.performBinaryOp('fold2', aNumber, adverb) }
717         wrap2 { arg aNumber, adverb; ^this.performBinaryOp('wrap2', aNumber, adverb) }
718         excess { arg aNumber, adverb; ^this.performBinaryOp('excess', aNumber, adverb) }
719         firstArg { arg aNumber, adverb; ^this.performBinaryOp('firstArg', aNumber, adverb) }
720         rrand { arg aNumber, adverb; ^this.performBinaryOp('rrand', aNumber, adverb) }
721         exprand { arg aNumber, adverb; ^this.performBinaryOp('exprand', aNumber, adverb) }
723         // math op dispatch support
724         performUnaryOp { arg aSelector;
725                 ^this.collect({ arg item; item.perform(aSelector) });
726         }
728         performBinaryOp { arg aSelector, theOperand, adverb;
729                 ^theOperand.performBinaryOpOnSeqColl(aSelector, this, adverb);
730         }
731         performBinaryOpOnSeqColl { arg aSelector, theOperand, adverb;
732                 var size, newList;
733                 if (adverb == nil) {
734                         size = this.size max: theOperand.size;
735                         newList = this.species.new(size);
736                         size.do({ arg i;
737                                 newList.add(theOperand.wrapAt(i).perform(aSelector, this.wrapAt(i)));
738                         });
739                         ^newList
740                 };
741                 if (adverb.isInteger) {
742                         if (adverb == 0) {
743                                 size = this.size max: theOperand.size;
744                                 newList = this.species.new(size);
745                                 size.do({ arg i;
746                                         newList.add(theOperand.wrapAt(i).perform(aSelector, this.wrapAt(i)));
747                                 });
748                                 ^newList
749                         }{
750                                 if (adverb > 0) {
751                                         ^theOperand.collect {|item, i| item.perform(aSelector, this, adverb-1) }
752                                 }{
753                                         ^this.collect {|item, i| theOperand.perform(aSelector, item, adverb+1) }
754                                 }
755                         }
756                 };
757                 if (adverb == 't') {
758 //                      size = this.size;
759 //                      newList = this.species.new(size);
760 //                      size.do({ arg i;
761 //                              newList.add(theOperand.perform(aSelector, this.at(i)));
762 //                      });
763 //                      ^newList
764                         size = theOperand.size;
765                         newList = this.species.new(size);
766                         size.do({ arg i;
767                                 newList.add(theOperand.at(i).perform(aSelector, this));
768                         });
769                         ^newList
770                 };
771                 if (adverb == 'x') {
772 //                      size = this.size;
773 //                      newList = this.species.new(size);
774 //                      size.do({ arg i;
775 //                              newList.add(theOperand.perform(aSelector, this.at(i)));
776 //                      });
777 //                      ^newList
778                         size = theOperand.size * this.size;
779                         newList = this.species.new(size);
780                         theOperand.do({ arg a;
781                                 this.do({ arg b;
782                                         newList.add(a.perform(aSelector, b));
783                                 });
784                         });
785                         ^newList
786                 };
787                 if (adverb == 's') {
788                         size = this.size min: theOperand.size;
789                         newList = this.species.new(size);
790                         size.do({ arg i;
791                                 newList.add(theOperand.wrapAt(i).perform(aSelector, this.wrapAt(i)));
792                         });
793                         ^newList
794                 };
795                 if (adverb == 'f') {
796                         size = this.size max: theOperand.size;
797                         newList = this.species.new(size);
798                         size.do({ arg i;
799                                 newList.add(theOperand.foldAt(i).perform(aSelector, this.foldAt(i)));
800                         });
801                         ^newList
802                 };
803                 error("unrecognized adverb: '" ++ adverb ++ "' for operator '" ++ aSelector ++ "'\n");
804                 ^nil
805         }
806         performBinaryOpOnSimpleNumber { arg aSelector, aNumber, adverb;
807                 ^this.collect({ arg item;
808                         aNumber.perform(aSelector, item, adverb)
809                 })
810         }
811         performBinaryOpOnComplex { arg aSelector, aComplex, adverb;
812                 ^this.collect({ arg item;
813                         aComplex.perform(aSelector, item, adverb)
814                 })
815         }
817         asFraction { arg denominator=100, fasterBetter=true;
818                 ^this.collect { |item| item.asFraction(denominator, fasterBetter) }
819         }
821         asPoint { ^Point(this[0] ? 0, this[1] ? 0) }
822         asRect { ^Rect(this[0] ? 0, this[1] ? 0, this[2] ? 0, this[3] ? 0) }
823         ascii { ^this.collect { arg item; item.ascii } }
826         // support UGen rate access
828         rate {
829                 var rate, rates;
830                 if(this.size == 1, { ^this.first.rate });
831                 ^this.collect({ arg item; item.rate }).minItem;
832                 // 'scalar' > 'control' > 'audio'
833         }
835         // if we don't catch the special case of an empty array,
836         // Object:multiChannelPerform goes into infinite recursion
837         multiChannelPerform { arg selector ... args;
838                 if(this.size > 0) {
839                         ^super.multiChannelPerform(selector, *args);
840                 } {
841                         ^this.class.new
842                 }
843         }
845         // support some UGen convenience methods.
846         // NOTE: don't forget to add a wrapper here when adding a method to UGen or AbstractFunction
847         clip { arg ... args; ^this.multiChannelPerform('clip', *args) }
848         wrap { arg ... args; ^this.multiChannelPerform('wrap', *args) }
849         fold { arg ... args; ^this.multiChannelPerform('fold', *args) }
850         linlin { arg ... args; ^this.multiChannelPerform('linlin', *args) }
851         linexp { arg ... args; ^this.multiChannelPerform('linexp', *args) }
852         explin { arg ... args; ^this.multiChannelPerform('explin', *args) }
853         expexp { arg ... args; ^this.multiChannelPerform('expexp', *args) }
854         lincurve { arg ... args; ^this.multiChannelPerform('lincurve', *args) }
855         curvelin { arg ... args; ^this.multiChannelPerform('curvelin', *args) }
856         range { arg ... args; ^this.multiChannelPerform('range', *args) }
857         exprange { arg ... args; ^this.multiChannelPerform('exprange', *args) }
858         unipolar { arg ... args; ^this.multiChannelPerform('unipolar', *args) }
859         bipolar { arg ... args; ^this.multiChannelPerform('bipolar', *args) }
860         lag { arg ... args; ^this.multiChannelPerform('lag', *args) }
861         lag2 { arg ... args; ^this.multiChannelPerform('lag2', *args) }
862         lag3 { arg ... args; ^this.multiChannelPerform('lag3', *args) }
863         lagud { arg ... args; ^this.multiChannelPerform('lagud', *args) }
864         lag2ud { arg ... args; ^this.multiChannelPerform('lag2ud', *args) }
865         lag3ud { arg ... args; ^this.multiChannelPerform('lag3ud', *args) }
866         varlag { arg ... args; ^this.multiChannelPerform('varlag', *args) }
867         blend { arg ... args; ^this.multiChannelPerform('blend', *args) }
868         checkBadValues { arg ... args; ^this.multiChannelPerform('checkBadValues', *args) }
869         prune { arg ... args; ^this.multiChannelPerform('prune', *args) }
871         minNyquist { ^min(this, SampleRate.ir * 0.5) }
873         // sorting
874         sort { arg function;
875                 if (function.isNil) { function = { arg a, b; a <= b }; };
876                 ^this.mergeSort(function)
877         }
878         sortBy { arg key;
879                 ^this.sort({| a, b | a[key] <= b[key] })
880         }
881         sortMap { arg function;
882                 ^this.sort({| a, b | function.value(a) <= function.value(b) })
883         }
884         sortedMedian {
885                 var index;
886                 if (this.size.odd) {
887                         ^this.middle
888                 }{
889                         index = this.middleIndex;
890                         ^(this[index] + this[index+1]) / 2;
891                 }
892         }
893         median { arg function;
894                 //^this.sort(function).sortedMedian
895                 // Note the copy, to prevent changing the input.
896                 ^this.copy.hoareMedian(function)
897         }
899         quickSort { arg function;
900                 this.quickSortRange(0, this.size - 1, function)
901         }
902         order { arg function;
903                 var array, orderFunc;
904                 // returns an array of indices that would sort the collection into order.
905                 if(this.isEmpty) { ^[] };
906                 if (function.isNil) { function = { arg a, b; a <= b }; };
907                 array = [this, (0..this.lastIndex)].flop;
908                 orderFunc = {|a,b| function.value(a[0], b[0]) };
909                 ^array.mergeSort(orderFunc).flop[1]
910         }
912         swap { arg i, j;
913                 var temp;
914                 temp = this[i];
915                 this[i] = this[j];
916                 this[j] = temp;
917         }
919         quickSortRange { arg i, j, function;
920                 //Sort elements i through j of this to be nondescending according to
921                 // function.
922                 var di, dij, dj, tt, ij, k, l, n;
924                 // The prefix d means the data at that index.
925                 if ((n = j + 1  - i) <= 1, { ^this });  // Nothing to sort.
927                 //Sort di,dj.
928                 di = this.at(i);
929                 dj = this.at(j);
930                 if (function.value(di, dj).not, { // i.e., should di precede dj?
931                         this.swap(i,j);
932                                  tt = di;
933                                  di = dj;
934                                  dj = tt;
935                 });
936                 if ( n > 2, { // More than two elements.
937                         ij = (i + j) div: 2;  // ij is the midpoint of i and j.
938                         dij = this.at(ij);  // Sort di,dij,dj.  Make dij be their median.
939                         if (function.value(di,  dij), {  // i.e. should di precede dij?
940                                 if (function.value(dij, dj).not, {  // i.e., should dij precede dj?
941                                         this.swap(j, ij);
942                                         dij = dj;
943                                 })
944                         },{ // i.e. di should come after dij"
945                                 this.swap(i, ij);
946                                 dij = di;
947                         });
948                         if ( n > 3, { // More than three elements.
949                                 // Find k>i and l<j such that dk,dij,dl are in reverse order.
950                                 // Swap k and l.  Repeat this procedure until k and l pass each other.
951                                 k = i;
952                                 l = j;
953                                 while ({
954                                         while ({
955                                                 l = l - 1;
956                                                 k <= l and: { function.value(dij, this.at(l)) }
957                                         }); // i.e. while dl succeeds dij
958                                         while ({
959                                                 k = k + 1;
960                                                 k <= l and: { function.value(this.at(k), dij) };
961                                         }); // i.e. while dij succeeds dk
962                                         k <= l
963                                 },{
964                                         this.swap(k, l);
965                                 });
966                 // Now l<k (either 1 or 2 less), and di through dl are all less than or equal to dk
967                 // through dj.  Sort those two segments.
968                                 this.quickSortRange(i, l, function);
969                                 this.quickSortRange(k, j, function);
970                         });
971                 });
972         }
976         mergeSort { arg function;
977                 var tempArray;
978                 tempArray = this.class.newClear(this.size);
979                 this.mergeSortTemp(function, tempArray, 0, this.size - 1);
980         }
982         mergeSortTemp { arg function, tempArray, left, right;
983                 var mid, size;
985                 size = right - left;
986                 if (size <= 0) { ^this };
987                 if (size <= 8) { ^this.insertionSortRange(function, left, right) };
989                 mid = (right + left) >> 1;
990                 this.mergeSortTemp(function, tempArray, left, mid);
991                 this.mergeSortTemp(function, tempArray, mid+1, right);
992                 this.mergeTemp(function, tempArray, left, mid+1, right);
993         }
995         mergeTemp { arg function, tempArray, left, mid, right;
996                 var i, leftEnd, size, tempPos;
997                 leftEnd = mid - 1;
998                 tempPos = left;
999                 size = right - left + 1;
1000                 while { (left <= leftEnd) && (mid <= right) }
1001                 {
1002                         if (function.value( this[left], this[mid] ))
1003                         {
1004                                 tempArray[tempPos] = this[left];
1005                                 tempPos = tempPos + 1;
1006                                 left = left + 1;
1007                         }
1008                         {
1009                                 tempArray[tempPos] = this[mid];
1010                                 tempPos = tempPos + 1;
1011                                 mid = mid + 1;
1012                         }
1013                 };
1014                 while { left <= leftEnd }
1015                 {
1016                         tempArray[tempPos] = this[left];
1017                         tempPos = tempPos + 1;
1018                         left = left + 1;
1019                 };
1020                 while { mid <= right }
1021                 {
1022                         tempArray[tempPos] = this[mid];
1023                         tempPos = tempPos + 1;
1024                         mid = mid + 1;
1025                 };
1026                 size.do {
1027                         this[right] = tempArray[right];
1028                         right = right - 1;
1029                 };
1030         }
1032         insertionSort { arg function;
1033                 ^this.insertionSortRange(function, 0, this.size - 1)
1034         }
1035         insertionSortRange { arg function, left, right;
1036                 var i, j, test;
1037                 i = left + 1;
1038                 while { i <= right }
1039                 {
1040                         test = this[i];
1041                         j = i;
1042                         while { (j > left) && { function.value(this[j-1], test).not } }
1043                         {
1044                                 this[j] = this[j-1];
1045                                 j = j - 1;
1046                         };
1047                         this[j] = test;
1048                         i = i + 1;
1049                 }
1050         }
1053         // Finds the median efficiently, by rearranging the array IN-PLACE.
1054         hoareMedian { |function|
1055                 if(this.isEmpty) { ^nil };
1056                 ^if(this.size.even, {
1057                         [this.hoareFind(this.size/ 2 - 1, function),
1058                          this.hoareFind(this.size/ 2,     function)].mean;
1059                 }, {
1060                         this.hoareFind(this.size - 1 / 2, function);
1061                 });
1062         }
1064         // Finds the kth element in the array, according to a given sorting function.
1065         // This is typically fast (order is O(n) rather than O(n log n)) because it
1066         // doesn't attempt to completely sort the array. Method is due to C. A. F. Hoare.
1067         // Note: this rearranges array elements IN PLACE.
1068         hoareFind { |k, function, left, right|
1069                 var i,j,p,r,l;
1071                 if (function.isNil) { function = { | a, b | a < b } };
1073                 i = left  ?  0;
1074                 j = right ?? {this.size-1};
1076                 while{ i < j }{
1077                         p = this[k];
1078                         # l, r = this.hoarePartition(i,j,p, function);
1079                         if(r < k, {
1080                                 // kth smallest is in right split
1081                                 i = l;
1082                         });
1083                         if(k < l, {
1084                                 // kth smallest is in left split
1085                                 j = r;
1086                         });
1087                 };
1088                 // The desired element is in desired position
1089                 ^this[k];
1090         }
1092         // In-place partitioning method used by hoareFind.
1093         // Note: for efficiency this doesn't check that function is defined, so you
1094         // must supply a function! See hoareFind for example
1095         hoarePartition { |l0, r0, p, function|
1096                 var l, r, tmp;
1098                 l = l0;
1099                 r = r0;
1101                 while({ l <= r }, {
1102                         // left_scan
1103                         while { (l < this.size) and: { function.value(this[l], p) } }{
1104                                 l = l + 1;
1105                         };
1106                         // right_scan
1107                         while { (r >= 0) and: { function.value(p, this[r]) } }{
1108                                 r = r - 1;
1109                         };
1110                         // check and exchange
1111                         if(l <= r){
1112                                 tmp = this[l];
1113                                 this[l] = this[r];
1114                                 this[r] = tmp;
1115                                 // then
1116                                 l = l + 1;
1117                                 r = r - 1;
1118                         };
1119                 });
1121                 ^[l,r];
1122         }
1125         // streaming
1126         *streamContents { arg function;
1127                 var stream;
1128                 stream = CollStream.on(this.new(100));
1129                 function.value(stream);
1130                 ^stream.contents
1131         }
1132         *streamContentsLimit { arg function, limit=2000;
1133                 var stream;
1134                 stream = LimitedWriteStream.on(this.new(100 min: limit));
1135                 stream.limit_(limit).limitFunc_({ ^stream.contents });
1136                 function.value(stream);
1137                 ^stream.contents
1138         }
1140         wrapAt { arg index;
1141                 index = index % this.size;
1142                 ^this.at(index)
1143         }
1144         wrapPut { arg index, value;
1145                 index = index % this.size;
1146                 ^this.put(index, value)
1147         }
1148         reduce { arg operator;
1149                 var once = true, result;
1150                 if(this.size==1){ ^this[0] };
1151                 this.doAdjacentPairs {|a, b|
1152                         if (once) {
1153                                 once = false;
1154                                 result = operator.applyTo(a, b);
1155                         }{
1156                                 result =  operator.applyTo(result, b);
1157                         };
1158                 };
1159                 ^result
1160         }
1161         join { arg joiner;
1162                 ^String.streamContents { arg stream;
1163                         var stop;
1164                         if(joiner.isNil) {
1165                                 this.do { arg item; stream << item };
1166                         } {
1167                                 stop = this.size - 1;
1168                                 this.do { arg item,i;
1169                                         stream << item;
1170                                         if(i < stop) { stream << joiner };
1171                                 };
1172                         }
1173                 }
1174         }
1177         // TempoClock play quantization
1178         nextTimeOnGrid { arg clock;
1179                 ^clock.nextTimeOnGrid(*this);
1180         }
1182         // we break up the array so that missing elements are set to nil in the Quant
1183         asQuant { ^Quant(*this) }
1185 //      asUGenInput { ^this.asArray.asUGenInput }
1187         schedBundleArrayOnClock { |clock, bundleArray, lag = 0, server, latency|
1188                 latency = latency ? server.latency;
1189                 if (lag != 0) {
1190                         lag = lag.asArray;
1191                         this.do { |time, i|
1192                                 clock.sched(time, {
1193                                                 SystemClock.sched(lag.wrapAt(i), {
1194                                                         server.sendBundle(latency, bundleArray.wrapAt(i)) })
1195                                 })
1196                         }
1197                 } {
1198                         this.do { |time, i|
1199                                 clock.sched(time, {
1200                                                 server.sendBundle(latency, bundleArray.wrapAt(i))
1201                                 })
1202                         }
1203                 }
1204         }