big-o 0.1 → 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/README.md +27 -3
- data/big-o.gemspec +2 -2
- data/lib/big-o/complexity_base.rb +20 -2
- data/spec/big-o/complexity_base_spec.rb +20 -1
- data/spec/big-o/space_complexity_spec.rb +8 -1
- data/spec/big-o/time_complexity_spec.rb +17 -5
- metadata +2 -2
data/README.md
CHANGED
@@ -45,7 +45,9 @@ space_complexity = BigO::SpaceComplexity({
|
|
45
45
|
:timeout => 10, # time in seconds
|
46
46
|
:approximation => 0.05, # percentage
|
47
47
|
:error_pct => 0.05, # percentage
|
48
|
-
:minimum_result_set_size => 3
|
48
|
+
:minimum_result_set_size => 3, # minimum results
|
49
|
+
:after_hook => proc { }, # See before/after hooks
|
50
|
+
:before_hook => proc { }
|
49
51
|
})
|
50
52
|
```
|
51
53
|
|
@@ -60,7 +62,7 @@ require 'big-o-matchers'
|
|
60
62
|
describe 'do_something_time_consuming' do
|
61
63
|
before :each do
|
62
64
|
@time_complexity = BigO::TimeComplexity({
|
63
|
-
:fn
|
65
|
+
:fn => lambda { |n| do_something_time_consuming(n) }
|
64
66
|
})
|
65
67
|
end
|
66
68
|
|
@@ -69,7 +71,7 @@ describe 'do_something_time_consuming' do
|
|
69
71
|
end
|
70
72
|
|
71
73
|
it 'should not have a complexity of O(1)' do
|
72
|
-
@time_complexity.
|
74
|
+
@time_complexity.should_not match_complexity_level 'O(1)', lambda { |_| 1 }
|
73
75
|
end
|
74
76
|
end
|
75
77
|
```
|
@@ -77,6 +79,28 @@ end
|
|
77
79
|
The string used as the first parameter (e.g. `'O(n)'`) is used to describe the lambda given as the
|
78
80
|
second parameter.
|
79
81
|
|
82
|
+
### After/Before hooks
|
83
|
+
|
84
|
+
Your function depends on something which needs to run before every call of your function? You need to
|
85
|
+
cleanup whatever dirty work your function performed? These operation are time/space consuming and they will
|
86
|
+
affect the values of your function? Well, just throw these things in the before/after hooks!
|
87
|
+
|
88
|
+
```ruby
|
89
|
+
time_complexity = BigO::TimeComplexity({
|
90
|
+
:fn => lambda { |_| whatever_action_which_doesnt_depend_on_n },
|
91
|
+
:level => lambda { |n| n**2 },
|
92
|
+
:before_hook => lambda { |n| prepare_fn_environment(n) },
|
93
|
+
:after_hook => lambda { |n| clean_up(n) }
|
94
|
+
})
|
95
|
+
time_complexity.process # will only time :fn
|
96
|
+
```
|
97
|
+
|
98
|
+
Warning: The timeout is still in effect during before and after hooks execution (in our example, it may stop
|
99
|
+
during clean_up). There should not be any sensitive code which needs to be executed in before/after hooks.
|
100
|
+
|
101
|
+
If you need to cleanup something after `time_complexity.process`, you will prefer to place this code out of :after_hook
|
102
|
+
or :before_hook.
|
103
|
+
|
80
104
|
## Reference
|
81
105
|
|
82
106
|
You may want to read more on the subject by reading:
|
data/big-o.gemspec
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
Gem::Specification.new do |s|
|
2
2
|
s.name = 'big-o'
|
3
|
-
s.version = '0.1'
|
4
|
-
s.date = '2012-07-
|
3
|
+
s.version = '0.1.1'
|
4
|
+
s.date = '2012-07-27'
|
5
5
|
s.summary = "Calculate function complexity (using Big O notation)"
|
6
6
|
s.description = "Big-O is a gem which analyse an anonymous function and verify that it " +
|
7
7
|
"follow a specific pattern in its memory usage or its execution time."
|
@@ -34,7 +34,9 @@ module BigO
|
|
34
34
|
:timeout => 10,
|
35
35
|
:approximation => 0.05,
|
36
36
|
:error_pct => 0.05,
|
37
|
-
:minimum_result_set_size => 3
|
37
|
+
:minimum_result_set_size => 3,
|
38
|
+
:before_hook => proc {},
|
39
|
+
:after_hook => proc {} }
|
38
40
|
|
39
41
|
@options.merge!(options)
|
40
42
|
end
|
@@ -63,7 +65,7 @@ module BigO
|
|
63
65
|
begin
|
64
66
|
Timeout::timeout(@options[:timeout]) do
|
65
67
|
@options[:range].each do |n|
|
66
|
-
next if (indicator =
|
68
|
+
next if (indicator = measurement(n)) == 0
|
67
69
|
real_complexity[n] = indicator
|
68
70
|
expected_complexity[n] = @options[:level].call(n)
|
69
71
|
end
|
@@ -78,6 +80,22 @@ module BigO
|
|
78
80
|
[real_complexity, expected_complexity]
|
79
81
|
end
|
80
82
|
|
83
|
+
# Measurement process.
|
84
|
+
#
|
85
|
+
# Composed of the measurement itself and executing two hooks :before_hook and :after_hook.
|
86
|
+
# Due to the structure of the measurement, the hooks are not (and should not) be part of the
|
87
|
+
# measure. In other words, any code executed within :before_hook or :after_hook will not affect
|
88
|
+
# the time/space measure of :fn.
|
89
|
+
#
|
90
|
+
# @param [Integer] n iteration
|
91
|
+
# @return [Float] indicator
|
92
|
+
def measurement(n)
|
93
|
+
@options[:before_hook].call(n)
|
94
|
+
indicator = measure(n, &@options[:fn])
|
95
|
+
@options[:after_hook].call(n)
|
96
|
+
indicator
|
97
|
+
end
|
98
|
+
|
81
99
|
# Parses data from <code>#run_simulation</code>.
|
82
100
|
#
|
83
101
|
# <code>examine_result_set</code> will return true only if these conditions are met:
|
@@ -6,7 +6,7 @@ describe ComplexityBase do
|
|
6
6
|
class FunctionComplexity
|
7
7
|
include ComplexityBase
|
8
8
|
|
9
|
-
def measure
|
9
|
+
def measure(*args, &b)
|
10
10
|
1
|
11
11
|
end
|
12
12
|
end
|
@@ -55,4 +55,23 @@ describe ComplexityBase do
|
|
55
55
|
@fn_complexity.options[:approximation].should == 0.1
|
56
56
|
end
|
57
57
|
end
|
58
|
+
|
59
|
+
describe 'before/after hooks' do
|
60
|
+
before :each do
|
61
|
+
@after_hook = double('lambda')
|
62
|
+
@before_hook = double('lambda')
|
63
|
+
@fn_complexity = FunctionComplexity.new({
|
64
|
+
:fn => lambda { |_| 1 },
|
65
|
+
:level => lambda { |_| 1},
|
66
|
+
:before_hook => @before_hook,
|
67
|
+
:after_hook => @after_hook })
|
68
|
+
|
69
|
+
end
|
70
|
+
|
71
|
+
it 'should be called every time we try to match a complexity level' do
|
72
|
+
@before_hook.should_receive(:call).with(kind_of(Integer)).at_most(20).times
|
73
|
+
@after_hook.should_receive(:call).with(kind_of(Integer)).at_most(20).times
|
74
|
+
@fn_complexity.process
|
75
|
+
end
|
76
|
+
end
|
58
77
|
end
|
@@ -12,6 +12,14 @@ describe SpaceComplexity do
|
|
12
12
|
lambda { @space_complexity.process }.should raise_error(Timeout::Error)
|
13
13
|
end
|
14
14
|
|
15
|
+
it 'should ignore whatever is happening in a before/after hook' do
|
16
|
+
@space_complexity.options[:fn] = lambda { |_| simulate_memory_space(1024) }
|
17
|
+
@space_complexity.options[:before_hook] = lambda { |n| simulate_memory_space(1024 * n) }
|
18
|
+
@space_complexity.options[:after_hook] = lambda { |n| simulate_memory_space(1024 * n) }
|
19
|
+
@space_complexity.options[:approximation] = 0.2
|
20
|
+
@space_complexity.should match_complexity_level 'O(1)', lambda { |_| 1 }
|
21
|
+
end
|
22
|
+
|
15
23
|
describe '#process on complexity O(1)' do
|
16
24
|
before :each do
|
17
25
|
@space_complexity.options[:fn] = lambda { |_| simulate_memory_space(1024) }
|
@@ -35,7 +43,6 @@ describe SpaceComplexity do
|
|
35
43
|
it 'should return false if the complexity does not match (too low)' do
|
36
44
|
@space_complexity.should_not match_complexity_level 'O(1)', lambda { |_| 1 }
|
37
45
|
end
|
38
|
-
|
39
46
|
end
|
40
47
|
|
41
48
|
describe '#process on complexity O(n**2)' do
|
@@ -6,15 +6,27 @@ describe TimeComplexity do
|
|
6
6
|
@time_complexity = TimeComplexity.new
|
7
7
|
end
|
8
8
|
|
9
|
+
MIN_TIME = 0.01
|
10
|
+
AVG_TIME = 0.05
|
11
|
+
|
9
12
|
it 'should raise an exception if timeout is reached and no result was found' do
|
10
13
|
@time_complexity.options[:timeout] = 0.001
|
11
14
|
@time_complexity.options[:fn] = proc { simulate_utime_processing(1) }
|
12
15
|
lambda { @time_complexity.process }.should raise_error(Timeout::Error)
|
13
16
|
end
|
14
17
|
|
18
|
+
it 'should ignore whatever is happening in a before/after hook' do
|
19
|
+
@time_complexity.options[:fn] = lambda { |_| simulate_utime_processing(MIN_TIME) }
|
20
|
+
@time_complexity.options[:before_hook] = lambda { |n| simulate_utime_processing(MIN_TIME * n) }
|
21
|
+
@time_complexity.options[:after_hook] = lambda { |n| simulate_utime_processing(MIN_TIME * n) }
|
22
|
+
@time_complexity.options[:approximation] = 0.2
|
23
|
+
@time_complexity.should match_complexity_level 'O(1)', lambda { |_| 1 }
|
24
|
+
end
|
25
|
+
|
26
|
+
|
15
27
|
describe '#process on complexity O(1)' do
|
16
28
|
before :each do
|
17
|
-
@time_complexity.options[:fn] = lambda { |_| simulate_utime_processing(
|
29
|
+
@time_complexity.options[:fn] = lambda { |_| simulate_utime_processing(AVG_TIME) }
|
18
30
|
end
|
19
31
|
|
20
32
|
it 'should run the function and produce a report when time threshold is hit' do
|
@@ -25,7 +37,7 @@ describe TimeComplexity do
|
|
25
37
|
|
26
38
|
describe '#process on complexity O(n)' do
|
27
39
|
before :each do
|
28
|
-
@time_complexity.options[:fn] = lambda { |n| simulate_utime_processing(
|
40
|
+
@time_complexity.options[:fn] = lambda { |n| simulate_utime_processing(AVG_TIME * n) }
|
29
41
|
end
|
30
42
|
|
31
43
|
it 'should run the function and produce a report when time threshold is hit' do
|
@@ -36,7 +48,7 @@ describe TimeComplexity do
|
|
36
48
|
describe '#process on complexity O(n**2)' do
|
37
49
|
before :each do
|
38
50
|
@time_complexity.options[:minimum_result_set_size] = 0
|
39
|
-
@time_complexity.options[:fn] = lambda { |n| simulate_utime_processing(
|
51
|
+
@time_complexity.options[:fn] = lambda { |n| simulate_utime_processing(AVG_TIME * (n**2)) }
|
40
52
|
end
|
41
53
|
|
42
54
|
it 'should run the function and produce a report when time threshold is hit' do
|
@@ -51,14 +63,14 @@ describe TimeComplexity do
|
|
51
63
|
|
52
64
|
describe 'very small execution time functions (0.1 second and below)' do
|
53
65
|
it 'should still be valid in case of O(n**2)' do
|
54
|
-
@time_complexity.options[:fn] = lambda { |n| simulate_utime_processing(
|
66
|
+
@time_complexity.options[:fn] = lambda { |n| simulate_utime_processing(MIN_TIME * n**2) }
|
55
67
|
@time_complexity.should match_complexity_level 'O(n**2)', lambda { |n| n**2 }
|
56
68
|
@time_complexity.should_not match_complexity_level 'O(n log(n))', lambda { |n| n * Math::log(n) }
|
57
69
|
@time_complexity.should_not match_complexity_level 'O(1)', lambda { |_| 1 }
|
58
70
|
end
|
59
71
|
|
60
72
|
it 'should still be valid in case of O(n)' do
|
61
|
-
@time_complexity.options[:fn] = lambda { |n| simulate_utime_processing(
|
73
|
+
@time_complexity.options[:fn] = lambda { |n| simulate_utime_processing(MIN_TIME * n) }
|
62
74
|
@time_complexity.should match_complexity_level 'O(n)', lambda { |n| n }
|
63
75
|
@time_complexity.should_not match_complexity_level 'O(1)', lambda { |_| 1 }
|
64
76
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: big-o
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 0.1.1
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2012-07-
|
12
|
+
date: 2012-07-27 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: rspec
|