to_robust 1.0.0.pre
Sign up to get free protection for your applications and to get access to all the features.
- data/LICENSE +20 -0
- data/README.md +39 -0
- data/lib/global/fix.rb +72 -0
- data/lib/global/fix/add_atom.rb +28 -0
- data/lib/global/fix/atom.rb +51 -0
- data/lib/global/fix/init.rb +4 -0
- data/lib/global/fix/remove_atom.rb +16 -0
- data/lib/global/fix/swap_atom.rb +26 -0
- data/lib/global/global.rb +155 -0
- data/lib/global/init.rb +10 -0
- data/lib/global/kernel/argument_error.rb +22 -0
- data/lib/global/kernel/exception.rb +9 -0
- data/lib/global/kernel/init.rb +4 -0
- data/lib/global/kernel/no_method_error.rb +15 -0
- data/lib/global/kernel/zero_division_error.rb +21 -0
- data/lib/global/report.rb +42 -0
- data/lib/global/robust_proc.rb +48 -0
- data/lib/global/strategies/divide_by_zero_error_strategy.rb +277 -0
- data/lib/global/strategies/init.rb +5 -0
- data/lib/global/strategies/no_method_error_strategy.rb +92 -0
- data/lib/global/strategies/wrong_arguments_error_strategy.rb +174 -0
- data/lib/global/strategy.rb +67 -0
- data/lib/local/init.rb +6 -0
- data/lib/local/kernel/init.rb +1 -0
- data/lib/local/kernel/module.rb +43 -0
- data/lib/local/local.rb +43 -0
- data/lib/local/strategies/bignum_division_strategy.rb +11 -0
- data/lib/local/strategies/bignum_modulus_strategy.rb +11 -0
- data/lib/local/strategies/fixnum_coerce_strategy.rb +11 -0
- data/lib/local/strategies/fixnum_division_strategy.rb +12 -0
- data/lib/local/strategies/fixnum_modulus_strategy.rb +12 -0
- data/lib/local/strategies/float_division_strategy.rb +11 -0
- data/lib/local/strategies/float_modulus_strategy.rb +12 -0
- data/lib/local/strategies/init.rb +37 -0
- data/lib/local/strategies/numeric_division_strategy.rb +11 -0
- data/lib/local/strategies/numeric_modulus_strategy.rb +12 -0
- data/lib/local/strategies/soft_binding_strategy.rb +108 -0
- data/lib/local/strategies/swap_method_strategy.rb +32 -0
- data/lib/local/strategy.rb +18 -0
- data/lib/to_robust.rb +4 -0
- data/test/local/float_division.rb +28 -0
- data/test/local/integer_division.rb +28 -0
- data/test/local/method_binding.rb +45 -0
- metadata +109 -0
@@ -0,0 +1,174 @@
|
|
1
|
+
# Robustness strategy for dealing with ArgumentError's thrown due to incorrect numbers of
|
2
|
+
# arguments being supplied to a method. Trims or pads the list of arguments so that the number
|
3
|
+
# of arguments is equal to the arity of the method (i.e. the number of provided arguments
|
4
|
+
# matches the expected number of arguments).
|
5
|
+
class ToRobust::Global::Strategies::WrongArgumentsErrorStrategy < ToRobust::Global::Strategy
|
6
|
+
|
7
|
+
# Fixes all calls to a given method on a provided line such that they all use
|
8
|
+
# an expected number of arguments. Method calls with too few parameters are padded with zeroes,
|
9
|
+
# whilst method calls with too many parameters are trimmed to the correct size.
|
10
|
+
#
|
11
|
+
# *Parameters:*
|
12
|
+
# * method, the method to fix calls to.
|
13
|
+
# * arity, the arity (expected number of parameters) of the method.
|
14
|
+
# * line, the line to fix method calls on.
|
15
|
+
#
|
16
|
+
# *Returns:*
|
17
|
+
# The fixed form of the line, with all calls to the given method meeting the argument length requirements.
|
18
|
+
def self.fix_calls(method, arity, line)
|
19
|
+
|
20
|
+
# Extract all calls to the affected method on the given line.
|
21
|
+
calls = extract_calls(line)
|
22
|
+
calls.reject! do |c|
|
23
|
+
method_full_name = line[c[0]...line.index('(', c[0])].split(/\.|::/)
|
24
|
+
method_full_name.last != method
|
25
|
+
end
|
26
|
+
|
27
|
+
# Re-order calls so that nested calls are processed first.
|
28
|
+
# First, order the calls by their starting position.
|
29
|
+
# Secondly, create a new list and insert each call at an smaller
|
30
|
+
# index to any calls that enclose it.
|
31
|
+
calls.sort!{|a,b| a[0] <=> b[0]}
|
32
|
+
temp = []
|
33
|
+
calls.each do |x|
|
34
|
+
index = insert_at = temp.length
|
35
|
+
if insert_at > 0
|
36
|
+
temp.reverse_each do |y|
|
37
|
+
index -= 1
|
38
|
+
insert_at = index if y[0] < x[0] and y[1] > x[1]
|
39
|
+
end
|
40
|
+
end
|
41
|
+
temp.insert(insert_at, x)
|
42
|
+
end
|
43
|
+
calls = temp
|
44
|
+
|
45
|
+
# Process each of the calls (in the safe order that has
|
46
|
+
# been established). Begin by extracting the current arguments
|
47
|
+
# for the method call, then either padding or shrinking those
|
48
|
+
# arguments to meet the required length.
|
49
|
+
calls.each_index do |call_index|
|
50
|
+
|
51
|
+
meth_start, meth_end = calls[call_index]
|
52
|
+
params_start = line.index('(', meth_start)+1
|
53
|
+
arg_start = params_start
|
54
|
+
|
55
|
+
# Process each character in the body of parameters.
|
56
|
+
open_brackets = 0
|
57
|
+
arguments = []
|
58
|
+
for char_index in params_start...meth_end
|
59
|
+
|
60
|
+
# Extract the character at the given point in the call.
|
61
|
+
char = line[char_index]
|
62
|
+
|
63
|
+
# Listen for bracket closings.
|
64
|
+
if char == '('
|
65
|
+
open_brackets += 1
|
66
|
+
|
67
|
+
# Listen for any bracket openings.
|
68
|
+
elsif char == ')'
|
69
|
+
open_brackets -= 1
|
70
|
+
|
71
|
+
# Check if this character marks the end of the argument.
|
72
|
+
# Only interpret it as the end of the argument if there are
|
73
|
+
# no open brackets.
|
74
|
+
elsif char == ',' and open_brackets == 0
|
75
|
+
arguments << [arg_start, char_index-1]
|
76
|
+
arg_start = char_index + 1
|
77
|
+
end
|
78
|
+
|
79
|
+
end
|
80
|
+
|
81
|
+
# Add the last argument.
|
82
|
+
arguments << [arg_start, meth_end-1]
|
83
|
+
|
84
|
+
# Shrink the arguments to the limit (better to do this before
|
85
|
+
# extracting their text contents, small optimisation), then
|
86
|
+
# retrieve their context contents (stripping any leading and
|
87
|
+
# trailing whitespace) and pad the arguments with zeroes
|
88
|
+
# where necessary.
|
89
|
+
arguments = arguments.first(arity)
|
90
|
+
arguments.map!{|a_start, a_end| line[a_start..a_end].strip}
|
91
|
+
arguments.fill('0', arguments.length...arity)
|
92
|
+
|
93
|
+
# Apply the transformation, calculate the length difference (delta)
|
94
|
+
# and re-adjust the boundaries of the remaining method calls.
|
95
|
+
transformed = "#{line[meth_start...line.index('(', meth_start)]}(#{arguments.join(',')})"
|
96
|
+
delta = transformed.length - (meth_end - meth_start + 1)
|
97
|
+
line[meth_start..meth_end] = transformed
|
98
|
+
(call_index+1...calls.length).each do |succ_call_index|
|
99
|
+
|
100
|
+
# Find the start and end points of the call.
|
101
|
+
succ_call_start, succ_call_end = calls[succ_call_index]
|
102
|
+
|
103
|
+
# Move the end point of any call containing the call that
|
104
|
+
# has been transformed.
|
105
|
+
if succ_call_start < meth_start and succ_call_end > meth_end
|
106
|
+
calls[succ_call_index][1] += delta
|
107
|
+
|
108
|
+
# Move the start and end points of each call that
|
109
|
+
# starts after the end of this call. (You could embed this
|
110
|
+
# within the if statement above, but this is nicer to read).
|
111
|
+
elsif succ_call_start > meth_end
|
112
|
+
calls[succ_call_index][0] += delta
|
113
|
+
calls[succ_call_index][1] += delta
|
114
|
+
end
|
115
|
+
|
116
|
+
end
|
117
|
+
|
118
|
+
end
|
119
|
+
|
120
|
+
# Return the transformed line.
|
121
|
+
return line
|
122
|
+
|
123
|
+
end
|
124
|
+
|
125
|
+
# Used to fix ArgumentError exceptions by ensuring that all calls to a given method
|
126
|
+
# on the line that the error occurred on use the expected number of arguments.
|
127
|
+
#
|
128
|
+
# When too few arguments are used during a method call, those arguments are padded
|
129
|
+
# with zeroes to reach the correct number of parameters.
|
130
|
+
#
|
131
|
+
# When too many arguments are supplied to a method, those arguments are trimmed to
|
132
|
+
# the number of arguments expected by the method.
|
133
|
+
#
|
134
|
+
# *Parameters:*
|
135
|
+
# * method, the affected method.
|
136
|
+
# * error, the error whose root cause should be fixed.
|
137
|
+
#
|
138
|
+
# *Returns:*
|
139
|
+
# A (possibly empty) array of candidate fixes to the root cause of the error.
|
140
|
+
def generate_candidates(method, error)
|
141
|
+
|
142
|
+
# Ensure that the error is a ArgumentError caused by an incorrect number
|
143
|
+
# of arguments being supplied to a method.
|
144
|
+
return [] unless error.is_a? ArgumentError and error.message.include? 'wrong number of arguments'
|
145
|
+
|
146
|
+
# Extract details of the error.
|
147
|
+
affected_method = error.affected_method
|
148
|
+
args_expected = error.args_expected
|
149
|
+
line_no = error.line_no
|
150
|
+
line_contents = method.source[line_no]
|
151
|
+
|
152
|
+
# We validate the fix by ensuring that any further errors are not
|
153
|
+
# ArgumentErrors for the incorrect number of arguments on the same
|
154
|
+
# method and on the same line.
|
155
|
+
validator = lambda do |method, old_error, new_error|
|
156
|
+
return true unless new_error.is_a? ArgumentError
|
157
|
+
return true unless new_error.message.include? 'wrong number of arguments'
|
158
|
+
return true unless new_error.line_no == old_error.line_no
|
159
|
+
return true unless new_error.affected_method == old_error.affected_method
|
160
|
+
return false
|
161
|
+
end
|
162
|
+
|
163
|
+
# Compose the sole candidate fix for this error.
|
164
|
+
line_contents = self.class.fix_calls(affected_method, args_expected, line_contents)
|
165
|
+
return [
|
166
|
+
ToRobust::Global::Fix.new(
|
167
|
+
[ToRobust::Global::Fix::SwapAtom.new(line_no, line_contents)],
|
168
|
+
validator
|
169
|
+
)
|
170
|
+
]
|
171
|
+
|
172
|
+
end
|
173
|
+
|
174
|
+
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
# Strategies are used by the global robustness layer to suggest candidate fixes to errors that occur
|
2
|
+
# within a given function.
|
3
|
+
class ToRobust::Global::Strategy
|
4
|
+
|
5
|
+
# Extracts the co-ordinates of all method calls on a given line.
|
6
|
+
#
|
7
|
+
# *Parameters:*
|
8
|
+
# * line, the line to extract method calls from.
|
9
|
+
#
|
10
|
+
# *Returns:*
|
11
|
+
# An array of co-ordinate pairs (each an array - could use a range?).
|
12
|
+
def self.extract_calls(line)
|
13
|
+
|
14
|
+
# Find the co-ordinates of every method call in the string.
|
15
|
+
stack = []
|
16
|
+
calls = []
|
17
|
+
(0..line.length).each do |index|
|
18
|
+
|
19
|
+
# Get the character at this index.
|
20
|
+
char = line[index]
|
21
|
+
|
22
|
+
# If this is the start of a method call,
|
23
|
+
# add the starting index to the stack.
|
24
|
+
if char == '('
|
25
|
+
stack << index
|
26
|
+
|
27
|
+
# If this is the end of a method call,
|
28
|
+
# pop the last index off the stack and combine
|
29
|
+
# it with the current index to give the co-ordinates
|
30
|
+
# of the call.
|
31
|
+
elsif char == ')'
|
32
|
+
calls << [stack.pop, index]
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
36
|
+
|
37
|
+
# Post-process the co-ordinates so that they start from the label
|
38
|
+
# of the method call rather than the bracket opening. Do this by
|
39
|
+
# moving towards the start of the original string until the method
|
40
|
+
# definition has finished (checked by looking at the character).
|
41
|
+
calls.each_index do |i|
|
42
|
+
start_at = calls[i][0]
|
43
|
+
until start_at == 0 do
|
44
|
+
char = line[start_at-1]
|
45
|
+
break if not (char.match(/^[[:alpha:]]$/) or ['_',':','.'].include? char)
|
46
|
+
start_at -= 1
|
47
|
+
end
|
48
|
+
calls[i][0] = start_at
|
49
|
+
end
|
50
|
+
|
51
|
+
return calls
|
52
|
+
|
53
|
+
end
|
54
|
+
|
55
|
+
# Generates a list of candidate fixes to a given problem.
|
56
|
+
#
|
57
|
+
# *Parameters:*
|
58
|
+
# * method, the affected method.
|
59
|
+
# * error, the error which occurred within the method.
|
60
|
+
#
|
61
|
+
# *Returns:*
|
62
|
+
# A (possibly empty) array of candidate solutions to fix the root of the error.
|
63
|
+
def generate_candidates(method, error)
|
64
|
+
raise NotImplementedError, 'No "generate_candidates" method was implemented by this Strategy.'
|
65
|
+
end
|
66
|
+
|
67
|
+
end
|
data/lib/local/init.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require_relative 'module.rb'
|
@@ -0,0 +1,43 @@
|
|
1
|
+
class Module
|
2
|
+
|
3
|
+
# Hides all public module methods specific to this module by prepending their names
|
4
|
+
# with "__" and making them private.
|
5
|
+
def hide_methods!
|
6
|
+
singleton_class.public_instance_methods(false).each do |original|
|
7
|
+
|
8
|
+
# Ignore method missing.
|
9
|
+
next if original == :method_missing
|
10
|
+
|
11
|
+
# Prepend "__" to the method name and make it private.
|
12
|
+
hidden = ('__' + original.to_s).to_sym
|
13
|
+
singleton_class.send(:alias_method, hidden, original)
|
14
|
+
singleton_class.send(:private, hidden)
|
15
|
+
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
# Unhides all previously hidden methods, restoring the object/class/module to its
|
20
|
+
# original state.
|
21
|
+
def unhide_methods!
|
22
|
+
hidden_method_symbols.each do |sym|
|
23
|
+
original = sym.to_s[2..-1].to_sym
|
24
|
+
singleton_class.send(:alias_method, original, sym)
|
25
|
+
singleton_class.send(:remove_method, sym)
|
26
|
+
singleton_class.send(:public, original)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
# Returns a hash of the hidden methods of this module (indexed by the original name
|
31
|
+
# of the methods, as strings).
|
32
|
+
def hidden_methods
|
33
|
+
Hash[hidden_method_symbols.map { |sym| [sym.to_s[2..-1], method(sym)] }]
|
34
|
+
end
|
35
|
+
|
36
|
+
private
|
37
|
+
|
38
|
+
# Returns an array of the symbols for each of the hidden methods (including their "__").
|
39
|
+
def hidden_method_symbols
|
40
|
+
singleton_class.private_instance_methods(false).select { |m| m.to_s.start_with? '__' }
|
41
|
+
end
|
42
|
+
|
43
|
+
end
|
data/lib/local/local.rb
ADDED
@@ -0,0 +1,43 @@
|
|
1
|
+
# The Local robustness module is a slightly tailored version of the local robustness layer
|
2
|
+
# proposed in Chris Timperley's Master's Thesis.
|
3
|
+
#
|
4
|
+
# For now patches are enabled and disabled by checking the status of the "enabled" flag in
|
5
|
+
# the Local robustness module for each individual patch.
|
6
|
+
#
|
7
|
+
# A far nicer alternative would be to implement patches as instances of a Patch class.
|
8
|
+
# This class would then contain details of the target class and method as well as a
|
9
|
+
# lambda function implementing the patched form of the method.
|
10
|
+
#
|
11
|
+
# WARNING: Thread safety is a concern.
|
12
|
+
#
|
13
|
+
# Author: Chris Timperley
|
14
|
+
module ToRobust::Local
|
15
|
+
|
16
|
+
# List of strategies.
|
17
|
+
@strategies = []
|
18
|
+
class << self
|
19
|
+
attr_reader :strategies
|
20
|
+
end
|
21
|
+
|
22
|
+
# Executes a given block under local robustness protection.
|
23
|
+
#
|
24
|
+
# *Parameters:*
|
25
|
+
# * *contexts, depickled list of context objects to protect method calls for.
|
26
|
+
# * &block, the block to execute under local robustness protection.
|
27
|
+
#
|
28
|
+
# *Returns:*
|
29
|
+
# * The result of the block execution.
|
30
|
+
def self.protected(*contexts, &block)
|
31
|
+
@strategies.each { |s| s.prepare!(contexts) }
|
32
|
+
@strategies.each { |s| s.enable! }
|
33
|
+
|
34
|
+
begin
|
35
|
+
result = block.call
|
36
|
+
ensure
|
37
|
+
@strategies.each { |s| s.disable! }
|
38
|
+
end
|
39
|
+
|
40
|
+
return result
|
41
|
+
end
|
42
|
+
|
43
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
# Ensures that integer division returns 0 if the denominator is zero.
|
2
|
+
class ToRobust::Local::Strategies::BignumDivisionStrategy < ToRobust::Local::Strategies::SwapMethodStrategy
|
3
|
+
|
4
|
+
# Constructs a new BignumDivisionStrategy.
|
5
|
+
def initialize
|
6
|
+
super(Bignum, :div, :__div) do |other|
|
7
|
+
other.zero? ? 0 : __div(other)
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
# Ensures that integer division returns 0 if the denominator is zero.
|
2
|
+
class ToRobust::Local::Strategies::BignumModulusStrategy < ToRobust::Local::Strategies::SwapMethodStrategy
|
3
|
+
|
4
|
+
# Constructs a new BignumModulusStrategy.
|
5
|
+
def initialize
|
6
|
+
super(Bignum, :%, :__mod) do |other|
|
7
|
+
other.zero? ? 0 : __mod(other)
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
# Coerce nil to 0.
|
2
|
+
class ToRobust::Local::Strategies::FixnumCoerceStrategy < ToRobust::Local::Strategies::SwapMethodStrategy
|
3
|
+
|
4
|
+
# Constructs a new FixnumCoerceStrategy.
|
5
|
+
def initialize
|
6
|
+
super(Fixnum, :coerce, :__coerce) do |other|
|
7
|
+
other.nil? ? 0 : __coerce(other)
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
# Ensures that Fixnum division never encounters zero division errors
|
2
|
+
# by returning zero when the denominator is zero.
|
3
|
+
class ToRobust::Local::Strategies::FixnumDivisionStrategy < ToRobust::Local::Strategies::SwapMethodStrategy
|
4
|
+
|
5
|
+
# Constructs a new FixnumDivisionStrategy.
|
6
|
+
def initialize
|
7
|
+
super(Fixnum, :/, :__div) do |other|
|
8
|
+
other.zero? ? 0 : __div(other)
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
# Ensures that the modulus operator returns zero if a divide
|
2
|
+
# by zero error would occur.
|
3
|
+
class ToRobust::Local::Strategies::FixnumModulusStrategy < ToRobust::Local::Strategies::SwapMethodStrategy
|
4
|
+
|
5
|
+
# Constructs a new FixnumModulusStrategy.
|
6
|
+
def initialize
|
7
|
+
super(Fixnum, :%, :__mod) do |other|
|
8
|
+
other.zero? ? 0 : __mod(other)
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
# Ensures that any float divided by zero gives 0.0
|
2
|
+
class ToRobust::Local::Strategies::FloatDivisionStrategy < ToRobust::Local::Strategies::SwapMethodStrategy
|
3
|
+
|
4
|
+
# Constructs a new FloatDivisionStrategy.
|
5
|
+
def initialize
|
6
|
+
super(Float, :/, :__fdiv) do |other|
|
7
|
+
other.zero? ? 0.0 : __fdiv(other)
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
# Ensures that the modulus operator returns zero if a divide
|
2
|
+
# by zero error would occur.
|
3
|
+
class ToRobust::Local::Strategies::FloatModulusStrategy < ToRobust::Local::Strategies::SwapMethodStrategy
|
4
|
+
|
5
|
+
# Constructs a new FloatModulusStrategy.
|
6
|
+
def initialize
|
7
|
+
super(Float, :%, :__mod) do |other|
|
8
|
+
other.zero? ? 0.0 : __mod(other)
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
module ToRobust::Local::Strategies; end
|
2
|
+
|
3
|
+
# Load all the strategy definitions.
|
4
|
+
require_relative 'swap_method_strategy.rb'
|
5
|
+
|
6
|
+
require_relative 'numeric_division_strategy.rb'
|
7
|
+
require_relative 'numeric_modulus_strategy.rb'
|
8
|
+
|
9
|
+
require_relative 'bignum_division_strategy.rb'
|
10
|
+
require_relative 'bignum_modulus_strategy.rb'
|
11
|
+
|
12
|
+
require_relative 'fixnum_division_strategy.rb'
|
13
|
+
require_relative 'fixnum_modulus_strategy.rb'
|
14
|
+
require_relative 'fixnum_coerce_strategy.rb'
|
15
|
+
|
16
|
+
require_relative 'float_division_strategy.rb'
|
17
|
+
require_relative 'float_modulus_strategy.rb'
|
18
|
+
|
19
|
+
require_relative 'soft_binding_strategy.rb'
|
20
|
+
|
21
|
+
# Instantiate and attach each of them.
|
22
|
+
# This could be done automatically (following class definition) using
|
23
|
+
# the "defined" gem, but this may cause some compatibility issues.
|
24
|
+
ToRobust::Local.strategies << ToRobust::Local::Strategies::NumericDivisionStrategy.new
|
25
|
+
ToRobust::Local.strategies << ToRobust::Local::Strategies::NumericModulusStrategy.new
|
26
|
+
|
27
|
+
ToRobust::Local.strategies << ToRobust::Local::Strategies::BignumDivisionStrategy.new
|
28
|
+
ToRobust::Local.strategies << ToRobust::Local::Strategies::BignumModulusStrategy.new
|
29
|
+
|
30
|
+
ToRobust::Local.strategies << ToRobust::Local::Strategies::FixnumDivisionStrategy.new
|
31
|
+
ToRobust::Local.strategies << ToRobust::Local::Strategies::FixnumModulusStrategy.new
|
32
|
+
ToRobust::Local.strategies << ToRobust::Local::Strategies::FixnumCoerceStrategy.new
|
33
|
+
|
34
|
+
ToRobust::Local.strategies << ToRobust::Local::Strategies::FloatModulusStrategy.new
|
35
|
+
ToRobust::Local.strategies << ToRobust::Local::Strategies::FloatDivisionStrategy.new
|
36
|
+
|
37
|
+
ToRobust::Local.strategies << ToRobust::Local::Strategies::SoftBindingStrategy.new
|