Here is an example of an invalid solution. While its rows add up to
10, its columns do not. So how one would go about finding a valid
solution?
Brute Force Approach
Well, we can start to search by evaluating every possible 4x4 matrix
until we find a valid solution. But how many matrices are there to
evaluate? How do we generate them? Thankfully, a 4x4 matrix can be
represented as a 16 digit number. Here is one, letโs call it \(s\), \(1111 \
1111 \ 1111 \ 1111\). That would be a starting point. We can be
certain that \(4444 \ 4444 \ 4444 \
4444\) is the end and lets name it \(e\). Everything in between is a potential
solution, so we have \(e-s\) which ends
up being over 3 quadrillion candidates or 3 million billion.
I wrote functions to convert between 16 digit numbers and 4x4
matrices and check whether the solution is valid according to the game
rules.
num_to_vec = function(n) {
'
Convert a number into a vector of its constituent digits.
'
vec = as.numeric(strsplit(as.character(n), "")[[1]])
stopifnot(length(vec) == 16)
return(vec)
}
vec_to_mat = function(v) {
'
Convert a 16 size vector to a 4x4 matrix.
'
if (typeof(v) == 'double'){v = num_to_vec(v)}
stopifnot(length(v) == 16)
return(matrix(v, ncol=4, byrow=TRUE))
}
check_solution = function(sol) {
'
Check if solution is valid.
'
# vector to matrix
sol = vec_to_mat(sol)
# diagonal positions
pos_diag1 = c(1,6,11,16)
pos_diag2 = c(4,7,10,13)
# col and row sums
d1 = sum(sol[pos_diag1]) == 10
d2 = sum(sol[pos_diag2]) == 10
col_sums = length(which(colSums(sol) != 10)) == 0
row_sums = length(which(rowSums(sol) != 10)) == 0
if(d1 & d2 & col_sums & row_sums) {
print(sol)
print('Solution is valid')
return(TRUE)
}
return(FALSE)
}
# define start and end
s = 1111111111111111
e = 4444444444444444
# convert number to vector
num_to_vec(s)
[1] 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
# convert vector to matrix and print
write_matex(vec_to_mat(s))
\(\begin{bmatrix} 1&1&1&1 \\
1&1&1&1 \\ 1&1&1&1 \\ 1&1&1&1 \\
\end{bmatrix}\)
# check if matrix is a valid solution
check_solution(s)
[1] FALSE
Now, we are ready to start looping through the quadrillions of
matrices. You will find out that this loop is unlikely to complete on a
domestic computer. It will not even reach one valid solution before the
computer runs out of memory.
# loop through all possible 4x4 matrices
candidates = s:e
for (c in candidates) {
res = check_solution(c)
if(res){print(c); break}
}
If we just try to load the last element in the candidates vector
(which willl generate the whole vector), the R interpreter lets us know
that we are short of just 12K GB of memory.
candidates[-1]
A generator with a yield statement would be my next step. Ultimately,
this brute force approach is too naive, does not take advantage of any
optimizations and it probably is better to implement in C or some other
low level language. Do we have other ways to get just one valid solution
with the help of R? Thankfully, we do.
Integer Linear Programming
ILP is a great tool to have in your toolkit. ILP enables us to find
optimal solutions for a given a set of constraints and an objective
function using linear optimization.
Below is a simple example. \(x_1\)
and \(x_2\) are implicitly assumed to
be integers. In some cases, they also may be set to binary, either a
\(0\) or \(1\) .
\[\begin{align}
\max \ x_1 + x_2 \\
\text{subject to} \notag
-x_1 + x_2 \leq 12 \\
3x_1 + 2x_2 = 5
\end{align}\]
The game rules I stated in the beginning define linear relationships
between the elements of the 4x4 matrix. We can translate those rules
into the constraints and the magic squares game into an Integer Linear
Program. So letโs do that. Letโs start with the objective function.
We already know that we have 16 elements in a 4x4 matrix, for each
element we may use one of the four numbers \(\{1,2,3,4\}\). This means we have \(16*4 = 64\) binary variables. Our objective
function will be the sum \(x_1,x_2,...,x_n\) where \(n\) is \(64\). Great. Normally we would say we want
to maximize the objective function. Here we know that we need exactly 16
elements for any valid solution out of 64. So we can just set the
objective function to be equal to 16. I will write down our ILP in
notation and explain each constraint.
\[\begin{align}
\sum_{n=1}^{64} x_n = 16 \\
\text{subject to} \notag
\sum diag(x_n) = 10 \ & (1) \\
\sum col(x_n) = 10 \ & (2) \\
\sum row(x_n) = 10 \ &(3) \\
\sum cell(x_n) = 1 \ & (4) \\
\sum digit(x_n) = 4 \ & (5)
\end{align}\]
I am certainly taking liberty in the way I use notation here. (1),
(2), (3) are straightforward and make sure that \(x\)โs that correspond to rows, columns and
diagonals sum up to 10. (4) makes sure that \(x\)โs that correspond to the same cell in
the matrix sum up to 1. Indeed, each cell in the matrix can only have
one number from four available and thats why only one corresponding
\(x\) can be non zero. (5) ensures that
all four available numbers are used at least once. Implicitly, the \(x\)โs are binary.
So we actually ended up having a system of equalities. Letโs solve
this system using the lpsolve package.
lpsolve will need the objective function, the constraint
matrix, the equality/inequality direction, and finally the right hand
side of the equalities/inequalities. I code everything below.
# Helper functions to build constraints and process solutions
position = function(el) {
'
Get vector of positions of elements in a ncols(el)*4
matrix that correspond to positions in the original 4*4 matrix.
'
x1 = el*4-4+1
x2 = el*4
return(x1:x2)
}
constraint = function(el_pos,val=1, raw_pos = FALSE) {
'
Create constraint vector with 64 elements given element positions
'
base_vec = rep(0,4*16)
if(!raw_pos) {poss = c(sapply(el_pos, position))}
else {poss = el_pos}
base_vec[poss] = rep(val,length(poss)/length(val))
return(base_vec)
}
# define coefficients in objective function
obj = rep(1,4*16)
# set up constraints base vector
cons_base = rep(0,4*16)
# diagonal sums are 10
pos_diag1 = c(1,6,11,16)
pos_diag2 = c(4,7,10,13)
con_diag1 = constraint(pos_diag1,val=1:4)
con_diag2 = constraint(pos_diag2,1:4)
diag_cons = rbind(con_diag1,con_diag2)
rownames(diag_cons) = rep('diag_sums',2)
# col sums are 10
col_cons = c()
col_positions = matrix(seq(1,16),nrow = 4)
for(row in 1:nrow(col_positions)) {
col_cons = rbind(col_cons, constraint(col_positions[row,],1:4))
}
rownames(col_cons) = rep('col_sums',4)
# row sums are 10
row_cons = c()
row_positions = matrix(seq(1,16), ncol = 4,byrow=TRUE)
for(row in 1:nrow(row_positions)) {
row_cons = rbind(row_cons,constraint(row_positions[row,],1:4))
}
rownames(row_cons) = rep('row_sums',4)
# each cell can have only one value out of 4 available
only_one_val = t(sapply(1:16,constraint))
# each number must appear 4 times
appear4 = rbind(constraint(seq(1,64,by=4), raw_pos=TRUE),
constraint(seq(2,64,by=4), raw_pos=TRUE),
constraint(seq(3,64,by=4), raw_pos=TRUE),
constraint(seq(4,64,by=4), raw_pos=TRUE))
# create constraint matrix
cons = rbind(diag_cons,col_cons,row_cons, only_one_val, appear4)
The cons matrix has 64 columns and a constraint for each
game rule we outlined. Letโs define relationship direction and right
hand sides.
# Set inequality/equality signs
f.dir <- c(rep("=",nrow(diag_cons)),
rep("=",nrow(col_cons)),
rep("=",nrow(row_cons)),
rep("=",nrow(only_one_val)),
rep("=",nrow(appear4))
)
# Set right hand side coefficients
f.rhs <- c(rep(10,nrow(diag_cons)),
rep(10,nrow(col_cons)),
rep(10,nrow(row_cons)),
rep(1,nrow(only_one_val)),
rep(4,nrow(appear4)))
Finally, we are ready to solve this ILP and see if can obtain at
least one valid solution without runnning out of memory or searching
through a quadrillion numbers.
library(lpSolve)
# Solve ILP
model = lp("max", obj, cons, f.dir, f.rhs, all.bin=T,use.rw=TRUE)
print(model)
Success: the objective function is 16
model$solution
[1] 0 0 0 1 1 0 0 0 0 0 0 1 1 0 0 0 1 0 0 0 0 0 1 0 0 1 0 0 0 0 0 1 0 1 0 0 0 0 0 1 1 0 0 0 0 0 1
[48] 0 0 0 1 0 0 1 0 0 0 0 1 0 0 1 0 0
Ok, great. Seems we got a solution quite quickly, almost instantly.
Looks confusing. Above vector shows us which 16 of 64 \(x\) variables are activated and can be
included in the solution matrix. I define a function to translate the
solution vector into a matrix. Letโs check!
collect_solutions = function(sol) {
'
Print out the solution matrices.
'
sol = sol[1:(as.integer(length(sol)/64)*64)]
base_vec = rep(1:4,16)
sol = matrix(sol,ncol=64, byrow = TRUE)
solutions = c()
for(s in 1:nrow(sol)){
sol_mat = matrix(base_vec[which(sol[s,]>0)], ncol = 4)
solutions = rbind(solutions,c(sol_mat))
}
return(solutions)
}
sol = collect_solutions(model$solution)
sol # the vector has 16 elements. Great!
[,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8] [,9] [,10] [,11] [,12] [,13] [,14] [,15] [,16]
[1,] 4 1 4 1 1 3 2 4 2 4 1 3 3 2 3 2
check_solution(sol)
[,1] [,2] [,3] [,4]
[1,] 4 1 4 1
[2,] 1 3 2 4
[3,] 2 4 1 3
[4,] 3 2 3 2
[1] "Solution is valid"
[1] TRUE
After checking our solution according to the game rules we see it is
valid. We basically created a solver for a variation of the magic
squares game using ILP and lpsolve. In general, any problem
with linear constraints and an objective can be solved using Linear
Optimization. Hope this illustration was informative to you.
\[\begin{bmatrix}
๐&๐ง&๐&๐ \\
๐&๐&๐&๐ \\
๐&๐&๐&๐ \\
๐&๐ก&๐&๐ \\
\end{bmatrix}\]
LS0tCnRpdGxlOiAiTWFnaWMgU3F1YXJlcyIKb3V0cHV0OgogIGh0bWxfbm90ZWJvb2s6CiAgICBjb2RlX2ZvbGRpbmc6IHNob3cKICBodG1sX2RvY3VtZW50OgogICAgZGZfcHJpbnQ6IHBhZ2VkCi0tLQoKYGBge3IsIGVjaG8gPSBGYWxzZSwgcmVzdWx0cz0naGlkZSd9CndyaXRlX21hdGV4IDwtIGZ1bmN0aW9uKHgsIGNlbnRlcj1GKSB7CiAgbWFyayA9ICckJwogIGlmIChjZW50ZXIpIHttYXJrPSckJCd9CiAgYmVnaW4gPC0gcGFzdGUwKG1hcmssIlxcYmVnaW57Ym1hdHJpeH0iKQogIGVuZCA8LSBwYXN0ZTAoIlxcZW5ke2JtYXRyaXh9IiwgbWFyaykKICBYIDwtCiAgICBhcHBseSh4LCAxLCBmdW5jdGlvbih4KSB7CiAgICAgIHBhc3RlKAogICAgICAgIHBhc3RlKHgsIGNvbGxhcHNlID0gIiYiKSwKICAgICAgICAiXFxcXCIKICAgICAgKQogICAgfSkKICB3cml0ZUxpbmVzKGMoYmVnaW4sIFgsIGVuZCkpCn0KYGBgCgokJFxiZWdpbntibWF0cml4fQrwn5CvJvCfmIUm8J+Sqybwn46pIFxcCvCfkLAm8J+Qhybwn4yZJvCfjJ8gXFwK4pyoJvCfjokm8J+OsSbwn4OPIFxcCvCflK4m8J+UkybinIUm8J+agCBcXApcZW5ke2JtYXRyaXh9JCQKVGhlIE1hZ2ljIFNxdWFyZXMgZ2FtZSBpcyBhIHNpbXBsZSB5ZXQgZW5nYWdpbmcgcHJvYmxlbS4gVGhlcmUgYXJlIG1hbnkgdmFyaWF0aW9ucyBhbmQgdGhlIG9uZSBJIGFtIGludGVyZXN0ZWQgaW4gaGFzIHRoZSBmb2xsb3dpbmcgcnVsZXMKCiAgLSAxLiBVc2UgNCBieSA0IG1hdHJpeAogIC0gMi4gRWFjaCBjZWxsIGNhbiBoYXZlIGEgbnVtYmVyIGZyb20gJFx7MSwyLDMsNFx9JAogIC0gMy4gQWxsIHJvd3Mgc2hvdWxkIGFkZCB1cCB0byAxMAogIC0gNC4gQWxsIGNvbHVtbnMgc2hvdWxkIGFkZCB1cCB0byAxMAogIC0gNS4gQWxsIGRpYWdvbmFscyBzaG91bGQgYWRkIHVwIHRvIDEwCiAgCkhlcmUgaXMgYW4gZXhhbXBsZSBvZiBhbiBpbnZhbGlkIHNvbHV0aW9uLiBXaGlsZSBpdHMgcm93cyBhZGQgdXAgdG8gMTAsIGl0cyBjb2x1bW5zIGRvIG5vdC4gU28gaG93IG9uZSB3b3VsZCBnbyBhYm91dCBmaW5kaW5nIGEgdmFsaWQgc29sdXRpb24/CgpgYGB7ciwgZWNobz1GYWxzZSwgcmVzdWx0cz0nYXNpcyd9Cm0gPSBtYXRyaXgocmVwKDE6NCw0KSxuY29sPTQsIGJ5cm93ID0gVCkKd3JpdGVfbWF0ZXgobSkKYGBgCiMgQnJ1dGUgRm9yY2UgQXBwcm9hY2gKCldlbGwsIHdlIGNhbiBzdGFydCB0byBzZWFyY2ggYnkgZXZhbHVhdGluZyBldmVyeSBwb3NzaWJsZSA0eDQgbWF0cml4IHVudGlsIHdlIGZpbmQgYSB2YWxpZCBzb2x1dGlvbi4gQnV0IGhvdyBtYW55IG1hdHJpY2VzIGFyZSB0aGVyZSB0byBldmFsdWF0ZT8gSG93IGRvIHdlIGdlbmVyYXRlIHRoZW0/IFRoYW5rZnVsbHksIGEgNHg0IG1hdHJpeCBjYW4gYmUgcmVwcmVzZW50ZWQgYXMgYSAxNiBkaWdpdCBudW1iZXIuIEhlcmUgaXMgb25lLCBsZXQncyBjYWxsIGl0ICRzJCwgJDExMTEgXCAxMTExIFwgMTExMSBcIDExMTEkLiBUaGF0IHdvdWxkIGJlIGEgc3RhcnRpbmcgcG9pbnQuIFdlIGNhbiBiZSBjZXJ0YWluIHRoYXQgJDQ0NDQgXCA0NDQ0IFwgNDQ0NCBcIDQ0NDQkIGlzIHRoZSBlbmQgYW5kIGxldHMgbmFtZSBpdCAkZSQuIEV2ZXJ5dGhpbmcgaW4gYmV0d2VlbiBpcyBhIHBvdGVudGlhbCBzb2x1dGlvbiwgc28gd2UgaGF2ZSAkZS1zJCB3aGljaCBlbmRzIHVwIGJlaW5nIG92ZXIgMyBxdWFkcmlsbGlvbiBjYW5kaWRhdGVzIG9yIDMgbWlsbGlvbiBiaWxsaW9uLiAKCkkgd3JvdGUgZnVuY3Rpb25zIHRvIGNvbnZlcnQgYmV0d2VlbiAxNiBkaWdpdCBudW1iZXJzIGFuZCA0eDQgbWF0cmljZXMgYW5kIGNoZWNrIHdoZXRoZXIgdGhlIHNvbHV0aW9uIGlzIHZhbGlkIGFjY29yZGluZyB0byB0aGUgZ2FtZSBydWxlcy4KCmBgYHtyLCBjbGFzcy5zb3VyY2UgPSAnZm9sZC1oaWRlJywgZXZhbD1GfQpudW1fdG9fdmVjID0gZnVuY3Rpb24obikgewogICcKICBDb252ZXJ0IGEgbnVtYmVyIGludG8gYSB2ZWN0b3Igb2YgaXRzIGNvbnN0aXR1ZW50IGRpZ2l0cy4KICAnCiAgdmVjID0gYXMubnVtZXJpYyhzdHJzcGxpdChhcy5jaGFyYWN0ZXIobiksICIiKVtbMV1dKQogIHN0b3BpZm5vdChsZW5ndGgodmVjKSA9PSAxNikKICByZXR1cm4odmVjKQp9Cgp2ZWNfdG9fbWF0ID0gZnVuY3Rpb24odikgewogICcKICBDb252ZXJ0IGEgMTYgc2l6ZSB2ZWN0b3IgdG8gYSA0eDQgbWF0cml4LgogICcKICBpZiAodHlwZW9mKHYpID09ICdkb3VibGUnKXt2ID0gbnVtX3RvX3ZlYyh2KX0KICBzdG9waWZub3QobGVuZ3RoKHYpID09IDE2KQogIHJldHVybihtYXRyaXgodiwgbmNvbD00LCBieXJvdz1UUlVFKSkKfQoKCmNoZWNrX3NvbHV0aW9uID0gZnVuY3Rpb24oc29sKSB7CiAgJwogIENoZWNrIGlmIHNvbHV0aW9uIGlzIHZhbGlkLgogICcKICAjIHZlY3RvciB0byBtYXRyaXgKICBzb2wgPSB2ZWNfdG9fbWF0KHNvbCkKICAKICAjIGRpYWdvbmFsIHBvc2l0aW9ucwogIHBvc19kaWFnMSA9IGMoMSw2LDExLDE2KQogIHBvc19kaWFnMiA9IGMoNCw3LDEwLDEzKQogIAogICMgY29sIGFuZCByb3cgc3VtcwogIGQxID0gc3VtKHNvbFtwb3NfZGlhZzFdKSA9PSAxMAogIGQyID0gc3VtKHNvbFtwb3NfZGlhZzJdKSA9PSAxMAogIAogIGNvbF9zdW1zID0gbGVuZ3RoKHdoaWNoKGNvbFN1bXMoc29sKSAhPSAxMCkpID09IDAKICByb3dfc3VtcyA9IGxlbmd0aCh3aGljaChyb3dTdW1zKHNvbCkgIT0gMTApKSA9PSAwCgogIGlmKGQxICYgZDIgJiBjb2xfc3VtcyAmIHJvd19zdW1zKSB7CiAgICBwcmludChzb2wpCiAgICBwcmludCgnU29sdXRpb24gaXMgdmFsaWQnKQogICAgcmV0dXJuKFRSVUUpCiAgfSAKICByZXR1cm4oRkFMU0UpCn0KCmBgYAoKYGBge3IsIGNsYXNzLnNvdXJjZT0nZm9sZC1zaG93JywgZXZhbCA9IFRSVUUsIHJlc3VsdHM9J2FzaXMnfQojIGRlZmluZSBzdGFydCBhbmQgZW5kIApzID0gMTExMTExMTExMTExMTExMQplID0gNDQ0NDQ0NDQ0NDQ0NDQ0NAoKIyBjb252ZXJ0IG51bWJlciB0byB2ZWN0b3IKbnVtX3RvX3ZlYyhzKQoKIyBjb252ZXJ0IHZlY3RvciB0byBtYXRyaXggYW5kIHByaW50CndyaXRlX21hdGV4KHZlY190b19tYXQocykpCgojIGNoZWNrIGlmIG1hdHJpeCBpcyBhIHZhbGlkIHNvbHV0aW9uCmNoZWNrX3NvbHV0aW9uKHMpCmBgYApOb3csIHdlIGFyZSByZWFkeSB0byBzdGFydCBsb29waW5nIHRocm91Z2ggdGhlIHF1YWRyaWxsaW9ucyBvZiBtYXRyaWNlcy4gWW91IHdpbGwgZmluZCBvdXQgdGhhdCB0aGlzIGxvb3AgaXMgdW5saWtlbHkgdG8gY29tcGxldGUgb24gYSBkb21lc3RpYyBjb21wdXRlci4gSXQgd2lsbCBub3QgZXZlbiByZWFjaCBvbmUgdmFsaWQgc29sdXRpb24gYmVmb3JlIHRoZSBjb21wdXRlciBydW5zIG91dCBvZiBtZW1vcnkuIApgYGB7ciwgZXZhbD1GfQojIGxvb3AgdGhyb3VnaCBhbGwgcG9zc2libGUgNHg0IG1hdHJpY2VzCmNhbmRpZGF0ZXMgPSBzOmUKZm9yIChjIGluIGNhbmRpZGF0ZXMpIHsKICByZXMgPSBjaGVja19zb2x1dGlvbihjKQogIGlmKHJlcyl7cHJpbnQoYyk7IGJyZWFrfQp9CmBgYAoKSWYgd2UganVzdCB0cnkgdG8gbG9hZCB0aGUgbGFzdCBlbGVtZW50IGluIHRoZSBjYW5kaWRhdGVzIHZlY3RvciAod2hpY2ggd2lsbGwgZ2VuZXJhdGUgdGhlIHdob2xlIHZlY3RvciksIHRoZSBSIGludGVycHJldGVyIGxldHMgdXMga25vdyB0aGF0IHdlIGFyZSBzaG9ydCBvZiBqdXN0IDEySyBHQiBvZiBtZW1vcnkuIApgYGB7ciwgZXZhbCA9IEZ9CmNhbmRpZGF0ZXNbLTFdCmBgYApBIGdlbmVyYXRvciB3aXRoIGEgeWllbGQgc3RhdGVtZW50IHdvdWxkIGJlIG15IG5leHQgc3RlcC4gVWx0aW1hdGVseSwgdGhpcyBicnV0ZSBmb3JjZSBhcHByb2FjaCBpcyB0b28gbmFpdmUsIGRvZXMgbm90IHRha2UgYWR2YW50YWdlIG9mIGFueSBvcHRpbWl6YXRpb25zIGFuZCBpdCBwcm9iYWJseSBpcyBiZXR0ZXIgdG8gaW1wbGVtZW50IGluIEMgb3Igc29tZSBvdGhlciBsb3cgbGV2ZWwgbGFuZ3VhZ2UuIERvIHdlIGhhdmUgb3RoZXIgd2F5cyB0byBnZXQganVzdCBvbmUgdmFsaWQgc29sdXRpb24gd2l0aCB0aGUgaGVscCBvZiBSPyBUaGFua2Z1bGx5LCB3ZSBkby4KCiMgSW50ZWdlciBMaW5lYXIgUHJvZ3JhbW1pbmcKCklMUCBpcyBhIGdyZWF0IHRvb2wgdG8gaGF2ZSBpbiB5b3VyIHRvb2xraXQuIElMUCBlbmFibGVzIHVzIHRvIGZpbmQgb3B0aW1hbCBzb2x1dGlvbnMgZm9yIGEgZ2l2ZW4gYSBzZXQgb2YgY29uc3RyYWludHMgYW5kIGFuIG9iamVjdGl2ZSBmdW5jdGlvbiB1c2luZyBsaW5lYXIgb3B0aW1pemF0aW9uLgoKQmVsb3cgaXMgYSBzaW1wbGUgZXhhbXBsZS4gJHhfMSQgYW5kICR4XzIkIGFyZSBpbXBsaWNpdGx5IGFzc3VtZWQgdG8gYmUgaW50ZWdlcnMuIEluIHNvbWUgY2FzZXMsIHRoZXkgYWxzbyBtYXkgYmUgc2V0IHRvIGJpbmFyeSwgZWl0aGVyIGEgJDAkIG9yICQxJCAuIAoKClxiZWdpbnthbGlnbn0KICAgXG1heCBcIHhfMSArIHhfMiBcXCAKCiAgIFx0ZXh0e3N1YmplY3QgdG99IFxub3RhZwogICAteF8xICsgeF8yIFxsZXEgMTIgXFwgCiAgIDN4XzEgKyAyeF8yID0gNQogICAKXGVuZHthbGlnbn0KCgpUaGUgZ2FtZSBydWxlcyBJIHN0YXRlZCBpbiB0aGUgYmVnaW5uaW5nIGRlZmluZSBsaW5lYXIgcmVsYXRpb25zaGlwcyBiZXR3ZWVuIHRoZSBlbGVtZW50cyBvZiB0aGUgNHg0IG1hdHJpeC4gV2UgY2FuIHRyYW5zbGF0ZSB0aG9zZSBydWxlcyBpbnRvIHRoZSBjb25zdHJhaW50cyBhbmQgdGhlIG1hZ2ljIHNxdWFyZXMgZ2FtZSBpbnRvIGFuIEludGVnZXIgTGluZWFyIFByb2dyYW0uIFNvIGxldCdzIGRvIHRoYXQuIExldCdzIHN0YXJ0IHdpdGggdGhlIG9iamVjdGl2ZSBmdW5jdGlvbi4KCldlIGFscmVhZHkga25vdyB0aGF0IHdlIGhhdmUgMTYgZWxlbWVudHMgaW4gYSA0eDQgbWF0cml4LCBmb3IgZWFjaCBlbGVtZW50IHdlIG1heSB1c2Ugb25lIG9mIHRoZSBmb3VyIG51bWJlcnMgJFx7MSwyLDMsNFx9JC4gVGhpcyBtZWFucyB3ZSBoYXZlICQxNio0ID0gNjQkIGJpbmFyeSB2YXJpYWJsZXMuIE91ciBvYmplY3RpdmUgZnVuY3Rpb24gd2lsbCBiZSB0aGUgc3VtICR4XzEseF8yLC4uLix4X24kIHdoZXJlICRuJCBpcyAkNjQkLiBHcmVhdC4gTm9ybWFsbHkgd2Ugd291bGQgc2F5IHdlIHdhbnQgdG8gbWF4aW1pemUgdGhlIG9iamVjdGl2ZSBmdW5jdGlvbi4gSGVyZSB3ZSBrbm93IHRoYXQgd2UgbmVlZCBleGFjdGx5IDE2IGVsZW1lbnRzIGZvciBhbnkgdmFsaWQgc29sdXRpb24gb3V0IG9mIDY0LiBTbyB3ZSBjYW4ganVzdCBzZXQgdGhlIG9iamVjdGl2ZSBmdW5jdGlvbiB0byBiZSBlcXVhbCB0byAxNi4gSSB3aWxsIHdyaXRlIGRvd24gb3VyIElMUCBpbiBub3RhdGlvbiBhbmQgZXhwbGFpbiBlYWNoIGNvbnN0cmFpbnQuCgpcYmVnaW57YWxpZ259CiAgIFxzdW1fe249MX1eezY0fSB4X24gPSAxNiAgXFwgCgogICBcdGV4dHtzdWJqZWN0IHRvfSBcbm90YWcKICAgXHN1bSBkaWFnKHhfbikgPSAxMCBcICYgKDEpIFxcCiAgIFxzdW0gY29sKHhfbikgPSAxMCBcICYgKDIpIFxcCiAgIFxzdW0gcm93KHhfbikgPSAxMCBcICYoMykgXFwKICAgXHN1bSBjZWxsKHhfbikgPSAxIFwgJiAoNCkgXFwKICAgXHN1bSBkaWdpdCh4X24pID0gNCBcICYgKDUpCiAgIApcZW5ke2FsaWdufQoKSSBhbSBjZXJ0YWlubHkgdGFraW5nIGxpYmVydHkgaW4gdGhlIHdheSBJIHVzZSBub3RhdGlvbiBoZXJlLiAoMSksICgyKSwgKDMpIGFyZSBzdHJhaWdodGZvcndhcmQgYW5kIG1ha2Ugc3VyZSB0aGF0ICR4JCdzIHRoYXQgY29ycmVzcG9uZCB0byByb3dzLCBjb2x1bW5zIGFuZCBkaWFnb25hbHMgc3VtIHVwIHRvIDEwLiAoNCkgbWFrZXMgc3VyZSB0aGF0ICR4JCdzIHRoYXQgY29ycmVzcG9uZCB0byB0aGUgc2FtZSBjZWxsIGluIHRoZSBtYXRyaXggc3VtIHVwIHRvIDEuIEluZGVlZCwgZWFjaCBjZWxsIGluIHRoZSBtYXRyaXggY2FuIG9ubHkgaGF2ZSBvbmUgbnVtYmVyIGZyb20gZm91ciBhdmFpbGFibGUgYW5kIHRoYXRzIHdoeSBvbmx5IG9uZSBjb3JyZXNwb25kaW5nICR4JCBjYW4gYmUgbm9uIHplcm8uICg1KSBlbnN1cmVzIHRoYXQgYWxsIGZvdXIgYXZhaWxhYmxlIG51bWJlcnMgYXJlIHVzZWQgYXQgbGVhc3Qgb25jZS4gSW1wbGljaXRseSwgdGhlICR4JCdzIGFyZSBiaW5hcnkuIAoKU28gd2UgYWN0dWFsbHkgZW5kZWQgdXAgaGF2aW5nIGEgc3lzdGVtIG9mIGVxdWFsaXRpZXMuIExldCdzIHNvbHZlIHRoaXMgc3lzdGVtIHVzaW5nIHRoZSBgbHBzb2x2ZWAgcGFja2FnZS4KCmBscHNvbHZlYCB3aWxsIG5lZWQgdGhlIG9iamVjdGl2ZSBmdW5jdGlvbiwgdGhlIGNvbnN0cmFpbnQgbWF0cml4LCB0aGUgZXF1YWxpdHkvaW5lcXVhbGl0eSBkaXJlY3Rpb24sIGFuZCBmaW5hbGx5IHRoZSByaWdodCBoYW5kIHNpZGUgb2YgdGhlIGVxdWFsaXRpZXMvaW5lcXVhbGl0aWVzLiBJIGNvZGUgZXZlcnl0aGluZyBiZWxvdy4KCmBgYHtyLGNsYXNzLnNvdXJjZSA9ICdmb2xkLWhpZGUnLCBldmFsPUZBTFNFfQojIEhlbHBlciBmdW5jdGlvbnMgdG8gYnVpbGQgY29uc3RyYWludHMgYW5kIHByb2Nlc3Mgc29sdXRpb25zCgpwb3NpdGlvbiA9IGZ1bmN0aW9uKGVsKSB7CiAgJwogIEdldCB2ZWN0b3Igb2YgcG9zaXRpb25zIG9mIGVsZW1lbnRzIGluIGEgbmNvbHMoZWwpKjQgCiAgbWF0cml4IHRoYXQgY29ycmVzcG9uZCB0byBwb3NpdGlvbnMgaW4gdGhlIG9yaWdpbmFsIDQqNCBtYXRyaXguCiAgJwogIHgxID0gZWwqNC00KzEKICB4MiA9IGVsKjQKIAogIHJldHVybih4MTp4MikKfQoKY29uc3RyYWludCA9IGZ1bmN0aW9uKGVsX3Bvcyx2YWw9MSwgcmF3X3BvcyA9IEZBTFNFKSB7CiAgJwogIENyZWF0ZSBjb25zdHJhaW50IHZlY3RvciB3aXRoIDY0IGVsZW1lbnRzIGdpdmVuIGVsZW1lbnQgcG9zaXRpb25zIAogICcKICBiYXNlX3ZlYyA9IHJlcCgwLDQqMTYpCiAgaWYoIXJhd19wb3MpIHtwb3NzID0gYyhzYXBwbHkoZWxfcG9zLCBwb3NpdGlvbikpfQogIGVsc2Uge3Bvc3MgPSBlbF9wb3N9CiAgCiAgYmFzZV92ZWNbcG9zc10gPSByZXAodmFsLGxlbmd0aChwb3NzKS9sZW5ndGgodmFsKSkKICByZXR1cm4oYmFzZV92ZWMpCiAgCn0KCmBgYAoKYGBge3J9CiMgZGVmaW5lIGNvZWZmaWNpZW50cyBpbiBvYmplY3RpdmUgZnVuY3Rpb24Kb2JqID0gcmVwKDEsNCoxNikKCiMgc2V0IHVwIGNvbnN0cmFpbnRzIGJhc2UgdmVjdG9yCmNvbnNfYmFzZSA9IHJlcCgwLDQqMTYpCgojIGRpYWdvbmFsIHN1bXMgYXJlIDEwCnBvc19kaWFnMSA9IGMoMSw2LDExLDE2KQpwb3NfZGlhZzIgPSBjKDQsNywxMCwxMykKCmNvbl9kaWFnMSA9IGNvbnN0cmFpbnQocG9zX2RpYWcxLHZhbD0xOjQpCmNvbl9kaWFnMiA9IGNvbnN0cmFpbnQocG9zX2RpYWcyLDE6NCkKCmRpYWdfY29ucyA9IHJiaW5kKGNvbl9kaWFnMSxjb25fZGlhZzIpCnJvd25hbWVzKGRpYWdfY29ucykgPSByZXAoJ2RpYWdfc3VtcycsMikKCiMgY29sIHN1bXMgYXJlIDEwCmNvbF9jb25zID0gYygpCmNvbF9wb3NpdGlvbnMgPSBtYXRyaXgoc2VxKDEsMTYpLG5yb3cgPSA0KQpmb3Iocm93IGluIDE6bnJvdyhjb2xfcG9zaXRpb25zKSkgewoKICBjb2xfY29ucyA9IHJiaW5kKGNvbF9jb25zLCBjb25zdHJhaW50KGNvbF9wb3NpdGlvbnNbcm93LF0sMTo0KSkKfQpyb3duYW1lcyhjb2xfY29ucykgPSByZXAoJ2NvbF9zdW1zJyw0KQoKIyByb3cgc3VtcyBhcmUgMTAKcm93X2NvbnMgPSBjKCkKcm93X3Bvc2l0aW9ucyA9IG1hdHJpeChzZXEoMSwxNiksIG5jb2wgPSA0LGJ5cm93PVRSVUUpCmZvcihyb3cgaW4gMTpucm93KHJvd19wb3NpdGlvbnMpKSB7CiAgcm93X2NvbnMgPSByYmluZChyb3dfY29ucyxjb25zdHJhaW50KHJvd19wb3NpdGlvbnNbcm93LF0sMTo0KSkKfQpyb3duYW1lcyhyb3dfY29ucykgPSByZXAoJ3Jvd19zdW1zJyw0KQoKIyBlYWNoIGNlbGwgY2FuIGhhdmUgb25seSBvbmUgdmFsdWUgb3V0IG9mIDQgYXZhaWxhYmxlCm9ubHlfb25lX3ZhbCA9IHQoc2FwcGx5KDE6MTYsY29uc3RyYWludCkpCgojIGVhY2ggbnVtYmVyIG11c3QgYXBwZWFyIDQgdGltZXMKYXBwZWFyNCA9IHJiaW5kKGNvbnN0cmFpbnQoc2VxKDEsNjQsYnk9NCksIHJhd19wb3M9VFJVRSksCiAgICAgICAgICAgICAgICBjb25zdHJhaW50KHNlcSgyLDY0LGJ5PTQpLCByYXdfcG9zPVRSVUUpLAogICAgICAgICAgICAgICAgY29uc3RyYWludChzZXEoMyw2NCxieT00KSwgcmF3X3Bvcz1UUlVFKSwKICAgICAgICAgICAgICAgIGNvbnN0cmFpbnQoc2VxKDQsNjQsYnk9NCksIHJhd19wb3M9VFJVRSkpCiAgCgojIGNyZWF0ZSBjb25zdHJhaW50IG1hdHJpeApjb25zID0gcmJpbmQoZGlhZ19jb25zLGNvbF9jb25zLHJvd19jb25zLCBvbmx5X29uZV92YWwsIGFwcGVhcjQpCmBgYAoKVGhlIGBjb25zYCBtYXRyaXggaGFzIDY0IGNvbHVtbnMgYW5kIGEgY29uc3RyYWludCBmb3IgZWFjaCBnYW1lIHJ1bGUgd2Ugb3V0bGluZWQuIExldCdzIGRlZmluZSByZWxhdGlvbnNoaXAgZGlyZWN0aW9uIGFuZCByaWdodCBoYW5kIHNpZGVzLgoKYGBge3J9CiMgU2V0IGluZXF1YWxpdHkvZXF1YWxpdHkgc2lnbnMKZi5kaXIgPC0gYyhyZXAoIj0iLG5yb3coZGlhZ19jb25zKSksCiAgICAgICAgICAgcmVwKCI9Iixucm93KGNvbF9jb25zKSksCiAgICAgICAgICAgcmVwKCI9Iixucm93KHJvd19jb25zKSksCiAgICAgICAgICAgcmVwKCI9Iixucm93KG9ubHlfb25lX3ZhbCkpLAogICAgICAgICAgIHJlcCgiPSIsbnJvdyhhcHBlYXI0KSkKICAgICAgICAgICApCgojIFNldCByaWdodCBoYW5kIHNpZGUgY29lZmZpY2llbnRzCmYucmhzIDwtIGMocmVwKDEwLG5yb3coZGlhZ19jb25zKSksCiAgICAgICAgICAgcmVwKDEwLG5yb3coY29sX2NvbnMpKSwKICAgICAgICAgICByZXAoMTAsbnJvdyhyb3dfY29ucykpLAogICAgICAgICAgIHJlcCgxLG5yb3cob25seV9vbmVfdmFsKSksCiAgICAgICAgICAgcmVwKDQsbnJvdyhhcHBlYXI0KSkpCmBgYAoKRmluYWxseSwgd2UgYXJlIHJlYWR5IHRvIHNvbHZlIHRoaXMgSUxQIGFuZCBzZWUgaWYgY2FuIG9idGFpbiBhdCBsZWFzdCBvbmUgdmFsaWQgc29sdXRpb24gd2l0aG91dCBydW5ubmluZyBvdXQgb2YgbWVtb3J5IG9yIHNlYXJjaGluZyB0aHJvdWdoIGEgcXVhZHJpbGxpb24gbnVtYmVycy4KCmBgYHtyfQpsaWJyYXJ5KGxwU29sdmUpCgojIFNvbHZlIElMUAptb2RlbCA9IGxwKCJtYXgiLCBvYmosIGNvbnMsIGYuZGlyLCBmLnJocywgYWxsLmJpbj1ULHVzZS5ydz1UUlVFKQpwcmludChtb2RlbCkKbW9kZWwkc29sdXRpb24KYGBgCk9rLCBncmVhdC4gU2VlbXMgd2UgZ290IGEgc29sdXRpb24gcXVpdGUgcXVpY2tseSwgYWxtb3N0IGluc3RhbnRseS4gTG9va3MgY29uZnVzaW5nLiBBYm92ZSB2ZWN0b3Igc2hvd3MgdXMgd2hpY2ggMTYgb2YgNjQgJHgkIHZhcmlhYmxlcyBhcmUgYWN0aXZhdGVkIGFuZCBjYW4gYmUgaW5jbHVkZWQgaW4gdGhlIHNvbHV0aW9uIG1hdHJpeC4gSSBkZWZpbmUgYSBmdW5jdGlvbiB0byB0cmFuc2xhdGUgdGhlIHNvbHV0aW9uIHZlY3RvciBpbnRvIGEgbWF0cml4LiBMZXQncyBjaGVjayEKYGBge3IsIGNsYXNzLnNvdXJjZSA9ICdmb2xkLWhpZGUnLCBldmFsPUZBTFNFfQpjb2xsZWN0X3NvbHV0aW9ucyA9IGZ1bmN0aW9uKHNvbCkgewogICcKICBQcmludCBvdXQgdGhlIHNvbHV0aW9uIG1hdHJpY2VzLgogICcKICBzb2wgPSBzb2xbMTooYXMuaW50ZWdlcihsZW5ndGgoc29sKS82NCkqNjQpXQogIGJhc2VfdmVjID0gcmVwKDE6NCwxNikKICBzb2wgPSBtYXRyaXgoc29sLG5jb2w9NjQsIGJ5cm93ID0gVFJVRSkKICBzb2x1dGlvbnMgPSBjKCkKICBmb3IocyBpbiAxOm5yb3coc29sKSl7CiAgICBzb2xfbWF0ID0gbWF0cml4KGJhc2VfdmVjW3doaWNoKHNvbFtzLF0+MCldLCBuY29sID0gNCkKICAgIHNvbHV0aW9ucyA9IHJiaW5kKHNvbHV0aW9ucyxjKHNvbF9tYXQpKQogIH0KICAKICByZXR1cm4oc29sdXRpb25zKQogIAp9CmBgYAoKYGBge3J9CnNvbCA9IGNvbGxlY3Rfc29sdXRpb25zKG1vZGVsJHNvbHV0aW9uKQpzb2wgIyB0aGUgdmVjdG9yIGhhcyAxNiBlbGVtZW50cy4gR3JlYXQhCmNoZWNrX3NvbHV0aW9uKHNvbCkKYGBgCgpBZnRlciBjaGVja2luZyBvdXIgc29sdXRpb24gYWNjb3JkaW5nIHRvIHRoZSBnYW1lIHJ1bGVzIHdlIHNlZSBpdCBpcyB2YWxpZC4gV2UgYmFzaWNhbGx5IGNyZWF0ZWQgYSBzb2x2ZXIgZm9yIGEgdmFyaWF0aW9uIG9mIHRoZSBtYWdpYyBzcXVhcmVzIGdhbWUgdXNpbmcgSUxQIGFuZCBgbHBzb2x2ZWAuIEluIGdlbmVyYWwsIGFueSBwcm9ibGVtIHdpdGggbGluZWFyIGNvbnN0cmFpbnRzIGFuZCBhbiBvYmplY3RpdmUgY2FuIGJlIHNvbHZlZCB1c2luZyBMaW5lYXIgT3B0aW1pemF0aW9uLiBIb3BlIHRoaXMgaWxsdXN0cmF0aW9uIHdhcyBpbmZvcm1hdGl2ZSB0byB5b3UuCgokJFxiZWdpbntibWF0cml4fQrwn5WbJvCflacm8J+VkCbwn5WcIFxcCvCflZEm8J+VnSbwn5WSJvCflZ4gXFwK8J+Vkybwn5WfJvCflZQm8J+VoCBcXArwn5WVJvCflaEm8J+Vlibwn42AIFxcClxlbmR7Ym1hdHJpeH0kJA==