require "spec"
require "spec/helpers/iterate"

private alias RecursiveArray = Array(RecursiveArray)

private class Spaceship
  getter value : Float64

  def initialize(@value : Float64, @return_nil = false)
  end

  def <=>(other : Spaceship)
    return nil if @return_nil

    value <=> other.value
  end
end

private def is_stable_sort(*, mutable, &block)
  n = 42
  # [Spaceship.new(0), ..., Spaceship.new(n - 1), Spaceship.new(0), ..., Spaceship.new(n - 1)]
  arr = Array.new(n * 2) { |i| Spaceship.new((i % n).to_f) }
  # [Spaceship.new(0), Spaceship.new(0), ..., Spaceship.new(n - 1), Spaceship.new(n - 1)]
  expected = Array.new(n * 2) { |i| arr[i % 2 * n + i // 2] }

  if mutable
    yield arr
    result = arr
  else
    result = yield arr
    result.should_not eq(arr)
  end

  result.size.should eq(expected.size)
  expected.zip(result) do |exp, res|
    res.should be(exp) # reference-equality is necessary to check sorting is stable.
  end
end

describe "Array" do
  describe "new" do
    it "creates with default value" do
      ary = Array.new(5, 3)
      ary.should eq([3, 3, 3, 3, 3])
    end

    it "creates with default value in block" do
      ary = Array.new(5) { |i| i * 2 }
      ary.should eq([0, 2, 4, 6, 8])

      ary = Array.new(5_u32) { |i| i * 2 }
      ary.should eq([0, 2, 4, 6, 8])
    end

    it "raises on negative count" do
      expect_raises(ArgumentError, "Negative array size") do
        Array.new(-1, 3)
      end
    end

    it "raises on negative capacity" do
      expect_raises(ArgumentError, "Negative array size") do
        Array(Int32).new(-1)
      end
    end
  end

  describe "==" do
    it "compares empty" do
      ([] of Int32).should eq([] of Int32)
      [1].should_not eq([] of Int32)
      ([] of Int32).should_not eq([1])
    end

    it "compares elements" do
      [1, 2, 3].should eq([1, 2, 3])
      [1, 2, 3].should_not eq([3, 2, 1])
    end

    it "compares other" do
      a = [1, 2, 3]
      b = [1, 2, 3]
      c = [1, 2, 3, 4]
      d = [1, 2, 4]
      (a == b).should be_true
      (b == c).should be_false
      (a == d).should be_false
    end
  end

  describe "&" do
    it "small arrays" do
      ([1, 2, 3] & [] of Int32).should eq([] of Int32)
      ([] of Int32 & [1, 2, 3]).should eq([] of Int32)
      ([1, 2, 3] & [3, 2, 4]).should eq([2, 3])
      ([1, 2, 3, 1, 2, 3] & [3, 2, 4, 3, 2, 4]).should eq([2, 3])
      ([1, 2, 3, 1, 2, 3, nil, nil] & [3, 2, 4, 3, 2, 4, nil]).should eq([2, 3, nil])
    end

    it "big arrays" do
      a1 = (1..64).to_a
      a2 = (33..96).to_a
      (a1 & a2).should eq((33..64).to_a)
    end
  end

  describe "|" do
    it "small arrays" do
      ([1, 2, 3, 2, 3] | ([] of Int32)).should eq([1, 2, 3])
      (([] of Int32) | [1, 2, 3, 2, 3]).should eq([1, 2, 3])
      ([1, 2, 3] | [5, 3, 2, 4]).should eq([1, 2, 3, 5, 4])
      ([1, 1, 2, 3, 3] | [4, 5, 5, 6]).should eq([1, 2, 3, 4, 5, 6])
    end

    it "large arrays" do
      a = [1, 2, 3] * 10
      b = [4, 5, 6] * 10
      (a | b).should eq([1, 2, 3, 4, 5, 6])
    end

    it "different types" do
      ([1, 2, 3] | ["hello"]).should eq([1, 2, 3, "hello"])
    end
  end

  it "does +" do
    a = [1, 2, 3]
    b = [4, 5]
    c = a + b
    c.size.should eq(5)
    0.upto(4) { |i| c[i].should eq(i + 1) }
  end

  it "does + with empty tuple converted to array (#909)" do
    ([1, 2] + Tuple.new.to_a).should eq([1, 2])
    (Tuple.new.to_a + [1, 2]).should eq([1, 2])
  end

  describe "-" do
    it "does it" do
      ([1, 2, 3, 4, 5] - [4, 2]).should eq([1, 3, 5])
    end

    it "does with larger array coming second" do
      ([4, 2] - [1, 2, 3]).should eq([4])
    end

    it "does with even larger arrays" do
      ((1..64).to_a - (1..32).to_a).should eq((33..64).to_a)
    end

    context "with different types" do
      it "small array" do
        ([1, 2, 3, 'c'] - [2, nil]).should eq [1, 3, 'c']
      end

      it "big array" do
        (((1..64).to_a + ['c']) - ((2..63).to_a + [nil])).should eq [1, 64, 'c']
      end
    end
  end

  it "does *" do
    (([] of Int32) * 10).should be_empty
    ([1, 2, 3] * 0).should be_empty
    ([1] * 3).should eq([1, 1, 1])
    ([1, 2, 3] * 3).should eq([1, 2, 3, 1, 2, 3, 1, 2, 3])
    ([1, 2] * 10).should eq([1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2])
  end

  describe "[]" do
    it "gets on positive index" do
      [1, 2, 3][1].should eq(2)
    end

    it "gets on negative index" do
      [1, 2, 3][-1].should eq(3)
    end

    it "gets on inclusive range" do
      [1, 2, 3, 4, 5, 6][1..4].should eq([2, 3, 4, 5])
    end

    it "gets on inclusive range with negative indices" do
      [1, 2, 3, 4, 5, 6][-5..-2].should eq([2, 3, 4, 5])
    end

    it "gets on exclusive range" do
      [1, 2, 3, 4, 5, 6][1...4].should eq([2, 3, 4])
    end

    it "gets on exclusive range with negative indices" do
      [1, 2, 3, 4, 5, 6][-5...-2].should eq([2, 3, 4])
    end

    it "gets on range with start higher than end" do
      [1, 2, 3][2..1].should eq([] of Int32)
      [1, 2, 3][3..1].should eq([] of Int32)
      expect_raises IndexError do
        [1, 2, 3][4..1]
      end
    end

    it "gets on range with start higher than negative end" do
      [1, 2, 3][1..-1].should eq([2, 3] of Int32)
      [1, 2, 3][2..-2].should eq([] of Int32)
    end

    it "gets on range without end" do
      [1, 2, 3][1..nil].should eq([2, 3])
    end

    it "gets on range without begin" do
      [1, 2, 3][nil..1].should eq([1, 2])
    end

    it "raises on index out of bounds with range" do
      expect_raises IndexError do
        [1, 2, 3][4..6]
      end
    end

    it "raises on index out of bounds with range without end" do
      expect_raises IndexError do
        [1, 2, 3][4..nil]
      end
    end

    it "gets with start and count" do
      [1, 2, 3, 4, 5, 6][1, 3].should eq([2, 3, 4])
    end

    it "gets with start and count exceeding size" do
      [1, 2, 3][1, 3].should eq([2, 3])
    end

    it "gets with negative start" do
      [1, 2, 3, 4, 5, 6][-4, 2].should eq([3, 4])
    end

    it "raises on index out of bounds with start and count" do
      expect_raises IndexError do
        [1, 2, 3][4, 0]
      end
    end

    it "raises on negative count" do
      expect_raises ArgumentError do
        [1, 2, 3][3, -1]
      end
    end

    it "raises on index out of bounds" do
      expect_raises IndexError do
        [1, 2, 3][-4, 2]
      end
    end

    it "raises on negative count" do
      expect_raises ArgumentError, /Negative count: -1/ do
        [1, 2, 3][1, -1]
      end
    end

    it "raises on negative count on empty Array" do
      expect_raises ArgumentError, /Negative count: -1/ do
        Array(Int32).new[0, -1]
      end
    end

    it "gets 0, 0 on empty array" do
      a = [] of Int32
      a[0, 0].should eq(a)
    end

    it "gets 0 ... 0 on empty array" do
      a = [] of Int32
      a[0..0].should eq(a)
    end

    it "doesn't exceed limits" do
      [1][0..3].should eq([1])
    end

    it "returns empty if at end" do
      [1][1, 0].should eq([] of Int32)
      [1][1, 10].should eq([] of Int32)
    end

    it "raises on too negative left bound" do
      expect_raises IndexError do
        [1, 2, 3][-4..0]
      end
    end
  end

  describe "[]?" do
    it "gets with index" do
      [1, 2, 3][2]?.should eq(3)
      [1, 2, 3][3]?.should be_nil
    end

    it "gets with range" do
      [1, 2, 3][1..2]?.should eq([2, 3])
      [1, 2, 3][4..-1]?.should be_nil
    end

    it "gets with start and count" do
      [1, 2, 3][1, 3]?.should eq([2, 3])
      [1, 2, 3][4, 0]?.should be_nil
    end

    it "gets with range without end" do
      [1, 2, 3][1..nil]?.should eq([2, 3])
      [1, 2, 3][4..nil]?.should be_nil
    end

    it "gets with range without beginning" do
      [1, 2, 3][nil..1]?.should eq([1, 2])
    end
  end

  describe "[]=" do
    it "sets on positive index" do
      a = [1, 2, 3]
      a[1] = 4
      a[1].should eq(4)
    end

    it "sets on negative index" do
      a = [1, 2, 3]
      a[-1] = 4
      a[2].should eq(4)
    end

    it "replaces a subrange with a single value" do
      a = [1, 2, 3, 4, 5]
      a[1, 3] = 6
      a.should eq([1, 6, 5])

      a = [1, 2, 3, 4, 5]
      a[1, 1] = 6
      a.should eq([1, 6, 3, 4, 5])

      a = [1, 2, 3, 4, 5]
      a[1, 0] = 6
      a.should eq([1, 6, 2, 3, 4, 5])

      a = [1, 2, 3, 4, 5]
      a[1, 10] = 6
      a.should eq([1, 6])

      a = [1, 2, 3, 4, 5]
      a[-3, 2] = 6
      a.should eq([1, 2, 6, 5])

      a = [1, 2, 3, 4, 5, 6, 7, 8]
      a[1, 3] = 6
      a.should eq([1, 6, 5, 6, 7, 8])

      expect_raises ArgumentError, "Negative count" do
        [1, 2, 3][0, -1]
      end

      a = [1, 2, 3, 4, 5]
      a[1..3] = 6
      a.should eq([1, 6, 5])

      a = [1, 2, 3, 4, 5]
      a[2..3] = 6
      a.should eq([1, 2, 6, 5])

      a = [1, 2, 3, 4, 5]
      a[1...1] = 6
      a.should eq([1, 6, 2, 3, 4, 5])

      a = [1, 2, 3, 4, 5]
      a[2..nil] = 6
      a.should eq([1, 2, 6])

      a = [1, 2, 3, 4, 5]
      a[nil..2] = 6
      a.should eq([6, 4, 5])
    end

    it "replaces a subrange with an array" do
      a = [1, 2, 3, 4, 5]
      a[1, 3] = [6, 7, 8]
      a.should eq([1, 6, 7, 8, 5])

      a = [1, 2, 3, 4, 5]
      a[1, 3] = [6, 7]
      a.should eq([1, 6, 7, 5])

      a = [1, 2, 3, 4, 5, 6, 7, 8]
      a[1, 3] = [6, 7]
      a.should eq([1, 6, 7, 5, 6, 7, 8])

      a = [1, 2, 3, 4, 5]
      a[1, 3] = [6, 7, 8, 9, 10]
      a.should eq([1, 6, 7, 8, 9, 10, 5])

      a = [1, 2, 3, 4, 5]
      a[1, 2] = [6, 7, 8, 9, 10]
      a.should eq([1, 6, 7, 8, 9, 10, 4, 5])

      a = [1, 2, 3, 4, 5]
      a[1..3] = [6, 7, 8]
      a.should eq([1, 6, 7, 8, 5])

      a = [1, 2, 3, 4, 5]
      a[2..nil] = [6, 7]
      a.should eq([1, 2, 6, 7])

      a = [1, 2, 3, 4, 5]
      a[nil..2] = [6, 7]
      a.should eq([6, 7, 4, 5])
    end

    it "optimizes when index is 0" do
      a = [1, 2, 3, 4, 5, 6, 7, 8]
      buffer = a.@buffer
      a[0..2] = 10
      a.should eq([10, 4, 5, 6, 7, 8])
      a.@offset_to_buffer.should eq(2)
      a.@buffer.should eq(buffer + 2)
    end

    it "replaces entire range with a value for empty array (#8341)" do
      a = [] of Int32
      a[..] = 6
      a.should eq([6])
    end

    it "pushes a new value with []=(...)" do
      a = [1, 2, 3]
      a[3..] = 4
      a.should eq([1, 2, 3, 4])
    end

    it "replaces entire range with an array for empty array (#8341)" do
      a = [] of Int32
      a[..] = [1, 2, 3]
      a.should eq([1, 2, 3])
    end

    it "concats a new array with []=(...)" do
      a = [1, 2, 3]
      a[3..] = [4, 5, 6]
      a.should eq([1, 2, 3, 4, 5, 6])
    end

    it "reuses the buffer if possible" do
      a = [1, 2, 3, 4, 5]
      a.pop
      a[4, 0] = [6]
      a.should eq([1, 2, 3, 4, 6])
      a.@capacity.should eq(5)
      a.@offset_to_buffer.should eq(0)
    end

    it "resizes the buffer if capacity is not enough" do
      a = [1, 2, 3, 4, 5]
      a.shift
      a[4, 0] = [6, 7, 8, 9]
      a.should eq([2, 3, 4, 5, 6, 7, 8, 9])
      a.@capacity.should eq(10)
      a.@offset_to_buffer.should eq(1)
    end
  end

  describe "values_at" do
    it "returns the given indexes" do
      ["a", "b", "c", "d"].values_at(1, 0, 2).should eq({"b", "a", "c"})
    end

    it "raises when passed an invalid index" do
      expect_raises IndexError do
        ["a"].values_at(10)
      end
    end

    it "works with mixed types" do
      [1, "a", 1.0, 'a'].values_at(0, 1, 2, 3).should eq({1, "a", 1.0, 'a'})
    end
  end

  it "find the element by using binary search" do
    [2, 5, 7, 10].bsearch { |x| x >= 4 }.should eq 5
    [2, 5, 7, 10].bsearch { |x| x > 10 }.should be_nil
    [2, 5, 7, 10].bsearch { |x| x >= 4 ? 1 : nil }.should eq 5
  end

  it "find the index by using binary search" do
    [2, 5, 7, 10].bsearch_index { |x, i| x >= 4 }.should eq 1
    [2, 5, 7, 10].bsearch_index { |x, i| x > 10 }.should be_nil

    [2, 5, 7, 10].bsearch_index { |x, i| i >= 3 }.should eq 3
    [2, 5, 7, 10].bsearch_index { |x, i| i > 3 }.should be_nil
  end

  it "does clear" do
    a = [1, 2, 3]
    a.clear
    a.should eq([] of Int32)
  end

  it "does clone" do
    x = {1 => 2}
    a = [x]
    b = a.clone
    b.should eq(a)
    a.should_not be(b)
    a[0].should_not be(b[0])
  end

  it "does clone with recursive array" do
    ary = [] of RecursiveArray
    ary << ary
    clone = ary.clone
    clone[0].should be(clone)
  end

  it "does compact" do
    a = [1, nil, 2, nil, 3]
    b = a.compact.should eq([1, 2, 3])
    a.should eq([1, nil, 2, nil, 3])
  end

  it "does compact!" do
    a = [1, nil, 2, nil, 3]
    b = a.compact!
    b.should eq([1, 2, 3])
    b.should be(a)
  end

  describe "concat" do
    it "concats small arrays" do
      a = [1, 2, 3]
      a.concat([4, 5, 6])
      a.should eq([1, 2, 3, 4, 5, 6])
    end

    it "concats large arrays" do
      a = [1, 2, 3]
      a.concat((4..1000).to_a)
      a.should eq((1..1000).to_a)
    end

    it "concats enumerable" do
      a = [1, 2, 3]
      a.concat((4..1000))
      a.should eq((1..1000).to_a)
    end

    it "concats enumerable to empty array (#2047)" do
      a = [] of Int32
      a.concat(1..1)
      a.@capacity.should eq(3)

      a = [] of Int32
      a.concat(1..4)
      a.@capacity.should eq(6)
    end

    it "concats indexable" do
      a = [1, 2, 3]
      a.concat(Slice.new(97) { |i| i + 4 })
      a.should eq((1..100).to_a)

      a = [1, 2, 3]
      a.concat(StaticArray(Int32, 97).new { |i| i + 4 })
      a.should eq((1..100).to_a)

      a = [1, 2, 3]
      a.concat(Deque.new(97) { |i| i + 4 })
      a.should eq((1..100).to_a)
    end

    it "concats a union of arrays" do
      a = [1, '2']
      a.concat([3] || ['4'])
      a.should eq([1, '2', 3])
    end
  end

  describe "delete" do
    it "deletes many" do
      a = [1, 2, 3, 1, 2, 3]
      a.delete(2).should eq(2)
      a.should eq([1, 3, 1, 3])
    end

    it "delete not found" do
      a = [1, 2]
      a.delete(4).should be_nil
      a.should eq([1, 2])
    end
  end

  describe "delete_at" do
    it "deletes positive index" do
      a = [1, 2, 3, 4]
      a.delete_at(1).should eq(2)
      a.should eq([1, 3, 4])
    end

    it "deletes at beginning is same as shift" do
      a = [1, 2, 3, 4]
      buffer = a.@buffer
      a.delete_at(0)
      a.should eq([2, 3, 4])
      a.@offset_to_buffer.should eq(1)
      a.@buffer.should eq(buffer + 1)
    end

    it "deletes use range" do
      a = [1, 2, 3]
      a.delete_at(1).should eq(2)
      a.should eq([1, 3])

      a = [1, 2, 3]
      a.delete_at(-1).should eq(3)
      a.should eq([1, 2])

      a = [1, 2, 3]
      a.delete_at(-2..-1).should eq([2, 3])
      a.should eq([1])

      a = [1, 2, 3]
      a.delete_at(1, 2).should eq([2, 3])
      a.should eq([1])

      a = [1, 2, 3]
      a.delete_at(1..5).should eq([2, 3])
      a.should eq([1])
      a.size.should eq(1)

      a = [1, 2, 3, 4, 5]
      a.delete_at(1..2)
      a.should eq([1, 4, 5])

      a = [1, 2, 3, 4, 5, 6, 7]
      a.delete_at(1..2)
      a.should eq([1, 4, 5, 6, 7])

      a = [1, 2, 3, 4, 5, 6, 7]
      a.delete_at(3..nil)
      a.should eq([1, 2, 3])
    end

    it "deletes with index and count" do
      a = [1, 2, 3, 4, 5]
      a.delete_at(1, 2)
      a.should eq([1, 4, 5])

      a = [1, 2, 3, 4, 5, 6, 7]
      a.delete_at(1, 2)
      a.should eq([1, 4, 5, 6, 7])
    end

    it "returns empty if at end" do
      a = [1]
      a.delete_at(1, 0).should eq([] of Int32)
      a.delete_at(1, 10).should eq([] of Int32)
      a.delete_at(1..0).should eq([] of Int32)
      a.delete_at(1..10).should eq([] of Int32)
      a.should eq([1])
    end

    it "deletes negative index" do
      a = [1, 2, 3, 4]
      a.delete_at(-3).should eq(2)
      a.should eq([1, 3, 4])
    end

    it "deletes negative index with range" do
      a = [1, 2, 3, 4, 5, 6]
      a.delete_at(-3, 2).should eq([4, 5])
      a.should eq([1, 2, 3, 6])
    end

    it "deletes negative index with range, out of bounds" do
      a = [1, 2, 3, 4, 5, 6]

      expect_raises IndexError do
        a.delete_at(-7, 2)
      end
    end

    it "deletes out of bounds" do
      expect_raises IndexError do
        [1].delete_at(2)
      end
      expect_raises IndexError do
        [1].delete_at(2, 1)
      end
      expect_raises IndexError do
        [1].delete_at(2..3)
      end
      expect_raises IndexError do
        [1].delete_at(-2..-1)
      end
    end
  end

  it "does dup" do
    x = {1 => 2}
    a = [x]
    b = a.dup
    b.should eq([x])
    a.should_not be(b)
    a[0].should be(b[0])
    b << {3 => 4}
    a.should eq([x])
  end

  it "does each_index" do
    a = [1, 1, 1]
    b = 0
    a.each_index { |i| b += i }.should be_nil
    b.should eq(3)
  end

  describe "empty" do
    it "is empty" do
      ([] of Int32).empty?.should be_true
    end

    it "is not empty" do
      [1].empty?.should be_false
    end
  end

  it "does equals? with custom block" do
    a = [1, 3, 2]
    b = [3, 9, 4]
    c = [5, 7, 3]
    d = [1, 3, 2, 4]
    f = ->(x : Int32, y : Int32) { (x % 2) == (y % 2) }
    a.equals?(b, &f).should be_true
    a.equals?(c, &f).should be_false
    a.equals?(d, &f).should be_false
  end

  describe "#fill" do
    it "replaces values in a subrange" do
      a = [0, 1, 2, 3, 4]
      a.fill(7).should be(a)
      a.should eq([7, 7, 7, 7, 7])

      a = [0, 1, 2, 3, 4]
      a.fill(7, 1, 2).should be(a)
      a.should eq([0, 7, 7, 3, 4])

      a = [0, 1, 2, 3, 4]
      a.fill(7, 2..3).should be(a)
      a.should eq([0, 1, 7, 7, 4])

      a = [0, 0, 0, 0, 0]
      a.fill { |i| i + 7 }.should be(a)
      a.should eq([7, 8, 9, 10, 11])

      a = [0, 0, 0, 0, 0]
      a.fill(offset: 2) { |i| i * i }.should be(a)
      a.should eq([4, 9, 16, 25, 36])

      a = [0, 0, 0, 0, 0]
      a.fill(1, 2) { |i| i + 7 }.should be(a)
      a.should eq([0, 8, 9, 0, 0])

      a = [0, 0, 0, 0, 0]
      a.fill(2..3) { |i| i + 7 }.should be(a)
      a.should eq([0, 0, 9, 10, 0])
    end
  end

  describe "first" do
    it "gets first when non empty" do
      a = [1, 2, 3]
      a.first.should eq(1)
    end

    it "raises when empty" do
      expect_raises Enumerable::EmptyError do
        ([] of Int32).first
      end
    end

    it "returns a sub array with given number of elements" do
      arr = [1, 2, 3]
      arr.first(0).should eq([] of Int32)
      arr.first(1).should eq [1]
      arr.first(2).should eq [1, 2]
      arr.first(3).should eq [1, 2, 3]
      arr.first(4).should eq [1, 2, 3]
    end
  end

  describe "first?" do
    it "gets first? when non empty" do
      a = [1, 2, 3]
      a.first?.should eq(1)
    end

    it "gives nil when empty" do
      ([] of Int32).first?.should be_nil
    end
  end

  it "does hash" do
    a = [1, 2, [3]]
    b = [1, 2, [3]]
    a.hash.should eq(b.hash)
  end

  describe "index" do
    it "performs without a block" do
      a = [1, 2, 3]
      a.index(3).should eq(2)
      a.index(4).should be_nil
    end

    it "performs without a block and offset" do
      a = [1, 2, 3, 1, 2, 3]
      a.index(3, offset: 3).should eq(5)
      a.index(3, offset: -3).should eq(5)
    end

    it "performs with a block" do
      a = [1, 2, 3]
      a.index { |i| i > 1 }.should eq(1)
      a.index { |i| i > 3 }.should be_nil
    end

    it "performs with a block and offset" do
      a = [1, 2, 3, 1, 2, 3]
      a.index(offset: 3) { |i| i > 1 }.should eq(4)
      a.index(offset: -3) { |i| i > 1 }.should eq(4)
    end

    it "raises if out of bounds" do
      expect_raises IndexError do
        [1, 2, 3][4]
      end
    end
  end

  describe "insert" do
    it "inserts with positive index" do
      a = [1, 3, 4]
      expected = [1, 2, 3, 4]
      a.insert(1, 2).should eq(expected)
      a.should eq(expected)
    end

    it "inserts with negative index" do
      a = [1, 2, 3]
      expected = [1, 2, 3, 4]
      a.insert(-1, 4).should eq(expected)
      a.should eq(expected)
    end

    it "inserts with negative index (2)" do
      a = [1, 2, 3]
      expected = [4, 1, 2, 3]
      a.insert(-4, 4).should eq(expected)
      a.should eq(expected)
    end

    it "inserts out of range" do
      a = [1, 3, 4]

      expect_raises IndexError do
        a.insert(4, 1)
      end
    end
  end

  describe "insert_all" do
    it "inserts with index 0" do
      a = [2, 3]
      a.insert_all(0, [0, 1]).should be a
      a.should eq([0, 1, 2, 3])
    end

    it "inserts with positive index" do
      a = [1, 2, 5, 6]
      a.insert_all(2, [3, 4]).should be a
      a.should eq([1, 2, 3, 4, 5, 6])
    end

    it "inserts with index of #size" do
      a = [1, 2, 3]
      a.insert_all(a.size, [4, 5]).should be a
      a.should eq([1, 2, 3, 4, 5])
    end

    it "inserts with negative index" do
      a = [1, 2, 3]
      a.insert_all(-1, [4, 5]).should be a
      a.should eq([1, 2, 3, 4, 5])
    end

    it "inserts with negative index (2)" do
      a = [1, 2, 5, 6]
      a.insert_all(-3, [3, 4]).should be a
      a.should eq([1, 2, 3, 4, 5, 6])
    end

    it "inserts when empty" do
      a = [] of Int32
      a.insert_all(0, [1, 2, 3]).should be a
      a.should eq([1, 2, 3])
    end

    it "inserts when other is empty" do
      a = [1, 2, 3]
      a.insert_all(1, [] of Int32).should be a
      a.should eq([1, 2, 3])
    end

    it "raises with index greater than size" do
      a = [1, 2, 3]
      expect_raises IndexError do
        a.insert_all(4, [4, 5])
      end
    end

    it "raises with negative index greater than size" do
      a = [1, 2, 3]
      expect_raises IndexError do
        a.insert_all(-5, [4, 5])
      end
    end

    it "inserts indexable" do
      a = [1, 9, 10]
      a.insert_all(1, Slice.new(3, 8)).should be a
      a.should eq([1, 8, 8, 8, 9, 10])

      a.insert_all(-6, StaticArray(Int32, 3).new { |i| i + 2 }).should be a
      a.should eq([1, 2, 3, 4, 8, 8, 8, 9, 10])

      a.insert_all(4, Deque{5, 6, 7}).should be a
      a.should eq([1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 9, 10])
    end
  end

  describe "inspect" do
    it { [1, 2, 3].inspect.should eq("[1, 2, 3]") }
  end

  describe "last" do
    it "gets last when non empty" do
      a = [1, 2, 3]
      a.last.should eq(3)
    end

    it "raises when empty" do
      expect_raises IndexError do
        ([] of Int32).last
      end
    end

    it "returns a sub array with given number of elements" do
      arr = [1, 2, 3]
      arr.last(0).should eq([] of Int32)
      arr.last(1).should eq [3]
      arr.last(2).should eq [2, 3]
      arr.last(3).should eq [1, 2, 3]
      arr.last(4).should eq [1, 2, 3]
    end
  end

  describe "size" do
    it "has size 0" do
      ([] of Int32).size.should eq(0)
    end

    it "has size 2" do
      [1, 2].size.should eq(2)
    end
  end

  it "does map" do
    a = [1, 2, 3]
    a.map { |x| x * 2 }.should eq([2, 4, 6])
    a.should eq([1, 2, 3])
  end

  it "does map!" do
    a = [1, 2, 3]
    a.map! { |x| x * 2 }
    a.should eq([2, 4, 6])
  end

  describe "pop" do
    it "pops when non empty" do
      a = [1, 2, 3]
      a.pop.should eq(3)
      a.should eq([1, 2])
    end

    it "raises when empty" do
      expect_raises IndexError do
        ([] of Int32).pop
      end
    end

    it "pops many elements" do
      a = [1, 2, 3, 4, 5]
      b = a.pop(3)
      b.should eq([3, 4, 5])
      a.should eq([1, 2])
    end

    it "pops more elements that what is available" do
      a = [1, 2, 3, 4, 5]
      b = a.pop(10)
      b.should eq([1, 2, 3, 4, 5])
      a.should eq([] of Int32)
    end

    it "pops negative count raises" do
      a = [1, 2]
      expect_raises ArgumentError do
        a.pop(-1)
      end
    end
  end

  it "does product with block" do
    r = [] of Int32
    [1, 2, 3].product([5, 6]) { |a, b| r << a; r << b }
    r.should eq([1, 5, 1, 6, 2, 5, 2, 6, 3, 5, 3, 6])
  end

  it "does product without block" do
    [1, 2, 3].product(['a', 'b']).should eq([{1, 'a'}, {1, 'b'}, {2, 'a'}, {2, 'b'}, {3, 'a'}, {3, 'b'}])
  end

  describe "push" do
    it "pushes one element" do
      a = [1, 2]
      a.push(3).should be(a)
      a.should eq [1, 2, 3]
    end

    it "pushes multiple elements" do
      a = [1, 2]
      a.push(3, 4).should be(a)
      a.should eq [1, 2, 3, 4]
    end

    it "pushes multiple elements to an empty array" do
      a = [] of Int32
      a.push(1, 2, 3).should be(a)
      a.should eq([1, 2, 3])
    end

    it "has the << alias" do
      a = [1, 2]
      a << 3
      a.should eq [1, 2, 3]
    end
  end

  describe "#replace" do
    it "replaces all elements" do
      a = [1, 2, 3]
      b = [4, 5, 6]
      a.replace(b).should be(a)
      a.should eq(b)
    end

    it "reuses the buffer if possible" do
      a = [1, 2, 3, 4, 5]
      a.shift
      b = [6, 7, 8, 9, 10]
      a.replace(b).should be(a)
      a.should eq(b)
      a.@capacity.should eq(5)
      a.@offset_to_buffer.should eq(0)

      a = [1, 2, 3, 4, 5]
      a.shift(2)
      b = [6, 7, 8, 9]
      a.replace(b).should be(a)
      a.should eq(b)
      a.@capacity.should eq(5)
      a.@offset_to_buffer.should eq(1)
    end

    it "resizes the buffer if capacity is not enough" do
      a = [1, 2, 3, 4, 5]
      b = [6, 7, 8, 9, 10, 11]
      a.replace(b).should be(a)
      a.should eq(b)
      a.@capacity.should eq(10)
      a.@offset_to_buffer.should eq(0)
    end

    it "clears unused elements if new size is smaller" do
      a = [1, 2, 3, 4, 5]
      b = [6, 7, 8]
      a.replace(b).should be(a)
      a.should eq(b)
      a.@capacity.should eq(5)
      a.@offset_to_buffer.should eq(0)
      a.unsafe_fetch(3).should eq(0)
      a.unsafe_fetch(4).should eq(0)
    end
  end

  it "does reverse with an odd number of elements" do
    a = [1, 2, 3]
    a.reverse.should eq([3, 2, 1])
    a.should eq([1, 2, 3])
  end

  it "does reverse with an even number of elements" do
    a = [1, 2, 3, 4]
    a.reverse.should eq([4, 3, 2, 1])
    a.should eq([1, 2, 3, 4])
  end

  it "does reverse! with an odd number of elements" do
    a = [1, 2, 3, 4, 5]
    a.reverse!
    a.should eq([5, 4, 3, 2, 1])
  end

  it "does reverse! with an even number of elements" do
    a = [1, 2, 3, 4, 5, 6]
    a.reverse!
    a.should eq([6, 5, 4, 3, 2, 1])
  end

  describe "rindex" do
    it "performs without a block" do
      a = [1, 2, 3, 4, 5, 3, 6]
      a.rindex(3).should eq(5)
      a.rindex(7).should be_nil
    end

    it "performs without a block and an offset" do
      a = [1, 2, 3, 4, 5, 3, 6]
      a.rindex(3, offset: 4).should eq(2)
      a.rindex(6, offset: 4).should be_nil
      a.rindex(3, offset: -2).should eq(5)
      a.rindex(3, offset: -3).should eq(2)
      a.rindex(3, offset: -100).should be_nil
    end

    it "performs with a block" do
      a = [1, 2, 3, 4, 5, 3, 6]
      a.rindex { |i| i > 1 }.should eq(6)
      a.rindex { |i| i > 6 }.should be_nil
    end

    it "performs with a block and offset" do
      a = [1, 2, 3, 4, 5, 3, 6]
      a.rindex { |i| i > 1 }.should eq(6)
      a.rindex { |i| i > 6 }.should be_nil
      a.rindex(offset: 4) { |i| i == 3 }.should eq(2)
      a.rindex(offset: -3) { |i| i == 3 }.should eq(2)
    end
  end

  describe "shift" do
    it "shifts when non empty" do
      a = [1, 2, 3]
      a.shift.should eq(1)
      a.should eq([2, 3])
    end

    it "raises when empty" do
      expect_raises IndexError do
        ([] of Int32).shift
      end
    end

    it "shifts many elements" do
      a = [1, 2, 3, 4, 5]
      b = a.shift(3)
      b.should eq([1, 2, 3])
      a.should eq([4, 5])
    end

    it "shifts more than what is available" do
      a = [1, 2, 3, 4, 5]
      b = a.shift(10)
      b.should eq([1, 2, 3, 4, 5])
      a.should eq([] of Int32)
    end

    it "shifts negative count raises" do
      a = [1, 2]
      expect_raises ArgumentError do
        a.shift(-1)
      end
    end

    it "shifts one and resizes" do
      a = [1, 2, 3, 4]
      old_capacity = a.@capacity
      a.shift.should eq(1)
      a.@offset_to_buffer.should eq(1)
      a << 5
      a.@capacity.should eq(old_capacity * 2)
      a.@offset_to_buffer.should eq(1)
      a.size.should eq(4)
      a.should eq([2, 3, 4, 5])
    end

    it "shifts almost all and then avoid resize" do
      a = [1, 2, 3, 4]
      old_capacity = a.@capacity
      (1..3).each do |i|
        a.shift.should eq(i)
      end
      a.@offset_to_buffer.should eq(3)
      a << 5
      a.@capacity.should eq(old_capacity)
      a.@offset_to_buffer.should eq(0)
      a.size.should eq(2)
      a.should eq([4, 5])
    end

    it "shifts and then concats Array" do
      size = 10_000
      a = (1..size).to_a
      (size - 1).times do
        a.shift
      end
      a.size.should eq(1)
      a.concat((1..size).to_a)
      a.size.should eq(size + 1)
      a.should eq([size] + (1..size).to_a)
    end

    it "shifts and then concats Enumerable" do
      size = 10_000
      a = (1..size).to_a
      (size - 1).times do
        a.shift
      end
      a.size.should eq(1)
      a.concat((1..size))
      a.size.should eq(size + 1)
      a.should eq([size] + (1..size).to_a)
    end

    it "shifts all" do
      a = [1, 2, 3, 4]
      buffer = a.@buffer
      4.times do
        a.shift
      end
      a.size.should eq(0)
      a.@offset_to_buffer.should eq(0)
      a.@buffer.should eq(buffer)
    end

    it "shifts all after pop" do
      a = [1, 2, 3, 4]
      buffer = a.@buffer
      a.pop
      3.times do
        a.shift
      end
      a.size.should eq(0)
      a.@offset_to_buffer.should eq(0)
      a.@buffer.should eq(buffer)
    end

    it "pops after shift" do
      a = [1, 2, 3, 4]
      buffer = a.@buffer
      3.times do
        a.shift
      end
      a.pop
      a.size.should eq(0)
      a.@offset_to_buffer.should eq(0)
      a.@buffer.should eq(buffer)
    end

    it "shifts all with shift(n)" do
      a = [1, 2, 3, 4]
      buffer = a.@buffer
      a.shift
      a.shift(3)
      a.size.should eq(0)
      a.@offset_to_buffer.should eq(0)
      a.@buffer.should eq(buffer)
    end
  end

  describe "shuffle" do
    it "shuffle!" do
      a = [1, 2, 3]
      a.shuffle!
      b = [1, 2, 3]
      3.times { a.should contain(b.shift) }
    end

    it "shuffle" do
      a = [1, 2, 3]
      b = a.shuffle
      a.should_not be(b)
      a.should eq([1, 2, 3])

      3.times { b.should contain(a.shift) }
    end

    it "shuffle! with random" do
      a = [1, 2, 3]
      a.shuffle!(Random.new(1))
      a.should eq([1, 3, 2])
    end

    it "shuffle with random" do
      a = [1, 2, 3]
      b = a.shuffle(Random.new(1))
      b.should eq([1, 3, 2])
    end
  end

  describe "sort" do
    {% for sort in ["sort".id, "unstable_sort".id] %}
      describe {{ "##{sort}" }} do
        it "without block" do
          a = [3, 4, 1, 2, 5, 6]
          b = a.{{ sort }}
          b.should eq([1, 2, 3, 4, 5, 6])
          a.should_not eq(b)
        end

        it "with a block" do
          a = ["foo", "a", "hello"]
          b = a.{{ sort }} { |x, y| x.size <=> y.size }
          b.should eq(["a", "foo", "hello"])
          a.should_not eq(b)
        end

        {% if sort == "sort" %}
          it "stable sort without a block" do
            is_stable_sort(mutable: false, &.sort)
          end

          it "stable sort with a block" do
            is_stable_sort(mutable: false, &.sort { |a, b| a.value <=> b.value })
          end
        {% end %}
      end

      describe {{ "##{sort}!" }} do
        it "without block" do
          a = [3, 4, 1, 2, 5, 6]
          a.{{ sort.id }}!
          a.should eq([1, 2, 3, 4, 5, 6])
        end

        it "with a block" do
          a = ["foo", "a", "hello"]
          a.{{ sort.id }}! { |x, y| x.size <=> y.size }
          a.should eq(["a", "foo", "hello"])
        end

        {% if sort == "sort" %}
          it "stable sort without a block" do
            is_stable_sort(mutable: true, &.sort!)
          end

          it "stable sort with a block" do
            is_stable_sort(mutable: true, &.sort! { |a, b| a.value <=> b.value })
          end
        {% end %}
      end

      describe {{ "##{sort}_by" }} do
        it "sorts" do
          a = ["foo", "a", "hello"]
          b = a.{{ sort }}_by(&.size)
          b.should eq(["a", "foo", "hello"])
          a.should_not eq(b)
        end

        it "unpacks tuple" do
          a = [{"d", 4}, {"a", 1}, {"c", 3}, {"e", 5}, {"b", 2}]
          b = a.{{ sort }}_by { |x, y| y }
          b.should eq([{"a", 1}, {"b", 2}, {"c", 3}, {"d", 4}, {"e", 5}])
          a.should_not eq(b)
        end

        {% if sort == "sort" %}
          it "stable sort" do
            is_stable_sort(mutable: false, &.sort_by(&.value))
          end
        {% end %}
      end

      describe {{ "##{sort}_by!" }} do
        it "sorts" do
          a = ["foo", "a", "hello"]
          a.{{ sort }}_by!(&.size)
          a.should eq(["a", "foo", "hello"])
        end

        it "calls given block exactly once for each element" do
          calls = Hash(String, Int32).new(0)
          a = ["foo", "a", "hello"]
          a.{{ sort }}_by! { |e| calls[e] += 1; e.size }
          calls.should eq({"foo" => 1, "a" => 1, "hello" => 1})
        end

        {% if sort == "sort" %}
          it "stable sort" do
            is_stable_sort(mutable: true, &.sort_by!(&.value))
          end
        {% end %}
      end
    {% end %}
  end

  describe "swap" do
    it "swaps" do
      a = [1, 2, 3]
      a.swap(0, 2)
      a.should eq([3, 2, 1])
    end

    it "swaps with negative indices" do
      a = [1, 2, 3]
      a.swap(-3, -1)
      a.should eq([3, 2, 1])
    end

    it "swaps but raises out of bounds on left" do
      a = [1, 2, 3]
      expect_raises IndexError do
        a.swap(3, 0)
      end
    end

    it "swaps but raises out of bounds on right" do
      a = [1, 2, 3]
      expect_raises IndexError do
        a.swap(0, 3)
      end
    end
  end

  describe "to_s" do
    it "does to_s" do
      [1, 2, 3].to_s.should eq("[1, 2, 3]")
    end

    it "does with recursive" do
      ary = [] of RecursiveArray
      ary << ary
      ary.to_s.should eq("[[...]]")
    end
  end

  describe "#truncate" do
    it "truncates with index and count" do
      a = [0, 1, 4, 9, 16, 25]
      a.truncate(2, 3).should be(a)
      a.should eq([4, 9, 16])
    end

    it "truncates with index and count == 0" do
      a = [0, 1, 4, 9, 16, 25]
      a.truncate(2, 0).should be(a)
      a.should be_empty
    end

    it "truncates with index and count, not enough elements" do
      a = [0, 1, 4, 9, 16, 25]
      a.truncate(4, 4).should be(a)
      a.should eq([16, 25])
    end

    it "truncates with index == size and count" do
      a = [0, 1, 4, 9, 16, 25]
      a.truncate(6, 1).should be(a)
      a.should be_empty
    end

    it "truncates with index < 0 and count" do
      a = [0, 1, 4, 9, 16, 25]
      a.truncate(-5, 3).should be(a)
      a.should eq([1, 4, 9])
    end

    it "raises on out of bound index" do
      a = [0, 1, 4, 9, 16, 25]
      expect_raises(IndexError) { a.truncate(-7, 1) }
    end

    it "raises on negative count" do
      a = [0, 1, 4, 9, 16, 25]
      expect_raises(ArgumentError) { a.truncate(0, -1) }
    end

    it "truncates with range" do
      a = [0, 1, 4, 9, 16, 25]
      a.truncate(2..4).should be(a)
      a.should eq([4, 9, 16])

      b = [0, 1, 4, 9, 16, 25]
      b.truncate(-5..-3).should be(b)
      b.should eq([1, 4, 9])
    end
  end

  describe "uniq" do
    it "uniqs without block" do
      a = [1, 2, 2, 3, 1, 4, 5, 3]
      b = a.uniq
      b.should eq([1, 2, 3, 4, 5])
      a.should_not be(b)
    end

    it "uniqs with block" do
      a = [-1, 1, 0, 2, -2]
      b = a.uniq &.abs
      b.should eq([-1, 0, 2])
      a.should_not be(b)
    end

    it "uniqs with true" do
      a = [1, 2, 3]
      b = a.uniq { true }
      b.should eq([1])
      a.should_not be(b)
    end

    it "uniqs large array" do
      a = (1..32).to_a
      (a * 4).uniq.should eq(a)
    end
  end

  describe "uniq!" do
    it "uniqs without block" do
      a = [1, 2, 2, 3, 1, 4, 5, 3]
      a.uniq!
      a.should eq([1, 2, 3, 4, 5])
    end

    it "uniqs with block" do
      a = [-1, 1, 0, 2, -2]
      a.uniq! &.abs
      a.should eq([-1, 0, 2])
    end

    it "uniqs with true" do
      a = [1, 2, 3]
      a.uniq! { true }
      a.should eq([1])
    end

    it "uniqs large array" do
      a = (1..32).to_a
      b = a * 2
      b.uniq!
      b.should eq(a)
    end
  end

  describe "unshift" do
    it "unshifts one element" do
      a = [1, 2]
      a.unshift(3).should be(a)
      a.should eq [3, 1, 2]
    end

    it "unshifts one elements three times" do
      a = [] of Int32
      3.times do |i|
        a.unshift(i)
      end
      a.should eq([2, 1, 0])
    end

    it "unshifts one element multiple times" do
      a = [1, 2]
      (3..100).each do |i|
        a.unshift(i)
      end
      a.should eq((3..100).to_a.reverse + [1, 2])
    end

    it "unshifts multiple elements" do
      a = [1, 2]
      a.unshift(3, 4).should be(a)
      a.should eq [3, 4, 1, 2]
    end

    it "unshifts multiple elements to an empty array" do
      a = [] of Int32
      a.unshift(1, 2, 3).should be(a)
      a.should eq([1, 2, 3])
    end

    it "unshifts after shift" do
      a = [1, 2, 3, 4]
      buffer = a.@buffer
      a.shift
      a.unshift(10)
      a.should eq([10, 2, 3, 4])
      a.@offset_to_buffer.should eq(0)
      a.@buffer.should eq(buffer)
    end

    it "unshifts many after many shifts" do
      a = [1, 2, 3, 4, 5, 6, 7, 8]
      buffer = a.@buffer
      3.times { a.shift }
      a.unshift(10, 20, 30)
      a.should eq([10, 20, 30, 4, 5, 6, 7, 8])
      a.@offset_to_buffer.should eq(0)
      a.@buffer.should eq(buffer)
    end

    it "repeated unshift/shift does not exhaust memory" do
      a = [] of Int32
      10.times do
        a.unshift(1)
        a.shift
      end
      a.@capacity.should eq(3)
    end

    it "repeated unshift/pop does not exhaust memory (#10748)" do
      a = [] of Int32
      10.times do
        a.unshift(1)
        a.pop
      end
      a.@capacity.should eq(3)
    end

    it "repeated unshift/clear does not exhaust memory" do
      a = [] of Int32
      10.times do
        a.unshift(1)
        a.clear
      end
      a.@capacity.should eq(3)
    end

    it "unshift of large array does not corrupt elements" do
      a = Array.new(300, &.itself)
      a.@capacity.should eq(300)
      a.unshift 1
      a.@capacity.should be > 300
      a[-1].should eq(299)
    end
  end

  it "does update" do
    a = [1, 2, 3]
    a.update(1) { |x| x * 2 }
    a.should eq([1, 4, 3])
  end

  it "does <=>" do
    a = [1, 2, 3]
    b = [4, 5, 6]
    c = [1, 2]

    (a <=> b).should be < 0
    (a <=> c).should be > 0
    (b <=> c).should be > 0
    (b <=> a).should be > 0
    (c <=> a).should be < 0
    (c <=> b).should be < 0
    (a <=> a).should eq(0)

    ([8] <=> [1, 2, 3]).should be > 0
    ([8] <=> [8, 1, 2]).should be < 0

    [[1, 2, 3], [4, 5], [8], [1, 2, 3, 4]].sort.should eq([[1, 2, 3], [1, 2, 3, 4], [4, 5], [8]])
  end

  it "does each while modifying array" do
    a = [1, 2, 3]
    count = 0
    a.each do
      count += 1
      a.clear
    end.should be_nil
    count.should eq(1)
  end

  it "does each index while modifying array" do
    a = [1, 2, 3]
    count = 0
    a.each_index do
      count += 1
      a.clear
    end.should be_nil
    count.should eq(1)
  end

  describe "zip" do
    describe "when a block is provided" do
      it "yields pairs of self's elements and passed array" do
        a, b, r = [1, 2, 3], [4, 5, 6], ""
        a.zip(b) { |x, y| r += "#{x}:#{y}," }
        r.should eq("1:4,2:5,3:6,")
      end

      it "works with iterable" do
        a = [1, 2, 3]
        b = ('a'..'c')
        r = [] of {Int32, Char}
        a.zip(b) do |x, y|
          r << {x, y}
        end
        r.should eq([{1, 'a'}, {2, 'b'}, {3, 'c'}])
      end

      it "works with iterator" do
        a = [1, 2, 3]
        b = ('a'..'c').each
        r = [] of {Int32, Char}
        a.zip(b) do |x, y|
          r << {x, y}
        end
        r.should eq([{1, 'a'}, {2, 'b'}, {3, 'c'}])
      end
    end

    describe "when no block is provided" do
      describe "and the arrays have different typed elements" do
        it "returns an array of paired elements (tuples)" do
          a, b = [1, 2, 3], ["a", "b", "c"]
          r = a.zip(b)
          r.should be_a(Array({Int32, String}))
          r.should eq([{1, "a"}, {2, "b"}, {3, "c"}])
        end

        it "works with iterable" do
          a = [1, 2, 3]
          b = ('a'..'c')
          r = a.zip(b)
          r.should be_a(Array({Int32, Char}))
          r.should eq([{1, 'a'}, {2, 'b'}, {3, 'c'}])
        end

        it "works with iterator" do
          a = [1, 2, 3]
          b = ('a'..'c').each
          r = a.zip(b)
          r.should be_a(Array({Int32, Char}))
          r.should eq([{1, 'a'}, {2, 'b'}, {3, 'c'}])
        end

        it "zips three things" do
          a = [1, 2, 3]
          b = 'a'..'c'
          c = ('x'..'z').each
          r = a.zip(b, c)
          r.should be_a(Array({Int32, Char, Char}))
          r.should eq([{1, 'a', 'x'}, {2, 'b', 'y'}, {3, 'c', 'z'}])
        end

        it "zips union type (#8608)" do
          a = [1, 2, 3]
          b = 'a'..'c'
          c = ('x'..'z').each
          r = a.zip(a || b || c)
          r.should be_a(Array({Int32, Int32 | Char}))
          r.should eq([{1, 1}, {2, 2}, {3, 3}])
        end
      end
    end
  end

  describe "zip?" do
    describe "when a block is provided" do
      describe "and size of an arg is less than receiver" do
        it "yields pairs of self's elements and passed array (with nil)" do
          a, b, r = [1, 2, 3], [4, 5], ""
          a.zip?(b) { |x, y| r += "#{x}:#{y}," }
          r.should eq("1:4,2:5,3:,")
        end

        it "works with iterable" do
          a = [1, 2, 3]
          b = ('a'..'b')
          r = [] of {Int32, Char?}
          a.zip?(b) do |x, y|
            r << {x, y}
          end
          r.should eq([{1, 'a'}, {2, 'b'}, {3, nil}])
        end

        it "works with iterator" do
          a = [1, 2, 3]
          b = ('a'..'b').each
          r = [] of {Int32, Char?}
          a.zip?(b) do |x, y|
            r << {x, y}
          end
          r.should eq([{1, 'a'}, {2, 'b'}, {3, nil}])
        end
      end
    end

    describe "when no block is provided" do
      describe "and the arrays have different typed elements" do
        describe "and size of an arg is less than receiver" do
          it "returns an array of paired elements (tuples with nil)" do
            a, b = [1, 2, 3], ["a", "b"]
            r = a.zip?(b)
            r.should eq([{1, "a"}, {2, "b"}, {3, nil}])
          end

          it "works with iterable" do
            a = [1, 2, 3]
            b = ('a'..'b')
            r = a.zip?(b)
            r.should be_a(Array({Int32, Char?}))
            r.should eq([{1, 'a'}, {2, 'b'}, {3, nil}])
          end

          it "works with iterator" do
            a = [1, 2, 3]
            b = ('a'..'b').each
            r = a.zip?(b)
            r.should be_a(Array({Int32, Char?}))
            r.should eq([{1, 'a'}, {2, 'b'}, {3, nil}])
          end

          it "zips three things" do
            a = [1, 2, 3]
            b = 'a'..'b'
            c = ('x'..'y').each
            r = a.zip?(b, c)
            r.should be_a(Array({Int32, Char?, Char?}))
            r.should eq([{1, 'a', 'x'}, {2, 'b', 'y'}, {3, nil, nil}])
          end

          it "zips union type (#8608)" do
            a = [1, 2, 3]
            b = 'a'..'c'
            c = ('x'..'z').each
            r = a.zip?(a || b || c)
            r.should be_a(Array({Int32, Int32 | Char | Nil}))
            r.should eq([{1, 1}, {2, 2}, {3, 3}])
          end
        end
      end
    end
  end

  it "does compact_map" do
    a = [1, 2, 3, 4, 5]
    b = a.compact_map { |e| e.divisible_by?(2) ? e : nil }
    b.size.should eq(2)
    b.should eq([2, 4])
  end

  it "does compact_map with false" do
    a = [1, 2, 3]
    b = a.compact_map do |e|
      case e
      when 1 then 1
      when 2 then nil
      else        false
      end
    end
    b.size.should eq(2)
    b.should eq([1, false])
  end

  it "builds from buffer" do
    ary = Array(Int32).build(4) do |buffer|
      buffer[0] = 1
      buffer[1] = 2
      2
    end
    ary.size.should eq(2)
    ary.should eq([1, 2])
  end

  it "selects!" do
    ary1 = [1, 2, 3, 4, 5]

    ary2 = ary1.select! { |elem| elem % 2 == 0 }
    ary2.should eq([2, 4])
    ary2.should be(ary1)
  end

  it "selects! with pattern" do
    ary1 = [1, 2, 3, 4, 5]

    ary2 = ary1.select!(2..4)
    ary2.should eq([2, 3, 4])
    ary2.should be(ary1)
  end

  it "rejects!" do
    ary1 = [1, 2, 3, 4, 5]

    ary2 = ary1.reject! { |elem| elem % 2 == 0 }
    ary2.should eq([1, 3, 5])
    ary2.should be(ary1)
  end

  it "rejects! with pattern" do
    ary1 = [1, 2, 3, 4, 5]

    ary2 = ary1.reject!(2..4)
    ary2.should eq([1, 5])
    ary2.should be(ary1)
  end

  it "does map_with_index" do
    ary = [1, 1, 2, 2]
    ary2 = ary.map_with_index { |e, i| e + i }
    ary2.should eq([1, 2, 4, 5])
  end

  it "does map_with_index, with offset" do
    ary = [1, 1, 2, 2]
    ary2 = ary.map_with_index(10) { |e, i| e + i }
    ary2.should eq([11, 12, 14, 15])
  end

  it "does map_with_index!" do
    ary = [0, 1, 2]
    ary2 = ary.map_with_index! { |e, i| i * 2 }
    ary.should eq([0, 2, 4])
    ary2.should be(ary)
  end

  it "does map_with_index!, with offset" do
    ary = [0, 1, 2]
    ary2 = ary.map_with_index!(10) { |e, i| i * 2 }
    ary.should eq([20, 22, 24])
    ary2.should be(ary)
  end

  it "does + with different types (#568)" do
    a = [1, 2, 3]
    a += ["hello"]
    a.should eq([1, 2, 3, "hello"])
  end

  it_iterates "#each", [1, 2, 3], [1, 2, 3].each
  it_iterates "#reverse_each", [3, 2, 1], [1, 2, 3].reverse_each

  it_iterates "#cycle", [1, 2, 3, 1, 2, 3, 1, 2], [1, 2, 3].cycle, infinite: true
  it_iterates "#cycle(limit)", [1, 2, 3, 1, 2, 3], [1, 2, 3].cycle(2), infinite: true

  it_iterates "#each_index", [0, 1, 2], [1, 2, 3].each_index

  describe "transpose" do
    it "transposes elements" do
      [['a', 'b'], ['c', 'd'], ['e', 'f']].transpose.should eq([['a', 'c', 'e'], ['b', 'd', 'f']])
      [['a', 'c', 'e'], ['b', 'd', 'f']].transpose.should eq([['a', 'b'], ['c', 'd'], ['e', 'f']])
      [['a']].transpose.should eq([['a']])
    end

    it "transposes union of arrays" do
      [[1, 2], [1.0, 2.0]].transpose.should eq([[1, 1.0], [2, 2.0]])
      [[1, 2.0], [1, 2.0]].transpose.should eq([[1, 1], [2.0, 2.0]])
      [[1, 1.0], ['a', "aaa"]].transpose.should eq([[1, 'a'], [1.0, "aaa"]])

      typeof([[1.0], [1]].transpose).should eq(Array(Array(Int32 | Float64)))
      typeof([[1, 1.0], ['a', "aaa"]].transpose).should eq(Array(Array(String | Int32 | Float64 | Char)))
    end

    it "transposes empty array" do
      e = [] of Array(Int32)
      e.transpose.should be_empty
      [e].transpose.should be_empty
      [e, e, e].transpose.should be_empty
    end

    it "raises IndexError error when size of element is invalid" do
      expect_raises(IndexError) { [[1], [1, 2]].transpose }
      expect_raises(IndexError) { [[1, 2], [1]].transpose }
    end

    it "transposes array of tuples" do
      [{1, 1.0}].transpose.should eq([[1], [1.0]])
      [{1}, {1.0}].transpose.should eq([[1, 1.0]])
      [{1, 1.0}, {'a', "aaa"}].transpose.should eq([[1, 'a'], [1.0, "aaa"]])

      typeof([{1, 1.0}].transpose).should eq(Array(Array(Int32 | Float64)))
      typeof([{1}, {1.0}].transpose).should eq(Array(Array(Int32 | Float64)))
      typeof([{1, 1.0}, {'a', "aaa"}].transpose).should eq(Array(Array(String | Int32 | Float64 | Char)))
    end
  end

  describe "rotate" do
    it "rotate!" do
      a = [1, 2, 3]
      a.rotate!.should be(a); a.should eq([2, 3, 1])
      a.rotate!.should be(a); a.should eq([3, 1, 2])
      a.rotate!.should be(a); a.should eq([1, 2, 3])
      a.rotate!.should be(a); a.should eq([2, 3, 1])
    end

    it "rotate" do
      a = [1, 2, 3]
      a.rotate.should eq([2, 3, 1])
      a.should eq([1, 2, 3])
      a.rotate.should eq([2, 3, 1])
    end

    it { a = [1, 2, 3]; a.rotate!(0); a.should eq([1, 2, 3]) }
    it { a = [1, 2, 3]; a.rotate!(1); a.should eq([2, 3, 1]) }
    it { a = [1, 2, 3]; a.rotate!(2); a.should eq([3, 1, 2]) }
    it { a = [1, 2, 3]; a.rotate!(3); a.should eq([1, 2, 3]) }
    it { a = [1, 2, 3]; a.rotate!(4); a.should eq([2, 3, 1]) }
    it { a = [1, 2, 3]; a.rotate!(3001); a.should eq([2, 3, 1]) }
    it { a = [1, 2, 3]; a.rotate!(-1); a.should eq([3, 1, 2]) }
    it { a = [1, 2, 3]; a.rotate!(-3001); a.should eq([3, 1, 2]) }

    it { a = [1, 2, 3]; a.rotate(0).should eq([1, 2, 3]); a.should eq([1, 2, 3]) }
    it { a = [1, 2, 3]; a.rotate(1).should eq([2, 3, 1]); a.should eq([1, 2, 3]) }
    it { a = [1, 2, 3]; a.rotate(2).should eq([3, 1, 2]); a.should eq([1, 2, 3]) }
    it { a = [1, 2, 3]; a.rotate(3).should eq([1, 2, 3]); a.should eq([1, 2, 3]) }
    it { a = [1, 2, 3]; a.rotate(4).should eq([2, 3, 1]); a.should eq([1, 2, 3]) }
    it { a = [1, 2, 3]; a.rotate(3001).should eq([2, 3, 1]); a.should eq([1, 2, 3]) }
    it { a = [1, 2, 3]; a.rotate(-1).should eq([3, 1, 2]); a.should eq([1, 2, 3]) }
    it { a = [1, 2, 3]; a.rotate(-3001).should eq([3, 1, 2]); a.should eq([1, 2, 3]) }

    it do
      a = Array(Int32).new(50) { |i| i }
      a.rotate!(5)
      a.should eq([5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 0, 1, 2, 3, 4])
    end

    it do
      a = Array(Int32).new(50) { |i| i }
      a.rotate!(-5)
      a.should eq([45, 46, 47, 48, 49, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44])
    end

    it do
      a = Array(Int32).new(50) { |i| i }
      a.rotate!(20)
      a.should eq([20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19])
    end

    it do
      a = Array(Int32).new(50) { |i| i }
      a.rotate!(-20)
      a.should eq([30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29])
    end
  end

  describe "repeated_permutations" do
    it { [1, 2, 2].repeated_permutations.should eq([[1, 1, 1], [1, 1, 2], [1, 1, 2], [1, 2, 1], [1, 2, 2], [1, 2, 2], [1, 2, 1], [1, 2, 2], [1, 2, 2], [2, 1, 1], [2, 1, 2], [2, 1, 2], [2, 2, 1], [2, 2, 2], [2, 2, 2], [2, 2, 1], [2, 2, 2], [2, 2, 2], [2, 1, 1], [2, 1, 2], [2, 1, 2], [2, 2, 1], [2, 2, 2], [2, 2, 2], [2, 2, 1], [2, 2, 2], [2, 2, 2]]) }
    it { [1, 2, 3].repeated_permutations.should eq([[1, 1, 1], [1, 1, 2], [1, 1, 3], [1, 2, 1], [1, 2, 2], [1, 2, 3], [1, 3, 1], [1, 3, 2], [1, 3, 3], [2, 1, 1], [2, 1, 2], [2, 1, 3], [2, 2, 1], [2, 2, 2], [2, 2, 3], [2, 3, 1], [2, 3, 2], [2, 3, 3], [3, 1, 1], [3, 1, 2], [3, 1, 3], [3, 2, 1], [3, 2, 2], [3, 2, 3], [3, 3, 1], [3, 3, 2], [3, 3, 3]]) }
    it { [1, 2, 3].repeated_permutations(1).should eq([[1], [2], [3]]) }
    it { [1, 2, 3].repeated_permutations(2).should eq([[1, 1], [1, 2], [1, 3], [2, 1], [2, 2], [2, 3], [3, 1], [3, 2], [3, 3]]) }
    it { [1, 2, 3].repeated_permutations(3).should eq([[1, 1, 1], [1, 1, 2], [1, 1, 3], [1, 2, 1], [1, 2, 2], [1, 2, 3], [1, 3, 1], [1, 3, 2], [1, 3, 3], [2, 1, 1], [2, 1, 2], [2, 1, 3], [2, 2, 1], [2, 2, 2], [2, 2, 3], [2, 3, 1], [2, 3, 2], [2, 3, 3], [3, 1, 1], [3, 1, 2], [3, 1, 3], [3, 2, 1], [3, 2, 2], [3, 2, 3], [3, 3, 1], [3, 3, 2], [3, 3, 3]]) }
    it { [1, 2, 3].repeated_permutations(0).should eq([[] of Int32]) }
    it { [1, 2, 3].repeated_permutations(4).should eq([[1, 1, 1, 1], [1, 1, 1, 2], [1, 1, 1, 3], [1, 1, 2, 1], [1, 1, 2, 2], [1, 1, 2, 3], [1, 1, 3, 1], [1, 1, 3, 2], [1, 1, 3, 3], [1, 2, 1, 1], [1, 2, 1, 2], [1, 2, 1, 3], [1, 2, 2, 1], [1, 2, 2, 2], [1, 2, 2, 3], [1, 2, 3, 1], [1, 2, 3, 2], [1, 2, 3, 3], [1, 3, 1, 1], [1, 3, 1, 2], [1, 3, 1, 3], [1, 3, 2, 1], [1, 3, 2, 2], [1, 3, 2, 3], [1, 3, 3, 1], [1, 3, 3, 2], [1, 3, 3, 3], [2, 1, 1, 1], [2, 1, 1, 2], [2, 1, 1, 3], [2, 1, 2, 1], [2, 1, 2, 2], [2, 1, 2, 3], [2, 1, 3, 1], [2, 1, 3, 2], [2, 1, 3, 3], [2, 2, 1, 1], [2, 2, 1, 2], [2, 2, 1, 3], [2, 2, 2, 1], [2, 2, 2, 2], [2, 2, 2, 3], [2, 2, 3, 1], [2, 2, 3, 2], [2, 2, 3, 3], [2, 3, 1, 1], [2, 3, 1, 2], [2, 3, 1, 3], [2, 3, 2, 1], [2, 3, 2, 2], [2, 3, 2, 3], [2, 3, 3, 1], [2, 3, 3, 2], [2, 3, 3, 3], [3, 1, 1, 1], [3, 1, 1, 2], [3, 1, 1, 3], [3, 1, 2, 1], [3, 1, 2, 2], [3, 1, 2, 3], [3, 1, 3, 1], [3, 1, 3, 2], [3, 1, 3, 3], [3, 2, 1, 1], [3, 2, 1, 2], [3, 2, 1, 3], [3, 2, 2, 1], [3, 2, 2, 2], [3, 2, 2, 3], [3, 2, 3, 1], [3, 2, 3, 2], [3, 2, 3, 3], [3, 3, 1, 1], [3, 3, 1, 2], [3, 3, 1, 3], [3, 3, 2, 1], [3, 3, 2, 2], [3, 3, 2, 3], [3, 3, 3, 1], [3, 3, 3, 2], [3, 3, 3, 3]]) }
    it { expect_raises(ArgumentError, "Size must be positive") { [1].repeated_permutations(-1) } }

    it "accepts a block" do
      sums = [] of Int32
      [1, 2, 3].each_repeated_permutation(2) do |a|
        sums << a.sum
      end.should be_nil
      sums.should eq([2, 3, 4, 3, 4, 5, 4, 5, 6])
    end

    it "yielding dup of arrays" do
      sums = [] of Int32
      [1, 2, 3].each_repeated_permutation(3) do |a|
        a.map! &.+(1)
        sums << a.sum
      end.should be_nil
      sums.should eq([6, 7, 8, 7, 8, 9, 8, 9, 10, 7, 8, 9, 8, 9, 10, 9, 10, 11, 8, 9, 10, 9, 10, 11, 10, 11, 12])
    end

    it "yields with reuse = true" do
      sums = [] of Int32
      object_ids = Set(UInt64).new
      [1, 2, 3].each_repeated_permutation(3, reuse: true) do |a|
        object_ids << a.object_id
        a.map! &.+(1)
        sums << a.sum
      end.should be_nil
      sums.should eq([6, 7, 8, 7, 8, 9, 8, 9, 10, 7, 8, 9, 8, 9, 10, 9, 10, 11, 8, 9, 10, 9, 10, 11, 10, 11, 12])
      object_ids.size.should eq(1)
    end

    it "yields with reuse = array" do
      sums = [] of Int32
      reuse = [] of Int32
      [1, 2, 3].each_repeated_permutation(3, reuse: reuse) do |a|
        a.should be(reuse)
        a.map! &.+(1)
        sums << a.sum
      end.should be_nil
      sums.should eq([6, 7, 8, 7, 8, 9, 8, 9, 10, 7, 8, 9, 8, 9, 10, 9, 10, 11, 8, 9, 10, 9, 10, 11, 10, 11, 12])
    end

    it { expect_raises(ArgumentError, "Size must be positive") { [1].each_repeated_permutation(-1) { } } }
  end

  describe "Array.each_product" do
    it "one empty array" do
      empty = [] of Int32
      res = [] of Array(Int32)
      Array.each_product([empty, [1, 2, 3]]) { |r| res << r }
      Array.each_product([[1, 2, 3], empty]) { |r| res << r }
      res.size.should eq(0)
    end

    it "single array" do
      res = [] of Array(Int32)
      Array.each_product([[1]]) { |r| res << r }
      res.should eq([[1]])
    end

    it "2 arrays" do
      res = [] of Array(Int32)
      Array.each_product([[1, 2], [3, 4]]) { |r| res << r }
      res.should eq([[1, 3], [1, 4], [2, 3], [2, 4]])
    end

    it "2 arrays different types" do
      res = [] of Array(Int32 | Char)
      Array.each_product([[1, 2], ['a', 'b']]) { |r| res << r }
      res.should eq([[1, 'a'], [1, 'b'], [2, 'a'], [2, 'b']])
    end

    it "more arrays" do
      res = [] of Array(Int32)
      Array.each_product([[1, 2], [3], [5, 6]]) { |r| res << r }
      res.should eq([[1, 3, 5], [1, 3, 6], [2, 3, 5], [2, 3, 6]])
    end

    it "more arrays, reuse = true" do
      res = [] of Array(Int32)
      object_ids = Set(UInt64).new
      Array.each_product([[1, 2], [3], [5, 6]], reuse: true) do |r|
        object_ids << r.object_id
        res << r.dup
      end
      res.should eq([[1, 3, 5], [1, 3, 6], [2, 3, 5], [2, 3, 6]])
      object_ids.size.should eq(1)
    end

    it "with splat" do
      res = [] of Array(Int32 | Char)
      Array.each_product([1, 2], ['a', 'b']) { |r| res << r }
      res.should eq([[1, 'a'], [1, 'b'], [2, 'a'], [2, 'b']])
    end
  end

  describe "Array.product" do
    it "with array" do
      Array.product([[1, 2], ['a', 'b']]).should eq([[1, 'a'], [1, 'b'], [2, 'a'], [2, 'b']])
    end

    it "with splat" do
      Array.product([1, 2], ['a', 'b']).should eq([[1, 'a'], [1, 'b'], [2, 'a'], [2, 'b']])
    end
  end

  it "doesn't overflow buffer with Array.new(size, value) (#1209)" do
    a = Array.new(1, 1_i64)
    b = Array.new(1, 1_i64)
    b << 2_i64 << 3_i64
    a.should eq([1])
    b.should eq([1, 2, 3])
  end

  it "flattens" do
    [[1, 'a'], [[[[true], "hi"]]]].flatten.should eq([1, 'a', true, "hi"])

    s = [1, 2, 3]
    t = [4, 5, 6, [7, 8]]
    u = [9, [10, 11].each]
    a = [s, t, u, 12, 13]
    result = a.flatten.to_a
    result.should eq([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13])
    result.should be_a(Array(Int32))
  end

  it "#skip" do
    ary = [1, 2, 3]
    ary.skip(0).should eq([1, 2, 3])
    ary.skip(1).should eq([2, 3])
    ary.skip(2).should eq([3])
    ary.skip(3).should eq([] of Int32)
    ary.skip(4).should eq([] of Int32)

    expect_raises(ArgumentError, "Attempt to skip negative size") do
      ary.skip(-1)
    end
  end

  describe "capacity re-sizing" do
    it "initializes an array capacity to INITIAL_CAPACITY" do
      a = [] of Int32
      a.push(1)
      a.@capacity.should eq(3)
    end

    it "doubles capacity for arrays smaller than CAPACITY_THRESHOLD" do
      a = Array.new(255, 1)
      a.push(1)
      a.@capacity.should eq(255 * 2)
    end

    it "uses slow growth heuristic for arrays larger than CAPACITY_THRESHOLD" do
      a = Array.new(512, 1)
      a.push(1)
      # ~63% larger
      a.@capacity.should eq(832)

      b = Array.new(4096, 1)
      b.push(1)
      # ~30% larger, starts converging toward 25%
      b.@capacity.should eq(5312)
    end
  end
end
