z3 0.0.20160221 → 0.0.20160323

Sign up to get free protection for your applications and to get access to all the features.
Files changed (48) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +3 -1
  3. data/examples/algebra_problems +24 -24
  4. data/examples/basic_int_math +8 -8
  5. data/examples/basic_logic +8 -8
  6. data/examples/bit_tricks +161 -0
  7. data/examples/bridges_solver +1 -1
  8. data/examples/clogic_puzzle_solver +135 -0
  9. data/examples/four_hackers_puzzle +194 -0
  10. data/examples/geometry_problem +11 -11
  11. data/examples/kakuro_solver +3 -3
  12. data/examples/kinematics_problems +37 -37
  13. data/examples/letter_connections_solver +11 -11
  14. data/examples/light_up_solver +5 -5
  15. data/examples/minisudoku_solver +4 -4
  16. data/examples/selfref_solver +35 -35
  17. data/examples/sudoku_solver +4 -4
  18. data/examples/verbal_arithmetic +2 -2
  19. data/lib/z3/exception.rb +25 -0
  20. data/lib/z3/func_decl.rb +7 -7
  21. data/lib/z3/interface.rb +255 -0
  22. data/lib/z3/low_level.rb +4 -6
  23. data/lib/z3/low_level_auto.rb +1551 -1547
  24. data/lib/z3/model.rb +3 -2
  25. data/lib/z3/solver.rb +65 -54
  26. data/lib/z3/sort/bitvec_sort.rb +40 -0
  27. data/lib/z3/sort/bool_sort.rb +31 -0
  28. data/lib/z3/sort/int_sort.rb +21 -0
  29. data/lib/z3/sort/real_sort.rb +36 -0
  30. data/lib/z3/sort/sort.rb +76 -0
  31. data/lib/z3/value/arith_value.rb +53 -0
  32. data/lib/z3/value/bitvec_value.rb +67 -0
  33. data/lib/z3/value/bool_value.rb +29 -0
  34. data/lib/z3/value/int_value.rb +7 -0
  35. data/lib/z3/value/real_value.rb +7 -0
  36. data/lib/z3/value/value.rb +48 -0
  37. data/lib/z3/very_low_level.rb +28 -45
  38. data/lib/z3/very_low_level_auto.rb +518 -516
  39. data/lib/z3.rb +23 -33
  40. data/spec/integration/bit_tricks_spec.rb +21 -0
  41. data/spec/model_spec.rb +9 -9
  42. data/spec/solver_spec.rb +2 -2
  43. data/spec/sort_spec.rb +38 -13
  44. data/spec/{ast_spec.rb → value_spec.rb} +60 -57
  45. metadata +21 -6
  46. data/lib/z3/ast.rb +0 -302
  47. data/lib/z3/sort.rb +0 -33
  48. /data/spec/integration/{bagic_int_math_spec.rb → basic_int_math_spec.rb} +0 -0
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 73f23f4e1511fe002de7b2e1d3d91945bc6f8be6
4
- data.tar.gz: e6662d712ccaedd2b76a9c560dac242228d51311
3
+ metadata.gz: 3ec7feb43474477a1c3a7220fbbd5c6e11246cd2
4
+ data.tar.gz: 687aedb2c8aaeca944108d4361459bba5ba08eb5
5
5
  SHA512:
6
- metadata.gz: 206d8cb1e08173e3013da2426359a922c72631c28e84957ac8b94ce1219e5295960436fbd78513eb128cd0eb1b396bdf4f534247b183848ecb92df4d16d9e452
7
- data.tar.gz: 04f74e3844e9e017bb6636cd26b96fd0cc79e05e59a86650c66fe18b271a3bc9798ee8481fb9979ec134cb298354f03c2b097b14f9c531166bf0f0ac87ea0ce6
6
+ metadata.gz: 9e641cf575e0810de111a96f394747b1316dc2f9cebd63c7a52390c2439c7836abf1660d67b52998f5e4a048fea74a569121e7f9fd9e4a2e32bce82367082362
7
+ data.tar.gz: 9d1c79300d21645a5ed1e121a8f069fd0a4504c5731ff07c5e5f6a6727647c94ae64fb885c4f4e5bfb2b4641183b2e697f8128d3f66f784cdc9ab6dcce3361fe
data/README.md CHANGED
@@ -8,10 +8,12 @@ It's in extremely early stages of development. Pull requests always welcome.
8
8
 
