hypothesis-specs 0.0.9 → 0.0.10

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 7e8eb07704e06280a79469b76b6b7267cf356797
4
- data.tar.gz: edd8bb7476a3a84dedf2751016385dbe2d6afcac
3
+ metadata.gz: 2e49a69e84d999619c8b73873bc8dc92f4e9e534
4
+ data.tar.gz: d51bc24f3c230ec37a4a19ddcbd43ca5532646ee
5
5
  SHA512:
6
- metadata.gz: 8e72ef276034266146155092d14827e75f5f20ec3986dd57245f94694e1f7da09d5cc62b74fced3829e0d8e5dbff522ad44d6fc88e2e49d274979eaae3f06e0e
7
- data.tar.gz: b6917a3a24f98febffaab647ecec0393827e3f116390762d3163cd9d2fe7e1cac49cfd79bc2db1e448beb044e862b0c96c79a5e3e8d7aed986ff718eef755d40
6
+ metadata.gz: 1fc1e55701ebf1b24184f243ee232eb4528d85d6306fbe83e95bfd37a0f6f1328077181496620214f3674a2e3f8063bd628178539cbf3eca5af28a7dea86e1a1
7
+ data.tar.gz: 87772f4660cdef5a8a93df5eb81d2322896c58536d3aff5f2ea9a93ec973b83c0ee99c26b4e08467a91af7948dc46ec73a556cbdc3af3a725f075bcdd2b1e148
data/CHANGELOG.md CHANGED
@@ -1,3 +1,11 @@
1
+ ## Hypothesis for Ruby 0.0.10 (2018-04-26)
2
+
3
+ This release is another update to shrinking:
4
+
5
+ * Cases where the value may be simplified without necessarily
6
+ becoming smaller will have better results.
7
+ * Duplicated values can now sometimes be simultaneously shrunk.
8
+
1
9
  ## Hypothesis for Ruby 0.0.9 (2018-04-20)
2
10
 
3
11
  This improves Hypothesis for Ruby's shrinking to be much closer
data/Rakefile CHANGED
@@ -16,7 +16,11 @@ begin
16
16
  t.verbose = true
17
17
  end
18
18
 
19
- task test: %i[build spec minitests]
19
+ task :rust_tests do
20
+ sh 'cargo test'
21
+ end
22
+
23
+ task test: %i[build spec minitests rust_tests]
20
24
  rescue LoadError
21
25
  end
22
26
 
