abst_int 0.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.coveralls.yml +1 -0
- data/.gitignore +22 -0
- data/.pryrc +18 -0
- data/.rspec +2 -0
- data/.ruby-version +1 -0
- data/.travis.yml +6 -0
- data/Gemfile +8 -0
- data/LICENSE.txt +22 -0
- data/README.md +34 -0
- data/Rakefile +12 -0
- data/abst_int.gemspec +26 -0
- data/lib/abst_int.rb +83 -0
- data/lib/abst_int/calculus_model/dfa.rb +287 -0
- data/lib/abst_int/calculus_model/nfa.rb +81 -0
- data/lib/abst_int/collection.rb +32 -0
- data/lib/abst_int/integer.rb +35 -0
- data/lib/abst_int/or_set.rb +105 -0
- data/lib/abst_int/set.rb +136 -0
- data/lib/abst_int/term.rb +125 -0
- data/lib/abst_int/variable.rb +18 -0
- data/lib/abst_int/version.rb +3 -0
- data/lib/fixnum.rb +10 -0
- data/spec/abst_int_spec.rb +37 -0
- data/spec/spec_helper.rb +94 -0
- metadata +141 -0
@@ -0,0 +1,81 @@
|
|
1
|
+
require "set"
|
2
|
+
require "abst_int/calculus_model/dfa"
|
3
|
+
|
4
|
+
module AbstInt::CalculusModel
|
5
|
+
class Nfa
|
6
|
+
def initialize
|
7
|
+
@states = Set.new
|
8
|
+
@initial_states = Set.new
|
9
|
+
@final_states = Set.new
|
10
|
+
@inputs = Set.new
|
11
|
+
@transition = {}
|
12
|
+
end
|
13
|
+
|
14
|
+
def add_initial state
|
15
|
+
@states << state
|
16
|
+
@initial_states << state
|
17
|
+
end
|
18
|
+
|
19
|
+
def add_final state
|
20
|
+
@states << state
|
21
|
+
@final_states << state
|
22
|
+
end
|
23
|
+
|
24
|
+
# [state] 1,2,3
|
25
|
+
# [input] 'a', 'b', 'c'
|
26
|
+
def add_trans state1, input, state2
|
27
|
+
@states << state1 << state2
|
28
|
+
@inputs << input
|
29
|
+
@transition[state1] ||= {}
|
30
|
+
@transition[state1][input] ||= Set.new
|
31
|
+
@transition[state1][input] << state2
|
32
|
+
end
|
33
|
+
|
34
|
+
def exists_initial?
|
35
|
+
not @initial_states.empty?
|
36
|
+
end
|
37
|
+
|
38
|
+
def to_dfa
|
39
|
+
dfa = AbstInt::CalculusModel::Dfa.new
|
40
|
+
dfa.set_initial @initial_states
|
41
|
+
dfa = generate_dfa dfa, @initial_states, Set.new
|
42
|
+
dfa.each_states do |states|
|
43
|
+
dfa.add_final states unless (states & @final_states).empty?
|
44
|
+
end
|
45
|
+
return dfa
|
46
|
+
end
|
47
|
+
|
48
|
+
def to_s
|
49
|
+
puts "[states]"
|
50
|
+
@states.each {|state| puts "#{state.is_a?(Array) ? state.map(&:to_s) : state}, " }
|
51
|
+
puts "[initial_states]"
|
52
|
+
@initial_states.each {|state| puts "#{state.is_a?(Array) ? state.map(&:to_s) : state}, " }
|
53
|
+
puts "[final_states]"
|
54
|
+
@final_states.each {|state| puts "#{state.is_a?(Array) ? state.map(&:to_s) : state}, " }
|
55
|
+
puts "[inputs]"
|
56
|
+
@inputs.each {|input| puts "#{input}, " }
|
57
|
+
puts "[transition]"
|
58
|
+
@transition.each do |state1, value|
|
59
|
+
value.each do |input, value|
|
60
|
+
value.each do |state2|
|
61
|
+
puts "#{state1.is_a?(Array) ? state1.map(&:to_s) : state1}, #{input}, #{state2.is_a?(Array) ? state2.map(&:to_s) : state2}"
|
62
|
+
end if value
|
63
|
+
end if value
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
private
|
68
|
+
def generate_dfa dfa, states, state_setset
|
69
|
+
return dfa if state_setset.include? states
|
70
|
+
old_dfa = dfa.dup
|
71
|
+
@inputs.each do |input|
|
72
|
+
next_states = states.inject(Set.new) do |result, state|
|
73
|
+
@transition.try(:[], state).try(:[], input) ? result + (@transition[state][input]) : result
|
74
|
+
end
|
75
|
+
dfa.add_trans states, input, next_states
|
76
|
+
dfa = generate_dfa dfa, next_states, (state_setset << states)
|
77
|
+
end
|
78
|
+
return dfa
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
class AbstInt::Collection < BasicObject
|
2
|
+
class NilClass
|
3
|
+
end
|
4
|
+
|
5
|
+
def initialize args
|
6
|
+
@objects = args
|
7
|
+
end
|
8
|
+
|
9
|
+
def == *args
|
10
|
+
self.method_missing :==, *args
|
11
|
+
end
|
12
|
+
|
13
|
+
def != *args
|
14
|
+
self.method_missing :"!=", *args
|
15
|
+
end
|
16
|
+
|
17
|
+
def equal? *args
|
18
|
+
self.method_missing :equal?, *args
|
19
|
+
end
|
20
|
+
|
21
|
+
def method_missing method_name, *args
|
22
|
+
result = ::AbstInt::Collection::NilClass.new
|
23
|
+
@objects.each do |object|
|
24
|
+
if result.is_a? ::AbstInt::Collection::NilClass
|
25
|
+
result = object.send(method_name, *args)
|
26
|
+
else
|
27
|
+
raise ::AbstInt::MultiResultError, "There are more results than one." unless result == object.send(method_name, *args)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
return result
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
require "abst_int"
|
2
|
+
|
3
|
+
class AbstInt::Integer
|
4
|
+
def initialize abst_int
|
5
|
+
@abst_int = abst_int
|
6
|
+
end
|
7
|
+
|
8
|
+
def + abst_int_or_int
|
9
|
+
@abst_int + abst_int_or_int
|
10
|
+
end
|
11
|
+
|
12
|
+
def - abst_int_or_int
|
13
|
+
@abst_int - abst_int_or_int
|
14
|
+
end
|
15
|
+
|
16
|
+
def * abst_int_or_int
|
17
|
+
@abst_int * abst_int_or_int
|
18
|
+
end
|
19
|
+
|
20
|
+
def / abst_int_or_int
|
21
|
+
@abst_int / abst_int_or_int
|
22
|
+
end
|
23
|
+
|
24
|
+
def % num
|
25
|
+
@abst_int % num
|
26
|
+
end
|
27
|
+
|
28
|
+
def to_s
|
29
|
+
@abst_int.to_s
|
30
|
+
end
|
31
|
+
|
32
|
+
def _terms
|
33
|
+
@abst_int.terms
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,105 @@
|
|
1
|
+
require "abst_int/set"
|
2
|
+
require "abst_int/calculus_model/nfa"
|
3
|
+
require "abst_int/collection"
|
4
|
+
|
5
|
+
class AbstInt::OrSet
|
6
|
+
def initialize coefficient = nil, be_variable = false
|
7
|
+
@elements = []
|
8
|
+
if coefficient.is_a? Fixnum
|
9
|
+
self << (AbstInt::Set.new coefficient, be_variable)
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
def + orset
|
14
|
+
return self if orset.nil?
|
15
|
+
new_orset = AbstInt::OrSet.new
|
16
|
+
orset.each do |set1|
|
17
|
+
self.each do |set2|
|
18
|
+
new_orset << (set1 + set2)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
return new_orset
|
22
|
+
end
|
23
|
+
|
24
|
+
def - orset
|
25
|
+
self + orset * AbstInt::OrSet.new(-1)
|
26
|
+
end
|
27
|
+
|
28
|
+
def * orset
|
29
|
+
new_orset = AbstInt::OrSet.new
|
30
|
+
orset.each do |set1|
|
31
|
+
self.each do |set2|
|
32
|
+
new_orset << (set1 * set2)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
return new_orset
|
36
|
+
end
|
37
|
+
|
38
|
+
def % num
|
39
|
+
current_mod = []
|
40
|
+
self.each do |set|
|
41
|
+
mod = set % num
|
42
|
+
current_mod << mod
|
43
|
+
end
|
44
|
+
return AbstInt::Collection.new(current_mod)
|
45
|
+
end
|
46
|
+
|
47
|
+
def | orset
|
48
|
+
return self if orset.is_a? NilClass
|
49
|
+
cloned_self = self.dup
|
50
|
+
orset.each do |set|
|
51
|
+
cloned_self << set
|
52
|
+
end
|
53
|
+
return cloned_self
|
54
|
+
end
|
55
|
+
|
56
|
+
def & orset
|
57
|
+
left_dfa = (self.to_nfa.to_dfa)
|
58
|
+
right_dfa = (orset.to_nfa.to_dfa)
|
59
|
+
result = left_dfa & right_dfa
|
60
|
+
result = result.to_orset
|
61
|
+
return result
|
62
|
+
end
|
63
|
+
|
64
|
+
def not
|
65
|
+
return self.to_nfa.to_dfa.not.to_orset
|
66
|
+
end
|
67
|
+
|
68
|
+
def star
|
69
|
+
new_orset = AbstInt::OrSet.new
|
70
|
+
self.each do |set|
|
71
|
+
new_orset << set.star
|
72
|
+
end
|
73
|
+
return new_orset
|
74
|
+
end
|
75
|
+
|
76
|
+
def to_s
|
77
|
+
@elements.map{|element| element.to_s}.join("|")
|
78
|
+
end
|
79
|
+
|
80
|
+
protected
|
81
|
+
def to_nfa
|
82
|
+
nfa = AbstInt::CalculusModel::Nfa.new
|
83
|
+
self.each do |set|
|
84
|
+
nfa = set.generate_nfa nfa
|
85
|
+
end
|
86
|
+
return nfa
|
87
|
+
end
|
88
|
+
|
89
|
+
def each &block
|
90
|
+
@elements.each(&block)
|
91
|
+
end
|
92
|
+
|
93
|
+
def << set
|
94
|
+
expanded = false
|
95
|
+
@elements.each { |self_set|
|
96
|
+
next if expanded
|
97
|
+
expanded_set = self_set.expand(set) || set.expand(self_set)
|
98
|
+
next if expanded_set.nil?
|
99
|
+
expanded = true
|
100
|
+
@elements = @elements - [self_set]
|
101
|
+
self << expanded_set
|
102
|
+
}
|
103
|
+
@elements << set unless expanded
|
104
|
+
end
|
105
|
+
end
|
data/lib/abst_int/set.rb
ADDED
@@ -0,0 +1,136 @@
|
|
1
|
+
require "abst_int/term"
|
2
|
+
require "abst_int/calculus_model/nfa"
|
3
|
+
|
4
|
+
class AbstInt::Set
|
5
|
+
def initialize coefficient = nil, be_variable = false
|
6
|
+
@elements = []
|
7
|
+
if coefficient.is_a? Fixnum
|
8
|
+
self << (AbstInt::Term.new coefficient, be_variable)
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
def + set
|
13
|
+
cloned_self = self.dup
|
14
|
+
set.each do |term|
|
15
|
+
cloned_self << term
|
16
|
+
end
|
17
|
+
return cloned_self
|
18
|
+
end
|
19
|
+
|
20
|
+
def - set
|
21
|
+
self + set * AbstInt::Set.new(-1)
|
22
|
+
end
|
23
|
+
|
24
|
+
def * set
|
25
|
+
result = AbstInt::Set.new
|
26
|
+
cloned_self = self.dup
|
27
|
+
cloned_self.each do |self_term|
|
28
|
+
set.each do |term|
|
29
|
+
result << (self_term * term)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
return result
|
33
|
+
end
|
34
|
+
|
35
|
+
def % num
|
36
|
+
@elements.inject(0){|result, term| [result, term % num].max }
|
37
|
+
end
|
38
|
+
|
39
|
+
def star
|
40
|
+
result = AbstInt::Set.new
|
41
|
+
self.each do |term|
|
42
|
+
result << term.star
|
43
|
+
end
|
44
|
+
return result
|
45
|
+
end
|
46
|
+
|
47
|
+
def to_s
|
48
|
+
@elements.map{|element| element.to_s}.join("+")
|
49
|
+
end
|
50
|
+
|
51
|
+
def generate_nfa nfa
|
52
|
+
self.each do |term|
|
53
|
+
# 各termごとの設定
|
54
|
+
nfa = term.generate_nfa nfa, self
|
55
|
+
end
|
56
|
+
unless nfa.exists_initial?
|
57
|
+
nfa.add_initial self
|
58
|
+
nfa.add_final self
|
59
|
+
end
|
60
|
+
return nfa
|
61
|
+
end
|
62
|
+
|
63
|
+
def include? set
|
64
|
+
# setの変数に0を代入して計算
|
65
|
+
set_offset = set.calc 0
|
66
|
+
# その値がselfに含まれているか確認
|
67
|
+
input_string = set_offset < 0 ? "b" * (- set_offset) : "a" * set_offset
|
68
|
+
return false unless self.to_nfa.to_dfa.accept? input_string
|
69
|
+
# 含まれているならば、変数項の係数の包含関係をチェック
|
70
|
+
set.each do |term|
|
71
|
+
next unless term.variable_exists?
|
72
|
+
include_check = false
|
73
|
+
self.each do |self_term|
|
74
|
+
next unless self_term.variable_exists?
|
75
|
+
include_check = include_check || self_term.include?(term)
|
76
|
+
end
|
77
|
+
return false unless include_check
|
78
|
+
end
|
79
|
+
return true
|
80
|
+
end
|
81
|
+
|
82
|
+
def expand set
|
83
|
+
return self if self.include? set
|
84
|
+
return nil unless self.check_lank == 1 && set.check_lank == 0
|
85
|
+
set_offset = set.calc 0
|
86
|
+
if self.calc(-1) == set_offset
|
87
|
+
new_set = AbstInt::Set.new
|
88
|
+
new_set << AbstInt::Term.new(set_offset)
|
89
|
+
self.each do |term|
|
90
|
+
new_set << term if term.variable_exists?
|
91
|
+
end
|
92
|
+
return new_set
|
93
|
+
else
|
94
|
+
return nil
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
def check_lank
|
99
|
+
lank = 0
|
100
|
+
self.each do |term|
|
101
|
+
lank += 1 if term.variable_exists?
|
102
|
+
end
|
103
|
+
return lank
|
104
|
+
end
|
105
|
+
|
106
|
+
protected
|
107
|
+
def each &block
|
108
|
+
@elements.each(&block)
|
109
|
+
end
|
110
|
+
|
111
|
+
def << term
|
112
|
+
new_elements = []
|
113
|
+
added_flag = false
|
114
|
+
@elements.each do |element|
|
115
|
+
if (element =~ term) && (not added_flag)
|
116
|
+
new_elements << (element + term)
|
117
|
+
added_flag = true
|
118
|
+
else
|
119
|
+
new_elements << element
|
120
|
+
end
|
121
|
+
end
|
122
|
+
new_elements << term unless added_flag
|
123
|
+
@elements = new_elements
|
124
|
+
return nil
|
125
|
+
end
|
126
|
+
|
127
|
+
def to_nfa
|
128
|
+
nfa = AbstInt::CalculusModel::Nfa.new
|
129
|
+
nfa = self.generate_nfa nfa
|
130
|
+
return nfa
|
131
|
+
end
|
132
|
+
|
133
|
+
def calc num
|
134
|
+
@elements.inject(0){|sum, x| sum + (x.calc num)}
|
135
|
+
end
|
136
|
+
end
|
@@ -0,0 +1,125 @@
|
|
1
|
+
require "abst_int/variable"
|
2
|
+
|
3
|
+
class AbstInt::Term
|
4
|
+
|
5
|
+
def initialize coefficient = nil, be_variable = false
|
6
|
+
@coefficient = coefficient || 1
|
7
|
+
@variables = []
|
8
|
+
self << AbstInt::Variable.new if be_variable
|
9
|
+
end
|
10
|
+
|
11
|
+
def << variable
|
12
|
+
@variables << variable
|
13
|
+
@variables.sort!
|
14
|
+
end
|
15
|
+
|
16
|
+
# like terms check
|
17
|
+
def =~ term
|
18
|
+
return false unless @variables.length == term.variables.length
|
19
|
+
@variables.each_with_index do |variable, i|
|
20
|
+
return false unless variable.id == term.variables[i].id
|
21
|
+
end
|
22
|
+
return true
|
23
|
+
end
|
24
|
+
|
25
|
+
# collect like terms
|
26
|
+
def + term
|
27
|
+
raise "this is not like terms." unless self =~ term
|
28
|
+
cloned_term = self.clone
|
29
|
+
cloned_term.coefficient += term.coefficient
|
30
|
+
return cloned_term
|
31
|
+
end
|
32
|
+
|
33
|
+
def * abst_int_or_term
|
34
|
+
cloned_term = self.clone
|
35
|
+
term = abst_int_or_term
|
36
|
+
cloned_term.coefficient *= term.coefficient
|
37
|
+
term.variables.each do |variable|
|
38
|
+
cloned_term << variable
|
39
|
+
end
|
40
|
+
return cloned_term
|
41
|
+
end
|
42
|
+
|
43
|
+
def % num
|
44
|
+
if @variables.empty?
|
45
|
+
return (@coefficient % num)
|
46
|
+
else
|
47
|
+
raise AbstInt::MultiResultError, "There are more results than one." if @coefficient % num != 0
|
48
|
+
return 0
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
def star
|
53
|
+
cloned_term = AbstInt::Term.new
|
54
|
+
cloned_term.coefficient = @coefficient
|
55
|
+
cloned_term.variables = @variables
|
56
|
+
cloned_term.variables = cloned_term.variables + [AbstInt::Variable.new] if @variables.empty? && @coefficient != 0
|
57
|
+
return cloned_term
|
58
|
+
end
|
59
|
+
|
60
|
+
def generate_nfa nfa, set
|
61
|
+
if @variables.empty?
|
62
|
+
if @coefficient == 0
|
63
|
+
elsif @coefficient < 0
|
64
|
+
nfa.add_initial [self, @coefficient]
|
65
|
+
nfa.add_final set
|
66
|
+
state_proc = proc { |x| (x == 0) ? set : [self, x] }
|
67
|
+
(@coefficient...0).each do |i|
|
68
|
+
nfa.add_trans state_proc.call(i), 'b', state_proc.call(i+1)
|
69
|
+
end
|
70
|
+
else
|
71
|
+
nfa.add_initial set
|
72
|
+
nfa.add_final [self, @coefficient]
|
73
|
+
state_proc = proc { |x| (x == 0) ? set : [self, x] }
|
74
|
+
(0...@coefficient).each do |i|
|
75
|
+
nfa.add_trans state_proc.call(i), 'a', state_proc.call(i+1)
|
76
|
+
end
|
77
|
+
end
|
78
|
+
else
|
79
|
+
state_proc = proc { |x| (x == 0 || x == @coefficient) ? set : [self, x] }
|
80
|
+
alphabet = @coefficient < 0 ? 'b' : 'a'
|
81
|
+
unsigned_coefficient = @coefficient < 0 ? -@coefficient : @coefficient
|
82
|
+
@coefficient.times do |i|
|
83
|
+
nfa.add_trans state_proc.call(i), alphabet, state_proc.call(i+1)
|
84
|
+
end
|
85
|
+
end
|
86
|
+
return nfa
|
87
|
+
end
|
88
|
+
|
89
|
+
# for debug
|
90
|
+
def to_s
|
91
|
+
"#{@coefficient}#{@variables.map{|var| var.to_s}.join}"
|
92
|
+
end
|
93
|
+
|
94
|
+
def calc num
|
95
|
+
return @variables.empty? ? @coefficient : @coefficient * num
|
96
|
+
end
|
97
|
+
|
98
|
+
def variable_exists?
|
99
|
+
return not(@variables.empty?)
|
100
|
+
end
|
101
|
+
|
102
|
+
# 一旦、変数ありterm同士の包含比較
|
103
|
+
def include? term
|
104
|
+
return false unless self.variable_exists? && term.variable_exists?
|
105
|
+
return true if term.coefficient % self.coefficient == 0
|
106
|
+
return false
|
107
|
+
end
|
108
|
+
|
109
|
+
protected
|
110
|
+
def coefficient
|
111
|
+
@coefficient
|
112
|
+
end
|
113
|
+
|
114
|
+
def coefficient= coefficient
|
115
|
+
@coefficient = coefficient
|
116
|
+
end
|
117
|
+
|
118
|
+
def variables
|
119
|
+
@variables
|
120
|
+
end
|
121
|
+
|
122
|
+
def variables= variables
|
123
|
+
@variables = variables
|
124
|
+
end
|
125
|
+
end
|