porolog 0.0.4 → 1.0.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.
Files changed (44) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +30 -5
  3. data/Rakefile +7 -2
  4. data/bin/porolog +58 -1
  5. data/coverage/badge.svg +1 -1
  6. data/coverage/index.html +76733 -2638
  7. data/doc/Array.html +1066 -0
  8. data/doc/Object.html +674 -0
  9. data/doc/Porolog.html +4153 -74
  10. data/doc/Symbol.html +501 -0
  11. data/doc/_index.html +280 -6
  12. data/doc/class_list.html +1 -1
  13. data/doc/file.README.html +34 -39
  14. data/doc/index.html +34 -39
  15. data/doc/method_list.html +1337 -57
  16. data/doc/top-level-namespace.html +4 -2
  17. data/lib/porolog.rb +1144 -4
  18. data/lib/porolog/arguments.rb +28 -24
  19. data/lib/porolog/core_ext.rb +188 -0
  20. data/lib/porolog/error.rb +9 -0
  21. data/lib/porolog/goal.rb +357 -0
  22. data/lib/porolog/instantiation.rb +346 -0
  23. data/lib/porolog/predicate.rb +74 -31
  24. data/lib/porolog/predicate/builtin.rb +825 -0
  25. data/lib/porolog/rule.rb +162 -0
  26. data/lib/porolog/scope.rb +4 -4
  27. data/lib/porolog/tail.rb +57 -0
  28. data/lib/porolog/value.rb +105 -0
  29. data/lib/porolog/variable.rb +325 -0
  30. data/test/porolog/arguments_test.rb +244 -195
  31. data/test/porolog/core_ext_test.rb +290 -0
  32. data/test/porolog/goal_test.rb +891 -0
  33. data/test/porolog/instantiation_test.rb +910 -0
  34. data/test/porolog/porolog_test.rb +2376 -13
  35. data/test/porolog/predicate/builtin_test.rb +1340 -0
  36. data/test/porolog/predicate_test.rb +84 -30
  37. data/test/porolog/rule_test.rb +527 -0
  38. data/test/porolog/scope_test.rb +0 -2
  39. data/test/porolog/tail_test.rb +127 -0
  40. data/test/porolog/value_test.rb +315 -0
  41. data/test/porolog/variable_test.rb +1614 -0
  42. data/test/samples_test.rb +277 -0
  43. data/test/test_helper.rb +115 -0
  44. metadata +34 -7
@@ -13,13 +13,38 @@ describe 'Porolog' do
13
13
  reset
14
14
  end
15
15
 
16
+ describe 'UNKNOWN_TAIL' do
17
+
18
+ describe '#inspect' do
19
+
20
+ it 'should return distinctive notation' do
21
+ assert_equal '...', UNKNOWN_TAIL.inspect
22
+ end
23
+
24
+ end
25
+
26
+ describe '#tail' do
27
+
28
+ it 'should return itself' do
29
+ assert_equal UNKNOWN_TAIL, UNKNOWN_TAIL.tail
30
+ assert_equal UNKNOWN_TAIL, UNKNOWN_TAIL.tail(1)
31
+ assert_equal UNKNOWN_TAIL, UNKNOWN_TAIL.tail(5)
32
+ end
33
+
34
+ end
35
+
36
+ end
37
+
16
38
  describe '#predicate' do
17
39
 
18
40
  it 'should create a Predicate' do
19
- assert_equal 0, Scope[:default].predicates.size
41
+ # -- Precondition Baseline --
42
+ assert_equal 0, Scope[:default].predicates.size
20
43
 
44
+ # -- Test --
21
45
  single_predicate = predicate :single
22
46
 
47
+ # -- Compare Result Against Baseline --
23
48
  assert_equal 1, Scope[:default].predicates.size
24
49
  assert_equal :single, Scope[:default].predicates.first.name
25
50
  assert_Predicate single_predicate, :single, []
@@ -31,23 +56,23 @@ describe 'Porolog' do
31
56
  predicate :delta
32
57
 
33
58
  assert respond_to?(:delta)
34
- assert_Arguments delta(1, :X, ['left','right']), :delta, [1, :X, ['left','right']]
59
+ assert_Arguments delta(1, :X, ['left','right']), :delta, [1, :X, ['left','right']]
35
60
  end
36
61
 
37
62
  it 'should create multiple Predicates' do
38
- assert_equal 0, Scope[:default].predicates.size
63
+ assert_equal 0, Scope[:default].predicates.size
39
64
 
40
65
  multiple_predicates = predicate :alpha, :beta, :gamma
41
66
 
42
- assert_equal 3, Scope[:default].predicates.size
43
- assert_equal :alpha, Scope[:default].predicates[0].name
44
- assert_equal :beta, Scope[:default].predicates[1].name
45
- assert_equal :gamma, Scope[:default].predicates[2].name
46
- assert_instance_of Array, multiple_predicates
47
- assert_equal 3, multiple_predicates.size
48
- assert_Predicate multiple_predicates[0], :alpha, []
49
- assert_Predicate multiple_predicates[1], :beta, []
50
- assert_Predicate multiple_predicates[2], :gamma, []
67
+ assert_equal 3, Scope[:default].predicates.size
68
+ assert_equal :alpha, Scope[:default].predicates[0].name
69
+ assert_equal :beta, Scope[:default].predicates[1].name
70
+ assert_equal :gamma, Scope[:default].predicates[2].name
71
+ assert_instance_of Array, multiple_predicates
72
+ assert_equal 3, multiple_predicates.size
73
+ assert_Predicate multiple_predicates[0], :alpha, []
74
+ assert_Predicate multiple_predicates[1], :beta, []
75
+ assert_Predicate multiple_predicates[2], :gamma, []
51
76
  end
52
77
 
53
78
  it 'should define multiple methods to create Arguments for solving' do
@@ -57,11 +82,2349 @@ describe 'Porolog' do
57
82
  predicate :epsilon, :upsilon
58
83
 
59
84
  assert respond_to?(:epsilon)
60
- assert_Arguments epsilon(), :epsilon, []
61
85
  assert respond_to?(:upsilon)
86
+ assert_Arguments epsilon(), :epsilon, []
62
87
  assert_Arguments upsilon([]), :upsilon, [[]]
63
88
  end
64
89
 
