class Erlang::Tuple

A `Tuple` is an ordered, integer-indexed collection of objects. Like Ruby's `Array`, `Tuple` indexing starts at zero and negative indexes count back from the end.

`Tuple` has a similar interface to `Array`. The main difference is methods that would destructively update an `Array` (such as {#insert} or {#delete_at}) instead return new `Tuple`s and leave the existing one unchanged.

### Creating New Tuples

Erlang::Tuple.new([:first, :second, :third])
Erlang::Tuple[1, 2, 3, 4, 5]

### Retrieving Elements from Tuples

tuple = Erlang::Tuple[1, 2, 3, 4, 5]

tuple[0]      # => 1
tuple[-1]     # => 5
tuple[0,3]    # => Erlang::Tuple[1, 2, 3]
tuple[1..-1]  # => Erlang::Tuple[2, 3, 4, 5]
tuple.first   # => 1
tuple.last    # => 5

### Creating Modified Tuples

tuple.add(6)            # => Erlang::Tuple[1, 2, 3, 4, 5, 6]
tuple.insert(1, :a, :b) # => Erlang::Tuple[1, :a, :b, 2, 3, 4, 5]
tuple.delete_at(2)      # => Erlang::Tuple[1, 2, 4, 5]
tuple + [6, 7]          # => Erlang::Tuple[1, 2, 3, 4, 5, 6, 7]

Licensing

Portions taken and modified from github.com/hamstergem/hamster

Copyright (c) 2009-2014 Simon Harris

Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:

The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

Constants

BITS_PER_LEVEL

@private

BLOCK_SIZE

@private

INDEX_MASK

@private

Attributes

arity[R]

Return the number of elements in this `Tuple` @return [Integer]

length[R]

Return the number of elements in this `Tuple` @return [Integer]

size[R]

Return the number of elements in this `Tuple` @return [Integer]

Public Class Methods

[](*elements) click to toggle source

Create a new `Tuple` populated with the given elements. @return [Tuple]

# File lib/erlang/tuple.rb, line 82
def [](*elements)
  return new(elements.freeze)
end
alloc(root, size, levels) click to toggle source

“Raw” allocation of a new `Tuple`. Used internally to create a new instance quickly after building a modified trie.

@return [Tuple] @private

# File lib/erlang/tuple.rb, line 99
def alloc(root, size, levels)
  obj = allocate
  obj.instance_variable_set(:@root, root)
  obj.instance_variable_set(:@size, size)
  obj.instance_variable_set(:@levels, levels)
  return obj
end
compare(a, b) click to toggle source
# File lib/erlang/tuple.rb, line 107
def compare(a, b)
  raise ArgumentError, "'a' must be of Erlang::Tuple type" if not a.kind_of?(Erlang::Tuple)
  raise ArgumentError, "'b' must be of Erlang::Tuple type" if not b.kind_of?(Erlang::Tuple)
  c = a.size <=> b.size
  i = 0
  while c == 0 and i < a.size and i < b.size
    c = Erlang.compare(a[i], b[i])
    i += 1
  end
  return c
end
empty() click to toggle source

Return an empty `Tuple`. If used on a subclass, returns an empty instance of that class.

@return [Tuple]

# File lib/erlang/tuple.rb, line 90
def empty
  return @empty ||= self.new
end
new(elements=[].freeze) click to toggle source
# File lib/erlang/tuple.rb, line 120
def initialize(elements=[].freeze)
  elements = elements.to_a.map { |element| Erlang.from(element) }
  if elements.size <= 32
    elements = elements.dup.freeze if !elements.frozen?
    @root, @size, @levels = elements, elements.size, 0
  else
    root, size, levels = elements, elements.size, 0
    while root.size > 32
      root = root.each_slice(32).to_a
      levels += 1
    end
    @root, @size, @levels = root.freeze, size, levels
  end
end

Public Instance Methods

*(times) click to toggle source

Repetition. Return a new `Tuple` built by concatenating `times` copies of this one together.

@example

Erlang::Tuple["A", "B"] * 3
# => Erlang::Tuple["A", "B", "A", "B", "A", "B"]

@param times [Integer] The number of times to repeat the elements in this tuple @return [Tuple]

# File lib/erlang/tuple.rb, line 806
def *(times)
  return self.class.empty if times == 0
  return self if times == 1
  result = (to_a * times)
  return result.is_a?(Array) ? self.class.new(result) : result
end
+(other) click to toggle source

Return a new `Tuple` built by concatenating this one with `other`. `other` can be any object which is convertible to an `Array` using `#to_a`.

@example

Erlang::Tuple["A", "B", "C"] + ["D", "E"]
# => Erlang::Tuple["A", "B", "C", "D", "E"]

@param other [Enumerable] The collection to concatenate onto this tuple @return [Tuple]

# File lib/erlang/tuple.rb, line 652
def +(other)
  other = Erlang.from(other)
  other = other.to_a
  other = other.dup if other.frozen?
  return replace_suffix(@size, other)
end
Also aliased as: concat
<<(element)
Alias for: add
==(other)
Alias for: eql?
[](arg, length = (missing_length = true))
Alias for: slice
add(element) click to toggle source

Return a new `Tuple` with `element` added after the last occupied position.

@example

Erlang::Tuple[1, 2].add(99)  # => Erlang::Tuple[1, 2, 99]

@param element [Object] The object to insert at the end of the tuple @return [Tuple]

# File lib/erlang/tuple.rb, line 169
def add(element)
  return update_root(@size, Erlang.from(element))
end
Also aliased as: <<, push
assoc(obj) click to toggle source

Assumes all elements are nested, indexable collections, and searches through them, comparing `obj` with the first element of each nested collection. Return the first nested collection which matches, or `nil` if none is found. Behaviour is undefined when elements do not meet assumptions (i.e. are not indexable collections).

@example

t = Erlang::Tuple[Erlang::Tuple["A", 10], Erlang::Tuple["B", 20], Erlang::Tuple["C", 30]]
t.assoc("B")  # => Erlang::Tuple["B", 20]

@param obj [Object] The object to search for @return [Object]

# File lib/erlang/tuple.rb, line 1294
def assoc(obj)
  obj = Erlang.from(obj)
  each do |array|
    next if !array.respond_to?(:[])
    return array if obj == array[0]
  end
  return nil
end
at(index)
Alias for: get
bsearch() { |val| ... } click to toggle source

Finds a value from this `Tuple` which meets the condition defined by the provided block, using a binary search. The tuple must already be sorted with respect to the block. See Ruby's `Array#bsearch` for details, behaviour is equivalent.

@example

t = Erlang::Tuple[1, 3, 5, 7, 9, 11, 13]
# Block returns true/false for exact element match:
t.bsearch { |e| e > 4 }      # => 5
# Block returns number to match an element in 4 <= e <= 7:
t.bsearch { |e| 1 - e / 4 }  # => 7

@yield Once for at most `log n` elements, where `n` is the size of the

tuple. The exact elements and ordering are undefined.

@yieldreturn [Boolean] `true` if this element matches the criteria, `false` otherwise. @yieldreturn [Integer] See `Array#bsearch` for details. @yieldparam [Object] element element to be evaluated @return [Object] The matched element, or `nil` if none found. @raise TypeError if the block returns a non-numeric, non-boolean, non-nil

value.
# File lib/erlang/tuple.rb, line 1187
def bsearch
  return enum_for(:bsearch) if not block_given?
  low, high, result = 0, @size, nil
  while low < high
    mid = (low + ((high - low) >> 1))
    val = get(mid)
    v   = yield val
    if v.is_a? Numeric
      if v == 0
        return val
      elsif v > 0
        high = mid
      else
        low = mid + 1
      end
    elsif v == true
      result = val
      high = mid
    elsif !v
      low = mid + 1
    else
      raise TypeError, "wrong argument type #{v.class} (must be numeric, true, false, or nil)"
    end
  end
  return result
end
clear() click to toggle source

Return an empty `Tuple` instance, of the same class as this one. Useful if you have multiple subclasses of `Tuple` and want to treat them polymorphically.

@return [Tuple]

# File lib/erlang/tuple.rb, line 1218
def clear
  return self.class.empty
end
collect()
Alias for: map
combination(n) { || ... } click to toggle source

When invoked with a block, yields all combinations of length `n` of elements from the `Tuple`, and then returns `self`. There is no guarantee about which order the combinations will be yielded.

If no block is given, an `Enumerator` is returned instead.

@example

t = Erlang::Tuple[5, 6, 7, 8]
t.combination(3) { |c| puts "Combination: #{c}" }

Combination: [5, 6, 7]
Combination: [5, 6, 8]
Combination: [5, 7, 8]
Combination: [6, 7, 8]
#=> Erlang::Tuple[5, 6, 7, 8]

@return [self, Enumerator]

# File lib/erlang/tuple.rb, line 886
def combination(n)
  return enum_for(:combination, n) if not block_given?
  return self if n < 0 || @size < n
  if n == 0
    yield []
  elsif n == 1
    each { |element| yield [element] }
  elsif n == @size
    yield self.to_a
  else
    combos = lambda do |result,index,remaining|
      while @size - index > remaining
        if remaining == 1
          yield result.dup << get(index)
        else
          combos[result.dup << get(index), index+1, remaining-1]
        end
        index += 1
      end
      index.upto(@size-1) { |i| result << get(i) }
      yield result
    end
    combos[[], 0, n]
  end
  return self
end
concat(other)
Alias for: +
delete(obj) click to toggle source

Return a new `Tuple` with all elements which are equal to `obj` removed. `#==` is used for checking equality.

@example

Erlang::Tuple["C", "B", "A", "B"].delete("B")  # => Erlang::Tuple["C", "A"]

@param obj [Object] The object to remove (every occurrence) @return [Tuple]

# File lib/erlang/tuple.rb, line 516
def delete(obj)
  obj = Erlang.from(obj)
  return select { |element| element != obj }
end
delete_at(index) click to toggle source

Return a new `Tuple` with the element at `index` removed. If the given `index` does not exist, return `self`.

@example

Erlang::Tuple["A", "B", "C", "D"].delete_at(2)
# => Erlang::Tuple["A", "B", "D"]

@param index [Integer] The index to remove @return [Tuple]

# File lib/erlang/tuple.rb, line 412
def delete_at(index)
  return self if index >= @size || index < -@size
  index += @size if index < 0

  suffix = flatten_suffix(@root, @levels * BITS_PER_LEVEL, index, [])
  return replace_suffix(index, suffix.tap { |a| a.shift })
end
drop(n) click to toggle source

Drop the first `n` elements and return the rest in a new `Tuple`.

@example

Erlang::Tuple["A", "B", "C", "D", "E", "F"].drop(2)
# => Erlang::Tuple["C", "D", "E", "F"]

@param n [Integer] The number of elements to remove @return [Tuple] @raise ArgumentError if `n` is negative.

# File lib/erlang/tuple.rb, line 749
def drop(n)
  return self if n == 0
  return self.class.empty if n >= @size
  raise ArgumentError, "attempt to drop negative size" if n < 0
  return self.class.new(flatten_suffix(@root, @levels * BITS_PER_LEVEL, n, []))
end
drop_while() click to toggle source

Drop elements up to, but not including, the first element for which the block returns `nil` or `false`. Gather the remaining elements into a new `Tuple`. If no block is given, an `Enumerator` is returned instead.

@example

Erlang::Tuple[1, 3, 5, 7, 6, 4, 2].drop_while { |e| e < 5 }
# => Erlang::Tuple[5, 7, 6, 4, 2]

@return [Tuple, Enumerator]

Calls superclass method
# File lib/erlang/tuple.rb, line 778
def drop_while
  return enum_for(:drop_while) if not block_given?
  return self.class.new(super)
end
each(&block) click to toggle source

Call the given block once for each element in the tuple, passing each element from first to last successively to the block. If no block is given, an `Enumerator` is returned instead.

@example

Erlang::Tuple["A", "B", "C"].each { |e| puts "Element: #{e}" }

Element: A
Element: B
Element: C
# => Erlang::Tuple["A", "B", "C"]

@return [self, Enumerator]

# File lib/erlang/tuple.rb, line 469
def each(&block)
  return to_enum unless block_given?
  traverse_depth_first(@root, @levels, &block)
  return self
end
empty?() click to toggle source

Return `true` if this `Tuple` contains no elements.

@return [Boolean]

# File lib/erlang/tuple.rb, line 138
def empty?
  return @size == 0
end
eql?(other) click to toggle source

Return true if `other` has the same type and contents as this `Tuple`.

@param other [Object] The collection to compare with @return [Boolean]

# File lib/erlang/tuple.rb, line 1343
def eql?(other)
  return true if other.equal?(self)
  if instance_of?(other.class)
    return false if @size != other.size
    return @root.eql?(other.instance_variable_get(:@root))
  else
    return !!(Erlang.compare(other, self) == 0)
  end
end
Also aliased as: ==
erlang_inspect(raw = false) click to toggle source

Allows this `Tuple` to be printed using `Erlang.inspect()`.

@return [String]

# File lib/erlang/tuple.rb, line 1376
def erlang_inspect(raw = false)
  result = '{'
  each_with_index { |obj, i| result << ',' if i > 0; result << Erlang.inspect(obj, raw: raw) }
  result << '}'
  return result
end
fetch(index, default = (missing_default = true)) { |index| ... } click to toggle source

Retrieve the value at `index` with optional default.

@overload fetch(index)

Retrieve the value at the given index, or raise an `IndexError` if not
found.

@param index [Integer] The index to look up
@raise [IndexError] if index does not exist
@example
  t = Erlang::Tuple["A", "B", "C", "D"]
  t.fetch(2)       # => "C"
  t.fetch(-1)      # => "D"
  t.fetch(4)       # => IndexError: index 4 outside of tuple bounds

@overload fetch(index) { |index| … }

Retrieve the value at the given index, or return the result of yielding
the block if not found.

@yield Once if the index is not found.
@yieldparam [Integer] index The index which does not exist
@yieldreturn [Object] Default value to return
@param index [Integer] The index to look up
@example
  t = Erlang::Tuple["A", "B", "C", "D"]
  t.fetch(2) { |i| i * i }   # => "C"
  t.fetch(4) { |i| i * i }   # => 16

@overload fetch(index, default)

Retrieve the value at the given index, or return the provided `default`
value if not found.

@param index [Integer] The index to look up
@param default [Object] Object to return if the key is not found
@example
  t = Erlang::Tuple["A", "B", "C", "D"]
  t.fetch(2, "Z")  # => "C"
  t.fetch(4, "Z")  # => "Z"

@return [Object]

# File lib/erlang/tuple.rb, line 298
def fetch(index, default = (missing_default = true))
  if index >= -@size && index < @size
    return get(index)
  elsif block_given?
    return Erlang.from(yield(index))
  elsif !missing_default
    return Erlang.from(default)
  else
    raise IndexError, "index #{index} outside of tuple bounds"
  end
end
fill(object, index = 0, length = nil) click to toggle source

Replace a range of indexes with the given object.

@overload fill(object)

Return a new `Tuple` of the same size, with every index set to
`object`.

@param [Object] object Fill value.
@example
  Erlang::Tuple["A", "B", "C", "D", "E", "F"].fill("Z")
  # => Erlang::Tuple["Z", "Z", "Z", "Z", "Z", "Z"]

@overload fill(object, index)

Return a new `Tuple` with all indexes from `index` to the end of the
tuple set to `object`.

@param [Object] object Fill value.
@param [Integer] index Starting index. May be negative.
@example
  Erlang::Tuple["A", "B", "C", "D", "E", "F"].fill("Z", 3)
  # => Erlang::Tuple["A", "B", "C", "Z", "Z", "Z"]

@overload fill(object, index, length)

Return a new `Tuple` with `length` indexes, beginning from `index`,
set to `object`. Expands the `Tuple` if `length` would extend beyond
the current length.

@param [Object] object Fill value.
@param [Integer] index Starting index. May be negative.
@param [Integer] length
@example
  Erlang::Tuple["A", "B", "C", "D", "E", "F"].fill("Z", 3, 2)
  # => Erlang::Tuple["A", "B", "C", "Z", "Z", "F"]
  Erlang::Tuple["A", "B"].fill("Z", 1, 5)
  # => Erlang::Tuple["A", "Z", "Z", "Z", "Z", "Z"]

@return [Tuple] @raise [IndexError] if index is out of negative range.

# File lib/erlang/tuple.rb, line 850
def fill(object, index = 0, length = nil)
  raise IndexError if index < -@size
  object = Erlang.from(object)
  index += @size if index < 0
  length ||= @size - index # to the end of the array, if no length given

  if index < @size
    suffix = flatten_suffix(@root, @levels * BITS_PER_LEVEL, index, [])
    suffix.fill(object, 0, length)
  elsif index == @size
    suffix = Array.new(length, object)
  else
    suffix = Array.new(index - @size, nil).concat(Array.new(length, object))
    index = @size
  end

  return replace_suffix(index, suffix)
end
find_all()
Alias for: select
first() click to toggle source

Return the first element in the `Tuple`. If the tuple is empty, return `nil`.

@example

Erlang::Tuple["A", "B", "C"].first  # => "A"

@return [Object]

# File lib/erlang/tuple.rb, line 148
def first
  return get(0)
end
flat_map() click to toggle source

Return a new `Tuple` with the concatenated results of running the block once for every element in this `Tuple`.

@example

Erlang::Tuple[1, 2, 3].flat_map { |x| [x, -x] }
# => Erlang::Tuple[1, -1, 2, -2, 3, -3]

@return [Tuple]

Calls superclass method
# File lib/erlang/tuple.rb, line 544
def flat_map
  return enum_for(:flat_map) if not block_given?
  return self if empty?
  return self.class.new(super)
end
flatten(level = -1) click to toggle source

Return a new `Tuple` with all nested tuples and arrays recursively “flattened out”. That is, their elements inserted into the new `Tuple` in the place where the nested array/tuple originally was. If an optional `level` argument is provided, the flattening will only be done recursively that number of times. A `level` of 0 means not to flatten at all, 1 means to only flatten nested arrays/tuples which are directly contained within this `Tuple`.

@example

t = Erlang::Tuple["A", Erlang::Tuple["B", "C", Erlang::Tuple["D"]]]
t.flatten(1)
# => Erlang::Tuple["A", "B", "C", Erlang::Tuple["D"]]
t.flatten
# => Erlang::Tuple["A", "B", "C", "D"]

@param level [Integer] The depth to which flattening should be applied @return [Tuple]

# File lib/erlang/tuple.rb, line 631
def flatten(level = -1)
  return self if level == 0
  array = self.to_a
  if array.frozen?
    return self.class.new(array.flatten(level).freeze)
  elsif array.flatten!(level) # returns nil if no changes were made
    return self.class.new(array.freeze)
  else
    return self
  end
end
get(index) click to toggle source

Retrieve the element at `index`. If there is none (either the provided index is too high or too low), return `nil`.

@example

t = Erlang::Tuple["A", "B", "C", "D"]
t.get(2)   # => "C"
t.get(-1)  # => "D"
t.get(4)   # => nil

@param index [Integer] The index to retrieve @return [Object]

# File lib/erlang/tuple.rb, line 251
def get(index)
  return nil if @size == 0
  index += @size if index < 0
  return nil if index >= @size || index < 0
  return leaf_node_for(@root, @levels * BITS_PER_LEVEL, index)[index & INDEX_MASK]
end
Also aliased as: at
hash() click to toggle source

See `Object#hash`. @return [Integer]

# File lib/erlang/tuple.rb, line 1356
def hash
  return reduce(Erlang::Tuple.hash) { |hash, item| (hash << 5) - hash + item.hash }
end
insert(index, *elements) click to toggle source

Return a new `Tuple` with the given values inserted before the element at `index`. If `index` is greater than the current length, `nil` values are added to pad the `Tuple` to the required size.

@example

Erlang::Tuple["A", "B", "C", "D"].insert(2, "X", "Y", "Z")
# => Erlang::Tuple["A", "B", "X", "Y", "Z", "C", "D"]
Erlang::Tuple[].insert(2, "X", "Y", "Z")
# => Erlang::Tuple[nil, nil, "X", "Y", "Z"]

@param index [Integer] The index where the new elements should go @param elements [Array] The elements to add @return [Tuple] @raise [IndexError] if index exceeds negative range.

# File lib/erlang/tuple.rb, line 384
def insert(index, *elements)
  raise IndexError if index < -@size
  index += @size if index < 0

  elements = elements.map { |element| Erlang.from(element) }

  if index < @size
    suffix = flatten_suffix(@root, @levels * BITS_PER_LEVEL, index, [])
    suffix.unshift(*elements)
  elsif index == @size
    suffix = elements
  else
    suffix = Array.new(index - @size, nil).concat(elements)
    index = @size
  end

  return replace_suffix(index, suffix)
end
keep_if()
Alias for: select
last() click to toggle source

Return the last element in the `Tuple`. If the tuple is empty, return `nil`.

@example

Erlang::Tuple["A", "B", "C"].last  # => "C"

@return [Object]

# File lib/erlang/tuple.rb, line 158
def last
  return get(-1)
end
map() click to toggle source

Invoke the given block once for each element in the tuple, and return a new `Tuple` containing the values returned by the block. If no block is provided, return an enumerator.

@example

Erlang::Tuple[3, 2, 1].map { |e| e * e }  # => Erlang::Tuple[9, 4, 1]

@return [Tuple, Enumerator]

Calls superclass method
# File lib/erlang/tuple.rb, line 529
def map
  return enum_for(:map) if not block_given?
  return self if empty?
  return self.class.new(super)
end
Also aliased as: collect
marshal_dump() click to toggle source

@return [::Array] @private

# File lib/erlang/tuple.rb, line 1362
def marshal_dump
  return to_a
end
marshal_load(array) click to toggle source

@private

# File lib/erlang/tuple.rb, line 1367
def marshal_load(array)
  initialize(array.freeze)
  __send__(:immutable!)
  return self
end
permutation(n = @size) { || ... } click to toggle source

Yields all permutations of length `n` of elements from the `Tuple`, and then returns `self`. If no length `n` is specified, permutations of all elements will be yielded.

There is no guarantee about which order the permutations will be yielded in.

If no block is given, an `Enumerator` is returned instead.

@example

t = Erlang::Tuple[5, 6, 7]
t.permutation(2) { |p| puts "Permutation: #{p}" }

Permutation: [5, 6]
Permutation: [5, 7]
Permutation: [6, 5]
Permutation: [6, 7]
Permutation: [7, 5]
Permutation: [7, 6]
# => Erlang::Tuple[5, 6, 7]

@return [self, Enumerator]

# File lib/erlang/tuple.rb, line 989
def permutation(n = @size)
  return enum_for(:permutation, n) if not block_given?
  if n < 0 || @size < n
    # yield nothing
  elsif n == 0
    yield []
  elsif n == 1
    each { |element| yield [element] }
  else
    used, result = [], []
    perms = lambda do |index|
      0.upto(@size-1) do |i|
        if !used[i]
          result[index] = get(i)
          if index < n-1
            used[i] = true
            perms[index+1]
            used[i] = false
          else
            yield result.dup
          end
        end
      end
    end
    perms[0]
  end
  return self
end
pop() click to toggle source

Return a new `Tuple` with the last element removed. Return `self` if empty.

@example

Erlang::Tuple["A", "B", "C"].pop  # => Erlang::Tuple["A", "B"]

@return [Tuple]

# File lib/erlang/tuple.rb, line 427
def pop
  return self if @size == 0
  return replace_suffix(@size-1, [])
end
product(*tuples) { |build_array[]| ... } click to toggle source

Cartesian product or multiplication.

@overload product(*tuples)

Return a `Tuple` of all combinations of elements from this `Tuple` and each
of the given tuples or arrays. The length of the returned `Tuple` is the product
of `self.size` and the size of each argument tuple or array.
@example
  t1 = Erlang::Tuple[1, 2, 3]
  t2 = Erlang::Tuple["A", "B"]
  t1.product(t2)
  # => [[1, "A"], [1, "B"], [2, "A"], [2, "B"], [3, "A"], [3, "B"]]

@overload product

Return the result of multiplying all the elements in this `Tuple` together.

@example
  Erlang::Tuple[1, 2, 3, 4, 5].product  # => 120

@return [Tuple]

Calls superclass method Erlang::Enumerable#product
# File lib/erlang/tuple.rb, line 1087
def product(*tuples)
  tuples = tuples.map { |tuple| Erlang.from(tuple) }
  # if no tuples passed, return "product" as in result of multiplying all elements
  return super if tuples.empty?

  tuples.unshift(self)

  if tuples.any?(&:empty?)
    return block_given? ? self : []
  end

  counters = Array.new(tuples.size, 0)

  bump_counters = lambda do
    i = tuples.size-1
    counters[i] += 1
    while counters[i] == tuples[i].size
      counters[i] = 0
      i -= 1
      return true if i == -1 # we are done
      counters[i] += 1
    end
    false # not done yet
  end
  build_array = lambda do
    array = []
    counters.each_with_index { |index,i| array << tuples[i][index] }
    array
  end

  if block_given?
    while true
      yield build_array[]
      return self if bump_counters[]
    end
  else
    result = []
    while true
      result << build_array[]
      return result if bump_counters[]
    end
  end
end
push(element)
Alias for: add
put(index, element = yield(get(index))) click to toggle source

Return a new `Tuple` with a new value at the given `index`. If `index` is greater than the length of the tuple, the returned tuple will be padded with `nil`s to the correct size.

@overload put(index, element)

Return a new `Tuple` with the element at `index` replaced by `element`.

@param element [Object] The object to insert into that position
@example
  Erlang::Tuple[1, 2, 3, 4].put(2, 99)
  # => Erlang::Tuple[1, 2, 99, 4]
  Erlang::Tuple[1, 2, 3, 4].put(-1, 99)
  # => Erlang::Tuple[1, 2, 3, 99]
  Erlang::Tuple[].put(2, 99)
  # => Erlang::Tuple[nil, nil, 99]

@overload put(index)

Return a new `Tuple` with the element at `index` replaced by the return
value of the block.

@yield (existing) Once with the existing value at the given `index`.
@example
  Erlang::Tuple[1, 2, 3, 4].put(2) { |v| v * 10 }
  # => Erlang::Tuple[1, 2, 30, 4]

@param index [Integer] The index to update. May be negative. @return [Tuple]

# File lib/erlang/tuple.rb, line 202
def put(index, element = yield(get(index)))
  raise IndexError, "index #{index} outside of tuple bounds" if index < -@size
  element = Erlang.from(element)
  index += @size if index < 0
  if index > @size
    suffix = Array.new(index - @size, nil)
    suffix << element
    return replace_suffix(@size, suffix)
  else
    return update_root(index, element)
  end
end
Also aliased as: set
rassoc(obj) click to toggle source

Assumes all elements are nested, indexable collections, and searches through them, comparing `obj` with the second element of each nested collection. Return the first nested collection which matches, or `nil` if none is found. Behaviour is undefined when elements do not meet assumptions (i.e. are not indexable collections).

@example

t = Erlang::Tuple[Erlang::Tuple["A", 10], Erlang::Tuple["B", 20], Erlang::Tuple["C", 30]]
t.rassoc(20)  # => Erlang::Tuple["B", 20]

@param obj [Object] The object to search for @return [Object]

# File lib/erlang/tuple.rb, line 1315
def rassoc(obj)
  obj = Erlang.from(obj)
  each do |array|
    next if !array.respond_to?(:[])
    return array if obj == array[1]
  end
  return nil
end
repeated_combination(n) { || ... } click to toggle source

When invoked with a block, yields all repeated combinations of length `n` of tuples from the `Tuple`, and then returns `self`. A “repeated combination” is one in which any tuple from the `Tuple` can appear consecutively any number of times.

There is no guarantee about which order the combinations will be yielded in.

If no block is given, an `Enumerator` is returned instead.

@example

t = Erlang::Tuple[5, 6, 7, 8]
t.repeated_combination(2) { |c| puts "Combination: #{c}" }

Combination: [5, 5]
Combination: [5, 6]
Combination: [5, 7]
Combination: [5, 8]
Combination: [6, 6]
Combination: [6, 7]
Combination: [6, 8]
Combination: [7, 7]
Combination: [7, 8]
Combination: [8, 8]
# => Erlang::Tuple[5, 6, 7, 8]

@return [self, Enumerator]

# File lib/erlang/tuple.rb, line 939
def repeated_combination(n)
  return enum_for(:repeated_combination, n) if not block_given?
  if n < 0
    # yield nothing
  elsif n == 0
    yield []
  elsif n == 1
    each { |element| yield [element] }
  elsif @size == 0
    # yield nothing
  else
    combos = lambda do |result,index,remaining|
      while index < @size-1
        if remaining == 1
          yield result.dup << get(index)
        else
          combos[result.dup << get(index), index, remaining-1]
        end
        index += 1
      end
      element = get(index)
      remaining.times { result << element }
      yield result
    end
    combos[[], 0, n]
  end
  return self
end
repeated_permutation(n = @size) { || ... } click to toggle source

When invoked with a block, yields all repeated permutations of length `n` of elements from the `Tuple`, and then returns `self`. A “repeated permutation” is one where any element from the `Tuple` can appear any number of times, and in any position (not just consecutively)

If no length `n` is specified, permutations of all elements will be yielded. There is no guarantee about which order the permutations will be yielded in.

If no block is given, an `Enumerator` is returned instead.

@example

t = Erlang::Tuple[5, 6, 7]
t.repeated_permutation(2) { |p| puts "Permutation: #{p}" }

Permutation: [5, 5]
Permutation: [5, 6]
Permutation: [5, 7]
Permutation: [6, 5]
Permutation: [6, 6]
Permutation: [6, 7]
Permutation: [7, 5]
Permutation: [7, 6]
Permutation: [7, 7]
# => Erlang::Tuple[5, 6, 7]

@return [self, Enumerator]

# File lib/erlang/tuple.rb, line 1044
def repeated_permutation(n = @size)
  return enum_for(:repeated_permutation, n) if not block_given?
  if n < 0
    # yield nothing
  elsif n == 0
    yield []
  elsif n == 1
    each { |element| yield [element] }
  else
    result = []
    perms = lambda do |index|
      0.upto(@size-1) do |i|
        result[index] = get(i)
        if index < n-1
          perms[index+1]
        else
          yield result.dup
        end
      end
    end
    perms[0]
  end
  return self
end
reverse() click to toggle source

Return a new `Tuple` with the same elements as this one, but in reverse order.

@example

Erlang::Tuple["A", "B", "C"].reverse  # => Erlang::Tuple["C", "B", "A"]

@return [Tuple]

# File lib/erlang/tuple.rb, line 593
def reverse
  return self.class.new(((array = to_a).frozen? ? array.reverse : array.reverse!).freeze)
end
reverse_each(&block) click to toggle source

Call the given block once for each element in the tuple, from last to first.

@example

Erlang::Tuple["A", "B", "C"].reverse_each { |e| puts "Element: #{e}" }

Element: C
Element: B
Element: A

@return [self]

# File lib/erlang/tuple.rb, line 486
def reverse_each(&block)
  return enum_for(:reverse_each) unless block_given?
  reverse_traverse_depth_first(@root, @levels, &block)
  return self
end
rindex(obj = (missing_arg = true)) { |element;| ... } click to toggle source

Find the index of an element, starting from the end of the tuple. Returns `nil` if no element is found.

@overload rindex(obj)

Return the index of the last element which is `#==` to `obj`.

@example
  t = Erlang::Tuple[7, 8, 9, 7, 8, 9]
  t.rindex(8) # => 4

@overload rindex

Return the index of the last element for which the block returns true.

@yield [element] Once for each element, last to first, until the block
                 returns true.
@example
  t = Erlang::Tuple[7, 8, 9, 7, 8, 9]
  t.rindex { |e| e.even? }  # => 4

@return [Integer]

# File lib/erlang/tuple.rb, line 1266
def rindex(obj = (missing_arg = true))
  obj = Erlang.from(obj)
  i = @size - 1
  if missing_arg
    if block_given?
      reverse_each { |element| return i if yield element; i -= 1 }
      return nil
    else
      return enum_for(:rindex)
    end
  else
    reverse_each { |element| return i if element == obj; i -= 1 }
    return nil
  end
end
rotate(count = 1) click to toggle source

Return a new `Tuple` with the same elements, but rotated so that the one at index `count` is the first element of the new tuple. If `count` is positive, the elements will be shifted left, and those shifted past the lowest position will be moved to the end. If `count` is negative, the elements will be shifted right, and those shifted past the last position will be moved to the beginning.

@example

t = Erlang::Tuple["A", "B", "C", "D", "E", "F"]
t.rotate(2)   # => Erlang::Tuple["C", "D", "E", "F", "A", "B"]
t.rotate(-1)  # => Erlang::Tuple["F", "A", "B", "C", "D", "E"]

@param count [Integer] The number of positions to shift elements by @return [Tuple]

# File lib/erlang/tuple.rb, line 610
def rotate(count = 1)
  return self if (count % @size) == 0
  return self.class.new(((array = to_a).frozen? ? array.rotate(count) : array.rotate!(count)).freeze)
end
sample() click to toggle source

Return a randomly chosen element from this `Tuple`. If the tuple is empty, return `nil`.

@example

Erlang::Tuple[1, 2, 3, 4, 5].sample  # => 2

@return [Object]

# File lib/erlang/tuple.rb, line 1228
def sample
  return get(rand(@size))
end
select() { |element| ... } click to toggle source

Return a new `Tuple` containing all elements for which the given block returns true.

@example

Erlang::Tuple["Bird", "Cow", "Elephant"].select { |e| e.size >= 4 }
# => Erlang::Tuple["Bird", "Elephant"]

@return [Tuple] @yield [element] Once for each element.

# File lib/erlang/tuple.rb, line 501
def select
  return enum_for(:select) unless block_given?
  return reduce(self.class.empty) { |tuple, element| yield(element) ? tuple.add(element) : tuple }
end
Also aliased as: find_all, keep_if
set(index, element = yield(get(index)))
Alias for: put
shift() click to toggle source

Return a new `Tuple` with the first element removed. If empty, return `self`.

@example

Erlang::Tuple["A", "B", "C"].shift  # => Erlang::Tuple["B", "C"]

@return [Tuple]

# File lib/erlang/tuple.rb, line 452
def shift
  return delete_at(0)
end
shuffle() click to toggle source

Return a new `Tuple` with the same elements as this one, but randomly permuted.

@example

Erlang::Tuple[1, 2, 3, 4].shuffle  # => Erlang::Tuple[4, 1, 3, 2]

@return [Tuple]

# File lib/erlang/tuple.rb, line 556
def shuffle
  return self.class.new(((array = to_a).frozen? ? array.shuffle : array.shuffle!).freeze)
end
slice(arg, length = (missing_length = true)) click to toggle source

Return specific objects from the `Tuple`. All overloads return `nil` if the starting index is out of range.

@overload tuple.slice(index)

Returns a single object at the given `index`. If `index` is negative,
count backwards from the end.

@param index [Integer] The index to retrieve. May be negative.
@return [Object]
@example
  t = Erlang::Tuple["A", "B", "C", "D", "E", "F"]
  t[2]  # => "C"
  t[-1] # => "F"
  t[6]  # => nil

@overload tuple.slice(index, length)

Return a subtuple starting at `index` and continuing for `length`
elements or until the end of the `Tuple`, whichever occurs first.

@param start [Integer] The index to start retrieving elements from. May be
                       negative.
@param length [Integer] The number of elements to retrieve.
@return [Tuple]
@example
  t = Erlang::Tuple["A", "B", "C", "D", "E", "F"]
  t[2, 3]  # => Erlang::Tuple["C", "D", "E"]
  t[-2, 3] # => Erlang::Tuple["E", "F"]
  t[20, 1] # => nil

@overload tuple.slice(index..end)

Return a subtuple starting at `index` and continuing to index
`end` or the end of the `Tuple`, whichever occurs first.

@param range [Range] The range of indices to retrieve.
@return [Tuple]
@example
  t = Erlang::Tuple["A", "B", "C", "D", "E", "F"]
  t[2..3]    # => Erlang::Tuple["C", "D"]
  t[-2..100] # => Erlang::Tuple["E", "F"]
  t[20..21]  # => nil
# File lib/erlang/tuple.rb, line 350
def slice(arg, length = (missing_length = true))
  if missing_length
    if arg.is_a?(Range)
      from, to = arg.begin, arg.end
      from += @size if from < 0
      to   += @size if to < 0
      to   += 1     if !arg.exclude_end?
      length = to - from
      length = 0 if length < 0
      return subsequence(from, length)
    else
      return get(arg)
    end
  else
    arg += @size if arg < 0
    return subsequence(arg, length)
  end
end
Also aliased as: []
sort(&comparator) click to toggle source

Return a new `Tuple` with the same elements, but sorted.

@overload sort

Compare elements with their natural sort key (`#<=>`).

@example
  Erlang::Tuple["Elephant", "Dog", "Lion"].sort
  # => Erlang::Tuple["Dog", "Elephant", "Lion"]

@overload sort

Uses the block as a comparator to determine sorted order.

@yield [a, b] Any number of times with different pairs of elements.
@yieldreturn [Integer] Negative if the first element should be sorted
                       lower, positive if the latter element, or 0 if
                       equal.
@example
  Erlang::Tuple["Elephant", "Dog", "Lion"].sort { |a,b| a.size <=> b.size }
  # => Erlang::Tuple["Dog", "Lion", "Elephant"]

@return [Tuple]

Calls superclass method
# File lib/erlang/tuple.rb, line 715
def sort(&comparator)
  comparator = Erlang.method(:compare) unless block_given?
  array = super(&comparator)
  return self.class.new(array)
end
sort_by() click to toggle source

Return a new `Tuple` with the same elements, but sorted. The sort order is determined by mapping the elements through the given block to obtain sort keys, and then sorting the keys according to their natural sort order (`#<=>`).

@yield [element] Once for each element. @yieldreturn a sort key object for the yielded element. @example

Erlang::Tuple["Elephant", "Dog", "Lion"].sort_by { |e| e.size }
# => Erlang::Tuple["Dog", "Lion", "Elephant"]

@return [Tuple]

Calls superclass method Erlang::Enumerable#sort_by
# File lib/erlang/tuple.rb, line 733
def sort_by
  return sort unless block_given?
  block = ->(x) { Erlang.from(transformer.call(x)) }
  array = super(&block)
  return self.class.new(array)
end
take(n) click to toggle source

Return only the first `n` elements in a new `Tuple`.

@example

Erlang::Tuple["A", "B", "C", "D", "E", "F"].take(4)
# => Erlang::Tuple["A", "B", "C", "D"]

@param n [Integer] The number of elements to retain @return [Tuple]

Calls superclass method
# File lib/erlang/tuple.rb, line 764
def take(n)
  return self if n >= @size
  return self.class.new(super)
end
take_while() click to toggle source

Gather elements up to, but not including, the first element for which the block returns `nil` or `false`, and return them in a new `Tuple`. If no block is given, an `Enumerator` is returned instead.

@example

Erlang::Tuple[1, 3, 5, 7, 6, 4, 2].take_while { |e| e < 5 }
# => Erlang::Tuple[1, 3]

@return [Tuple, Enumerator]

Calls superclass method
# File lib/erlang/tuple.rb, line 792
def take_while
  return enum_for(:take_while) if not block_given?
  return self.class.new(super)
end
to_a() click to toggle source

Return an `Array` with the same elements, in the same order. The returned `Array` may or may not be frozen.

@return [Array]

# File lib/erlang/tuple.rb, line 1328
def to_a
  if @levels == 0
    # When initializing a Tuple with 32 or less elements, we always make
    # sure @root is frozen, so we can return it directly here
    return @root
  else
    return flatten_node(@root, @levels * BITS_PER_LEVEL, [])
  end
end
Also aliased as: to_ary
to_ary()
Alias for: to_a
transpose() click to toggle source

Assume all elements are tuples or arrays and transpose the rows and columns. In other words, take the first element of each nested tuple/array and gather them together into a new `Tuple`. Do likewise for the second, third, and so on down to the end of each nested Tuple/array. Gather all the resulting `Tuple`s into a new `Tuple` and return it.

This operation is closely related to {#zip}. The result is almost the same as calling {#zip} on the first nested Tuple/array with the others supplied as arguments.

@example

Erlang::Tuple[["A", 10], ["B", 20], ["C", 30]].transpose
# => Erlang::Tuple[Erlang::Tuple["A", "B", "C"], Erlang::Tuple[10, 20, 30]]

@return [Tuple] @raise [IndexError] if elements are not of the same size. @raise [TypeError] if an element can not be implicitly converted to an array (using `#to_ary`)

# File lib/erlang/tuple.rb, line 1148
def transpose
  return self.class.empty if empty?
  result = Array.new(first.size) { [] }

  0.upto(@size-1) do |i|
    source = get(i)
    if source.size != result.size
      raise IndexError, "element size differs (#{source.size} should be #{result.size})"
    end

    0.upto(result.size-1) do |j|
      result[j].push(source[j])
    end
  end

  result.map! { |a| self.class.new(a) }
  return self.class.new(result)
end
uniq(&block) click to toggle source

Return a new `Tuple` with no duplicate elements, as determined by `#hash` and `#eql?`. For each group of equivalent elements, only the first will be retained.

@example

Erlang::Tuple["A", "B", "C", "B"].uniq      # => Erlang::Tuple["A", "B", "C"]
Erlang::Tuple["a", "A", "b"].uniq(&:upcase) # => Erlang::Tuple["a", "b"]

@return [Tuple]

# File lib/erlang/tuple.rb, line 568
def uniq(&block)
  array = self.to_a
  if block_given?
    if array.frozen?
      return self.class.new(array.uniq(&block).freeze)
    elsif array.uniq!(&block) # returns nil if no changes were made
      return self.class.new(array.freeze)
    else
      return self
    end
  elsif array.frozen?
    return self.class.new(array.uniq.freeze)
  elsif array.uniq! # returns nil if no changes were made
    return self.class.new(array.freeze)
  else
    return self
  end
end
unshift(object) click to toggle source

Return a new `Tuple` with `object` inserted before the first element, moving the other elements upwards.

@example

Erlang::Tuple["A", "B"].unshift("Z")
# => Erlang::Tuple["Z", "A", "B"]

@param object [Object] The value to prepend @return [Tuple]

# File lib/erlang/tuple.rb, line 441
def unshift(object)
  return insert(0, Erlang.from(object))
end
values_at(*indices) click to toggle source

Return a new `Tuple` with only the elements at the given `indices`, in the order specified by `indices`. If any of the `indices` do not exist, `nil`s will appear in their places.

@example

t = Erlang::Tuple["A", "B", "C", "D", "E", "F"]
t.values_at(2, 4, 5)   # => Erlang::Tuple["C", "E", "F"]

@param indices [Array] The indices to retrieve and gather into a new `Tuple` @return [Tuple]

# File lib/erlang/tuple.rb, line 1242
def values_at(*indices)
  return self.class.new(indices.map { |i| get(i) }.freeze)
end
zip(*others) click to toggle source

Combine two tuples by “zipping” them together. `others` should be arrays and/or tuples. The corresponding elements from this `Tuple` and each of `others` (that is, the elements with the same indices) will be gathered into arrays.

If `others` contains fewer elements than this tuple, `nil` will be used for padding.

@overload zip(*others)

Return a new tuple containing the new arrays.

@return [Tuple]

@overload zip(*others)

@yield [pair] once for each array
@return [nil]

@example

t1 = Erlang::Tuple["A", "B", "C"]
t2 = Erlang::Tuple[1, 2]
t1.zip(t2)
# => Erlang::Tuple[["A", 1], ["B", 2], ["C", nil]]

@param others [Array] The arrays/tuples to zip together with this one @return [Tuple]

Calls superclass method
# File lib/erlang/tuple.rb, line 685
def zip(*others)
  others = others.map { |other| Erlang.from(other) }
  if block_given?
    return super(*others)
  else
    return self.class.new(super(*others))
  end
end

Private Instance Methods

flatten_node(node, bitshift, result) click to toggle source
# File lib/erlang/tuple.rb, line 1465
def flatten_node(node, bitshift, result)
  if bitshift == 0
    result.concat(node)
  elsif bitshift == BITS_PER_LEVEL
    node.each { |a| result.concat(a) }
  else
    bitshift -= BITS_PER_LEVEL
    node.each { |a| flatten_node(a, bitshift, result) }
  end
  return result
end
flatten_range(node, bitshift, from, to) click to toggle source
# File lib/erlang/tuple.rb, line 1431
def flatten_range(node, bitshift, from, to)
  from_slot = (from >> bitshift) & INDEX_MASK
  to_slot   = (to   >> bitshift) & INDEX_MASK

  if bitshift == 0 # are we at the bottom?
    return node.slice(from_slot, to_slot-from_slot+1)
  elsif from_slot == to_slot
    return flatten_range(node[from_slot], bitshift - BITS_PER_LEVEL, from, to)
  else
    # the following bitmask can be used to pick out the part of the from/to indices
    #   which will be used to direct path BELOW this node
    mask   = ((1 << bitshift) - 1)
    result = []

    if from & mask == 0
      flatten_node(node[from_slot], bitshift - BITS_PER_LEVEL, result)
    else
      result.concat(flatten_range(node[from_slot], bitshift - BITS_PER_LEVEL, from, from | mask))
    end

    (from_slot+1).upto(to_slot-1) do |slot_index|
      flatten_node(node[slot_index], bitshift - BITS_PER_LEVEL, result)
    end

    if to & mask == mask
      flatten_node(node[to_slot], bitshift - BITS_PER_LEVEL, result)
    else
      result.concat(flatten_range(node[to_slot], bitshift - BITS_PER_LEVEL, to & ~mask, to))
    end

    return result
  end
end
flatten_suffix(node, bitshift, from, result) click to toggle source
# File lib/erlang/tuple.rb, line 1484
def flatten_suffix(node, bitshift, from, result)
  from_slot = (from >> bitshift) & INDEX_MASK

  if bitshift == 0
    if from_slot == 0
      return result.concat(node)
    else
      return result.concat(node.slice(from_slot, 32)) # entire suffix of node. excess length is ignored by #slice
    end
  else
    mask = ((1 << bitshift) - 1)
    if from & mask == 0
      from_slot.upto(node.size-1) do |i|
        flatten_node(node[i], bitshift - BITS_PER_LEVEL, result)
      end
    elsif child = node[from_slot]
      flatten_suffix(child, bitshift - BITS_PER_LEVEL, from, result)
      (from_slot+1).upto(node.size-1) do |i|
        flatten_node(node[i], bitshift - BITS_PER_LEVEL, result)
      end
    end
    return result
  end
end
leaf_node_for(node, bitshift, index) click to toggle source
# File lib/erlang/tuple.rb, line 1395
def leaf_node_for(node, bitshift, index)
  while bitshift > 0
    node = node[(index >> bitshift) & INDEX_MASK]
    bitshift -= BITS_PER_LEVEL
  end
  return node
end
replace_node_suffix(node, bitshift, from, suffix) click to toggle source
# File lib/erlang/tuple.rb, line 1541
def replace_node_suffix(node, bitshift, from, suffix)
  from_slot = (from >> bitshift) & INDEX_MASK

  if bitshift == 0
    if from_slot == 0
      return suffix.shift(32)
    else
      return node.take(from_slot).concat(suffix.shift(32 - from_slot))
    end
  else
    mask = ((1 << bitshift) - 1)
    if from & mask == 0
      if from_slot == 0
        new_node = suffix.shift(32 * (1 << bitshift))
        while bitshift != 0
          new_node = new_node.each_slice(32).to_a
          bitshift -= BITS_PER_LEVEL
        end
        return new_node
      else
        result = node.take(from_slot)
        remainder = suffix.shift((32 - from_slot) * (1 << bitshift))
        while bitshift != 0
          remainder = remainder.each_slice(32).to_a
          bitshift -= BITS_PER_LEVEL
        end
        return result.concat(remainder)
      end
    elsif child = node[from_slot]
      result = node.take(from_slot)
      result.push(replace_node_suffix(child, bitshift - BITS_PER_LEVEL, from, suffix))
      remainder = suffix.shift((31 - from_slot) * (1 << bitshift))
      while bitshift != 0
        remainder = remainder.each_slice(32).to_a
        bitshift -= BITS_PER_LEVEL
      end
      return result.concat(remainder)
    else
      raise "Shouldn't happen"
    end
  end
end
replace_suffix(from, suffix) click to toggle source
# File lib/erlang/tuple.rb, line 1509
def replace_suffix(from, suffix)
  # new suffix can go directly after existing elements
  raise IndexError if from > @size
  root, levels = @root, @levels

  if (from >> (BITS_PER_LEVEL * (@levels + 1))) != 0
    # index where new suffix goes doesn't fall within current tree
    # we will need to deepen tree
    root = [root].freeze
    levels += 1
  end

  new_size = from + suffix.size
  root = replace_node_suffix(root, levels * BITS_PER_LEVEL, from, suffix)

  if !suffix.empty?
    levels.times { suffix = suffix.each_slice(32).to_a }
    root.concat(suffix)
    while root.size > 32
      root = root.each_slice(32).to_a
      levels += 1
    end
  else
    while root.size == 1 && levels > 0
      root = root[0]
      levels -= 1
    end
  end

  return self.class.alloc(root.freeze, new_size, levels)
end
reverse_traverse_depth_first(node, level, &block) click to toggle source
# File lib/erlang/tuple.rb, line 1390
def reverse_traverse_depth_first(node, level, &block)
  return node.reverse_each(&block) if level == 0
  return node.reverse_each { |child| reverse_traverse_depth_first(child, level - 1, &block) }
end
subsequence(from, length) click to toggle source
# File lib/erlang/tuple.rb, line 1477
def subsequence(from, length)
  return nil if from > @size || from < 0 || length < 0
  length = @size - from if @size < from + length
  return self.class.empty if length == 0
  return self.class.new(flatten_range(@root, @levels * BITS_PER_LEVEL, from, from + length - 1))
end
traverse_depth_first(node, level, &block) click to toggle source
# File lib/erlang/tuple.rb, line 1385
def traverse_depth_first(node, level, &block)
  return node.each(&block) if level == 0
  return node.each { |child| traverse_depth_first(child, level - 1, &block) }
end
update_leaf_node(node, bitshift, index, item) click to toggle source
# File lib/erlang/tuple.rb, line 1417
def update_leaf_node(node, bitshift, index, item)
  slot_index = (index >> bitshift) & INDEX_MASK
  if bitshift > 0
    old_child = node[slot_index] || []
    item = update_leaf_node(old_child, bitshift - BITS_PER_LEVEL, index, item)
  end
  existing_item = node[slot_index]
  if existing_item.equal?(item)
    return node
  else
    return node.dup.tap { |n| n[slot_index] = item }.freeze
  end
end
update_root(index, item) click to toggle source
# File lib/erlang/tuple.rb, line 1403
def update_root(index, item)
  root, levels = @root, @levels
  while index >= (1 << (BITS_PER_LEVEL * (levels + 1)))
    root = [root].freeze
    levels += 1
  end
  new_root = update_leaf_node(root, levels * BITS_PER_LEVEL, index, item)
  if new_root.equal?(root)
    return self
  else
    return self.class.alloc(new_root, @size > index ? @size : index + 1, levels)
  end
end