factbase 0.0.50 → 0.0.52

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.
@@ -0,0 +1,95 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Copyright (c) 2024 Yegor Bugayenko
4
+ #
5
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ # of this software and associated documentation files (the 'Software'), to deal
7
+ # in the Software without restriction, including without limitation the rights
8
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ # copies of the Software, and to permit persons to whom the Software is
10
+ # furnished to do so, subject to the following conditions:
11
+ #
12
+ # The above copyright notice and this permission notice shall be included in all
13
+ # copies or substantial portions of the Software.
14
+ #
15
+ # THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFINGEMENT. IN NO EVENT SHALL THE
18
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ # SOFTWARE.
22
+
23
+ require_relative '../../factbase'
24
+
25
+ # Logical terms.
26
+ #
27
+ # Author:: Yegor Bugayenko (yegor256@gmail.com)
28
+ # Copyright:: Copyright (c) 2024 Yegor Bugayenko
29
+ # License:: MIT
30
+ module Factbase::Term::Logical
31
+ def always(_fact, _maps)
32
+ assert_args(0)
33
+ true
34
+ end
35
+
36
+ def never(_fact, _maps)
37
+ assert_args(0)
38
+ false
39
+ end
40
+
41
+ def not(fact, maps)
42
+ assert_args(1)
43
+ !_only_bool(the_values(0, fact, maps), 0)
44
+ end
45
+
46
+ def or(fact, maps)
47
+ (0..@operands.size - 1).each do |i|
48
+ return true if _only_bool(the_values(i, fact, maps), i)
49
+ end
50
+ false
51
+ end
52
+
53
+ def and(fact, maps)
54
+ (0..@operands.size - 1).each do |i|
55
+ return false unless _only_bool(the_values(i, fact, maps), i)
56
+ end
57
+ true
58
+ end
59
+
60
+ def and_or_simplify
61
+ strs = []
62
+ ops = []
63
+ @operands.each do |o|
64
+ o = o.simplify
65
+ s = o.to_s
66
+ next if strs.include?(s)
67
+ strs << s
68
+ ops << o
69
+ end
70
+ return ops[0] if ops.size == 1
71
+ Factbase::Term.new(@op, ops)
72
+ end
73
+
74
+ def and_simplify
75
+ and_or_simplify
76
+ end
77
+
78
+ def or_simplify
79
+ and_or_simplify
80
+ end
81
+
82
+ def when(fact, maps)
83
+ assert_args(2)
84
+ a = @operands[0]
85
+ b = @operands[1]
86
+ !a.evaluate(fact, maps) || (a.evaluate(fact, maps) && b.evaluate(fact, maps))
87
+ end
88
+
89
+ def _only_bool(val, pos)
90
+ val = val[0] if val.is_a?(Array)
91
+ return false if val.nil?
92
+ return val if val.is_a?(TrueClass) || val.is_a?(FalseClass)
93
+ raise "Boolean expected, while #{val.class} received from #{@operands[pos]}"
94
+ end
95
+ end
@@ -0,0 +1,91 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Copyright (c) 2024 Yegor Bugayenko
4
+ #
5
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ # of this software and associated documentation files (the 'Software'), to deal
7
+ # in the Software without restriction, including without limitation the rights
8
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ # copies of the Software, and to permit persons to whom the Software is
10
+ # furnished to do so, subject to the following conditions:
11
+ #
12
+ # The above copyright notice and this permission notice shall be included in all
13
+ # copies or substantial portions of the Software.
14
+ #
15
+ # THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFINGEMENT. IN NO EVENT SHALL THE
18
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ # SOFTWARE.
22
+
23
+ require_relative '../../factbase'
24
+
25
+ # Math terms.
26
+ #
27
+ # Author:: Yegor Bugayenko (yegor256@gmail.com)
28
+ # Copyright:: Copyright (c) 2024 Yegor Bugayenko
29
+ # License:: MIT
30
+ module Factbase::Term::Math
31
+ def plus(fact, maps)
32
+ arithmetic(:+, fact, maps)
33
+ end
34
+
35
+ def minus(fact, maps)
36
+ arithmetic(:-, fact, maps)
37
+ end
38
+
39
+ def times(fact, maps)
40
+ arithmetic(:*, fact, maps)
41
+ end
42
+
43
+ def div(fact, maps)
44
+ arithmetic(:/, fact, maps)
45
+ end
46
+
47
+ def zero(fact, maps)
48
+ assert_args(1)
49
+ vv = the_values(0, fact, maps)
50
+ return false if vv.nil?
51
+ vv.any? { |v| (v.is_a?(Integer) || v.is_a?(Float)) && v.zero? }
52
+ end
53
+
54
+ def eq(fact, maps)
55
+ cmp(:==, fact, maps)
56
+ end
57
+
58
+ def lt(fact, maps)
59
+ cmp(:<, fact, maps)
60
+ end
61
+
62
+ def gt(fact, maps)
63
+ cmp(:>, fact, maps)
64
+ end
65
+
66
+ def cmp(op, fact, maps)
67
+ assert_args(2)
68
+ lefts = the_values(0, fact, maps)
69
+ return false if lefts.nil?
70
+ rights = the_values(1, fact, maps)
71
+ return false if rights.nil?
72
+ lefts.any? do |l|
73
+ l = l.floor if l.is_a?(Time) && op == :==
74
+ rights.any? do |r|
75
+ r = r.floor if r.is_a?(Time) && op == :==
76
+ l.send(op, r)
77
+ end
78
+ end
79
+ end
80
+
81
+ def arithmetic(op, fact, maps)
82
+ assert_args(2)
83
+ lefts = the_values(0, fact, maps)
84
+ return nil if lefts.nil?
85
+ raise 'Too many values at first position, one expected' unless lefts.size == 1
86
+ rights = the_values(1, fact, maps)
87
+ return nil if rights.nil?
88
+ raise 'Too many values at second position, one expected' unless rights.size == 1
89
+ lefts[0].send(op, rights[0])
90
+ end
91
+ end
@@ -0,0 +1,73 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Copyright (c) 2024 Yegor Bugayenko
4
+ #
5
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ # of this software and associated documentation files (the 'Software'), to deal
7
+ # in the Software without restriction, including without limitation the rights
8
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ # copies of the Software, and to permit persons to whom the Software is
10
+ # furnished to do so, subject to the following conditions:
11
+ #
12
+ # The above copyright notice and this permission notice shall be included in all
13
+ # copies or substantial portions of the Software.
14
+ #
15
+ # THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFINGEMENT. IN NO EVENT SHALL THE
18
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ # SOFTWARE.
22
+
23
+ require_relative '../../factbase'
24
+
25
+ # Meta terms.
26
+ #
27
+ # Author:: Yegor Bugayenko (yegor256@gmail.com)
28
+ # Copyright:: Copyright (c) 2024 Yegor Bugayenko
29
+ # License:: MIT
30
+ module Factbase::Term::Meta
31
+ def exists(fact, _maps)
32
+ assert_args(1)
33
+ !by_symbol(0, fact).nil?
34
+ end
35
+
36
+ def absent(fact, _maps)
37
+ assert_args(1)
38
+ by_symbol(0, fact).nil?
39
+ end
40
+
41
+ def size(fact, _maps)
42
+ assert_args(1)
43
+ v = by_symbol(0, fact)
44
+ return 0 if v.nil?
45
+ return 1 unless v.is_a?(Array)
46
+ v.size
47
+ end
48
+
49
+ def type(fact, _maps)
50
+ assert_args(1)
51
+ v = by_symbol(0, fact)
52
+ return 'nil' if v.nil?
53
+ v = v[0] if v.is_a?(Array) && v.size == 1
54
+ v.class.to_s
55
+ end
56
+
57
+ def nil(fact, maps)
58
+ assert_args(1)
59
+ the_values(0, fact, maps).nil?
60
+ end
61
+
62
+ def many(fact, maps)
63
+ assert_args(1)
64
+ v = the_values(0, fact, maps)
65
+ !v.nil? && v.size > 1
66
+ end
67
+
68
+ def one(fact, maps)
69
+ assert_args(1)
70
+ v = the_values(0, fact, maps)
71
+ !v.nil? && v.size == 1
72
+ end
73
+ end
@@ -0,0 +1,51 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Copyright (c) 2024 Yegor Bugayenko
4
+ #
5
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ # of this software and associated documentation files (the 'Software'), to deal
7
+ # in the Software without restriction, including without limitation the rights
8
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ # copies of the Software, and to permit persons to whom the Software is
10
+ # furnished to do so, subject to the following conditions:
11
+ #
12
+ # The above copyright notice and this permission notice shall be included in all
13
+ # copies or substantial portions of the Software.
14
+ #
15
+ # THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFINGEMENT. IN NO EVENT SHALL THE
18
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ # SOFTWARE.
22
+
23
+ require_relative '../../factbase'
24
+
25
+ # Ordering terms.
26
+ #
27
+ # Author:: Yegor Bugayenko (yegor256@gmail.com)
28
+ # Copyright:: Copyright (c) 2024 Yegor Bugayenko
29
+ # License:: MIT
30
+ module Factbase::Term::Ordering
31
+ def prev(fact, maps)
32
+ assert_args(1)
33
+ before = @prev
34
+ v = the_values(0, fact, maps)
35
+ @prev = v
36
+ before
37
+ end
38
+
39
+ def unique(fact, _maps)
40
+ @uniques = [] if @uniques.nil?
41
+ assert_args(1)
42
+ vv = by_symbol(0, fact)
43
+ return false if vv.nil?
44
+ vv = [vv] unless vv.is_a?(Array)
45
+ vv.each do |v|
46
+ return false if @uniques.include?(v)
47
+ @uniques << v
48
+ end
49
+ true
50
+ end
51
+ end
@@ -0,0 +1,41 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Copyright (c) 2024 Yegor Bugayenko
4
+ #
5
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ # of this software and associated documentation files (the 'Software'), to deal
7
+ # in the Software without restriction, including without limitation the rights
8
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ # copies of the Software, and to permit persons to whom the Software is
10
+ # furnished to do so, subject to the following conditions:
11
+ #
12
+ # The above copyright notice and this permission notice shall be included in all
13
+ # copies or substantial portions of the Software.
14
+ #
15
+ # THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFINGEMENT. IN NO EVENT SHALL THE
18
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ # SOFTWARE.
22
+
23
+ require_relative '../../factbase'
24
+
25
+ # String terms.
26
+ #
27
+ # Author:: Yegor Bugayenko (yegor256@gmail.com)
28
+ # Copyright:: Copyright (c) 2024 Yegor Bugayenko
29
+ # License:: MIT
30
+ module Factbase::Term::Strings
31
+ def matches(fact, maps)
32
+ assert_args(2)
33
+ str = the_values(0, fact, maps)
34
+ return false if str.nil?
35
+ raise 'Exactly one string expected' unless str.size == 1
36
+ re = the_values(1, fact, maps)
37
+ raise 'Regexp is nil' if re.nil?
38
+ raise 'Exactly one regexp expected' unless re.size == 1
39
+ str[0].to_s.match?(re[0])
40
+ end
41
+ end
@@ -59,11 +59,11 @@ class Factbase::ToXML
59
59
  if vv.is_a?(Array)