90
+ it 'should define a method in a base class as an instance method and a class method' do
91
+ class Base1 ; end
92
+
93
+ refute_includes Base1.methods, :localised_predicate
94
+ refute_includes Base1.instance_methods, :localised_predicate
95
+
96
+ class Base1
97
+
98
+ builtin :between, class_base: self
99
+ predicate :localised_predicate, class_base: self
100
+
101
+ localised_predicate(:X) << [
102
+ between(:X, 1, 5)
103
+ ]
104
+
105
+ def process
106
+ localised_predicate(:X).solve_for(:X)
107
+ end
108
+ end
109
+
110
+ assert_includes Base1.methods, :localised_predicate
111
+ assert_includes Base1.instance_methods, :localised_predicate
112
+
113
+ refute_includes methods, :localised_predicate
114
+
115
+ assert_solutions Base1.localised_predicate(:X), [
116
+ { X: 1 },
117
+ { X: 2 },
118
+ { X: 3 },
119
+ { X: 4 },
120
+ { X: 5 },
121
+ ]
122
+
123
+ assert_equal [1,2,3,4,5], Base1.new.process
124
+ end
125
+
126
+ end
127
+
128
+ describe '#unify_goals' do
129
+
130
+ it 'should unify goals for the same predicate' do
131
+ # -- Setup --
132
+ goal1 = new_goal :p, :m, :n
133
+ goal2 = new_goal :p, :s, :t
134
+
135
+ # -- Precondition Baseline --
136
+ assert_Goal_variables goal1, { m: nil, n: nil }, [
137
+ 'Goal1.:m',
138
+ 'Goal1.:n',
139
+ ].join("\n")
140
+
141
+ assert_Goal_variables goal2, { s: nil, t: nil }, [
142
+ 'Goal2.:s',
143
+ 'Goal2.:t',
144
+ ].join("\n")
145
+
146
+ # -- Test --
147
+ assert unify_goals(goal1, goal2), name
148
+
149
+ # -- Compare Result Against Baseline --
150
+ assert_Goal_variables goal1, { m: nil, n: nil }, [
151
+ 'Goal1.:m',
152
+ ' Goal2.:s',
153
+ 'Goal1.:n',
154
+ ' Goal2.:t',
155
+ ].join("\n")
156
+
157
+ assert_Goal_variables goal2, { s: nil, t: nil }, [
158
+ 'Goal2.:s',
159
+ ' Goal1.:m',
160
+ 'Goal2.:t',
161
+ ' Goal1.:n',
162
+ ].join("\n")
163
+ end
164
+
165
+ it 'should not unify goals for different predicates' do
166
+ goal1 = new_goal :p, :x, :y
167
+ goal2 = new_goal :q, :x, :y
168
+
169
+ refute unify_goals(goal1, goal2), name
170
+
171
+ assert_equal ['Cannot unify goals because they are for different predicates: :p and :q'], goal1.log
172
+ assert_equal ['Cannot unify goals because they are for different predicates: :p and :q'], goal2.log
173
+ end
174
+
175
+ end
176
+
177
+ describe '#instantiate_unifications' do
178
+
179
+ let(:goal1) { new_goal :p, :x, :y }
180
+ let(:goal2) { new_goal :q, :i, :j, :k }
181
+ let(:goal3) { new_goal :r, :n }
182
+
183
+ it 'should precheck inconsistent unifications' do
184
+ unifications = [
185
+ [:x, :k, goal1, goal2],
186
+ [:x, :j, goal1, goal2],
187
+ [:x, 12, goal1, goal3],
188
+ [:x, 37, goal1, goal3],
189
+ ]
190
+
191
+ refute instantiate_unifications(unifications), name
192
+ end
193
+
194
+ it 'should create instantiations' do
195
+ # -- Setup --
196
+ unifications = [
197
+ [:x, :k, goal1, goal2],
198
+ [:x, :j, goal1, goal2],
199
+ [:x, 12, goal1, goal3],
200
+ ]
201
+
202
+ # -- Precondition Baseline --
203
+ assert_Goal_variables goal1, { x: nil, y: nil }, [
204
+ 'Goal1.:x',
205
+ 'Goal1.:y',
206
+ ].join("\n")
207
+
208
+ assert_Goal_variables goal2, { i: nil, j: nil, k: nil }, [
209
+ 'Goal2.:i',
210
+ 'Goal2.:j',
211
+ 'Goal2.:k',
212
+ ].join("\n")
213
+
214
+ assert_Goal_variables goal3, { n: nil }, [
215
+ 'Goal3.:n',
216
+ ].join("\n")
217
+
218
+ # -- Test --
219
+ assert instantiate_unifications(unifications), name
220
+
221
+ # -- Compare Result Against Baseline --
222
+ assert_Goal_variables goal1, { x: 12, y: nil }, [
223
+ 'Goal1.:x',
224
+ ' Goal2.:k',
225
+ ' Goal2.:j',
226
+ ' Goal3.12',
227
+ 'Goal1.:y',
228
+ ].join("\n")
229
+
230
+ assert_Goal_variables goal2, { i: nil, j: 12, k: 12 }, [
231
+ 'Goal2.:i',
232
+ 'Goal2.:j',
233
+ ' Goal1.:x',
234
+ ' Goal2.:k',
235
+ ' Goal3.12',
236
+ 'Goal2.:k',
237
+ ' Goal1.:x',
238
+ ' Goal2.:j',
239
+ ' Goal3.12',
240
+ ].join("\n")
241
+
242
+ assert_Goal_variables goal3, { n: nil }, [
243
+ 'Goal3.:n',
244
+ ].join("\n")
245
+ end
246
+
247
+ it 'should return false for inconsistent unifications not picked up by the precheck' do
248
+ # 12
249
+ # /
250
+ # i
251
+ # \
252
+ # x
253
+ # /
254
+ # j
255
+ # \
256
+ # y
257
+ # /
258
+ # k
259
+ # \
260
+ # 37
261
+ unifications = [
262
+ [:x, :i, goal1, goal2],
263
+ [:i, 12, goal2, goal3],
264
+ [:j, :x, goal2, goal1],
265
+ [:j, :y, goal2, goal1],
266
+ [:k, :y, goal2, goal1],
267
+ [:k, 37, goal2, goal3],
268
+ ]
269
+
270
+ refute instantiate_unifications(unifications), name
271
+
272
+ assert_Goal_variables goal1, { x: nil, y: nil }, [
273
+ 'Goal1.:x',
274
+ 'Goal1.:y',
275
+ ].join("\n")
276
+
277
+ assert_Goal_variables goal2, { i: nil, j: nil, k: nil }, [
278
+ 'Goal2.:i',
279
+ 'Goal2.:j',
280
+ 'Goal2.:k',
281
+ ].join("\n")
282
+
283
+ assert_Goal_variables goal3, { n: nil }, [
284
+ 'Goal3.:n',
285
+ ].join("\n")
286
+ end
287
+
288
+ end
289
+
290
+ describe '#unify' do
291
+
292
+ let(:goal) { new_goal :p, :x, :y }
293
+ let(:goal2) { new_goal :q, :x, :y }
294
+
295
+ describe 'when [:atomic,:atomic]' do
296
+
297
+ it 'should unify equal atomic numbers' do
298
+ assert unify(42, 42, goal), name
299
+ end
300
+
301
+ it 'should not unify unequal atomic numbers' do
302
+ refute unify(42, 99, goal), name
303
+
304
+ expected_log = [
305
+ 'Cannot unify because 42 != 99 (atomic != atomic)',
306
+ ]
307
+
308
+ assert_equal expected_log, goal.log
309
+ end
310
+
311
+ end
312
+
313
+ describe 'when [:array,:array]' do
314
+
315
+ before do
316
+ @spy = Spy.on(self, :unify_arrays).and_call_through
317
+ end
318
+
319
+ after do
320
+ assert_equal 1, @spy.calls.size, "expected unify_arrays to be called once for #{name}"
321
+ end
322
+
323
+ it 'should unify empty arrays' do
324
+ assert unify([], [], goal), name
325
+ end
326
+
327
+ it 'should unify equal arrays' do
328
+ assert unify([7,11,13], [7,11,13], goal), name
329
+ end
330
+
331
+ it 'should not unify unequal arrays' do
332
+ refute unify([7,11,13], [7,11,14], goal), name
333
+
334
+ expected_log = [
335
+ 'Cannot unify incompatible values: 13 with 14',
336
+ 'Cannot unify because [7, 11, 13] != [7, 11, 14] (array != array)',
337
+ ]
338
+
339
+ assert_equal expected_log, goal.log
340
+ end
341
+
342
+ it 'should not unify arrays of different lengths' do
343
+ refute unify([7,11,13], [7,11,13,13], goal), name
344
+
345
+ expected_log = [
346
+ 'Cannot unify arrays of different lengths: [7, 11, 13] with [7, 11, 13, 13]',
347
+ 'Cannot unify because [7, 11, 13] != [7, 11, 13, 13] (array != array)',
348
+ ]
349
+
350
+ assert_equal expected_log, goal.log
351
+ end
352
+
353
+ end
354
+
355
+ describe 'when [:variable,:atomic]' do
356
+
357
+ before do
358
+ expects(:unify_arrays).times(0)
359
+ end
360
+
361
+ it 'should return an instantiation' do
362
+ assert_equal [[:word, 'word', goal, goal]], unify(:word, 'word', goal), name
363
+ end
364
+
365
+ it 'should not unify an instantiated variable with a different value' do
366
+ goal.instantiate :word, 'other'
367
+
368
+ assert_nil unify(:word, 'word', goal), name
369
+
370
+ expected_log = [
371
+ 'Cannot unify because Goal1."other" != "word" (variable != atomic)',
372
+ ]
373
+
374
+ assert_equal expected_log, goal.log
375
+ end
376
+
377
+ end
378
+
379
+ describe 'when [:atomic,:variable]' do
380
+
381
+ before do
382
+ expects(:unify_arrays).times(0)
383
+ end
384
+
385
+ it 'should return an instantiation' do
386
+ assert_equal [[:word, 'word', goal, goal]], unify('word', :word, goal), name
387
+ end
388
+
389
+ it 'should not unify instantiated variables with different values' do
390
+ goal.instantiate :word, 'something'
391
+
392
+ assert_nil unify('word', :word, goal), name
393
+
394
+ expected_log = [
395
+ 'Cannot unify because "word" != Goal1."something" (atomic != variable)',
396
+ ]
397
+
398
+ assert_equal expected_log, goal.log
399
+ end
400
+
401
+ end
402
+
403
+ describe 'when [:variable, :variable]' do
404
+
405
+ before do
406
+ expects(:unify_arrays).times(0)
407
+ end
408
+
409
+ it 'should return an instantiation' do
410
+ assert_equal [[:x, :y, goal, goal2]], unify(:x, :y, goal, goal2), name
411
+ end
412
+
413
+ it 'should not unify instantiated variables with different values' do
414
+ goal.instantiate :word, 'something'
415
+ goal.instantiate :draw, 'picturing'
416
+
417
+ assert_nil unify(:draw, :word, goal), name
418
+
419
+ expected_log = [
420
+ 'Cannot unify because "picturing" != "something" (variable != variable)'
421
+ ]
422
+
423
+ assert_equal expected_log, goal.log
424
+ end
425
+
426
+ it 'should unify the unknown array with a variable' do
427
+ goal.instantiate :X, UNKNOWN_ARRAY
428
+ goal.instantiate :Y, [1,2,3,4]
429
+
430
+ unifications = unify(goal[:X], goal[:Y], goal)
431
+
432
+ expected_unifications = [
433
+ [goal[:X], goal[:Y], goal, goal]
434
+ ]
435
+
436
+ assert_equal expected_unifications, unifications
437
+
438
+ expected_log = []
439
+
440
+ assert_equal expected_log, goal.log
441
+ end
442
+
443
+ it 'should unify the unknown array with an array' do
444
+ goal.instantiate :X, [1,2,3,4]
445
+ goal.instantiate :Y, UNKNOWN_ARRAY
446
+
447
+ unifications = unify(goal[:X], goal[:Y], goal)
448
+
449
+ expected_unifications = [
450
+ [goal[:X], goal[:Y], goal, goal]
451
+ ]
452
+
453
+ assert_equal expected_unifications, unifications
454
+
455
+ expected_log = []
456
+
457
+ assert_equal expected_log, goal.log
458
+ end
459
+
460
+ end
461
+
462
+ describe 'when [:variable, :array]' do
463
+
464
+ it 'should return an instantiation' do
465
+ expects(:unify_arrays).times(0)
466
+
467
+ assert_equal [[:list, [7,11,13], goal, goal]], unify(:list, [7,11,13], goal), name
468
+ end
469
+
470
+ it 'should not unify instantiated variables with different values' do
471
+ goal.instantiate :word, [1,2,3,4]
472
+ expects(:unify_arrays).times(1)
473
+
474
+ assert_nil unify(:word, [1,2,3,5], goal), name
475
+
476
+ expected_log = [
477
+ 'Cannot unify because Goal1.[1, 2, 3, 4] != [1, 2, 3, 5] (variable/array != array)'
478
+ ]
479
+
480
+ assert_equal expected_log, goal.log
481
+ end
482
+
483
+ it 'should not unify instantiated variables with a non-array value' do
484
+ goal.instantiate :word, 1234
485
+ expects(:unify_arrays).times(0)
486
+
487
+ assert_nil unify(:word, [1,2,3,5], goal), name
488
+
489
+ expected_log = [
490
+ 'Cannot unify because Goal1.1234 != [1, 2, 3, 5] (variable != array)'
491
+ ]
492
+
493
+ assert_equal expected_log, goal.log
494
+ end
495
+
496
+ end
497
+
498
+ describe 'when [:array, :variable]' do
499
+
500
+ it 'should not unify instantiated variables that are not instantiated with an array' do
501
+ goal.instantiate :word, '1 2 3 4'
502
+
503
+ assert_nil unify([1,2,3,4], :word, goal), name
504
+
505
+ expected_log = [
506
+ 'Cannot unify because [1, 2, 3, 4] != Goal1."1 2 3 4" (array != variable)'
507
+ ]
508
+
509
+ assert_equal expected_log, goal.log
510
+ end
511
+
512
+ it 'should return an instantiation' do
513
+ expects(:unify_arrays).times(0)
514
+
515
+ assert_equal [[:list, [7,11,13], goal, goal]], unify([7,11,13], :list, goal), name
516
+ end
517
+
518
+ it 'should not unify instantiated variables with different values' do
519
+ goal.instantiate :word, [1,2,3,4]
520
+ expects(:unify_arrays).times(1)
521
+
522
+ assert_nil unify([1,2,3,5], :word, goal), name
523
+
524
+ expected_log = [
525
+ 'Cannot unify because [1, 2, 3, 5] != Goal1.[1, 2, 3, 4] (variable/array != array)'
526
+ ]
527
+
528
+ assert_equal expected_log, goal.log
529
+ end
530
+
531
+ it 'should unify instantiated variables with unifiable values' do
532
+ goal.instantiate :word, [1,2,3]/:w
533
+
534
+ instantiations = unify([nil,nil,3,4,5], :word, goal)
535
+
536
+ expected_log = []
537
+ expected_instantiations = [
538
+ [:w, [4,5], goal, goal],
539
+ ]
540
+
541
+ assert_equal expected_log, goal.log
542
+ assert_equal expected_instantiations, instantiations
543
+ end
544
+
545
+ end
546
+
547
+ describe 'when [:array,:atomic], [:atomic,:array]' do
548
+
549
+ before do
550
+ expects(:unify_arrays).times(0)
551
+ end
552
+
553
+ it 'should return nil for unifying array and atomic' do
554
+ assert_nil unify([7,11,13], 'word', goal), name
555
+
556
+ expected_log = [
557
+ 'Cannot unify [7, 11, 13] with "word" (array != atomic)',
558
+ ]
559
+
560
+ assert_equal expected_log, goal.log
561
+ end
562
+
563
+ it 'should return nil for unifying atomic and array' do
564
+ assert_nil unify('word', [7,11,13], goal), name
565
+
566
+ expected_log = [
567
+ 'Cannot unify "word" with [7, 11, 13] (atomic != array)',
568
+ ]
569
+
570
+ assert_equal expected_log, goal.log
571
+ end
572
+
573
+ end
574
+
575
+ describe 'when [:array, :tail]' do
576
+
577
+ let(:goal) { new_goal :p, :x, :y }
578
+
579
+ it 'should unify an array and a tail' do
580
+ assert unify([7,11,13], Tail.new([[7,11,goal[:P]]]), goal), name
581
+
582
+ expected_log = []
583
+
584
+ assert_equal expected_log, goal.log
585
+ end
586
+
587
+ it 'should not unify an array and tail when they cannot be unified' do
588
+ refute unify([7,11,13], Tail.new([[7,11,14]]), goal), name
589
+
590
+ expected_log = [
591
+ 'Cannot unify incompatible values: 13 with 14',
592
+ 'Cannot unify: [7, 11, 13] with [7, 11, 14]',
593
+ 'Cannot unify because [7, 11, 13] != *[[7, 11, 14]] (array != tail)'
594
+ ]
595
+
596
+ assert_equal expected_log, goal.log
597
+ end
598
+
599
+ end
600
+
601
+ describe 'when [:tail, :array]' do
602
+
603
+ let(:goal) { new_goal :p, :x, :y }
604
+
605
+ it 'should unify a tail and an array' do
606
+ assert unify(Tail.new([[7,11,13]]), [7,11,goal[:P]], goal), name
607
+
608
+ expected_log = []
609
+
610
+ assert_equal expected_log, goal.log
611
+ end
612
+
613
+ it 'should not unify a tail and an array when they cannot be unified' do
614
+ refute unify(Tail.new([[7,11,13]]), [7,11,14], goal), name
615
+
616
+ expected_log = [
617
+ 'Cannot unify incompatible values: 13 with 14',
618
+ 'Cannot unify: [7, 11, 13] with [7, 11, 14]',
619
+ 'Cannot unify because *[[7, 11, 13]] != [7, 11, 14] (tail != array)'
620
+ ]
621
+
622
+ assert_equal expected_log, goal.log
623
+ end
624
+
625
+ end
626
+
627
+ describe 'when [:tail, :tail]' do
628
+
629
+ it 'something' do
630
+ expected_unifications = [
631
+ [goal[:B], [goal[:H]]/:T, goal, goal],
632
+ [goal[:C], [goal[:H]]/:NT, goal, goal]
633
+ ]
634
+ assert_equal expected_unifications, unify(
635
+ [:A, :B, :C],
636
+ [:A, [:H]/:T, [:H]/:NT],
637
+ goal
638
+ )
639
+ end
640
+
641
+ end
642
+
643
+ end
644
+
645
+ describe '#unify_arrays' do
646
+
647
+ let(:g1) { new_goal(:p, :x, :y) }
648
+ let(:g2) { new_goal(:q, :a, :b) }
649
+ let(:goals) { [g1, g2] }
650
+
651
+ it 'should not unify arrays when left is not an Array' do
652
+ expect_unify_arrays_with_calls 0, 0, 0
653
+
654
+ refute_Unify_arrays [{}, []], goals, [
655
+ 'Cannot unify a non-array with an array: {} with []',
656
+ ]
657
+ end
658
+
659
+ it 'should not unify arrays when right is not an Array' do
660
+ expect_unify_arrays_with_calls 0, 0, 0
661
+
662
+ refute_Unify_arrays [[], 4], goals, [
663
+ 'Cannot unify a non-array with an array: [] with 4',
664
+ ]
665
+ end
666
+
667
+ it 'should unify but without unifications when left is the unknown array' do
668
+ expect_unify_arrays_with_calls 0, 0, 0
669
+
670
+ assert_Unify_arrays [UNKNOWN_ARRAY, [:a,:b,:c]], goals, [g2[:a], g2[:b], g2[:c]]
671
+ end
672
+
673
+ it 'should unify but without unifications when right is the unknown array' do
674
+ expect_unify_arrays_with_calls 0, 0, 0
675
+
676
+ assert_Unify_arrays [[:x,:y,:z], UNKNOWN_ARRAY], goals, [g1[:x], g1[:y], g1[:z]]
677
+ end
678
+
679
+ it 'should unify but without unifications when left and right are the unknown array' do
680
+ expect_unify_arrays_with_calls 0, 0, 0
681
+
682
+ assert_Unify_arrays [[UNKNOWN_TAIL], UNKNOWN_ARRAY], goals, UNKNOWN_ARRAY
683
+ end
684
+
685
+ arrays_without_tails = [
686
+ [],
687
+ [1, 2, 3],
688
+ [1, 2, 3, 4],
689
+ [:x, :y, :z],
690
+ [['one'], ['word'], ['sentences']],
691
+ ]
692
+
693
+ arrays_with_tails = [
694
+ []/:t,
695
+ [1]/:z,
696
+ [1, 2]/:s,
697
+ [1, UNKNOWN_TAIL],
698
+ [1, 2, 3, UNKNOWN_TAIL],
699
+ [1, 2, 3, 4, UNKNOWN_TAIL],
700
+ [:x, :y, :z, UNKNOWN_TAIL],
701
+ [['one'], ['word'], ['sentences']]/:tail,
702
+ ]
703
+
704
+ arrays_without_tails.combination(2).each do |arrays|
705
+ it "should call unify_arrays_with_no_tails when there are no tails: #{arrays.map(&:inspect).join(' and ')}" do
706
+ expect_unify_arrays_with_calls 1, 0, 0
707
+
708
+ unify_arrays(*arrays, *goals)
709
+ end
710
+ end
711
+
712
+ arrays_with_tails.combination(2).each do |arrays|
713
+ it "should call unify_arrays_with_all_tails when all arrays have tails: #{arrays.map(&:inspect).join(' and ')}" do
714
+ expect_unify_arrays_with_calls 0, 0, 1
715
+
716
+ unify_arrays(*arrays, *goals)
717
+ end
718
+ end
719
+
720
+ (
721
+ arrays_without_tails.product(arrays_with_tails) +
722
+ arrays_with_tails.product(arrays_without_tails)
723
+ ).each do |arrays|
724
+ it "should call unify_arrays_with_some_tails when one array has a tail and the other does not: #{arrays.map(&:inspect).join(' and ')}" do
725
+ expect_unify_arrays_with_calls 0, 1, 0
726
+
727
+ unify_arrays(*arrays, *goals)
728
+ end
729
+ end
730
+
731
+ it 'should unify identical arrays without variables' do
732
+ assert_Unify_arrays [[1,2,3], [1,2,3]], goals, [1,2,3]
733
+ end
734
+
735
+ it 'should unify an array of variables with an array of atomics' do
736
+ assert_Unify_arrays [[:a,:b,:c], [1,2,3]], goals, [1,2,3], [
737
+ [:a, 1, g1, g2],
738
+ [:b, 2, g1, g2],
739
+ [:c, 3, g1, g2],
740
+ ]
741
+ end
742
+
743
+ it 'should unify an array of atomics with an array of variables' do
744
+ assert_Unify_arrays [[1,2,3], [:a,:b,:c]], goals, [1,2,3], [
745
+ [:a, 1, g2, g1],
746
+ [:b, 2, g2, g1],
747
+ [:c, 3, g2, g1],
748
+ ]
749
+ end
750
+
751
+ it 'should unify arrays of variables' do
752
+ assert_Unify_arrays [[:x,:y,:z], [:a,:b,:c]], goals, [nil,nil,nil], [
753
+ [:x, :a, g1, g2],
754
+ [:y, :b, g1, g2],
755
+ [:z, :c, g1, g2],
756
+ ]
757
+ end
758
+
759
+ it 'should unify a fixed array with an array with a tail' do
760
+ assert_Unify_arrays [[:a, 2, 3], [1]/:t], goals, [1,2,3], [
761
+ [:a, 1, g1, g2],
762
+ [:t, [2,3], g2, g1],
763
+ ]
764
+ end
765
+
766
+ it 'should unify a fixed array with the unknown array' do
767
+ assert_Unify_arrays [[1,2,3], UNKNOWN_ARRAY], goals, [1,2,3]
768
+ end
769
+
770
+ it 'should unify the unknown array with the unknown array' do
771
+ assert_Unify_arrays [UNKNOWN_ARRAY, UNKNOWN_ARRAY], goals, UNKNOWN_ARRAY
772
+ end
773
+
774
+ it 'should unify arrays with variable tails' do
775
+ assert_Unify_arrays [[:a, :b]/:c, [:x, :y]/:z], goals, [nil,nil,UNKNOWN_TAIL], [
776
+ [:a, :x, g1, g2],
777
+ [:b, :y, g1, g2],
778
+ [:c, :z, g1, g2],
779
+ ]
780
+ end
781
+
782
+ it 'should unify arrays with variable tails of different lenths' do
783
+ assert_Unify_arrays [[:a, :b]/:c, [:x]/:z], goals, [nil,nil,UNKNOWN_TAIL], [
784
+ [g2[:x], g1[:a], g2, g1],
785
+ [g2[:z], [g1[:b]]/g1[:c], g2, g1],
786
+ ]
787
+ end
788
+
789
+ it 'should unify arrays with overlapping variables' do
790
+ assert_Unify_arrays [[:a,:a,:c,:c,:e], [1,:b,:b,:d,:d]], goals, [1, nil, nil, nil, nil], [
791
+ [g1[:a], g2[1], g1, g2],
792
+ [g1[:a], g2[:b], g1, g2],
793
+ [g1[:c], g2[:b], g1, g2],
794
+ [g1[:c], g2[:d], g1, g2],
795
+ [g1[:e], g2[:d], g1, g2],
796
+ ]
797
+ end
798
+
799
+ it 'should unify complementary arrays' do
800
+ assert_Unify_arrays [[1,2]/:t, [:a,:b,3,4,5]], goals, [1,2,3,4,5], [
801
+ [g2[:a], g1[1], g2, g1],
802
+ [g2[:b], g1[2], g2, g1],
803
+ [g1[:t], [3, 4, 5], g1, g2],
804
+ ]
805
+ end
806
+
807
+ end
808
+
809
+ describe '#unify_many_arrays' do
810
+
811
+ let(:goal) { new_goal(:p, :x, :y) }
812
+
813
+ it 'should return nil when not all arrays are Arrays or variables' do
814
+ arrays = [
815
+ [1,2,3],
816
+ 123,
817
+ :x,
818
+ ]
819
+
820
+ assert_nil unify_many_arrays(arrays, [goal] * arrays.size), name
821
+
822
+ expected_log = [
823
+ 'Cannot unify: [1, 2, 3] with 123 with :x',
824
+ ]
825
+
826
+ assert_equal expected_log, goal.log
827
+ end
828
+
829
+ it 'should not return nil when all arrays are Arrays or variables' do
830
+ arrays = [
831
+ [1,2,3],
832
+ UNKNOWN_ARRAY,
833
+ :x,
834
+ ]
835
+
836
+ result = unify_many_arrays(arrays, [goal] * arrays.size)
837
+ refute_nil result, name
838
+
839
+ merged, unifications = result
840
+
841
+ expected_merged = [1,2,3]
842
+ expected_unifications = [
843
+ ]
844
+
845
+ assert_equal expected_merged, merged
846
+ assert_equal expected_unifications, unifications
847
+ end
848
+
849
+ it 'should automatically merge unknown arrays' do
850
+ arrays = [
851
+ [1,2,3],
852
+ UNKNOWN_ARRAY,
853
+ ]
854
+
855
+ assert_equal [[1,2,3], []], unify_many_arrays(arrays, [goal] * arrays.size), name
856
+ end
857
+
858
+ it 'should call unify_arrays_with_no_tails when there are no Arrays with Tails' do
859
+ expect_unify_arrays_with_calls 1, 0, 0
860
+
861
+ arrays = [
862
+ [1, 2, 3, nil],
863
+ [1, nil, 3, 4],
864
+ [nil, 2, 3, nil],
865
+ ]
866
+
867
+ unify_many_arrays(arrays, [goal] * arrays.size)
868
+ end
869
+
870
+ it 'should call unify_arrays_with_all_tails when all Arrays have Tails' do
871
+ expect_unify_arrays_with_calls 0, 0, 1
872
+
873
+ arrays = [
874
+ [1, 2, 3, UNKNOWN_TAIL],
875
+ [1, nil, 3, ]/:x,
876
+ ]
877
+
878
+ unify_many_arrays(arrays, [goal] * arrays.size)
879
+ end
880
+
881
+ it 'should call unify_arrays_with_some_tails when there is a combination of Arrays with and without Tails' do
882
+ expect_unify_arrays_with_calls 0, 1, 0
883
+
884
+ arrays = [
885
+ [1, 2, 3, UNKNOWN_TAIL],
886
+ [1, nil, 3, 4],
887
+ [nil, 2, 3, nil],
888
+ ]
889
+
890
+ unify_many_arrays(arrays, [goal] * arrays.size)
891
+ end
892
+
893
+ end
894
+
895
+ describe '#has_tail?' do
896
+
897
+ # -- Non-Arrays --
898
+ let(:goal) { new_goal(:p, :x, :y) }
899
+ let(:variable) { goal.variable(:x) }
900
+ let(:object) { Object.new }
901
+ let(:symbol) { :symbol }
902
+ let(:integer) { 789 }
903
+
904
+ # -- Arrays --
905
+ let(:empty_array) { [] }
906
+ let(:finite_array) { [1,2,3,4,5] }
907
+ let(:array_with_unknown_tail) { [1,2,3,4,5,UNKNOWN_TAIL] }
908
+ let(:array_with_variable_tail) { [1,2,3,4,5]/:tail }
909
+ let(:unknown_array) { UNKNOWN_ARRAY }
910
+
911
+ describe 'when not given an Array' do
912
+
913
+ it 'should return false when given an uninstantiated variable' do
914
+ refute has_tail?(variable), name
915
+ end
916
+
917
+ it 'should return false when given an Object' do
918
+ refute has_tail?(object), name
919
+ end
920
+
921
+ it 'should return false when given a Symbol' do
922
+ refute has_tail?(symbol), name
923
+ end
924
+
925
+ it 'should return false when given an Integer' do
926
+ refute has_tail?(integer), name
927
+ end
928
+
929
+ end
930
+
931
+ describe 'when given an Array' do
932
+
933
+ it 'should return false when the Array is empty' do
934
+ refute has_tail?(empty_array), name
935
+ end
936
+
937
+ it 'should return false when the last element is atomic' do
938
+ refute has_tail?(finite_array), name
939
+ end
940
+
941
+ it 'should return true when the last element is unknown' do
942
+ assert has_tail?(array_with_unknown_tail), name
943
+ end
944
+
945
+ it 'should return true when the last element is a Tail' do
946
+ assert has_tail?(array_with_variable_tail), name
947
+ end
948
+
949
+ it 'should return true when given an unknown array' do
950
+ assert has_tail?(unknown_array), name
951
+ end
952
+
953
+ describe 'and the array has an instantiated tail' do
954
+
955
+ before do
956
+ goal.instantiate :a, 1
957
+ goal.instantiate :b, 2
958
+ goal.instantiate :c, 3
959
+ goal.instantiate :d, 4
960
+ goal.instantiate :e, [5]
961
+ end
962
+
963
+ describe 'and to apply value to the array' do
964
+
965
+ it 'should return false' do
966
+ array = goal.variablise([:a, :b, :c, :d]/:e)
967
+
968
+ refute has_tail?(array, true), name
969
+ end
970
+
971
+ end
972
+
973
+ describe 'and to not apply value to the array' do
974
+
975
+ it 'should return true' do
976
+ array = goal.variablise([:a, :b, :c, :d]/:e)
977
+
978
+ assert has_tail?(array, false), name
979
+ end
980
+
981
+ end
982
+
983
+ end
984
+
985
+ end
986
+
987
+ end
988
+
989
+ describe '#expand_splat' do
990
+
991
+ it 'should return an empty array as is' do
992
+ assert_equal [], expand_splat([])
993
+ end
994
+
995
+ it 'should return non-arrays as is' do
996
+ assert_equal 5, expand_splat(5)
997
+ end
998
+
999
+ it 'should expand an array of just a Tail' do
1000
+ assert_equal :t, expand_splat([]/:t)
1001
+ end
1002
+
1003
+ it 'should not expand an array with a Tail' do
1004
+ assert_equal [1,2,3]/:t, expand_splat([1,2,3]/:t)
1005
+ end
1006
+
1007
+ it 'should expand a tail' do
1008
+ assert_equal [1,2,3,4,5], expand_splat([1,2,3,Tail.new([4,5])])
1009
+ end
1010
+
1011
+ it 'should expand nested arrays' do
1012
+ assert_equal [99, [1,2,3,4], 99], expand_splat([99,[1,2,3,Tail.new([4])],99])
1013
+ end
1014
+
1015
+ end
1016
+
1017
+ describe '#unify_arrays_with_no_tails' do
1018
+
1019
+ let(:g1) { new_goal(:p, :x, :y) }
1020
+ let(:g2) { new_goal(:q, :a, :b) }
1021
+ let(:g3) { new_goal(:r, :s, :t) }
1022
+ let(:g4) { new_goal(:v, :i, :j) }
1023
+ let(:goals) { [g1, g2, g3, g4] }
1024
+
1025
+ it 'should return nil when any array has a tail' do
1026
+ arrays = [
1027
+ [1, 2, 3, 4],
1028
+ [1, 2, 3]/:tail,
1029
+ [:w, :x, :y, :z]
1030
+ ]
1031
+ arrays_goals = goals[0..arrays.size]
1032
+
1033
+ assert_nil unify_arrays_with_no_tails(arrays, arrays_goals, []), name
1034
+
1035
+ expected_log = [
1036
+ 'Wrong unification method called: no_tails but one or more of [[1, 2, 3, 4], [1, 2, 3, *:tail], [:w, :x, :y, :z]] has a tail',
1037
+ ]
1038
+
1039
+ arrays_goals.each do |goal|
1040
+ assert_equal expected_log, goal.log
1041
+ end
1042
+ end
1043
+
1044
+ it 'should not unify finite arrays with unequal sizes' do
1045
+ # -- First > Last --
1046
+ arrays = [
1047
+ [nil, nil, nil, nil],
1048
+ [1, 2, 3]
1049
+ ]
1050
+ arrays_goals = goals[0...arrays.size]
1051
+
1052
+ assert_nil unify_arrays_with_no_tails(arrays, arrays_goals, []), name
1053
+
1054
+ # -- First = Last --
1055
+ arrays = [
1056
+ [nil, nil, nil],
1057
+ [1, 2, 3]
1058
+ ]
1059
+ arrays_goals = goals[0...arrays.size]
1060
+
1061
+ expected_merged = [1,2,3]
1062
+ expected_unifications = []
1063
+
1064
+ merged, unifications = unify_arrays_with_no_tails(arrays, arrays_goals, [])
1065
+
1066
+ assert_equal expected_merged, merged
1067
+ assert_equal expected_unifications, unifications
1068
+
1069
+ # -- First < Last --
1070
+ arrays = [
1071
+ [nil, nil],
1072
+ [1, 2, 3]
1073
+ ]
1074
+ arrays_goals = goals[0...arrays.size]
1075
+
1076
+ assert_nil unify_arrays_with_no_tails(arrays, arrays_goals, []), name
1077
+
1078
+ # -- Check Log --
1079
+ expected_log = [
1080
+ 'Cannot unify arrays of different lengths: [nil, nil, nil, nil] with [1, 2, 3]',
1081
+ 'Cannot unify arrays of different lengths: [nil, nil] with [1, 2, 3]',
1082
+ ]
1083
+
1084
+ arrays_goals.each do |goal|
1085
+ assert_equal expected_log, goal.log
1086
+ end
1087
+ end
1088
+
1089
+ it 'should merge values with nil as an empty slot' do
1090
+ arrays = [
1091
+ [1, 2, 3, nil, nil],
1092
+ [nil, nil, nil, 4, nil],
1093
+ [nil, nil, nil, nil, 5],
1094
+ ]
1095
+ arrays_goals = goals[0...arrays.size]
1096
+
1097
+ merged, unifications = unify_arrays_with_no_tails(arrays, arrays_goals, [])
1098
+
1099
+ expected_merged = [1, 2, 3, 4, 5]
1100
+ expected_unifications = []
1101
+
1102
+ assert_equal expected_merged, merged
1103
+ assert_equal expected_unifications, unifications
1104
+ end
1105
+
1106
+ it 'should return nil when the finite arrays have different sizes' do
1107
+ arrays = [
1108
+ [nil],
1109
+ [nil, nil, nil, nil],
1110
+ [nil, nil, nil, nil, nil, nil],
1111
+ ]
1112
+ arrays_goals = goals[0...arrays.size]
1113
+
1114
+ assert_nil unify_arrays_with_no_tails(arrays, arrays_goals, []), name
1115
+
1116
+ expected_log = [
1117
+ 'Cannot unify arrays of different lengths: [nil] with [nil, nil, nil, nil] with [nil, nil, nil, nil, nil, nil]',
1118
+ ]
1119
+
1120
+ arrays_goals.each do |goal|
1121
+ assert_equal expected_log, goal.log
1122
+ end
1123
+ end
1124
+
1125
+ it 'should return nil when the arrays have incompatible values' do
1126
+ arrays = [
1127
+ [1, 2, 3, nil, nil],
1128
+ [nil, nil, nil, 4, nil],
1129
+ [8, nil, nil, nil, 5],
1130
+ ]
1131
+ arrays_goals = goals[0...arrays.size]
1132
+
1133
+ assert_nil unify_arrays_with_no_tails(arrays, arrays_goals, []), name
1134
+
1135
+ expected_log = [
1136
+ 'Cannot unify incompatible values: 1 with 8',
1137
+ ]
1138
+
1139
+ arrays_goals.each do |goal|
1140
+ assert_equal expected_log, goal.log
1141
+ end
1142
+ end
1143
+
1144
+ it 'should return necessary unifications to unify variables' do
1145
+ arrays = [
1146
+ [1, 2, nil, nil, nil],
1147
+ [:j, :n, :n, 4, nil],
1148
+ [:x, nil, :m, :y, :z],
1149
+ ]
1150
+ arrays_goals = goals[0...arrays.size]
1151
+
1152
+ merged, unifications = unify_arrays_with_no_tails(arrays, arrays_goals, [])
1153
+
1154
+ expected_merged = [1, 2, nil, 4, nil]
1155
+ expected_unifications = [
1156
+ [g2[:j], g1[1], g2, g1],
1157
+ [g3[:x], g1[1], g3, g1],
1158
+ [g2[:j], g3[:x], g2, g3],
1159
+ [g2[:n], g1[2], g2, g1],
1160
+ [g2[:n], g3[:m], g2, g3],
1161
+ [g3[:y], g2[4], g3, g2],
1162
+ ]
1163
+
1164
+ assert_equal expected_merged, merged
1165
+ assert_equal expected_unifications, unifications
1166
+ end
1167
+
1168
+ it 'should deduce array goals from array elements when missing' do
1169
+ arrays = [
1170
+ [ nil, 2, nil, g1.value(4), nil],
1171
+ g2.value([1, nil, 3, nil, nil]),
1172
+ g3.value([nil, :x, nil, nil, 5]),
1173
+ ]
1174
+ missing_goals = [nil, nil, nil]
1175
+
1176
+ merged, unifications = unify_arrays_with_no_tails(arrays, missing_goals, [])
1177
+
1178
+ expected_merged = [1, 2, 3, 4, 5]
1179
+ expected_unifications = [
1180
+ [g3.variable(:x), g1.value(2), g3, g1]
1181
+ ]
1182
+
1183
+ assert_equal expected_merged, merged
1184
+ assert_equal expected_unifications, unifications
1185
+ end
1186
+
1187
+ it 'should map Values as is' do
1188
+ value1 = g1.value(91)
1189
+ g1.instantiate(:gi, [nil, value1, nil, 93])
1190
+
1191
+ arrays = [
1192
+ [ nil, 2, nil, :gi, nil],
1193
+ g2.value([1, nil, 3, [90, nil, nil, nil], nil]),
1194
+ g3.value([nil, :x, nil, [nil, nil, 92, :gy], 5]),
1195
+ ]
1196
+
1197
+ arrays_goals = [g1, nil, nil]
1198
+
1199
+ merged, unifications = unify_arrays_with_no_tails(arrays, arrays_goals, [])
1200
+
1201
+ expected_merged = [1, 2, 3, [90, 91, 92, 93], 5]
1202
+ expected_unifications = [
1203
+ [g3.variable(:x), g1.value(2), g3, g1],
1204
+ [g3.variable(:gy), g1.value(93), g3, g1],
1205
+ ]
1206
+
1207
+ assert_equal expected_merged, merged
1208
+ assert_equal expected_unifications, unifications
1209
+ end
1210
+
1211
+ it 'should detect ununifiable values embedded in subarrays' do
1212
+ value1 = g1.value(91)
1213
+ g1.instantiate(:gi, [nil, value1, 92.1, 93])
1214
+
1215
+ arrays = [
1216
+ [ nil, 2, nil, :gi, nil],
1217
+ g2.value([1, nil, 3, [90, nil, nil, nil], nil]),
1218
+ g3.value([nil, :x, nil, [nil, nil, 92, :gy], 5]),
1219
+ ]
1220
+
1221
+ arrays_goals = [g1, nil, nil]
1222
+
1223
+ assert_nil unify_arrays_with_no_tails(arrays, arrays_goals, []), name
1224
+
1225
+ expected_log = [
1226
+ 'Cannot unify incompatible values: 92.1 with 92',
1227
+ 'Cannot unify: [nil, 91, 92.1, 93] with [90, nil, nil, nil] with [nil, nil, 92, Goal3.:gy]',
1228
+ ]
1229
+
1230
+ [g1, g2, g3].each do |goal|
1231
+ assert_equal expected_log, goal.log
1232
+ end
1233
+ end
1234
+
1235
+ it 'should not expand variables when they are instantiated to the unknown array' do
1236
+ arrays_goals = goals[0...4]
1237
+ g4.instantiate :z, UNKNOWN_ARRAY
1238
+ arrays = [
1239
+ [1, 2, nil, nil, nil],
1240
+ [:j, :n, :n, 4, nil],
1241
+ [:x, nil, :m, :y, :z],
1242
+ :z
1243
+ ]
1244
+
1245
+ assert_equal UNKNOWN_ARRAY, g4[:z].value
1246
+
1247
+ merged, unifications = unify_arrays_with_no_tails(arrays, arrays_goals, [])
1248
+
1249
+ expected_merged = [1, 2, nil, 4, g3[:z]]
1250
+ expected_unifications = [
1251
+ [g2[:j], g1[1], g2, g1],
1252
+ [g2[:n], g1[2], g2, g1],
1253
+ [g3[:x], g1[1], g3, g1],
1254
+ [g4[:z], [g1[1], g1[2], g1.value(nil), g1.value(nil), g1.value(nil)], g4, g1],
1255
+ [g2[:j], g3[:x], g2, g3],
1256
+ [g2[:n], g3[:m], g2, g3],
1257
+ [g3[:y], g2[4], g3, g2],
1258
+ [g4[:z], [g2[:j], g2[:n], g2[:n], g2[4], g2.value(nil)], g4, g2],
1259
+ [g4[:z], [g3[:x], g3.value(nil), g3[:m], g3[:y], g3[:z]], g4, g3],
1260
+ ]
1261
+
1262
+ assert_equal expected_merged, merged
1263
+ assert_equal expected_unifications, unifications
1264
+ end
1265
+
1266
+ it 'should not expand variables when they are instantiated to the unknown tail' do
1267
+ arrays_goals = goals[0...4]
1268
+ g4.instantiate :z, UNKNOWN_TAIL
1269
+ arrays = [
1270
+ [1, 2, nil, nil, nil],
1271
+ [:j, :n, :n, 4, nil],
1272
+ [:x, nil, :m, :y, :z],
1273
+ :z
1274
+ ]
1275
+
1276
+ assert_equal g4[:z], g4[:z].value
1277
+
1278
+ merged, unifications = unify_arrays_with_no_tails(arrays, arrays_goals, [])
1279
+
1280
+ expected_merged = [1, 2, nil, 4, nil]
1281
+ expected_unifications = [
1282
+ [g2[:j], g1[1], g2, g1],
1283
+ [g2[:n], g1[2], g2, g1],
1284
+ [g3[:x], g1[1], g3, g1],
1285
+ [g4[:z], [g1[1], g1[2], g1.value(nil), g1.value(nil), g1.value(nil)], g4, g1],
1286
+ [g2[:j], g3[:x], g2, g3],
1287
+ [g2[:n], g3[:m], g2, g3],
1288
+ [g3[:y], g2[4], g3, g2],
1289
+ [g4[:z], [g2[:j], g2[:n], g2[:n], g2[4], g2.value(nil)], g4, g2],
1290
+ [g4[:z], [g3[:x], g3.value(nil), g3[:m], g3[:y], g3[:z]], g4, g3],
1291
+ ]
1292
+
1293
+ assert_equal expected_merged, merged
1294
+ assert_equal expected_unifications, unifications
1295
+ end
1296
+
1297
+ it 'should not unify an atomic with an embedded unknown array ' do
1298
+ value1 = g1.value(91)
1299
+ g1.instantiate(:gi, [nil, value1, 92.1, 93])
1300
+
1301
+ arrays = [
1302
+ [ nil, 2, nil, :gi, nil],
1303
+ g2.value([1, nil, 3, [90, nil, nil, nil], nil]),
1304
+ g3.value([nil, :x, nil, [nil, nil, UNKNOWN_ARRAY, :gy], 5]),
1305
+ ]
1306
+
1307
+ arrays_goals = [g1, nil, nil]
1308
+
1309
+ assert_nil unify_arrays_with_no_tails(arrays, arrays_goals, []), name
1310
+
1311
+ expected_log = [
1312
+ 'Cannot unify incompatible values: 92.1 with [...]',
1313
+ 'Cannot unify: [nil, 91, 92.1, 93] with [90, nil, nil, nil] with [nil, nil, [...], Goal3.:gy]',
1314
+ ]
1315
+
1316
+ [g1, g2, g3].each do |goal|
1317
+ assert_equal expected_log, goal.log
1318
+ end
1319
+ end
1320
+
1321
+ it 'should not unify arrays where one embedded variable cannot be unified' do
1322
+ g1.instantiate(:a, :b)
1323
+ g2.instantiate(:a, 2)
1324
+ g3.instantiate(:a, 3)
1325
+
1326
+ arrays = [
1327
+ [[:a]],
1328
+ [[:a]],
1329
+ [[:a]],
1330
+ ]
1331
+ arrays_goals = goals[0...arrays.size]
1332
+
1333
+ assert_nil unify_arrays_with_no_tails(arrays, arrays_goals, []), name
1334
+
1335
+ assert_equal [
1336
+ 'Cannot unify: [Goal1.:a] with [Goal2.2] with [Goal3.3]',
1337
+ ], g1.log
1338
+
1339
+ assert_equal [
1340
+ 'Cannot unify because Goal2.2 != Goal3.3 (atomic != atomic)',
1341
+ 'Cannot unify: Goal2.2 with Goal3.3',
1342
+ 'Cannot unify: [Goal1.:a] with [Goal2.2] with [Goal3.3]',
1343
+ ], g2.log
1344
+
1345
+ assert_equal [
1346
+ 'Cannot unify because Goal2.2 != Goal3.3 (atomic != atomic)',
1347
+ 'Cannot unify: Goal2.2 with Goal3.3',
1348
+ 'Cannot unify: [Goal1.:a] with [Goal2.2] with [Goal3.3]',
1349
+ ], g3.log
1350
+ end
1351
+
1352
+ end
1353
+
1354
+ describe '#unify_arrays_with_some_tails' do
1355
+
1356
+ let(:g1) { new_goal(:p, :x, :y) }
1357
+ let(:g2) { new_goal(:q, :a, :b) }
1358
+ let(:g3) { new_goal(:r, :s, :t) }
1359
+ let(:goals) { [g1, g2, g3] }
1360
+
1361
+ it 'should not unify a finite array with an array with a tail that has more finite elements' do
1362
+ arrays = [
1363
+ [nil, nil, nil, nil, UNKNOWN_TAIL],
1364
+ [1, 2, 3]
1365
+ ]
1366
+ arrays_goals = goals[0...arrays.size]
1367
+
1368
+ assert_nil unify_arrays_with_some_tails(arrays, arrays_goals, []), name
1369
+
1370
+ expected_log = [
1371
+ 'Cannot unify enough elements: [nil, nil, nil, nil, ...] with [Goal2.1, Goal2.2, Goal2.3]',
1372
+ ]
1373
+
1374
+ assert_equal expected_log, g1.log
1375
+ assert_equal expected_log, g2.log
1376
+ end
1377
+
1378
+ it 'should unify a finite array with an array with a tail that has less finite elements' do
1379
+ arrays = [
1380
+ [nil, nil, UNKNOWN_TAIL],
1381
+ [1, 2, 3]
1382
+ ]
1383
+ arrays_goals = goals[0...arrays.size]
1384
+
1385
+ assert_equal [[1,2,3],[]], unify_arrays_with_some_tails(arrays, arrays_goals, []), name
1386
+ end
1387
+
1388
+ it 'should merge values with an unknown tail' do
1389
+ arrays = [
1390
+ [1, 2, 3, UNKNOWN_TAIL],
1391
+ [nil, nil, nil, 4, nil],
1392
+ [nil, nil, nil, nil, 5],
1393
+ ]
1394
+ arrays_goals = goals[0...arrays.size]
1395
+
1396
+ assert_equal [[1,2,3,4,5],[]], unify_arrays_with_some_tails(arrays, arrays_goals, []), name
1397
+ end
1398
+
1399
+ it 'should return nil when the finite arrays have different sizes' do
1400
+ arrays = [
1401
+ [nil, UNKNOWN_TAIL],
1402
+ [nil, nil, nil, nil],
1403
+ [nil, nil, nil, nil, nil, nil],
1404
+ ]
1405
+ arrays_goals = goals[0...arrays.size]
1406
+
1407
+ assert_nil unify_arrays_with_some_tails(arrays, arrays_goals, []), name
1408
+
1409
+ expected_log = [
1410
+ 'Cannot unify different sizes of arrays: [nil, ...] with [nil, nil, nil, nil] with [nil, nil, nil, nil, nil, nil]',
1411
+ ]
1412
+
1413
+ assert_equal expected_log, g1.log
1414
+ assert_equal expected_log, g2.log
1415
+ assert_equal expected_log, g3.log
1416
+ end
1417
+
1418
+ it 'should return nil when the arrays have incompatible values' do
1419
+ arrays = [
1420
+ [1, 2, 3, UNKNOWN_TAIL],
1421
+ [nil, nil, nil, 4, nil],
1422
+ [5, nil, nil, nil, 5],
1423
+ ]
1424
+ arrays_goals = goals[0...arrays.size]
1425
+
1426
+ assert_nil unify_arrays_with_some_tails(arrays, arrays_goals, []), name
1427
+
1428
+ expected_log = [
1429
+ 'Cannot unify enough elements: Goal1.1 with Goal3.5',
1430
+ ]
1431
+
1432
+ assert_equal expected_log, g1.log
1433
+ assert_equal expected_log, g2.log
1434
+ assert_equal expected_log, g3.log
1435
+ end
1436
+
1437
+ it 'should return necessary unification to unify variables' do
1438
+ arrays = [
1439
+ [1, 2, nil, UNKNOWN_TAIL],
1440
+ [:j, :n, :n, 4, nil],
1441
+ [:x, nil, :m, :y, :z],
1442
+ ]
1443
+ arrays_goals = goals[0...arrays.size]
1444
+
1445
+ merged, unifications = unify_arrays_with_some_tails(arrays, arrays_goals, [])
1446
+
1447
+ expected_merged = [1, 2, nil, 4, g3[:z]]
1448
+ expected_unifications = [
1449
+ [g2.variable(:j), g1.value(1), g2, g1],
1450
+ [g3.variable(:x), g1.value(1), g3, g1],
1451
+ [g2.variable(:j), g3.variable(:x), g2, g3],
1452
+ [g2.variable(:n), g1.value(2), g2, g1],
1453
+ [g2.variable(:n), g3.variable(:m), g2, g3],
1454
+ [g3.variable(:y), g2.value(4), g3, g2],
1455
+ ]
1456
+
1457
+ assert_equal [expected_merged, expected_unifications], [merged, unifications], name
1458
+ end
1459
+
1460
+ it 'should handle a range of cases' do
1461
+ [
1462
+ [[[], [1,UNKNOWN_TAIL] ], nil, ['Cannot unify enough elements: [] with [Goal2.1, ...]']],
1463
+ [[[]/:t, [1] ], [[1], [[g1.variable(:t), [1], g1, g2]]]],
1464
+ [[[], [1,2]/:s ], nil, ['Cannot unify enough elements: [] with [Goal2.1, Goal2.2, *Goal2.:s]']],
1465
+ [[[]/:t, [1,2] ], [[1,2], [[g1.variable(:t), [1,2], g1, g2]]]],
1466
+ [[[1,2,3], [1,UNKNOWN_TAIL] ], [[1,2,3],[]]],
1467
+ [[[1,2,3,UNKNOWN_TAIL], [1] ], nil, ['Cannot unify enough elements: [Goal1.1, Goal1.2, Goal1.3, ...] with [Goal2.1]']],
1468
+ [[[1], [1,2,3,UNKNOWN_TAIL] ], nil, ['Cannot unify enough elements: [Goal1.1] with [Goal2.1, Goal2.2, Goal2.3, ...]']],
1469
+ [[[1]/:z, [1,2,3] ], [[1,2,3], [[g1.variable(:z), [2, 3], g1, g2]]]],
1470
+ [[[:x,:y,:z], [1,2,3,4,UNKNOWN_TAIL]], nil, ['Cannot unify enough elements: [Goal1.:x, Goal1.:y, Goal1.:z] with [Goal2.1, Goal2.2, Goal2.3, Goal2.4, ...]']],
1471
+ [[[:x,:y,:z,UNKNOWN_TAIL], [1,2,3,4] ], [
1472
+ [1,2,3,4],
1473
+ [
1474
+ [g1.variable(:x), g2.value(1), g1, g2],
1475
+ [g1.variable(:y), g2.value(2), g1, g2],
1476
+ [g1.variable(:z), g2.value(3), g1, g2]
1477
+ ]
1478
+ ]],
1479
+ ].each do |arrays, expected, expected_log|
1480
+ arrays_goals = goals[0...arrays.size]
1481
+
1482
+ result = unify_arrays_with_some_tails(arrays, arrays_goals, [])
1483
+
1484
+ if expected.nil?
1485
+ assert_nil result
1486
+
1487
+ arrays_goals.uniq.each do |goal|
1488
+ assert_equal expected_log, goal.log
1489
+ goal.log[0..-1] = []
1490
+ end
1491
+ else
1492
+ assert_equal expected, result
1493
+ end
1494
+ end
1495
+ end
1496
+
1497
+ it 'should raise a no goal error when no goals are supplied and the arrays have no embedded goals' do
1498
+ arrays = [
1499
+ [nil, nil, nil, nil, UNKNOWN_TAIL],
1500
+ [1, 2, 3]
1501
+ ]
1502
+ arrays_goals = [nil] * arrays.size
1503
+
1504
+ error = assert_raises Porolog::NoGoalError do
1505
+ unify_arrays_with_some_tails(arrays, arrays_goals, [])
1506
+ end
1507
+
1508
+ assert_equal '[nil, nil, nil, nil, ...] has no associated goal! Cannot variablise!', error.message
1509
+ end
1510
+
1511
+ it 'should derive goals from embedded goals when the goals are not supplied' do
1512
+ arrays_goals = [g1, nil]
1513
+ arrays = [
1514
+ [nil, nil, nil, nil, UNKNOWN_TAIL],
1515
+ [1, 2, g2[3], 4]
1516
+ ]
1517
+
1518
+ refute arrays.all?{|array| has_tail?(array, false) }, "not all arrays should have a tail"
1519
+ refute arrays.all?{|array| !has_tail?(array, false) }, "not all arrays should not have a tail"
1520
+
1521
+ merged, unifications = unify_arrays_with_some_tails(arrays, arrays_goals, [])
1522
+
1523
+ expected_merged = [1,2,3,4]
1524
+ expected_unifications = [
1525
+ ]
1526
+
1527
+ assert_equal expected_merged, merged
1528
+ assert_equal expected_unifications, unifications
1529
+ end
1530
+
1531
+ it 'should extract embedded variables and values in an array in a value' do
1532
+ arrays = [
1533
+ g1.value([nil, :b, nil, nil, 5]),
1534
+ [1, 2, g2[3], 4]/:t
1535
+ ]
1536
+ arrays_goals = goals[0...arrays.size]
1537
+
1538
+ refute arrays.all?{|array| has_tail?(array, false) }, "not all arrays should have a tail"
1539
+ refute arrays.all?{|array| !has_tail?(array, false) }, "not all arrays should not have a tail"
1540
+
1541
+ merged, unifications = unify_arrays_with_some_tails(arrays, arrays_goals, [])
1542
+
1543
+ expected_merged = [1,2,3,4,5]
1544
+ expected_unifications = [
1545
+ [g1[:b], g2[2], g1, g2],
1546
+ [g2[:t], [5], g2, g1],
1547
+ ]
1548
+
1549
+ assert_equal expected_merged, merged
1550
+ assert_equal expected_unifications, unifications
1551
+ end
1552
+
1553
+ it 'should unify multiple atomics and tails' do
1554
+ arrays = [
1555
+ g1.value([nil, :b, nil, nil, 5, 6, 7]),
1556
+ [1, 2, g2[3], 4]/:t,
1557
+ [1, 2, g3[3], 4]/:t,
1558
+ ]
1559
+ arrays_goals = goals[0...arrays.size]
1560
+
1561
+ refute arrays.all?{|array| has_tail?(array, false) }, "not all arrays should have a tail"
1562
+ refute arrays.all?{|array| !has_tail?(array, false) }, "not all arrays should not have a tail"
1563
+
1564
+ merged, unifications = unify_arrays_with_some_tails(arrays, arrays_goals, [])
1565
+
1566
+ expected_merged = [1,2,3,4,5,6,7]
1567
+ expected_unifications = [
1568
+ [g1[:b], g2[2], g1, g2],
1569
+ [g1[:b], g3[2], g1, g3],
1570
+ [g2[:t], [5,6,7], g2, g1],
1571
+ [g3[:t], [5,6,7], g3, g1],
1572
+ ]
1573
+
1574
+ assert_equal expected_merged, merged
1575
+ assert_equal expected_unifications, unifications
1576
+ end
1577
+
1578
+ it 'should not unify incompatible atomics' do
1579
+ arrays = [
1580
+ g1.value([nil, :b, nil, nil, 5]),
1581
+ [1, 2, g2[3], 4]/:t,
1582
+ [1, 3, g3[3], 4]/:t,
1583
+ ]
1584
+ arrays_goals = goals[0...arrays.size]
1585
+
1586
+ refute arrays.all?{|array| has_tail?(array, false) }, "not all arrays should have a tail"
1587
+ refute arrays.all?{|array| !has_tail?(array, false) }, "not all arrays should not have a tail"
1588
+
1589
+ assert_nil unify_arrays_with_some_tails(arrays, arrays_goals, []), name
1590
+
1591
+ assert_equal [
1592
+ 'Cannot unify non-variables: 2 with 3'
1593
+ ], g1.log
1594
+
1595
+ assert_equal [
1596
+ 'Cannot unify non-variables: 2 with 3'
1597
+ ], g2.log
1598
+
1599
+ assert_equal [
1600
+ 'Cannot unify non-variables: 2 with 3'
1601
+ ], g3.log
1602
+ end
1603
+
1604
+ end
1605
+
1606
+ describe '#unify_arrays_with_all_tails' do
1607
+
1608
+ let(:g1) { new_goal(:p, :x, :y) }
1609
+ let(:g2) { new_goal(:q, :a, :b) }
1610
+ let(:g3) { new_goal(:r, :s, :t) }
1611
+ let(:goals) { [g1, g2, g3] }
1612
+
1613
+ let(:array_with_tail_1) { [1, 2, Tail.new(:h)] }
1614
+ let(:array_with_tail_2) { [:h, :b]/:t }
1615
+ let(:array_with_tail_3) { [:x, :y, UNKNOWN_TAIL] }
1616
+ let(:head_and_tail_1) { [1, Tail.new([2,3])] }
1617
+ let(:head_and_tail_2) { [:h]/:t }
1618
+ let(:head_and_tail_3) { [:x, UNKNOWN_TAIL] }
1619
+
1620
+ it 'should call unify_tail_with_tail when no array is headtail' do
1621
+ expects(:unify_tail_with_tail).times(1)
1622
+ expects(:unify_headtail_with_tail).times(0)
1623
+ expects(:unify_headtail_with_headtail).times(0)
1624
+
1625
+ arrays = [
1626
+ array_with_tail_1,
1627
+ array_with_tail_2,
1628
+ array_with_tail_3,
1629
+ ]
1630
+ arrays_goals = goals[0...arrays.size]
1631
+
1632
+ unify_arrays_with_all_tails(arrays, arrays_goals, [])
1633
+ end
1634
+
1635
+ it 'should call unify_headtail_with_tail when all arrays but one are headtail' do
1636
+ expects(:unify_tail_with_tail).times(0)
1637
+ expects(:unify_headtail_with_tail).times(1)
1638
+ expects(:unify_headtail_with_headtail).times(0)
1639
+
1640
+ arrays = [
1641
+ array_with_tail_1,
1642
+ head_and_tail_2,
1643
+ head_and_tail_3,
1644
+ ]
1645
+ arrays_goals = goals[0...arrays.size]
1646
+
1647
+ unify_arrays_with_all_tails(arrays, arrays_goals, [])
1648
+ end
1649
+
1650
+ it 'should include unifications of variables in a tail' do
1651
+ arrays = [
1652
+ [nil, nil, nil]/:list,
1653
+ [1, 2, 3, nil, :y, UNKNOWN_TAIL],
1654
+ [nil, :x, nil, 4, Tail.new([nil,6])],
1655
+ ]
1656
+ arrays_goals = goals[0...arrays.size]
1657
+
1658
+ g1.instantiate :list, [4,5,:z]
1659
+
1660
+ merged, unifications = unify_arrays_with_all_tails(arrays, arrays_goals, [])
1661
+
1662
+ expected_merged = [1,2,3,4,5,6]
1663
+ expected_unifications = [
1664
+ [g3[:x], g2[2], g3, g2],
1665
+ [g2[:y], g1[5], g2, g1],
1666
+ [g1[:z], g3[6], g1, g3],
1667
+ [g1[:z], g1[6], g1, g1],
1668
+ ]
1669
+
1670
+ assert_equal expected_merged, merged
1671
+ assert_equal expected_unifications, unifications
1672
+ end
1673
+
1674
+ it 'should not unify arrays where one embedded variable cannot be unified' do
1675
+ g1.instantiate(:a, [g1.value(1), g1.value(2), g1.value(3), UNKNOWN_TAIL])
1676
+ g2.instantiate(:a, :b)
1677
+ g3.instantiate(:a, :b)
1678
+
1679
+ arrays = [
1680
+ :a,
1681
+ [:a,Tail.new([1])],
1682
+ [:a, UNKNOWN_TAIL],
1683
+ ]
1684
+ arrays_goals = goals[0...arrays.size]
1685
+
1686
+ assert_nil unify_arrays_with_all_tails(arrays, arrays_goals, []), name
1687
+
1688
+ assert_equal [
1689
+ 'Cannot unify enough elements: Goal1.[Goal1.1, Goal1.2, Goal1.3, ...] with [Goal2.:a, Goal2.1]',
1690
+ 'Cannot unify because Goal1.[Goal1.1, Goal1.2, Goal1.3, ...] != [:a, 1] (variable/array != array)',
1691
+ 'Cannot unify embedded arrays: :a with [:a, 1]',
1692
+ ], g1.log
1693
+
1694
+ assert_equal [
1695
+ 'Cannot unify enough elements: Goal1.[Goal1.1, Goal1.2, Goal1.3, ...] with [Goal2.:a, Goal2.1]',
1696
+ 'Cannot unify because Goal1.[Goal1.1, Goal1.2, Goal1.3, ...] != [:a, 1] (variable/array != array)',
1697
+ 'Cannot unify embedded arrays: :a with [:a, 1]',
1698
+ ], g2.log
1699
+
1700
+ assert_equal [
1701
+ 'Cannot unify embedded arrays: :a with [:a, 1]',
1702
+ ], g3.log
1703
+ end
1704
+
1705
+ it 'should unify arrays where all embedded variables can be unified' do
1706
+ arrays = [
1707
+ [g1.variable(:a), g1.value(2), g1.variable(:c), g1.value(4)],
1708
+ [g2.value(1), g1.variable(:b), g2.value(3), UNKNOWN_TAIL],
1709
+ [:a, :b, Tail.new([3,:d])],
1710
+ ]
1711
+ arrays_goals = goals[0...arrays.size]
1712
+
1713
+ assert arrays.map(&:headtail?).none?
1714
+
1715
+ expected_merged = [1,2,3,4]
1716
+ expected_unifications = [
1717
+ [g1.variable(:a), g2.value(1), g1, g2],
1718
+ [g1.variable(:a), g3.variable(:a), g1, g3],
1719
+ [g3.variable(:a), g2.value(1), g3, g2],
1720
+ [g1.variable(:b), g1.value(2), g1, g1],
1721
+ [g3.variable(:b), g1.value(2), g3, g1],
1722
+ [g1.variable(:b), g3.variable(:b), g1, g3],
1723
+ [g1.variable(:c), g2.value(3), g1, g2],
1724
+ [g1.variable(:c), g3.value(3), g1, g3],
1725
+ [g3.variable(:d), g1.value(4), g3, g1]
1726
+ ]
1727
+ merged, unifications = unify_arrays_with_all_tails(arrays, arrays_goals, [])
1728
+
1729
+ assert_equal [], g1.log
1730
+ assert_equal [], g2.log
1731
+ assert_equal [], g3.log
1732
+
1733
+ assert_equal expected_merged, merged
1734
+ assert_equal expected_unifications, unifications
1735
+ end
1736
+
1737
+ it 'should unify embedded arrays where all embedded variables can be unified' do
1738
+ arrays = [
1739
+ :e,
1740
+ [[1],[2],[3],[4]],
1741
+ [[1],[2],[3],[4]],
1742
+ ]
1743
+ arrays_goals = goals[0...arrays.size]
1744
+
1745
+ assert arrays.map(&:headtail?).none?
1746
+
1747
+ expected_merged = [[1],[2],[3],[4]]
1748
+ expected_unifications = [
1749
+ [:e, [[1], [2], [3], [4]], g1, g2]
1750
+ ]
1751
+ merged, unifications = unify_arrays_with_all_tails(arrays, arrays_goals, [])
1752
+
1753
+ assert_equal [], g1.log
1754
+ assert_equal [], g2.log
1755
+ assert_equal [], g3.log
1756
+
1757
+ assert_equal expected_merged, merged
1758
+ assert_equal expected_unifications, unifications
1759
+ end
1760
+
1761
+ end
1762
+
1763
+ describe '#unify_tail_with_tail' do
1764
+
1765
+ let(:g1) { new_goal(:p, :x, :y) }
1766
+ let(:g2) { new_goal(:q, :a, :b) }
1767
+ let(:g3) { new_goal(:r, :s, :t) }
1768
+ let(:g4) { new_goal(:j, :k, :l) }
1769
+ let(:goals) { [g1, g2, g3, g4] }
1770
+
1771
+ it 'should reject a non-headtail array' do
1772
+ arrays = [
1773
+ [1,Tail.new([2,3])],
1774
+ [4],
1775
+ [7,UNKNOWN_TAIL],
1776
+ ]
1777
+ arrays_goals = goals[0...arrays.size]
1778
+
1779
+ refute arrays.all?(&:headtail?)
1780
+
1781
+ assert_nil unify_tail_with_tail(arrays, arrays_goals, [])
1782
+
1783
+ msg = 'Wrong method called to unify [[1, *[2, 3]], [4], [7, ...]]'
1784
+
1785
+ assert_equal [msg], g1.log
1786
+ assert_equal [msg], g2.log
1787
+ assert_equal [msg], g3.log
1788
+ end
1789
+
1790
+ it 'should process more than two arrays that are a non-headtail' do
1791
+ arrays = [
1792
+ [1, 2, Tail.new([3,4])],
1793
+ [:a, :b]/:c,
1794
+ [:d, :e, UNKNOWN_TAIL],
1795
+ ]
1796
+ arrays_goals = goals[0...arrays.size]
1797
+
1798
+ assert arrays.all?{|array| has_tail?(array, false) }
1799
+ assert arrays.map(&:headtail?).map(&:!).all?
1800
+
1801
+ merged, unifications = unify_tail_with_tail(arrays, arrays_goals, [])
1802
+
1803
+ expected_merged = [1,2,3,4]
1804
+
1805
+ expected_unifications = [
1806
+ [g2.variable(:a), g1.value(1), g2, g1],
1807
+ [g3.variable(:d), g1.value(1), g3, g1],
1808
+ [g2.variable(:a), g3.variable(:d), g2, g3],
1809
+ [g2.variable(:b), g1.value(2), g2, g1],
1810
+ [g3.variable(:e), g1.value(2), g3, g1],
1811
+ [g2.variable(:b), g3.variable(:e), g2, g3],
1812
+ [g2.variable(:c), g1.variablise([3,4]), g2, g1],
1813
+ ]
1814
+
1815
+ assert_equal expected_merged, merged
1816
+ assert_equal expected_unifications, unifications
1817
+ end
1818
+
1819
+ it 'should unify arrays of different lengths' do
1820
+ arrays = [
1821
+ [1, 2, UNKNOWN_TAIL],
1822
+ [nil, nil, 3, 4, UNKNOWN_TAIL],
1823
+ [nil, nil, nil, nil, 5, 6, UNKNOWN_TAIL],
1824
+ ]
1825
+ arrays_goals = goals[0...arrays.size]
1826
+
1827
+ assert arrays.all?{|array| has_tail?(array) }
1828
+ assert arrays.map(&:headtail?).map(&:!).all?
1829
+
1830
+ merged, unifications = unify_tail_with_tail(arrays, arrays_goals, [])
1831
+
1832
+ expected_merged = [
1833
+ g1.value(1),
1834
+ g1.value(2),
1835
+ g2.value(3),
1836
+ g2.value(4),
1837
+ g3.value(5),
1838
+ g3.value(6),
1839
+ UNKNOWN_TAIL,
1840
+ ]
1841
+
1842
+ expected_unifications = [
1843
+ ]
1844
+
1845
+ assert_equal expected_merged, merged
1846
+ assert_equal expected_unifications, unifications
1847
+ end
1848
+
1849
+ it 'should unify all combinations of tails and tails' do
1850
+ arrays = [
1851
+ [:a,:b]/:c,
1852
+ [:d,:e]/:f,
1853
+ [:g,:h]/:i,
1854
+ [:j,:k]/:l,
1855
+ ]
1856
+ arrays_goals = goals[0...arrays.size]
1857
+
1858
+ assert arrays.all?{|array| has_tail?(array) }
1859
+ assert arrays.map(&:headtail?).map(&:!).all?
1860
+
1861
+ merged, unifications = unify_tail_with_tail(arrays, arrays_goals, [])
1862
+
1863
+ expected_merged = [nil, nil, UNKNOWN_TAIL]
1864
+
1865
+ expected_unifications = [
1866
+ [g1[:a], g2[:d], g1, g2],
1867
+ [g1[:a], g3[:g], g1, g3],
1868
+ [g1[:a], g4[:j], g1, g4],
1869
+ [g2[:d], g3[:g], g2, g3],
1870
+ [g2[:d], g4[:j], g2, g4],
1871
+ [g3[:g], g4[:j], g3, g4],
1872
+ [g1[:b], g2[:e], g1, g2],
1873
+ [g1[:b], g3[:h], g1, g3],
1874
+ [g1[:b], g4[:k], g1, g4],
1875
+ [g2[:e], g3[:h], g2, g3],
1876
+ [g2[:e], g4[:k], g2, g4],
1877
+ [g3[:h], g4[:k], g3, g4],
1878
+ [g1[:c], g2[:f], g1, g2],
1879
+ [g1[:c], g3[:i], g1, g3],
1880
+ [g1[:c], g4[:l], g1, g4],
1881
+ [g2[:f], g3[:i], g2, g3],
1882
+ [g2[:f], g4[:l], g2, g4],
1883
+ [g3[:i], g4[:l], g3, g4],
1884
+ ]
1885
+
1886
+ assert_equal expected_merged, merged
1887
+ assert_equal expected_unifications, unifications
1888
+ end
1889
+
1890
+ it 'should not unify ununifiable arrays' do
1891
+ arrays = [
1892
+ [1,2,3,4,UNKNOWN_TAIL],
1893
+ [1,4,3,2,UNKNOWN_TAIL],
1894
+ ]
1895
+ arrays_goals = goals[0...arrays.size]
1896
+
1897
+ assert arrays.all?{|array| has_tail?(array) }
1898
+ assert arrays.map(&:headtail?).map(&:!).all?
1899
+
1900
+ assert_nil unify_tail_with_tail(arrays, arrays_goals, []), name
1901
+
1902
+ expected_log = [
1903
+ 'Cannot unify incompatible values: 2 with 4',
1904
+ 'Cannot unify Goal1.2 with Goal2.4',
1905
+ ]
1906
+
1907
+ arrays_goals.each do |goal|
1908
+ assert_equal expected_log, goal.log
1909
+ end
1910
+ end
1911
+
1912
+ end
1913
+
1914
+ describe '#unify_headtail_with_tail' do
1915
+
1916
+ let(:g1) { new_goal(:p, :x, :y) }
1917
+ let(:g2) { new_goal(:q, :a, :b) }
1918
+ let(:g3) { new_goal(:r, :s, :t) }
1919
+ let(:g4) { new_goal(:j, :k, :l) }
1920
+ let(:goals) { [g1, g2, g3, g4] }
1921
+
1922
+ it 'should reject a non-tail array' do
1923
+ arrays = [
1924
+ [1,Tail.new([2,3])],
1925
+ [4],
1926
+ [1,2,3,7,UNKNOWN_TAIL],
1927
+ ]
1928
+ arrays_goals = goals[0...arrays.size]
1929
+
1930
+ refute arrays.all?{|array| has_tail?(array) }
1931
+
1932
+ assert_nil unify_headtail_with_tail(arrays, arrays_goals, [])
1933
+
1934
+ msg = 'Wrong method called to unify [[1, *[2, 3]], [4], [1, 2, 3, 7, ...]]'
1935
+
1936
+ assert_equal [msg], g1.log
1937
+ assert_equal [msg], g2.log
1938
+ assert_equal [msg], g3.log
1939
+ end
1940
+
1941
+ it 'should unify more than two arrays that have a tail' do
1942
+ arrays = [
1943
+ [1, Tail.new([2,3])],
1944
+ [:a, :b]/:f,
1945
+ [:c, UNKNOWN_TAIL],
1946
+ [:d, :e, UNKNOWN_TAIL]
1947
+ ]
1948
+ arrays_goals = goals[0...arrays.size]
1949
+
1950
+ assert arrays.all?{|array| has_tail?(array, false) }
1951
+
1952
+ merged, unifications = unify_headtail_with_tail(arrays, arrays_goals, [])
1953
+
1954
+ expected_merged = [1,2,3]
1955
+
1956
+ expected_unifications = [
1957
+ [g3[:c], g1[1], g3, g1],
1958
+ [g2[:a], g4[:d], g2, g4],
1959
+ [g2[:b], g4[:e], g2, g4],
1960
+ [g2[:a], g1[1], g2, g1],
1961
+ [g2[:b], g1[2], g2, g1],
1962
+ [g2[:f], [3], g2, g1],
1963
+ [g4[:d], g1[1], g4, g1],
1964
+ [g4[:e], g1[2], g4, g1],
1965
+ [g3[:c], g2[:a], g3, g2],
1966
+ [g3[:c], g4[:d], g3, g4]
1967
+ ]
1968
+
1969
+ assert_equal expected_merged, merged
1970
+ assert_equal expected_unifications, unifications
1971
+ end
1972
+
1973
+ it 'should unify unifiable arrays with different length tails' do
1974
+ arrays = [
1975
+ [1, UNKNOWN_TAIL],
1976
+ [:a, :b, :c, :d]/:f,
1977
+ [:c, UNKNOWN_TAIL],
1978
+ [:d, 2, UNKNOWN_TAIL]
1979
+ ]
1980
+ arrays_goals = goals[0...arrays.size]
1981
+
1982
+ assert arrays.all?{|array| has_tail?(array, false) }
1983
+
1984
+ merged, unifications = unify_headtail_with_tail(arrays, arrays_goals, [])
1985
+
1986
+ expected_merged = [1,2,nil,nil,UNKNOWN_TAIL]
1987
+
1988
+ expected_unifications = [
1989
+ [g3[:c], g1[1], g3, g1],
1990
+ [g2[:a], g4[:d], g2, g4],
1991
+ [g2[:b], g4[2], g2, g4],
1992
+ [g2[:a], g1[1], g2, g1],
1993
+ [g4[:d], g1[1], g4, g1],
1994
+ [g3[:c], g2[:a], g3, g2],
1995
+ [g3[:c], g4[:d], g3, g4],
1996
+ ]
1997
+
1998
+ assert_equal expected_merged, merged
1999
+ assert_equal expected_unifications, unifications
2000
+ end
2001
+
2002
+ it 'should not unify unifiable heads across a headtail and an array with a tail' do
2003
+ g1.instantiate :h, 1
2004
+ g2.instantiate :h, 2
2005
+ arrays = [
2006
+ [:h]/:t,
2007
+ [:h,:b]/:t,
2008
+ [:h]/:t,
2009
+ [:h]/:t,
2010
+ ]
2011
+ arrays_goals = goals[0...arrays.size]
2012
+
2013
+ assert arrays.all?{|array| has_tail?(array, false) }, 'all arrays should have a tail'
2014
+ refute arrays.map(&:headtail?).all?, 'not all arrays should be a headtail array'
2015
+ refute arrays.map(&:headtail?).map(&:!).all?, 'not all arrays should be an array with a tail'
2016
+
2017
+ assert_nil unify_headtail_with_tail(arrays, arrays_goals, []), name
2018
+
2019
+ assert_equal [
2020
+ 'Cannot unify because 1 != 2 (variable != variable)',
2021
+ 'Cannot unify heads: Goal1.:h with Goal2.:h',
2022
+ ], g1.log
2023
+ assert_equal [
2024
+ 'Cannot unify because 1 != 2 (variable != variable)',
2025
+ 'Cannot unify heads: Goal1.:h with Goal2.:h',
2026
+ ], g2.log
2027
+ assert_equal [
2028
+ ], g3.log
2029
+ assert_equal [
2030
+ ], g4.log
2031
+ end
2032
+
2033
+ it 'should not unify tails with different values' do
2034
+ g1.instantiate :t, [2,3]
2035
+ g2.instantiate :t, [2,4]
2036
+ g3.instantiate :t, 4
2037
+ arrays = [
2038
+ [:h]/:t,
2039
+ [:h]/:t,
2040
+ [:h,:b]/:t,
2041
+ [:h]/:t,
2042
+ ]
2043
+ arrays_goals = goals[0...arrays.size]
2044
+
2045
+ assert arrays.all?{|array| has_tail?(array, false) }, 'all arrays should have a tail'
2046
+ refute arrays.map(&:headtail?).all?, 'not all arrays should be a headtail array'
2047
+ refute arrays.map(&:headtail?).map(&:!).all?, 'not all arrays should be an array with a tail'
2048
+
2049
+ assert_nil unify_headtail_with_tail(arrays, arrays_goals, []), name
2050
+
2051
+ assert_equal [
2052
+ 'Cannot unify because [2, 3] != [2, 4] (variable != variable)',
2053
+ 'Cannot unify headtail tails: Goal1.:t with Goal2.:t',
2054
+ 'Could not unify headtail arrays: [Goal1.:h, Goal1.2, Goal1.3] with [Goal2.:h, Goal2.2, Goal2.4] with [Goal4.:h, *Goal4.:t]',
2055
+ ], g1.log
2056
+ assert_equal [
2057
+ 'Cannot unify because [2, 3] != [2, 4] (variable != variable)',
2058
+ 'Cannot unify headtail tails: Goal1.:t with Goal2.:t',
2059
+ 'Could not unify headtail arrays: [Goal1.:h, Goal1.2, Goal1.3] with [Goal2.:h, Goal2.2, Goal2.4] with [Goal4.:h, *Goal4.:t]',
2060
+ ], g2.log
2061
+ assert_equal [
2062
+ ], g3.log
2063
+ assert_equal [
2064
+ 'Could not unify headtail arrays: [Goal1.:h, Goal1.2, Goal1.3] with [Goal2.:h, Goal2.2, Goal2.4] with [Goal4.:h, *Goal4.:t]',
2065
+ ], g4.log
2066
+ end
2067
+
2068
+ it 'should not unify variable length arrays that have been instantiatd to different lengths' do
2069
+ g1.instantiate :t, [2,3]
2070
+ g2.instantiate :t, 3
2071
+ g3.instantiate :t, [2,3,4]
2072
+ arrays = [
2073
+ [:h]/:t,
2074
+ [:h,:b]/:t,
2075
+ [:h]/:t,
2076
+ [:h]/:t,
2077
+ ]
2078
+ arrays_goals = goals[0...arrays.size]
2079
+
2080
+ assert arrays.all?{|array| has_tail?(array, false) }, 'all arrays should have a tail'
2081
+ refute arrays.map(&:headtail?).all?, 'not all arrays should be a headtail array'
2082
+ refute arrays.map(&:headtail?).map(&:!).all?, 'not all arrays should be an array with a tail'
2083
+
2084
+ assert_nil unify_headtail_with_tail(arrays, arrays_goals, []), name
2085
+
2086
+ assert_equal [
2087
+ ], g1.log
2088
+ assert_equal [
2089
+ ], g2.log
2090
+ assert_equal [
2091
+ 'Cannot unify [Goal3.:h, Goal3.2, Goal3.3, Goal3.4] because it has a different length from 3'
2092
+ ], g3.log
2093
+ assert_equal [
2094
+ ], g4.log
2095
+ end
2096
+
2097
+ it 'should not unify incompatible tails' do
2098
+ g1.instantiate :t, [2,3]
2099
+ g2.instantiate :b, 2
2100
+ g2.instantiate :t, [3]
2101
+ g3.instantiate :t, [4]
2102
+ arrays = [
2103
+ [:h]/:t,
2104
+ [:h,:b]/:t,
2105
+ [:h,:b]/:t,
2106
+ [:h]/:t,
2107
+ ]
2108
+ arrays_goals = goals[0...arrays.size]
2109
+
2110
+ assert arrays.all?{|array| has_tail?(array, false) }, 'all arrays should have a tail'
2111
+ refute arrays.map(&:headtail?).all?, 'not all arrays should be a headtail array'
2112
+ refute arrays.map(&:headtail?).map(&:!).all?, 'not all arrays should be an array with a tail'
2113
+
2114
+ assert_nil unify_headtail_with_tail(arrays, arrays_goals, []), name
2115
+
2116
+ assert_equal [
2117
+ ], g1.log
2118
+ assert_equal [
2119
+ 'Cannot unify incompatible values: 3 with 4',
2120
+ 'Cannot unify: [3] with [4]',
2121
+ ], g2.log
2122
+ assert_equal [
2123
+ 'Cannot unify incompatible values: 3 with 4',
2124
+ 'Cannot unify: [3] with [4]',
2125
+ ], g3.log
2126
+ assert_equal [
2127
+ ], g4.log
2128
+ end
2129
+
2130
+ it 'should expand tails that overlap extended heads' do
2131
+ g1.instantiate :t, [3,3]
2132
+ g2.instantiate :b, 2
2133
+ g2.instantiate :t, [3]
2134
+ arrays = [
2135
+ [:h]/:t,
2136
+ [:h,:b]/:t,
2137
+ [:h,:b]/:t,
2138
+ [:h]/:t,
2139
+ ]
2140
+ arrays_goals = goals[0...arrays.size]
2141
+
2142
+ assert arrays.all?{|array| has_tail?(array, false) }, 'all arrays should have a tail'
2143
+ refute arrays.map(&:headtail?).all?, 'not all arrays should be a headtail array'
2144
+ refute arrays.map(&:headtail?).map(&:!).all?, 'not all arrays should be an array with a tail'
2145
+
2146
+ assert_nil unify_headtail_with_tail(arrays, arrays_goals, []), name
2147
+
2148
+ assert_equal [
2149
+ 'Cannot unify incompatible values: 3 with 2',
2150
+ 'Cannot unify because Goal1.[3, 3] != [Goal2.:b, *Goal2.:t] (variable/array != array)',
2151
+ 'Cannot unify tails: Goal1.:t with [Goal2.:b, *Goal2.:t]',
2152
+ ], g1.log
2153
+ assert_equal [
2154
+ 'Cannot unify incompatible values: 3 with 2',
2155
+ 'Cannot unify because Goal1.[3, 3] != [Goal2.:b, *Goal2.:t] (variable/array != array)',
2156
+ 'Cannot unify tails: Goal1.:t with [Goal2.:b, *Goal2.:t]',
2157
+ ], g2.log
2158
+ assert_equal [
2159
+ ], g3.log
2160
+ assert_equal [
2161
+ ], g4.log
2162
+ end
2163
+
2164
+ it 'prioritises X over Y when merging' do
2165
+ # -- atomic over known array over non-array over unknown array/tail --
2166
+ # atomic over known
2167
+ # known array over non-array
2168
+ # non-array over unknown array/tail
2169
+ # atomic over non-array
2170
+ # array over unknown array/tail
2171
+ # atomic over unknown array/tail
2172
+
2173
+ g1.instantiate :t, [1,2,3]
2174
+ arrays = [
2175
+ :h/:t,
2176
+ UNKNOWN_ARRAY,
2177
+ ]
2178
+ arrays_goals = goals[0...arrays.size]
2179
+
2180
+ merged, unifications = unify_headtail_with_tail(arrays, arrays_goals, [])
2181
+
2182
+ expected_merged = [nil, 1, 2, 3]
2183
+ expected_unifications = []
2184
+
2185
+ assert_equal expected_merged, merged
2186
+ assert_equal expected_unifications, unifications
2187
+ end
2188
+
2189
+ end
2190
+
2191
+ describe '#unify_headtail_with_headtail' do
2192
+
2193
+ let(:g1) { new_goal(:p, :x, :y) }
2194
+ let(:g2) { new_goal(:q, :a, :b) }
2195
+ let(:g3) { new_goal(:r, :s, :t) }
2196
+ let(:g4) { new_goal(:j, :k, :l) }
2197
+ let(:goals) { [g1, g2, g3, g4] }
2198
+
2199
+ it 'should reject a non-headtail array' do
2200
+ arrays = [
2201
+ [1,Tail.new([2,3])],
2202
+ [4],
2203
+ [7,UNKNOWN_TAIL],
2204
+ ]
2205
+ arrays_goals = goals[0...arrays.size]
2206
+
2207
+ refute arrays.all?(&:headtail?)
2208
+
2209
+ assert_nil unify_headtail_with_headtail(arrays, arrays_goals, [])
2210
+
2211
+ msg = 'Wrong method called to unify [[1, *[2, 3]], [4], [7, ...]]'
2212
+
2213
+ arrays_goals.each do |goal|
2214
+ assert_equal [msg], goal.log
2215
+ end
2216
+ end
2217
+
2218
+ it 'should not unify unequal heads' do
2219
+ arrays = [
2220
+ [1,Tail.new([2,3])],
2221
+ [4]/:y,
2222
+ [7,UNKNOWN_TAIL],
2223
+ ]
2224
+ arrays_goals = goals[0...arrays.size]
2225
+
2226
+ assert arrays.all?(&:headtail?), "arrays should all be headtails: #{arrays.reject(&:headtail?).map(&:inspect).join(' and ')}"
2227
+
2228
+ assert_nil unify_headtail_with_headtail(arrays, arrays_goals, [])
2229
+
2230
+ assert_equal [
2231
+ 'Cannot unify because 1 != 4 (atomic != atomic)',
2232
+ 'Cannot unify headtail heads: "1 with 4"',
2233
+ ], g1.log
2234
+ assert_equal [
2235
+ 'Cannot unify because 1 != 4 (atomic != atomic)',
2236
+ 'Cannot unify headtail heads: "1 with 4"',
2237
+ ], g2.log
2238
+ assert_equal [
2239
+ ], g3.log
2240
+ end
2241
+
2242
+ it 'should process more than two arrays that are a headtail' do
2243
+ arrays = [
2244
+ [1, Tail.new([2,3])],
2245
+ [:a]/:f,
2246
+ [:c, UNKNOWN_TAIL],
2247
+ ]
2248
+ arrays_goals = goals[0...arrays.size]
2249
+
2250
+ assert arrays.all?(&:headtail?)
2251
+
2252
+ merged, unifications = unify_headtail_with_headtail(arrays, arrays_goals, [])
2253
+
2254
+ expected_merged = [1,2,3]
2255
+
2256
+ expected_unifications = [
2257
+ [:a, 1, g2, g1],
2258
+ [:f, [2,3], g2, g1],
2259
+ [:c, 1, g3, g1],
2260
+ [:a, :c, g2, g3],
2261
+ [:f, UNKNOWN_TAIL, g2, g3],
2262
+ ]
2263
+
2264
+ assert_equal expected_merged, merged
2265
+ assert_equal expected_unifications, unifications
2266
+
2267
+ arrays_goals.each do |goal|
2268
+ assert_equal [], goal.log
2269
+ end
2270
+ end
2271
+
2272
+ it 'should unify all combinations of heads and tails' do
2273
+ arrays = [
2274
+ [:a]/:b,
2275
+ [:c]/:d,
2276
+ [:e]/:f,
2277
+ [:g]/:h,
2278
+ ]
2279
+ arrays_goals = goals[0...arrays.size]
2280
+
2281
+ assert arrays.all?(&:headtail?)
2282
+
2283
+ merged, unifications = unify_headtail_with_headtail(arrays, arrays_goals, [])
2284
+
2285
+ expected_merged = [nil, UNKNOWN_TAIL]
2286
+
2287
+ expected_unifications = [
2288
+ [:a, :c, g1, g2],
2289
+ [:b, :d, g1, g2],
2290
+ [:a, :e, g1, g3],
2291
+ [:b, :f, g1, g3],
2292
+ [:a, :g, g1, g4],
2293
+ [:b, :h, g1, g4],
2294
+ [:c, :e, g2, g3],
2295
+ [:d, :f, g2, g3],
2296
+ [:c, :g, g2, g4],
2297
+ [:d, :h, g2, g4],
2298
+ [:e, :g, g3, g4],
2299
+ [:f, :h, g3, g4],
2300
+ ]
2301
+
2302
+ assert_equal expected_merged, merged
2303
+ assert_equal expected_unifications, unifications
2304
+
2305
+ arrays_goals.each do |goal|
2306
+ assert_equal [], goal.log
2307
+ end
2308
+ end
2309
+
2310
+ end
2311
+
2312
+ describe '#builtin' do
2313
+
2314
+ it 'should create a Predicate' do
2315
+ # -- Precondition Baseline --
2316
+ assert_equal 0, Scope[:default].predicates.size
2317
+
2318
+ # -- Test --
2319
+ single_predicate = builtin :member
2320
+
2321
+ # -- Compare Result Against Baseline --
2322
+ assert_equal 1, Scope[:default].predicates.size
2323
+ assert_equal :member, Scope[:default].predicates.first.name
2324
+ assert_Predicate single_predicate, :member, []
2325
+ end
2326
+
2327
+ it 'should define a method to create Arguments for solving' do
2328
+ Object.class_eval{
2329
+ remove_method(:member) if public_method_defined?(:member)
2330
+ }
2331
+
2332
+ refute respond_to?(:member)
2333
+
2334
+ builtin :member
2335
+
2336
+ assert respond_to?(:member)
2337
+ assert_Arguments member(:X, ['left','right']), :member, [:X, ['left','right']]
2338
+
2339
+ assert_includes methods, :member
2340
+ end
2341
+
2342
+ it 'should create multiple Predicates' do
2343
+ assert_equal 0, Scope[:default].predicates.size
2344
+
2345
+ multiple_predicates = builtin :member, :append, :is
2346
+
2347
+ assert_equal 3, Scope[:default].predicates.size
2348
+ assert_equal :member, Scope[:default].predicates[0].name
2349
+ assert_equal :append, Scope[:default].predicates[1].name
2350
+ assert_equal :is, Scope[:default].predicates[2].name
2351
+ assert_instance_of Array, multiple_predicates
2352
+ assert_equal 3, multiple_predicates.size
2353
+ assert_Predicate multiple_predicates[0], :member, []
2354
+ assert_Predicate multiple_predicates[1], :append, []
2355
+ assert_Predicate multiple_predicates[2], :is, []
2356
+ end
2357
+
2358
+ it 'should define multiple methods to create Arguments for solving' do
2359
+ Object.class_eval{
2360
+ remove_method(:member) if public_method_defined?(:member)
2361
+ remove_method(:append) if public_method_defined?(:append)
2362
+ }
2363
+
2364
+ refute respond_to?(:member)
2365
+ refute respond_to?(:append)
2366
+
2367
+ builtin :member, :append
2368
+
2369
+ assert respond_to?(:member)
2370
+ assert respond_to?(:append)
2371
+ assert_Arguments member(:X, :L), :member, [:X, :L]
2372
+ assert_Arguments append(:A, :B, :AB), :append, [:A, :B, :AB]
2373
+ end
2374
+
2375
+ it 'should define a method in a base class as an instance method and a class method' do
2376
+ Object.class_eval{
2377
+ remove_method(:member) if public_method_defined?(:member)
2378
+ }
2379
+
2380
+ class Base2 ; end
2381
+
2382
+ refute_includes Base2.methods, :member
2383
+ refute_includes Base2.instance_methods, :member
2384
+
2385
+ builtin :member, class_base: Base2
2386
+
2387
+ assert_includes Base2.methods, :member
2388
+ assert_includes Base2.instance_methods, :member
2389
+
2390
+ refute_includes methods, :member
2391
+
2392
+ class Base2
2393
+ def process
2394
+ member(:X, [4,5,6]).solve_for(:X)
2395
+ end
2396
+ end
2397
+
2398
+ assert_solutions Base2.member(:X, [1,2,3]), [
2399
+ { X: 1 },
2400
+ { X: 2 },
2401
+ { X: 3 },
2402
+ ]
2403
+
2404
+ assert_equal [4,5,6], Base2.new.process
2405
+ end
2406
+
2407
+ end
2408
+
2409
+ describe '#_' do
2410
+
2411
+ let(:goal) { new_goal :p, :x, :y }
2412
+
2413
+ it 'should return a different variable on each call' do
2414
+ # _ is overridden by MiniTest
2415
+ variable1 = Porolog._
2416
+ variable2 = Porolog._
2417
+ variable3 = Porolog._
2418
+
2419
+ assert_equal :_a, variable1
2420
+ assert_equal :_b, variable2
2421
+ assert_equal :_c, variable3
2422
+
2423
+ refute_equal variable1, variable2
2424
+ refute_equal variable2, variable3
2425
+ refute_equal variable3, variable1
2426
+ end
2427
+
65
2428
  end
66
2429
 
67
2430
  end