cauldron 0.1.5 → 0.1.6

Sign up to get free protection for your applications and to get access to all the features.
Files changed (89) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +4 -1
  3. data/.rspec +1 -0
  4. data/Gemfile +16 -9
  5. data/Gemfile.lock +134 -64
  6. data/README.md +26 -0
  7. data/Rakefile +24 -99
  8. data/build_sandbox.rb +41 -0
  9. data/cucumber.yml +8 -0
  10. data/features/chop.feature +23 -0
  11. data/features/create_dynamic_statements.feature +14 -0
  12. data/features/generate_known_solution.feature +42 -0
  13. data/features/generate_new_statement.feature +20 -0
  14. data/features/step_definitions/cauldron_steps.rb +47 -0
  15. data/features/support/env.rb +1 -1
  16. data/features/use_existing_statements.feature +23 -0
  17. data/lib/cauldron.rb +41 -5
  18. data/lib/cauldron/actualized_composite.rb +35 -0
  19. data/lib/cauldron/array_collect_template/default.rb +95 -0
  20. data/lib/cauldron/array_collect_template/template.rb +57 -0
  21. data/lib/cauldron/builder.rb +60 -0
  22. data/lib/cauldron/caret.rb +50 -0
  23. data/lib/cauldron/dynamic_operator.rb +90 -0
  24. data/lib/cauldron/dynamic_operator_module.rb +140 -0
  25. data/lib/cauldron/example.rb +18 -0
  26. data/lib/cauldron/example_set.rb +36 -0
  27. data/lib/cauldron/histories.rb +53 -0
  28. data/lib/cauldron/history.rb +34 -0
  29. data/lib/cauldron/if_relationship.rb +4 -6
  30. data/lib/cauldron/number_addition_template/add_five.rb +71 -0
  31. data/lib/cauldron/number_addition_template/template.rb +56 -0
  32. data/lib/cauldron/operator.rb +62 -0
  33. data/lib/cauldron/operator/array_reverse_operator.rb +160 -0
  34. data/lib/cauldron/operator/concat_operator.rb +72 -0
  35. data/lib/cauldron/operator/hash_key_value_operator.rb +74 -0
  36. data/lib/cauldron/operator/numeric_operator.rb +115 -0
  37. data/lib/cauldron/operator/string_asterisk_operator.rb +131 -0
  38. data/lib/cauldron/operator/to_s_operator.rb +18 -0
  39. data/lib/cauldron/operator/var_collect_operator.rb +29 -0
  40. data/lib/cauldron/pot.rb +136 -26
  41. data/lib/cauldron/scope.rb +24 -0
  42. data/lib/cauldron/solution/composite.rb +236 -0
  43. data/lib/cauldron/solution/one.rb +49 -0
  44. data/lib/cauldron/statement_generator.rb +298 -0
  45. data/lib/cauldron/template_base.rb +14 -0
  46. data/lib/cauldron/tracer.rb +55 -0
  47. data/lib/cauldron/version.rb +1 -1
  48. data/lib/pry_tester.rb +76 -0
  49. data/ruby_to_sexp.rb +74 -0
  50. data/sandbox.rb +7 -0
  51. data/sexp_to_ruby.rb +150 -0
  52. data/spec/cauldron/actualized_composite_spec.rb +140 -0
  53. data/spec/cauldron/array_collect_template/default_spec.rb +41 -0
  54. data/spec/cauldron/builder_spec.rb +186 -0
  55. data/spec/cauldron/dynamic/add_number_template_spec.rb +30 -0
  56. data/spec/cauldron/dynamic_operator_spec.rb +416 -0
  57. data/spec/cauldron/example_set_spec.rb +49 -0
  58. data/spec/cauldron/example_spec.rb +33 -0
  59. data/spec/cauldron/histories_spec.rb +135 -0
  60. data/spec/cauldron/history_spec.rb +118 -0
  61. data/spec/cauldron/if_relationship_spec.rb +1 -1
  62. data/spec/cauldron/operator/array_reverse_operator_spec.rb +73 -0
  63. data/spec/cauldron/{concat_operator_spec.rb → operator/concat_operator_spec.rb} +30 -12
  64. data/spec/cauldron/operator/hash_key_value_operator_spec.rb +98 -0
  65. data/spec/cauldron/operator/numeric_operator_spec.rb +110 -0
  66. data/spec/cauldron/operator/string_asterisk_operator_spec.rb +196 -0
  67. data/spec/cauldron/operator/var_collect_operator_spec.rb +38 -0
  68. data/spec/cauldron/pot_spec.rb +176 -14
  69. data/spec/cauldron/solution/composite_spec.rb +421 -0
  70. data/spec/cauldron/solution/one_spec.rb +24 -0
  71. data/spec/cauldron/statement_generator_spec.rb +211 -0
  72. data/spec/cauldron/terminal_spec.rb +2 -2
  73. data/spec/spec_helper.rb +5 -1
  74. data/spec/support/code_matcher.rb +55 -0
  75. data/spec/support/include_instance_of_matcher.rb +9 -0
  76. data/spec/support/shared_examples_for_leaf_operators.rb +22 -0
  77. data/spec/support/shared_examples_for_operators.rb +23 -0
  78. data/syntax_spec.txt +2 -0
  79. metadata +104 -41
  80. data/README +0 -1
  81. data/README.rdoc +0 -19
  82. data/VERSION +0 -1
  83. data/features/cauldron_new_approach.feature +0 -46
  84. data/lib/cauldron/array_reverse_operator.rb +0 -39
  85. data/lib/cauldron/concat_operator.rb +0 -34
  86. data/lib/cauldron/numeric_operator.rb +0 -45
  87. data/lib/cauldron/relationship.rb +0 -5
  88. data/spec/cauldron/array_reverse_operator_spec.rb +0 -59
  89. data/spec/cauldron/numeric_operator_spec.rb +0 -70
