# An `Indexable` container that is additionally mutable. # # Including types may write values to numeric indices, apart from reading them. # This module does not cover cases where the container is resized. module Indexable::Mutable(T) include Indexable(T) # Sets the element at the given *index* to *value*, without doing any bounds # check. # # `Indexable::Mutable` makes sure to invoke this method with *index* in # `0...size`, so converting negative indices to positive ones is not needed # here. # # Clients never invoke this method directly. Instead, they modify elements # with `#[]=(index, value)`. # # This method should only be directly invoked if you are absolutely # sure the index is in bounds, to avoid a bounds check for a small boost # of performance. abstract def unsafe_put(index : Int, value : T) # Sets the given *value* at the given *index*. Returns *value*. # # Negative indices can be used to start counting from the end of the # container. Raises `IndexError` if trying to set an element outside the # container's range. # # ``` # ary = [1, 2, 3] # ary[0] = 5 # ary # => [5, 2, 3] # # ary[3] = 5 # raises IndexError # ``` @[AlwaysInline] def []=(index : Int, value : T) : T index = check_index_out_of_bounds index unsafe_put(index, value) value end # Yields the current element at the given *index* and updates the value # at that *index* with the block's value. Returns the new value. # # Raises `IndexError` if trying to set an element outside the container's # range. # # ``` # array = [1, 2, 3] # array.update(1) { |x| x * 2 } # => 4 # array # => [1, 4, 3] # array.update(5) { |x| x * 2 } # raises IndexError # ``` def update(index : Int, & : T -> _) : T index = check_index_out_of_bounds index value = yield unsafe_fetch(index) unsafe_put(index, value) value end # Swaps the elements at *index0* and *index1*. Returns `self`. # # Negative indices can be used to start counting from the end of the # container. Raises `IndexError` if either index is out of bounds. # # ``` # a = ["first", "second", "third"] # a.swap(1, 2) # => ["first", "third", "second"] # a # => ["first", "third", "second"] # a.swap(0, -1) # => ["second", "third", "first"] # a # => ["second", "third", "first"] # a.swap(2, 3) # raises IndexError # ``` def swap(index0 : Int, index1 : Int) : self index0 = check_index_out_of_bounds(index0) index1 = check_index_out_of_bounds(index1) unless index0 == index1 tmp = unsafe_fetch(index0) unsafe_put(index0, unsafe_fetch(index1)) unsafe_put(index1, tmp) end self end # Reverses in-place all the elements of `self`. Returns `self`. def reverse! : self return self if size <= 1 index0 = 0 index1 = size - 1 while index0 < index1 swap(index0, index1) index0 += 1 index1 -= 1 end self end # Replaces every element in `self` with the given *value*. Returns `self`. # # ``` # array = [1, 2, 3, 4] # array.fill(2) # => [2, 2, 2, 2] # array # => [2, 2, 2, 2] # ``` def fill(value : T) : self each_index do |i| unsafe_put(i, value) end self end # Replaces *count* or less (if there aren't enough) elements starting at the # given *start* index with *value*. Returns `self`. # # Negative values of *start* count from the end of the container. # # Raises `IndexError` if the *start* index is out of range. # # Raises `ArgumentError` if *count* is negative. # # ``` # array = [1, 2, 3, 4, 5] # array.fill(9, 2, 2) # => [1, 2, 9, 9, 5] # array # => [1, 2, 9, 9, 5] # ``` def fill(value : T, start : Int, count : Int) : self return self if count <= 0 start, count = normalize_start_and_count(start, count) count.times do |i| unsafe_put(start + i, value) end self end # Replaces the elements within the given *range* with *value*. Returns `self`. # # Negative indices count backward from the end of the container. # # Raises `IndexError` if the starting index is out of range. # # ``` # array = [1, 2, 3, 4, 5] # array.fill(9, 2..3) # => [1, 2, 9, 9, 5] # array # => [1, 2, 9, 9, 5] # ``` def fill(value : T, range : Range) : self fill(value, *Indexable.range_to_index_and_count(range, size) || raise IndexError.new) end # Yields each index of `self` to the given block and then assigns # the block's value in that position. Returns `self`. # # Accepts an optional *offset* parameter, which tells the block to start # counting from there. # # ``` # array = [2, 1, 1, 1] # array.fill { |i| i * i } # => [0, 1, 4, 9] # array # => [0, 1, 4, 9] # array.fill(offset: 3) { |i| i * i } # => [9, 16, 25, 36] # array # => [9, 16, 25, 36] # ``` def fill(*, offset : Int = 0, & : Int32 -> T) : self each_index do |i| unsafe_put(i, yield offset + i) end self end # Yields each index of `self`, starting at *start* and for *count* times (or # less if there aren't enough elements), to the given block and then assigns # the block's value in that position. Returns `self`. # # Negative values of *start* count from the end of the container. # # Has no effect if *count* is zero or negative. # # Raises `IndexError` if *start* is outside the array range. # # ``` # a = [1, 2, 3, 4, 5, 6] # a.fill(2, 3) { |i| i * i * i } # => [1, 2, 8, 27, 64, 6] # ``` def fill(start : Int, count : Int, & : Int32 -> T) : self return self if count <= 0 start, count = normalize_start_and_count(start, count) count.times do |i| unsafe_put(start + i, yield start + i) end self end # Yields each index of `self`, in the given *range*, to the given block and # then assigns the block's value in that position. Returns `self`. # # Negative indices count backward from the end of the container. # # Raises `IndexError` if the starting index is out of range. # # ``` # a = [1, 2, 3, 4, 5, 6] # a.fill(2..4) { |i| i * i * i } # => [1, 2, 8, 27, 64, 6] # ``` def fill(range : Range, & : Int32 -> T) : self fill(*Indexable.range_to_index_and_count(range, size) || raise IndexError.new) do |i| yield i end end {% begin %} # Invokes the given block for each element of `self`, replacing the element # with the value returned by the block. Returns `self`. # # ``` # a = [1, 2, 3] # a.map! { |x| x * x } # a # => [1, 4, 9] # ``` {% if compare_versions(Crystal::VERSION, "1.1.1") >= 0 %} def map!(& : T -> _) : self # TODO: add as constant {% else %} def map!(&) # it doesn't compile with the type annotation in the 1.0.0 compiler {% end %} each_index do |i| unsafe_put(i, yield unsafe_fetch(i)) end self end {% end %} # Like `#map!`, but the block gets passed both the element and its index. # # Accepts an optional *offset* parameter, which tells it to start counting # from there. # # ``` # gems = ["crystal", "pearl", "diamond"] # gems.map_with_index! { |gem, i| "#{i}: #{gem}" } # gems # => ["0: crystal", "1: pearl", "2: diamond"] # ``` def map_with_index!(offset = 0, & : T, Int32 -> _) : self each_index do |i| unsafe_put(i, yield(unsafe_fetch(i), offset + i)) end self end # Modifies `self` by randomizing the order of elements in the collection. # Returns `self`. # # ``` # a = [1, 2, 3, 4, 5] # a.shuffle! # => [3, 5, 2, 4, 1] # a # => [3, 5, 2, 4, 1] # ``` # # Uses the *random* instance when provided if the randomness needs to be # controlled or to follow some traits. For example the following shuffle will # always result in the same order: # # ``` # a = [1, 2, 3, 4, 5] # a.shuffle!(Random.new(42)) # => [3, 2, 4, 5, 1] # a.shuffle!(Random.new(42)) # => [3, 2, 4, 5, 1] # a # => [3, 2, 4, 5, 1] # ``` def shuffle!(random : Random? = nil) : self rng = random || Random.thread_default (size - 1).downto(1) do |i| j = rng.rand(i + 1) swap(i, j) end self end # Shifts all elements of `self` to the left *n* times. Returns `self`. # # ``` # a1 = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] # a2 = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] # a3 = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] # # a1.rotate! # a2.rotate!(1) # a3.rotate!(3) # # a1 # => [1, 2, 3, 4, 5, 6, 7, 8, 9, 0] # a2 # => [1, 2, 3, 4, 5, 6, 7, 8, 9, 0] # a3 # => [3, 4, 5, 6, 7, 8, 9, 0, 1, 2] # ``` def rotate!(n : Int = 1) : self return self if size <= 1 n %= size return self if n == 0 # juggling algorithm size.gcd(n).times do |i| tmp = unsafe_fetch(i) j = i while true k = j + n k -= size if k >= size break if k == i unsafe_put(j, unsafe_fetch(k)) j = k end unsafe_put(j, tmp) end self end # Sorts all elements in `self` based on the return value of the comparison # method `T#<=>` (see `Comparable#<=>`), using a stable sort algorithm. # # ``` # a = [3, 1, 2] # a.sort! # a # => [1, 2, 3] # ``` # # This sort operation modifies `self`. See `#sort` for a non-modifying option # that allocates a new instance. # # See `Slice#sort!` for details on the implementation. # # Raises `ArgumentError` if the comparison between any two elements returns `nil`. def sort! : self slice = Slice.new(size) { |i| unsafe_fetch(i) }.sort! each_index do |i| unsafe_put(i, slice.unsafe_fetch(i)) end self end # Sorts all elements in `self` based on the return value of the comparison # method `T#<=>` (see `Comparable#<=>`), using an unstable sort algorithm. # # ``` # a = [3, 1, 2] # a.unstable_sort! # a # => [1, 2, 3] # ``` # # This sort operation modifies `self`. See `#unstable_sort` for a non-modifying # option that allocates a new instance. # # See `Slice#unstable_sort!` for details on the implementation. # # Raises `ArgumentError` if the comparison between any two elements returns `nil`. def unstable_sort! : self slice = Slice.new(size) { |i| unsafe_fetch(i) }.unstable_sort! each_index do |i| unsafe_put(i, slice.unsafe_fetch(i)) end self end # Sorts all elements in `self` based on the comparator in the given block, using # a stable sort algorithm. # # The block must implement a comparison between two elements *a* and *b*, # where `a < b` returns `-1`, `a == b` returns `0`, and `a > b` returns `1`. # The comparison operator `<=>` can be used for this. # # ``` # a = [3, 1, 2] # # This is a reverse sort (forward sort would be `a <=> b`) # a.sort! { |a, b| b <=> a } # a # => [3, 2, 1] # ``` # # This sort operation modifies `self`. See `#sort(&block : T, T -> U)` for a # non-modifying option that allocates a new instance. # # See `Slice#sort!(&block : T, T -> U)` for details on the implementation. # # Raises `ArgumentError` if for any two elements the block returns `nil`. def sort!(&block : T, T -> U) : self forall U {% unless U <= Int32? %} {% raise "Expected block to return Int32 or Nil, not #{U}" %} {% end %} slice = Slice.new(size) { |i| unsafe_fetch(i) }.sort!(&block) each_index do |i| unsafe_put(i, slice.unsafe_fetch(i)) end self end # Sorts all elements in `self` based on the comparator in the given block, # using an unstable sort algorithm. # # The block must implement a comparison between two elements *a* and *b*, # where `a < b` returns `-1`, `a == b` returns `0`, and `a > b` returns `1`. # The comparison operator `<=>` can be used for this. # # ``` # a = [3, 1, 2] # # This is a reverse sort (forward sort would be `a <=> b`) # a.unstable_sort! { |a, b| b <=> a } # a # => [3, 2, 1] # ``` # # This sort operation modifies `self`. See `#unstable_sort(&block : T, T -> U)` # for a non-modifying option that allocates a new instance. # # See `Slice#unstable_sort!(&block : T, T -> U)` for details on the implementation. # # Raises `ArgumentError` if for any two elements the block returns `nil`. def unstable_sort!(&block : T, T -> U) : self forall U {% unless U <= Int32? %} {% raise "Expected block to return Int32 or Nil, not #{U}" %} {% end %} slice = Slice.new(size) { |i| unsafe_fetch(i) }.unstable_sort!(&block) each_index do |i| unsafe_put(i, slice.unsafe_fetch(i)) end self end # Sorts all elements in `self` by the output value of the # block. The output values are compared via the comparison method `#<=>` # (see `Comparable#<=>`), using a stable sort algorithm. # # ``` # a = %w(apple pear fig) # a.sort_by! { |word| word.size } # a # => ["fig", "pear", "apple"] # ``` # # This sort operation modifies `self`. See `#sort_by(&block : T -> _)` for a # non-modifying option that allocates a new instance. # # If stability is expendable, `#unstable_sort_by!(&block : T -> _)` provides a # performance advantage over stable sort. # # See `#sort!(&block : T -> _)` for details on the sorting mechanism. # # Raises `ArgumentError` if the comparison between any two comparison values returns `nil`. def sort_by!(&block : T -> _) : self slice = Slice.new(size) do |i| elem = unsafe_fetch(i) {elem, (yield elem)} end.sort! { |x, y| x[1] <=> y[1] } each_index do |i| unsafe_put(i, slice.unsafe_fetch(i)[0]) end self end # Sorts all elements in `self` by the output value of the # block. The output values are compared via the comparison method `#<=>` # (see `Comparable#<=>`), using an unstable sort algorithm. # # ``` # a = %w(apple pear fig) # a.unstable_sort_by! { |word| word.size } # a # => ["fig", "pear", "apple"] # ``` # # This sort operation modifies `self`. See `#unstable_sort_by(&block : T -> _)` # for a non-modifying option that allocates a new instance. # # If stability is necessary, use `#sort_by!(&block : T -> _)` instead. # # See `#unstable_sort!(&block : T -> _)` for details on the sorting mechanism. # # Raises `ArgumentError` if the comparison between any two comparison values returns `nil`. def unstable_sort_by!(&block : T -> _) : self slice = Slice.new(size) do |i| elem = unsafe_fetch(i) {elem, (yield elem)} end.unstable_sort! { |x, y| x[1] <=> y[1] } each_index do |i| unsafe_put(i, slice.unsafe_fetch(i)[0]) end self end end