9
9
  The rest of `Z3` is high level API, but the interface is extremely unstable at this point, and it's pretty much guaranteed to change many times. Check specs or `examples/` directory for usage.
10
10
 
11
- You can use most Ruby operators to construct ASTs, but use `~ | &` instead of `! || &&` for boolean operators.
11
+ 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.
12
12
 
13
13
  As for API internals, attributes starting with `_` are FFI internals you shouldn't touch, other attributes are generally legitimate Ruby objects.
14
14
 
15
+ Bit vectors are treated as signed by default. [well, mostly, more systematic treatment of this is on TODO list]
16
+
15
17
  ### Requirements
16
18
 
17
19
  To use it, you'll need to install `z3`. On OSX that would be:
@@ -22,7 +22,7 @@ end
22
22
 
23
23
  class AlgebraProblem01 < AlgebraProblem
24
24
  def solve!
25
- x = Z3::Ast.real("x")
25
+ x = Z3.Real("x")
26
26
  solver.assert 5*(-3*x - 2) - (x - 3) == -4*(4*x + 5) + 13
27
27
  print_solution! "01"
28
28
  # No nice way to say x is unconstrained
@@ -31,17 +31,17 @@ end
31
31
 
32
32
  class AlgebraProblem03 < AlgebraProblem
33
33
  def declare_abs(var, expr)