@@ -0,0 +1,24 @@
1
+ module Cauldron
2
+
3
+ class Scope
4
+ include Enumerable
5
+
6
+ attr_reader :variables
7
+
8
+ def initialize(variables)
9
+ @variables = variables
10
+ end
11
+
12
+ def new_variable!
13
+ #@variables << "var#{variables.length+1}"
14
+ @variables << "var#{variables.length}"
15
+ @variables.last
16
+ end
17
+
18
+ def [](index)
19
+ variables[index]
20
+ end
21
+
22
+ end
23
+
24
+ end
@@ -0,0 +1,236 @@
1
+ module Cauldron::Solution
2
+
3
+ class Composite
4
+
5
+ attr_reader :operators
6
+
7
+ def initialize(children)
8
+ raise StandardError.new('inital value should be an array') unless children.kind_of?(Array)
9
+ @operators = children
10
+ end
11
+
12
+ def record(example)
13
+ # TODO params passed twice - and example not used at all
14
+ insert_tracking(example.params).process(example)
15
+ end
16
+
17
+ def end_points
18
+ results = []
19
+ operators.each do |x|
20
+ if x.content.branch?
21
+ results << [0,x.children.length]
22
+ end
23
+ end
24
+ results << [operators.length]
25
+ end
26
+
27
+ def clone_solution
28
+ #self.clone
29
+ tree_operators = operators.collect do |node|
30
+ Tree::TreeNode.new('x', node.content.clone_statement)
31
+ end
32
+ Composite.new(tree_operators)
33
+ end
34
+
35
+ def add_statement_at(statement, point)
36
+ if point.length == 2
37
+ container = self.operators[0]
38
+ #return self if container.length > 1 # TODO: Quick hack to get it working
39
+ container << Tree::TreeNode.new('SASA', statement)
40
+ elsif point.length == 1
41
+ operators << Tree::TreeNode.new('SASA', statement)
42
+ else
43
+ raise StandardError.new('Have not written code: '+point.inspect)
44
+ end
45
+ self
46
+ end
47
+
48
+ def insert_tracking(params)
49
+ scope = Cauldron::Scope.new(params.clone)
50
+
51
+ # TODO Might be useful
52
+ # trace = TracePoint.new(:call) do |tp|
53
+ # p [tp.lineno, tp.event, tp.raised_exception]
54
+ # end
55
+
56
+
57
+ # NEW: Implementation
58
+ m = %Q{
59
+ def function(#{params.join(',')})
60
+ #{to_ruby(Cauldron::Scope.new(params.clone))}
61
+ end
62
+ }
63
+
64
+ sexp = Ripper::SexpBuilder.new(m).parse
65
+ rendered_code = Sorcerer.source(sexp, indent: true)
66
+ caret = Cauldron::Caret.new
67
+
68
+ rendered_code = Sorcerer.source(sexp, indent: true).gsub(/end/,"\nend").split("\n").reject(&:empty?).join("\n")
69
+
70
+ # Generate tracking code with pending substitutions
71
+ tracked_code = []
72
+ rendered_code.each_line do |line|
73
+ #if line.match /end\s+/
74
+ if line.match /end/
75
+ tracked_code << Sorcerer.source(Ripper::SexpBuilder.new(Cauldron::Tracer.substitue_tracking).parse) #Sorcerer.source(Cauldron::Tracer.substitue_tracking)
76
+ end
77
+ tracked_code << line
78
+ end
79
+ sexp = Ripper::SexpBuilder.new(tracked_code.join("\n")).parse
80
+ code_tracking = Sorcerer.source(sexp, indent: true)
81
+ code_tracking.split("\n")
82
+
83
+ current_line = -1
84
+ total_lines = 0
85
+ new_tracked_code = []
86
+ last_line = nil
87
+ relative_line = 0
88
+ placeholder = nil
89
+ point = [0,0]
90
+ current_depth = 0
91
+ caret = Cauldron::Caret.new
92
+
93
+ points = end_points
94
+
95
+ code_tracking.split("\n").each do |line|
96
+
97
+ if line.match /record/
98
+ depth = (line.match(/^(\s+)/)[0].length / 2) -1
99
+ if depth > current_depth
100
+ relative_line = 0
101
+ end
102
+ current_depth = depth
103
+
104
+ new_tracked_code << last_line
105
+ new_tracked_code << Sorcerer.source(
106
+ Cauldron::Tracer.tracking(relative_line, depth, total_lines, points.shift)
107
+ )
108
+ new_tracked_code << placeholder
109
+ else
110
+ total_lines += 1
111
+
112
+ unless line['=']
113
+ placeholder = "#{'placeholder_'+rand(10000000000).to_s}"
114
+ last_line = "#{placeholder} = "+line
115
+ end
116
+
117
+ if last_line
118
+ if !last_line.match(/\s+end/).nil? || !last_line.match(/function/).nil? # || last_line.match /function/
119
+ last_line = nil
120
+ placeholder = nil
121
+ end
122
+ end
123
+
124
+ if line.match /end$/
125
+ unless line.strip == 'end'
126
+ line = line.gsub(/end$/,'')
127
+ end
128
+ end
129
+
130
+ new_tracked_code << line
131
+ current_line += 1
132
+ end
133
+ #total_lines += 1
134
+ end
135
+
136
+ # NOTE: Keep this to debug before conversion of S-EXP
137
+ sexp = Ripper::SexpBuilder.new(new_tracked_code.join("\n")).parse
138
+
139
+ Cauldron::Tracer.new(sexp)
140
+
141
+ end
142
+
143
+ def reset_and_track(caret)
144
+ caret.return_depth(0)
145
+ Cauldron::Tracer.tracking(caret.line, caret.current_depth, caret.total_lines)
146
+ end
147
+
148
+ def to_sexp(scope=Cauldron::Scope.new)
149
+
150
+ res = operators.collect do |operator|
151
+ #begin
152
+ operator.content.to_ruby(scope, operator.children)
153
+ # rescue NoMethodError => e
154
+ # binding.pry
155
+ # end
156
+
157
+ end.join("\n")
158
+
159
+ sexp = Ripper::SexpBuilder.new(res).parse
160
+ return sexp
161
+ end
162
+
163
+ def to_ruby(scope)
164
+ return '' if operators.empty?
165
+ Sorcerer.source(to_sexp(scope))
166
+ end
167
+
168
+ def add_first_statement(statement)
169
+ [:stmts_add, [:stmts_new], statement]
170
+ end
171
+
172
+ def add_statement(statement, inner)
173
+ [:stmts_add, inner, statement]
174
+ end
175
+
176
+ def solution?(problems)
177
+ o = Object.new
178
+ m = %Q{
179
+ def function(#{problems.variables.join(',')})
180
+ #{to_ruby(problems.scope)}
181
+ end
182
+ }
183
+ o.instance_eval(m)
184
+
185
+ #o.function *problems.examples.first.arguments
186
+ problems.all? do |example|
187
+ o.function(*example.arguments) == example.response
188
+ end
189
+
190
+ # TODO: Remove this resque - it is just a temp
191
+ rescue NoMethodError => e
192
+ return false
193
+ rescue NameError => e
194
+ return false
195
+ rescue TypeError => e
196
+ return false
197
+ end
198
+
199
+ # TODO Drop this method
200
+ def successful?(problem)
201
+
202
+ # # TODO track the parameters of the operator
203
+ # operators.trace(problem)
204
+
205
+ # # TODO For now just evalute the code
206
+ # return true if problem[:arguments].first == problem[:response]
207
+ # false
208
+
209
+ pt = PryTester.new
210
+
211
+ args = problem.arguments
212
+ variables = problem.params #(0...args.length).collect {|x| 'var'+x.to_s}
213
+ a = [
214
+ 'def function('+variables.join(',')+');'+self.to_ruby(variables)+"; end",
215
+ 'function('+problem.arguments.collect {|x| to_programme(x) }.join(',')+')'
216
+ ]
217
+
218
+ res = pt.eval(
219
+ ['def function('+variables.join(',')+');'+self.to_ruby(variables)+"; end", 'function('+problem.arguments.collect {|x| to_programme(x) }.join(',')+')']
220
+ )
221
+
222
+ problem.response == res
223
+ end
224
+
225
+ def to_programme(value)
226
+ if value.kind_of?(String)
227
+ return %Q{'#{value}'}
228
+ end
229
+ value.to_s
230
+ end
231
+
232
+ # TODO Add a safety evalutor
233
+
234
+ end
235
+
236
+ end
@@ -0,0 +1,49 @@
1
+ # def function(var0)
2
+ # var0.collect { |x| x * 2 }
3
+ # end
4
+
5
+ module Cauldron::Solution
6
+
7
+ class One
8
+
9
+ def initialize
10
+
11
+ end
12
+
13
+ def successful?(problem)
14
+ return false unless problem.arguments.first.kind_of?(Array)
15
+ return false unless problem.arguments.first[0] * 2 == problem.response[0]
16
+ return false unless problem.arguments.first[1] * 2 == problem.response[1]
17
+ true
18
+ end
19
+
20
+ def to_ruby(variables)
21
+ sexp =
22
+ [:method_add_block,
23
+ [:call,
24
+ [:vcall,
25
+ [:@ident, variables[0]]
26
+ ],
27
+ :".",
28
+ [:@ident, "collect"]
29
+ ],
30
+ [:brace_block,
31
+ [:block_var,
32
+ [:params,
33
+ [[:@ident, "x"]]
34
+ ]
35
+ ],
36
+ [
37
+ :binary,
38
+ [:var_ref, [:@ident, "x"]],
39
+ :*,
40
+ [:@int, "2"]
41
+ ]
42
+ ]
43
+ ]
44
+ Sorcerer.source(sexp)
45
+ end
46
+
47
+ end
48
+
49
+ end
@@ -0,0 +1,298 @@
1
+ module Cauldron
2
+
3
+ class StatementGenerator
4
+
5
+ # Build appropriate classes that can build appropriate instances - instances
6
+ # must have the constants
7
+ def build(instance,dynamic_methods,declare_variable=false)
8
+ dynamic_methods.collect do |x|
9
+ build_template(instance,x)
10
+ end
11
+ end
12
+
13
+ # TODO Change to build_blue_print
14
+ def build_template(instance, dynamic_method)
15
+ build_class(instance, dynamic_method)
16
+ end
17
+
18
+ def sexp_method_to_ruby(instance, dynamic_method)
19
+ %Q{
20
+ def to_ruby(scope, operators)
21
+ Sorcerer.source self.to_sexp(scope, operators)
22
+ end
23
+ }
24
+ end
25
+
26
+ def sexp_method_to_desc
27
+ %Q{
28
+ def to_desc
29
+ to_ruby( Cauldron::Scope.new(['var0']), [] )
30
+ end
31
+ }
32
+ end
33
+
34
+ def method_to_sexp(instance, dynamic_method)
35
+
36
+ # Does it expect arguments?
37
+ begin
38
+ instance.send(dynamic_method)
39
+ rescue ArgumentError => e
40
+
41
+ number_of_arguments = e.message.match(/(\d+)\)/)[1].to_i
42
+
43
+ #statement = "scope[@indexes[0]] #{dynamic_method}"
44
+ statement = "scope[@indexes[0]]"
45
+
46
+ to_sexp_method = %Q^
47
+ def to_sexp(scope, operators)
48
+ Ripper::SexpBuilder.new(#{statement}).parse
49
+ end
50
+ ^
51
+ # to_sexp_method = %Q^
52
+ # def to_sexp(scope, operators)
53
+ # Ripper::SexpBuilder.new("\#{scope[@indexes[0]]} + \#{constant}").parse
54
+ # end
55
+ # ^
56
+ return to_sexp_method
57
+ end
58
+
59
+ if instance.send(dynamic_method).class == Enumerator
60
+ %Q^
61
+ def to_sexp(scope, operators)
62
+ scope_var = scope.new_variable!
63
+ scope_var_two = scope.new_variable!
64
+ dynamic_method = '#{dynamic_method}'
65
+
66
+ first_variable = 'var'+@indexes[0].to_s
67
+
68
+ a = "\#{scope_var} = \#{first_variable}.\#{dynamic_method} do |\#{scope_var_two}|"+"\n"
69
+ a += operators.collect {|x| x.content.to_ruby(scope, x.children) }.join("\n")
70
+ a += "\n"+"end"+"\n"
71
+ puts a
72
+ Ripper::SexpBuilder.new(a).parse
73
+
74
+ end
75
+ ^
76
+ else
77
+ %Q{
78
+ def to_sexp(scope, operators)
79
+ first_variable = 'var'+@indexes[0].to_s
80
+ [:call,
81
+ [:vcall, [:@ident, first_variable ]],
82
+ :".",
83
+ [:@ident, "#{dynamic_method}"]
84
+ ]
85
+ end
86
+ }
87
+ end
88
+ end
89
+
90
+ def requires_arguments?(instance, dynamic_method)
91
+ instance.send(dynamic_method)
92
+ false
93
+ rescue ArgumentError => e
94
+ true
95
+ end
96
+
97
+ def expects_block?(instance, dynamic_method)
98
+ instance.send(dynamic_method).class == Enumerator
99
+ end
100
+
101
+ def branch_method(instance, dynamic_method)
102
+ if requires_arguments?(instance, dynamic_method)
103
+ return %q{
104
+ def branch?
105
+ false
106
+ end
107
+ }
108
+ end
109
+
110
+ if expects_block?(instance, dynamic_method)
111
+ return %q{
112
+ def branch?
113
+ true
114
+ end
115
+ }
116
+ end
117
+ %q{
118
+ def branch?
119
+ false
120
+ end
121
+ }
122
+ end
123
+
124
+ def template_sexp(instance, dynamic_method)
125
+
126
+ res = %Q{
127
+
128
+ #{sexp_method_to_ruby(instance, dynamic_method)}
129
+
130
+ #{method_to_sexp(instance, dynamic_method)}
131
+
132
+ #{sexp_method_to_desc}
133
+
134
+ #{branch_method(instance, dynamic_method)}
135
+
136
+ def self.instances(histories, composite, examples, insert_points)
137
+
138
+ # TEMP
139
+ unless examples.class == ExampleSet
140
+ raise StandardError.new('Examples should be an example')
141
+ end
142
+
143
+ # Print out each insertable statements
144
+ scope = examples.scope
145
+
146
+ # self.init([0]).to_ruby(scope)
147
+ # - this will print out "var0.chop"
148
+
149
+ # Get the variables available at each point
150
+ results = []
151
+
152
+ insert_points.each do |point|
153
+
154
+ # Find the variables at a particular point
155
+ # TODO Change to test
156
+ contexts = histories.contexts_at(point)
157
+ composites = context_instances(contexts)
158
+
159
+ composites.each do |x|
160
+ if contexts.all? do |context|
161
+ x.context_realizable?(context)
162
+ end
163
+ results << extend_actualized_composite(x, composite, examples, point)
164
+ end
165
+ end
166
+
167
+ end
168
+
169
+ results
170
+ end
171
+
172
+ }
173
+ Ripper::SexpBuilder.new(res).parse
174
+ end
175
+
176
+ def dynamic_template_name(instance, dynamic_method)
177
+ dynamic_method_name = dynamic_method.to_s.gsub(/\+/,'Add')
178
+ dynamic_name = ('Dynamic'+'_'+instance.class.to_s+'_'+dynamic_method_name.to_s).camelize
179
+ dynamic_name+'Template'
180
+ end
181
+
182
+ def default_template(instance, dynamic_method)
183
+ blue_print = build_class(instance, dynamic_method)
184
+ blue_print.statement_classes.first
185
+ end
186
+
187
+ def build_class(instance, dynamic_method)
188
+ sexp = template_sexp(instance, dynamic_method)
189
+ information = { constants: false }
190
+
191
+ template_name = dynamic_template_name(instance, dynamic_method)
192
+
193
+ # http://ruby-doc.org/core-2.3.0/Class.html
194
+ # http://stackoverflow.com/questions/4113479/dynamic-class-definition-with-a-class-name
195
+ unless Object.const_defined? template_name
196
+ c = Object.const_set(
197
+ template_name,
198
+ Class.new do
199
+
200
+ attr_reader :indexes, :dynamic_name, :sexp_methods
201
+ attr_accessor :failed_uses
202
+
203
+ def initialize(information, sexp_methods)
204
+ @information, @sexp_methods = information, sexp_methods
205
+ @failed_uses = []
206
+ end
207
+
208
+ # NOTE: These theses classes define the constants
209
+ def statement_classes(examples = nil)
210
+
211
+ #binding.pry
212
+
213
+ # Find the constants
214
+ b = Object.const_set(
215
+ self.class.to_s+rand(4000000).to_s,
216
+ Class.new do
217
+
218
+ include Cauldron::Operator
219
+ include Cauldron::DynamicOperatorModule
220
+
221
+ attr_reader :indexes
222
+ attr_accessor :failed_uses
223
+
224
+ def initialize(indexes)
225
+ @indexes = indexes
226
+ @failed_uses = []
227
+ end
228
+
229
+ def self.context_instances(contexts)
230
+ temp = []
231
+ contexts.each do |context|
232
+ temp << context.keys.collect(&:to_s).select {|x| x.match(/var\d/) }
233
+ end
234
+ results = temp.flatten.uniq
235
+
236
+ variable_numbers = results.collect { |x| x.match(/var(\d+)/)[1] }
237
+ # TODO Presumes that only one variable is passed
238
+ variable_numbers.collect { |x| new([x.to_i])}
239
+ end
240
+
241
+ def self.extend_actualized_composite(x, container, examples, point)
242
+ cloned_container = container.clone_solution
243
+ cloned_container.add_statement_at(x, point)
244
+ cloned_container
245
+ Cauldron::ActualizedComposite.new(cloned_container, examples)
246
+ end
247
+
248
+ def rip2
249
+ %Q{
250
+ def function(var0)
251
+ #{Sorcerer.source(to_sexp(Cauldron::Scope.new(['var0']),[]), indent: true)}
252
+ end
253
+ }
254
+ end
255
+
256
+ end
257
+ )
258
+ b.class_eval(Sorcerer.source(sexp_methods, indent: true))
259
+ [b]
260
+ end
261
+
262
+ end
263
+ )
264
+
265
+
266
+ #a = c.new(information, sexp.clone)
267
+
268
+ #return a.statement_classes.first
269
+ return c.new(information, sexp.clone)
270
+ else
271
+ #a = eval(template_name).new(information, sexp.clone)
272
+ return eval(template_name).new(information, sexp.clone) #a.statement_classes.first
273
+ end
274
+
275
+ raise StandardError.new('Should not get here')
276
+ end
277
+
278
+ end
279
+
280
+ end
281
+
282
+ # TODO stacking mulitple rescues does not work
283
+ # realizable?
284
+
285
+ # def realizable?(composite, examples)
286
+ # o = Object.new
287
+ # composite.to_ruby(examples.scope)
288
+ # sexp = rip(composite,examples)
289
+ # o.instance_eval(Sorcerer.source(sexp, indent: true))
290
+ # begin
291
+ # o.function(examples.examples.first.arguments.first)
292
+ # rescue NoMethodError
293
+ # # TODO Need to record failing tests here
294
+ # failed_uses << { composite:composite, examples: examples}
295
+ # return false
296
+ # end
297
+ # true
298
+ # end