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