34
- solver.assert Z3::Ast.or(
35
- Z3::Ast.and(var == expr, expr >= 0),
36
- Z3::Ast.and(var == -expr, expr <= 0)
34
+ solver.assert Z3.Or(
35
+ Z3.And(var == expr, expr >= 0),
36
+ Z3.And(var == -expr, expr <= 0)
37
37
  )
38
38
  end
39
39
 
40
40
  def solve!
41
- x = Z3::Ast.real("x")
42
- xm2abs = Z3::Ast.real("|x-2|")
41
+ x = Z3.Real("x")
42
+ xm2abs = Z3.Real("|x-2|")
43
43
  declare_abs(xm2abs, x-2)
44
- m6abs = Z3::Ast.real("|-6|")
44
+ m6abs = Z3.Real("|-6|")
45
45
  declare_abs(m6abs, -6)
46
46
  solver.assert x < 2
47
47
  solver.assert xm2abs == 4*m6abs
@@ -56,11 +56,11 @@ class AlgebraProblem04 < AlgebraProblem
56
56
  end
57
57
 
58
58
  def solve!
59
- ax = Z3::Ast.real("ax")
60
- ay = Z3::Ast.real("ay")
61
- bx = Z3::Ast.real("bx")
62
- by = Z3::Ast.real("by")
63
- a_b = Z3::Ast.real("|a-b|")
59
+ ax = Z3.Real("ax")
60
+ ay = Z3.Real("ay")
61
+ bx = Z3.Real("bx")
62
+ by = Z3.Real("by")
63
+ a_b = Z3.Real("|a-b|")
64
64
  declare_distance(a_b, ax, ay, bx, by)
65
65
  solver.assert ax == -4
66
66
  solver.assert ay == -5
@@ -72,8 +72,8 @@ end
72
72
 
73
73
  class AlgebraProblem05 < AlgebraProblem
74
74
  def solve!
75
- x = Z3::Ast.real("x")
76
- y = Z3::Ast.real("y")
75
+ x = Z3.Real("x")
76
+ y = Z3.Real("y")
77
77
  solver.assert 2*x - 4*y == 9
78
78
  solver.assert y == 0
79
79
  print_solution! "05"
@@ -86,11 +86,11 @@ class AlgebraProblem06 < AlgebraProblem
86
86
  end
87
87
 
88
88
  def solve!
89
- x1 = Z3::Ast.real("x1")
90
- y1 = Z3::Ast.real("y1")
91
- x2 = Z3::Ast.real("x2")
92
- y2 = Z3::Ast.real("y2")
93
- answer = Z3::Ast.real("answer")
89
+ x1 = Z3.Real("x1")
90
+ y1 = Z3.Real("y1")
91
+ x2 = Z3.Real("x2")
92
+ y2 = Z3.Real("y2")
93
+ answer = Z3.Real("answer")
94
94
  solver.assert x2 == 2
95
95
  solver.assert x1 == 1
96
96
  declare_f(x1, y1)
@@ -102,15 +102,15 @@ end
102
102
 
103
103
  class AlgebraProblem10 < AlgebraProblem
104
104
  def declare_abs(var, expr)
105
- solver.assert Z3::Ast.or(
106
- Z3::Ast.and(var == expr, expr >= 0),
107
- Z3::Ast.and(var == -expr, expr <= 0)
105
+ solver.assert Z3.Or(
106
+ Z3.And(var == expr, expr >= 0),
107
+ Z3.And(var == -expr, expr <= 0)
108
108
  )
109
109
  end
110
110
 
111
111
  def solve!
112
- x = Z3::Ast.real("x")
113
- y = Z3::Ast.real("|-2x + 2|")
112
+ x = Z3.Real("x")
113
+ y = Z3.Real("|-2x + 2|")
114
114
  declare_abs(y, -2*x + 2)
115
115
  solver.assert y - 3 == -3
116
116
  print_solution! "10"
@@ -3,8 +3,8 @@
3
3
  require_relative "../lib/z3"
4
4
 
5
5
  def check_multiplication_laws_1
6
- a = Z3::Ast.int("a")
7
- b = Z3::Ast.int("b")
6
+ a = Z3.Int("a")
7
+ b = Z3.Int("b")
8
8
  solver = Z3::Solver.new
9
9
  puts "Checking if (a+b)(a-b)==a*a-b*b"
10
10
  solver.prove!(
@@ -13,17 +13,17 @@ def check_multiplication_laws_1
13
13
  end
14
14
 
15
15
  def check_inequalities
16
- a = Z3::Ast.int("a")
17
- b = Z3::Ast.int("b")
16
+ a = Z3.Int("a")
17
+ b = Z3.Int("b")
18
18
  solver = Z3::Solver.new
19
19
  puts "Checking if a+b >= a"
20
- solver.prove!(a+b >= a)
20
+ solver.prove! a+b >= a
21
21
 
22
22
  solver = Z3::Solver.new
23
- solver.assert(a >= 0)
24
- solver.assert(b >= 0)
23
+ solver.assert a >= 0
24
+ solver.assert b >= 0
25
25
  puts "Checking if a+b >= a if a,b >= 0"
26
- solver.prove!(a+b >= a)
26
+ solver.prove! a+b >= a
27
27
  end
28
28
 
29
29
  check_multiplication_laws_1
data/examples/basic_logic CHANGED
@@ -3,16 +3,16 @@
3
3
  require_relative "../lib/z3"
4
4
 
5
5
  def check_if_true_is_true
6
- a = Z3::Ast.true
7
- b = Z3::Ast.true
6
+ a = Z3.True
7
+ b = Z3.True
8
8
  solver = Z3::Solver.new
9
9
  puts "Checking if true == true"
10
10
  solver.prove!(a == b)
11
11
  end
12
12
 
13
13
  def check_if_true_is_false
14
- a = Z3::Ast.true
15
- b = Z3::Ast.false
14
+ a = Z3.True
15
+ b = Z3.False
16
16
  solver = Z3::Solver.new
17
17
  puts "Checking if true == false"
18
18
  solver.prove!(a==b)
@@ -20,16 +20,16 @@ end
20
20
 
21
21
  # We prove it by checking that negation is unsatisfiable
22
22
  def check_de_morgan_law_1
23
- a = Z3::Ast::bool("a")
24
- b = Z3::Ast::bool("b")
23
+ a = Z3.Bool("a")
24
+ b = Z3.Bool("b")
25
25
  solver = Z3::Solver.new
26
26
  puts "Proving ~(a & b) == (~a | ~b)"
27
27
  solver.prove!(~(a & b) == (~a | ~b))
28
28
  end
29
29
 
30
30
  def check_de_morgan_law_2
31
- a = Z3::Ast::bool("a")
32
- b = Z3::Ast::bool("b")
31
+ a = Z3.Bool("a")
32
+ b = Z3.Bool("b")
33
33
  solver = Z3::Solver.new
34
34
  puts "Proving ~(a | b) == (~a & ~b)"
35
35
  solver.prove!(~(a | b) == (~a & ~b))
@@ -0,0 +1,161 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require_relative "../lib/z3"
4
+
5
+ def validate_sign_by_shift!
6
+ """
7
+ sign = v >> (sizeof(int) * CHAR_BIT - 1);
8
+ """
9
+ solver = Z3::Solver.new
10
+ v = Z3.Bitvec("v", 32)
11
+ s = v >> 31
12
+ puts "Validating sign trick:"
13
+ solver.prove! ((v < 0) & (s == -1)) | ((v >= 0) & (s == 0))
14
+ end
15
+
16
+ def validate_opposite_sign_by_xor!
17
+ """
18
+ int x, y; // input values to compare signs
19
+ bool f = ((x ^ y) < 0); // true iff x and y have opposite signs
20
+ """
21
+ solver = Z3::Solver.new
22
+ x = Z3.Bitvec("x", 32)
23
+ y = Z3.Bitvec("y", 32)
24
+ f = (x^y) < 0
25
+
26
+ puts "Validating sign trick:"
27
+ solver.prove!(
28
+ Z3.Or(
29
+ Z3.And(x >= 0, y >= 0, f == false),
30
+ Z3.And(x < 0, y < 0, f == false),
31
+ Z3.And(x >= 0, y < 0, f == true),
32
+ Z3.And(x < 0, y >= 0, f == true),
33
+ )
34
+ )
35
+ end
36
+
37
+ def validate_abs_without_branching_1!
38
+ """
39
+ int v; // we want to find the absolute value of v
40
+ unsigned int r; // the result goes here
41
+ int const mask = v >> sizeof(int) * CHAR_BIT - 1;
42
+ r = (v + mask) ^ mask;
43
+ """
44
+ solver = Z3::Solver.new
45
+ v = Z3.Bitvec("v", 32)
46
+ mask = v >> 31
47
+ r = (v + mask) ^ mask
48
+ puts "Validating abs without branching, version 1"
49
+ solver.prove!(
50
+ Z3.Or(
51
+ Z3.And(v >= 0, r==v),
52
+ Z3.And(v < 0, r==-v)
53
+ )
54
+ )
55
+ end
56
+
57
+ def validate_abs_without_branching_2!
58
+ """
59
+ int v; // we want to find the absolute value of v
60
+ unsigned int r; // the result goes here
61
+ int const mask = v >> sizeof(int) * CHAR_BIT - 1;
62
+ r = (v ^ mask) - mask;
63
+ """
64
+ solver = Z3::Solver.new
65
+ v = Z3.Bitvec("v", 32)
66
+ mask = v >> 31
67
+ r = (v^mask) - mask
68
+ puts "Validating abs without branching, version 2"
69
+ solver.prove!(
70
+ Z3.Or(
71
+ Z3.And(v >= 0, r==v),
72
+ Z3.And(v < 0, r==-v)
73
+ )
74
+ )
75
+ end
76
+
77
+ def c_boolean(solver, bool_expr, name)
78
+ expr_val = Z3.Bitvec(name, 32)
79
+ solver.assert(
80
+ Z3.Or(
81
+ Z3.And(bool_expr, expr_val == 1),
82
+ Z3.And(~bool_expr, expr_val == 0),
83
+ )
84
+ )
85
+ expr_val
86
+ end
87
+
88
+ def validate_min_without_branching!
89
+ """
90
+ int x; // we want to find the minimum of x and y
91
+ int y;
92
+ int r; // the result goes here
93
+ r = y ^ ((x ^ y) & -(x < y)); // min(x, y)
94
+ """
95
+ solver = Z3::Solver.new
96
+ x = Z3.Bitvec("x", 32)
97
+ y = Z3.Bitvec("y", 32)
98
+ r = y ^ ((x ^ y) & -c_boolean(solver, x < y, "t"))
99
+ puts "Validating min without branching"
100
+ solver.prove!(
101
+ Z3.Or(
102
+ Z3.And(x <= y, r == x),
103
+ Z3.And(y <= y, r == y),
104
+ )
105
+ )
106
+ end
107
+
108
+ def validate_max_without_branching!
109
+ """
110
+ int x; // we want to find the minimum of x and y
111
+ int y;
112
+ int r; // the result goes here
113
+ r = x ^ ((x ^ y) & -(x < y)); // max(x, y)
114
+ """
115
+ solver = Z3::Solver.new
116
+ x = Z3.Bitvec("x", 32)
117
+ y = Z3.Bitvec("y", 32)
118
+ r = x ^ ((x ^ y) & -c_boolean(solver, x < y, "t"))
119
+ puts "Validating max without branching"
120
+ solver.prove!(
121
+ Z3.Or(
122
+ Z3.And(x >= y, r == x),
123
+ Z3.And(y >= y, r == y),
124
+ )
125
+ )
126
+ end
127
+
128
+ def validate_is_power_of_two!
129
+ """
130
+ unsigned int v; // we want to see if v is a power of 2
131
+ bool f; // the result goes here
132
+
133
+ f = (v & (v - 1)) == 0;
134
+ """
135
+ solver = Z3::Solver.new
136
+ v = Z3.Bitvec("v", 32)
137
+ f = (v & (v - 1)) == 0
138
+
139
+ powers_of_two = (0..31).map{|i| 2**i}
140
+ naive_is_f_power_of_two = Z3.Or(*powers_of_two.map{|k| v == k})
141
+
142
+ puts "Validating is power of two"
143
+ solver.prove!(
144
+ Z3.Or(
145
+ f == naive_is_f_power_of_two,
146
+ v == 0,
147
+ )
148
+ )
149
+ end
150
+
151
+ validate_sign_by_shift!
152
+ validate_opposite_sign_by_xor!
153
+ validate_abs_without_branching_1!
154
+ validate_abs_without_branching_2!
155
+ validate_min_without_branching!
156
+ validate_max_without_branching!
157
+ validate_is_power_of_two!
158
+
159
+ """
160
+ Based on https://graphics.stanford.edu/~seander/bithacks.html
161
+ """
@@ -112,7 +112,7 @@ class Bridges
112
112
  end
113
113
 
114
114
  def int012(x, y, d)
115
- v = Z3::Ast.int("#{x},#{y},#{d}")
115
+ v = Z3.Int("#{x},#{y},#{d}")
116
116
  @solver.assert v >= 0
117
117
  @solver.assert v <= 2
118
118
  v
@@ -0,0 +1,135 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ # NOTE: This is not part of integration test suite as it's fairly slow (30s or so on my machine)
4
+
5
+ require_relative "../lib/z3"
6
+
7
+ class CLogicPuzzleSolver
8
+ attr_reader :solver, :wv
9
+ def initialize
10
+ @solver = Z3::Solver.new
11
+ @digits = ["b", "c", "d", "f", "g", "h", "i", "j", "l", "n", "o", "q", "s", "t", "u", "v", "w", "x", "y", "z"]
12
+ @digit_vars = Hash[@digits.map{|v| [v, digit_var(v)]}]
13
+ @words = [
14
+ "bytwycju",
15
+ "bzuwtwol",
16
+ "cgyoyfjg",
17
+ "ctvtwysu",
18
+ "dhcqxtfw",
19
+ "diffhlnl",
20
+ "fjivucti",
21
+ "htwizvwi",
22
+ "jqxizzxq",
23
+ "niiqztgs",
24
+ "nshtztns",
25
+ "nttuhlnq",
26
+ "oigsjgoj",
27
+ "oncycbxh",
28
+ "oqbctlzh",
29
+ "oqcnwbsd",
30
+ "sfgsoxdd",
31
+ "sugvqgww",
32
+ "syhjizjq",
33
+ "szblgodf",
34
+ "thhwohwn",
35
+ "ttligxut",
36
+ "uclfqvdu",
37
+ "udluvhcz",
38
+ "ugdztnwv",
39
+ "uxztiywn",
40
+ "vugljtyn",
41
+ "vyhyjivb",
42
+ "wwnnnqbw",
43
+ "xbfziozy",
44
+ "xsvuojtx",
45
+ "yjjowdqh",
46
+ "yzdgotby",
47
+ "yzvyjjdy",
48
+ "zoljwdfl",
49
+ ]
50
+ @wv = Hash[@words.map{|w| [w, word_var(w)]}]
51
+ end
52
+
53
+ def digit_var(name)
54
+ v = Z3.Bitvec(name, 32)
55
+ solver.assert v >= 0
56
+ solver.assert v <= 9
57
+ v
58
+ end
59
+
60
+ def word_var(name)
61
+ v = Z3.Bitvec(name, 32)
62
+ digits = name.chars.map{|c| @digit_vars[c] }
63
+ val_v = digits.inject(0){|a,b| a*10+b}
64
+ solver.assert val_v == v
65
+ v
66
+ end
67
+
68
+ def solve!
69
+ # Everyone occurs twice, this is not quite that
70
+ solver.assert Z3.Add(*@digit_vars.values) == 90
71
+
72
+ # ^| operator precedence is not like Python's for some reason
73
+
74
+ solver.assert wv["bytwycju"] + wv["yzvyjjdy"] ^ wv["vugljtyn"] + wv["ugdztnwv"] | wv["xbfziozy"] == wv["bzuwtwol"]
75
+ solver.assert wv["wwnnnqbw"] - wv["uclfqvdu"] & wv["oncycbxh"] |(wv["oqcnwbsd"] ^ wv["cgyoyfjg"])== wv["vyhyjivb"]
76
+ solver.assert wv["yzdgotby"] | wv["oigsjgoj"] | wv["ttligxut"] - wv["dhcqxtfw"] & wv["szblgodf"] == wv["sfgsoxdd"]
77
+ solver.assert wv["yjjowdqh"] & wv["niiqztgs"] + wv["ctvtwysu"] & wv["diffhlnl"] - wv["thhwohwn"] == wv["xsvuojtx"]
78
+ solver.assert wv["nttuhlnq"] ^ wv["oqbctlzh"] - wv["nshtztns"] ^ wv["htwizvwi"] + wv["udluvhcz"] == wv["syhjizjq"]
79
+
80
+ solver.assert wv["bytwycju"] ^ wv["wwnnnqbw"] & wv["yzdgotby"] + wv["yjjowdqh"] - wv["nttuhlnq"] == wv["fjivucti"]
81
+ solver.assert wv["yzvyjjdy"] ^ wv["uclfqvdu"] & wv["oigsjgoj"] + wv["niiqztgs"] - wv["oqbctlzh"] == wv["zoljwdfl"]
82
+ solver.assert wv["vugljtyn"] ^ wv["oncycbxh"] & wv["ttligxut"] + wv["ctvtwysu"] - wv["nshtztns"] == wv["sugvqgww"]
83
+ solver.assert wv["ugdztnwv"] ^ wv["oqcnwbsd"] & wv["dhcqxtfw"] + wv["diffhlnl"] - wv["htwizvwi"] == wv["uxztiywn"]
84
+ solver.assert wv["xbfziozy"] ^ wv["cgyoyfjg"] & wv["szblgodf"] + wv["thhwohwn"] - wv["udluvhcz"] == wv["jqxizzxq"]
85
+
86
+ key = %W[iw hu fv lu dv cy og lc gy fq od lo fq is ig gu hs hi ds cy oo os iu fs gu lh dq lv gu iw hv gu di hs cy oc iw gc]
87
+
88
+ if solver.check == :sat
89
+ model = solver.model
90
+ model_vars = {}
91
+ model.each do |k,v|
92
+ v = v.to_s.sub(/\A#x/, "").to_i(16)
93
+ model_vars[k] = v
94
+ puts "#{k.to_s}=#{v.to_s}"
95
+ end
96
+ result = key.map do |ab|
97
+ a,b = ab.chars
98
+ av = model_vars[a]
99
+ bv = model_vars[b]
100
+ (10*av+bv).chr
101
+ end
102
+ puts "Result: #{result.join}"
103
+ else
104
+ puts "failed to solve"
105
+ end
106
+ end
107
+ end
108
+
109
+ CLogicPuzzleSolver.new.solve!
110
+
111
+ # The puzzle:
112
+ """
113
+ We've captured a strange message. It looks like it is encrypted somehow ...
114
+ iw, hu, fv, lu, dv, cy, og, lc, gy, fq, od, lo, fq, is, ig, gu, hs, hi, ds, cy, oo, os, iu, fs, gu, lh, dq, lv, gu, iw, hv, gu, di, hs, cy, oc, iw, gc
115
+
116
+
117
+ We've also intercepted what seems to be a hint to the key:
118
+ bytwycju + yzvyjjdy ^ vugljtyn + ugdztnwv | xbfziozy = bzuwtwol
119
+ ^ ^ ^ ^ ^
120
+ wwnnnqbw - uclfqvdu & oncycbxh | oqcnwbsd ^ cgyoyfjg = vyhyjivb
121
+ & & & & &
122
+ yzdgotby | oigsjgoj | ttligxut - dhcqxtfw & szblgodf = sfgsoxdd
123
+ + + + + +
124
+ yjjowdqh & niiqztgs + ctvtwysu & diffhlnl - thhwohwn = xsvuojtx
125
+ - - - - -
126
+ nttuhlnq ^ oqbctlzh - nshtztns ^ htwizvwi + udluvhcz = syhjizjq
127
+ = = = = =
128
+ fjivucti zoljwdfl sugvqgww uxztiywn jqxizzxq
129
+
130
+ Note:
131
+ assume q != 0
132
+ a letter is a decimal digit is a letter
133
+ each digit has exactly two different letter representations
134
+ C-like operator precedence
135
+ """