Skip to content

Commit

Permalink
add more
Browse files Browse the repository at this point in the history
  • Loading branch information
sebyx07 committed Aug 10, 2024
1 parent 6999f38 commit 913ae7f
Show file tree
Hide file tree
Showing 12 changed files with 313 additions and 8 deletions.
27 changes: 27 additions & 0 deletions lib/native_ruby/array/flatten.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# frozen_string_literal: true

class Array
# Flattens the array to the specified level.
#
# If no level is provided, flattens recursively.
#
# @param level [Integer, nil] The level of flattening. If nil, flatten recursively.
# @return [Array] A new array with the flattened elements.
#
# @example
# [1, [2, 3, [4, 5]]].flatten #=> [1, 2, 3, 4, 5]
# [1, [2, 3, [4, 5]]].flatten(1) #=> [1, 2, 3, [4, 5]]
def flatten(level = nil)
return self.dup if level == 0

result = []
each do |element|
if element.is_a?(Array) && (level.nil? || level > 0)
result.concat(level.nil? ? element.flatten : element.flatten(level - 1))
else
result << element
end
end
result
end
end
29 changes: 29 additions & 0 deletions lib/native_ruby/iterators/immutable/array/each_with_index.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# frozen_string_literal: true

class Array
# Iterates the given block for each element with its index.
#
# This implementation is considered immutable because it does not modify
# the array's size or structure during iteration. The array's length is
# calculated once at the beginning and remains constant throughout the iteration.
#
# If no block is given, returns an Enumerator object.
#
# @yield [Object, Integer] Passes each element of the array and its index to the block.
# @yieldparam element [Object] The current element in the iteration.
# @yieldparam index [Integer] The index of the current element.
# @return [Array] Returns self.
# @return [Enumerator] If no block is given.
def each_with_index
return to_enum(:each_with_index) { self.length } unless block_given?

i = 0
length = self.length
while i < length
yield self[i], i
i = i.succ
end

self
end
end
2 changes: 2 additions & 0 deletions lib/native_ruby/iterators/immutable/array/map.rb
Original file line number Diff line number Diff line change
Expand Up @@ -27,4 +27,6 @@ def map
end
result
end

alias_method :collect, :map
end
29 changes: 29 additions & 0 deletions lib/native_ruby/iterators/mutable/array/each_with_index.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# frozen_string_literal: true

class Array
# Iterates the given block for each element with index.
#
# This implementation is considered mutable because:
# 1. It recalculates self.length on each iteration, allowing for potential
# changes to the array's size during iteration.
# 2. If the array is modified during iteration (e.g., by the yielded block),
# the method will operate on the modified array.
#
# If no block is given, returns an Enumerator object.
#
# @yield [Object, Integer] Passes each element of the array and its index to the block.
# @yieldparam element [Object] The current element in the iteration.
# @yieldparam index [Integer] The index of the current element.
# @return [Array] Returns self.
# @return [Enumerator] If no block is given.
def each_with_index
return to_enum(:each_with_index) { self.length } unless block_given?

i = 0
while i < self.length # Note: self.length is evaluated on each iteration
yield self[i], i
i = i.succ
end
self
end
end
2 changes: 2 additions & 0 deletions lib/native_ruby/iterators/mutable/array/map.rb
Original file line number Diff line number Diff line change
Expand Up @@ -28,4 +28,6 @@ def map
end
result
end

alias_method :collect, :map
end
2 changes: 0 additions & 2 deletions spec/array/all?_spec.rb
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
# frozen_string_literal: true

require 'benchmark'

RSpec.describe 'Array#all?' do
before do
class Array
Expand Down
2 changes: 0 additions & 2 deletions spec/array/assoc_spec.rb
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
# frozen_string_literal: true

require 'benchmark'

RSpec.describe 'Array#assoc' do
before do
class Array
Expand Down
80 changes: 80 additions & 0 deletions spec/array/flatten_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
# frozen_string_literal: true

RSpec.describe 'Array#flatten' do
before do
class Array
alias_method :original_flatten, :flatten
end

NativeRuby.config { |c| c.load(:class, { class: Array, method: :flatten }) }
end

after do
class Array
alias_method :flatten, :original_flatten
end
end

it 'flattens nested arrays' do
expect([1, [2, 3, [4, 5]]].flatten).to eq([1, 2, 3, 4, 5])
end

it 'flattens to the specified level' do
expect([1, [2, 3, [4, 5]]].flatten(1)).to eq([1, 2, 3, [4, 5]])
end

it 'returns a new array' do
original = [1, [2, 3]]
flattened = original.flatten
expect(flattened).not_to be(original)
end

it 'does not modify the original array' do
original = [1, [2, 3, [4, 5]]]
original.flatten
expect(original).to eq([1, [2, 3, [4, 5]]])
end

it 'handles empty arrays' do
expect([].flatten).to eq([])
end

it 'handles arrays with no nested arrays' do
expect([1, 2, 3].flatten).to eq([1, 2, 3])
end

it 'handles deeply nested arrays' do
expect([1, [2, [3, [4, [5]]]]].flatten).to eq([1, 2, 3, 4, 5])
end

it 'handles arrays with nil elements' do
expect([1, [2, nil, [3, nil]]].flatten).to eq([1, 2, nil, 3, nil])
end

