@@ -53,16 +53,16 @@ module Shards
53
53
@clauses << clause
54
54
end
55
55
56
- protected def literal_to_s (literal : Literal ) : String
56
+ private def literal_to_s (literal : Literal ) : String
57
57
String .build { |str | literal_to_s(str, literal) }
58
58
end
59
59
60
- protected def literal_to_s (io : IO , literal : Literal ) : Nil
60
+ private def literal_to_s (io : IO , literal : Literal ) : Nil
61
61
io << '~' unless literal & 1 == 0
62
62
io << @variables [literal >> 1 ]
63
63
end
64
64
65
- protected def clause_to_s (clause : Clause ) : String
65
+ private def clause_to_s (clause : Clause ) : String
66
66
String .build do |str |
67
67
clause.each_with_index do |literal , index |
68
68
str << ' ' unless index == 0
@@ -71,7 +71,7 @@ module Shards
71
71
end
72
72
end
73
73
74
- protected def assignment_to_s (assignment , brief = false )
74
+ private def assignment_to_s (assignment , brief = false )
75
75
String .build do |str |
76
76
assignment.each_with_index do |a , index |
77
77
if a.selected?
@@ -83,33 +83,47 @@ module Shards
83
83
end
84
84
end
85
85
86
- protected def to_variables (assignment , brief = false )
87
- assignment.each_with_index.compact_map do |(a , index )|
86
+ private def to_variables (result , assignment , brief = false )
87
+ result.clear
88
+
89
+ assignment.each_with_index do |a , index |
88
90
if a.selected?
89
- @variables [index]
91
+ result << @variables [index]
90
92
elsif ! brief && a.not_selected?
91
- " ~#{ @variables [index]} "
93
+ result << " ~#{ @variables [index]} "
92
94
end
93
- end .to_a
95
+ end
94
96
end
95
97
96
- # Solves SAT and yields solutions.
98
+ # Solves SAT and yields proposed solution.
99
+ #
100
+ # Reuses the yielded array for performance reasons (avoids many
101
+ # allocations); you must duplicate the array if you want to memorize a
102
+ # solution. For example:
103
+ #
104
+ # ```
105
+ # solution = nil
106
+ # ast.solve { |proposal| solution = proposal.dup }
107
+ # ```
97
108
def solve (brief = true , verbose = false ) : Nil
98
109
watchlist = setup_watchlist
99
110
assignment = Array (Assignment ).new(@variables .size) { Assignment ::UNDEFINED }
100
111
112
+ result = [] of String
113
+
101
114
solve(watchlist, assignment, 0 , verbose) do |solution |
102
- yield to_variables(solution, brief)
115
+ to_variables(result, solution, brief)
116
+ yield result
103
117
end
104
118
end
105
119
106
120
# Iteratively solve SAT by assigning to variables d, d+1, ..., n-1.
107
121
# Assumes variables 0, ..., d-1 are assigned so far.
108
122
private def solve (watchlist , assignment , d , verbose )
109
- # The state list wil keep track of what values for which variables
110
- # we have tried so far. A value of 0 means nothing has been tried yet,
111
- # a value of 1 means False has been tried but not True, 2 means True
112
- # but not False, and 3 means both have been tried.
123
+ # The state list keeps track of what values for which variables we have
124
+ # tried so far. A value of 0 means nothing has been tried yet, a value of
125
+ # 1 means False has been tried but not True, 2 means True but not False,
126
+ # and 3 means both have been tried.
113
127
n = @variables .size
114
128
state = Array (Literal ).new(n) { Literal .new(0 ) }
115
129
@@ -120,7 +134,7 @@ module Shards
120
134
next
121
135
end
122
136
123
- # Let's try assigning a value to v . Here would be the place to insert
137
+ # Let's try assigning a value to 'v' . Here would be the place to insert
124
138
# heuristics of which value to try first.
125
139
tried_something = false
126
140
@@ -130,7 +144,7 @@ module Shards
130
144
131
145
tried_something = true
132
146
133
- # set the bit indicating a has been tried for d :
147
+ # set the bit indicating 'a' has been tried for 'd' :
134
148
state[d] |= 1 << a
135
149
assignment[d] = Assignment .from_value(a)
136
150
@@ -174,8 +188,8 @@ module Shards
174
188
175
189
# Updates the watch list after literal 'false_literal' was just assigned
176
190
# `false`, by making any clause watching false_literal watch something else.
177
- # Returns False it is impossible to do so, meaning a clause is contradicted by
178
- # the current assignment.
191
+ # Returns `false` if it's impossible to do so, meaning a clause is
192
+ # contradicted by the current assignment.
179
193
private def update_watchlist (watchlist , false_literal , assignment , verbose )
180
194
while clause = watchlist[false_literal].first?
181
195
found_alternative = false
0 commit comments