porolog 0.0.7 → 0.0.8

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