\[\begin{bmatrix} ๐Ÿฏ&๐Ÿ˜…&๐Ÿ’ซ&๐ŸŽฉ \\ ๐Ÿฐ&๐Ÿ‡&๐ŸŒ™&๐ŸŒŸ \\ โœจ&๐ŸŽ‰&๐ŸŽฑ&๐Ÿƒ \\ ๐Ÿ”ฎ&๐Ÿ”“&โœ…&๐Ÿš€ \\ \end{bmatrix}\] The Magic Squares game is a simple yet engaging problem. There are many variations and the one I am interested in has the following rules

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?

\(\begin{bmatrix} 1&2&3&4 \\ 1&2&3&4 \\ 1&2&3&4 \\ 1&2&3&4 \\ \end{bmatrix}\)

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==