fable 0.5.0
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 +7 -0
- data/.circleci/config.yml +30 -0
- data/.gitignore +57 -0
- data/.ruby-version +1 -0
- data/CODE_OF_CONDUCT.md +74 -0
- data/Gemfile +7 -0
- data/Gemfile.lock +30 -0
- data/LICENSE +21 -0
- data/README.md +2 -0
- data/Rakefile +10 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/bin/test +8 -0
- data/fable.gemspec +34 -0
- data/fable.sublime-project +8 -0
- data/lib/fable.rb +49 -0
- data/lib/fable/call_stack.rb +351 -0
- data/lib/fable/choice.rb +31 -0
- data/lib/fable/choice_point.rb +65 -0
- data/lib/fable/container.rb +218 -0
- data/lib/fable/control_command.rb +156 -0
- data/lib/fable/debug_metadata.rb +13 -0
- data/lib/fable/divert.rb +100 -0
- data/lib/fable/glue.rb +7 -0
- data/lib/fable/ink_list.rb +425 -0
- data/lib/fable/list_definition.rb +44 -0
- data/lib/fable/list_definitions_origin.rb +35 -0
- data/lib/fable/native_function_call.rb +324 -0
- data/lib/fable/native_function_operations.rb +149 -0
- data/lib/fable/observer.rb +205 -0
- data/lib/fable/path.rb +186 -0
- data/lib/fable/pointer.rb +42 -0
- data/lib/fable/profiler.rb +287 -0
- data/lib/fable/push_pop_type.rb +11 -0
- data/lib/fable/runtime_object.rb +159 -0
- data/lib/fable/search_result.rb +20 -0
- data/lib/fable/serializer.rb +560 -0
- data/lib/fable/state_patch.rb +47 -0
- data/lib/fable/story.rb +1447 -0
- data/lib/fable/story_state.rb +915 -0
- data/lib/fable/tag.rb +14 -0
- data/lib/fable/value.rb +334 -0
- data/lib/fable/variable_assignment.rb +20 -0
- data/lib/fable/variable_reference.rb +38 -0
- data/lib/fable/variables_state.rb +327 -0
- data/lib/fable/version.rb +3 -0
- data/lib/fable/void.rb +4 -0
- data/zork_mode.rb +23 -0
- metadata +149 -0
@@ -0,0 +1,44 @@
|
|
1
|
+
module Fable
|
2
|
+
class ListDefinition
|
3
|
+
attr_accessor :name, :items
|
4
|
+
|
5
|
+
# The main representation should be simple item names, rather than a
|
6
|
+
# RawListItem, since we mainly want to access items based on their
|
7
|
+
# simple name, since that's how they'll be most commonly requested
|
8
|
+
# from ink
|
9
|
+
attr_accessor :item_name_to_values
|
10
|
+
|
11
|
+
def items
|
12
|
+
if @items.nil?
|
13
|
+
@items = {}
|
14
|
+
@item_name_to_values.each do |key, value|
|
15
|
+
item = InkList::InkListItem.new(origin_name: name, item_name: key)
|
16
|
+
@items[item] = value
|
17
|
+
end
|
18
|
+
end
|
19
|
+
@items
|
20
|
+
end
|
21
|
+
|
22
|
+
def value_for_item(item)
|
23
|
+
return item_name_to_values[item.item_name] || 0
|
24
|
+
end
|
25
|
+
|
26
|
+
def contains?(item)
|
27
|
+
return false if item.origin_name != self.name
|
28
|
+
return contains_item_with_name?(item.item_name)
|
29
|
+
end
|
30
|
+
|
31
|
+
def contains_item_with_name?(item_name)
|
32
|
+
return item_name_to_values.has_key?(item_name)
|
33
|
+
end
|
34
|
+
|
35
|
+
def item_for_value(int_value)
|
36
|
+
return item_name_to_values.key(int_value)
|
37
|
+
end
|
38
|
+
|
39
|
+
def initialize(name, items)
|
40
|
+
self.name = name
|
41
|
+
self.item_name_to_values = items
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
module Fable
|
2
|
+
class ListDefinitionsOrigin
|
3
|
+
attr_accessor :_lists, :all_unambiguous_list_value_cache
|
4
|
+
|
5
|
+
def lists
|
6
|
+
self._lists.map{|k,v| v}
|
7
|
+
end
|
8
|
+
|
9
|
+
def find_list(name)
|
10
|
+
self._lists[name]
|
11
|
+
end
|
12
|
+
|
13
|
+
def initialize(lists)
|
14
|
+
self._lists = {}
|
15
|
+
self.all_unambiguous_list_value_cache = {}
|
16
|
+
|
17
|
+
lists.each do |list|
|
18
|
+
self._lists[list.name] = list
|
19
|
+
|
20
|
+
list.items.each do |item, int_value|
|
21
|
+
list_value = ListValue.new(item, int_value)
|
22
|
+
|
23
|
+
# May be ambiguous, but compiler should've caught that,
|
24
|
+
# so we may be doing some replacement here, but that's okay
|
25
|
+
self.all_unambiguous_list_value_cache[item.item_name] = list_value
|
26
|
+
self.all_unambiguous_list_value_cache[item.full_name] = list_value
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def find_single_item_list_with_name(name)
|
32
|
+
return all_unambiguous_list_value_cache[name]
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,324 @@
|
|
1
|
+
module Fable
|
2
|
+
class NativeFunctionCall < RuntimeObject
|
3
|
+
extend NativeFunctionOperations
|
4
|
+
|
5
|
+
FUNCTIONS = {
|
6
|
+
# native functions
|
7
|
+
ADDITION: "+",
|
8
|
+
SUBTRACTION: "-",
|
9
|
+
DIVIDE: "/",
|
10
|
+
MULTIPLY: "*",
|
11
|
+
MODULO: "%",
|
12
|
+
NEGATE: "_",
|
13
|
+
|
14
|
+
EQUALS: "==",
|
15
|
+
GREATER_THAN: ">",
|
16
|
+
LESS_THAN: "<",
|
17
|
+
GREATER_THAN_OR_EQUAL_TO: ">=",
|
18
|
+
LESS_THAN_OR_EQUAL_TO: "<=",
|
19
|
+
NOT_EQUAL: "!=",
|
20
|
+
NOT: "!",
|
21
|
+
|
22
|
+
|
23
|
+
AND: "&&",
|
24
|
+
OR: "||",
|
25
|
+
|
26
|
+
MIN: "MIN",
|
27
|
+
MAX: "MAX",
|
28
|
+
|
29
|
+
POWER: "POW",
|
30
|
+
FLOOR: "FLOOR",
|
31
|
+
CEILING: "CEILING",
|
32
|
+
INT_VALUE: "INT",
|
33
|
+
FLOAT_VALUE: "FLOAT",
|
34
|
+
|
35
|
+
HAS: "?",
|
36
|
+
HAS_NOT: "!?",
|
37
|
+
INTERSECTION: "^",
|
38
|
+
|
39
|
+
LIST_MINIMUM: "LIST_MIN",
|
40
|
+
LIST_MAXIMUM: "LIST_MAX",
|
41
|
+
LIST_ALL: "LIST_ALL",
|
42
|
+
LIST_COUNT: "LIST_COUNT",
|
43
|
+
VALUE_OF_LIST: "LIST_VALUE",
|
44
|
+
LIST_INVERT: "LIST_INVERT",
|
45
|
+
}.freeze
|
46
|
+
|
47
|
+
NUMBER_OF_PARAMETERS = {
|
48
|
+
ADDITION: 2,
|
49
|
+
SUBTRACTION: 2,
|
50
|
+
DIVIDE: 2,
|
51
|
+
MULTIPLY: 2,
|
52
|
+
MODULO: 2,
|
53
|
+
NEGATE: 1,
|
54
|
+
|
55
|
+
EQUALS: 2,
|
56
|
+
GREATER_THAN: 2,
|
57
|
+
LESS_THAN: 2,
|
58
|
+
GREATER_THAN_OR_EQUAL_TO: 2,
|
59
|
+
LESS_THAN_OR_EQUAL_TO: 2,
|
60
|
+
NOT_EQUAL: 2,
|
61
|
+
NOT: 1,
|
62
|
+
|
63
|
+
|
64
|
+
AND: 2,
|
65
|
+
OR: 2,
|
66
|
+
|
67
|
+
MIN: 2,
|
68
|
+
MAX: 2,
|
69
|
+
|
70
|
+
POWER: 2,
|
71
|
+
FLOOR: 1,
|
72
|
+
CEILING: 1,
|
73
|
+
INT_VALUE: 1,
|
74
|
+
FLOAT_VALUE: 1,
|
75
|
+
|
76
|
+
HAS: 2,
|
77
|
+
HAS_NOT: 2,
|
78
|
+
INTERSECTION: 2,
|
79
|
+
|
80
|
+
LIST_MINIMUM: 1,
|
81
|
+
LIST_MAXIMUM: 1,
|
82
|
+
LIST_ALL: 1,
|
83
|
+
LIST_COUNT: 1,
|
84
|
+
VALUE_OF_LIST: 1,
|
85
|
+
LIST_INVERT: 1,
|
86
|
+
}.freeze
|
87
|
+
|
88
|
+
LOOKUP = FUNCTIONS.invert.freeze
|
89
|
+
|
90
|
+
attr_accessor :name, :number_of_parameters
|
91
|
+
|
92
|
+
def self.is_native_function?(value)
|
93
|
+
LOOKUP.has_key?(value)
|
94
|
+
end
|
95
|
+
|
96
|
+
def initialize(function_symbol)
|
97
|
+
super()
|
98
|
+
self.name = LOOKUP[function_symbol]
|
99
|
+
self.number_of_parameters = NUMBER_OF_PARAMETERS[self.name]
|
100
|
+
end
|
101
|
+
|
102
|
+
def call!(parameters)
|
103
|
+
if parameters.size != self.number_of_parameters
|
104
|
+
raise StoryError, "Unexpected number of parameters"
|
105
|
+
end
|
106
|
+
|
107
|
+
has_list = false
|
108
|
+
|
109
|
+
parameters.each do |parameter|
|
110
|
+
case parameter
|
111
|
+
when Void
|
112
|
+
raise StoryError, "Attempting to perform operation on a void value. Did you forget to 'return' a value from a function you called here?"
|
113
|
+
when ListValue
|
114
|
+
has_list = true
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
# Binary operations on lists are treated outside of the standard coercion rules
|
119
|
+
if parameters.size == 2 && has_list
|
120
|
+
return call_binary_list_operation(parameters)
|
121
|
+
end
|
122
|
+
|
123
|
+
coerced_parameters = coerce_values_to_single_type(parameters)
|
124
|
+
|
125
|
+
return underlying_function_call(coerced_parameters)
|
126
|
+
end
|
127
|
+
|
128
|
+
def underlying_function_call(parameters)
|
129
|
+
parameter_1 = parameters.first
|
130
|
+
value_type = parameter_1.class
|
131
|
+
|
132
|
+
if parameters.size > 2
|
133
|
+
raise Error, "Unexpected number of parameters to NativeFunctionCall: #{parameters.size}"
|
134
|
+
end
|
135
|
+
|
136
|
+
# if !can_perform_function_on?(value_type)
|
137
|
+
# raise Error, "Cannot perform operation '#{self.name}' on #{value_type}"
|
138
|
+
# end
|
139
|
+
|
140
|
+
# Binary function
|
141
|
+
if parameters.size == 2
|
142
|
+
parameter_2 = parameters.last
|
143
|
+
result = run_operation(parameter_1.value, parameter_2.value)
|
144
|
+
|
145
|
+
return Value.create(result)
|
146
|
+
# Unary Function
|
147
|
+
else
|
148
|
+
result = run_operation(parameter_1.value)
|
149
|
+
return Value.create(result)
|
150
|
+
end
|
151
|
+
end
|
152
|
+
|
153
|
+
def call_binary_list_operation(parameters)
|
154
|
+
value_1 = parameters[0]
|
155
|
+
value_2 = parameters[1]
|
156
|
+
|
157
|
+
# List-Int addition/subtraction returns a List (eg: "alpha" + 1 = "beta")
|
158
|
+
if (name == :ADDITION || name == :SUBTRACTION) && value_1.is_a?(ListValue) && value_2.is_a?(IntValue)
|
159
|
+
return call_list_increment_operation(parameters)
|
160
|
+
end
|
161
|
+
|
162
|
+
# And/or with any other types required coercion to bool (int)
|
163
|
+
if (name == :AND || :OR) && (!value_1.is_a?(ListValue) || !value_2.is_a?(ListValue))
|
164
|
+
value_1_as_boolean = value_1.truthy? ? 1 : 0
|
165
|
+
value_2_as_boolean = value_2.truthy? ? 1 : 0
|
166
|
+
|
167
|
+
result = run_operation(value_1_as_boolean, value_2_as_boolean)
|
168
|
+
return IntValue.new(result)
|
169
|
+
end
|
170
|
+
|
171
|
+
# Normal (list X list) operation
|
172
|
+
if value_1.is_a?(ListValue) && value_2.is_a?(ListValue)
|
173
|
+
if name == :HAS || name == :HAS_NOT
|
174
|
+
return run_operation(value_1.value, value_2.value)
|
175
|
+
else
|
176
|
+
result = run_operation(value_1.value, value_2.value)
|
177
|
+
return ListValue.new(result)
|
178
|
+
end
|
179
|
+
end
|
180
|
+
|
181
|
+
raise Error, "Can not call '#{name}' operation on '#{value_1.class}' and '#{value_2.class}'"
|
182
|
+
end
|
183
|
+
|
184
|
+
def call_list_increment_operation(parameters)
|
185
|
+
list_value = parameters[0]
|
186
|
+
int_value = parameters[1]
|
187
|
+
|
188
|
+
result_list = InkList.new
|
189
|
+
|
190
|
+
list_value.value.items.each do |list_item, list_item_value|
|
191
|
+
target_integer = run_operation(list_item_value, int_value.value)
|
192
|
+
|
193
|
+
# Find this item's origin
|
194
|
+
item_origin = list_value.value.origins.find{|origin| origin.name == list_item.origin_name }
|
195
|
+
|
196
|
+
if !item_origin.nil?
|
197
|
+
incremented_item = item_origin.item_for_value(target_integer)
|
198
|
+
if !incremented_item.nil?
|
199
|
+
result_list.add(incremented_item, target_integer)
|
200
|
+
end
|
201
|
+
end
|
202
|
+
end
|
203
|
+
|
204
|
+
return ListValue.new(result_list)
|
205
|
+
end
|
206
|
+
|
207
|
+
def coerce_values_to_single_type(parameters)
|
208
|
+
given_types = parameters.map(&:class)
|
209
|
+
special_case_list = parameters.find{|x| x.is_a?(ListValue) }
|
210
|
+
|
211
|
+
# Find out what the output type is; "higher-level" types infect both
|
212
|
+
# so that binary operations use the same type on both sides
|
213
|
+
# (eg: binary operation of int & float causes the int to be casted as a float)
|
214
|
+
value_type = ([IntValue] + given_types).max{|a,b| OrderedValueTypes[a] <=> OrderedValueTypes[b] }
|
215
|
+
|
216
|
+
# Coerce to this chosen type
|
217
|
+
parameters_out = []
|
218
|
+
|
219
|
+
# Special case: Coercing Ints to Lists
|
220
|
+
# We have to do it early when we have both parameters
|
221
|
+
# to hand, so that we can make use of the list's origin
|
222
|
+
if value_type == ListValue
|
223
|
+
parameters.each do |parameter|
|
224
|
+
if parameter.is_a?(ListValue)
|
225
|
+
parameters_out << parameter
|
226
|
+
elsif parameter.is_a?(IntValue)
|
227
|
+
int_value = parameter.value
|
228
|
+
list = special_case_list.value.origin_of_max_item
|
229
|
+
|
230
|
+
item = list.item_for_value(int_value)
|
231
|
+
|
232
|
+
if !item.nil?
|
233
|
+
parameters_out << ListValue.new(item, int_value)
|
234
|
+
else
|
235
|
+
raise Error, "Could not find List item with the value '#{int_value}' in #{list.name}"
|
236
|
+
end
|
237
|
+
else
|
238
|
+
raise Error, "Cannot mix Lists and #{parameter.class} values in this operation"
|
239
|
+
end
|
240
|
+
end
|
241
|
+
# Normal coercing, with standard casting
|
242
|
+
else
|
243
|
+
parameters.each do |parameter|
|
244
|
+
parameters_out << parameter.cast(value_type)
|
245
|
+
end
|
246
|
+
end
|
247
|
+
|
248
|
+
return parameters_out
|
249
|
+
end
|
250
|
+
|
251
|
+
def to_s
|
252
|
+
"Native '#{name}'"
|
253
|
+
end
|
254
|
+
|
255
|
+
protected
|
256
|
+
|
257
|
+
def run_operation(*parameters)
|
258
|
+
case name
|
259
|
+
when :ADDITION
|
260
|
+
return self.class.addition(parameters[1], parameters[0])
|
261
|
+
when :SUBTRACTION
|
262
|
+
return self.class.subtraction(parameters[1], parameters[0])
|
263
|
+
when :DIVIDE
|
264
|
+
return self.class.divide(parameters[1], parameters[0])
|
265
|
+
when :MULTIPLY
|
266
|
+
return self.class.multiply(parameters[1], parameters[0])
|
267
|
+
when :MODULO
|
268
|
+
return self.class.modulo(parameters[1], parameters[0])
|
269
|
+
when :NEGATE
|
270
|
+
return self.class.negate(parameter[0])
|
271
|
+
when :EQUALS
|
272
|
+
return self.class.equal(parameters[1], parameters[0])
|
273
|
+
when :GREATER_THAN
|
274
|
+
return self.class.greater(parameters[1], parameters[0])
|
275
|
+
when :LESS_THAN
|
276
|
+
return self.class.less(parameters[1], parameters[0])
|
277
|
+
when :GREATER_THAN_OR_EQUAL_TO
|
278
|
+
return self.class.greater_than_or_equal(parameters[1], parameters[0])
|
279
|
+
when :LESS_THAN_OR_EQUAL_TO
|
280
|
+
return self.class.less_than_or_equal(parameters[1], parameters[0])
|
281
|
+
when :NOT_EQUAL
|
282
|
+
return self.class.not_equal(parameters[1], parameters[0])
|
283
|
+
when :NOT
|
284
|
+
return self.class.not(parameters[0])
|
285
|
+
when :AND
|
286
|
+
return self.class.and(parameters[1], parameters[0])
|
287
|
+
when :OR
|
288
|
+
return self.class.or(parameters[1], parameters[0])
|
289
|
+
when :MIN
|
290
|
+
return self.class.min(parameters[1], parameters[0])
|
291
|
+
when :MAX
|
292
|
+
return self.class.max(parameters[1], parameters[0])
|
293
|
+
when :POWER
|
294
|
+
return self.class.pow(parameters[1], parameters[0])
|
295
|
+
when :FLOOR
|
296
|
+
return self.class.floor(parameters[0])
|
297
|
+
when :CEILING
|
298
|
+
return self.class.ceiling(parameters[0])
|
299
|
+
when :INT_VALUE
|
300
|
+
return self.class.int_value(parameters[0])
|
301
|
+
when :FLOAT_VALUE
|
302
|
+
return self.class.float_value(parameters[0])
|
303
|
+
when :HAS
|
304
|
+
return self.class.has(parameters[1], parameters[0])
|
305
|
+
when :HAS_NOT
|
306
|
+
return self.class.has_not(parameters[1], parameters[0])
|
307
|
+
when :INTERSECTION
|
308
|
+
return self.class.intersection(parameters[1], parameters[0])
|
309
|
+
when :LIST_MINIMUM
|
310
|
+
return self.class.list_min(parameters[0])
|
311
|
+
when :LIST_MAXIMUM
|
312
|
+
return self.class.list_max(parameters[0])
|
313
|
+
when :LIST_ALL
|
314
|
+
return self.class.all(parameters[0])
|
315
|
+
when :LIST_COUNT
|
316
|
+
return self.class.count(parameters[0])
|
317
|
+
when :VALUE_OF_LIST
|
318
|
+
return self.class.value_of_list(parameters[0])
|
319
|
+
when :LIST_INVERT
|
320
|
+
return self.class.invert(parameters[0])
|
321
|
+
end
|
322
|
+
end
|
323
|
+
end
|
324
|
+
end
|
@@ -0,0 +1,149 @@
|
|
1
|
+
module Fable
|
2
|
+
module NativeFunctionOperations
|
3
|
+
def addition(x, y)
|
4
|
+
return x + y
|
5
|
+
end
|
6
|
+
|
7
|
+
def subtraction(x,y)
|
8
|
+
return x - y
|
9
|
+
end
|
10
|
+
|
11
|
+
def multiply(x,y)
|
12
|
+
return x * y
|
13
|
+
end
|
14
|
+
|
15
|
+
def divide(x,y)
|
16
|
+
x / y
|
17
|
+
end
|
18
|
+
|
19
|
+
def modulo(x,y)
|
20
|
+
return x % y
|
21
|
+
end
|
22
|
+
|
23
|
+
def negate(x)
|
24
|
+
return -x
|
25
|
+
end
|
26
|
+
|
27
|
+
def equal(x,y)
|
28
|
+
return (x == y) ? 1 : 0
|
29
|
+
end
|
30
|
+
|
31
|
+
def greater(x,y)
|
32
|
+
return (x > y) ? 1 : 0
|
33
|
+
end
|
34
|
+
|
35
|
+
def less(x,y)
|
36
|
+
return (x < y) ? 1 : 0
|
37
|
+
end
|
38
|
+
|
39
|
+
def greater_than_or_equal(x,y)
|
40
|
+
return (x >= y) ? 1 : 0
|
41
|
+
end
|
42
|
+
|
43
|
+
def less_than_or_equal(x,y)
|
44
|
+
return (x <= y) ? 1 : 0
|
45
|
+
end
|
46
|
+
|
47
|
+
def not_equal(x,y)
|
48
|
+
return (x != y) ? 1 : 0
|
49
|
+
end
|
50
|
+
|
51
|
+
def not(x)
|
52
|
+
if x.is_a?(InkList)
|
53
|
+
return (x.size == 0) ? 1 : 0
|
54
|
+
else
|
55
|
+
return (x == 0) ? 1 : 0
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
def and(x,y)
|
60
|
+
if x.is_a?(InkList)
|
61
|
+
return (x.size > 0 && y.size > 0) ? 1 : 0
|
62
|
+
else
|
63
|
+
return (x != 0 && y != 0) ? 1 : 0
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
def or(x,y)
|
68
|
+
if x.is_a?(InkList)
|
69
|
+
return (x.size > 0 || y.size > 0) ? 1 : 0
|
70
|
+
else
|
71
|
+
return (x != 0 || y != 0) ? 1 : 0
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
def max(x,y)
|
76
|
+
return [x,y].max
|
77
|
+
end
|
78
|
+
|
79
|
+
def min(x,y)
|
80
|
+
return [x,y].min
|
81
|
+
end
|
82
|
+
|
83
|
+
def pow(x,y)
|
84
|
+
return x ** y
|
85
|
+
end
|
86
|
+
|
87
|
+
def floor(x)
|
88
|
+
x.floor.to_f
|
89
|
+
end
|
90
|
+
|
91
|
+
def ceiling(x)
|
92
|
+
x.ceil.to_f
|
93
|
+
end
|
94
|
+
|
95
|
+
def int_value(x)
|
96
|
+
x.to_i
|
97
|
+
end
|
98
|
+
|
99
|
+
def float_value(x)
|
100
|
+
x.to_f
|
101
|
+
end
|
102
|
+
|
103
|
+
def has(x,y)
|
104
|
+
if x.is_a?(InkList)
|
105
|
+
return x.contains?(y) ? 1 : 0
|
106
|
+
else
|
107
|
+
return x.include?(y) ? 1 : 0
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
def has_not(x,y)
|
112
|
+
has_return = has(x,y)
|
113
|
+
# Need to invert values
|
114
|
+
if has_return == 0
|
115
|
+
return 1
|
116
|
+
else
|
117
|
+
return 0
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
def intersection(x,y)
|
122
|
+
return x & y
|
123
|
+
end
|
124
|
+
|
125
|
+
def invert(x)
|
126
|
+
x.inverse
|
127
|
+
end
|
128
|
+
|
129
|
+
def all(x)
|
130
|
+
x.all
|
131
|
+
end
|
132
|
+
|
133
|
+
def list_min(x)
|
134
|
+
x.min_as_list
|
135
|
+
end
|
136
|
+
|
137
|
+
def list_max(x)
|
138
|
+
x.max_as_list
|
139
|
+
end
|
140
|
+
|
141
|
+
def count(x)
|
142
|
+
x.count
|
143
|
+
end
|
144
|
+
|
145
|
+
def value_of_list(x)
|
146
|
+
x.max_item[1]
|
147
|
+
end
|
148
|
+
end
|
149
|
+
end
|