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
@@ -0,0 +1,1340 @@
1
+ #
2
+ # test/porolog/predicate/builtin_test.rb - Test Suite for Porolog::Predicate::Builtin
3
+ #
4
+ # Luis Esteban 30 July 2020
5
+ # created
6
+ #
7
+
8
+ require_relative '../../test_helper'
9
+
10
+ describe 'Porolog' do
11
+
12
+ describe 'Predicate' do
13
+
14
+ describe 'Builtin' do
15
+
16
+ before(:all) do
17
+ reset
18
+ end
19
+
20
+ describe '#write' do
21
+
22
+ it 'should output an atom' do
23
+ assert_output 'hello' do
24
+ builtin :write
25
+
26
+ assert_solutions write('hello'), [{}]
27
+ end
28
+ end
29
+
30
+ it 'should output an integer' do
31
+ assert_output '456' do
32
+ builtin :write
33
+
34
+ assert_solutions write(456), [{}]
35
+ end
36
+ end
37
+
38
+ it 'should output an uninstantiated variable' do
39
+ assert_output ':X' do
40
+ builtin :write
41
+
42
+ assert_solutions write(:X), [{ X: nil }]
43
+ end
44
+ end
45
+
46
+ it 'should output an instantiated variable' do
47
+ assert_output '72' do
48
+ builtin :write, :is
49
+ predicate :inst
50
+
51
+ inst(:X, :Y) << [
52
+ is(:Y, :X) {|x| x + 4 },
53
+ write(:Y)
54
+ ]
55
+
56
+ assert_solutions inst(23 + 45, :Y), [{ Y: 72 }]
57
+ end
58
+ end
59
+
60
+ end
61
+
62
+ describe '#writenl' do
63
+
64
+ it 'should output an atom and a new line' do
65
+ assert_output "hello\n" do
66
+ builtin :writenl
67
+
68
+ assert_solutions writenl('hello'), [{}]
69
+ end
70
+ end
71
+
72
+ it 'should output an integer and a new line' do
73
+ assert_output "456\n" do
74
+ builtin :writenl
75
+
76
+ assert_solutions writenl(456), [{}]
77
+ end
78
+ end
79
+
80
+ it 'should output an uninstantiated variable and a new line' do
81
+ assert_output ":X\n" do
82
+ builtin :writenl
83
+
84
+ assert_solutions writenl(:X), [{ X: nil }]
85
+ end
86
+ end
87
+
88
+ it 'should output an instantiated variable and a new line' do
89
+ assert_output "72\n" do
90
+ builtin :writenl, :is
91
+ predicate :inst
92
+
93
+ inst(:X, :Y) << [
94
+ is(:Y, :X) {|x| x + 4 },
95
+ writenl(:Y)
96
+ ]
97
+
98
+ assert_solutions inst(23 + 45, :Y), [{ Y: 72 }]
99
+ end
100
+ end
101
+
102
+ end
103
+
104
+ describe '#nl' do
105
+
106
+ it 'should output a newline' do
107
+ assert_output "\n\n\n" do
108
+ builtin :nl
109
+ predicate :f, :n
110
+
111
+ n(1).fact!
112
+ n(5).fact!
113
+ n(7).fact!
114
+
115
+ f() << [
116
+ n(:X),
117
+ nl
118
+ ]
119
+
120
+ assert_solutions f(), [
121
+ {},
122
+ {},
123
+ {},
124
+ ]
125
+ end
126
+ end
127
+
128
+ end
129
+
130
+ describe '#is' do
131
+
132
+ it 'should raise an exception when not provided with a variable' do
133
+ builtin :is
134
+ predicate :is_test
135
+
136
+ is_test(:variable) << [
137
+ # :nocov:
138
+ is(6) { 6 }
139
+ # :nocov:
140
+ ]
141
+
142
+ assert_raises NonVariableError do
143
+ assert_solutions is_test(6), [{}]
144
+ end
145
+ end
146
+
147
+ it 'should not raise an exception when provided with an instantiated variable' do
148
+ builtin :is
149
+ predicate :is_test
150
+
151
+ is_test(:variable) << [
152
+ is(:variable) { 6 }
153
+ ]
154
+
155
+ assert_solutions is_test(6), [{}]
156
+ end
157
+
158
+ it 'should call the block provided and instantiate the variable to the result of the provided block' do
159
+ builtin :is
160
+ predicate :is_test
161
+
162
+ calls = []
163
+
164
+ is_test(:variable1, :variable2) << [
165
+ is(:variable1) {
166
+ calls << 'first is called'
167
+ 'first'
168
+ },
169
+ is(:variable2) {
170
+ calls << 'second is called'
171
+ 'second'
172
+ }
173
+ ]
174
+
175
+ assert_solutions is_test(:X, :Y), [{ X: 'first', Y: 'second'}]
176
+ assert_equal [
177
+ 'first is called',
178
+ 'second is called',
179
+ ], calls
180
+ end
181
+
182
+ it 'should return false when the variable cannot be instantiated' do
183
+ builtin :is
184
+ predicate :is_test
185
+
186
+ calls = []
187
+
188
+ is_test(:variable1, :variable2) << [
189
+ is(:variable1) {
190
+ calls << 'first is called'
191
+ 'first'
192
+ },
193
+ is(:variable2) {
194
+ # :nocov:
195
+ calls << 'second is called'
196
+ 'second'
197
+ # :nocov:
198
+ }
199
+ ]
200
+
201
+ assert_solutions is_test('one', 'two'), []
202
+ assert_equal [
203
+ 'first is called',
204
+ ], calls
205
+ end
206
+
207
+ end
208
+
209
+ describe '#eq' do
210
+
211
+ it 'should return no solutions when given unequal values' do
212
+ builtin :eq
213
+
214
+ assert_solutions eq([3,2,1,4], [1,2,3,4]), []
215
+ end
216
+
217
+ it 'should return a solution when given equal values' do
218
+ builtin :eq
219
+
220
+ assert_solutions eq([1,2,3,4], [1,2,3,4]), [{}]
221
+ end
222
+
223
+ it 'should return a solution with an unbound variable when given the same unbound variable' do
224
+ builtin :eq
225
+
226
+ assert_solutions eq(:X, :X), [{ X: nil}]
227
+ end
228
+
229
+ it 'should return no solutions when given two unbound variables' do
230
+ builtin :eq
231
+
232
+ assert_solutions eq(:X, :Y), []
233
+ end
234
+
235
+ it 'should return one solution with unbound variables when given given two uninstantiated variables that are bound to each other' do
236
+ builtin :eq, :is
237
+ predicate :bind_and_eq
238
+
239
+ bind_and_eq(:X, :Y) << [
240
+ is(:X, :Y) { |y| y },
241
+ eq(:X, :Y)
242
+ ]
243
+
244
+ assert_solutions bind_and_eq(:X, :Y), [{ X: nil, Y: nil }]
245
+ end
246
+
247
+ it 'should return no solutions when given two uninstantiated variables that are not bound to each other' do
248
+ builtin :eq, :is
249
+ predicate :bind_and_eq
250
+
251
+ bind_and_eq(:X, :Y) << [
252
+ is(:X, :P) { |p| p },
253
+ is(:Y, :Q) { |q| q },
254
+ eq(:X, :Y)
255
+ ]
256
+
257
+ assert_solutions bind_and_eq(:X, :Y), []
258
+ end
259
+
260
+ it 'should return no solutions when given a value and an uninstantiated variable' do
261
+ builtin :eq
262
+
263
+ assert_solutions eq([1,2,3,4], :L), []
264
+ assert_solutions eq(:L, [1,2,3,4]), []
265
+ end
266
+
267
+ end
268
+
269
+ describe '#is_eq' do
270
+
271
+ it 'should return no solutions when given unequal values' do
272
+ builtin :is_eq
273
+
274
+ assert_solutions is_eq([3,2,1,4], [1,2,3,4]), []
275
+ end
276
+
277
+ it 'should return a solution when given equal values' do
278
+ builtin :is_eq
279
+
280
+ assert_solutions is_eq([1,2,3,4], [1,2,3,4]), []
281
+ end
282
+
283
+ it 'should return a solution with an unbound variable when given the same unbound variable' do
284
+ builtin :is_eq
285
+
286
+ assert_solutions is_eq(:X, :X), [{ X: nil}]
287
+ end
288
+
289
+ it 'should return no solutions when given given two unbound variables' do
290
+ builtin :is_eq
291
+
292
+ assert_solutions is_eq(:X, :Y), [{ X: nil, Y: nil }]
293
+ end
294
+
295
+ it 'should return one solution with unbound variables when given given two uninstantiated variables that are bound to each other' do
296
+ builtin :is_eq, :is
297
+ predicate :bind_and_eq
298
+
299
+ bind_and_eq(:X, :Y) << [
300
+ is(:X, :Y) { |y| y },
301
+ is_eq(:X, :Y)
302
+ ]
303
+
304
+ assert_solutions bind_and_eq(:X, :Y), [{ X: nil, Y: nil }]
305
+ end
306
+
307
+ it 'should return no solutions when given given two uninstantiated variables that are not bound to each other' do
308
+ builtin :is_eq, :is
309
+ predicate :bind_and_eq
310
+
311
+ bind_and_eq(:X, :Y) << [
312
+ is(:X, :P) { |p| p },
313
+ is(:Y, :Q) { |q| q },
314
+ is_eq(:X, :Y)
315
+ ]
316
+
317
+ assert_solutions bind_and_eq(:X, :Y), [{ X: nil, Y: nil }]
318
+ end
319
+
320
+ it 'should not instantiate the right hand side when not given a left hand side variable' do
321
+ builtin :is_eq
322
+
323
+ assert_solutions is_eq([1,2,3,4], :L), []
324
+ end
325
+
326
+ it 'should instantiate the left hand side variable' do
327
+ builtin :is_eq
328
+
329
+ assert_solutions is_eq(:L, [1,2,3,4]), [{ L: [1,2,3,4] }]
330
+ end
331
+
332
+ end
333
+
334
+ describe '#ruby' do
335
+
336
+ it 'should execute ruby code' do
337
+ builtin :ruby
338
+ predicate :logic, :data
339
+
340
+ data(1).fact!
341
+ data(5).fact!
342
+ data(7).cut_fact!
343
+ data(9).fact!
344
+
345
+ ruby_log = []
346
+
347
+ logic(:N) << [
348
+ ruby(:N){|goal, n|
349
+ ruby_log << (n.type == :variable ? n.to_sym : n)
350
+ },
351
+ data(:N),
352
+ ruby(:N){|goal, n|
353
+ ruby_log << (n.type == :variable ? n.to_sym : n)
354
+ }
355
+ ]
356
+
357
+ assert_solutions logic(:N), [
358
+ { N: 1 },
359
+ { N: 5 },
360
+ { N: 7 },
361
+ ]
362
+ assert_equal [:N,1,5,7], ruby_log
363
+ end
364
+
365
+ it 'should execute ruby code which triggers a failure' do
366
+ builtin :ruby
367
+ predicate :logic, :data
368
+
369
+ data(1).fact!
370
+ data(5).fact!
371
+ data(7).cut_fact!
372
+ data(9).fact!
373
+
374
+ ruby_log = []
375
+
376
+ logic(:N) << [
377
+ ruby(:N){|goal, n|
378
+ ruby_log << (n.type == :variable ? n.to_sym : n)
379
+ },
380
+ data(:N),
381
+ ruby(:N){|goal, n|
382
+ ruby_log << (n.type == :variable ? n.to_sym : n)
383
+ :fail
384
+ }
385
+ ]
386
+
387
+ assert_solutions logic(:N), [
388
+ ]
389
+ assert_equal [:N,1,5,7], ruby_log
390
+ end
391
+
392
+ end
393
+
394
+ describe '#noteq' do
395
+
396
+ it 'should return no solutions when given unequal values' do
397
+ builtin :noteq
398
+
399
+ assert_solutions noteq([3,2,1,4], [1,2,3,4]), [{}]
400
+ end
401
+
402
+ it 'should return a solution when given equal values' do
403
+ builtin :noteq
404
+
405
+ assert_solutions noteq([1,2,3,4], [1,2,3,4]), []
406
+ end
407
+
408
+ it 'should return a solution with an unbound variable when given the same unbound variable' do
409
+ builtin :noteq
410
+
411
+ assert_solutions noteq(:X, :X), []
412
+ end
413
+
414
+ it 'should return no solutions when given two unbound variables' do
415
+ builtin :noteq
416
+
417
+ assert_solutions noteq(:X, :Y), [{ X: nil, Y: nil }]
418
+ end
419
+
420
+ it 'should return one solution with unbound variables when given given two uninstantiated variables that are bound to each other' do
421
+ builtin :noteq, :is
422
+ predicate :bind_and_noteq
423
+
424
+ bind_and_noteq(:X, :Y) << [
425
+ is(:X, :Y) { |y| y },
426
+ noteq(:X, :Y)
427
+ ]
428
+
429
+ assert_solutions bind_and_noteq(:X, :Y), []
430
+ end
431
+
432
+ it 'should return no solutions when given two uninstantiated variables that are not bound to each other' do
433
+ builtin :noteq, :is
434
+ predicate :bind_and_noteq
435
+
436
+ bind_and_noteq(:X, :Y) << [
437
+ is(:X, :P) { |p| p },
438
+ is(:Y, :Q) { |q| q },
439
+ noteq(:X, :Y)
440
+ ]
441
+
442
+ assert_solutions bind_and_noteq(:X, :Y), [{ X: nil, Y: nil }]
443
+ end
444
+
445
+ it 'should return a solution when given an unbound variable' do
446
+ builtin :noteq
447
+
448
+ assert_solutions noteq([1,2,3,4], :L), [{ L: nil }]
449
+ assert_solutions noteq(:L, [1,2,3,4]), [{ L: nil }]
450
+ end
451
+
452
+ end
453
+
454
+ describe '#is_noteq' do
455
+
456
+ it 'should return solutions without the exclusions' do
457
+ builtin :is_noteq
458
+
459
+ assert_solutions is_noteq(:X, [1,2,3,4,5], 2, 5), [
460
+ { X: 1, _a: [1,3,4] },
461
+ { X: 3, _a: [1,3,4] },
462
+ { X: 4, _a: [1,3,4] }
463
+ ]
464
+ end
465
+
466
+ it 'should return no solutions when the exclusions are not unique' do
467
+ builtin :is_noteq
468
+
469
+ assert_solutions is_noteq(:X, [1,2,3,4,5], 2, 5, 2), []
470
+ end
471
+
472
+ it 'should return no solutions when the target is not a variable' do
473
+ builtin :is_noteq
474
+
475
+ assert_solutions is_noteq(7, [1,2,3,4,5], 2, 5, 2), []
476
+ end
477
+
478
+ end
479
+
480
+ describe '#less' do
481
+
482
+ it 'should return no solutions when given a greater value' do
483
+ builtin :less
484
+
485
+ assert_solutions less(7, 4), []
486
+ assert_solutions less('h', 'b'), []
487
+ assert_solutions less(1.01, 1.003), []
488
+ end
489
+
490
+ it 'should return no solutions when given equal values' do
491
+ builtin :less
492
+
493
+ assert_solutions less(4, 4), []
494
+ assert_solutions less('b', 'b'), []
495
+ assert_solutions less(1.003, 1.003), []
496
+ end
497
+
498
+ it 'should return a solution when given lesser values' do
499
+ builtin :less
500
+
501
+ assert_solutions less(2, 4), [{}]
502
+ end
503
+
504
+ it 'should return no solutions with an unbound variable when given the same unbound variable' do
505
+ builtin :less
506
+
507
+ assert_solutions less(:X, :X), []
508
+ end
509
+
510
+ it 'should return no solutions when given two unbound variables' do
511
+ builtin :less
512
+
513
+ assert_solutions less(:X, :Y), []
514
+ end
515
+
516
+ end
517
+
518
+ describe '#gtr' do
519
+
520
+ it 'should return no solutions when given a lesser value' do
521
+ builtin :gtr
522
+
523
+ assert_solutions gtr(4, 7), []
524
+ assert_solutions gtr('b', 'h'), []
525
+ assert_solutions gtr(1.003, 1.01), []
526
+ end
527
+
528
+ it 'should return no solutions when given equal values' do
529
+ builtin :gtr
530
+
531
+ assert_solutions gtr(4, 4), []
532
+ assert_solutions gtr('b', 'b'), []
533
+ assert_solutions gtr(1.003, 1.003), []
534
+ end
535
+
536
+ it 'should return a solution when given greater values' do
537
+ builtin :gtr
538
+
539
+ assert_solutions gtr(4, 2), [{}]
540
+ end
541
+
542
+ it 'should return no solutions with an unbound variable when given the same unbound variable' do
543
+ builtin :gtr
544
+
545
+ assert_solutions gtr(:X, :X), []
546
+ end
547
+
548
+ it 'should return no solutions when given two unbound variables' do
549
+ builtin :gtr
550
+
551
+ assert_solutions gtr(:X, :Y), []
552
+ end
553
+
554
+ end
555
+
556
+ describe '#lesseq' do
557
+
558
+ it 'should return no solutions when given a greater value' do
559
+ builtin :lesseq
560
+
561
+ assert_solutions lesseq(7, 4), []
562
+ assert_solutions lesseq('h', 'b'), []
563
+ assert_solutions lesseq(1.01, 1.003), []
564
+ end
565
+
566
+ it 'should return a solution when given equal values' do
567
+ builtin :lesseq
568
+
569
+ assert_solutions lesseq(4, 4), [{}]
570
+ assert_solutions lesseq('b', 'b'), [{}]
571
+ assert_solutions lesseq(1.003, 1.003), [{}]
572
+ end
573
+
574
+ it 'should return a solution when given lesser values' do
575
+ builtin :lesseq
576
+
577
+ assert_solutions lesseq(2, 4), [{}]
578
+ assert_solutions lesseq('b', 'h'), [{}]
579
+ assert_solutions lesseq(1.003, 1.01), [{}]
580
+ end
581
+
582
+ it 'should return a solution with an unbound variable when given the same unbound variable' do
583
+ builtin :lesseq
584
+
585
+ assert_solutions lesseq(:X, :X), [{ X: nil }]
586
+ end
587
+
588
+ it 'should return no solutions when given two unbound variables' do
589
+ builtin :lesseq
590
+
591
+ assert_solutions lesseq(:X, :Y), []
592
+ end
593
+
594
+ it 'should return no solutions when given one unbound variable' do
595
+ builtin :lesseq
596
+
597
+ assert_solutions lesseq(:X, 11), []
598
+ end
599
+
600
+ end
601
+
602
+ describe '#gtreq' do
603
+
604
+ it 'should return no solutions when given a lesser value' do
605
+ builtin :gtreq
606
+
607
+ assert_solutions gtreq(4, 7), []
608
+ assert_solutions gtreq('b', 'h'), []
609
+ assert_solutions gtreq(1.003, 1.01), []
610
+ end
611
+
612
+ it 'should return a solution when given equal values' do
613
+ builtin :gtreq
614
+
615
+ assert_solutions gtreq(4, 4), [{}]
616
+ assert_solutions gtreq('b', 'b'), [{}]
617
+ assert_solutions gtreq(1.003, 1.003), [{}]
618
+ end
619
+
620
+ it 'should return a solution when given gtreqer values' do
621
+ builtin :gtreq
622
+
623
+ assert_solutions gtreq(4, 2), [{}]
624
+ assert_solutions gtreq('h', 'b'), [{}]
625
+ assert_solutions gtreq(1.01, 1.003), [{}]
626
+ end
627
+
628
+ it 'should return a solution with an unbound variable when given the same unbound variable' do
629
+ builtin :gtreq
630
+
631
+ assert_solutions gtreq(:X, :X), [{ X: nil }]
632
+ end
633
+
634
+ it 'should return no solutions when given two unbound variables' do
635
+ builtin :gtreq
636
+
637
+ assert_solutions gtreq(:X, :Y), []
638
+ end
639
+
640
+ it 'should return no solutions when given one unbound variable' do
641
+ builtin :gtreq
642
+
643
+ assert_solutions gtreq(:X, 11), []
644
+ end
645
+
646
+ end
647
+
648
+ describe '#length' do
649
+
650
+ it 'should return a solution for an array and an integer' do
651
+ builtin :length
652
+
653
+ assert_solutions length([1,2,3,4,5], 5), [{}]
654
+ end
655
+
656
+ it 'should return no solutions for an array and an integer that do not satisy the length' do
657
+ builtin :length
658
+
659
+ assert_solutions length([1,2,3,4,5], 2), []
660
+ end
661
+
662
+ it 'should instaniate the length of the list' do
663
+ builtin :length
664
+
665
+ assert_solutions length([1,2,3,4,5], :N), [{ N: 5 }]
666
+ end
667
+
668
+ it 'should instaniate the list' do
669
+ builtin :length
670
+
671
+ assert_solutions length(:L, 4), [
672
+ { L: [nil, nil, nil, nil], _a: nil, _b: nil, _c: nil, _d: nil }
673
+ ]
674
+ end
675
+
676
+ it 'should return no solutions for a variable array and length' do
677
+ builtin :length
678
+
679
+ assert_solutions length(:L, :N), []
680
+ end
681
+
682
+ it 'should return no solutions for invalid types' do
683
+ builtin :length
684
+
685
+ assert_solutions length(3, [1,2,3]), []
686
+ end
687
+
688
+ end
689
+
690
+ describe '#between' do
691
+
692
+ it 'should return a solution when the value is in range' do
693
+ builtin :between
694
+
695
+ assert_solutions between(3, 1, 10), [{}]
696
+ end
697
+
698
+ it 'should return no solutions when the value is not in range' do
699
+ builtin :between
700
+
701
+ assert_solutions between(-40, 1, 10), []
702
+ end
703
+
704
+ it 'should return no solutions when given an uninstantiated variable for the range' do
705
+ builtin :between
706
+
707
+ assert_solutions between(:X, 1, :Ten), []
708
+ assert_solutions between(:X, :One, 10), []
709
+ end
710
+
711
+ it 'should return solutions for instantiating' do
712
+ builtin :between
713
+
714
+ assert_solutions between(:X, 1, 10), [
715
+ { X: 1 },
716
+ { X: 2 },
717
+ { X: 3 },
718
+ { X: 4 },
719
+ { X: 5 },
720
+ { X: 6 },
721
+ { X: 7 },
722
+ { X: 8 },
723
+ { X: 9 },
724
+ { X: 10 },
725
+ ]
726
+ end
727
+
728
+ it 'should instantiate the minimum possible when the value is above the lower bound' do
729
+ builtin :between
730
+
731
+ assert_solutions between(4, 1, :upper), [{ upper: 4}]
732
+ end
733
+
734
+ it 'should instantiate the maximum possible when the value is below the upper bound' do
735
+ builtin :between
736
+
737
+ assert_solutions between(4, :lower, 10), [{ lower: 4}]
738
+ end
739
+
740
+ it 'should return no solutions the value is below the lower bound' do
741
+ builtin :between
742
+
743
+ assert_solutions between(-7, 1, :upper), []
744
+ end
745
+
746
+ it 'should return no solutions the value is above the upper bound' do
747
+ builtin :between
748
+
749
+ assert_solutions between(24, :lower, 10), []
750
+ end
751
+
752
+ it 'should instantiate both bounds when given a value' do
753
+ builtin :between
754
+
755
+ assert_solutions between(24, :lower, :upper), [{ lower: 24, upper: 24 }]
756
+ end
757
+
758
+ end
759
+
760
+ describe '#member' do
761
+
762
+ it 'should return no solutions for a non-member' do
763
+ builtin :member
764
+
765
+ assert_solutions member(5, [1,2,3,4]), []
766
+ end
767
+
768
+ it 'should return no solutions for a non-list' do
769
+ builtin :member
770
+
771
+ assert_solutions member(5, 5), []
772
+ end
773
+
774
+ it 'should return a solution for a member' do
775
+ builtin :member
776
+
777
+ assert_solutions member(3, [1,2,3,4]), [{}]
778
+ end
779
+
780
+ it 'should return a solution for an array member' do
781
+ builtin :member
782
+
783
+ assert_solutions member([2,3], [[1,2],[2,3],[3,4]]), [{}]
784
+ end
785
+
786
+ it 'should return a solution for a member' do
787
+ builtin :member
788
+
789
+ assert_solutions member(3, [1,2,3,4]), [{}]
790
+ end
791
+
792
+ it 'should return a solution for a member' do
793
+ builtin :member
794
+
795
+ assert_solutions member(3, [1,2,:C,4]), [{ C: 3 }]
796
+ end
797
+
798
+ it 'should return a solution for an array member' do
799
+ builtin :member
800
+
801
+ assert_solutions member([:A,3], [[1,2],[2,3],[3,4]]), [{ A: 2 }]
802
+ end
803
+
804
+ it 'should return a solution for an array member' do
805
+ builtin :member
806
+
807
+ assert_solutions member([2,3], [[1,:B],[:B,3],[3,4]]), [{ B: 2 }]
808
+ end
809
+
810
+ it 'should return solutions for each member' do
811
+ builtin :member
812
+
813
+ assert_solutions member(:M, [[1,2],[2,3],[3,4]]), [
814
+ { M: [1,2] },
815
+ { M: [2,3] },
816
+ { M: [3,4] },
817
+ ]
818
+ end
819
+
820
+ it 'should return limited solutions for variables' do
821
+ builtin :member
822
+
823
+ assert_solutions member(:M, :L, 12), Array.new(12){|i|
824
+ v = '_a'
825
+ { M: nil, L: [*([nil] * (i+1)), UNKNOWN_TAIL] }.merge((1..(i * (i + 1) / 2)).map{ m = v.dup ; v.succ! ; [m.to_sym, nil] }.to_h)
826
+ }
827
+ end
828
+
829
+ end
830
+
831
+ describe '#append' do
832
+
833
+ it 'should return no solutions for a non-append' do
834
+ builtin :append
835
+
836
+ assert_solutions append([1,2,3], [4,5,6], [1,2,7,4,5,6]), []
837
+ end
838
+
839
+ it 'should return a solution for a valid append' do
840
+ builtin :append
841
+
842
+ assert_solutions append([1,2,3], [4,5,6], [1,2,3,4,5,6]), [{}]
843
+ end
844
+
845
+ it 'should return no solutions for a non-list' do
846
+ builtin :append
847
+
848
+ assert_solutions append(5, 5, 55), []
849
+ end
850
+
851
+ it 'should return a solution for a valid append' do
852
+ builtin :append
853
+
854
+ assert_solutions append([1,2,3], [4,5,6], [1,2,3,4,5,6]), [{}]
855
+ end
856
+
857
+ it 'should return a solution for an array append' do
858
+ builtin :append
859
+
860
+ assert_solutions append([[1,2],[2,3],[3,4]], [[4,5],[5,6],[6,7]], [[1,2],[2,3],[3,4],[4,5],[5,6],[6,7]]), [{}]
861
+ end
862
+
863
+ it 'should instantiate prefix variable' do
864
+ builtin :append
865
+
866
+ assert_solutions append(:prefix, [[4,5],[5,6],[6,7]], [[1,2],[2,3],[3,4],[4,5],[5,6],[6,7]]), [
867
+ { prefix: [[1, 2], [2, 3], [3, 4]] }
868
+ ]
869
+ end
870
+
871
+ it 'should instantiate suffix variable' do
872
+ builtin :append
873
+
874
+ assert_solutions append([[1,2],[2,3],[3,4]], :suffix, [[1,2],[2,3],[3,4],[4,5],[5,6],[6,7]]), [
875
+ { suffix: [[4, 5], [5, 6], [6, 7]] }
876
+ ]
877
+ end
878
+
879
+ it 'should instantiate embedded variables' do
880
+ builtin :append
881
+
882
+ assert_solutions append([[:A,:B],[:C,:D],[:E,:F]], [:G,:H,:I], [[1,2],[2,3],[3,4],[4,5],[5,6],[6,7]]), [
883
+ { A: 1, B: 2, C: 2, D: 3, E: 3, F: 4, G: [4, 5], H: [5, 6], I: [6, 7] }
884
+ ]
885
+ end
886
+
887
+ it 'should instantiate embedded variables' do
888
+ builtin :append
889
+
890
+ assert_solutions append([1,2,3,4], ['apple','banana','carrot'], :L), [
891
+ { L: [1, 2, 3, 4, 'apple', 'banana', 'carrot'] }
892
+ ]
893
+ end
894
+
895
+ it 'should return solutions for all possible splits of a list' do
896
+ builtin :append
897
+
898
+ assert_solutions append(:M, :L, [1,2,3,4,5]), [
899
+ { M: [], L: [1, 2, 3, 4, 5] },
900
+ { M: [1], L: [2, 3, 4, 5] },
901
+ { M: [1, 2], L: [3, 4, 5] },
902
+ { M: [1, 2, 3], L: [4, 5] },
903
+ { M: [1, 2, 3, 4], L: [5] },
904
+ { M: [1, 2, 3, 4, 5], L: [] }
905
+ ]
906
+ end
907
+
908
+ it 'should instantiate using a head and tail for an unbound variable and an array' do
909
+ builtin :append, :is_eq
910
+ predicate :append_is_eq
911
+
912
+ append_is_eq(:A, :B, :C) << [
913
+ append(:A, :B, :C),
914
+ is_eq(:A, [1, 2, 3]),
915
+ ]
916
+
917
+ assert_solutions append_is_eq(:A, [4,5,6], :C), [
918
+ { A: [1, 2, 3], C: [1, 2, 3, 4, 5, 6] }
919
+ ]
920
+ end
921
+
922
+ it 'should instantiate using a head and tail for unbound variables' do
923
+ builtin :append, :is_eq
924
+ predicate :append_is_eq
925
+
926
+ append_is_eq(:A, :B, :C) << [
927
+ append(:A, :B, :C),
928
+ is_eq(:B, [4, 5, 6]),
929
+ ]
930
+
931
+ assert_solutions append_is_eq([1,2,3], :B, :C), [
932
+ { B: [4, 5, 6], C: [1, 2, 3, 4, 5, 6] }
933
+ ]
934
+ end
935
+
936
+ it 'should instantiate using a head and tail for unbound variables' do
937
+ builtin :append, :is_eq
938
+ predicate :append_is_eq
939
+
940
+ append_is_eq(:A, :B, :C) << [
941
+ append(:A, :B, :C),
942
+ is_eq(:A, [1, 2, 3]),
943
+ is_eq(:B, [4, 5, 6]),
944
+ ]
945
+
946
+ assert_solutions append_is_eq(:A, :B, :C), [
947
+ { A: [1, 2, 3], B: [4, 5, 6], C: [1, 2, 3, 4, 5, 6] }
948
+ ]
949
+ end
950
+
951
+ end
952
+
953
+ describe '#permutation' do
954
+
955
+ it 'returns no solutions for non-arrays' do
956
+ builtin :permutation
957
+
958
+ assert_solutions permutation('1234', '1234'), []
959
+ end
960
+
961
+ it 'returns a solution for identical arrays without variables' do
962
+ builtin :permutation
963
+
964
+ assert_solutions permutation([1,2,3,4], [1,2,3,4]), [{}]
965
+ end
966
+
967
+ it 'returns a solutions for rearranged arrays without variables' do
968
+ builtin :permutation
969
+
970
+ assert_solutions permutation([3,2,1,4], [1,2,3,4]), [{}]
971
+ end
972
+
973
+ it 'returns a solution and instantiates a variable' do
974
+ builtin :permutation
975
+
976
+ assert_solutions permutation([3,2,:A,4], [1,2,3,4]), [{ A: 1 }]
977
+ end
978
+
979
+ it 'returns solutions of permutations of instantiations' do
980
+ builtin :permutation
981
+
982
+ assert_solutions permutation([3,2,:A,:B], [1,2,3,4]), [
983
+ { A: 1, B: 4 },
984
+ { A: 4, B: 1 },
985
+ ]
986
+ end
987
+
988
+ it 'returns solutions of permutations of instantiations in the other list' do
989
+ builtin :permutation
990
+
991
+ assert_solutions permutation([3,2,1,4], [:A,2,3,:B]), [
992
+ { A: 1, B: 4 },
993
+ { A: 4, B: 1 },
994
+ ]
995
+ end
996
+
997
+ it 'infers instantiations of variables in both arrays' do
998
+ builtin :permutation
999
+
1000
+ assert_solutions permutation([3,2,:A,4], [1,2,:C,4]), [
1001
+ { A: 1, C: 3 }
1002
+ ]
1003
+ end
1004
+
1005
+ it 'infers instantiations of variables in both arrays when they represent the same value' do
1006
+ builtin :permutation
1007
+
1008
+ assert_solutions permutation([:A,2,1,4], [1,2,:C,4]), [
1009
+ { A: 1, C: 1 },
1010
+ { A: 2, C: 2 },
1011
+ { A: nil, C: nil },
1012
+ { A: 4, C: 4 }
1013
+ ]
1014
+ end
1015
+
1016
+ it 'returns all permutations of an array' do
1017
+ builtin :permutation
1018
+
1019
+ assert_solutions permutation([3,2,1,4], :L), [
1020
+ { L: [3, 2, 1, 4] },
1021
+ { L: [3, 2, 4, 1] },
1022
+ { L: [3, 1, 2, 4] },
1023
+ { L: [3, 1, 4, 2] },
1024
+ { L: [3, 4, 2, 1] },
1025
+ { L: [3, 4, 1, 2] },
1026
+ { L: [2, 3, 1, 4] },
1027
+ { L: [2, 3, 4, 1] },
1028
+ { L: [2, 1, 3, 4] },
1029
+ { L: [2, 1, 4, 3] },
1030
+ { L: [2, 4, 3, 1] },
1031
+ { L: [2, 4, 1, 3] },
1032
+ { L: [1, 3, 2, 4] },
1033
+ { L: [1, 3, 4, 2] },
1034
+ { L: [1, 2, 3, 4] },
1035
+ { L: [1, 2, 4, 3] },
1036
+ { L: [1, 4, 3, 2] },
1037
+ { L: [1, 4, 2, 3] },
1038
+ { L: [4, 3, 2, 1] },
1039
+ { L: [4, 3, 1, 2] },
1040
+ { L: [4, 2, 3, 1] },
1041
+ { L: [4, 2, 1, 3] },
1042
+ { L: [4, 1, 3, 2] },
1043
+ { L: [4, 1, 2, 3] }
1044
+ ], goals: 1
1045
+ end
1046
+
1047
+ it 'returns all permutations of an array when arguments are swapped' do
1048
+ builtin :permutation
1049
+
1050
+ assert_solutions permutation(:L, [3,2,1,4]), [
1051
+ { L: [3, 2, 1, 4] },
1052
+ { L: [3, 2, 4, 1] },
1053
+ { L: [3, 1, 2, 4] },
1054
+ { L: [3, 1, 4, 2] },
1055
+ { L: [3, 4, 2, 1] },
1056
+ { L: [3, 4, 1, 2] },
1057
+ { L: [2, 3, 1, 4] },
1058
+ { L: [2, 3, 4, 1] },
1059
+ { L: [2, 1, 3, 4] },
1060
+ { L: [2, 1, 4, 3] },
1061
+ { L: [2, 4, 3, 1] },
1062
+ { L: [2, 4, 1, 3] },
1063
+ { L: [1, 3, 2, 4] },
1064
+ { L: [1, 3, 4, 2] },
1065
+ { L: [1, 2, 3, 4] },
1066
+ { L: [1, 2, 4, 3] },
1067
+ { L: [1, 4, 3, 2] },
1068
+ { L: [1, 4, 2, 3] },
1069
+ { L: [4, 3, 2, 1] },
1070
+ { L: [4, 3, 1, 2] },
1071
+ { L: [4, 2, 3, 1] },
1072
+ { L: [4, 2, 1, 3] },
1073
+ { L: [4, 1, 3, 2] },
1074
+ { L: [4, 1, 2, 3] }
1075
+ ], goals: 1
1076
+ end
1077
+
1078
+ end
1079
+
1080
+ describe '#reverse' do
1081
+
1082
+ it 'returns no solutions for non-arrays' do
1083
+ builtin :reverse
1084
+
1085
+ assert_solutions reverse('1234', '1234'), []
1086
+ end
1087
+
1088
+ it 'returns a solution for mirrored arrays without variables' do
1089
+ builtin :reverse
1090
+
1091
+ assert_solutions reverse([1,2,3,4], [4,3,2,1]), [{}]
1092
+ end
1093
+
1094
+ it 'returns a solution and instantiates a variable' do
1095
+ builtin :reverse
1096
+
1097
+ assert_solutions reverse([4,3,:A,1], [1,2,3,4]), [{ A: 2 }]
1098
+ end
1099
+
1100
+ it 'returns a solution and instantiates multiple variables' do
1101
+ builtin :reverse
1102
+
1103
+ assert_solutions reverse([4,3,:A,:B], [1,2,3,4]), [
1104
+ { A: 2, B: 1 },
1105
+ ]
1106
+ end
1107
+
1108
+ it 'returns a solution and instantiates multiple variables in the other list' do
1109
+ builtin :reverse
1110
+
1111
+ assert_solutions reverse([4,3,2,1], [:A,2,3,:B]), [
1112
+ { A: 1, B: 4 },
1113
+ ]
1114
+ end
1115
+
1116
+ it 'infers instantiations of variables in both arrays' do
1117
+ builtin :reverse
1118
+
1119
+ assert_solutions reverse([4,3,:A,1], [1,2,:C,4]), [
1120
+ { A: 2, C: 3 }
1121
+ ]
1122
+ end
1123
+
1124
+ it 'infers instantiations of variables in both arrays when they represent the same value' do
1125
+ builtin :reverse
1126
+
1127
+ assert_solutions reverse([:A,3,2,1], [1,:A,3,:C]), [
1128
+ { A: 2, C: 2 },
1129
+ ]
1130
+ end
1131
+
1132
+ it 'allows unbound variables' do
1133
+ builtin :reverse
1134
+
1135
+ assert_solutions reverse([:A,3,2,1], :L), [
1136
+ { A: nil, L: [1,2,3,nil] },
1137
+ ]
1138
+ end
1139
+
1140
+ it 'instantiates a variable to the reversed array' do
1141
+ builtin :reverse
1142
+
1143
+ assert_solutions reverse([4,3,2,1], :L), [
1144
+ { L: [1, 2, 3, 4] },
1145
+ ], goals: 1
1146
+ end
1147
+
1148
+ it 'instantiates a variable to the reversed array when arguments are swapped' do
1149
+ builtin :reverse
1150
+
1151
+ assert_solutions reverse(:L, [4,3,2,1]), [
1152
+ { L: [1, 2, 3, 4] },
1153
+ ], goals: 1
1154
+ end
1155
+
1156
+ it 'returns no solutions for uninstantiated variables' do
1157
+ builtin :reverse
1158
+
1159
+ assert_solutions reverse(:X, :Y), [], goals: 1
1160
+ end
1161
+
1162
+ end
1163
+
1164
+ describe '#var' do
1165
+
1166
+ it 'should be unsuccessful for an atom' do
1167
+ builtin :var
1168
+
1169
+ assert_solutions var('1234'), []
1170
+ end
1171
+
1172
+ it 'should be successful for an uninstantiated variable' do
1173
+ builtin :var, :is_eq
1174
+ predicate :vartest
1175
+
1176
+ vartest(:variable) << [
1177
+ var(:variable),
1178
+ is_eq(:variable, 'value')
1179
+ ]
1180
+
1181
+ assert_solutions vartest(:V), [{ V: 'value' }]
1182
+ end
1183
+
1184
+ it 'should be successful for an uninstantiated bound variable' do
1185
+ builtin :var, :is_eq
1186
+ predicate :vartest
1187
+
1188
+ vartest(:variable) << [
1189
+ is_eq(:variable, :bound),
1190
+ var(:variable),
1191
+ is_eq(:bound, 'bound')
1192
+ ]
1193
+
1194
+ assert_solutions vartest(:V), [{ V: 'bound' }]
1195
+ end
1196
+
1197
+ it 'should be successful for an uninstantiated bound variable' do
1198
+ builtin :var, :is_eq
1199
+ predicate :vartest
1200
+
1201
+ vartest(:variable) << [
1202
+ is_eq(:variable, 'instantiated'),
1203
+ var(:variable)
1204
+ ]
1205
+
1206
+ assert_solutions vartest(:V), []
1207
+ end
1208
+
1209
+ end
1210
+
1211
+ describe '#nonvar' do
1212
+
1213
+ it 'should be successful for an atom' do
1214
+ builtin :nonvar
1215
+
1216
+ assert_solutions nonvar('1234'), [{}]
1217
+ end
1218
+
1219
+ it 'should be unsuccessful for an uninstantiated variable' do
1220
+ builtin :nonvar, :is_eq
1221
+ predicate :nonvartest
1222
+
1223
+ nonvartest(:variable) << [
1224
+ nonvar(:variable),
1225
+ is_eq(:variable, 'value')
1226
+ ]
1227
+
1228
+ assert_solutions nonvartest(:V), []
1229
+ end
1230
+
1231
+ it 'should be unsuccessful for an uninstantiated bound variable' do
1232
+ builtin :nonvar, :is_eq
1233
+ predicate :nonvartest
1234
+
1235
+ nonvartest(:variable) << [
1236
+ is_eq(:variable, :bound),
1237
+ nonvar(:variable),
1238
+ is_eq(:bound, 'bound')
1239
+ ]
1240
+
1241
+ assert_solutions nonvartest(:V), []
1242
+ end
1243
+
1244
+ it 'should be unsuccessful for an uninstantiated bound variable' do
1245
+ builtin :nonvar, :is_eq
1246
+ predicate :nonvartest
1247
+
1248
+ nonvartest(:variable) << [
1249
+ is_eq(:variable, 'instantiated'),
1250
+ nonvar(:variable)
1251
+ ]
1252
+
1253
+ assert_solutions nonvartest(:V), [{ V: 'instantiated' }]
1254
+ end
1255
+
1256
+ end
1257
+
1258
+ describe '#atom' do
1259
+
1260
+ it 'should be satisfied with a String' do
1261
+ builtin :atom
1262
+
1263
+ assert_solutions atom('banana'), [{}]
1264
+ end
1265
+
1266
+ it 'should not be satisfied with an Integer' do
1267
+ builtin :atom
1268
+
1269
+ assert_solutions atom(6), []
1270
+ end
1271
+
1272
+ it 'should not be satisfied with an Array' do
1273
+ builtin :atom
1274
+
1275
+ assert_solutions atom([]), []
1276
+ end
1277
+
1278
+ end
1279
+
1280
+ describe '#atomic' do
1281
+
1282
+ it 'should be satisfied with a String' do
1283
+ builtin :atomic
1284
+
1285
+ assert_solutions atomic('banana'), [{}]
1286
+ end
1287
+
1288
+ it 'should be satisfied with an Integer' do
1289
+ builtin :atomic
1290
+
1291
+ assert_solutions atomic(6), [{}]
1292
+ end
1293
+
1294
+ it 'should not be satisfied with an Array' do
1295
+ builtin :atomic
1296
+
1297
+ assert_solutions atomic([]), []
1298
+ end
1299
+
1300
+ it 'should not be satisfied with an uninstantiated variable' do
1301
+ builtin :atomic
1302
+
1303
+ assert_solutions atomic(:X), []
1304
+ end
1305
+
1306
+ end
1307
+
1308
+ describe '#integer' do
1309
+
1310
+ it 'should not be satisfied with a String' do
1311
+ builtin :integer
1312
+
1313
+ assert_solutions integer('banana'), []
1314
+ end
1315
+
1316
+ it 'should be satisfied with an Integer' do
1317
+ builtin :integer
1318
+
1319
+ assert_solutions integer(6), [{}]
1320
+ end
1321
+
1322
+ it 'should not be satisfied with an Array' do
1323
+ builtin :integer
1324
+
1325
+ assert_solutions integer([]), []
1326
+ end
1327
+
1328
+ it 'should not be satisfied with an uninstantiated variable' do
1329
+ builtin :integer
1330
+
1331
+ assert_solutions integer(:X), []
1332
+ end
1333
+
1334
+ end
1335
+
1336
+ end
1337
+
1338
+ end
1339
+
1340
+ end