require "spec" require "spec/helpers/iterate" private class DequeTester # Execute the same actions on an Array and a Deque and compare them at each step. @deque : Deque(Int32) @array : Array(Int32) @i : Int32 @c : Array(Int32) | Deque(Int32) | Nil def step(&) @c = @deque yield @c = @array yield @deque.to_a.should eq(@array) @i += 1 end def initialize @deque = Deque(Int32).new @array = Array(Int32).new @i = 1 end getter i def c @c.not_nil! end def test(&) with self yield end end private alias RecursiveDeque = Deque(RecursiveDeque) # Yields multiple forms of `Deque{}`, `Deque{0}`, `Deque{0, 1}`, `Deque{0, 1, 2}`, ..., # `Deque{0, 1, 2, ..., max_size - 1}`. All deques have *max_size* as their # capacity; each deque is yielded *max_size* times with a different start # position in the internal buffer. Every deque is a fresh instance. private def each_queue_repr(max_size, &) (0..max_size).each do |size| max_size.times do |i| x = Deque(Int32).new(max_size, 0) x.rotate!(i) x.pop(max_size - size) x.fill &.itself yield x end end end describe "Deque" do describe "implementation" do it "works the same as array" do DequeTester.new.test do step { c.unshift i } step { c.pop } step { c.push i } step { c.shift } step { c.push i } step { c.push i } step { c.push i } step { c.push i } step { c.push i } step { c.push i } step { c.pop } step { c.shift } step { c.push i } step { c.push i } step { c.push i } step { c.push i } step { c.push i } step { c.unshift i } step { c.unshift i } step { c.unshift i } step { c.unshift i } step { c.unshift i } step { c.unshift i } step { c.insert(1, i) } step { c.insert(0, i) } step { c.insert(17, i) } step { c.insert(14, i) } step { c.insert(10, i) } step { c.insert(10, i) } end end it "works the same as array when inserting at 1/8 size and deleting at 3/4 size" do DequeTester.new.test do 1000.times do step { c.insert(c.size // 8, i) } end 1000.times do step { c.delete_at(c.size * 3 // 4) } end end end it "works the same as array when inserting at 3/4 size and deleting at 1/8 size" do DequeTester.new.test do 1000.times do step { c.insert(c.size * 3 // 4, i) } end 1000.times do step { c.delete_at(c.size // 8) } end end end end describe "new" do it "creates with default value" do deq = Deque.new(5, 3) deq.should eq(Deque{3, 3, 3, 3, 3}) end it "creates with default value in block" do deq = Deque.new(5) { |i| i * 2 } deq.should eq(Deque{0, 2, 4, 6, 8}) end it "creates from an array" do deq = Deque(Int32).new([1, 2, 3, 4, 5]) deq.should eq(Deque{1, 2, 3, 4, 5}) end it "raises on negative count" do expect_raises(ArgumentError, "Negative deque size") do Deque.new(-1, 3) end end it "raises on negative capacity" do expect_raises(ArgumentError, "Negative deque capacity") do Deque(Int32).new(-1) end end end describe "==" do it "compares empty" do Deque(Int32).new.should eq(Deque(Int32).new) Deque{1}.should_not eq(Deque(Int32).new) Deque(Int32).new.should_not eq(Deque{1}) end it "compares elements" do Deque{1, 2, 3}.should eq(Deque{1, 2, 3}) Deque{1, 2, 3}.should_not eq(Deque{3, 2, 1}) end it "compares other" do a = Deque{1, 2, 3} b = Deque{1, 2, 3} c = Deque{1, 2, 3, 4} d = Deque{1, 2, 4} (a == b).should be_true (b == c).should be_false (a == d).should be_false end it "compares other types" do a = Deque{1, 2, 3} b = Deque{true, false} c = "other type" (a == b).should be_false (b == c).should be_false (a == c).should be_false end end describe "+" do it "does +" do a = Deque{1, 2, 3} b = Deque{4, 5} c = a + b c.size.should eq(5) 0.upto(4) { |i| c[i].should eq(i + 1) } end it "does + with different types" do a = Deque{1, 2, 3} a += Deque{"hello"} a.should eq(Deque{1, 2, 3, "hello"}) end end describe "[]" do it "gets on positive index" do Deque{1, 2, 3}[1].should eq(2) end it "gets on negative index" do Deque{1, 2, 3}[-1].should eq(3) end it "gets nilable" do Deque{1, 2, 3}[2]?.should eq(3) Deque{1, 2, 3}[3]?.should be_nil end end describe "[]=" do it "sets on positive index" do a = Deque{1, 2, 3} a[1] = 4 a[1].should eq(4) end it "sets on negative index" do a = Deque{1, 2, 3} a[-1] = 4 a[2].should eq(4) end end it "does clear" do a = Deque{1, 2, 3} a.clear a.should eq(Deque(Int32).new) end it "does clone" do x = {1 => 2} a = Deque{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 type" do deq = Deque(RecursiveDeque).new deq << deq clone = deq.clone clone.should be(clone.first) end describe "concat" do it "concats deque" do a = Deque{1, 2, 3} a.concat(Deque{4, 5, 6}) a.should eq(Deque{1, 2, 3, 4, 5, 6}) end it "concats large deques" do a = Deque{1, 2, 3} a.concat((4..1000).to_a) a.should eq(Deque.new((1..1000).to_a)) end it "concats enumerable" do a = Deque{1, 2, 3} a.concat((4..1000)) a.should eq(Deque.new((1..1000).to_a)) end it "concats indexable" do each_queue_repr(16) do |a| size = a.size b = Array.new(10, &.+(size)) a.concat(b).should be(a) a.should eq(Deque.new(size + 10, &.itself)) end each_queue_repr(16) do |a| size = a.size b = Slice.new(10, &.+(size)) a.concat(b).should be(a) a.should eq(Deque.new(size + 10, &.itself)) end each_queue_repr(16) do |a| size = a.size b = StaticArray(Int32, 10).new(&.+(size)) a.concat(b).should be(a) a.should eq(Deque.new(size + 10, &.itself)) end each_queue_repr(16) do |a| size = a.size b = Deque.new(10, &.+(size)) a.concat(b).should be(a) a.should eq(Deque.new(size + 10, &.itself)) end end it "concats itself" do each_queue_repr(16) do |a| size = a.size a.concat(a).should be(a) a.should eq(Deque.new((0...size).to_a * 2)) end end end describe "delete" do it "deletes many" do a = Deque{1, 2, 3, 1, 2, 3} a.delete(2).should be_true a.should eq(Deque{1, 3, 1, 3}) end it "delete not found" do a = Deque{1, 2} a.delete(4).should be_false a.should eq(Deque{1, 2}) end end describe "delete_at" do it "deletes positive index" do a = Deque{1, 2, 3, 4, 5} a.delete_at(3).should eq(4) a.should eq(Deque{1, 2, 3, 5}) end it "deletes negative index" do a = Deque{1, 2, 3, 4, 5} a.delete_at(-4).should eq(2) a.should eq(Deque{1, 3, 4, 5}) end it "deletes out of bounds" do a = Deque{1, 2, 3, 4} expect_raises IndexError do a.delete_at(4) end end end it "does dup" do x = {1 => 2} a = Deque{x} b = a.dup b.should eq(Deque{x}) a.should_not be(b) a[0].should be(b[0]) b << {3 => 4} a.should eq(Deque{x}) end it "does each" do a = Deque{1, 1, 1} b = 0 a.each { |i| b += i }.should be_nil b.should eq(3) end it "does each_index" do a = Deque{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 (Deque(Int32).new.empty?).should be_true end it "is not empty" do Deque{1}.empty?.should be_false end end it "does equals? with custom block" do a = Deque{1, 3, 2} b = Deque{3, 9, 4} c = Deque{5, 7, 3} d = Deque{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 "first" do it "gets first when non empty" do a = Deque{1, 2, 3} a.first.should eq(1) end it "raises when empty" do expect_raises Enumerable::EmptyError do Deque(Int32).new.first end end end describe "first?" do it "gets first? when non empty" do a = Deque{1, 2, 3} a.first?.should eq(1) end it "gives nil when empty" do Deque(Int32).new.first?.should be_nil end end it "does hash" do a = Deque{1, 2, Deque{3}} b = Deque{1, 2, Deque{3}} a.hash.should eq(b.hash) end describe "insert" do it "inserts with positive index" do a = Deque{1, 3, 4} expected = Deque{1, 2, 3, 4} a.insert(1, 2).should eq(expected) a.should eq(expected) end it "inserts with negative index" do a = Deque{1, 2, 3} expected = Deque{1, 2, 3, 4} a.insert(-1, 4).should eq(expected) a.should eq(expected) end it "inserts with negative index (2)" do a = Deque{1, 2, 3} expected = Deque{4, 1, 2, 3} a.insert(-4, 4).should eq(expected) a.should eq(expected) end it "inserts out of range" do a = Deque{1, 3, 4} expect_raises IndexError do a.insert(4, 1) end end end describe "inspect" do it { Deque{1, 2, 3}.inspect.should eq("Deque{1, 2, 3}") } end describe "last" do it "gets last when non empty" do a = Deque{1, 2, 3} a.last.should eq(3) end it "raises when empty" do expect_raises IndexError do Deque(Int32).new.last end end end describe "size" do it "has size 0" do Deque(Int32).new.size.should eq(0) end it "has size 2" do Deque{1, 2}.size.should eq(2) end end describe "pop" do it "pops when non empty" do a = Deque{1, 2, 3} a.pop.should eq(3) a.should eq(Deque{1, 2}) end it "raises when empty" do expect_raises IndexError do Deque(Int32).new.pop end end it "pops many elements" do a = Deque{1, 2, 3, 4, 5} a.pop(3) a.should eq(Deque{1, 2}) end it "pops more elements than what is available" do a = Deque{1, 2, 3, 4, 5} a.pop(10) a.should eq(Deque(Int32).new) end it "pops negative count raises" do a = Deque{1, 2} expect_raises ArgumentError do a.pop(-1) end end end describe "push" do it "adds one element to the deque" do a = Deque{"a", "b"} a.push("c") a.should eq Deque{"a", "b", "c"} end it "returns the deque" do a = Deque{"a", "b"} a.push("c").should eq Deque{"a", "b", "c"} end it "has the << alias" do a = Deque{"a", "b"} a << "c" a.should eq Deque{"a", "b", "c"} end end describe "reject!" do it "with block" do a1 = Deque{1, 2, 3, 4, 5} a2 = a1.reject! &.even? a2.should eq(Deque{1, 3, 5}) a2.should be(a1) end it "with pattern" do a1 = Deque{1, 2, 3, 4, 5} a2 = a1.reject!(2..4) a2.should eq(Deque{1, 5}) a2.should be(a1) end end describe "rotate!" do it "rotates" do a = Deque{1, 2, 3, 4, 5} a.rotate! a.should eq(Deque{2, 3, 4, 5, 1}) a.rotate!(-2) a.should eq(Deque{5, 1, 2, 3, 4}) a.rotate!(10) a.should eq(Deque{5, 1, 2, 3, 4}) end it "rotates with size=capacity" do a = Deque{1, 2, 3, 4} a.rotate! a.should eq(Deque{2, 3, 4, 1}) a.rotate!(-2) a.should eq(Deque{4, 1, 2, 3}) a.rotate!(8) a.should eq(Deque{4, 1, 2, 3}) end it "rotates with size=0" do a = Deque(Int32).new a.rotate! a.should eq(Deque(Int32).new) end it "rotates with size=1" do a = Deque{1} a.rotate! a.should eq(Deque{1}) end end describe "select!" do it "with block" do a1 = Deque{1, 2, 3, 4, 5} a2 = a1.select! &.even? a2.should eq(Deque{2, 4}) a2.should be(a1) end it "with pattern" do a1 = Deque{1, 2, 3, 4, 5} a2 = a1.select!(2..4) a2.should eq(Deque{2, 3, 4}) a2.should be(a1) end end describe "shift" do it "shifts when non empty" do a = Deque{1, 2, 3} a.shift.should eq(1) a.should eq(Deque{2, 3}) end it "raises when empty" do expect_raises IndexError do Deque(Int32).new.shift end end it "shifts many elements" do a = Deque{1, 2, 3, 4, 5} a.shift(3) a.should eq(Deque{4, 5}) end it "shifts more than what is available" do a = Deque{1, 2, 3, 4, 5} a.shift(10) a.should eq(Deque(Int32).new) end it "shifts negative count raises" do a = Deque{1, 2} expect_raises ArgumentError do a.shift(-1) end end end describe "swap" do it "swaps" do a = Deque{1, 2, 3} a.swap(0, 2) a.should eq(Deque{3, 2, 1}) end it "swaps with negative indices" do a = Deque{1, 2, 3} a.swap(-3, -1) a.should eq(Deque{3, 2, 1}) end it "swaps but raises out of bounds on left" do a = Deque{1, 2, 3} expect_raises IndexError do a.swap(3, 0) end end it "swaps but raises out of bounds on right" do a = Deque{1, 2, 3} expect_raises IndexError do a.swap(0, 3) end end end describe "to_s" do it "does to_s" do Deque{1, 2, 3}.to_s.should eq("Deque{1, 2, 3}") end it "does with recursive" do deq = Deque(RecursiveDeque).new deq << deq deq.to_s.should eq("Deque{Deque{...}}") end end it "does unshift" do a = Deque{2, 3} expected = Deque{1, 2, 3} a.unshift(1).should eq(expected) a.should eq(expected) end describe "each iterator" do it_iterates "#each", [1, 2, 3], Deque{1, 2, 3}.each it "works while modifying deque" do a = Deque{1, 2, 3} count = 0 it = a.each it.each do count += 1 a.clear end count.should eq(1) end end describe "each_index iterator" do it_iterates "#each_index", [0, 1, 2], Deque{1, 2, 3}.each_index it "works while modifying deque" do a = Deque{1, 2, 3} count = 0 it = a.each_index it.each do count += 1 a.clear end count.should eq(1) end end it_iterates "#reverse_each", [3, 2, 1], Deque{1, 2, 3}.reverse_each it_iterates "#cycle", [1, 2, 3, 1, 2, 3, 1, 2], Deque{1, 2, 3}.cycle, infinite: true it_iterates "#cycle(limit)", [1, 2, 3, 1, 2, 3], Deque{1, 2, 3}.cycle(2) end