big-o 0.1 → 0.1.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|