z3 0.0.20161010 → 0.0.20161117
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +10 -12
- data/examples/crossflip +82 -0
- data/examples/mortal_coil_puzzle +105 -0
- data/examples/mortal_coil_puzzle-9.txt +6 -0
- data/examples/oneofus +102 -0
- data/examples/regexp_crossword/beginner-1.txt +5 -0
- data/examples/regexp_crossword/beginner-2.txt +5 -0
- data/examples/regexp_crossword/beginner-3.txt +5 -0
- data/examples/regexp_crossword/beginner-4.txt +5 -0
- data/examples/regexp_crossword/beginner-5.txt +5 -0
- data/examples/regexp_crossword/experienced-1.txt +9 -0
- data/examples/regexp_crossword/experienced-2.txt +11 -0
- data/examples/regexp_crossword/experienced-3.txt +11 -0
- data/examples/regexp_crossword/experienced-4.txt +11 -0
- data/examples/regexp_crossword/experienced-5.txt +11 -0
- data/examples/regexp_crossword/tutorial-1.txt +3 -0
- data/examples/regexp_crossword/tutorial-2.txt +3 -0
- data/examples/regexp_crossword/tutorial-3.txt +3 -0
- data/examples/regexp_crossword/tutorial-4.txt +4 -0
- data/examples/regexp_crossword/tutorial-5.txt +4 -0
- data/examples/regexp_crossword/tutorial-6.txt +4 -0
- data/examples/regexp_crossword/tutorial-7.txt +4 -0
- data/examples/regexp_crossword/tutorial-8.txt +4 -0
- data/examples/regexp_crossword/tutorial-9.txt +3 -0
- data/examples/regexp_crossword_solver +80 -0
- data/examples/regexp_solver +44 -0
- data/examples/regexp_string_matcher.rb +124 -0
- data/examples/simple_regexp_parser.rb +187 -0
- data/lib/z3/expr/bitvec_expr.rb +4 -0
- data/lib/z3/expr/bool_expr.rb +4 -0
- data/lib/z3/probe.rb +4 -0
- data/spec/bitvec_expr_spec.rb +2 -1
- data/spec/bool_expr_spec.rb +3 -1
- data/spec/integration/crossflip_spec.rb +8 -0
- data/spec/integration/mortal_coil_puzzle_spec.rb +12 -0
- data/spec/integration/oneofus_spec.rb +19 -0
- data/spec/integration/regexp_crossword_solver_spec.rb +31 -0
- data/spec/integration/regexp_solver_spec.rb +27 -0
- metadata +48 -5
- data/lib/z3/expr/.DS_Store +0 -0
- data/lib/z3/sort/.DS_Store +0 -0
- data/spec/.DS_Store +0 -0
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 23036614e8d142331481794ca573d49be3d4c18f
|
4
|
+
data.tar.gz: 1d765179f046c53ae950605fbf4d431d9bc3d7b6
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f49fb4df1ff7df3b283aa2f2eada3b263d13af54ddf0b9e7640d315efcae9fe5cdcaa00b5f4693e5ead379d9babf45923832c269de5dedef84b516a9c1245f78
|
7
|
+
data.tar.gz: c936f488e71e3b8fe99f86ec8602bfde64848d1c008407a73316739dec194e0f99a0567487f3d6371ba775265431bfb44fa64ea52c1a7042941f4b5289a71fcb
|
data/README.md
CHANGED
@@ -1,18 +1,16 @@
|
|
1
1
|
This is Ruby interface for Z3 [ https://github.com/Z3Prover/z3 ].
|
2
2
|
|
3
|
-
It's in
|
3
|
+
It's in very early stages of development. Pull requests always welcome.
|
4
4
|
|
5
5
|
### Interface
|
6
6
|
|
7
|
-
|
7
|
+
The public interface is various methods in `Z3` module, and on objects created by it. `examples/` directory is probably the best place to start.
|
8
8
|
|
9
|
-
|
9
|
+
You can use most Ruby operators to construct Z3 expressions, but use `| &` instead of `|| &&` for boolean operators. They unfortunately have wrong operator precedence so you'll need to use some extra parentheses.
|
10
10
|
|
11
|
-
|
11
|
+
The interface is potentially unstable, and can change in the future.
|
12
12
|
|
13
|
-
|
14
|
-
|
15
|
-
Bit vectors are treated as signed by default. [well, mostly, more systematic treatment of this is on TODO list]
|
13
|
+
`Z3::VeryLowLever` and `Z3::LowLevel` are FFI interface for internal use, and they shouldn't be used directly. Also don't use any method starting with `_`. Doing this is likely to lead to segmentation faults unless extreme care is taken.
|
16
14
|
|
17
15
|
### Requirements
|
18
16
|
|
@@ -22,12 +20,12 @@ To use it, you'll need to install `z3`. On OSX that would be:
|
|
22
20
|
|
23
21
|
On other systems use appropriate package manager.
|
24
22
|
|
25
|
-
### Known
|
23
|
+
### Known Issues
|
26
24
|
|
27
|
-
Ruby API tries to
|
25
|
+
As Z3 is a C library, doing anything weird with it will segfault your process. Ruby API tries its best to prevent such problems and turn them into exceptions instead, but if you do anything weird (especially touch any method prefixed with `_` or `Z3::LowLevel` interface), crashes are possible. If you have reproducible crash on reasonable looking code, definitely submit it as a bug, and I'll try to come up with a workaround.
|
28
26
|
|
29
|
-
|
27
|
+
As Z3 mixes aggressively interning ASTs and reference counting, it's not very compatible with Ruby style memory management, so memory will leak a good deal. It's usually not much worse than the usual Symbol memory leak, but you might want to avoid Z3 in a long running processes exposed to public input.
|
30
28
|
|
31
|
-
### Python
|
29
|
+
### Python examples
|
32
30
|
|
33
|
-
|
31
|
+
Some of example solvers also have Python versions available from https://github.com/taw/puzzle-solvers
|
data/examples/crossflip
ADDED
@@ -0,0 +1,82 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require "z3"
|
4
|
+
|
5
|
+
class CrossFlipSolver
|
6
|
+
def initialize(board)
|
7
|
+
# 0 - light
|
8
|
+
# 1 - dark
|
9
|
+
# 2 - wall
|
10
|
+
@board = board.split(",").map{|line| line.chars.map(&:to_i)}
|
11
|
+
@ysize = @board.size
|
12
|
+
@xsize = @board[0].size
|
13
|
+
end
|
14
|
+
|
15
|
+
def board(x,y)
|
16
|
+
@board[y][x]
|
17
|
+
end
|
18
|
+
|
19
|
+
def wall?(x,y)
|
20
|
+
board(x,y) == 2
|
21
|
+
end
|
22
|
+
|
23
|
+
def click(x,y)
|
24
|
+
Z3.Bool("click[#{x},#{y}]")
|
25
|
+
end
|
26
|
+
|
27
|
+
def cells_visible_from(x0,y0)
|
28
|
+
result = []
|
29
|
+
result << click(x0,y0)
|
30
|
+
(x0+1...@xsize).each do |x|
|
31
|
+
break if wall?(x,y0)
|
32
|
+
result << click(x,y0)
|
33
|
+
end
|
34
|
+
(0..x0-1).reverse_each do |x|
|
35
|
+
break if wall?(x,y0)
|
36
|
+
result << click(x,y0)
|
37
|
+
end
|
38
|
+
(y0+1...@ysize).each do |y|
|
39
|
+
break if wall?(x0,y)
|
40
|
+
result << click(x0,y)
|
41
|
+
end
|
42
|
+
(0..y0-1).reverse_each do |y|
|
43
|
+
break if wall?(x0,y)
|
44
|
+
result << click(x0,y)
|
45
|
+
end
|
46
|
+
result
|
47
|
+
end
|
48
|
+
|
49
|
+
def solve!
|
50
|
+
@solver = Z3::Solver.new
|
51
|
+
|
52
|
+
@ysize.times do |y|
|
53
|
+
@xsize.times do |x|
|
54
|
+
if wall?(x,y)
|
55
|
+
@solver.assert click(x,y) == false
|
56
|
+
else
|
57
|
+
starts_light = (board(x,y) == 0)
|
58
|
+
ends_light = Z3.Xor(*cells_visible_from(x,y)) ^ starts_light
|
59
|
+
@solver.assert ends_light
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
unless @solver.satisfiable?
|
65
|
+
raise "This can't be solved, go away!"
|
66
|
+
end
|
67
|
+
model = @solver.model
|
68
|
+
|
69
|
+
@ysize.times do |y|
|
70
|
+
@xsize.times do |x|
|
71
|
+
clicked = (model[click(x,y)].to_s == "true")
|
72
|
+
print clicked ? "[x] " : "[ ] "
|
73
|
+
end
|
74
|
+
print "\n"
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
|
80
|
+
|
81
|
+
board = "1021,1000"
|
82
|
+
CrossFlipSolver.new(board).solve!
|
@@ -0,0 +1,105 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require_relative "../lib/z3"
|
4
|
+
require "pathname"
|
5
|
+
|
6
|
+
# From hacker.org
|
7
|
+
class MortalCoilSolver
|
8
|
+
def initialize(path)
|
9
|
+
data = Pathname(path).read
|
10
|
+
data = data.strip.split("\n").map do |line|
|
11
|
+
line.chars.map{|c| c == "x" ? true : false}
|
12
|
+
end
|
13
|
+
@data = data
|
14
|
+
@solver = Z3::Solver.new
|
15
|
+
@size_y = @data.size
|
16
|
+
@size_x = @data[0].size
|
17
|
+
@empty_count = @data.flatten.count{|x| !x}
|
18
|
+
end
|
19
|
+
|
20
|
+
def setup_vars!
|
21
|
+
empty_cell_vars = []
|
22
|
+
@vars = {}
|
23
|
+
(0...@size_x).each do |x|
|
24
|
+
(0...@size_y).each do |y|
|
25
|
+
v = Z3.Int("c#{x},#{y}")
|
26
|
+
if @data[y][x]
|
27
|
+
@solver.assert v == -1
|
28
|
+
else
|
29
|
+
@solver.assert v >= 1
|
30
|
+
@solver.assert v <= @empty_count
|
31
|
+
empty_cell_vars << v
|
32
|
+
end
|
33
|
+
@vars[[x,y]] = v
|
34
|
+
end
|
35
|
+
end
|
36
|
+
@solver.assert Z3.Distinct(*empty_cell_vars)
|
37
|
+
end
|
38
|
+
|
39
|
+
def line_continuity!
|
40
|
+
(0...@size_y).each do |y|
|
41
|
+
(0...@size_x).each do |x|
|
42
|
+
v = @vars[[x,y]]
|
43
|
+
neighbours = [[x-1,y], [x+1,y], [x,y-1], [x,y+1]].map{|a,b| @vars[[a,b]]}.compact
|
44
|
+
@solver.assert Z3.Or(v == -1, v == @empty_count, *neighbours.map{|n| n == v+1})
|
45
|
+
@solver.assert Z3.Or(v == -1, v == 1, *neighbours.map{|n| n == v-1})
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def keep_going!(a,b,c)
|
51
|
+
# If line goes from a to b (b == a+1), then it must:
|
52
|
+
# * hit a wall (c == nil)
|
53
|
+
# * hit a filled block (c == -1)
|
54
|
+
# * continue (c == b+1)
|
55
|
+
# * hit earlier line (c < b)
|
56
|
+
return unless a and c
|
57
|
+
@solver.assert (b == a+1).implies(Z3.Or(
|
58
|
+
c == -1,
|
59
|
+
c == b+1,
|
60
|
+
c < b,
|
61
|
+
)
|
62
|
+
)
|
63
|
+
end
|
64
|
+
|
65
|
+
def line_goes_until_it_hits_something!
|
66
|
+
(0...@size_y).each do |y|
|
67
|
+
(0...@size_x).each do |x|
|
68
|
+
v = @vars[[x,y]]
|
69
|
+
left = @vars[[x-1,y]]
|
70
|
+
right = @vars[[x+1,y]]
|
71
|
+
up = @vars[[x,y-1]]
|
72
|
+
down = @vars[[x,y+1]]
|
73
|
+
keep_going! left, v, right
|
74
|
+
keep_going! right, v, left
|
75
|
+
keep_going! up, v, down
|
76
|
+
keep_going! down, v, up
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
def solve!
|
82
|
+
setup_vars!
|
83
|
+
line_continuity!
|
84
|
+
line_goes_until_it_hits_something!
|
85
|
+
|
86
|
+
if @solver.satisfiable?
|
87
|
+
model = @solver.model
|
88
|
+
(0...@size_y).each do |y|
|
89
|
+
puts (0...@size_x).map{|x|
|
90
|
+
v = model[@vars[[x,y]]].to_i
|
91
|
+
if v == -1
|
92
|
+
"###"
|
93
|
+
else
|
94
|
+
"% 3d" % v
|
95
|
+
end
|
96
|
+
}.join(" ")
|
97
|
+
end
|
98
|
+
else
|
99
|
+
puts "Puzzle has no solution"
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
path = ARGV[0] || Pathname(__dir__) + "mortal_coil_puzzle-9.txt"
|
105
|
+
MortalCoilSolver.new(path).solve!
|
data/examples/oneofus
ADDED
@@ -0,0 +1,102 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require "z3"
|
4
|
+
require "pry"
|
5
|
+
|
6
|
+
class OneofusSolver
|
7
|
+
def initialize
|
8
|
+
@x_size = 3
|
9
|
+
@y_size = 3
|
10
|
+
@board_str = "CbBbCbAaCbCaCbAbCb"
|
11
|
+
@solver = Z3::Solver.new
|
12
|
+
|
13
|
+
@board = (0...@x_size).map{|x|
|
14
|
+
(0...@y_size).map{|y| @board_str[(y * @x_size+x) * 2, 2] }
|
15
|
+
}.transpose
|
16
|
+
end
|
17
|
+
|
18
|
+
def print_board
|
19
|
+
3.times do |y|
|
20
|
+
puts 3.times.map{|x|
|
21
|
+
cell = @board[y][x]
|
22
|
+
shape = {"C" => "Triangle", "B" => "Circle", "A" => "Square"}[cell[0]]
|
23
|
+
color = {"b" => "grey", "a"=> "purple"}[cell[1]]
|
24
|
+
"#{color} #{shape}"
|
25
|
+
}.join(" ")
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def shape_number(x,y)
|
30
|
+
cell = @board[y][x]
|
31
|
+
{"C" => 2, "B" => 1, "A" => 0}[cell[0]]
|
32
|
+
end
|
33
|
+
|
34
|
+
def color_number(x,y)
|
35
|
+
cell = @board[y][x]
|
36
|
+
{"b" => 1, "a"=> 0}[cell[1]]
|
37
|
+
end
|
38
|
+
|
39
|
+
def run!
|
40
|
+
print_board
|
41
|
+
puts ""
|
42
|
+
|
43
|
+
9.times do |i|
|
44
|
+
cx = Z3.Int("click_#{i}_x")
|
45
|
+
cy = Z3.Int("click_#{i}_y")
|
46
|
+
cc = Z3.Int("click_#{i}_color")
|
47
|
+
cs = Z3.Int("click_#{i}_shape")
|
48
|
+
@solver.assert (cx >= 0) & (cx <= 2)
|
49
|
+
@solver.assert (cy >= 0) & (cy <= 2)
|
50
|
+
cn = Z3.Int("click_#{i}_n")
|
51
|
+
@solver.assert cn == (3 * cx + cy)
|
52
|
+
end
|
53
|
+
@solver.assert Z3.Distinct(
|
54
|
+
*9.times.map{|i|
|
55
|
+
Z3.Int("click_#{i}_n")
|
56
|
+
}
|
57
|
+
)
|
58
|
+
8.times do |i|
|
59
|
+
cx = Z3.Int("click_#{i}_x")
|
60
|
+
cy = Z3.Int("click_#{i}_y")
|
61
|
+
cc = Z3.Int("click_#{i}_color")
|
62
|
+
cs = Z3.Int("click_#{i}_shape")
|
63
|
+
cx_next = Z3.Int("click_#{i+1}_x")
|
64
|
+
cy_next = Z3.Int("click_#{i+1}_y")
|
65
|
+
cc_next = Z3.Int("click_#{i+1}_color")
|
66
|
+
cs_next = Z3.Int("click_#{i+1}_shape")
|
67
|
+
@solver.assert (cx == cx_next) | (cy == cy_next)
|
68
|
+
@solver.assert (cc == cc_next) | (cs == cs_next)
|
69
|
+
end
|
70
|
+
9.times do |i|
|
71
|
+
3.times do |y|
|
72
|
+
3.times do |x|
|
73
|
+
cx = Z3.Int("click_#{i}_x")
|
74
|
+
cy = Z3.Int("click_#{i}_y")
|
75
|
+
cc = Z3.Int("click_#{i}_color")
|
76
|
+
cs = Z3.Int("click_#{i}_shape")
|
77
|
+
@solver.assert (
|
78
|
+
(cx == x) & (cy == y)
|
79
|
+
).implies(
|
80
|
+
(cs == shape_number(x,y)) & (cc == color_number(x,y))
|
81
|
+
)
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
print_solution
|
87
|
+
end
|
88
|
+
|
89
|
+
def print_solution
|
90
|
+
raise "FAIL" unless @solver.satisfiable?
|
91
|
+
model = @solver.model
|
92
|
+
9.times do |i|
|
93
|
+
cx = model[Z3.Int("click_#{i}_x")].to_i
|
94
|
+
cy = model[Z3.Int("click_#{i}_y")].to_i
|
95
|
+
cc = ["purple", "grey"][model[Z3.Int("click_#{i}_color")].to_i]
|
96
|
+
cs = ["Square", "Circle", "Triangle"][model[Z3.Int("click_#{i}_shape")].to_i]
|
97
|
+
puts "Click #{i}: #{cx},#{cy} - #{cc} #{cs}"
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
OneofusSolver.new.run!
|
@@ -0,0 +1,80 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require_relative "../lib/z3"
|
4
|
+
require "pathname"
|
5
|
+
require_relative "simple_regexp_parser"
|
6
|
+
require_relative "regexp_string_matcher"
|
7
|
+
|
8
|
+
class RegexpCrosswordSolver
|
9
|
+
def initialize(path)
|
10
|
+
data = Pathname(path).read
|
11
|
+
data = data.strip.split("\n")
|
12
|
+
col_number = data.index("")
|
13
|
+
row_number = data.size - col_number - 1
|
14
|
+
# left to right
|
15
|
+
@cols = data.first(col_number)
|
16
|
+
# top to bottom
|
17
|
+
@rows = data.last(row_number)
|
18
|
+
@solver = Z3::Solver.new
|
19
|
+
@xsize = @cols.size
|
20
|
+
@ysize = @rows.size
|
21
|
+
end
|
22
|
+
|
23
|
+
def setup_cell(x,y)
|
24
|
+
v = Z3.Int("cell#{x},#{y}")
|
25
|
+
@solver.assert v >= 0
|
26
|
+
@solver.assert v <= 127
|
27
|
+
v
|
28
|
+
end
|
29
|
+
|
30
|
+
def col_str(x)
|
31
|
+
(0...@ysize).map{|y| @crossword[[x,y]]}
|
32
|
+
end
|
33
|
+
|
34
|
+
def row_str(y)
|
35
|
+
(0...@xsize).map{|x| @crossword[[x,y]]}
|
36
|
+
end
|
37
|
+
|
38
|
+
def col_rx(x)
|
39
|
+
SimpleRegexpParser.new(@cols[x], "col-#{x}").parse
|
40
|
+
end
|
41
|
+
|
42
|
+
def row_rx(y)
|
43
|
+
SimpleRegexpParser.new(@rows[y], "row-#{y}").parse
|
44
|
+
end
|
45
|
+
|
46
|
+
def solve!
|
47
|
+
@crossword = {}
|
48
|
+
@xsize.times do |x|
|
49
|
+
@ysize.times do |y|
|
50
|
+
@crossword[[x,y]] = setup_cell(x,y)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
@xsize.times do |x|
|
55
|
+
RegexpStringMatcher.new(@solver, col_rx(x), col_str(x), "col-#{x}").run!
|
56
|
+
end
|
57
|
+
@ysize.times do |y|
|
58
|
+
RegexpStringMatcher.new(@solver, row_rx(y), row_str(y), "row-#{y}").run!
|
59
|
+
end
|
60
|
+
|
61
|
+
print_answer
|
62
|
+
end
|
63
|
+
|
64
|
+
def print_answer
|
65
|
+
if @solver.satisfiable?
|
66
|
+
@model = @solver.model
|
67
|
+
@ysize.times do |y|
|
68
|
+
puts @xsize.times.map{|x|
|
69
|
+
@model[@crossword[[x,y]]].to_i.chr
|
70
|
+
}.join.inspect[1...-1]
|
71
|
+
end
|
72
|
+
else
|
73
|
+
raise
|
74
|
+
print "Crossword has no solution"
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
path = ARGV[0] || Pathname(__dir__) + "regexp_crossword/tutorial-1.txt"
|
80
|
+
RegexpCrosswordSolver.new(path).solve!
|
@@ -0,0 +1,44 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require_relative "../lib/z3"
|
4
|
+
require_relative "simple_regexp_parser"
|
5
|
+
require_relative "regexp_string_matcher"
|
6
|
+
|
7
|
+
class RegexpSolver
|
8
|
+
def initialize(length, rx)
|
9
|
+
@length = length
|
10
|
+
@rx = SimpleRegexpParser.new(rx, "").parse
|
11
|
+
@solver = Z3::Solver.new
|
12
|
+
end
|
13
|
+
|
14
|
+
def solve!
|
15
|
+
@str = (0...@length).map do |i|
|
16
|
+
v = Z3.Int("char[#{i}]")
|
17
|
+
@solver.assert v >= 0
|
18
|
+
@solver.assert v <= 127
|
19
|
+
v
|
20
|
+
end
|
21
|
+
|
22
|
+
RegexpStringMatcher.new(@solver, @rx, @str, "").run!
|
23
|
+
print_answer
|
24
|
+
end
|
25
|
+
|
26
|
+
def print_answer
|
27
|
+
if @solver.satisfiable?
|
28
|
+
@model = @solver.model
|
29
|
+
puts @str.map{|c| @model[c].to_i.chr }.join.inspect[1...-1]
|
30
|
+
else
|
31
|
+
raise
|
32
|
+
print "Crossword has no solution"
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
unless ARGV.size == 2
|
38
|
+
STDERR.puts "Usage: #{$0} <length> <regexp>"
|
39
|
+
exit 1
|
40
|
+
end
|
41
|
+
|
42
|
+
length = ARGV[0].to_i
|
43
|
+
regexp = ARGV[1]
|
44
|
+
RegexpSolver.new(length, regexp).solve!
|
@@ -0,0 +1,124 @@
|
|
1
|
+
require "set"
|
2
|
+
|
3
|
+
class RegexpStringMatcher
|
4
|
+
# rx comes from SimpleRegexpParser
|
5
|
+
# str is a bunch of Z3.Ints with proper constraints (0..127)
|
6
|
+
def initialize(solver, rx, str, context)
|
7
|
+
@solver = solver
|
8
|
+
@rx = rx
|
9
|
+
@str = str
|
10
|
+
@context = context
|
11
|
+
@subexprs = Set[]
|
12
|
+
end
|
13
|
+
|
14
|
+
def group_equal(group_identifier, start_idx, end_idx)
|
15
|
+
str = @str[start_idx...end_idx]
|
16
|
+
Z3.And(
|
17
|
+
Z3.Int("group-#{group_identifier}-size") == str.size,
|
18
|
+
*str.each_with_index.map{|s,i| Z3.Int("group-#{group_identifier}-char[#{i}]") == s }
|
19
|
+
)
|
20
|
+
end
|
21
|
+
|
22
|
+
def anchor_match(anchor, start_idx)
|
23
|
+
case anchor[0]
|
24
|
+
when :bos
|
25
|
+
start_idx == 0
|
26
|
+
when :eos
|
27
|
+
start_idx == @str.size
|
28
|
+
when :bol
|
29
|
+
if start_idx == 0
|
30
|
+
true
|
31
|
+
else
|
32
|
+
@str[start_idx-1] == 10
|
33
|
+
end
|
34
|
+
when :eol
|
35
|
+
# This is actually tricky
|
36
|
+
# $ matches both before and after \n
|
37
|
+
if start_idx == @str.size
|
38
|
+
true
|
39
|
+
elsif start_idx == 0
|
40
|
+
(@str[start_idx] == 10)
|
41
|
+
else
|
42
|
+
(@str[start_idx] == 10) | (@str[start_idx-1] == 10)
|
43
|
+
end
|
44
|
+
when :lookahead
|
45
|
+
regexp_match([:seq, anchor[1], [:any_star]], start_idx, @str.size)
|
46
|
+
when :negative_lookahead
|
47
|
+
~regexp_match([:seq, anchor[1], [:any_star]], start_idx, @str.size)
|
48
|
+
when :lookbehind
|
49
|
+
regexp_match([:seq, [:any_star], anchor[1]], 0, start_idx)
|
50
|
+
when :lookbehind
|
51
|
+
~regexp_match([:seq, [:any_star], anchor[1]], 0, start_idx)
|
52
|
+
else
|
53
|
+
raise "Unknown anchor type #{anchor[0]}"
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
# @subexprs just prevents loops for self-referential regexps
|
58
|
+
def regexp_match(rx, start_idx, end_idx)
|
59
|
+
str = @str[start_idx...end_idx]
|
60
|
+
var = Z3.Bool("#{rx} =~ #{@context}[#{start_idx}...#{end_idx}]")
|
61
|
+
return var if @subexprs.include?(var)
|
62
|
+
@subexprs << var
|
63
|
+
|
64
|
+
expr = case rx[0]
|
65
|
+
when :seq
|
66
|
+
rx_a, rx_b = rx[1], rx[2]
|
67
|
+
possibilities = (start_idx..end_idx).map do |i|
|
68
|
+
a_ok = regexp_match(rx_a, start_idx, i)
|
69
|
+
b_ok = regexp_match(rx_b, i, end_idx)
|
70
|
+
a_ok & b_ok
|
71
|
+
end
|
72
|
+
Z3.Or(*possibilities)
|
73
|
+
when :set
|
74
|
+
if str.size == 1
|
75
|
+
Z3.Or(*rx[1].map{|c| str[0] == c })
|
76
|
+
else
|
77
|
+
false
|
78
|
+
end
|
79
|
+
when :neg_set
|
80
|
+
if str.size == 1
|
81
|
+
~Z3.Or(*rx[1].map{|c| str[0] == c })
|
82
|
+
else
|
83
|
+
false
|
84
|
+
end
|
85
|
+
when :alt
|
86
|
+
rx_a, rx_b = rx[1], rx[2]
|
87
|
+
regexp_match(rx_a, start_idx, end_idx) | regexp_match(rx_b, start_idx, end_idx)
|
88
|
+
when :star
|
89
|
+
if str.size == 0
|
90
|
+
true
|
91
|
+
else
|
92
|
+
r = rx[1]
|
93
|
+
possibilities = (start_idx+1..end_idx).map do |i|
|
94
|
+
regexp_match(r, start_idx, i) & regexp_match(rx, i, end_idx)
|
95
|
+
end
|
96
|
+
Z3.Or(*possibilities)
|
97
|
+
end
|
98
|
+
when :group
|
99
|
+
n = rx[1]
|
100
|
+
rx_a = rx[2]
|
101
|
+
regexp_match(rx_a, start_idx, end_idx) & group_equal(n, start_idx, end_idx)
|
102
|
+
when :backref
|
103
|
+
group_equal(rx[1], start_idx, end_idx)
|
104
|
+
when :empty
|
105
|
+
(str.size == 0)
|
106
|
+
when :anchor
|
107
|
+
if str.size == 0
|
108
|
+
anchor_match(rx[1..-1], start_idx)
|
109
|
+
else
|
110
|
+
false
|
111
|
+
end
|
112
|
+
when :any_star # like /.*/m or /.*/s depending on language
|
113
|
+
true
|
114
|
+
else
|
115
|
+
raise "Unknown regexp type #{rx[0]}"
|
116
|
+
end
|
117
|
+
@solver.assert (var == expr).simplify
|
118
|
+
var
|
119
|
+
end
|
120
|
+
|
121
|
+
def run!
|
122
|
+
@solver.assert regexp_match(@rx, 0, @str.length)
|
123
|
+
end
|
124
|
+
end
|
@@ -0,0 +1,187 @@
|
|
1
|
+
require "regexp_parser"
|
2
|
+
|
3
|
+
# Format generated by Regexp::Parser is not convenient at all,
|
4
|
+
# let's translate it into something more useful
|
5
|
+
class SimpleRegexpParser
|
6
|
+
def initialize(str, context)
|
7
|
+
@tree = Regexp::Parser.parse(str)
|
8
|
+
@context = context
|
9
|
+
@group_number = 0
|
10
|
+
end
|
11
|
+
|
12
|
+
def new_group
|
13
|
+
@group_number += 1
|
14
|
+
"#{@context}-#{@group_number}"
|
15
|
+
end
|
16
|
+
|
17
|
+
def sequence(*parts)
|
18
|
+
parts = parts.select{|x| x[0] != :empty}
|
19
|
+
case parts.size
|
20
|
+
when 0
|
21
|
+
[:empty]
|
22
|
+
when 1
|
23
|
+
parts[0]
|
24
|
+
else
|
25
|
+
while parts.size > 1
|
26
|
+
parts.unshift [:seq, parts.shift, parts.shift]
|
27
|
+
end
|
28
|
+
parts[0]
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def alternative(*parts)
|
33
|
+
case parts.size
|
34
|
+
when 0
|
35
|
+
raise "Can't have empty alternative"
|
36
|
+
when 1
|
37
|
+
parts[0]
|
38
|
+
else
|
39
|
+
while parts.size > 1
|
40
|
+
parts.unshift [:alt, parts.shift, parts.shift]
|
41
|
+
end
|
42
|
+
parts[0]
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
# Saves us time to reuse ruby regexp engine for 1 character case
|
47
|
+
def character_type(char_rx)
|
48
|
+
char_rx = Regexp.new(char_rx)
|
49
|
+
codes = (0..127).select{|c| c.chr =~ char_rx}
|
50
|
+
# This is mostly here to make debugging easier
|
51
|
+
if codes.size > 127-codes.size
|
52
|
+
[:neg_set, (0..127).to_a - codes]
|
53
|
+
else
|
54
|
+
[:set, codes]
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
def character_set(negated, members)
|
59
|
+
if negated
|
60
|
+
character_type "[^#{members.join}]"
|
61
|
+
else
|
62
|
+
character_type "[#{members.join}]"
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
def literal(chars)
|
67
|
+
sequence(*chars.map{|c| character_type(c)})
|
68
|
+
end
|
69
|
+
|
70
|
+
def star(part)
|
71
|
+
[:star, part]
|
72
|
+
end
|
73
|
+
|
74
|
+
def backref(num)
|
75
|
+
[:backref, "#{@context}-#{num}"]
|
76
|
+
end
|
77
|
+
|
78
|
+
def group(number, part)
|
79
|
+
[:group, number, part]
|
80
|
+
end
|
81
|
+
|
82
|
+
def empty
|
83
|
+
[:empty]
|
84
|
+
end
|
85
|
+
|
86
|
+
def repeat(part, min, max)
|
87
|
+
if max == -1
|
88
|
+
sequence(star(part), *([part]*min))
|
89
|
+
else
|
90
|
+
maybe_part = alternative([:empty], part)
|
91
|
+
sequence(*([part]*min), *([maybe_part] * (max-min)))
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
# Groups and qualifiers interact in weird ways, (a){3} is actually aa(a)
|
96
|
+
# We need to do extensive rewriting to make it work
|
97
|
+
def repeat_group(part, min, max)
|
98
|
+
base = part[2]
|
99
|
+
if max == -1
|
100
|
+
if min == 0 # (a)* -> |a*(a)
|
101
|
+
alternative(
|
102
|
+
empty,
|
103
|
+
sequence(repeat(base, min, max), part)
|
104
|
+
)
|
105
|
+
else # (a){2,} -> a{1,}(a)
|
106
|
+
sequence(repeat(base, min-1, max), part)
|
107
|
+
end
|
108
|
+
elsif max == 0 # a{0} -> empty, not really a thing
|
109
|
+
:empty
|
110
|
+
else
|
111
|
+
if min == 0
|
112
|
+
# (a){2,3} -> |a{1,2}(a)
|
113
|
+
alternative(
|
114
|
+
empty,
|
115
|
+
sequence(repeat(base, min, max-1), part)
|
116
|
+
)
|
117
|
+
else # (a){2,3} -> a{1,2}(a)
|
118
|
+
sequence(repeat(base, min-1, max-1), part)
|
119
|
+
end
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
# Try to express regexps with minimum number of primitives:
|
124
|
+
# * seq - ab
|
125
|
+
# * alt - a|b
|
126
|
+
# * star - a*
|
127
|
+
# * set - a [a-z] [^a-z]
|
128
|
+
# * empty
|
129
|
+
# * backref - \1
|
130
|
+
# * group - (a)
|
131
|
+
def parse(node=@tree)
|
132
|
+
result = case node
|
133
|
+
when Regexp::Expression::Group::Capture
|
134
|
+
# Assumes it's going to be parsed in right order
|
135
|
+
group(new_group, sequence(*node.expressions.map{|n| parse(n)}))
|
136
|
+
when Regexp::Expression::Alternation
|
137
|
+
alternative(*node.expressions.map{|n| parse(n)})
|
138
|
+
when Regexp::Expression::Assertion::Lookahead
|
139
|
+
[:anchor, :lookahead, sequence(*node.expressions.map{|n| parse(n)})]
|
140
|
+
when Regexp::Expression::Assertion::NegativeLookahead
|
141
|
+
[:anchor, :negative_lookahead, sequence(*node.expressions.map{|n| parse(n)})]
|
142
|
+
when Regexp::Expression::Assertion::Lookbehind
|
143
|
+
[:anchor, :lookbehind, sequence(*node.expressions.map{|n| parse(n)})]
|
144
|
+
when Regexp::Expression::Assertion::NegativeLookbehind
|
145
|
+
[:anchor, :negative_lookbehind, sequence(*node.expressions.map{|n| parse(n)})]
|
146
|
+
when Regexp::Expression::Subexpression
|
147
|
+
# It's annoyingly subtypes a lot
|
148
|
+
raise unless node.class == Regexp::Expression::Subexpression or
|
149
|
+
node.class == Regexp::Expression::Group::Passive or
|
150
|
+
node.class == Regexp::Expression::Root or
|
151
|
+
node.class == Regexp::Expression::Alternative
|
152
|
+
sequence(*node.expressions.map{|n| parse(n)})
|
153
|
+
when Regexp::Expression::CharacterSet
|
154
|
+
character_set(node.negative?, node.members)
|
155
|
+
when Regexp::Expression::Literal
|
156
|
+
literal(node.text.chars)
|
157
|
+
when Regexp::Expression::CharacterType::Base
|
158
|
+
character_type(node.text)
|
159
|
+
when Regexp::Expression::EscapeSequence::Base
|
160
|
+
character_type(node.text)
|
161
|
+
when Regexp::Expression::Backreference::Number
|
162
|
+
num = node.text[%r[\A\\(\d+)\z], 1] or raise "Parse error"
|
163
|
+
backref(num.to_i)
|
164
|
+
when Regexp::Expression::Anchor::BeginningOfString
|
165
|
+
[:anchor, :bos]
|
166
|
+
when Regexp::Expression::Anchor::EndOfString
|
167
|
+
[:anchor, :eos]
|
168
|
+
when Regexp::Expression::Anchor::BeginningOfLine
|
169
|
+
[:anchor, :bol]
|
170
|
+
when Regexp::Expression::Anchor::EndOfLine
|
171
|
+
[:anchor, :eol]
|
172
|
+
else
|
173
|
+
raise "Unknown expression"
|
174
|
+
end
|
175
|
+
if node.quantified?
|
176
|
+
min = node.quantifier.min
|
177
|
+
max = node.quantifier.max
|
178
|
+
result = if result[0] == :group
|
179
|
+
repeat_group(result, min, max)
|
180
|
+
else
|
181
|
+
repeat(result, min, max)
|
182
|
+
end
|
183
|
+
end
|
184
|
+
|
185
|
+
result
|
186
|
+
end
|
187
|
+
end
|
data/lib/z3/expr/bitvec_expr.rb
CHANGED
data/lib/z3/expr/bool_expr.rb
CHANGED
data/lib/z3/probe.rb
CHANGED
data/spec/bitvec_expr_spec.rb
CHANGED
@@ -63,8 +63,9 @@ module Z3
|
|
63
63
|
expect([a == 50, b == -a]).to have_solution(b => 206)
|
64
64
|
end
|
65
65
|
|
66
|
-
it "~" do
|
66
|
+
it "~ and !" do
|
67
67
|
expect([a == 50, b == ~a]).to have_solution(b => 205)
|
68
|
+
expect([a == 50, b == !a]).to have_solution(b => 205)
|
68
69
|
end
|
69
70
|
|
70
71
|
it ">> (sign-dependent)" do
|
data/spec/bool_expr_spec.rb
CHANGED
@@ -54,9 +54,11 @@ module Z3
|
|
54
54
|
expect([a == false, b == false, c == (a == b)]).to have_solution(c => true)
|
55
55
|
end
|
56
56
|
|
57
|
-
it "~" do
|
57
|
+
it "~ and !" do
|
58
58
|
expect([a == true, b == ~a]).to have_solution(b => false)
|
59
59
|
expect([a == false, b == ~a]).to have_solution(b => true)
|
60
|
+
expect([a == true, b == !a]).to have_solution(b => false)
|
61
|
+
expect([a == false, b == !a]).to have_solution(b => true)
|
60
62
|
end
|
61
63
|
|
62
64
|
it "if then else" do
|
@@ -0,0 +1,19 @@
|
|
1
|
+
describe "OneOfUs" do
|
2
|
+
it do
|
3
|
+
expect("oneofus").to have_output <<EOF
|
4
|
+
grey Triangle grey Circle grey Triangle
|
5
|
+
purple Square grey Triangle purple Triangle
|
6
|
+
grey Triangle grey Square grey Triangle
|
7
|
+
|
8
|
+
Click 0: 0,1 - purple Square
|
9
|
+
Click 1: 2,1 - purple Triangle
|
10
|
+
Click 2: 2,2 - grey Triangle
|
11
|
+
Click 3: 1,2 - grey Square
|
12
|
+
Click 4: 1,1 - grey Triangle
|
13
|
+
Click 5: 1,0 - grey Circle
|
14
|
+
Click 6: 2,0 - grey Triangle
|
15
|
+
Click 7: 0,0 - grey Triangle
|
16
|
+
Click 8: 0,2 - grey Triangle
|
17
|
+
EOF
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
describe "Regexp Crossword Solver" do
|
2
|
+
let(:binary) { Pathname(__dir__) + "../../examples/regexp_crossword_solver" }
|
3
|
+
|
4
|
+
examples = (Pathname(__dir__) + "../../examples/regexp_crossword")
|
5
|
+
.children
|
6
|
+
.select{|x| x.extname == ".txt"}
|
7
|
+
examples.each do |example_path|
|
8
|
+
describe example_path.basename.to_s do
|
9
|
+
let(:example) { example_path.readlines.map(&:chomp) }
|
10
|
+
let(:col_number) { example.index("") }
|
11
|
+
let(:row_number) { example.size - col_number - 1 }
|
12
|
+
# left to right
|
13
|
+
let(:cols) { example.first(col_number) }
|
14
|
+
# top to bottom
|
15
|
+
let(:rows) { example.last(row_number) }
|
16
|
+
|
17
|
+
it "matches output" do
|
18
|
+
output_rows = `#{binary} #{example_path}`.split("\n").map(&:chomp)
|
19
|
+
output_cols = output_rows.map(&:chars).transpose.map(&:join)
|
20
|
+
expect(output_rows.size).to eq(rows.size)
|
21
|
+
expect(output_cols.size).to eq(cols.size)
|
22
|
+
output_rows.zip(rows).each do |s, rx|
|
23
|
+
expect(s).to match Regexp.new(/\A(?:#{rx})\z/)
|
24
|
+
end
|
25
|
+
output_cols.zip(cols).each do |s, rx|
|
26
|
+
expect(s).to match Regexp.new(/\A(?:#{rx})\z/)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
require "yaml"
|
2
|
+
|
3
|
+
describe "Regexp Solver" do
|
4
|
+
let(:binary) { Pathname(__dir__) + "../../examples/regexp_solver" }
|
5
|
+
|
6
|
+
examples = [
|
7
|
+
[3 ,'lol'],
|
8
|
+
[3, '\d\d\d'],
|
9
|
+
[4, 'a*\Ab*\zc*'],
|
10
|
+
[2, '^\n^\n'],
|
11
|
+
[1, '(?=[ab])(?=[bc])[bd]'],
|
12
|
+
[1, '(?=[ab])(?![bc])[ac]'],
|
13
|
+
[2, '(?=a$)a\n'],
|
14
|
+
[2, '(?=a\s$)a\n'],
|
15
|
+
]
|
16
|
+
|
17
|
+
examples.each do |length, rx|
|
18
|
+
describe rx do
|
19
|
+
it "matches output" do
|
20
|
+
actual_esc = `#{binary} #{length} '#{rx}'`.chomp
|
21
|
+
actual = YAML.load(%Q(---\n"#{actual_esc}"\n)) # avoid eval
|
22
|
+
expect(actual.size).to eq(length)
|
23
|
+
expect(actual).to match Regexp.new(/\A(?:#{rx})\z/)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: z3
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.20161117
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Tomasz Wegrzanowski
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2016-
|
11
|
+
date: 2016-11-17 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: pry
|
@@ -66,6 +66,20 @@ dependencies:
|
|
66
66
|
- - ">="
|
67
67
|
- !ruby/object:Gem::Version
|
68
68
|
version: '0'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: regexp_parser
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - ">="
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - ">="
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0'
|
69
83
|
- !ruby/object:Gem::Dependency
|
70
84
|
name: ffi
|
71
85
|
requirement: !ruby/object:Gem::Requirement
|
@@ -97,6 +111,7 @@ files:
|
|
97
111
|
- examples/bridges-1.txt
|
98
112
|
- examples/circuit_problems
|
99
113
|
- examples/clogic_puzzle
|
114
|
+
- examples/crossflip
|
100
115
|
- examples/four_hackers_puzzle
|
101
116
|
- examples/geometry_problem
|
102
117
|
- examples/kakuro
|
@@ -109,8 +124,34 @@ files:
|
|
109
124
|
- examples/light_up-1.txt
|
110
125
|
- examples/minisudoku
|
111
126
|
- examples/minisudoku-1.txt
|
127
|
+
- examples/mortal_coil_puzzle
|
128
|
+
- examples/mortal_coil_puzzle-9.txt
|
112
129
|
- examples/nonogram
|
130
|
+
- examples/oneofus
|
131
|
+
- examples/regexp_crossword/beginner-1.txt
|
132
|
+
- examples/regexp_crossword/beginner-2.txt
|
133
|
+
- examples/regexp_crossword/beginner-3.txt
|
134
|
+
- examples/regexp_crossword/beginner-4.txt
|
135
|
+
- examples/regexp_crossword/beginner-5.txt
|
136
|
+
- examples/regexp_crossword/experienced-1.txt
|
137
|
+
- examples/regexp_crossword/experienced-2.txt
|
138
|
+
- examples/regexp_crossword/experienced-3.txt
|
139
|
+
- examples/regexp_crossword/experienced-4.txt
|
140
|
+
- examples/regexp_crossword/experienced-5.txt
|
141
|
+
- examples/regexp_crossword/tutorial-1.txt
|
142
|
+
- examples/regexp_crossword/tutorial-2.txt
|
143
|
+
- examples/regexp_crossword/tutorial-3.txt
|
144
|
+
- examples/regexp_crossword/tutorial-4.txt
|
145
|
+
- examples/regexp_crossword/tutorial-5.txt
|
146
|
+
- examples/regexp_crossword/tutorial-6.txt
|
147
|
+
- examples/regexp_crossword/tutorial-7.txt
|
148
|
+
- examples/regexp_crossword/tutorial-8.txt
|
149
|
+
- examples/regexp_crossword/tutorial-9.txt
|
150
|
+
- examples/regexp_crossword_solver
|
151
|
+
- examples/regexp_solver
|
152
|
+
- examples/regexp_string_matcher.rb
|
113
153
|
- examples/selfref
|
154
|
+
- examples/simple_regexp_parser.rb
|
114
155
|
- examples/sudoku
|
115
156
|
- examples/sudoku-1.txt
|
116
157
|
- examples/verbal_arithmetic
|
@@ -118,7 +159,6 @@ files:
|
|
118
159
|
- lib/z3/ast.rb
|
119
160
|
- lib/z3/context.rb
|
120
161
|
- lib/z3/exception.rb
|
121
|
-
- lib/z3/expr/.DS_Store
|
122
162
|
- lib/z3/expr/arith_expr.rb
|
123
163
|
- lib/z3/expr/array_expr.rb
|
124
164
|
- lib/z3/expr/bitvec_expr.rb
|
@@ -138,7 +178,6 @@ files:
|
|
138
178
|
- lib/z3/printer.rb
|
139
179
|
- lib/z3/probe.rb
|
140
180
|
- lib/z3/solver.rb
|
141
|
-
- lib/z3/sort/.DS_Store
|
142
181
|
- lib/z3/sort/array_sort.rb
|
143
182
|
- lib/z3/sort/bitvec_sort.rb
|
144
183
|
- lib/z3/sort/bool_sort.rb
|
@@ -151,7 +190,6 @@ files:
|
|
151
190
|
- lib/z3/tactic.rb
|
152
191
|
- lib/z3/very_low_level.rb
|
153
192
|
- lib/z3/very_low_level_auto.rb
|
154
|
-
- spec/.DS_Store
|
155
193
|
- spec/array_expr_spec.rb
|
156
194
|
- spec/array_sort_spec.rb
|
157
195
|
- spec/bitvec_expr_spec.rb
|
@@ -171,6 +209,7 @@ files:
|
|
171
209
|
- spec/integration/bit_tricks_spec.rb
|
172
210
|
- spec/integration/bridges_spec.rb
|
173
211
|
- spec/integration/cicruit_problem_spec.rb
|
212
|
+
- spec/integration/crossflip_spec.rb
|
174
213
|
- spec/integration/four_hackers_puzzle_spec.rb
|
175
214
|
- spec/integration/geometry_problem_spec.rb
|
176
215
|
- spec/integration/kakuro_spec.rb
|
@@ -179,7 +218,11 @@ files:
|
|
179
218
|
- spec/integration/letter_connections_spec.rb
|
180
219
|
- spec/integration/light_up_spec.rb
|
181
220
|
- spec/integration/minisudoku_spec.rb
|
221
|
+
- spec/integration/mortal_coil_puzzle_spec.rb
|
182
222
|
- spec/integration/nonogram_spec.rb
|
223
|
+
- spec/integration/oneofus_spec.rb
|
224
|
+
- spec/integration/regexp_crossword_solver_spec.rb
|
225
|
+
- spec/integration/regexp_solver_spec.rb
|
183
226
|
- spec/integration/selfref_spec.rb
|
184
227
|
- spec/integration/sudoku_spec.rb
|
185
228
|
- spec/integration/verbal_arithmetic_spec.rb
|
data/lib/z3/expr/.DS_Store
DELETED
Binary file
|
data/lib/z3/sort/.DS_Store
DELETED
Binary file
|
data/spec/.DS_Store
DELETED
Binary file
|