60
60
  xml.send(:"#{k}_") do
61
61
  vv.each do |v|
62
- xml.send(:v, to_str(v))
62
+ xml.send(:v, to_str(v), t: type_of(v))
63
63
  end
64
64
  end
65
65
  else
66
- xml.send(:"#{k}_", to_str(vv))
66
+ xml.send(:"#{k}_", to_str(vv), t: type_of(vv))
67
67
  end
68
68
  end
69
69
  end
@@ -81,4 +81,8 @@ class Factbase::ToXML
81
81
  val.to_s
82
82
  end
83
83
  end
84
+
85
+ def type_of(val)
86
+ val.class.to_s[0]
87
+ end
84
88
  end
data/lib/factbase.rb CHANGED
@@ -74,12 +74,14 @@ require 'yaml'
74
74
  # It's important to use +binwrite+ and +binread+, because the content is
75
75
  # a chain of bytes, not a text.
76
76
  #
77
+ # It is NOT thread-safe!
78
+ #
77
79
  # Author:: Yegor Bugayenko (yegor256@gmail.com)
78
80
  # Copyright:: Copyright (c) 2024 Yegor Bugayenko
79
81
  # License:: MIT
80
82
  class Factbase
81
83
  # Current version of the gem (changed by .rultor.yml on every release)