it 'returns an Enumerator when called without arguments' do
expect([1, [2, 3]].method(:flatten).arity).to eq(-1)
end

it 'works with a large array' do
large_array = (1..1000).to_a.map { |i| [i, [i + 1000, [i + 2000]]] }
flattened = large_array.flatten
expect(flattened.size).to eq(3000)
expect(flattened.first).to eq(1)
expect(flattened.last).to eq(3000)
end

it 'benchmarks native implementation against original' do
array = (1..10000).to_a.map { |i| [i, [i + 10000, [i + 20000]]] }
iterations = 10

puts "Benchmark results (average over #{iterations} iterations):"
Benchmark.bm(20) do |x|
x.report('Original flatten:') do
iterations.times { array.original_flatten }
end
x.report('Native flatten:') do
iterations.times { array.flatten }
end
end
end
end
2 changes: 0 additions & 2 deletions spec/iterators/immutable/array/each_spec.rb
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
# frozen_string_literal: true

require 'benchmark'

RSpec.describe 'Immutable Array#each' do
before do
class Array
Expand Down
72 changes: 72 additions & 0 deletions spec/iterators/immutable/array/each_with_index_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
# frozen_string_literal: true

RSpec.describe 'Array#each_with_index' do
before do
class Array
alias_method :original_each_with_index, :each_with_index
end

NativeRuby.config { |c| c.load(:iterators, { class: Array, method: :each_with_index, mutable: false }) }
end

after do
class Array
alias_method :each_with_index, :original_each_with_index
end
end

it 'iterates the given block for each element with its index' do
array = %w[a b c]
result = []
array.each_with_index { |item, index| result << [item, index] }
expect(result).to eq([['a', 0], ['b', 1], ['c', 2]])
end

it 'returns self' do
array = [1, 2, 3]
expect(array.each_with_index { |item, index| }).to eq(array)
end

it 'does not modify the original array' do
array = [1, 2, 3]
array.each_with_index { |item, index| item * 2 }
expect(array).to eq([1, 2, 3])
end

it 'works with an empty array' do
result = []
[].each_with_index { |item, index| result << [item, index] }
expect(result).to be_empty
end

it 'returns an Enumerator if no block is given' do
expect([1, 2, 3].each_with_index).to be_an(Enumerator)
end

it 'returns an Enumerator with the correct size' do
enum = [1, 2, 3].each_with_index
expect(enum.size).to eq(3)
end

it 'works with a large array' do
large_array = (1..1_000_000).to_a
count = 0
large_array.each_with_index { |item, index| count += 1 if item == index + 1 }
expect(count).to eq(1_000_000)
end

it 'benchmarks native implementation against original' do
array = (1..1_000_000).to_a
iterations = 10

puts "Benchmark results (average over #{iterations} iterations):"
Benchmark.bm(25) do |x|
x.report('Original each_with_index:') do
iterations.times { array.original_each_with_index { |item, index| } }
end
x.report('Native each_with_index:') do
iterations.times { array.each_with_index { |item, index| } }
end
end
end
end
2 changes: 0 additions & 2 deletions spec/iterators/immutable/array/map_spec.rb
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
# frozen_string_literal: true

require 'benchmark'

RSpec.describe 'Immutable Array#map' do
before do
class Array
Expand Down
72 changes: 72 additions & 0 deletions spec/iterators/mutable/array/each_with_index_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
# frozen_string_literal: true

RSpec.describe 'Array#each_with_index' do
before do
class Array
alias_method :original_each_with_index, :each_with_index
end

NativeRuby.config { |c| c.load(:iterators, { class: Array, method: :each_with_index, mutable: true }) }
end

after do
class Array
alias_method :each_with_index, :original_each_with_index
end
end

it 'iterates the given block for each element with its index' do
array = %w[a b c]
result = []
array.each_with_index { |item, index| result << [item, index] }
expect(result).to eq([['a', 0], ['b', 1], ['c', 2]])
end

it 'returns self' do
array = [1, 2, 3]
expect(array.each_with_index { |item, index| }).to eq(array)
end

it 'does not modify the original array' do
array = [1, 2, 3]
array.each_with_index { |item, index| item * 2 }
expect(array).to eq([1, 2, 3])
end

it 'works with an empty array' do
result = []
[].each_with_index { |item, index| result << [item, index] }
expect(result).to be_empty
end

it 'returns an Enumerator if no block is given' do
expect([1, 2, 3].each_with_index).to be_an(Enumerator)
end

it 'returns an Enumerator with the correct size' do
enum = [1, 2, 3].each_with_index
expect(enum.size).to eq(3)
end

it 'works with a large array' do
large_array = (1..1_000_000).to_a
count = 0
large_array.each_with_index { |item, index| count += 1 if item == index + 1 }
expect(count).to eq(1_000_000)
end

it 'benchmarks native implementation against original' do
array = (1..1_000_000).to_a
iterations = 10

puts "Benchmark results (average over #{iterations} iterations):"
Benchmark.bm(25) do |x|
x.report('Original each_with_index:') do
iterations.times { array.original_each_with_index { |item, index| } }
end
x.report('Native each_with_index:') do
iterations.times { array.each_with_index { |item, index| } }
end
end
end
end

0 comments on commit 913ae7f

Please sign in to comment.