theft 0.0.1.placeholder → 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +13 -5
- data/examples/stable_sort.rb +116 -0
- data/lib/theft.rb +90 -2
- data/lib/theft/version.rb +1 -1
- metadata +8 -7
checksums.yaml
CHANGED
@@ -1,7 +1,15 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
|
2
|
+
!binary "U0hBMQ==":
|
3
|
+
metadata.gz: !binary |-
|
4
|
+
YjBiNDUwODExOTFjYjM4NzVhYmRjZGM2OWFjNmVlOTE5NGU4YzM1Zg==
|
5
|
+
data.tar.gz: !binary |-
|
6
|
+
YzRmM2UyZmJkNWUwOTUxZmQwYzY2MzM2OTA1NTZlNjNlNTQ2NDU3YQ==
|
5
7
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
|
8
|
+
metadata.gz: !binary |-
|
9
|
+
NTY0NDg0N2I5ZGRjYTIwYjc1OWJiN2QzMzhiYmUzZjhlYmZkNWQ5MmYyODY0
|
10
|
+
ZTMyMDQ1ODczN2U5Zjk1ZjE3MDJkOGRmOTg1MGI1M2I1ZTk3ZjMzZGMyYWY3
|
11
|
+
YTNiNzVlZDUyYWIxMmUwZDQ0OWE2YjBkYjFkZDNlMjk3MjU4ZGY=
|
12
|
+
data.tar.gz: !binary |-
|
13
|
+
NTcxMGM5NzU4Y2NiNTdjODhmZGUwNzVkZTdlOTg1MjEzYTFhMjBiZTY4Njlh
|
14
|
+
YzJjMjY0NDFjMWIxODdjYTlmZjFjY2UzNTJhNWIzYmE0NjQ1ODkzNjFlNDgy
|
15
|
+
MTA5NGE1YzRhMzg2YjZmNTAzMGE3MmNjMGU3ZmFmNmU5MTE3Mzg=
|
@@ -0,0 +1,116 @@
|
|
1
|
+
require 'digest'
|
2
|
+
require_relative '../lib/theft'
|
3
|
+
|
4
|
+
class BoxedFixNum
|
5
|
+
attr_reader :n
|
6
|
+
def initialize(n)
|
7
|
+
@n = n
|
8
|
+
end
|
9
|
+
|
10
|
+
def <=>(other)
|
11
|
+
@n <=> other.n
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
class BoxedFixNumArgDescriptor
|
16
|
+
class << self
|
17
|
+
def setup(rng, args={})
|
18
|
+
[].tap do |ar|
|
19
|
+
rng.rand(0..100).times do |i|
|
20
|
+
ar << b(rng.rand(0..100))
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def teardown(list)
|
26
|
+
# do stuff
|
27
|
+
end
|
28
|
+
|
29
|
+
def hash(list)
|
30
|
+
Digest::MD5.new.digest to_s(list)
|
31
|
+
end
|
32
|
+
|
33
|
+
def to_s(list)
|
34
|
+
"[#{list.size}] " + list.map(&:n).map(&:to_s).join(",")
|
35
|
+
end
|
36
|
+
|
37
|
+
def shrink(list, tactic)
|
38
|
+
size = list.size
|
39
|
+
if size < 2
|
40
|
+
puts "#shrink .. dead end"
|
41
|
+
return :dead_end
|
42
|
+
end
|
43
|
+
case tactic
|
44
|
+
when 0
|
45
|
+
return list[0..size/2]
|
46
|
+
when 1
|
47
|
+
return list[size/2..-1]
|
48
|
+
when 2
|
49
|
+
return list[1..-1]
|
50
|
+
when 3
|
51
|
+
return list[0..-2]
|
52
|
+
when 4
|
53
|
+
mid = size/2
|
54
|
+
return list[0..mid-1] + list[mid+1..-1]
|
55
|
+
end
|
56
|
+
|
57
|
+
return :tried_all_tactics
|
58
|
+
end
|
59
|
+
|
60
|
+
def b(n)
|
61
|
+
BoxedFixNum.new n
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
class Sorter
|
67
|
+
def self.sort(items)
|
68
|
+
unstable items
|
69
|
+
# stable items
|
70
|
+
end
|
71
|
+
|
72
|
+
def self.stable(items)
|
73
|
+
items.sort_by.with_index { |x, idx| [x, idx] }
|
74
|
+
end
|
75
|
+
|
76
|
+
def self.unstable(items)
|
77
|
+
items.sort
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
if $0 == __FILE__
|
82
|
+
t = Theft::Runner.new autosize: true
|
83
|
+
|
84
|
+
property_sorting_should_be_stable = lambda do |generated_arg| # , other_generated_arg, etc
|
85
|
+
sorted = Sorter.sort(generated_arg)
|
86
|
+
|
87
|
+
sorted.chunk{|item| item.n}.each do |n, items|
|
88
|
+
if items.size > 1
|
89
|
+
# lazily spot check
|
90
|
+
first = items.first
|
91
|
+
last = items.last
|
92
|
+
|
93
|
+
if generated_arg.index(first) > generated_arg.index(last)
|
94
|
+
return :fail unless sorted.index(first) > sorted.index(last)
|
95
|
+
else
|
96
|
+
return :fail unless sorted.index(first) < sorted.index(last)
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
:pass
|
102
|
+
end
|
103
|
+
|
104
|
+
config = {
|
105
|
+
description: "sorting should be stable",
|
106
|
+
property: property_sorting_should_be_stable,
|
107
|
+
arg_descriptors: [BoxedFixNumArgDescriptor],
|
108
|
+
trials: 3,
|
109
|
+
# must_have_seeds: [],
|
110
|
+
# seed: 1408486348,
|
111
|
+
progress: lambda{|trial_num, args, status| STDOUT.write '.' if trial_num % 2 == 0 },
|
112
|
+
env: :whatevs
|
113
|
+
}
|
114
|
+
|
115
|
+
t.run config
|
116
|
+
end
|
data/lib/theft.rb
CHANGED
@@ -1,5 +1,93 @@
|
|
1
|
-
|
1
|
+
require_relative "theft/version"
|
2
|
+
require 'set'
|
2
3
|
|
3
4
|
module Theft
|
4
|
-
|
5
|
+
class Runner
|
6
|
+
def initialize(args={})
|
7
|
+
@auto_size = args.has_key?(:auto_size) ? args[:auto_size] : true
|
8
|
+
@evaluated_inputs = Set.new
|
9
|
+
end
|
10
|
+
|
11
|
+
def run(config)
|
12
|
+
@seed = config[:seed] || Time.now.to_i
|
13
|
+
@rng = Random.new(@seed)
|
14
|
+
puts "seed: #{@seed}"
|
15
|
+
|
16
|
+
@trials = config[:trials] || 100
|
17
|
+
|
18
|
+
property = config[:property]
|
19
|
+
|
20
|
+
fails = 0
|
21
|
+
passes = 0
|
22
|
+
@trials.times do |trial_num|
|
23
|
+
@descriptors = config[:arg_descriptors]
|
24
|
+
args = @descriptors.map do |desc|
|
25
|
+
desc.setup(@rng, config[:env])
|
26
|
+
end
|
27
|
+
|
28
|
+
unless has_been_tried?(@descriptors, args)
|
29
|
+
result = property.call(*args)
|
30
|
+
|
31
|
+
case result
|
32
|
+
when :fail
|
33
|
+
fails += 1
|
34
|
+
|
35
|
+
args = try_to_shrink(args, property)
|
36
|
+
|
37
|
+
puts "failed on trial: #{trial_num}"
|
38
|
+
@descriptors.each.with_index do |desc, i|
|
39
|
+
puts desc.to_s(args[i])
|
40
|
+
end
|
41
|
+
when :pass
|
42
|
+
passes += 1
|
43
|
+
end
|
44
|
+
|
45
|
+
config[:progress].call(trial_num, args, :passing_for_now)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
puts
|
50
|
+
puts "FAILS: #{fails}"
|
51
|
+
puts "PASSES: #{passes}"
|
52
|
+
end
|
53
|
+
|
54
|
+
def try_to_shrink(args, property)
|
55
|
+
|
56
|
+
@descriptors.each.with_index do |desc, i|
|
57
|
+
tactic_counter = 0
|
58
|
+
|
59
|
+
shrink_result = desc.shrink(args[i], tactic_counter)
|
60
|
+
while shrink_result != :tried_all_tactics
|
61
|
+
#puts desc.to_s(args[i])
|
62
|
+
if shrink_result == :dead_end
|
63
|
+
tactic_counter += 1
|
64
|
+
else
|
65
|
+
copy_args = args.dup
|
66
|
+
copy_args[i] = shrink_result
|
67
|
+
result = property.call(*copy_args)
|
68
|
+
if result == :fail
|
69
|
+
args[i] = shrink_result
|
70
|
+
else
|
71
|
+
tactic_counter += 1
|
72
|
+
end
|
73
|
+
end
|
74
|
+
shrink_result = desc.shrink(args[i], tactic_counter)
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
args
|
79
|
+
end
|
80
|
+
|
81
|
+
def has_been_tried?(descriptors, args)
|
82
|
+
@evaluated_inputs.include? hash_inputs(descriptors, args)
|
83
|
+
end
|
84
|
+
|
85
|
+
def hash_inputs(descriptors, args)
|
86
|
+
hashes = descriptors.map.with_index do |desc, i|
|
87
|
+
desc.hash(args[i])
|
88
|
+
end
|
89
|
+
hash = Digest::MD5.new.digest hashes.join(",")
|
90
|
+
@evaluated_inputs << hash
|
91
|
+
end
|
92
|
+
end
|
5
93
|
end
|
data/lib/theft/version.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: theft
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.1
|
4
|
+
version: 0.0.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Scott Vokes
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2014-
|
12
|
+
date: 2014-09-02 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: bundler
|
@@ -29,14 +29,14 @@ dependencies:
|
|
29
29
|
name: rake
|
30
30
|
requirement: !ruby/object:Gem::Requirement
|
31
31
|
requirements:
|
32
|
-
- - '>='
|
32
|
+
- - ! '>='
|
33
33
|
- !ruby/object:Gem::Version
|
34
34
|
version: '0'
|
35
35
|
type: :development
|
36
36
|
prerelease: false
|
37
37
|
version_requirements: !ruby/object:Gem::Requirement
|
38
38
|
requirements:
|
39
|
-
- - '>='
|
39
|
+
- - ! '>='
|
40
40
|
- !ruby/object:Gem::Version
|
41
41
|
version: '0'
|
42
42
|
description: Ruby port of the theft C library.
|
@@ -52,6 +52,7 @@ files:
|
|
52
52
|
- LICENSE.txt
|
53
53
|
- README.md
|
54
54
|
- Rakefile
|
55
|
+
- examples/stable_sort.rb
|
55
56
|
- lib/theft.rb
|
56
57
|
- lib/theft/version.rb
|
57
58
|
- theft.gemspec
|
@@ -65,14 +66,14 @@ require_paths:
|
|
65
66
|
- lib
|
66
67
|
required_ruby_version: !ruby/object:Gem::Requirement
|
67
68
|
requirements:
|
68
|
-
- - '>='
|
69
|
+
- - ! '>='
|
69
70
|
- !ruby/object:Gem::Version
|
70
71
|
version: '0'
|
71
72
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
72
73
|
requirements:
|
73
|
-
- - '
|
74
|
+
- - ! '>='
|
74
75
|
- !ruby/object:Gem::Version
|
75
|
-
version:
|
76
|
+
version: '0'
|
76
77
|
requirements: []
|
77
78
|
rubyforge_project:
|
78
79
|
rubygems_version: 2.2.2
|