82
- VERSION = '0.0.50'
84
+ VERSION = '0.0.52'
83
85
 
84
86
  # An exception that may be thrown in a transaction, to roll it back.
85
87
  class Rollback < StandardError; end
@@ -0,0 +1,76 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Copyright (c) 2024 Yegor Bugayenko
4
+ #
5
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ # of this software and associated documentation files (the 'Software'), to deal
7
+ # in the Software without restriction, including without limitation the rights
8
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ # copies of the Software, and to permit persons to whom the Software is
10
+ # furnished to do so, subject to the following conditions:
11
+ #
12
+ # The above copyright notice and this permission notice shall be included in all
13
+ # copies or substantial portions of the Software.
14
+ #
15
+ # THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFINGEMENT. IN NO EVENT SHALL THE
18
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ # SOFTWARE.
22
+
23
+ require 'minitest/autorun'
24
+ require_relative '../../../lib/factbase/term'
25
+ require_relative '../../../lib/factbase/syntax'
26
+
27
+ # Aggregates test.
28
+ # Author:: Yegor Bugayenko (yegor256@gmail.com)
29
+ # Copyright:: Copyright (c) 2024 Yegor Bugayenko
30
+ # License:: MIT
31
+ class TestAggregates < Minitest::Test
32
+ def test_aggregation
33
+ maps = [
34
+ { 'x' => [1], 'y' => [0], 'z' => [4] },
35
+ { 'x' => [2], 'y' => [42], 'z' => [3] },
36
+ { 'x' => [3], 'y' => [42], 'z' => [5] },
37
+ { 'x' => [4], 'y' => [42], 'z' => [2] },
38
+ { 'x' => [5], 'y' => [42], 'z' => [1] },
39
+ { 'x' => [8], 'y' => [0], 'z' => [6] }
40
+ ]
41
+ {
42
+ '(eq x (agg (eq y 42) (min x)))' => '(eq x 2)',
43
+ '(eq z (agg (eq y 0) (max z)))' => '(eq x 8)',
44
+ '(eq x (agg (and (eq y 42) (gt z 1)) (max x)))' => '(eq x 4)',
45
+ '(and (eq x (agg (eq y 42) (min x))) (eq z 3))' => '(eq x 2)',
46
+ '(eq x (agg (eq y 0) (nth 0 x)))' => '(eq x 1)',
47
+ '(eq x (agg (eq y 0) (first x)))' => '(eq x 1)',
48
+ '(agg (eq foo 42) (always))' => '(eq x 1)'
49
+ }.each do |q, r|
50
+ t = Factbase::Syntax.new(q).to_term
51
+ f = maps.find { |m| t.evaluate(fact(m), maps) }
52
+ assert(!f.nil?, "nothing found by: #{q}")
53
+ assert(Factbase::Syntax.new(r).to_term.evaluate(fact(f), []))
54
+ end
55
+ end
56
+
57
+ def test_empty
58
+ maps = [
59
+ { 'x' => [1], 'y' => [0], 'z' => [4] },
60
+ { 'x' => [8], 'y' => [0] }
61
+ ]
62
+ {
63
+ '(empty (eq y 42))' => true,
64
+ '(empty (eq x 1))' => false
65
+ }.each do |q, r|
66
+ t = Factbase::Syntax.new(q).to_term
67
+ assert_equal(r, t.evaluate(nil, maps), q)
68
+ end
69
+ end
70
+
71
+ private
72
+
73
+ def fact(map = {})
74
+ Factbase::Fact.new(Mutex.new, map)
75
+ end
76
+ end
@@ -0,0 +1,79 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Copyright (c) 2024 Yegor Bugayenko
4
+ #
5
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ # of this software and associated documentation files (the 'Software'), to deal
7
+ # in the Software without restriction, including without limitation the rights
8
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ # copies of the Software, and to permit persons to whom the Software is
10
+ # furnished to do so, subject to the following conditions:
11
+ #
12
+ # The above copyright notice and this permission notice shall be included in all
13
+ # copies or substantial portions of the Software.
14
+ #
15
+ # THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFINGEMENT. IN NO EVENT SHALL THE
18
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ # SOFTWARE.
22
+
23
+ require 'minitest/autorun'
24
+ require_relative '../../../lib/factbase/term'
25
+ require_relative '../../../lib/factbase/syntax'
26
+ require_relative '../../../lib/factbase/accum'
27
+
28
+ # Aliases test.
29
+ # Author:: Yegor Bugayenko (yegor256@gmail.com)
30
+ # Copyright:: Copyright (c) 2024 Yegor Bugayenko
31
+ # License:: MIT
32
+ class TestAliases < Minitest::Test
33
+ def test_aliases
34
+ maps = [
35
+ { 'x' => [1], 'y' => [0] },
36
+ { 'x' => [2], 'y' => [42] }
37
+ ]
38
+ {
39
+ '(as foo (plus x 1))' => '(exists foo)',
40
+ '(as foo (plus x y))' => '(gt foo 0)',
41
+ '(as foo (plus bar 1))' => '(absent foo)'
42
+ }.each do |q, r|
43
+ t = Factbase::Syntax.new(q).to_term
44
+ maps.each do |m|
45
+ f = Factbase::Accum.new(fact(m), {}, false)
46
+ next unless t.evaluate(f, maps)
47
+ assert(Factbase::Syntax.new(r).to_term.evaluate(f, []), "#{q} -> #{f}")
48
+ end
49
+ end
50
+ end
51
+
52
+ def test_join
53
+ maps = [
54
+ { 'x' => 1, 'y' => 0, 'z' => 4 },
55
+ { 'x' => [2], 'bar' => [44, 55, 66] }
56
+ ]
57
+ {
58
+ '(join "foo_x<=x" (gt x 1))' => '(exists foo_x)',
59
+ '(join "foo <=bar " (exists bar))' => '(and (eq foo 44) (eq foo 55))',
60
+ '(join "uuu" (eq x 1))' => '(absent uuu)',
61
+ '(join "uuu <= fff" (eq fff 1))' => '(absent uuu)'
62
+ }.each do |q, r|
63
+ t = Factbase::Syntax.new(q).to_term
64
+ maps.each do |m|
65
+ f = Factbase::Accum.new(fact(m), {}, false)
66
+ require_relative '../../../lib/factbase/looged'
67
+ f = Factbase::Looged::Fact.new(f, Loog::NULL)
68
+ next unless t.evaluate(f, maps)
69
+ assert(Factbase::Syntax.new(r).to_term.evaluate(f, []), "#{q} -> #{f} doesn't match #{r}")
70
+ end
71
+ end
72
+ end
73
+
74
+ private
75
+
76
+ def fact(map = {})
77
+ Factbase::Fact.new(Mutex.new, map)
78
+ end
79
+ end
@@ -0,0 +1,99 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Copyright (c) 2024 Yegor Bugayenko
4
+ #
5
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ # of this software and associated documentation files (the 'Software'), to deal
7
+ # in the Software without restriction, including without limitation the rights
8
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ # copies of the Software, and to permit persons to whom the Software is
10
+ # furnished to do so, subject to the following conditions:
11
+ #
12
+ # The above copyright notice and this permission notice shall be included in all
13
+ # copies or substantial portions of the Software.
14
+ #
15
+ # THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFINGEMENT. IN NO EVENT SHALL THE
18
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ # SOFTWARE.
22
+
23
+ require 'minitest/autorun'
24
+ require_relative '../../../lib/factbase/term'
25
+
26
+ # Aggregates test.
27
+ # Author:: Yegor Bugayenko (yegor256@gmail.com)
28
+ # Copyright:: Copyright (c) 2024 Yegor Bugayenko
29
+ # License:: MIT
30
+ class TestMath < Minitest::Test
31
+ def test_simple
32
+ t = Factbase::Term.new(:eq, [:foo, 42])
33
+ assert(t.evaluate(fact('foo' => [42]), []))
34
+ assert(!t.evaluate(fact('foo' => 'Hello!'), []))
35
+ assert(!t.evaluate(fact('bar' => ['Hello!']), []))
36
+ end
37
+
38
+ def test_zero
39
+ t = Factbase::Term.new(:zero, [:foo])
40
+ assert(t.evaluate(fact('foo' => [0]), []))
41
+ assert(t.evaluate(fact('foo' => [10, 5, 6, -8, 'hey', 0, 9, 'fdsf']), []))
42
+ assert(!t.evaluate(fact('foo' => [100]), []))
43
+ assert(!t.evaluate(fact('foo' => []), []))
44
+ assert(!t.evaluate(fact('bar' => []), []))
45
+ end
46
+
47
+ def test_eq
48
+ t = Factbase::Term.new(:eq, [:foo, 42])
49
+ assert(t.evaluate(fact('foo' => 42), []))
50
+ assert(t.evaluate(fact('foo' => [10, 5, 6, -8, 'hey', 42, 9, 'fdsf']), []))
51
+ assert(!t.evaluate(fact('foo' => [100]), []))
52
+ assert(!t.evaluate(fact('foo' => []), []))
53
+ assert(!t.evaluate(fact('bar' => []), []))
54
+ end
55
+
56
+ def test_eq_time
57
+ now = Time.now
58
+ t = Factbase::Term.new(:eq, [:foo, Time.parse(now.iso8601)])
59
+ assert(t.evaluate(fact('foo' => now), []))
60
+ assert(t.evaluate(fact('foo' => [now, Time.now]), []))
61
+ end
62
+
63
+ def test_lt
64
+ t = Factbase::Term.new(:lt, [:foo, 42])
65
+ assert(t.evaluate(fact('foo' => [10]), []))
66
+ assert(!t.evaluate(fact('foo' => [100]), []))
67
+ assert(!t.evaluate(fact('foo' => 100), []))
68
+ assert(!t.evaluate(fact('bar' => 100), []))
69
+ end
70
+
71
+ def test_gt
72
+ t = Factbase::Term.new(:gt, [:foo, 42])
73
+ assert(t.evaluate(fact('foo' => [100]), []))
74
+ assert(t.evaluate(fact('foo' => 100), []))
75
+ assert(!t.evaluate(fact('foo' => [10]), []))
76
+ assert(!t.evaluate(fact('foo' => 10), []))
77
+ assert(!t.evaluate(fact('bar' => 10), []))
78
+ end
79
+
80
+ def test_lt_time
81
+ t = Factbase::Term.new(:lt, [:foo, Time.now])
82
+ assert(t.evaluate(fact('foo' => [Time.now - 100]), []))
83
+ assert(!t.evaluate(fact('foo' => [Time.now + 100]), []))
84
+ assert(!t.evaluate(fact('bar' => [100]), []))
85
+ end
86
+
87
+ def test_gt_time
88
+ t = Factbase::Term.new(:gt, [:foo, Time.now])
89
+ assert(t.evaluate(fact('foo' => [Time.now + 100]), []))
90
+ assert(!t.evaluate(fact('foo' => [Time.now - 100]), []))
91
+ assert(!t.evaluate(fact('bar' => [100]), []))
92
+ end
93
+
94
+ private
95
+
96
+ def fact(map = {})
97
+ Factbase::Fact.new(Mutex.new, map)
98
+ end
99
+ end