Temporary tag for this failure. Updated CI spec coming.
[rbx.git] / kernel / core / array.rb
blobe0addda078c7ba53af057ee553092ea3e89e77f3
1 # depends on: class.rb enumerable.rb tuple.rb kernel.rb
3 ##
4 # Arrays are ordered, integer-indexed collections of any object.  Array
5 # indexing starts at 0, as in C or Java.  A negative index is assumed to be
6 # relative to the end of the array---that is, an index of -1 indicates the
7 # last element of the array, -2 is the next to last element in the array, and
8 # so on.
10 # Arrays can be created with the <tt>[]</tt> syntax, or via <tt>Array.new</tt>.
12 class Array
13   def total=(n) ; @total = n ; end
14   def tuple=(t) ; @tuple = t ; end
15   def start=(s) ; @start = s ; end
17   include Enumerable
19   # The flow control for many of these methods is
20   # pretty evil due to how MRI works. There is
21   # also a lot of duplication of code due to very
22   # subtle processing differences and, in some
23   # cases, to avoid mutual dependency. Apologies.
26   # Returns a new Array populated with the given objects
27   def self.[](*args)
28     new args
29   end
31   def self.allocate
32     ary = super()
33     ary.start = 0
34     ary.total = 0
35     ary.tuple = Tuple.new 8
36     ary
37   end
39   # Creates a new Array. Without arguments, an empty
40   # Array is returned. If the only argument is an object
41   # that responds to +to_ary+, a copy of that Array is
42   # created. Otherwise the first argument is the size
43   # of the new Array (default 0.) The second argument
44   # may be an object used to fill the Array up to the
45   # size given (the same object, not a copy.) Alternatively,
46   # a block that takes the numeric index can be given and
47   # will be run size times to fill the Array with its
48   # result. The block supercedes any object given. If
49   # neither is provided, the Array is filled with nil.
50   def initialize(*args)
51     raise ArgumentError, "Wrong number of arguments, #{args.size} for 2" if args.size > 2
53     unless args.empty?
54       if args.size == 1 and (args.first.__kind_of__ Array or args.first.respond_to? :to_ary)
55         ary = Type.coerce_to args.first, Array, :to_ary
57         tuple = Tuple.new(ary.size + 10)
58         @total = ary.size
59         tuple.copy_from ary.tuple, ary.start, 0
60         @tuple = tuple
61       else
62         count = Type.coerce_to args.first, Fixnum, :to_int
63         raise ArgumentError, "Size must be positive" if count < 0
64         obj = args[1]
66         @tuple = Tuple.new(count + 10)
67         @total = count
69         if block_given?
70           count.times { |i| @tuple.put i, yield(i) }
71         else
72           count.times { |i| @tuple.put i, obj }
73         end
74       end
75     end
77     self
78   end
80   private :initialize
82   # Element reference, returns the element at the given index or
83   # a subarray starting from the index continuing for length
84   # elements or returns a subarray from range elements. Negative
85   # indices count from the end. Returns nil if the index or subarray
86   # request cannot be completed. Array#slice is synonymous with #[].
87   # Subclasses return instances of themselves.
88   def [](one, two = nil)
89     Ruby.primitive :array_aref
91     # Normalise the argument variants
92     start, finish, count, simple, is_range = nil, nil, nil, false, false
94     if one.kind_of? Range
95       is_range = true
96       start, finish = one.begin, one.end
97     elsif two
98       start, count = one, Type.coerce_to(two, Fixnum, :to_int)
99       return nil if count < 0       # No need to go further
100     else
101       start, finish, simple = one, one, true
102     end
104     # Convert negative indices
105     start = Type.coerce_to start, Fixnum, :to_int
106     start += @total if start < 0
108     if simple
109       return nil if start < 0 or start >= @total
110       return @tuple.at(@start + start)
112     # ONE past end only, MRI compat
113     elsif start == @total
114       return self.class.new
116     elsif start < 0 or start >= @total
117       return nil
118     end
121     finish = Type.coerce_to finish, Fixnum, :to_int if finish
122     finish = (start + count - 1) if count    # For non-ranges
124     finish += @total if finish < 0
126     finish -= 1 if is_range and one.exclude_end?
128     # Going past the end is ignored (sort of)
129     finish = (@total - 1) if finish >= @total
131     if finish < 0
132       return self.class.new if is_range
133       return nil
134     end
136     out = self.class.new
138     return out if finish < start or count == 0
140     start.upto(finish) { |i| out << at(i) }
141     out
142   end
144   alias_method :slice, :[]
146   def []=(idx, ent, *args)
147     Ruby.primitive :array_aset
149     cnt = nil
150     if args.size != 0
151       cnt = ent.to_int
152       ent = args[0]             # 2nd arg (cnt) is the optional one!
153     end
155     # Normalise Ranges
156     if idx.is_a?(Range)
157       if cnt
158         raise ArgumentError, "Second argument invalid with a range"
159       end
161       unless idx.first.respond_to?(:to_int)
162         raise TypeError, "can't convert #{idx.first.class} into Integer"
163       end
165       unless idx.last.respond_to?(:to_int)
166         raise TypeError, "can't convert #{idx.last.class} into Integer"
167       end
169       lst = idx.last.to_int
170       if lst < 0
171         lst += @total
172       end
173       lst += 1 unless idx.exclude_end?
175       idx = idx.first.to_int
176       if idx < 0
177         idx += @total
178         raise RangeError if idx < 0
179       end
181       # m..n, m > n allowed
182       lst = idx if idx > lst
184       cnt = lst - idx
185     end
187     idx = idx.to_int
189     if idx < 0
190       idx += @total
191       raise IndexError.new("Index #{idx -= @total} out of bounds") if idx < 0
192     end
194     if cnt
195       # count < 0 not allowed
196       raise IndexError.new("Negative length #{cnt}") if cnt < 0
198       cnt = @total - idx if cnt > @total - idx # MRI seems to be forgiving here!
200       if ent.nil?
201         replacement = []
202       elsif ent.is_a?(Array)
203         replacement = ent.dup
204       elsif ent.respond_to?(:to_ary)
205         replacement = ent.to_ary
206       else
207         replacement = [ent]
208       end
210       if replacement.size > cnt
211         newtotal = @total + replacement.size - cnt
212         if newtotal > @tuple.fields - @start
213           nt = Tuple.new(newtotal + 10)
214           nt.copy_from @tuple, @start, 0 # FIXME: double copy of right part
215           @start = 0
216           @tuple = nt
217         end                     # this should be an else
218         f = @total
219         t = newtotal
220         while f > idx + cnt
221           t -= 1
222           f -= 1
223           @tuple.put(@start+t, @tuple.at(@start+f))
224         end
225         @total = newtotal
226       end
227       replacement.each_with_index { |el, i|
228         @tuple.put(@start+idx+i, el)
229       }
231       if replacement.size < cnt
232         f = @start + idx + cnt
233         t = @start + idx + replacement.size
235         # shift fields to the left
236         while f < @total
237           @tuple.put(t, @tuple.at(f))
238           t += 1
239           f += 1
240         end
242         # unset any extraneous fields
243         while t < @tuple.fields
244           @tuple.put(t, nil)
245           t += 1
246         end
248         @total -= (cnt - replacement.size)
249       end
251       return ent
252     end
254     nt = @start + idx + 1
255     reallocate(nt) if @tuple.size < nt
257     @tuple.put @start + idx, ent
258     if idx >= @total - 1
259       @total = idx + 1
260     end
261     return ent
262   end
264   # Appends the object to the end of the Array.
265   # Returns self so several appends can be chained.
266   def <<(obj)
267     nt = @start + @total + 1
268     reallocate nt if @tuple.size < nt
269     @tuple.put @start + @total, obj
270     @total += 1
271     self
272   end
274   # Creates a new Array containing only elements common to
275   # both Arrays, without duplicates. Also known as a 'set
276   # intersection'
277   def &(other)
278     other = Type.coerce_to other, Array, :to_ary
280     out, set_include = [], {}
282     other.each { |x| set_include[x] = [true, x] }
283     each { |x|
284       if set_include[x] and set_include[x].last.eql?(x)
285         out << x
286         set_include[x] = false
287       end
288     }
290     out
291   end
293   # Creates a new Array by combining the two Arrays' items,
294   # without duplicates. Also known as a 'set union.'
295   def |(other)
296     other = Type.coerce_to other, Array, :to_ary
298     out, exclude = [], {}
300     (self + other).each { |x|
301       unless exclude[x]
302         out << x
303         exclude[x] = true
304       end
305     }
307     out
308   end
310   # Repetition operator when supplied a #to_int argument:
311   # returns a new Array as a concatenation of the given number
312   # of the original Arrays. With an argument that responds to
313   # #to_str, functions exactly like #join instead.
314   def *(val)
315     if val.respond_to? :to_str
316       return join(val)
318     else
319       # Aaargh stupid MRI's stupid specific stupid error stupid types stupid
320       val = Type.coerce_to val, Fixnum, :to_int
322       raise ArgumentError, "Count cannot be negative" if val < 0
324       out = self.class.new
325       val.times { out.push(*self) }
326       out
327     end
328   end
330   # Create a concatenation of the two Arrays.
331   def +(other)
332     other = Type.coerce_to other, Array, :to_ary
333     out = []
335     each { |e| out << e }
336     other.each { |e| out << e }
338     out
339   end
341   # Creates a new Array that contains the items of the original
342   # Array that do not appear in the other Array, effectively
343   # 'deducting' those items. The matching method is Hash-based.
344   def -(other)
345     other = Type.coerce_to other, Array, :to_ary
346     out, exclude = [], {}
348     other.each { |x| exclude[x] = true }
349     each { |x| out << x unless exclude[x] }
351     out
352   end
354   # Compares the two Arrays and returns -1, 0 or 1 depending
355   # on whether the first one is 'smaller', 'equal' or 'greater'
356   # in relation to the second. Two Arrays are equal only if all
357   # their elements are 0 using first_e <=> second_e and their
358   # lengths are the same. The element comparison is the primary
359   # and length is only checked if the former results in 0's.
360   def <=>(other)
361     other = Type.coerce_to other, Array, :to_ary
363     size.times { |i|
364       return 1 unless other.size > i
366       diff = at(i) <=> other.at(i)
367       return diff if diff != 0
368     }
370     return 1 if size > other.size
371     return -1 if size < other.size
372     0
373   end
375   # The two Arrays are considered equal only if their
376   # lengths are the same and each of their elements
377   # are equal according to first_e == second_e . Both
378   # Array subclasses and to_ary objects are accepted.
379   def ==(other)
380     unless other.kind_of? Array
381       return false unless other.respond_to? :to_ary
382       other = other.to_ary
383     end
385     return false unless size == other.size
387     size.times { |i| return false unless @tuple.at(@start + i) == other.at(i) }
389     true
390   end
392   # Assumes the Array contains other Arrays and searches through
393   # it comparing the given object with the first element of each
394   # contained Array using elem == obj. Returns the first contained
395   # Array that matches (the first 'associated' Array) or nil.
396   def assoc(obj)
397     # FIX: use break when it works again
398     found, res = nil, nil
400     each { |elem|
401       if found.nil? and elem.kind_of? Array and elem.first == obj
402         found, res = true, elem
403       end
404     }
406     res
407   end
409   # Returns the element at the given index. If the
410   # index is negative, counts from the end of the
411   # Array. If the index is out of range, nil is
412   # returned. Slightly faster than +Array#[]+
413   def at(idx)
414     Ruby.primitive :array_aref
415     idx = Type.coerce_to idx, Fixnum, :to_int
416     idx += @total if idx < 0
418     return nil if idx < 0 or idx >= @total
420     @tuple.at @start + idx
421   end
423   # Removes all elements in the Array and leaves it empty
424   def clear()
425     @tuple = Tuple.new(1)
426     @total = 0
427     @start = 0
428     self
429   end
431   # Returns a copy of self with all nil elements removed
432   def compact()
433     dup.compact! || self
434   end
436   # Removes all nil elements from self, returns nil if no changes
437   # TODO: Needs improvement
438   def compact!()
439     i = @start
440     tot = @start + @total
442     # Low-level because pretty much anything else breaks everything
443     while i < tot
444       if @tuple.at(i).nil?
445         j = i
446         i += 1
448         while i < tot
449           if @tuple.at(i) != nil
450             @tuple.put j, @tuple.at(i)
451             j += 1
452           end
454           i += 1
455         end
457         # OK to leave tuple size larger?
458         @total = j - @start
459         return self
460       end
462       i += 1
463     end
465     nil
466   end
468   # Appends the elements in the other Array to self
469   def concat(other)
470     ary = Type.coerce_to(other, Array, :to_ary)
471     size = @total + ary.size
472     tuple = Tuple.new size
473     tuple.copy_from @tuple, @start, 0 if @total > 0
474     tuple.copy_from ary.tuple, ary.start, @total
475     @tuple = tuple
476     @start = 0
477     @total = size
478     self
479   end
481   # Stupid subtle differences prevent proper reuse in these three
483   # Removes all elements from self that are #== to the given object.
484   # If the object does not appear at all, nil is returned unless a
485   # block is provided in which case the value of running it is
486   # returned instead.
487   def delete(obj)
488     i = @start
489     tot = @start + @total
491     # Leaves the tuple to the original size still
492     while i < tot
493       if @tuple.at(i) == obj
494         j = i
495         i += 1
497         while i < tot
498           if @tuple.at(i) != obj
499             @tuple.put(j, @tuple.at(i))
500             j += 1
501           end
503           i += 1
504         end
506         @total = j - @start
507         return obj
508       end
510       i += 1
511     end
513     yield if block_given?
514   end
516   # Deletes the element at the given index and returns
517   # the deleted element or nil if the index is out of
518   # range. Negative indices count backwards from end.
519   def delete_at(idx)
520     idx = Type.coerce_to idx, Fixnum, :to_int
522     # Flip to positive and weed out out of bounds
523     idx += @total if idx < 0
524     return nil if idx < 0 or idx >= @total
526     # Grab the object and adjust the indices for the rest
527     obj = @tuple.at(@start + idx)
529     idx.upto(@total - 2) { |i| @tuple.put(@start + i, @tuple.at(@start + i + 1)) }
530     @tuple.put(@start + @total - 1, nil)
532     @total -= 1
533     obj
534   end
536   # Deletes every element from self for which block evaluates to true
537   def delete_if()
538     i = @start
539     tot = @total + @start
541     # Leaves the tuple to the original size still
542     while i < tot
543       if yield @tuple.at(i)
544         j = i
545         i += 1
547         while i < tot
548           unless yield @tuple.at(i)
549             @tuple.put(j, @tuple.at(i))
550             j += 1
551           end
553           i += 1
554         end
556         @total = j - @start
557         return self
558       end
560       i += 1
561     end
563     return self
564   end
566   # Passes each element in the Array to the given block
567   # and returns self.  We re-evaluate @total each time
568   # through the loop in case the array has changed.
569   def each()
570     i = 0
571     while i < @total
572       yield at(i)
573       i += 1
574     end
575     self
576   end
578   # Passes each index of the Array to the given block
579   # and returns self.  We re-evaluate @total each time
580   # through the loop in case the array has changed.
581   def each_index()
582     i = 0
583     while i < @total
584       yield i
585       i += 1
586     end
587     self
588   end
590   # Returns true if both are the same object or if both
591   # have the same elements (#eql? used for testing.)
592   def eql?(other)
593     return true if equal? other
594     return false unless other.kind_of?(Array)
595     return false if @total != other.size
597     each_with_index { |o, i| return false unless o.eql?(other[i]) }
599     true
600   end
602   # True if Array has no elements.
603   def empty?()
604     @total == 0
605   end
607   # Attempts to return the element at the given index. By default
608   # an IndexError is raised if the element is out of bounds. The
609   # user may supply either a default value or a block that takes
610   # the index object instead.
611   def fetch(idx, *rest)
612     raise ArgumentError, "Expected 1-2, got #{1 + rest.length}" if rest.length > 1
613     warn 'Block supercedes default object' if !rest.empty? && block_given?
615     idx, orig = Type.coerce_to(idx, Fixnum, :to_int), idx
616     idx += @total if idx < 0
618     if idx < 0 || idx >= @total
619       return yield(orig) if block_given?
620       return rest.at(0) unless rest.empty?
622       raise IndexError, "Index #{idx} out of array" if rest.empty?
623     end
625     at(idx)
626   end
628   # Fill some portion of the Array with a given element. The
629   # element to be used can be either given as the first argument
630   # or as a block that takes the index as its argument. The
631   # section that is to be filled can be defined by the following
632   # arguments. The first following argument is either a starting
633   # index or a Range. If the first argument is a starting index,
634   # the second argument can be the length. No length given
635   # defaults to rest of Array, no starting defaults to 0. Negative
636   # indices are treated as counting backwards from the end. Negative
637   # counts leave the Array unchanged. Returns self.
638   #
639   # array.fill(obj)                                -> array
640   # array.fill(obj, start [, length])              -> array
641   # array.fill(obj, range)                         -> array
642   # array.fill {|index| block }                    -> array
643   # array.fill(start [, length]) {|index| block }  -> array
644   # array.fill(range) {|index| block }             -> array
645   #
646   def fill(*args)
647     raise ArgumentError, "Wrong number of arguments" if block_given? and args.size > 2
648     raise ArgumentError, "Wrong number of arguments" if args.size > 3
650     # Normalise arguments
651     start, finish, obj = 0, (@total - 1), nil
653     obj = args.shift unless block_given?
654     one, two = args.at(0), args.at(1)
656     if one.kind_of? Range
657       raise TypeError, "Length invalid with range" if args.size > 1   # WTF, MRI, TypeError?
659       start, finish = Type.coerce_to(one.begin, Fixnum, :to_int), Type.coerce_to(one.end, Fixnum, :to_int)
661       start += @total if start < 0
662       finish += @total if finish < 0
664       if one.exclude_end?
665         return self if start == finish
666         finish -= 1
667       end
669       raise RangeError, "#{one.inspect} out of range" if start < 0
670       return self if finish < 0           # Nothing to modify
672     else
673       if one
674         start = Type.coerce_to one, Fixnum, :to_int
676         start += @total if start < 0
677         start = 0 if start < 0            # MRI comp adjusts to 0
679         if two
680           finish = Type.coerce_to two, Fixnum, :to_int
681           raise ArgumentError, "argument too big" if finish < 0 && start < finish.abs
682           return self if finish < 1       # Nothing to modify
684           finish = start + finish - 1
685         end
686       end
687     end                                   # Argument normalisation
689     # Adjust the size progressively
690     unless finish < @total
691       nt = finish + 1
692       reallocate(nt) if @tuple.size < nt
693       @total = finish + 1
694     end
696     if block_given?
697       start.upto(finish) { |i|  @tuple.put @start + i, yield(i) }
698     else
699       start.upto(finish) { |i|  @tuple.put @start + i, obj }
700     end
703     self
704   end
706   # Returns the first or first n elements of the Array.
707   # If no argument is given, returns nil if the item
708   # is not found. If there is an argument, an empty
709   # Array is returned instead.
710   def first(n = nil)
711     return at(0) unless n
713     n = Type.coerce_to n, Fixnum, :to_int
714     raise ArgumentError, "Size must be positive" if n < 0
716     Array.new(self[0...n])
717   end
719   # Recursively flatten any contained Arrays into an one-dimensional result.
720   def flatten()
721     dup.flatten! || self
722   end
724   # Flattens self in place as #flatten. If no changes are
725   # made, returns nil, otherwise self.
726   def flatten!
727     ret, out = nil, []
729     ret = recursively_flatten(self, out)
730     replace(out) if ret
731     ret
732   end
734   # Computes a Fixnum hash code for this Array. Any two
735   # Arrays with the same content will have the same hash
736   # code (similar to #eql?)
737   def hash()
738     # IMPROVE: This is a really really poor implementation of hash for an array, but
739     # it does work. It should be replaced with something much better, but I'm not sure
740     # what level it belongs at.
741     str = ""
742     each { |item| str << item.hash.to_s }
743     str.hash
744   end
746   # Returns true if the given obj is present in the Array.
747   # Presence is determined by calling elem == obj until found.
748   def include?(obj)
749     @total.times { |i| return true if @tuple.at(@start + i) == obj }
750     false
751   end
753   # Returns the index of the first element in the Array
754   # for which elem == obj is true or nil.
755   def index(obj)
756     @total.times { |i| return i if @tuple.at(@start + i) == obj }
757     nil
758   end
760   # Returns an Array populated with the objects at the given indices of the original.
761   # Range arguments are given as nested Arrays as from #[].
762   def indexes(*args)
763     warn 'Array#indexes is deprecated, use Array#values_at instead'
765     out = []
767     args.each { |a|
768       if a.kind_of? Range
769         out << self[a]
770       else
771         out << at(Type.coerce_to(a, Fixnum, :to_int))
772       end
773     }
775     out
776   end
778   alias_method :indices, :indexes
780   # For a positive index, inserts the given values before
781   # the element at the given index. Negative indices count
782   # backwards from the end and the values are inserted
783   # after them.
784   def insert(idx, *items)
785     return self if items.length == 0
787     # Adjust the index for correct insertion
788     idx = Type.coerce_to idx, Fixnum, :to_int
789     idx += (@total + 1) if idx < 0    # Negatives add AFTER the element
790     raise IndexError, "#{idx} out of bounds" if idx < 0
792     self[idx, 0] = items   # Cheat
793     self
794   end
796   # Produces a printable string of the Array. The string
797   # is constructed by calling #inspect on all elements.
798   # Descends through contained Arrays, recursive ones
799   # are indicated as [...].
800   def inspect()
801     return "[...]" if RecursionGuard.inspecting?(self)
803     out = []
804     RecursionGuard.inspect(self) do
805       each { |o|
806         out << o.inspect
807       }
808     end
810     "[#{out.join ', '}]"
811   end
813   # Generates a string from converting all elements of
814   # the Array to strings, inserting a separator between
815   # each. The separator defaults to $,. Detects recursive
816   # Arrays.
817   def join(sep = nil, method = :to_s)
818     return "" if @total == 0
819     sep ||= $,
820     begin
821       sep = sep.to_str
822     rescue NoMethodError
823       raise TypeError, "Cannot convert #{sep.inspect} to str"
824     end
826     out = ""
828     @total.times do |i|
829       elem = at(i)
831       out << sep unless i == 0
832       if elem.kind_of?(Array)
833         if RecursionGuard.inspecting?(elem)
834           out << "[...]"
835         else
836           RecursionGuard.inspect(self) do
837             out << elem.join(sep, method)
838           end
839         end
840       else
841         out << elem.__send__(method)
842       end
843     end
844     out
845   end
847   # Returns the last element or n elements of self. If
848   # the Array is empty, without a count nil is returned,
849   # otherwise an empty Array. Always returns an Array.
850   def last(n = nil)
851     return at(-1) unless n
853     n = Type.coerce_to n, Fixnum, :to_int
854     return [] if n.zero?
855     raise ArgumentError, "Number must be positive" if n < 0
857     n = size if n > size
858     Array.new self[-n..-1]
859   end
861   # Creates a new Array from the return values of passing
862   # each element in self to the supplied block.
863   def map()
864     out = []
865     each { |elem| out << yield(elem) }
866     out
867   end
869   alias_method :collect, :map
871   # Replaces each element in self with the return value
872   # of passing that element to the supplied block.
873   def map!(&block)
874     replace(map(&block))
875   end
877   alias_method :collect!, :map!
879   # Returns number of non-nil elements in self, may be zero
880   def nitems
881     sum = 0
882     each { |elem| sum += 1 unless elem.nil? }
883     sum
884   end
886   BASE_64_B2A = {}
887   def self.after_loaded
888     (00..25).each {|x| BASE_64_B2A[x] = (?A + x - 00).chr}
889     (26..51).each {|x| BASE_64_B2A[x] = (?a + x - 26).chr}
890     (52..61).each {|x| BASE_64_B2A[x] = (?0 + x - 52).chr}
891     BASE_64_B2A[62] = '+'
892     BASE_64_B2A[63] = '/'
893   end
895   ##
896   #  call-seq:
897   #     arr.pack ( aTemplateString ) -> aBinaryString
898   #
899   #  Packs the contents of <i>arr</i> into a binary sequence according to
900   #  the directives in <i>aTemplateString</i> (see the table below)
901   #  Directives ``A,'' ``a,'' and ``Z'' may be followed by a count,
902   #  which gives the width of the resulting field. The remaining
903   #  directives also may take a count, indicating the number of array
904   #  elements to convert. If the count is an asterisk
905   #  (``<code>*</code>''), all remaining array elements will be
906   #  converted. Any of the directives ``<code>sSiIlL</code>'' may be
907   #  followed by an underscore (``<code>_</code>'') to use the underlying
908   #  platform's native size for the specified type; otherwise, they use a
909   #  platform-independent size. Spaces are ignored in the template
910   #  string. See also <code>String#unpack</code>.
911   #
912   #     a = [ "a", "b", "c" ]
913   #     n = [ 65, 66, 67 ]
914   #     a.pack("A3A3A3")   #=> "a  b  c  "
915   #     a.pack("a3a3a3")   #=> "a\000\000b\000\000c\000\000"
916   #     n.pack("ccc")      #=> "ABC"
917   #
918   #  Directives for +pack+.
919   #
920   #   Directive    Meaning
921   #   ---------------------------------------------------------------
922   #       @     |  Moves to absolute position
923   #       A     |  ASCII string (space padded, count is width)
924   #       a     |  ASCII string (null padded, count is width)
925   #       B     |  Bit string (descending bit order)
926   #       b     |  Bit string (ascending bit order)
927   #       C     |  Unsigned char
928   #       c     |  Char
929   #       D, d  |  Double-precision float, native format
930   #       E     |  Double-precision float, little-endian byte order
931   #       e     |  Single-precision float, little-endian byte order
932   #       F, f  |  Single-precision float, native format
933   #       G     |  Double-precision float, network (big-endian) byte order
934   #       g     |  Single-precision float, network (big-endian) byte order
935   #       H     |  Hex string (high nibble first)
936   #       h     |  Hex string (low nibble first)
937   #       I     |  Unsigned integer
938   #       i     |  Integer
939   #       L     |  Unsigned long
940   #       l     |  Long
941   #       M     |  Quoted printable, MIME encoding (see RFC2045)
942   #       m     |  Base64 encoded string
943   #       N     |  Long, network (big-endian) byte order
944   #       n     |  Short, network (big-endian) byte-order
945   #       P     |  Pointer to a structure (fixed-length string)
946   #       p     |  Pointer to a null-terminated string
947   #       Q, q  |  64-bit number
948   #       S     |  Unsigned short
949   #       s     |  Short
950   #       U     |  UTF-8
951   #       u     |  UU-encoded string
952   #       V     |  Long, little-endian byte order
953   #       v     |  Short, little-endian byte order
954   #       w     |  BER-compressed integer\fnm
955   #       X     |  Back up a byte
956   #       x     |  Null byte
957   #       Z     |  Same as ``a'', except that null is added with *
959   def pack schema
960     # The schema is an array of arrays like [["A", "6"], ["u", "*"],
961     # ["X", ""]]. It represents the parsed form of "A6u*X".  Remove
962     # strings in the schema between # and \n
963     schema = schema.gsub(/#.*/, '')
964     schema = schema.scan(/([^\s\d\*][\d\*]*)/).flatten.map {|x|
965       x.match(/([^\s\d\*])([\d\*]*)/)[1..-1]
966     }
968     ret = ""
969     arr_idx = 0
971     schema.each do |kind, t|
972       # p :iter => [kind, t]
973       item = self[arr_idx]
974       t = nil if t.empty?
976       # MRI nil compatibilty for string functions
977       item = "" if item.nil? && kind =~ /[aAZbBhH]/
979       # if there's no item, that means there's more schema items than
980       # array items, so throw an error. All actions that DON'T
981       # increment arr_idx must occur before this test.
982       raise ArgumentError, "too few array elements" if
983         arr_idx >= self.length and kind !~ /x/i
985       case kind # TODO: switch kind to ints
986       when 'X' then
987         size = (t || 1).to_i
988         raise ArgumentError, "you're backing up too far" if size > ret.size
989         ret[-size..-1] = '' if size > 0
990       when 'x' then
991         size = (t || 1).to_i
992         ret << "\000" * size
993       when 'N' then
994         parts = []
995         4.times do                          # TODO: const?
996           parts << (item % 256).chr
997           item >>= 8
998         end
999         ret << parts.reverse.join
1000         arr_idx += 1
1001         item = nil
1002         next # HACK
1003       when 'V' then
1004         parts = []
1005         4.times do                          # TODO: const?
1006           parts << (item % 256).chr
1007           item >>= 8
1008         end
1009         ret << parts.join
1010         arr_idx += 1
1011       when 'v' then
1012         parts = []
1013         2.times do
1014           parts << (item % 256).chr
1015           item >>= 8
1016         end
1017         ret << parts.join
1018         arr_idx += 1
1019       when 'a', 'A', 'Z' then
1020         item = Type.coerce_to(item, String, :to_str)
1021         size = case t
1022                when nil
1023                  1
1024                when '*' then
1025                  item.size + (kind == "Z" ? 1 : 0)
1026                else
1027                  t.to_i
1028                end
1030         padsize = size - item.size
1031         filler  = kind == "A" ? " " : "\0"
1033         ret << item.split(//).first(size).join
1034         ret << filler * padsize if padsize > 0
1036         arr_idx += 1
1037       when 'b', 'B' then
1038         item = Type.coerce_to(item, String, :to_str)
1039         byte = 0
1040         lsb  = (kind == "b")
1041         size = case t
1042                when nil
1043                  1
1044                when '*' then
1045                  item.size
1046                else
1047                  t.to_i
1048                end
1050         bits = item.split(//).map { |c| c[0] & 01 }
1051         min = [size, item.size].min
1053         bits.first(min).each_with_index do |bit, i| # TODO: this can be cleaner
1054           i &= 07
1056           byte |= bit << (lsb ? i : 07 - i)
1058           if i == 07 then
1059             ret << byte.chr
1060             byte = 0
1061           end
1062         end
1064         # always output an incomplete byte
1065         if ((size & 07) != 0 || min != size) && item.size > 0 then
1066           ret << byte.chr
1067         end
1069         # Emulate the weird MRI spec for every 2 chars over output a \000 # FIX
1070         (item.length).step(size-1, 2) { |i| ret << 0 } if size > item.length
1072         arr_idx += 1
1073       when 'c', 'C' then
1074         size = case t
1075                when nil
1076                  1
1077                when '*' then
1078                  self.size # TODO: - arr_idx?
1079                else
1080                  t.to_i
1081                end
1083         # FIX: uhh... size is the same as length. just tests that arr_idx == 0
1084         raise ArgumentError, "too few array elements" if
1085           arr_idx + size > self.length
1087         sub = self[arr_idx...arr_idx+size]
1088         sub.map! { |o| (Type.coerce_to(o, Integer, :to_int) & 0xff).chr }
1089         ret << sub.join
1091         arr_idx += size
1092       when 'M' then
1093         # for some reason MRI responds to to_s here
1094         item = Type.coerce_to(item, String, :to_s)
1095         ret << item.scan(/.{1,73}/m).map { |line| # 75 chars per line incl =\n
1096           line.gsub(/[^ -<>-~\t\n]/) { |m| "=%02X" % m[0] } + "=\n"
1097         }.join
1098         arr_idx += 1
1099       when 'm' then # REFACTOR: merge with u
1100         item = Type.coerce_to(item, String, :to_str)
1102         ret << item.scan(/.{1,45}/m).map { |line|
1103           encoded = line.scan(/(.)(.?)(.?)/m).map { |a,b,c|
1104             a = a[0]
1105             b = b[0] || 0
1106             c = c[0] || 0
1108             [BASE_64_B2A[( a >> 2                    ) & 077],
1109              BASE_64_B2A[((a << 4) | ((b >> 4) & 017)) & 077],
1110              BASE_64_B2A[((b << 2) | ((c >> 6) & 003)) & 077],
1111              BASE_64_B2A[( c                         ) & 077]]
1112           }
1114           "#{encoded.flatten.join}\n"
1115         }.join.sub(/(A{1,2})\n\Z/) { "#{'=' * $1.size}\n" }
1117         arr_idx += 1
1118       when 'w' then
1119         item = Type.coerce_to(item, Integer, :to_i)
1120         raise ArgumentError, "can't compress negative numbers" if item < 0
1122         ret << (item & 0x7f)
1123         while (item >>= 7) > 0 do
1124           ret << ((item & 0x7f) | 0x80)
1125         end
1127         ret.reverse! # FIX - breaks anything following BER?
1128         arr_idx += 1
1129       when 'u' then # REFACTOR: merge with m
1130         item = Type.coerce_to(item, String, :to_str)
1132         # http://www.opengroup.org/onlinepubs/009695399/utilities/uuencode.html
1133         ret << item.scan(/.{1,45}/m).map { |line|
1134           encoded = line.scan(/(.)(.?)(.?)/m).map { |a,b,c|
1135             a = a[0]
1136             b = b[0] || 0
1137             c = c[0] || 0
1139             [(?\s + (( a >> 2                    ) & 077)).chr,
1140              (?\s + (((a << 4) | ((b >> 4) & 017)) & 077)).chr,
1141              (?\s + (((b << 2) | ((c >> 6) & 003)) & 077)).chr,
1142              (?\s + (( c                         ) & 077)).chr]
1143           }.flatten
1145           "#{(line.size + ?\s).chr}#{encoded.join}\n"
1146         }.join.gsub(/ /, '`')
1147         arr_idx += 1
1148       when 'i', 's', 'l', 'n', 'I', 'S', 'L' then
1149         size = case t
1150                when nil
1151                  1
1152                when '*' then
1153                  self.size
1154                else
1155                  t.to_i
1156                end
1158         native        = t && t == '_'
1159         unsigned      = (kind =~ /I|S|L/)
1160         little_endian = kind !~ /n/i && endian?(:little)
1162         raise "unsupported - fix me" if native
1164         unless native then
1165           bytes = case kind
1166                   when /n/i then 2
1167                   when /s/i then 2
1168                   when /i/i then 4
1169                   when /l/i then 4
1170                   end
1171         end
1173         size.times do |i|
1174           item = Type.coerce_to(self[arr_idx], Integer, :to_i)
1176           # MRI seems only only raise RangeError at 2**32 and above, even shorts
1177           raise RangeError, "bignum too big to convert into 'unsigned long'" if
1178             item.abs >= 2**32 # FIX: const
1180             ret << if little_endian then
1181                      item += 2 ** (8 * bytes) if item < 0
1182                      (0...bytes).map { |b| ((item >> (b * 8)) & 0xFF).chr }
1183                    else # ugly
1184                      (0...bytes).map {n=(item % 256).chr;item /= 256; n}.reverse
1185                    end.join
1186           arr_idx += 1
1187         end
1188       when 'H', 'h' then
1189         size = if t.nil?
1190                  0
1191                elsif t == "*"
1192                  item.length
1193                else
1194                  t.to_i
1195                end
1196         str = item.scan(/..?/).first(size)
1198         ret << if kind == "h" then
1199                  str.map { |b| b.reverse.hex.chr }.join
1200                else
1201                  str.map { |b| b.        hex.chr }.join
1202                end
1204         arr_idx += 1
1205       when 'U' then
1206         count = if t.nil? then
1207                   1
1208                 elsif t == "*"
1209                   self.size - arr_idx
1210                 else
1211                   t.to_i
1212                 end
1214         raise ArgumentError, "too few array elements" if
1215           arr_idx + count > self.length
1217         count.times do
1218           item = Type.coerce_to(self[arr_idx], Integer, :to_i)
1220           raise RangeError, "pack(U): value out of range" if item < 0
1222           bytes = 0
1223           f = [2 ** 7, 2 ** 11, 2 ** 16, 2 ** 21, 2 ** 26, 2 ** 31].find { |n|
1224             bytes += 1
1225             item < n
1226           }
1228           raise RangeError, "pack(U): value out of range" if f.nil?
1230           if bytes == 1 then
1231             ret << item
1232             bytes = 0
1233           end
1235           i = bytes
1237           buf = []
1238           if i > 0 then
1239             (i-1).times do
1240               buf.unshift((item | 0x80) & 0xBF)
1241               item >>= 6
1242             end
1243             # catch the highest bits - the mask depends on the byte count
1244             buf.unshift(item | ((0x3F00 >> bytes)) & 0xFC)
1245           end
1247           ret << buf.map { |n| n.chr }.join
1249           arr_idx += 1
1250         end
1251       else
1252         raise ArgumentError, "Unknown kind #{kind}"
1253       end
1254     end
1256     return ret
1257   end
1259   # Removes and returns the last element from the Array.
1260   def pop()
1261     return nil if empty?
1263     # TODO Reduce tuple if there are a lot of free slots
1265     elem = at(-1)
1266     @total -= 1
1267     elem
1268   end
1270   # Rubinius-only, better inspect representation of the Array
1271   def indented_inspect(indent = 0)
1272     # Here there be dragons. In fact, there is one jusAAAAAAAARGH
1273     str = "["
1275     sub = false
1276     i = 0
1277     lst = size - 1
1278     while i < size
1279       element = self[i]
1280       if Array === element
1281         estr = element.indented_inspect(indent + 2)
1282         if str.size > 30 or estr.size > 30
1283           if estr[0] != ?\s
1284             estr = "#{' ' * (indent + 2)}#{estr}"
1285           end
1287           str << "\n#{estr}"
1288           sub = true
1289         else
1290           str << estr
1291         end
1292       else
1293         str << element.inspect
1294       end
1296       str << ", " unless i == lst
1297       i += 1
1298     end
1300     if sub
1301       str << "\n#{' ' * indent}]"
1302     else
1303       str << "]"
1304     end
1306     if sub
1307       return "#{' ' * indent}#{str}"
1308     end
1310     return str
1311   end
1313   # Appends the given object(s) to the Array and returns
1314   # the modified self.
1315   def push(*args)
1316     args.each { |ent| self << ent }
1317     self
1318   end
1320   # Searches through contained Arrays within the Array,
1321   # comparing obj with the second element of each using
1322   # elem == obj. Returns the first matching Array, nil
1323   # on failure. See also Array#assoc.
1324   def rassoc(obj)
1325     # FIX: Use break when it works
1326     found, res = nil, nil
1328     each { |elem|
1329       if found.nil? and elem.kind_of? Array and elem.at(1) == obj
1330         res, found = elem, true
1331       end
1332     }
1334     res
1335   end
1337   # Returns a new Array by removing items from self for
1338   # which block is true. An Array is also returned when
1339   # invoked on subclasses. See #reject!
1340   def reject(&block)
1341     Array.new(self).reject!(&block) || self
1342   end
1344   # Equivalent to #delete_if except that returns nil if
1345   # no changes were made.
1346   def reject!(&block)
1347     was = length
1348     self.delete_if(&block)
1350     self if was != length     # Too clever?
1351   end
1353   # Replaces contents of self with contents of other,
1354   # adjusting size as needed.
1355   def replace(other)
1356     other = Type.coerce_to other, Array, :to_ary
1358     @tuple = other.tuple.dup
1359     @total = other.total
1360     @start = other.start
1361     self
1362   end
1364   # Returns a new Array or subclass populated from self
1365   # but in reverse order.
1366   def reverse()
1367     dup.reverse!
1368   end
1370   # Reverses the order of elements in self. Returns self
1371   # even if no changes are made
1372   def reverse!
1373     return self unless @total > 1
1375     tuple = Tuple.new @total
1376     i = 0
1377     (@start + @total - 1).downto(@start) do |j|
1378       tuple.put i, @tuple.at(j)
1379       i += 1
1380     end
1382     @tuple = tuple
1383     @start = 0
1385     self
1386   end
1388   # Goes through the Array back to front and yields
1389   # each element to the supplied block. Returns self.
1390   def reverse_each()
1391     (@total - 1).downto(0) { |i| yield(at(i)) }
1392     self
1393   end
1395   # Returns the index of the last element in the Array
1396   # for which elem == obj is true.
1397   def rindex(obj)
1398     (@total - 1).downto(0) { |i| return i if at(i) == obj }
1399     nil
1400   end
1402   # Removes and returns the first element in the
1403   # Array or nil if empty. All other elements are
1404   # moved down one index.
1405   def shift()
1406     return nil if @total == 0
1408     obj = @tuple.at(@start)
1409     @start += 1
1410     @total -= 1
1412     obj
1413   end
1415   # Deletes the element(s) given by an index (optionally with a length)
1416   # or by a range. Returns the deleted object, subarray, or nil if the
1417   # index is out of range. Equivalent to:
1418   def slice!(*args)
1419     out = self[*args]
1420     if !(Range === args[0])
1421       # make sure that negative values are not passed through to the
1422       # []= assignment
1423       args[0] = Type.coerce_to args[0], Integer, :to_int
1424       args[0] = args[0] + self.length if args[0] < 0
1425       # This is to match the MRI behaviour of not extending the array
1426       # with nil when specifying an index greater than the length
1427       # of the array.
1428       if args.size == 1
1429         return out unless args[0] >= 0 && args[0] < self.length
1430         args << 1
1431       end
1432     end
1433     self[*args] = []
1434     out
1435   end
1437   # Returns a new Array created by sorting elements in self. Comparisons
1438   # to sort are either done using <=> or a block may be provided. The
1439   # block must take two parameters and return -1, 0, 1 depending on
1440   # whether the first parameter is smaller, equal or larger than the
1441   # second parameter.
1442   def sort(&block)
1443     dup.sort!(&block)
1444   end
1446   # Sorts this Array in-place. See #sort.
1447   def sort!(&block)
1448     return self unless @total > 1
1450     if (@total - @start) < 6
1451       if block
1452         isort_block! @start, (@total - 1), block
1453       else
1454         isort! @start, (@total - 1)
1455       end
1456     else
1457       if block
1458         qsort_block! block
1459       else
1460         qsort!
1461       end
1462     end
1464     self
1465   end
1467   # Returns self except on subclasses which are converted
1468   # or 'upcast' to Arrays.
1469   def to_a()
1470     if self.class == Array
1471       self
1472     else
1473       Array.new(self[0..-1])
1474     end
1475   end
1477   # Returns self
1478   def to_ary()
1479     self
1480   end
1482   # Produces a string by joining all elements without a
1483   # separator. See #join
1484   def to_s()
1485     self.join
1486   end
1488   # Treats all elements as being Arrays of equal lengths and
1489   # transposes their rows and columns so that the first contained
1490   # Array in the result includes the first elements of all the
1491   # Arrays and so on.
1492   def transpose()
1493     return [] if empty?
1495     out, max = [], nil
1497     each { |ary|
1498       ary = Type.coerce_to ary, Array, :to_ary
1499       max ||= ary.size
1501       # Catches too-large as well as too-small (for which #fetch would suffice)
1502       raise IndexError, "All arrays must be same length" if ary.size != max
1504       ary.size.times { |i| (out[i] ||= []) << ary.at(i) }
1505     }
1507     out
1508   end
1510   # Returns a new Array by removing duplicate entries
1511   # from self. Equality is determined by using a Hash
1512   def uniq()
1513     seen, out = {}, self.class.new
1515     each { |elem|
1516       out << elem unless seen[elem]
1517       seen[elem] = true
1518     }
1520     out
1521   end
1523   # Removes duplicates from the Array in place as #uniq
1524   def uniq!()
1525     ary = uniq
1526     replace(ary) if size != ary.size
1527   end
1529 #  # Inserts the element to the front of the Array and
1530 #  # moves the other elements up by one index.
1531 #  def unshift(*val)
1532 #    raise TypeError, "Array is frozen" if frozen?
1533 #    return self if val.empty?
1535 #    self[0, 0] = val
1536 #    self
1537 #  end
1539   # Returns a new Array populated from the elements in
1540   # self corresponding to the given selector(s.) The
1541   # arguments may be one or more integer indices or
1542   # Ranges.
1543   def values_at(*args)
1544     out = []
1546     args.each { |elem|
1547       # Cannot use #[] because of subtly different errors
1548       if elem.kind_of? Range
1549         finish = elem.last
1550         start = elem.first
1552         start, finish = Type.coerce_to(start, Fixnum, :to_int), Type.coerce_to(finish, Fixnum, :to_int)
1554         start += @total if start < 0
1555         next if start < 0
1557         finish += @total if finish < 0
1558         finish -= 1 if elem.exclude_end?
1559         finish = @total unless finish < @total
1561         next if finish < start
1563         start.upto(finish) { |i| out << at(i) }
1565       else
1566         i = Type.coerce_to elem, Fixnum, :to_int
1567         out << at(i)
1568       end
1569     }
1571     out
1572   end
1574   # Interleaves all given :to_ary's so that the n-th element of each
1575   # Array is inserted into the n-th subarray of the returned Array.
1576   # If a block is provided, then each subarray is passed to it
1577   # instead. The maximum number of subarrays and therefore elements
1578   # used is the size of self. Missing indices are filled in with
1579   # nils and any elements past self.size in the other Arrays are
1580   # ignored.
1581   def zip(*others)
1582     out = Array.new(size) { [] }
1583     others = others.map { |ary| ary.to_a }
1585     size.times do |i|
1586       slot = out.at(i)
1587       slot << @tuple.at(@start + i)
1588       others.each { |ary| slot << ary.at(i) }
1589     end
1591     if block_given?
1592       out.each { |ary| yield ary }
1593       return nil
1594     end
1596     out
1597   end
1599   def size
1600     @total
1601   end
1603   def length
1604     @total
1605   end
1607   def unshift(*values)
1608     while values.size > 0 && @start > 0
1609       @start -= 1
1610       @total += 1
1611       @tuple.put @start, values.pop
1612     end
1614     @tuple = @tuple.shifted(values.size)
1616     values.each_with_index do |val, i|
1617       @tuple.put i, val
1618     end
1620     @total += values.size
1621     self
1622   end
1624   # Exactly the same as #replace but private
1625   def initialize_copy(other)
1626     replace other
1627   end
1629   private :initialize_copy
1631   # Reallocates the internal Tuple to accommodate at least given size
1632   def reallocate(at_least)
1633     return if at_least < @tuple.size
1635     new_size = @tuple.size * 2
1637     if new_size < at_least
1638       new_size = at_least
1639     end
1641     tuple = Tuple.new(new_size)
1643     tuple.copy_from @tuple, @start, 0     # Swap over old data
1645     @tuple = tuple
1646     @start = 0
1647   end
1649   private :reallocate
1651   # Helper to recurse through flattening since the method
1652   # is not allowed to recurse itself. Detects recursive structures.
1653   def recursively_flatten(array, out, recursive_placeholder = Undefined)
1654     if RecursionGuard.inspecting?(array)
1655       if recursive_placeholder.equal? Undefined
1656         raise ArgumentError, "tried to flatten recursive array"
1657       else
1658         out << recursive_placeholder
1659         return nil
1660       end
1661     end
1663     ret = nil
1664     array.each { |o|
1665       if o.kind_of? Array
1666         RecursionGuard.inspect(array) do
1667           recursively_flatten(o, out, recursive_placeholder)
1668           ret = self
1669         end
1670       else
1671         out << o
1672       end
1673     }
1675     ret
1676   end
1678   private :recursively_flatten
1680   def remove_outer_arrays(array=self)
1681     if RecursionGuard.inspecting?(array)
1682       array
1683     elsif array.size == 1 && array.first.kind_of?(Array)
1684       new_array = nil
1685       RecursionGuard.inspect(array) do
1686         new_array = remove_outer_arrays(array.first)
1687       end
1688       new_array
1689     else
1690       array
1691     end
1692   end
1694   private :remove_outer_arrays
1696   ISORT_THRESHOLD   = 7
1697   MEDIAN_THRESHOLD  = 11
1699   # In-place non-recursive sort between the given indexes.
1700   def qsort!()
1701     # Stack stores the indexes that still need sorting
1702     stack = []
1703     left_end, right_end = @start, (@total - 1)
1705     # We are either processing a 'new' partition or one that
1706     # was saved to stack earlier.
1707     while true
1708       left, right = left_end, (right_end - 1)   # Leave room for pivot
1709       eqls_left, eqls_right = (left_end - 1), right_end
1711       # Choose pivot from median
1712       # CAUTION: This is NOT the same as #qsort_block!
1713       middle = left_end + ((right_end - left_end) / 2)
1714       low, mid, hi = @tuple.at(left_end), @tuple.at(middle), @tuple.at(right_end)
1716       segment_size = right_end - left_end
1718       # "Heuristic" to avoid problems with reverse-sorted
1719       if segment_size > 1000 and (low <=> mid) == 1 and (mid <=> hi) == 1
1720         semi_left = @tuple.at(left_end + ((middle - left_end) / 2))
1721         semi_right = @tuple.at(middle + ((right_end - middle) / 2))
1723         if (low <=> semi_left) == 1 and (semi_left <=> mid) == 1 and
1724            (mid <=> semi_right) == 1 and (semi_right <=> hi) == 1
1726           r = segment_size / 4
1727           while r > 0
1728             @tuple.swap(rand(segment_size), rand(segment_size))
1729             r -= 1
1730           end
1732           middle += (right_end - middle) / 2
1733         end
1734       end
1736       # These can be reordered which may help with sorting randomly
1737       # distributed elements at the cost of presorted. Be sure to
1738       # mark down the correct order though..
1739       @tuple.swap(left_end, right_end)  if (hi <=> low) < 0
1740       @tuple.swap(left_end, middle)     if (mid <=> low) < 0
1741       @tuple.swap(middle, right_end)    if (hi <=> mid) < 0
1743       pivot = @tuple.at(middle)
1744       @tuple.swap(right_end, middle)  # Known position to help partition three ways
1746       # Partition
1747       while true
1748         while left < right_end
1749           case @tuple.at(left) <=> pivot
1750           when -1
1751             left += 1
1752           when 0
1753             @tuple.swap(left, (eqls_left += 1))
1754             left += 1
1755           else
1756             break
1757           end
1758         end
1760         while right > left_end
1761           case @tuple.at(right) <=> pivot
1762           when 1
1763             right -= 1
1764           when 0
1765             @tuple.swap(right, (eqls_right -= 1))
1766             right -= 1
1767           else
1768             break
1769           end
1770         end
1772         break if left >= right
1773         @tuple.swap(left, right)
1774       end
1776       # Move pivot back to the middle
1777       @tuple.swap(left, right_end)
1778       left, right = (left - 1), (left + 1)
1780       # Move the stashed == pivot elements back to the middle
1781       while eqls_left >= left_end
1782         @tuple.swap(eqls_left, left)
1783         left -= 1
1784         eqls_left -= 1
1785       end
1787       unless right >= right_end
1788         while eqls_right < right_end
1789           @tuple.swap(eqls_right, right)
1790           right += 1
1791           eqls_right += 1
1792         end
1793       end
1795       # Continue processing the now smaller partitions or if
1796       # done with this segment, restore a stored one from the
1797       # stack until nothing remains either way.
1798       left_size, right_size = (left - left_end), (right_end - right)
1800       # Insertion sort is faster at anywhere below 7-9 elements
1801       if left_size < ISORT_THRESHOLD
1802         isort! left_end, left
1804         # We can restore next saved if both of these are getting sorted
1805         if right_size < ISORT_THRESHOLD
1806           isort! right, right_end
1807           break if stack.empty?       # Completely done, no stored ones left either
1808           left_end, right_end = stack.pop
1809         else
1810           left_end = right
1811         end
1813       elsif right_size < ISORT_THRESHOLD
1814         isort! right, right_end
1815         right_end = left
1817       # Save whichever is the larger partition and do the smaller first
1818       else
1819         if left_size > right_size
1820           stack.push [left_end, left]
1821           left_end = right
1822         else
1823           stack.push [right, right_end]
1824           right_end = left
1825         end
1826       end
1827     end
1828   end
1830   # In-place non-recursive sort between the given indexes using a block.
1831   def qsort_block!(block)
1832     # Stack stores the indexes that still need sorting
1833     stack = []
1834     left_end, right_end = @start, (@total - 1)
1836     # We are either processing a 'new' partition or one that
1837     # was saved to stack earlier.
1838     while true
1839       left, right = left_end, (right_end - 1)   # Leave room for pivot
1840       eqls_left, eqls_right = (left_end - 1), right_end
1842       # Choose pivot from median
1843       # CAUTION: This is NOT the same as #qsort!
1844       middle = left_end + ((right_end - left_end) / 2)
1845       low, mid, hi = @tuple.at(left_end), @tuple.at(middle), @tuple.at(right_end)
1847       segment_size = right_end - left_end
1849       # "Heuristic" for reverse-sorted
1850       if segment_size > 1000 and block.call(low, mid) == 1 and block.call(mid, hi) == 1
1851         semi_left = @tuple.at(left_end + ((middle - left_end) / 2))
1852         semi_right = @tuple.at(middle + ((right_end - middle) / 2))
1854         if block.call(low, semi_left) == 1 and block.call(semi_left, mid) == 1 and
1855            block.call(mid, semi_right) == 1 and block.call(semi_right, hi) == 1
1857           r = segment_size / 4
1858           while r > 0
1859             @tuple.swap(rand(segment_size), rand(segment_size))
1860             r -= 1
1861           end
1863           middle += (right_end - middle) / 2
1864         end
1865       end
1867       # These can be reordered which may help with sorting randomly
1868       # distributed elements at the cost of presorted. Be sure to
1869       # mark down the correct order though..
1870       @tuple.swap(left_end, right_end)  if block.call(hi, low)  < 0
1871       @tuple.swap(left_end, middle)     if block.call(mid, low) < 0
1872       @tuple.swap(middle, right_end)    if block.call(hi, mid)  < 0
1874       pivot = @tuple.at(middle)
1875       @tuple.swap(right_end, middle)  # Known position to help partition three ways
1877       # Partition
1878       while true
1879         while left < right_end
1880           case block.call(@tuple.at(left), pivot)
1881           when -1
1882             left += 1
1883           when 0
1884             @tuple.swap(left, (eqls_left += 1))
1885             left += 1
1886           else
1887             break
1888           end
1889         end
1891         while right > left_end
1892           case block.call(@tuple.at(right), pivot)
1893           when 1
1894             right -= 1
1895           when 0
1896             @tuple.swap(right, (eqls_right -= 1))
1897             right -= 1
1898           else
1899             break
1900           end
1901         end
1903         break if left >= right
1904         @tuple.swap(left, right)
1905       end
1907       # Move pivot back to the middle
1908       @tuple.swap(left, right_end)
1909       left, right = (left - 1), (left + 1)
1911       # Move the stashed == pivot elements back to the middle
1912       while eqls_left >= left_end
1913         @tuple.swap(eqls_left, left)
1914         left -= 1
1915         eqls_left -= 1
1916       end
1918       unless right >= right_end
1919         while eqls_right < right_end
1920           @tuple.swap(eqls_right, right)
1921           right += 1
1922           eqls_right += 1
1923         end
1924       end
1926       # Continue processing the now smaller partitions or if
1927       # done with this segment, restore a stored one from the
1928       # stack until nothing remains either way.
1929       left_size, right_size = (left - left_end), (right_end - right)
1931       # Insertion sort is faster at anywhere below 7-9 elements
1932       if left_size < ISORT_THRESHOLD
1933         isort_block! left_end, left, block
1935         # We can restore next saved if both of these are getting sorted
1936         if right_size < ISORT_THRESHOLD
1937           isort_block! right, right_end, block
1938           break if stack.empty?       # Completely done, no stored ones left either
1939           left_end, right_end = stack.pop
1940         else
1941           left_end = right
1942         end
1944       elsif right_size < ISORT_THRESHOLD
1945         isort_block! right, right_end, block
1946         right_end = left
1948       # Save whichever is the larger partition and do the smaller first
1949       else
1950         if left_size > right_size
1951           stack.push [left_end, left]
1952           left_end = right
1953         else
1954           stack.push [right, right_end]
1955           right_end = left
1956         end
1957       end
1958     end
1959   end
1961   # Insertion sort in-place between the given indexes.
1962   def isort!(left, right)
1963     i = left + 1
1965     while i <= right
1966       j = i
1968       while j > 0 and (@tuple.at(j - 1) <=> @tuple.at(j)) > 0
1969         @tuple.swap(j, (j - 1))
1970         j -= 1
1971       end
1973       i += 1
1974     end
1975   end
1977   # Insertion sort in-place between the given indexes using a block.
1978   def isort_block!(left, right, block)
1979     i = left + 1
1981     while i <= right
1982       j = i
1984       while j > 0 and block.call(@tuple.at(j - 1), @tuple.at(j)) > 0
1985         @tuple.swap(j, (j - 1))
1986         j -= 1
1987       end
1989       i += 1
1990     end
1991   end
1993   private :qsort
1994   private :isort
1995   private :qsort_block
1996   private :isort_block