Class: Ibex::DeltaReducer

Inherits:
Object
  • Object
show all
Defined in:
lib/ibex/delta_reducer.rb,
sig/ibex/delta_reducer.rbs

Overview

Deterministic, trial-bounded delta debugging over an ordered sequence.

Defined Under Namespace

Classes: Result

Instance Method Summary collapse

Constructor Details

#initialize(max_trials: 1_000) ⇒ DeltaReducer

Returns a new instance of DeltaReducer.

RBS:

  • (?max_trials: Integer) -> void

Parameters:

  • max_trials: (Integer) (defaults to: 1_000)


25
26
27
28
29
# File 'lib/ibex/delta_reducer.rb', line 25

def initialize(max_trials: 1_000)
  raise ArgumentError, "max_trials must be positive" unless max_trials.positive?

  @max_trials = max_trials
end

Instance Method Details

#checked_trial!(trials) ⇒ Integer

RBS:

  • (Integer trials) -> Integer

Parameters:

  • trials (Integer)

Returns:

  • (Integer)


86
87
88
89
90
# File 'lib/ibex/delta_reducer.rb', line 86

def checked_trial!(trials)
  raise Ibex::Error, "(reduce):1:1: trial limit of #{@max_trials} is exhausted" if trials >= @max_trials

  trials + 1
end

#minimize(items, &failure) ⇒ void

This method returns an undefined value.

failure must return true while the failure of interest is preserved.

RBS:

  • [T] (Array[T] items) { (Array[T]) -> bool } -> Result



33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
# File 'lib/ibex/delta_reducer.rb', line 33

def minimize(items, &failure)
  raise ArgumentError, "a failure predicate is required" unless failure

  current = items.dup
  original_size = current.length
  trials = 0
  trials = checked_trial!(trials)
  unless failure.call(current.freeze)
    raise Ibex::Error, "(reduce):1:1: original input does not reproduce the failure"
  end

  granularity = 2
  while current.length >= 1
    chunks = partitions(current.length, granularity)
    reduced = false
    chunks.each do |range|
      return result(current, trials, false, original_size) if trials >= @max_trials

      candidate = current.dup
      candidate.slice!(range)
      trials = checked_trial!(trials)
      next unless failure.call(candidate.freeze)

      current = candidate
      granularity = [granularity - 1, 2].max
      reduced = true
      break
    end
    next if reduced
    break if granularity >= current.length

    granularity = [granularity * 2, current.length].min
  end

  result(current, trials, true, original_size)
end

#partitions(length, count) ⇒ Array[Range[Integer]]

RBS:

  • (Integer length, Integer count) -> Array[Range[Integer]]

Parameters:

  • length (Integer)
  • count (Integer)

Returns:

  • (Array[Range[Integer]])


73
74
75
76
77
78
79
80
81
82
83
# File 'lib/ibex/delta_reducer.rb', line 73

def partitions(length, count)
  width = (length.to_f / count).ceil
  ranges = [] #: Array[Range[Integer]]
  count.times do |index|
    start = index * width
    break if start >= length

    ranges << (start...[start + width, length].min)
  end
  ranges
end

#result(items, trials, complete, original_size) ⇒ Result

RBS:

  • (Array[untyped] items, Integer trials, bool complete, Integer original_size) -> Result

Parameters:

  • items (Array[untyped])
  • trials (Integer)
  • complete (Boolean)
  • original_size (Integer)

Returns:



93
94
95
96
97
# File 'lib/ibex/delta_reducer.rb', line 93

def result(items, trials, complete, original_size)
  Result.new(
    items: items.dup.freeze, trials: trials, complete: complete, original_size: original_size
  ).freeze
end