data/src/data.rs CHANGED
@@ -39,6 +39,7 @@ pub struct Draw {
39
39
  pub struct DataSource {
40
40
  bitgenerator: BitGenerator,
41
41
  record: DataStream,
42
+ sizes: Vec<u64>,
42
43
  draws: Vec<DrawInProgress>,
43
44
  draw_stack: Vec<usize>,
44
45
  written_indices: HashSet<usize>,
@@ -49,6 +50,7 @@ impl DataSource {
49
50
  return DataSource {
50
51
  bitgenerator: generator,
51
52
  record: DataStream::new(),
53
+ sizes: Vec::new(),
52
54
  draws: Vec::new(),
53
55
  draw_stack: Vec::new(),
54
56
  written_indices: HashSet::new(),
@@ -88,6 +90,7 @@ impl DataSource {
88
90
  match self.bitgenerator {
89
91
  BitGenerator::Recorded(ref mut v) if self.record.len() >= v.len() => Err(FailedDraw),
90
92
  _ => {
93
+ self.sizes.push(0);
91
94
  self.record.push(value);
92
95
  Ok(())
93
96
  }
@@ -95,6 +98,7 @@ impl DataSource {
95
98
  }
96
99
 
97
100
  pub fn bits(&mut self, n_bits: u64) -> Result<u64, FailedDraw> {
101
+ self.sizes.push(n_bits);
98
102
  let mut result = match self.bitgenerator {
99
103
  BitGenerator::Random(ref mut random) => random.next_u64(),
100
104
  BitGenerator::Recorded(ref mut v) => if self.record.len() >= v.len() {
@@ -119,6 +123,7 @@ impl DataSource {
119
123
  record: self.record,
120
124
  status: status,
121
125
  written_indices: self.written_indices,
126
+ sizes: self.sizes,
122
127
  draws: self.draws
123
128
  .drain(..)
124
129
  .filter_map(|d| match d {
@@ -174,5 +179,6 @@ pub struct TestResult {
174
179
  pub record: DataStream,
175
180
  pub status: Status,
176
181
  pub draws: Vec<Draw>,
182
+ pub sizes: Vec<u64>,
177
183
  pub written_indices: HashSet<usize>,
178
184
  }
data/src/distributions.rs CHANGED
@@ -1,8 +1,8 @@
1
1
  use data::{DataSource, FailedDraw};
2
2
 
3
+ use std::cmp::{Ord, Ordering, PartialOrd, Reverse};
3
4
  use std::collections::BinaryHeap;
4
5
  use std::mem;
5
- use std::cmp::{Ord, Ordering, PartialOrd, Reverse};
6
6
 
7
7
  use std::u64::MAX as MAX64;
8
8
 
@@ -216,16 +216,71 @@ impl Sampler {
216
216
  }
217
217
 
218
218
  pub fn good_bitlengths() -> Sampler {
219
- let weights = vec!(
220
- 4.0, 4.0, 4.0, 4.0, 4.0, 4.0, 4.0, 4.0, // 1 byte
221
- 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, // 2 bytes
222
- 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, // 3 bytes
223
- 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, // 4 bytes
224
- 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, // 5 bytes
225
- 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, // 6 bytes
226
- 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, // 7 bytes
227
- 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, // 8 bytes (last bit spare for sign)
228
- );
219
+ let weights = vec![
220
+ 4.0,
221
+ 4.0,
222
+ 4.0,
223
+ 4.0,
224
+ 4.0,
225
+ 4.0,
226
+ 4.0,
227
+ 4.0, // 1 byte
228
+ 2.0,
229
+ 2.0,
230
+ 2.0,
231
+ 2.0,
232
+ 2.0,
233
+ 2.0,
234
+ 2.0,
235
+ 2.0, // 2 bytes
236
+ 1.0,
237
+ 1.0,
238
+ 1.0,
239
+ 1.0,
240
+ 1.0,
241
+ 1.0,
242
+ 1.0,
243
+ 1.0, // 3 bytes
244
+ 0.5,
245
+ 0.5,
246
+ 0.5,
247
+ 0.5,
248
+ 0.5,
249
+ 0.5,
250
+ 0.5,
251
+ 0.5, // 4 bytes
252
+ 0.1,
253
+ 0.1,
254
+ 0.1,
255
+ 0.1,
256
+ 0.1,
257
+ 0.1,
258
+ 0.1,
259
+ 0.1, // 5 bytes
260
+ 0.1,
261
+ 0.1,
262
+ 0.1,
263
+ 0.1,
264
+ 0.1,
265
+ 0.1,
266
+ 0.1,
267
+ 0.1, // 6 bytes
268
+ 0.1,
269
+ 0.1,
270
+ 0.1,
271
+ 0.1,
272
+ 0.1,
273
+ 0.1,
274
+ 0.1,
275
+ 0.1, // 7 bytes
276
+ 0.1,
277
+ 0.1,
278
+ 0.1,
279
+ 0.1,
280
+ 0.1,
281
+ 0.1,
282
+ 0.1, // 8 bytes (last bit spare for sign)
283
+ ];
229
284
  assert!(weights.len() == 63);
230
285
  Sampler::new(weights)
231
286
  }
data/src/engine.rs CHANGED
@@ -3,11 +3,14 @@
3
3
 
4
4
  use rand::{ChaChaRng, Rng, SeedableRng};
5
5
 
6
+ use std::cmp::Reverse;
7
+ use std::collections::HashMap;
8
+ use std::mem;
6
9
  use std::sync::mpsc::{sync_channel, Receiver, SyncSender};
7
10
  use std::thread;
8
- use std::mem;
9
11
 
10
12
  use data::{DataSource, DataStream, Status, TestResult};
13
+ use intminimize::minimize_integer;
11
14
 
12
15
  #[derive(Debug, Clone)]
13
16
  enum LoopExitReason {
@@ -153,7 +156,8 @@ where
153
156
  while prev != self.changes {
154
157
  prev = self.changes;
155
158
  self.adaptive_delete()?;
156
- self.binary_search_blocks()?;
159
+ self.minimize_individual_blocks()?;
160
+ self.minimize_duplicated_blocks()?;
157
161
  if prev == self.changes {
158
162
  self.expensive_passes_enabled = true;
159
163
  }
@@ -354,27 +358,14 @@ where
354
358
  }
355
359
  }
356
360
 
357
- fn binary_search_blocks(&mut self) -> StepResult {
361
+ fn minimize_individual_blocks(&mut self) -> StepResult {
358
362
  let mut i = 0;
359
363
 
360
364
  while i < self.shrink_target.record.len() {
361
- let mut hi = self.shrink_target.record[i];
362
-
363
- if hi > 0 && !self.shrink_target.written_indices.contains(&i) {
364
- let zeroed = self.try_lowering_value(i, 0)?;
365
- if !zeroed {
366
- let mut lo = 0;
367
- // Binary search to find the smallest value we can
368
- // replace this with.
369
- while lo + 1 < hi {
370
- let mid = lo + (hi - lo) / 2;
371
- if self.try_lowering_value(i, mid)? {
372
- hi = mid;
373
- } else {
374
- lo = mid;
375
- }
376
- }
377
- }
365
+ if !self.shrink_target.written_indices.contains(&i) {
366
+ minimize_integer(self.shrink_target.record[i], |v| {
367
+ self.try_lowering_value(i, v)
368
+ })?;
378
369
  }
379
370
 
380
371
  i += 1;
@@ -383,6 +374,61 @@ where
383
374
  Ok(())
384
375
  }
385
376
 
377
+ fn calc_duplicates(&self) -> Vec<Vec<usize>> {
378
+ assert!(self.shrink_target.record.len() == self.shrink_target.sizes.len());
379
+ let mut duplicates: HashMap<(u64, u64), Vec<usize>> = HashMap::new();
380
+ for (i, (u, v)) in self.shrink_target
381
+ .record
382
+ .iter()
383
+ .zip(self.shrink_target.sizes.iter())
384
+ .enumerate()
385
+ {
386
+ if !self.shrink_target.written_indices.contains(&i) {
387
+ duplicates
388
+ .entry((*u, *v))
389
+ .or_insert_with(|| Vec::new())
390
+ .push(i);
391
+ }
392
+ }
393
+
394
+ let mut result: Vec<Vec<usize>> = duplicates
395
+ .drain()
396
+ .filter_map(|(_, elements)| {
397
+ if elements.len() > 1 {
398
+ Some(elements)
399
+ } else {
400
+ None
401
+ }
402
+ })
403
+ .collect();
404
+ result.sort_by_key(|v| Reverse(v.len()));
405
+ result
406
+ }
407
+
408
+ fn minimize_duplicated_blocks(&mut self) -> StepResult {
409
+ let mut i = 0;
410
+ let mut targets = self.calc_duplicates();
411
+ while i < targets.len() {
412
+ let target = mem::replace(&mut targets[i], Vec::new());
413
+ i += 1;
414
+ assert!(target.len() > 0);
415
+ let v = self.shrink_target.record[target[0]];
416
+ let base = self.shrink_target.record.clone();
417
+
418
+ let w = minimize_integer(v, |t| {
419
+ let mut attempt = base.clone();
420
+ for i in &target {
421
+ attempt[*i] = t
422
+ }
423
+ self.incorporate(&attempt)
424
+ })?;
425
+ if w != v {
426
+ targets = self.calc_duplicates();
427
+ }
428
+ }
429
+ Ok(())
430
+ }
431
+
386
432
  fn execute(&mut self, buf: &DataStream) -> Result<(bool, TestResult), LoopExitReason> {
387
433
  // TODO: Later there will be caching here
388
434
  let result = self.main_loop.execute(DataSource::from_vec(buf.clone()))?;
@@ -0,0 +1,149 @@
1
+ use std::cmp::min;
2
+
3
+ const SMALL: u64 = 5;
4
+
5
+ struct Minimizer<'a, F: 'a> {
6
+ criterion: &'a mut F,
7
+ best: u64,
8
+ }
9
+
10
+ impl<'a, F, T> Minimizer<'a, F>
11
+ where
12
+ F: 'a + FnMut(u64) -> Result<bool, T>,
13
+ {
14
+ fn test(&mut self, candidate: u64) -> Result<bool, T> {
15
+ if candidate == self.best {
16
+ return Ok(true);
17
+ }
18
+ if candidate > self.best {
19
+ return Ok(false);
20
+ }
21
+ let result = (self.criterion)(candidate)?;
22
+ if result {
23
+ self.best = candidate;
24
+ }
25
+ Ok(result)
26
+ }
27
+
28
+ fn modify<G>(&mut self, g: G) -> Result<bool, T>
29
+ where
30
+ G: Fn(u64) -> u64,
31
+ {
32
+ let x = g(self.best);
33
+ self.test(x)
34
+ }
35
+ }
36
+
37
+ pub fn minimize_integer<F, T>(start: u64, mut criterion: F) -> Result<u64, T>
38
+ where
39
+ F: FnMut(u64) -> Result<bool, T>,
40
+ {
41
+ if start == 0 {
42
+ return Ok(start);
43
+ }
44
+
45
+ for i in 0..min(start, SMALL) {
46
+ if criterion(i)? {
47
+ return Ok(i);
48
+ }
49
+ }
50
+ if start <= SMALL {
51
+ return Ok(start);
52
+ }
53
+
54
+ let mut minimizer = Minimizer {
55
+ best: start,
56
+ criterion: &mut criterion,
57
+ };
58
+
59
+ loop {
60
+ if !minimizer.modify(|x| x >> 1)? {
61
+ break;
62
+ }
63
+ }
64
+
65
+ for i in 0..64 {
66
+ minimizer.modify(|x| x ^ (1 << i))?;
67
+ }
68
+
69
+ assert!(minimizer.best >= SMALL);
70
+
71
+ for i in 0..64 {
72
+ let left_mask = 1 << i;
73
+ let mut right_mask = left_mask >> 1;
74
+ while right_mask != 0 {
75
+ minimizer.modify(|x| {
76
+ if x & left_mask == 0 || x & right_mask != 0 {
77
+ x
78
+ } else {
79
+ x ^ (right_mask | left_mask)
80
+ }
81
+ })?;
82
+ right_mask >>= 1;
83
+ }
84
+ }
85
+
86
+ if !minimizer.modify(|x| x - 1)? {
87
+ return Ok(minimizer.best);
88
+ }
89
+
90
+ let mut lo = 0;
91
+ let mut hi = minimizer.best;
92
+ while lo + 1 < hi {
93
+ let mid = lo + (hi - lo) / 2;
94
+ if minimizer.test(mid)? {
95
+ hi = mid;
96
+ } else {
97
+ lo = mid;
98
+ }
99
+ }
100
+
101
+ Ok(minimizer.best)
102
+ }
103
+
104
+ #[cfg(test)]
105
+ mod tests {
106
+ use super::*;
107
+
108
+ fn non_failing_minimize<F>(start: u64, criterion: F) -> u64
109
+ where
110
+ F: Fn(u64) -> bool,
111
+ {
112
+ let mut best = start;
113
+
114
+ loop {
115
+ let ran: Result<u64, ()> = minimize_integer(best, |x| Ok(criterion(x)));
116
+ let result = ran.unwrap();
117
+ assert!(result <= best);
118
+ if result == best {
119
+ return best;
120
+ }
121
+ best = result;
122
+ }
123
+ }
124
+
125
+ #[test]
126
+ fn minimize_down_to() {
127
+ let n = non_failing_minimize(100, |x| x >= 10);
128
+ assert_eq!(n, 10);
129
+ }
130
+
131
+ #[test]
132
+ fn unset_relevant_bits() {
133
+ let x = 0b101010101010;
134
+ let y = 0b111111111111;
135
+ let n = non_failing_minimize(y, |k| k & x == x);
136
+ assert_eq!(n, x);
137
+ }
138
+
139
+ #[test]
140
+ fn sort_bits() {
141
+ let x: u64 = 0b1011011011000111;
142
+ let y: u64 = 0b0000001111111111;
143
+ let c = x.count_ones();
144
+ assert_eq!(c, y.count_ones());
145
+
146
+ let n = non_failing_minimize(x, |k| k.count_ones() == c);
147
+ assert_eq!(y, n);
148
+ }
149
+ }
data/src/lib.rs CHANGED
@@ -12,15 +12,16 @@ extern crate core;
12
12
  extern crate helix;
13
13
  extern crate rand;
14
14
 
15
- mod engine;
16
15
  mod data;
17
16
  mod distributions;
17
+ mod engine;
18
+ mod intminimize;
18
19
 
19
20
  use std::mem;
20
21
 
21
- use engine::Engine;
22
22
  use data::{DataSource, Status};
23
23
  use distributions::Repeat;
24
+ use engine::Engine;
24
25
 
25
26
  ruby! {
26
27
  class HypothesisCoreDataSource {
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: hypothesis-specs
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.9
4
+ version: 0.0.10
5
5
  platform: ruby
6
6
  authors:
7
7
  - David R. Maciver
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2018-04-20 00:00:00.000000000 Z
11
+ date: 2018-04-26 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: helix_runtime
@@ -70,6 +70,7 @@ files:
70
70
  - src/data.rs
71
71
  - src/distributions.rs
72
72
  - src/engine.rs
73
+ - src/intminimize.rs
73
74
  - src/lib.rs
74
75
  homepage: http://github.com/HypothesisWorks/hypothesis-ruby
75
76
  licenses: