R markdown description

This is an R Markdown Notebook. When you execute code within the notebook, the results appear beneath the code.

Try executing this chunk by clicking the Run button within the chunk or by placing your cursor inside it and pressing Ctrl+Shift+Enter.

Tutorial description

The tutorial explains how to execute and use the FCM package on the “book_example.xlsx” example. The tutorial aims to walk through some of the main functions in the package and to show their interdependencies to consistently compute the core components of the fuzzy cognitive maps.

Initiating the variables and inputting data

Let’s start with initiating an object for storing current data set.

library(FCM)
init_matrix <- list(data = NULL)

We need to input the file. It could be done by using the following code, which reads the matrix from the 4th sheet of the example file.

library(xlsx)
dataRow <- read.xlsx('book_example.xlsx',sheetIndex = 4, startRow=1, endRow=8)

After inputting the matrix we assign the input matrix to the variable mat1 as follows.

mat1<-data.matrix(dataRow[,seq(2, 8)], rownames.force = NA)

Let’s look at the obtained matrix.

mat1
       X1  X2  X3  X4  X5   X6   X7
[1,]  0.0 0.0 0.6 0.9 0.0  0.0  0.0
[2,]  0.1 0.0 0.0 0.0 0.0  0.0  0.0
[3,]  0.0 0.7 0.0 0.0 0.9  0.0  0.0
[4,]  0.0 0.0 0.0 0.0 0.0  0.0  0.9
[5,]  0.0 0.0 0.0 0.0 0.0 -0.9 -0.9
[6,] -0.3 0.0 0.0 0.0 0.0  0.0  0.0
[7,]  0.0 0.0 0.0 0.0 0.0  0.8  0.0

Shaping the input data

Let’s name the rows of the matrix.

rownames(mat1) <- rownames(mat1, do.NULL = FALSE, prefix = "f_")
mat1
      X1  X2  X3  X4  X5   X6   X7
f_1  0.0 0.0 0.6 0.9 0.0  0.0  0.0
f_2  0.1 0.0 0.0 0.0 0.0  0.0  0.0
f_3  0.0 0.7 0.0 0.0 0.9  0.0  0.0
f_4  0.0 0.0 0.0 0.0 0.0  0.0  0.9
f_5  0.0 0.0 0.0 0.0 0.0 -0.9 -0.9
f_6 -0.3 0.0 0.0 0.0 0.0  0.0  0.0
f_7  0.0 0.0 0.0 0.0 0.0  0.8  0.0

Now the matrix has row and column names. Further, we fill the init_matrix “data” with the values from mat1.

init_matrix$data <- mat1
summary(init_matrix)
     Length Class  Mode   
data 49     -none- numeric

Introducting some parameters

Now, let’s initiate some parameters in order to calculate the transitive matrix. In the case of “min t-norm”, “drastic sum” and make lists of s- and t-norms:

tnorm <- min_tnorm(0.5,0.3)
tnorm
[1] 0.3
snormElement <- get_snorm('Drastic Sum')(0.3,0.7,0,0)
snormElement
[1] 1
snormMatrix <-get_snorm('Drastic Sum')(0.3,0.7)
snormMatrix
[1] 1

And make lists of t- and s-norm functions.

tnorm_functions <- list(min_tnorm = min_tnorm, 
                        hamacher_product_tnorm = hamacher_product_tnorm, 
                        algebraic_product_tnorm = algebraic_product_tnorm, 
                        einstein_product_tnorm = einstein_product_tnorm, 
                        bounded_difference_tnorm = bounded_difference_tnorm, 
                        drastic_product_tnorm = drastic_product_tnorm, 
                        parameterized_mean_intersection_operator_tnorm = parameterized_mean_intersection_operator_tnorm, 
                        dubois_intersection_operator_tnorm = dubois_intersection_operator_tnorm, 
                        hamacher_intersection_operator_tnorm = hamacher_intersection_operator_tnorm, 
                        yager_intersection_operator_tnorm = yager_intersection_operator_tnorm)

snorm_functions <- list(drastic_sum_snorm = get_snorm("Drastic Sum"), 
                        bounded_sum_snorm = get_snorm("Bounded Sum"), 
                        einstein_sum_snorm = get_snorm("Einstein Sum"), 
                        algebraic_sum_snorm = get_snorm("Algebraic Sum"), 
                        hamacher_sum_snorm = get_snorm("Hamacher Sum"), 
                        max_snorm = get_snorm("Max"), 
                        hamacher_union_operator_snorm = get_snorm("Hamacher-union operator"), 
                        yager_union_operator_snorm = get_snorm("Yager-union operator"))

Calculating transitive closure matrix

Now, we are able to calculate the “eigen numbers” and “positive matrix”. This, in turn, allows us to calculate respective transitive closure matrix.

eigen_numbers <- eigen_module(init_matrix$data)
positive_matrix <- positive_matrix_calc(init_matrix$data)
matrix <- transitive_closure(positive_matrix, tnorm_functions, tnorm, snorm_functions, snormElement, snormMatrix,
gammaTnormMean=0.05, algaTnorm=0.1, gammaTnorm=0.02, piTnorm=0.2)

transitive_matrix <- matrix

We have calculated the transitive matrix. Let’s check whether it is not null.

!is.null(transitive_matrix)
[1] TRUE

Once we have done the above steps, we can check whether the matrix is indeed transitive.

tr_maximum_check <- multiply_matrix(transitive_matrix, transitive_matrix, tnorm_functions, tnorm, snorm_functions, snormElement,
                                        gammaTnormMean, algaTnorm, gammaTnorm, piTnorm, 
                                        gammaSnormElement, piSnormElement)
aa <- transitive_matrix - tr_maximum_check
if (sum(aa < -0.00001) == 0 ) 'yes' else 'no'
[1] "yes"

We concluded that matrix is a transitive one.

Now, we can calculate the joined transitive matrix.

transitive_matrix_joined <- matrix_transitive_join(transitive_matrix, snorm_functions, snormElement,gammaSnorm, piSnorm)
transitive_matrix_joined
     [,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8] [,9] [,10] [,11] [,12] [,13]
[1,]    1   -1    1   -1    1   -1    1   -1    1    -1     1    -1     1
[2,]    1   -1    1   -1    1   -1    1   -1    1    -1     1    -1     1
[3,]    1   -1    1   -1    1   -1    1   -1    1    -1     1    -1     1
[4,]    1   -1    1   -1    1   -1    1   -1    1    -1     1    -1     1
[5,]    1   -1    1   -1    1   -1    1   -1    1    -1     1    -1     1
[6,]    1   -1    1   -1    1   -1    1   -1    1    -1     1    -1     1
[7,]    1   -1    1   -1    1   -1    1   -1    1    -1     1    -1     1
     [,14]
[1,]    -1
[2,]    -1
[3,]    -1
[4,]    -1
[5,]    -1
[6,]    -1
[7,]    -1

Network data

For an init_matrix$data we may present the graph of nodes using the qgraph library.

library(qgraph)
Registered S3 methods overwritten by 'htmltools':
  method               from         
  print.html           tools:rstudio
  print.shiny.tag      tools:rstudio
  print.shiny.tag.list tools:rstudio
Registered S3 method overwritten by 'htmlwidgets':
  method           from         
  print.htmlwidget tools:rstudio
Registered S3 method overwritten by 'data.table':
  method           from
  print.data.table     
qgraph(init_matrix$data,edge.labels=TRUE)

Impulse

Let’s calculate the impulse data for our initial matrix.

m2 <- matrix(cbind(0), ncol = 1, nrow = nrow(init_matrix$data))
colnames(m2) <- c("x")
DF = m2

For the step impulse of 10, the impulse data will be as follows:

stepImpulse <- 10
DF <- DF[,'x']
k <- DF
      
for(i in 1:stepImpulse) 
      {
        k <- t(init_matrix$data) %*% k
        i <- i+1
        DF <- cbind(DF, round(k, 5))
      }
colnames(DF) <- c('x', 1:stepImpulse)

data_impulse <- DF
data.frame(data_impulse)

The corresponding plot for the obtained impulse data is shown below.

matplot(t(data_impulse),type = "l", xlab = NULL, ylab = "Impulse")
legend("topright", inset=0, legend=c(1:nrow(data_impulse)), pch=1,   col=c(1:nrow(data_impulse)), horiz=TRUE)

Direct task

To calculate the direct task, the control vector should be identified.

finalvector <- length(which(colSums(init_matrix$data == 0) == ncol(init_matrix$data)))
DF_control_vector <- finalvector
DF_control_vector
[1] 0

Since the value of DF_control_vector is 0, there aren’t any control vectors in data.

Therefore, the direct solution matrix is NULL as well.

Reverse task

Let’s obtain the DF_goal_vector to calculate the reverse task.

if (!is.null(init_matrix$data)) {
     finalvector <- length(which(rowSums(init_matrix$data == 0) == nrow(init_matrix$data)))
      if (finalvector > 0)
      data.frame(N = rep(0, finalvector),
                 G = rep(0, finalvector),
                 stringsAsFactors = FALSE)
    }
DF_goal_vector <- finalvector

DF_goal_vector
[1] 0

The same as for direct task, the reverse task control vector is absent as well as the corresponding solution matrix.

Consonanse

Let’s look at the consonanse plot for recently calculated transitive_matrix_joined. For that purpose we should firstly use cross_consonanse function.

c_mat <- cross_consonanse(transitive_matrix_joined)
borderCut = 0.7
c_cut <-  ifelse(c_mat<borderCut,0,1)
qgraph(c_cut,edge.labels=TRUE,title="Consonance")

Dissonanse

Very similarly to consonanse we can obtain the dissonanse plot.

d_mat <- cross_dissonanse(transitive_matrix_joined)
d_cut <-  ifelse(d_mat<borderCut,0,1)
qgraph(d_cut,edge.labels=TRUE,title="Dissonance")

Positive influence

Positive influence calculation in almost identical to the previous ones.

p_mat <- cross_positive_influence(transitive_matrix_joined)
p_cut <-  ifelse(p_mat<borderCut,0,1)
qgraph(p_cut,edge.labels=TRUE,title="Positive influence")

Negative influence

Finally, we can plot the negative influence.

n_mat <- cross_negative_influence(transitive_matrix_joined)
n_cut <-  ifelse(abs(n_mat)<borderCut,0,1)
qgraph(n_cut,edge.labels=TRUE,title="Negative influence")

Table consonanse

We can also present the consonanse / dissonanse information in the table.

consonanse_dissonanse <- consonanse_dissonanse(transitive_matrix_joined)
data.frame(consonanse_dissonanse)

IK positive

We can now calculate IK positive for the calculated above transitive joined matrix. As for parameters, from and to are set manually.

from = 1
to = 1
IKPositive <- ik_pos_maximum(transitive_matrix_joined, init_matrix$data, from, to)
data.frame(IKPositive)

IK negative

Very similarly to the previous section, we can now calculate IK negative with the same parameters and present the results as a data frame.

IKNegative <- ik_neg_maximum(transitive_matrix_joined, init_matrix$data,from,to)
data.frame(IKNegative)

Conclusion

So far, for the given book example matrix we were managed to calculate transitive matrix, check whether it is transitive to verify the correctness of the calculations. Furthemore, for the respective transitive matrix we allowed to get calculate and plot netword data in the form of nodes, impulse, direct and reverse tasks, consonanse/dissonanse plots and the respective consonanse table, positive and negative influence plots as well as the IK positive and negative.

During the tutorial we manually assigned some values to the parameters. For the purpose of the further research, you may try different values and compare the obtained results.

To conclude, using the FCM package we are able to calculate different components which further could be used as a core for a separate application.

LS0tDQp0aXRsZTogIkZDTSBwYWNrYWdlIHR1dG9yaWFsIg0Kb3V0cHV0OiBodG1sX25vdGVib29rDQotLS0NCg0KIyMgUiBtYXJrZG93biBkZXNjcmlwdGlvbg0KDQpUaGlzIGlzIGFuIFtSIE1hcmtkb3duXShodHRwOi8vcm1hcmtkb3duLnJzdHVkaW8uY29tKSBOb3RlYm9vay4gV2hlbiB5b3UgZXhlY3V0ZSBjb2RlIHdpdGhpbiB0aGUgbm90ZWJvb2ssIHRoZSByZXN1bHRzIGFwcGVhciBiZW5lYXRoIHRoZSBjb2RlLiANCg0KVHJ5IGV4ZWN1dGluZyB0aGlzIGNodW5rIGJ5IGNsaWNraW5nIHRoZSAqUnVuKiBidXR0b24gd2l0aGluIHRoZSBjaHVuayBvciBieSBwbGFjaW5nIHlvdXIgY3Vyc29yIGluc2lkZSBpdCBhbmQgcHJlc3NpbmcgKkN0cmwrU2hpZnQrRW50ZXIqLg0KDQojIFR1dG9yaWFsIGRlc2NyaXB0aW9uDQoNClRoZSB0dXRvcmlhbCBleHBsYWlucyBob3cgdG8gZXhlY3V0ZSBhbmQgdXNlIHRoZSBGQ00gcGFja2FnZSBvbiB0aGUgImJvb2tfZXhhbXBsZS54bHN4IiBleGFtcGxlLiBUaGUgdHV0b3JpYWwgYWltcyB0byB3YWxrIHRocm91Z2ggc29tZSBvZiB0aGUgbWFpbiBmdW5jdGlvbnMgaW4gdGhlIHBhY2thZ2UgYW5kIHRvIHNob3cgdGhlaXIgaW50ZXJkZXBlbmRlbmNpZXMgdG8gY29uc2lzdGVudGx5IGNvbXB1dGUgdGhlIGNvcmUgY29tcG9uZW50cyBvZiB0aGUgZnV6enkgY29nbml0aXZlIG1hcHMuDQoNCiMjIEluaXRpYXRpbmcgdGhlIHZhcmlhYmxlcyBhbmQgaW5wdXR0aW5nIGRhdGENCg0KTGV0J3Mgc3RhcnQgd2l0aCBpbml0aWF0aW5nIGFuIG9iamVjdCBmb3Igc3RvcmluZyBjdXJyZW50IGRhdGEgc2V0Lg0KYGBge3J9DQppbml0X21hdHJpeCA8LSBsaXN0KGRhdGEgPSBOVUxMKQ0KYGBgDQoNCg0KV2UgbmVlZCB0byBpbnB1dCB0aGUgZmlsZS4gSXQgY291bGQgYmUgZG9uZSBieSB1c2luZyB0aGUgZm9sbG93aW5nIGNvZGUsIHdoaWNoIHJlYWRzIHRoZSBtYXRyaXggZnJvbSB0aGUgNHRoIHNoZWV0IG9mIHRoZSBleGFtcGxlIGZpbGUuDQoNCg0KYGBge3J9DQpsaWJyYXJ5KHhsc3gpDQpkYXRhUm93IDwtIHJlYWQueGxzeCgnYm9va19leGFtcGxlLnhsc3gnLHNoZWV0SW5kZXggPSA0LCBzdGFydFJvdz0xLCBlbmRSb3c9OCkNCmBgYA0KDQpBZnRlciBpbnB1dHRpbmcgdGhlIG1hdHJpeCB3ZSBhc3NpZ24gdGhlIGlucHV0IG1hdHJpeCB0byB0aGUgdmFyaWFibGUgYG1hdDFgIGFzIGZvbGxvd3MuDQoNCmBgYHtyfQ0KbWF0MTwtZGF0YS5tYXRyaXgoZGF0YVJvd1ssc2VxKDIsIDgpXSwgcm93bmFtZXMuZm9yY2UgPSBOQSkNCmBgYA0KDQpMZXQncyBsb29rIGF0IHRoZSBvYnRhaW5lZCBtYXRyaXguDQpgYGB7cn0NCm1hdDENCmBgYA0KIyMgU2hhcGluZyB0aGUgaW5wdXQgZGF0YQ0KDQpMZXQncyBuYW1lIHRoZSByb3dzIG9mIHRoZSBtYXRyaXguDQpgYGB7cn0NCnJvd25hbWVzKG1hdDEpIDwtIHJvd25hbWVzKG1hdDEsIGRvLk5VTEwgPSBGQUxTRSwgcHJlZml4ID0gImZfIikNCm1hdDENCmBgYA0KTm93IHRoZSBtYXRyaXggaGFzIHJvdyBhbmQgY29sdW1uIG5hbWVzLiBGdXJ0aGVyLCB3ZSBmaWxsIHRoZSBgaW5pdF9tYXRyaXhgICJkYXRhIiB3aXRoIHRoZSB2YWx1ZXMgZnJvbSBgbWF0MWAuDQpgYGB7cn0NCmluaXRfbWF0cml4JGRhdGEgPC0gbWF0MQ0Kc3VtbWFyeShpbml0X21hdHJpeCkNCmBgYA0KIyMgSW50cm9kdWN0aW5nIHNvbWUgcGFyYW1ldGVycw0KDQpOb3csIGxldCdzIGluaXRpYXRlIHNvbWUgcGFyYW1ldGVycyBpbiBvcmRlciB0byBjYWxjdWxhdGUgdGhlIHRyYW5zaXRpdmUgbWF0cml4LiBJbiB0aGUgY2FzZSBvZiAibWluIHQtbm9ybSIsICJkcmFzdGljIHN1bSIgYW5kIG1ha2UgbGlzdHMgb2Ygcy0gYW5kIHQtbm9ybXM6DQpgYGB7cn0NCnRub3JtIDwtIG1pbl90bm9ybSgwLjUsMC4zKQ0KdG5vcm0NCmBgYA0KYGBge3J9DQpzbm9ybUVsZW1lbnQgPC0gZ2V0X3Nub3JtKCdEcmFzdGljIFN1bScpKDAuMywwLjcsMCwwKQ0Kc25vcm1FbGVtZW50DQpgYGANCmBgYHtyfQ0Kc25vcm1NYXRyaXggPC1nZXRfc25vcm0oJ0RyYXN0aWMgU3VtJykoMC4zLDAuNykNCnNub3JtTWF0cml4DQpgYGANCkFuZCBtYWtlIGxpc3RzIG9mIHQtIGFuZCBzLW5vcm0gZnVuY3Rpb25zLg0KYGBge3J9DQp0bm9ybV9mdW5jdGlvbnMgPC0gbGlzdChtaW5fdG5vcm0gPSBtaW5fdG5vcm0sIA0KICAgICAgICAgICAgICAgICAgICAgICAgaGFtYWNoZXJfcHJvZHVjdF90bm9ybSA9IGhhbWFjaGVyX3Byb2R1Y3RfdG5vcm0sIA0KICAgICAgICAgICAgICAgICAgICAgICAgYWxnZWJyYWljX3Byb2R1Y3RfdG5vcm0gPSBhbGdlYnJhaWNfcHJvZHVjdF90bm9ybSwgDQogICAgICAgICAgICAgICAgICAgICAgICBlaW5zdGVpbl9wcm9kdWN0X3Rub3JtID0gZWluc3RlaW5fcHJvZHVjdF90bm9ybSwgDQogICAgICAgICAgICAgICAgICAgICAgICBib3VuZGVkX2RpZmZlcmVuY2VfdG5vcm0gPSBib3VuZGVkX2RpZmZlcmVuY2VfdG5vcm0sIA0KICAgICAgICAgICAgICAgICAgICAgICAgZHJhc3RpY19wcm9kdWN0X3Rub3JtID0gZHJhc3RpY19wcm9kdWN0X3Rub3JtLCANCiAgICAgICAgICAgICAgICAgICAgICAgIHBhcmFtZXRlcml6ZWRfbWVhbl9pbnRlcnNlY3Rpb25fb3BlcmF0b3JfdG5vcm0gPSBwYXJhbWV0ZXJpemVkX21lYW5faW50ZXJzZWN0aW9uX29wZXJhdG9yX3Rub3JtLCANCiAgICAgICAgICAgICAgICAgICAgICAgIGR1Ym9pc19pbnRlcnNlY3Rpb25fb3BlcmF0b3JfdG5vcm0gPSBkdWJvaXNfaW50ZXJzZWN0aW9uX29wZXJhdG9yX3Rub3JtLCANCiAgICAgICAgICAgICAgICAgICAgICAgIGhhbWFjaGVyX2ludGVyc2VjdGlvbl9vcGVyYXRvcl90bm9ybSA9IGhhbWFjaGVyX2ludGVyc2VjdGlvbl9vcGVyYXRvcl90bm9ybSwgDQogICAgICAgICAgICAgICAgICAgICAgICB5YWdlcl9pbnRlcnNlY3Rpb25fb3BlcmF0b3JfdG5vcm0gPSB5YWdlcl9pbnRlcnNlY3Rpb25fb3BlcmF0b3JfdG5vcm0pDQoNCnNub3JtX2Z1bmN0aW9ucyA8LSBsaXN0KGRyYXN0aWNfc3VtX3Nub3JtID0gZ2V0X3Nub3JtKCJEcmFzdGljIFN1bSIpLCANCiAgICAgICAgICAgICAgICAgICAgICAgIGJvdW5kZWRfc3VtX3Nub3JtID0gZ2V0X3Nub3JtKCJCb3VuZGVkIFN1bSIpLCANCiAgICAgICAgICAgICAgICAgICAgICAgIGVpbnN0ZWluX3N1bV9zbm9ybSA9IGdldF9zbm9ybSgiRWluc3RlaW4gU3VtIiksIA0KICAgICAgICAgICAgICAgICAgICAgICAgYWxnZWJyYWljX3N1bV9zbm9ybSA9IGdldF9zbm9ybSgiQWxnZWJyYWljIFN1bSIpLCANCiAgICAgICAgICAgICAgICAgICAgICAgIGhhbWFjaGVyX3N1bV9zbm9ybSA9IGdldF9zbm9ybSgiSGFtYWNoZXIgU3VtIiksIA0KICAgICAgICAgICAgICAgICAgICAgICAgbWF4X3Nub3JtID0gZ2V0X3Nub3JtKCJNYXgiKSwgDQogICAgICAgICAgICAgICAgICAgICAgICBoYW1hY2hlcl91bmlvbl9vcGVyYXRvcl9zbm9ybSA9IGdldF9zbm9ybSgiSGFtYWNoZXItdW5pb24gb3BlcmF0b3IiKSwgDQogICAgICAgICAgICAgICAgICAgICAgICB5YWdlcl91bmlvbl9vcGVyYXRvcl9zbm9ybSA9IGdldF9zbm9ybSgiWWFnZXItdW5pb24gb3BlcmF0b3IiKSkNCmBgYA0KDQojIyBDYWxjdWxhdGluZyB0cmFuc2l0aXZlIGNsb3N1cmUgbWF0cml4DQoNCk5vdywgd2UgYXJlIGFibGUgdG8gY2FsY3VsYXRlIHRoZSAiZWlnZW4gbnVtYmVycyIgYW5kICJwb3NpdGl2ZSBtYXRyaXgiLiBUaGlzLCBpbiB0dXJuLCBhbGxvd3MgdXMgdG8gY2FsY3VsYXRlIHJlc3BlY3RpdmUgdHJhbnNpdGl2ZSBjbG9zdXJlIG1hdHJpeC4NCg0KYGBge3J9DQplaWdlbl9udW1iZXJzIDwtIGVpZ2VuX21vZHVsZShpbml0X21hdHJpeCRkYXRhKQ0KcG9zaXRpdmVfbWF0cml4IDwtIHBvc2l0aXZlX21hdHJpeF9jYWxjKGluaXRfbWF0cml4JGRhdGEpDQptYXRyaXggPC0gdHJhbnNpdGl2ZV9jbG9zdXJlKHBvc2l0aXZlX21hdHJpeCwgdG5vcm1fZnVuY3Rpb25zLCB0bm9ybSwgc25vcm1fZnVuY3Rpb25zLCBzbm9ybUVsZW1lbnQsIHNub3JtTWF0cml4LA0KZ2FtbWFUbm9ybU1lYW49MC4wNSwgYWxnYVRub3JtPTAuMSwgZ2FtbWFUbm9ybT0wLjAyLCBwaVRub3JtPTAuMikNCg0KdHJhbnNpdGl2ZV9tYXRyaXggPC0gbWF0cml4DQpgYGANCldlIGhhdmUgY2FsY3VsYXRlZCB0aGUgdHJhbnNpdGl2ZSBtYXRyaXguIExldCdzIGNoZWNrIHdoZXRoZXIgaXQgaXMgbm90IG51bGwuDQpgYGB7cn0NCiFpcy5udWxsKHRyYW5zaXRpdmVfbWF0cml4KQ0KYGBgDQpPbmNlIHdlIGhhdmUgZG9uZSB0aGUgYWJvdmUgc3RlcHMsIHdlIGNhbiBjaGVjayB3aGV0aGVyIHRoZSBtYXRyaXggaXMgaW5kZWVkIHRyYW5zaXRpdmUuDQpgYGB7cn0NCnRyX21heGltdW1fY2hlY2sgPC0gbXVsdGlwbHlfbWF0cml4KHRyYW5zaXRpdmVfbWF0cml4LCB0cmFuc2l0aXZlX21hdHJpeCwgdG5vcm1fZnVuY3Rpb25zLCB0bm9ybSwgc25vcm1fZnVuY3Rpb25zLCBzbm9ybUVsZW1lbnQsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZ2FtbWFUbm9ybU1lYW4sIGFsZ2FUbm9ybSwgZ2FtbWFUbm9ybSwgcGlUbm9ybSwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZ2FtbWFTbm9ybUVsZW1lbnQsIHBpU25vcm1FbGVtZW50KQ0KYWEgPC0gdHJhbnNpdGl2ZV9tYXRyaXggLSB0cl9tYXhpbXVtX2NoZWNrDQppZiAoc3VtKGFhIDwgLTAuMDAwMDEpID09IDAgKSAneWVzJyBlbHNlICdubycNCmBgYA0KV2UgY29uY2x1ZGVkIHRoYXQgbWF0cml4IGlzIGEgdHJhbnNpdGl2ZSBvbmUuDQoNCk5vdywgd2UgY2FuIGNhbGN1bGF0ZSB0aGUgam9pbmVkIHRyYW5zaXRpdmUgbWF0cml4Lg0KDQpgYGB7cn0NCnRyYW5zaXRpdmVfbWF0cml4X2pvaW5lZCA8LSBtYXRyaXhfdHJhbnNpdGl2ZV9qb2luKHRyYW5zaXRpdmVfbWF0cml4LCBzbm9ybV9mdW5jdGlvbnMsIHNub3JtRWxlbWVudCxnYW1tYVNub3JtLCBwaVNub3JtKQ0KdHJhbnNpdGl2ZV9tYXRyaXhfam9pbmVkDQpgYGANCiMjIE5ldHdvcmsgZGF0YQ0KDQpGb3IgYW4gYGluaXRfbWF0cml4JGRhdGFgIHdlIG1heSBwcmVzZW50IHRoZSBncmFwaCBvZiBub2RlcyB1c2luZyB0aGUgYHFncmFwaGAgbGlicmFyeS4gDQoNCmBgYHtyfQ0KbGlicmFyeShxZ3JhcGgpDQpxZ3JhcGgoaW5pdF9tYXRyaXgkZGF0YSxlZGdlLmxhYmVscz1UUlVFKQ0KYGBgDQojIyBJbXB1bHNlDQoNCkxldCdzIGNhbGN1bGF0ZSB0aGUgaW1wdWxzZSBkYXRhIGZvciBvdXIgaW5pdGlhbCBtYXRyaXguDQpgYGB7cn0NCm0yIDwtIG1hdHJpeChjYmluZCgwKSwgbmNvbCA9IDEsIG5yb3cgPSBucm93KGluaXRfbWF0cml4JGRhdGEpKQ0KY29sbmFtZXMobTIpIDwtIGMoIngiKQ0KREYgPSBtMg0KYGBgDQoNCkZvciB0aGUgc3RlcCBpbXB1bHNlIG9mIDEwLCB0aGUgaW1wdWxzZSBkYXRhIHdpbGwgYmUgYXMgZm9sbG93czoNCmBgYHtyfQ0Kc3RlcEltcHVsc2UgPC0gMTANCkRGIDwtIERGWywneCddDQprIDwtIERGDQogICAgICANCmZvcihpIGluIDE6c3RlcEltcHVsc2UpIA0KICAgICAgew0KICAgICAgICBrIDwtIHQoaW5pdF9tYXRyaXgkZGF0YSkgJSolIGsNCiAgICAgICAgaSA8LSBpKzENCiAgICAgICAgREYgPC0gY2JpbmQoREYsIHJvdW5kKGssIDUpKQ0KICAgICAgfQ0KY29sbmFtZXMoREYpIDwtIGMoJ3gnLCAxOnN0ZXBJbXB1bHNlKQ0KDQpkYXRhX2ltcHVsc2UgPC0gREYNCmRhdGEuZnJhbWUoZGF0YV9pbXB1bHNlKQ0KYGBgDQpUaGUgY29ycmVzcG9uZGluZyBwbG90IGZvciB0aGUgb2J0YWluZWQgaW1wdWxzZSBkYXRhIGlzIHNob3duIGJlbG93Lg0KDQpgYGB7cn0NCm1hdHBsb3QodChkYXRhX2ltcHVsc2UpLHR5cGUgPSAibCIsIHhsYWIgPSBOVUxMLCB5bGFiID0gIkltcHVsc2UiKQ0KbGVnZW5kKCJ0b3ByaWdodCIsIGluc2V0PTAsIGxlZ2VuZD1jKDE6bnJvdyhkYXRhX2ltcHVsc2UpKSwgcGNoPTEsICAgY29sPWMoMTpucm93KGRhdGFfaW1wdWxzZSkpLCBob3Jpej1UUlVFKQ0KYGBgDQojIyBEaXJlY3QgdGFzaw0KDQpUbyBjYWxjdWxhdGUgdGhlIGRpcmVjdCB0YXNrLCB0aGUgY29udHJvbCB2ZWN0b3Igc2hvdWxkIGJlIGlkZW50aWZpZWQuDQoNCmBgYHtyfQ0KZmluYWx2ZWN0b3IgPC0gbGVuZ3RoKHdoaWNoKGNvbFN1bXMoaW5pdF9tYXRyaXgkZGF0YSA9PSAwKSA9PSBuY29sKGluaXRfbWF0cml4JGRhdGEpKSkNCkRGX2NvbnRyb2xfdmVjdG9yIDwtIGZpbmFsdmVjdG9yDQpERl9jb250cm9sX3ZlY3Rvcg0KYGBgDQpTaW5jZSB0aGUgdmFsdWUgb2YgYERGX2NvbnRyb2xfdmVjdG9yYCBpcyAwLCB0aGVyZSBhcmVuJ3QgYW55IGNvbnRyb2wgdmVjdG9ycyBpbiBkYXRhLg0KDQpUaGVyZWZvcmUsIHRoZSBkaXJlY3Qgc29sdXRpb24gbWF0cml4IGlzIE5VTEwgYXMgd2VsbC4NCg0KIyMgUmV2ZXJzZSB0YXNrDQpMZXQncyBvYnRhaW4gdGhlIERGX2dvYWxfdmVjdG9yIHRvIGNhbGN1bGF0ZSB0aGUgcmV2ZXJzZSB0YXNrLg0KDQpgYGB7cn0NCmlmICghaXMubnVsbChpbml0X21hdHJpeCRkYXRhKSkgew0KICAgICBmaW5hbHZlY3RvciA8LSBsZW5ndGgod2hpY2gocm93U3Vtcyhpbml0X21hdHJpeCRkYXRhID09IDApID09IG5yb3coaW5pdF9tYXRyaXgkZGF0YSkpKQ0KICAgICAgaWYgKGZpbmFsdmVjdG9yID4gMCkNCiAgICAgIGRhdGEuZnJhbWUoTiA9IHJlcCgwLCBmaW5hbHZlY3RvciksDQogICAgICAgICAgICAgICAgIEcgPSByZXAoMCwgZmluYWx2ZWN0b3IpLA0KICAgICAgICAgICAgICAgICBzdHJpbmdzQXNGYWN0b3JzID0gRkFMU0UpDQogICAgfQ0KREZfZ29hbF92ZWN0b3IgPC0gZmluYWx2ZWN0b3INCg0KREZfZ29hbF92ZWN0b3INCmBgYA0KVGhlIHNhbWUgYXMgZm9yIGRpcmVjdCB0YXNrLCB0aGUgcmV2ZXJzZSB0YXNrIGNvbnRyb2wgdmVjdG9yIGlzIGFic2VudCBhcyB3ZWxsIGFzIHRoZSBjb3JyZXNwb25kaW5nIHNvbHV0aW9uIG1hdHJpeC4NCg0KDQojIyBDb25zb25hbnNlDQoNCkxldCdzIGxvb2sgYXQgdGhlIGNvbnNvbmFuc2UgcGxvdCBmb3IgcmVjZW50bHkgY2FsY3VsYXRlZCBgdHJhbnNpdGl2ZV9tYXRyaXhfam9pbmVkYC4gRm9yIHRoYXQgcHVycG9zZSB3ZSBzaG91bGQgZmlyc3RseSB1c2UgYGNyb3NzX2NvbnNvbmFuc2VgIGZ1bmN0aW9uLg0KDQpgYGB7cn0NCmNfbWF0IDwtIGNyb3NzX2NvbnNvbmFuc2UodHJhbnNpdGl2ZV9tYXRyaXhfam9pbmVkKQ0KYm9yZGVyQ3V0ID0gMC43DQpjX2N1dCA8LSAgaWZlbHNlKGNfbWF0PGJvcmRlckN1dCwwLDEpDQpxZ3JhcGgoY19jdXQsZWRnZS5sYWJlbHM9VFJVRSx0aXRsZT0iQ29uc29uYW5jZSIpDQpgYGANCiMjIERpc3NvbmFuc2UNCg0KVmVyeSBzaW1pbGFybHkgdG8gY29uc29uYW5zZSB3ZSBjYW4gb2J0YWluIHRoZSBkaXNzb25hbnNlIHBsb3QuDQoNCmBgYHtyfQ0KZF9tYXQgPC0gY3Jvc3NfZGlzc29uYW5zZSh0cmFuc2l0aXZlX21hdHJpeF9qb2luZWQpDQpkX2N1dCA8LSAgaWZlbHNlKGRfbWF0PGJvcmRlckN1dCwwLDEpDQpxZ3JhcGgoZF9jdXQsZWRnZS5sYWJlbHM9VFJVRSx0aXRsZT0iRGlzc29uYW5jZSIpDQpgYGANCiMjIFBvc2l0aXZlIGluZmx1ZW5jZQ0KDQpQb3NpdGl2ZSBpbmZsdWVuY2UgY2FsY3VsYXRpb24gaW4gYWxtb3N0IGlkZW50aWNhbCB0byB0aGUgcHJldmlvdXMgb25lcy4NCg0KYGBge3J9DQpwX21hdCA8LSBjcm9zc19wb3NpdGl2ZV9pbmZsdWVuY2UodHJhbnNpdGl2ZV9tYXRyaXhfam9pbmVkKQ0KcF9jdXQgPC0gIGlmZWxzZShwX21hdDxib3JkZXJDdXQsMCwxKQ0KcWdyYXBoKHBfY3V0LGVkZ2UubGFiZWxzPVRSVUUsdGl0bGU9IlBvc2l0aXZlIGluZmx1ZW5jZSIpDQpgYGANCiMjIE5lZ2F0aXZlIGluZmx1ZW5jZQ0KDQpGaW5hbGx5LCB3ZSBjYW4gcGxvdCB0aGUgbmVnYXRpdmUgaW5mbHVlbmNlLg0KDQpgYGB7cn0NCm5fbWF0IDwtIGNyb3NzX25lZ2F0aXZlX2luZmx1ZW5jZSh0cmFuc2l0aXZlX21hdHJpeF9qb2luZWQpDQpuX2N1dCA8LSAgaWZlbHNlKGFicyhuX21hdCk8Ym9yZGVyQ3V0LDAsMSkNCnFncmFwaChuX2N1dCxlZGdlLmxhYmVscz1UUlVFLHRpdGxlPSJOZWdhdGl2ZSBpbmZsdWVuY2UiKQ0KYGBgDQoNCiMjIFRhYmxlIGNvbnNvbmFuc2UNCg0KV2UgY2FuIGFsc28gcHJlc2VudCB0aGUgY29uc29uYW5zZSAvIGRpc3NvbmFuc2UgaW5mb3JtYXRpb24gaW4gdGhlIHRhYmxlLg0KYGBge3J9DQpjb25zb25hbnNlX2Rpc3NvbmFuc2UgPC0gY29uc29uYW5zZV9kaXNzb25hbnNlKHRyYW5zaXRpdmVfbWF0cml4X2pvaW5lZCkNCmRhdGEuZnJhbWUoY29uc29uYW5zZV9kaXNzb25hbnNlKQ0KYGBgDQojIyBJSyBwb3NpdGl2ZQ0KDQpXZSBjYW4gbm93IGNhbGN1bGF0ZSBJSyBwb3NpdGl2ZSBmb3IgdGhlIGNhbGN1bGF0ZWQgYWJvdmUgdHJhbnNpdGl2ZSBqb2luZWQgbWF0cml4LiBBcyBmb3IgcGFyYW1ldGVycywgYGZyb21gIGFuZCBgdG9gIGFyZSBzZXQgbWFudWFsbHkuDQpgYGB7cn0NCmZyb20gPSAxDQp0byA9IDENCklLUG9zaXRpdmUgPC0gaWtfcG9zX21heGltdW0odHJhbnNpdGl2ZV9tYXRyaXhfam9pbmVkLCBpbml0X21hdHJpeCRkYXRhLCBmcm9tLCB0bykNCmRhdGEuZnJhbWUoSUtQb3NpdGl2ZSkNCmBgYA0KIyMgSUsgbmVnYXRpdmUNCg0KVmVyeSBzaW1pbGFybHkgdG8gdGhlIHByZXZpb3VzIHNlY3Rpb24sIHdlIGNhbiBub3cgY2FsY3VsYXRlIElLIG5lZ2F0aXZlIHdpdGggdGhlIHNhbWUgcGFyYW1ldGVycyBhbmQgcHJlc2VudCB0aGUgcmVzdWx0cyBhcyBhIGRhdGEgZnJhbWUuDQpgYGB7cn0NCklLTmVnYXRpdmUgPC0gaWtfbmVnX21heGltdW0odHJhbnNpdGl2ZV9tYXRyaXhfam9pbmVkLCBpbml0X21hdHJpeCRkYXRhLGZyb20sdG8pDQpkYXRhLmZyYW1lKElLTmVnYXRpdmUpDQpgYGANCiMjIENvbmNsdXNpb24NCg0KU28gZmFyLCBmb3IgdGhlIGdpdmVuIGJvb2sgZXhhbXBsZSBtYXRyaXggd2Ugd2VyZSBtYW5hZ2VkIHRvIGNhbGN1bGF0ZSB0cmFuc2l0aXZlIG1hdHJpeCwgY2hlY2sgd2hldGhlciBpdCBpcyB0cmFuc2l0aXZlIHRvIHZlcmlmeSB0aGUgY29ycmVjdG5lc3Mgb2YgdGhlIGNhbGN1bGF0aW9ucy4gRnVydGhlbW9yZSwgZm9yIHRoZSByZXNwZWN0aXZlIHRyYW5zaXRpdmUgbWF0cml4IHdlIGFsbG93ZWQgdG8gZ2V0IGNhbGN1bGF0ZSBhbmQgcGxvdCBuZXR3b3JkIGRhdGEgaW4gdGhlIGZvcm0gb2Ygbm9kZXMsIGltcHVsc2UsIGRpcmVjdCBhbmQgcmV2ZXJzZSB0YXNrcywgY29uc29uYW5zZS9kaXNzb25hbnNlIHBsb3RzIGFuZCB0aGUgcmVzcGVjdGl2ZSBjb25zb25hbnNlIHRhYmxlLCBwb3NpdGl2ZSBhbmQgbmVnYXRpdmUgaW5mbHVlbmNlIHBsb3RzIGFzIHdlbGwgYXMgdGhlIElLIHBvc2l0aXZlIGFuZCBuZWdhdGl2ZS4NCg0KRHVyaW5nIHRoZSB0dXRvcmlhbCB3ZSBtYW51YWxseSBhc3NpZ25lZCBzb21lIHZhbHVlcyB0byB0aGUgcGFyYW1ldGVycy4gRm9yIHRoZSBwdXJwb3NlIG9mIHRoZSBmdXJ0aGVyIHJlc2VhcmNoLCB5b3UgbWF5IHRyeSBkaWZmZXJlbnQgdmFsdWVzIGFuZCBjb21wYXJlIHRoZSBvYnRhaW5lZCByZXN1bHRzLg0KDQpUbyBjb25jbHVkZSwgdXNpbmcgdGhlIEZDTSBwYWNrYWdlIHdlIGFyZSBhYmxlIHRvIGNhbGN1bGF0ZSBkaWZmZXJlbnQgY29tcG9uZW50cyB3aGljaCBmdXJ0aGVyIGNvdWxkIGJlIHVzZWQgYXMgYSBjb3JlIGZvciBhIHNlcGFyYXRlIGFwcGxpY2F0aW9uLg0K