library(RColorBrewer)
library(colorspace)
library(igraph)
library(rgl)
library(mapproj)
library(maps)
library(OpenStreetMap)
library(shiny)
library(shinyjs)
library(graphics)
library(ShinyDash)
#library(shinydashboard)
#library(shinyIncubator)
library(digest)
library(googleVis)
library(lattice)
library(fields)
library(clue)
library(gplots)
library(rCharts)
library(ggplot2)
library(d3Network)
library(session)
library(d3heatmap)
library(networkD3)
library(dplyr)


source("version.R")
source("global.R")
source("language.R")
source("muxLib.R")

#to open/save a session.. still experimental
#https://github.com/jcheng5/shiny-resume
#print(objects(all.names = TRUE))
#http://www.inside-r.org/packages/cran/session/docs
    
#to make a CRAN package in the next future
#http://hilaryparker.com/2014/04/29/writing-an-r-package-from-scratch/

textInputRow <- function (inputId, label, value = "", width=NULL) {
    div(style="display:inline-block",
        tags$label(label, `for` = inputId), 
        tags$input(id = inputId, type = "text", value = value,class="input-small", width=width))
}



shinyServer(function(input, output, session) {
    welcomeFunction()
    
    commonRunif <<- runif(1)
    #commonRunif <- 0.0148374617565423
    #print(paste("SEED:",commonRunif))


    values <- reactiveValues(communityMultiplexOK = F,
                                            communitySingleLayerOK = F,
                                            communityMultiplexBatchOK = F,
                                            componentsMultiplexOK = F,
                                            componentsSingleLayerOK = F,
                                            triadsMultiplexOK = F,
                                            triadsSingleLayerOK = F
                                            )

    result <- tryCatch({


        output$btnGenerateReport <- downloadHandler(
        # For PDF output, change this to "report.pdf"
            filename = "report.html",
            content = function(file) {
                # Copy the report file to a temporary directory before processing it, in
                # case we don't have write permissions to the current working dir (which
                # can happen when deployed).
                tempReport <- file.path(tempdir(), "report.Rmd")
                file.copy("report.Rmd", tempReport, overwrite = TRUE)
                
                # Set up parameters to pass to Rmd document
                params <- list(x = output$matrixExplorerMultilayerHeatmap)
                
                # Knit the document, passing in the `params` list, and eval it in a
                # child of the global environment (this isolates the code in the document
                # from the code in this app).
                rmarkdown::render(tempReport, output_file = file,
                                                params = params,
                                                envir = new.env(parent = globalenv())
                )
            }
        )        

        #https://github.com/rstudio/shiny/wiki/Bookmarkable-state
        onBookmark(function(state) {
            # Save content to a file
            messageFile <- file.path(state$dir, "message.txt")
            cat(as.character(Sys.time()), file = messageFile)
            
            state$values$listDiagnostics <- listDiagnostics
            state$values$listDiagnosticsSingleLayer <- listDiagnosticsSingleLayer
            state$values$listDiagnosticsMergeSingleLayer <- listDiagnosticsMergeSingleLayer
            state$values$listDiagnosticsMerge <- listDiagnosticsMerge
            
            state$values$listTriads <- listTriads
            state$values$listTriadsSingleLayer <- listTriadsSingleLayer
            state$values$listTriadsMerge <- listTriadsMerge
            state$values$listTriadsMergeSingleLayer <- listTriadsMergeSingleLayer
            state$values$sumTriadsMerge <- sumTriadsMerge
            state$values$sumTriadsMergeSingleLayer <- sumTriadsMergeSingleLayer
            
            state$values$listCommunities <- listCommunities
            state$values$listCommunitiesSingleLayer <- listCommunitiesSingleLayer
            state$values$listCommunitiesMerge <- listCommunitiesMerge
            state$values$listCommunitiesMergeSingleLayer <- listCommunitiesMergeSingleLayer
            state$values$sumCommunitiesMerge <- sumCommunitiesMerge
            state$values$sumCommunitiesMergeSingleLayer <- sumCommunitiesMergeSingleLayer
            
            state$values$listComponents <- listComponents
            state$values$listComponentsSingleLayer <- listComponentsSingleLayer
            state$values$listComponentsMerge <- listComponentsMerge
            state$values$listComponentsMergeSingleLayer <- listComponentsMergeSingleLayer
            state$values$sumComponentsMerge <- sumComponentsMerge
            state$values$sumComponentsMergeSingleLayer <- sumComponentsMergeSingleLayer
            
            state$values$listDistanceSimilarity <- listDistanceSimilarity
            state$values$listInterPearson <- listInterPearson
            state$values$listInterSpearman <- listInterSpearman
            state$values$listOverlap <- listOverlap
            state$values$listNodeOverlap <- listNodeOverlap
            state$values$listMotifs <- listMotifs
            
            state$values$listQueryResult <- listQueryResult
            
            state$values$listReducibility <- listReducibility
            
            state$values$dfTimeline <- dfTimeline
            
            state$values$defaultVsize <- defaultVsize
            state$values$defaultEsize <- defaultEsize
            state$values$defaultVcolor <- defaultVcolor
            state$values$defaultEcolor <- defaultEcolor
            
            state$values$inputOK <- inputOK
            state$values$diagnosticsMultiplexOK <- diagnosticsMultiplexOK
            state$values$diagnosticsSingleLayerOK <- diagnosticsSingleLayerOK
            state$values$diagnosticsOK <- diagnosticsOK
            state$values$communityOK <- communityOK
            state$values$componentsOK <- componentsOK
            state$values$triadsOK <- triadsOK
            state$values$communityMultiplexOK <- communityMultiplexOK
            state$values$communitySingleLayerOK <- communitySingleLayerOK
            state$values$communityMultiplexBatchOK <- communityMultiplexBatchOK
            state$values$componentsMultiplexOK <- componentsMultiplexOK
            state$values$componentsSingleLayerOK <- componentsSingleLayerOK
            state$values$triadsMultiplexOK <- triadsMultiplexOK
            state$values$triadsSingleLayerOK <- triadsSingleLayerOK
            
            state$values$avgGlobalOverlapping <- avgGlobalOverlapping
            state$values$avgGlobalOverlappingMatrix.df <- avgGlobalOverlappingMatrix.df
            state$values$avgGlobalNodeOverlappingMatrix.df <- avgGlobalNodeOverlappingMatrix.df
            state$values$interPearson.df <- interPearson.df
            state$values$interSpearman.df <- interSpearman.df
            state$values$frobeniusNorm.df <- frobeniusNorm.df

            state$values$communityBatchMembership <- communityBatchMembership
            state$values$communityBatchData <- communityBatchData

            state$values$orientationRGL <- orientationRGL

            state$values$externalEdgeSizeFlag <- externalEdgeSizeFlag
            state$values$externalEdgeColorFlag <- externalEdgeColorFlag
            state$values$externalEdgeColorTable <- externalEdgeColorTable
            state$values$externalNodeSizeFlag <- externalNodeSizeFlag
            state$values$externalNodeColorFlag <- externalNodeColorFlag
            state$values$externalNodeColorTable <- externalNodeColorTable
            state$values$nodeLabelSeqIdConvTable <- nodeLabelSeqIdConvTable
            
            state$values$layouts <- layouts
            
            #needed for renderUI stuff
            reactive.values <<- lapply(reactiveValuesToList(input), unclass)
            
            updateSavedSessionsList()
            
            showModal(modalDialog(title = "Save session", "Done!", easyClose = TRUE))
        })
    
        #this must be -ed and not -e to allow some renderUI things to work
        onRestored(function(state) {
            # Read the file
            messageFile <- file.path(state$dir, "message.txt")
            timeText <- readChar(messageFile, 1000)
    
            # updateTextInput must be called in onRestored, as opposed to onRestore,
            # because onRestored happens after the client browser is ready.
#            output$txtLastSavedSession <- reactive({
#                return(list(lastSavedSession = paste0("Last saved session @ ", as.character(Sys.time())) ))
#            })
        
            listDiagnostics <<- state$values$listDiagnostics
            listDiagnosticsSingleLayer <<- state$values$listDiagnosticsSingleLayer
            listDiagnosticsMergeSingleLayer <<- state$values$listDiagnosticsMergeSingleLayer
            listDiagnosticsMerge <<- state$values$listDiagnosticsMerge
            
            listTriads <<- state$values$listTriads
            listTriadsSingleLayer <<- state$values$listTriadsSingleLayer
            listTriadsMerge <<- state$values$listTriadsMerge
            listTriadsMergeSingleLayer <<- state$values$listTriadsMergeSingleLayer
            sumTriadsMerge <<- state$values$sumTriadsMerge
            sumTriadsMergeSingleLayer <<- state$values$sumTriadsMergeSingleLayer
            
            listCommunities <<- state$values$listCommunities
            listCommunitiesSingleLayer <<- state$values$listCommunitiesSingleLayer
            listCommunitiesMerge <<- state$values$listCommunitiesMerge
            listCommunitiesMergeSingleLayer <<- state$values$listCommunitiesMergeSingleLayer
            sumCommunitiesMerge <<- state$values$sumCommunitiesMerge
            sumCommunitiesMergeSingleLayer <<- state$values$sumCommunitiesMergeSingleLayer
            
            listComponents <<- state$values$listComponents
            listComponentsSingleLayer <<- state$values$listComponentsSingleLayer
            listComponentsMerge <<- state$values$listComponentsMerge
            listComponentsMergeSingleLayer <<- state$values$listComponentsMergeSingleLayer
            sumComponentsMerge <<- state$values$sumComponentsMerge
            sumComponentsMergeSingleLayer <<- state$values$sumComponentsMergeSingleLayer
            
            listDistanceSimilarity <<- state$values$listDistanceSimilarity
            listInterPearson <<- state$values$listInterPearson
            listInterSpearman <<- state$values$listInterSpearman
            listOverlap <<- state$values$listOverlap
            listNodeOverlap <<- state$values$listNodeOverlap
            listMotifs <<- state$values$listMotifs
            
            listQueryResult <<- state$values$listQueryResult
            
            listReducibility <<- state$values$listReducibility
            
            dfTimeline <<- state$values$dfTimeline
            
            defaultVsize <<- state$values$defaultVsize
            defaultEsize <<- state$values$defaultEsize
            defaultVcolor <<- state$values$defaultVcolor
            defaultEcolor <<- state$values$defaultEcolor
            
            inputOK <<- state$values$inputOK
            diagnosticsMultiplexOK <<- state$values$diagnosticsMultiplexOK
            diagnosticsSingleLayerOK <<- state$values$diagnosticsSingleLayerOK
            diagnosticsOK <<- state$values$diagnosticsOK
            communityOK <<- state$values$communityOK
            componentsOK <<- state$values$componentsOK
            triadsOK <<- state$values$triadsOK
            communityMultiplexBatchOK <<- state$values$communityMultiplexBatchOK
            communityMultiplexOK <<- state$values$communityMultiplexOK
            communitySingleLayerOK <<- state$values$communitySingleLayerOK
            componentsMultiplexOK <<- state$values$componentsMultiplexOK
            componentsSingleLayerOK <<- state$values$componentsSingleLayerOK
            triadsMultiplexOK <<- state$values$triadsMultiplexOK
            triadsSingleLayerOK <<- state$values$triadsSingleLayerOK

            avgGlobalOverlapping <<- state$values$avgGlobalOverlapping
            avgGlobalOverlappingMatrix.df <<- state$values$avgGlobalOverlappingMatrix.df
            avgGlobalNodeOverlappingMatrix.df <<- state$values$avgGlobalNodeOverlappingMatrix.df
            interPearson.df <<- state$values$interPearson.df
            interSpearman.df <<- state$values$interSpearman.df
            frobeniusNorm.df <<- state$values$frobeniusNorm.df

            communityBatchMembership <<- state$values$communityBatchMembership
            communityBatchData <<- state$values$communityBatchData

            orientationRGL <<- state$values$orientationRGL

            externalEdgeSizeFlag <<- state$values$externalEdgeSizeFlag
            externalEdgeColorFlag <<- state$values$externalEdgeColorFlag
            externalEdgeColorTable <<- state$values$externalEdgeColorTable
            externalNodeSizeFlag <<- state$values$externalNodeSizeFlag
            externalNodeColorFlag <<- state$values$externalNodeColorFlag
            externalNodeColorTable <<- state$values$externalNodeColorTable
            nodeLabelSeqIdConvTable <<- state$values$nodeLabelSeqIdConvTable
            
            layouts <<- state$values$layouts
            
            #print(reactive.values)
            
            #for renderUI stuff
            if (exists("reactive.values")) {
                cat("Restoring reactive values...\n")
                lapply(names(values),
                    function(x) session$sendInputMessage(x, list(value = values[[x]]))
                )
            }
        })


        #HELP
        observeEvent(input$btnSessionManagerHelp, {
              showModal(modalDialog(title = "Help", HTML(getText("SessionManagerHelp")), easyClose = TRUE))
        })

        observeEvent(input$btnCentralityHelp, {
              showModal(modalDialog(title = "Help", HTML(getText("GlobalDiagnosticsCentralityHelp")), easyClose = TRUE))
        })

        observeEvent(input$btnImportHelp, {
              showModal(modalDialog(title = "Help", HTML(getText("ImportHelp")), easyClose = TRUE))
        })

        observeEvent(input$btnConsoleHelp, {
              showModal(modalDialog(title = "Help", HTML(getText("ConsoleHelp")), easyClose = TRUE))
        })

        observeEvent(input$btnQueryHelp, {
              showModal(modalDialog(title = "Help", HTML(getText("QueryHelp")), easyClose = TRUE))
        })

        observeEvent(input$btnGlobalDiagnosticsCorrelationHelp, {
              showModal(modalDialog(title = "Help", HTML(getText("GlobalDiagnosticsCorrelationHelp")), easyClose = TRUE))
        })        

        observeEvent(input$btnTriadsHelp, {
              showModal(modalDialog(title = "Help", HTML(getText("TriadsHelp")), easyClose = TRUE))
        })

        observeEvent(input$btnMotifsHelp, {
              showModal(modalDialog(title = "Help", HTML(getText("MotifsHelp")), easyClose = TRUE))
        })

        observeEvent(input$btnComponentsHelp, {
              showModal(modalDialog(title = "Help", HTML(getText("ComponentsHelp")), easyClose = TRUE))
        })        

        observeEvent(input$btnCommunityHelp, {
              showModal(modalDialog(title = "Help", HTML(getText("CommunityHelp")), easyClose = TRUE))
        })        

        observeEvent(input$btnGlobalDiagnosticsAnnularVizHelp, {
              showModal(modalDialog(title = "Help", HTML(getText("GlobalDiagnosticsAnnularVizHelp")), easyClose = TRUE))
        })        

        observeEvent(input$btnGlobalDiagnosticsNetworkLayersHelp, {
              showModal(modalDialog(title = "Help", HTML(getText("GlobalDiagnosticsNetworkLayersHelp")), easyClose = TRUE))
        })        

        observeEvent(input$btnVisualizationHelp, {
              showModal(modalDialog(title = "Help", HTML(getText("VisualizationHelp")), easyClose = TRUE))
        })        

        observeEvent(input$btnReducibilityHelp, {
              showModal(modalDialog(title = "Help", HTML(getText("ReducibilityHelp")), easyClose = TRUE))
        })        

        observeEvent(input$btnDynamicsHelp, {
              showModal(modalDialog(title = "Help", HTML(getText("DynamicsHelp")), easyClose = TRUE))
        })        


        observeEvent(input$btnMultiplexTypeInfo, {
              showModal(modalDialog(title = "Info", HTML(getText("txtMultiplexType")), easyClose = TRUE))
        })

        observeEvent(input$btnEdgeListFileSepInfo, {
              showModal(modalDialog(title = "Info", HTML(getText("txtEdgeListFileSepHelp")), easyClose = TRUE))
        })        

        observeEvent(input$btnReducibilityPaletteInfo, {
              showModal(modalDialog(title = "Info", HTML("<img width='500' src='img/colorpalettes.png' alt=''/>"), easyClose = TRUE))
        })        

        observeEvent(input$btnCommunityColorPaletteInfo, {
              showModal(modalDialog(title = "Info", HTML("<img width='500' src='img/colorpalettes.png' alt=''/>"), easyClose = TRUE))
        })        

        observeEvent(input$btnMultiplexColorPaletteInfo, {
              showModal(modalDialog(title = "Info", HTML("<img width='500' src='img/colorpalettes.png' alt=''/>"), easyClose = TRUE))
        })        

        observeEvent(input$btnMultiplexEdgeColorPaletteInfo, {
              showModal(modalDialog(title = "Info", HTML("<img width='500' src='img/colorpalettes.png' alt=''/>"), easyClose = TRUE))
        })        

        observeEvent(input$btnCentralityColorPaletteInfo, {
              showModal(modalDialog(title = "Info", HTML("<img width='500' src='img/colorpalettes.png' alt=''/>"), easyClose = TRUE))
        })        

        observeEvent(input$btnComponentColorPaletteInfo, {
              showModal(modalDialog(title = "Info", HTML("<img width='500' src='img/colorpalettes.png' alt=''/>"), easyClose = TRUE))
        })        

        observeEvent(input$btnAssortativityPaletteInfo, {
              showModal(modalDialog(title = "Info", HTML("<img width='500' src='img/colorpalettes.png' alt=''/>"), easyClose = TRUE))
        })        

        observeEvent(input$btnMotifsPaletteInfo, {
              showModal(modalDialog(title = "Info", HTML("<img width='500' src='img/colorpalettes.png' alt=''/>"), easyClose = TRUE))
        })        

        observeEvent(input$btnMatrixExplorerPaletteInfo, {
              showModal(modalDialog(title = "Info", HTML("<img width='500' src='img/colorpalettes.png' alt=''/>"), easyClose = TRUE))
        })                

        observeEvent(input$btnTriadsPaletteInfo, {
              showModal(modalDialog(title = "Info", HTML("<img width='500' src='img/colorpalettes.png' alt=''/>"), easyClose = TRUE))
        })                        

        observeEvent(input$btnComponentsPaletteInfo, {
              showModal(modalDialog(title = "Info", HTML("<img width='500' src='img/colorpalettes.png' alt=''/>"), easyClose = TRUE))
        })                               

        observeEvent(input$btnCommunityPaletteInfo, {
              showModal(modalDialog(title = "Info", HTML("<img width='500' src='img/colorpalettes.png' alt=''/>"), easyClose = TRUE))
        })                                       
        
        observeEvent(input$btnAnnularPaletteInfo, {
              showModal(modalDialog(title = "Info", HTML("<img width='500' src='img/colorpalettes.png' alt=''/>"), easyClose = TRUE))
        })                                       

        observeEvent(input$btnGeographicMap, {
              showModal(modalDialog(title = "Info", HTML("<img width='500' src='img/backmaps.png' alt=''/>"), easyClose = TRUE))
        })        


        output$communityChoices <- renderUI({
            if(input$radMultiplexModel=='MULTIPLEX_IS_EDGECOLORED'){
                radioButtons('radCommunityAlgorithm', '',
                   # c(Multislice_ModMax='COMMUNITY_MULTIPLEX_MODMAX',
                     c(Multiplex_Infomap='COMMUNITY_MULTIPLEX_INFOMAP',
                        Infomap='COMMUNITY_INFOMAP',
                        Louvain='COMMUNITY_LOUVAIN',
                        Random_Walk_Trap='COMMUNITY_RANDOM_WALK_TRAP',
                        Edge_Betweenness='COMMUNITY_EDGE_BETWEENNESS'),
                        selected='COMMUNITY_MULTIPLEX_INFOMAP'
                    )
            }else{
                radioButtons('radCommunityAlgorithm', '',
                    c(Multiplex_Infomap='COMMUNITY_MULTIPLEX_INFOMAP',
                        Infomap='COMMUNITY_INFOMAP',
                        Louvain='COMMUNITY_LOUVAIN',
                        Random_Walk_Trap='COMMUNITY_RANDOM_WALK_TRAP',
                        Edge_Betweenness='COMMUNITY_EDGE_BETWEENNESS'),
                        selected='COMMUNITY_MULTIPLEX_INFOMAP'
                    )                
            }
        })
            
            
        output$communityParameters <- renderUI({
            #control the parameters to show while choosing a community detection method
            if(!is.null(input$radCommunityAlgorithm)){
                if(input$radCommunityAlgorithm == "COMMUNITY_MULTIPLEX_MODMAX" && input$radMultiplexModel=='MULTIPLEX_IS_EDGECOLORED'){
                    list(textInput("txtGamma", label=HTML("Resolution parameter:"), "1"),
                         helpText("Hint: the inter-layer strength must be set from the 'Mux Set Up' tab")
                        )
                }else if(input$radCommunityAlgorithm == "COMMUNITY_MULTIPLEX_INFOMAP"  && input$radMultiplexModel=='MULTIPLEX_IS_EDGECOLORED'){
                    list(textInputRow("txtMultimapTries", label=HTML("Runs:"), "10"),
                        textInputRow("txtMultimapRelaxRate", label=HTML("Relax rate:"), "0.75"),
                        checkboxInput("chkMultimapBatchExploration", "Multiscale analysis", value = FALSE),
                        conditionalPanel("input.chkMultimapBatchExploration",
                            textInputRow("txtMultimapMinRelaxRate", label=HTML("Min rate:"), "0.01"),
                            textInputRow("txtMultimapMaxRelaxRate", label=HTML("Max rate:"), "1"),
                            textInputRow("txtMultimapStepsRelaxRate", label=HTML("Steps:"), "5")
                        )
                    )
                }else if(input$radCommunityAlgorithm == "COMMUNITY_MULTIPLEX_INFOMAP"  && input$radMultiplexModel!='MULTIPLEX_IS_EDGECOLORED'){
                    textInput("txtMultimapTries", label=HTML("Number of independent runs:"), "10")
                }
            }
        })


        #Fill the table summarizing the config file
        output$dataTable <- renderDataTable({
            inFile <- "mux_dataset.csv"
            
            if (is.null(inFile))
                return(NULL)
    
            #return(gvisTable(read.csv(inFile, header=T, sep=";"),options=list(page='enable',pageSize=100)))
            return(read.csv(inFile, header=T, sep=";"))
        })

        output$dataPieChart <- renderChart2({
            inFile <- "mux_dataset.csv"
            
            if (is.null(inFile))
                return(NULL)

            data <- read.csv(inFile, header=T, sep=";")
            distrib <- table(data$Type)
            dfChart <- data.frame(Type=names(distrib), N=as.numeric(distrib))
            dfChart$perc <- round(100*dfChart$N/sum(dfChart$N),2)
            doughnut <- nPlot(N~Type, data = dfChart, type = "pieChart")
            doughnut$chart(tooltipContent = "#! function(key, y, e, graph){return '<h4>' + 'Category: ' + key + '</h4>' + '<p>'+ 'Networks: ' + e.point.N + ' (' + e.point.perc + '%)'} !#")
            doughnut$set(width = 600, height = 400) # mk changed width to 800 and height to 500

            doughnut$chart(donut = TRUE)

            return(doughnut)
        })


#        output$dataPieChart <- renderGvis({
#            inFile <- "mux_dataset.csv"
#            
#            if (is.null(inFile))
#                return(NULL)
#
#            data <- read.csv(inFile, header=T, sep=";")
#            distrib <- table(data$Type)
#            dfChart <- data.frame(Type=names(distrib), N=as.numeric(distrib))
#
#            doughnut <- gvisPieChart(dfChart, 
#                      options=list(
#                        width=600,
#                        height=300,
#                        colors="['black','orange', '#2b8cbe', 
#                        'red', '#756bb1', '#31a354', 'gray']",
#                        pieSliceText='percentage',
#                        title='Multiplex Data Types',
#                        pieHole=0.3),
#                      chartid="doughnut")
#            return(doughnut)
#        })

        output$dataScatterPlot <- renderChart2({
            inFile <- "mux_dataset.csv"
            
            if (is.null(inFile))
                return(NULL)

            data <- read.csv(inFile, header=T, sep=";")
            data$logEdges <- log10(data$Edges)
            data$logNodes <- log10(data$Nodes)
            data$logLayers <- log10(data$Layers)
            
            rplot <- nPlot(logEdges~logNodes, data=data, 
                                group="Type", type="scatterChart", opacity=list(const=0.7), height=400,width=600)
            rplot$yAxis(axisLabel="log10 #Edges")
            rplot$xAxis(axisLabel="log10 #Nodes")
            rplot$chart(size = '#! function(d){return d.logLayers} !#')
            rplot$chart(tooltipContent = "#! function(key, x, y, e){ return  '<h3>Network:</h3> ' + e.point.Name + '<br><b>Category:</b> ' + key + '<br><b>Ref:</b> ' + e.point.Reference } !#")
            rplot$chart(forceY=c(floor(min(data$logEdges)),floor(max(data$logEdges))+1), 
                                forceX=c(floor(min(data$logNodes)),floor(max(data$logNodes))+1))
            return(rplot)
        })
        
      	################################################
      	# Network of layers
      	################################################

        observe({
            input$btnRenderNetworkOfLayers
            #print(multilayerEdges)
            if(is.null(multilayerEdges)) return(NULL)
            if(nrow(multilayerEdges)==0 || LAYERS<=0) return(NULL)

            isolate({
                progress <- shiny::Progress$new(session)
                on.exit(progress$close())
                progress$set(message = 'Building the network...', value = 0.2)
                Sys.sleep(1)
    
    
                #this is not the optimal approach.. but for the networks handled by muxViz shoudl be enough
                #alternatively: use igraph to create the weighted network, then convert again to data.frame
                #see http://lists.nongnu.org/archive/html/igraph-help/2013-02/msg00079.html
                dfNoN <- data.frame()
                
                #sub.multi <- multilayerEdges #multilayerEdges[ multilayerEdges[,2]!=multilayerEdges[,4], ]
                #sub.multi$V1 <- NULL
                #sub.multi$V3 <- NULL
                #print(sub.multi)
                sub.multi <- multilayerEdges[,c(2,4,5)]
                
                colnames(sub.multi) <- c("from", "to", "weight")
                g.non <- graph.data.frame(sub.multi, directed=DIRECTED)
                g.non <- simplify(g.non, edge.attr.comb="sum", remove.loops=F)
                
                dfNoN <- as.data.frame( cbind( get.edgelist(g.non) , E(g.non)$weight) )
                colnames(dfNoN) <- c("from", "to", "weight")
                dfNoN$from <- as.numeric(dfNoN$from) - 1
                dfNoN$to <- as.numeric(dfNoN$to) - 1
                dfNoN$weight <- log(1+as.numeric(dfNoN$weight))
    
                if(nrow(dfNoN)>0){
                    comm.non <- as.numeric(membership(cluster_louvain(as.undirected(g.non))))
                    dfNodes <- data.frame(ID=0:(LAYERS-1), name=unlist(layerLabel)[1:LAYERS], group=comm.non)
                    #print(dfNodes)
                    #print(dfNoN)
                    output$networkOfLayersPlot <- renderPrint({
                        return(d3ForceNetwork(Nodes = dfNodes,
                                        Links = dfNoN,
                                        Source = "from", Target = "to",
                                        Value = "weight", NodeID = "name",
                                        Group = "group", width = 600, height = 600,
                                        opacity = 0.8, standAlone = FALSE, zoom=TRUE,
                                        parentElement = '#networkOfLayersPlot'))
                    })
                }else{
                    progress$set(message = 'No network of layers from the data...', value = 0.5)
                    Sys.sleep(1)
                }
    
                progress$set(detail = 'Done!', value = 1)
                Sys.sleep(2)
            })
        })

      	################################################
      	# Layers table
      	################################################
        
        #Read the configuration file and set the global variables
        importNetworksFromConfigurationFile <- function(){
            if (input$btnImportNetworks == 0) return(NULL)

            #if the table with paths to layers is valid, read the edgelist from each file
            if(length(input$project_file)>0){
                if(!file.exists(input$project_file$datapath)){
                    progress <- shiny::Progress$new(session)
                    on.exit(progress$close())
                    progress$set(message = paste('ERROR! File',input$project_file$datapath,'does not exist.'), value = 0.01)
                    Sys.sleep(10)
                    return(NULL)
                }
                fileInput <<- readLines(input$project_file$datapath)

                LAYERS <<- 0

                #By default the input is undirected and unweighted, check only variations from this assumption
                if(input$selEdgeListType == "Directed"){
                    DIRECTED <<- TRUE
                }else{
                    DIRECTED <<- FALSE
                }
                if(input$chkEdgeListWeighted){
                    WEIGHTED <<- TRUE
                }else{
                    WEIGHTED <<- FALSE
                }

                if(input$radMultiplexModel=="MULTIPLEX_IS_EDGECOLORED"){
                    print("Network model is edge-colored. Expecting one edges list per layer.")
                    #the number of lines in the config equals the number of layers
                    LAYERS <<- length(fileInput)
                }else{
                    print("Network model is not edge-colored. Expecting one multilayer edges list in extended format.")
                    #only one line in the config, must calculate from attributes (ie, LayerInfoPath file)
                    layer.info.file <- strsplit(fileInput[1],';')[[1]][2]

                    if(!file.exists(layer.info.file)){
                        progress <- shiny::Progress$new(session)
                        on.exit(progress$close())
                        progress$set(message = paste('ERROR! File',layer.info.file,'does not exist.'), value = 0.01)
                        Sys.sleep(10)
                        return(NULL)
                    }                    

                    layer.info <- read.table(layer.info.file, header=T, sep=as.character(input$txtEdgeListFileSep))
                    LAYERS <<- nrow(layer.info)
                }
                
                layerEdges <<- vector("list",LAYERS+1)
                fileName <<- vector("list",LAYERS)
                layerLabel <<- vector("list",LAYERS+1)
                layerLayoutFile <<- vector("list",LAYERS)
                layerLayout <<- vector("list",LAYERS+1)
                nodesLabel <<- vector("list",LAYERS+1)


                print(paste("Expected layers:", LAYERS))

                if(input$radMultiplexModel=="MULTIPLEX_IS_EDGECOLORED"){
                    for(l in 1:LAYERS){
                        fileName[l] <<- strsplit(fileInput[l],';')[[1]][1]
    
                        if(!file.exists(fileName[[l]][1])){
                            progress <- shiny::Progress$new(session)
                            on.exit(progress$close())
                            progress$set(message = paste('ERROR! File',fileName[[l]][1],'does not exist.'), value = 0.01)
                            Sys.sleep(10)
                            return(NULL)
                        }                    
                        
                        layerLabel[l] <<- strsplit(fileInput[l],';')[[1]][2]
                        layerLayoutFile[l] <<- strsplit(fileInput[l],';')[[1]][3]
    
                        if(!file.exists(layerLayoutFile[[l]][1])){
                            progress <- shiny::Progress$new(session)
                            on.exit(progress$close())
                            progress$set(message = paste('ERROR! File',layerLayoutFile[[l]][1],'does not exist.'), value = 0.01)
                            Sys.sleep(10)
                            return(NULL)
                        }                    
                        
                        print(paste("file",fileName[[l]][1]))
                        layerEdges[[l]] <<-  read.table(fileName[[l]][1], header=input$chkEdgeListFileHeader, sep=as.character(input$txtEdgeListFileSep))
                        print("  Edges list imported...")

                        if(ncol(layerEdges[[l]])==2){ 
                            layerEdges[[l]]$V3 <<- rep(1, nrow(layerEdges[[l]])) 
                        }else{                    
                            if(!WEIGHTED){
                                layerEdges[[l]]$V3 <<- rep(1, nrow(layerEdges[[l]]))
                            }
                        }

                        
                        if(input$chkEdgeListLabel){
                            #edges list are with labeled nodes instead of sequential integer IDs, we transform it
                            #according to the layout file
                            print("  Input is label-based: converting to sequential integer IDs...")
                            
                            if(layerLayoutFile[[l]][1] !="" && (!is.na(layerLayoutFile[[l]][1])) && file.exists(layerLayoutFile[[l]][1])){
                                layerTable <- read.table(layerLayoutFile[[l]][1], header=T, sep=as.character(input$txtEdgeListFileSep))
    
                                if("nodeLabel" %in% colnames(layerTable)){
                                    layerTable$nodeID <- 1:nrow(layerTable)
                                    convTable = setNames(layerTable$nodeID, as.character(layerTable$nodeLabel))
                                    nodeLabelSeqIdConvTable <<- convTable
                                    for(i in 1:2) layerEdges[[l]][,i] <<- convTable[ as.character(unlist(layerEdges[[l]][,i])) ]
                                    
                                    write.table(layerEdges[[l]], paste(fileName[[l]][1],".rel",sep=""), quote=F, row.names=F, col.names=F)
                                    
                                    print("  Done!")
                                }else{
                                    progress <- shiny::Progress$new(session)
                                    on.exit(progress$close())
                                    progress$set(message = paste('ERROR! Layout file',layerLayoutFile[[l]][1],'is not in a valid format (missing nodeLabel column). This format is required when edges lists use labeled nodes instead of sequential integer IDs.'), value = 0.01)
                                    print("  Error: invalid layout format")
                                    Sys.sleep(20)
                                    return(NULL)
                                }                            
                            }else{
                                progress <- shiny::Progress$new(session)
                                on.exit(progress$close())
                                progress$set(message = paste('ERROR! Layout file',layerLayoutFile[[l]][1],'is not specified or does not exist. This file is required when edges lists use labeled nodes instead of sequential integer IDs.'), value = 0.01)
                                print("  Error: invalid layout file")
                                Sys.sleep(20)
                                return(NULL)
                            }
                        }else{
                            #check if the input is numeric, as expected, or raise errors:
                            for(i in 1:ncol(layerEdges[[l]])){
                                if( !is.numeric(layerEdges[[l]][,i]) ){
                                    progress <- shiny::Progress$new(session)
                                    on.exit(progress$close())
                                    progress$set(message = paste('ERROR! Edges list (',fileName[[l]][1],') is not specified by nodes with sequential integer IDs or weights (if any) are not numeric. If you use labels instead of sequential integer IDs you have to check the corresponding box before importing the networks.'), value = 0.01)
                                    print("  Error: invalid edges list file")
                                    Sys.sleep(20)
                                    return(NULL)
                                }
                            }
                        }
                        
                        if(layerLabel[[l]][1]=="" || is.na(layerLabel[[l]][1])){
                            layerLabel[[l]][1] <<- as.character(paste(input$txtLAYER_LABEL_PREFIX, l))
                        }
                        
                        print("  Basic safety checks passed!")                        
                    }
                }else{
                    #model is not edge-colored, one line expected
                    
                    for(l in 1:LAYERS){
                        #assign same file name to all layers, for compatibility. We will not use a lot this vector
                        fileName[l] <<- strsplit(fileInput[1],';')[[1]][1]
                    }
                    if(!file.exists(fileName[[1]][1])){
                        progress <- shiny::Progress$new(session)
                        on.exit(progress$close())
                        progress$set(message = paste('ERROR! File',fileName[[1]][1],'does not exist.'), value = 0.01)
                        Sys.sleep(10)
                        return(NULL)
                    }

                    layer.info.file <- strsplit(fileInput[1],';')[[1]][2]
                    layer.info <- read.table(layer.info.file, header=T, sep=as.character(input$txtEdgeListFileSep))
                    multilayerEdges <<- read.table(fileName[[1]][1], header=input$chkEdgeListFileHeader, sep=as.character(input$txtEdgeListFileSep))
     
                    if(ncol(multilayerEdges)==4){ 
                        multilayerEdges$V5 <<- rep(1, nrow(multilayerEdges)) 
                    }else{                    
                        if(!WEIGHTED){
                            multilayerEdges$V5 <<- rep(1, nrow(multilayerEdges))
                        }
                    }
                    #format: node layer node layer [weight]

                    for(l in 1:LAYERS){
                        layerLayoutFile[l] <<- strsplit(fileInput[1],';')[[1]][3]

                        if(!file.exists(layerLayoutFile[[l]][1])){
                            progress <- shiny::Progress$new(session)
                            on.exit(progress$close())
                            progress$set(message = paste('ERROR! File',layerLayoutFile[[l]][1],'does not exist.'), value = 0.01)
                            Sys.sleep(10)
                            return(NULL)
                        }                    

                        print(paste("File",fileName[[l]][1]))
                        
                        if(input$chkEdgeListLabel){
                            #edges list are with labeled nodes instead of sequential integer IDs, we transform it
                            #according to the layout file
                            print("  Input is label-based: converting to sequential integer IDs...")
                            
                            if(layerLayoutFile[[l]][1] !="" && (!is.na(layerLayoutFile[[l]][1])) && file.exists(layerLayoutFile[[l]][1])){
                                layerTable <- read.table(layerLayoutFile[[l]][1], header=T, sep=as.character(input$txtEdgeListFileSep))
    
                                if("nodeLabel" %in% colnames(layerTable)){
                                    if("layerLabel" %in% colnames(layer.info)){
                                        #convert nodes
                                        layerTable <- read.table(layerLayoutFile[[l]][1], header=T, sep=as.character(input$txtEdgeListFileSep))                                    
                                        layerTable$nodeID <- 1:nrow(layerTable)
                                        convTable = setNames(layerTable$nodeID, as.character(layerTable$nodeLabel))

                                        #convert layers, do this only one time, no need for each layer
                                        if(l==1){            
                                            #print(multilayerEdges)                        
                                            layer.info$layerID <- 1:nrow(layer.info)
                                            convLayerTable = setNames(layer.info$layerID, as.character(layer.info$layerLabel))
                                            multilayerEdges[,1] <<- as.numeric(convTable[ as.character(multilayerEdges[,1]) ])
                                            multilayerEdges[,3] <<- as.numeric(convTable[ as.character(multilayerEdges[,3]) ])

                                            if(input$chkEdgeListLabel2){
                                                multilayerEdges[,2] <<- as.numeric(convLayerTable[ as.character(multilayerEdges[,2]) ])
                                                multilayerEdges[,4] <<- as.numeric(convLayerTable[ as.character(multilayerEdges[,4]) ])
                                            }else{
                                                multilayerEdges[,2] <<- as.numeric(multilayerEdges[,2])
                                                multilayerEdges[,4] <<- as.numeric(multilayerEdges[,4])
                                            }                                                                                        
                                            write.table(multilayerEdges, paste0(fileName[[l]][1],".rel"), quote=F, row.names=F, col.names=F)
                                            #print(multilayerEdges)
                                        }                                        
                                        
                                        layerEdges[[l]] <<- multilayerEdges[ multilayerEdges[,2]==l & multilayerEdges[,4]==l, c(1,3,5)]                                        
                                        write.table(layerEdges[[l]], paste0(fileName[[l]][1],"_layer",l,".rel"), quote=F, row.names=F, col.names=F)
                                       # print(paste("Edge list for layer",l))
                                        #print(layerEdges[[l]])
                                        print("  Done!")
                                    }else{
                                        progress <- shiny::Progress$new(session)
                                        on.exit(progress$close())
                                        progress$set(message = paste('ERROR! Layer info file',layer.info.file,'is not in a valid format (missing layerLabel column). This format is required when edges lists use labeled layers instead of sequential integer IDs.'), value = 0.01)
                                        print("  Error: invalid layer info format")
                                        Sys.sleep(20)
                                        return(NULL)                                        
                                    }
                                }else{
                                    progress <- shiny::Progress$new(session)
                                    on.exit(progress$close())
                                    progress$set(message = paste('ERROR! Layout file',layerLayoutFile[[l]][1],'is not in a valid format (missing nodeLabel column). This format is required when edges lists use labeled nodes instead of sequential integer IDs.'), value = 0.01)
                                    print("  Error: invalid layout format")
                                    Sys.sleep(20)
                                    return(NULL)
                                }                            
                            }else{
                                progress <- shiny::Progress$new(session)
                                on.exit(progress$close())
                                progress$set(message = paste('ERROR! Layout file',layerLayoutFile[[l]][1],'is not specified or does not exist. This file is required when edges lists use labeled nodes instead of sequential integer IDs.'), value = 0.01)
                                print("  Error: invalid layout file")
                                Sys.sleep(20)
                                return(NULL)
                            }
                        }else{
                            #check if the input is numeric, as expected, or raise errors:
                            for(i in 1:ncol(multilayerEdges)){
                                if( !is.numeric(multilayerEdges[,i]) ){
                                    progress <- shiny::Progress$new(session)
                                    on.exit(progress$close())
                                    progress$set(message = paste('ERROR! Edges list (',fileName[[l]][1],') is not specified by nodes/layers with sequential integer IDs or weights (if any) are not numeric. If you use labels instead of sequential integer IDs you have to check the corresponding box before importing the networks.'), value = 0.01)
                                    print("  Error: invalid edges list file")
                                    Sys.sleep(20)
                                    return(NULL)
                                }
                            }
                            
                            layerEdges[[l]] <<- multilayerEdges[ multilayerEdges[,2]==l & multilayerEdges[,4]==l, c(1,3,5)]
                        }

                        #it could be done more efficiently, but to mantain compatibility with existing structure I need this.
                        #for the size of networks muxViz can deal, this is perfectly fine
                        if("layerLabel" %in% colnames(layer.info)){
                            layerLabel[l] <<- as.character(layer.info[ as.numeric(layer.info$layerID)==l, ]$layerLabel)
                        }
                        
                        if(layerLabel[[l]][1]=="" || is.na(layerLabel[[l]][1])){
                            layerLabel[[l]][1] <<- as.character(paste(input$txtLAYER_LABEL_PREFIX, l))
                        }
                    }
                }
                
                layerLabel[[LAYERS+1]][1] <<- input$txtLAYER_AGGREGATE_LABEL_PREFIX

                
                #Find the minimum and maximum node ID in the multiplex
                idmin <- 1e100
                idmax <- 0
                offset <- 0
                cntEdges <- 0
                
                if(input$radMultiplexModel=="MULTIPLEX_IS_EDGECOLORED"){
                    for(l in 1:LAYERS){
                        if( min(layerEdges[[l]][,1:2],na.rm=T) < idmin) idmin <- min(layerEdges[[l]][,1:2],na.rm=T)
                        if( max(layerEdges[[l]][,1:2],na.rm=T) > idmax) idmax <- max(layerEdges[[l]][,1:2],na.rm=T)
        
                        cntEdges <- cntEdges + nrow(layerEdges[[l]])
                    }
                    
                    Edges <<- cntEdges
                }else{
                    idmin <- min( min(multilayerEdges[,1], na.rm=T), min(multilayerEdges[,3], na.rm=T) )
                    idmax <- max( max(multilayerEdges[,1], na.rm=T), max(multilayerEdges[,3], na.rm=T) )
                    
                    Edges <<- nrow(multilayerEdges)
                }
                
                                
                if(idmin == 0) offset <- 1
    
                Nodes <<- idmax + offset
                offsetNode <<- offset
                minNodeID <<- idmin
                maxNodeID <<- idmax
                
                #reset flags and tables        
                SupraAdjacencyMatrix <<- NULL        
                diagnosticsMultiplexOK <<- F
                diagnosticsSingleLayerOK <<- F
                diagnosticsOK <<- F
                communityOK <<- F
                componentsOK <<- F
                triadsOK <<- F
                communityMultiplexBatchOK <<- F
                communityMultiplexOK <<- F
                communitySingleLayerOK <<- F
                componentsMultiplexOK <<- F
                componentsSingleLayerOK <<- F
                triadsMultiplexOK <<- F
                triadsSingleLayerOK <<- F

                listDiagnostics <<- data.frame()
                listDiagnosticsSingleLayer <<- data.frame()
                listDiagnosticsMerge <<- data.frame()
                listDiagnosticsMergeSingleLayer <<- data.frame()

                listTriads <<- data.frame()
                listTriadsSingleLayer <<- data.frame()
                listTriadsMerge <<- data.frame()
                listTriadsMergeSingleLayer <<- data.frame()
                sumTriadsMerge <<- data.frame()
                sumTriadsMergeSingleLayer <<- data.frame()

                listCommunities <<- data.frame()
                listCommunitiesSingleLayer <<- data.frame()
                listCommunitiesMerge <<- data.frame()
                listCommunitiesMergeSingleLayer <<- data.frame()
                sumCommunitiesMerge <<- data.frame()
                sumCommunitiesMergeSingleLayer <<- data.frame()
                
                listComponents <<- data.frame()
                listComponentsSingleLayer <<- data.frame()
                listComponentsMerge <<- data.frame()
                listComponentsMergeSingleLayer <<- data.frame()
                sumComponentsMerge <<- data.frame()
                sumComponentsMergeSingleLayer <<- data.frame()
                
                #External layouts. Check if all external layouts have been provided:
                LAYOUT_EXTERNAL <<- !is.na(all(layerLayoutFile != "")) 
                GEOGRAPHIC_LAYOUT <<- LAYOUT_EXTERNAL
                XMAX <<- -1e10
                YMAX <<- -1e10
                ZMAX <<- -1e10
                XMIN <<- 1e10
                YMIN <<- 1e10
                ZMIN <<- 1e10
                LONGMAX <<- -1e10
                LATMAX <<- -1e10
                LONGMIN <<- 1e10
                LATMIN <<- 1e10
                
                print("Network properties set up")
                print("Verifying external layout...")

                if(input$radMultiplexModel=="MULTIPLEX_IS_EDGECOLORED"){
                    #If each layout is specified correctly
                    for(l in 1:LAYERS){
                        if(layerLayoutFile[[l]][1] !="" && (!is.na(layerLayoutFile[[l]][1]))){
                            layerTable <- read.table(layerLayoutFile[[l]][1], header=T, sep=as.character(input$txtEdgeListFileSep))
                            if(input$chkEdgeListLabel) layerTable$nodeID <- 1:nrow(layerTable)
                            layerLayout[[l]] <<- matrix(c(1),nrow=Nodes,ncol=2)
                            
                            if(length(layerTable$nodeLat)==Nodes && length(layerTable$nodeLong)==Nodes){
                                print(paste("Layout for layer",l,"is geographic. Converting."))
                                #Get boundaries
                                longBounds = c(min(layerTable$nodeLong,na.rm=T),max(layerTable$nodeLong,na.rm=T))
                                latBounds = c(min(layerTable$nodeLat,na.rm=T),max(layerTable$nodeLat,na.rm=T))
                    
                                if(min(layerTable$nodeLong,na.rm=T) < LONGMIN) LONGMIN <<- min(layerTable$nodeLong,na.rm=T)
                                if(min(layerTable$nodeLat,na.rm=T) < LATMIN) LATMIN <<- min(layerTable$nodeLat,na.rm=T)
                                if(max(layerTable$nodeLong,na.rm=T) > LONGMAX) LONGMAX <<- max(layerTable$nodeLong,na.rm=T)
                                if(max(layerTable$nodeLat,na.rm=T) > LATMAX) LATMAX <<- max(layerTable$nodeLat,na.rm=T)
                                                                                
                                print(paste("  Latitude boundaries: ",LATMIN,LATMAX))
                                print(paste("  Longitude boundaries: ",LONGMIN,LONGMAX))
                                
                                #The input layout is geographic, we must convert it to cartesian
                                sphCoordinates <- list()
                                sphCoordinates$x <- layerTable$nodeLong
                                sphCoordinates$y <- layerTable$nodeLat
                                cartCoordinates <- mapproject(sphCoordinates,proj="mercator")
                                
                                layerTable$nodeX <- cartCoordinates$x
                                layerTable$nodeY <- cartCoordinates$y
                            }else{
                                #layout is not geographic
                                GEOGRAPHIC_LAYOUT <<- F
                            }
                            
                            if(length(layerTable$nodeX)==Nodes && length(layerTable$nodeY)==Nodes){
                                layerLayout[[l]][layerTable$nodeID + offsetNode,1:2] <<- cbind(layerTable$nodeX,layerTable$nodeY)
                    
                                if(min(layerTable$nodeX,na.rm=T) < XMIN) XMIN <<- min(layerTable$nodeX,na.rm=T)
                                if(min(layerTable$nodeY,na.rm=T) < YMIN) YMIN <<- min(layerTable$nodeY,na.rm=T)
                                if(max(layerTable$nodeX,na.rm=T) > XMAX) XMAX <<- max(layerTable$nodeX,na.rm=T)
                                if(max(layerTable$nodeY,na.rm=T) > YMAX) YMAX <<- max(layerTable$nodeY,na.rm=T)
                                
                                GEOGRAPHIC_LAYOUT <<- GEOGRAPHIC_LAYOUT && T
                                print(paste("Layout for layer",l,"specified correctly from external file",layerLayoutFile[[l]][1]))
                            }else{
                                print(paste("Layout for layer",l,"not specified correctly. Proceeding with automatic layouting."))
                                LAYOUT_EXTERNAL <<- F
                                GEOGRAPHIC_LAYOUT <<- F
                            }
                    
                            if(length(layerTable$nodeLabel)==Nodes){
                                print(paste("Nodes' labels for layer",l,"specified correctly from external file",layerLayoutFile[[l]][1]))            
                                #Assign labels to nodes
                                nodesLabel[[l]][layerTable$nodeID + offsetNode] <<- as.character(layerTable$nodeLabel)
                                print("Assigned labels.")
                            }else{
                                print(paste("Nodes' labels for layer",l,"not specified correctly. Proceeding with automatic labeling."))
                                nodesLabel[[l]] <<- 1:Nodes
                            }
                        }else{
                            print(paste("Layout for layer",l,"not specified correctly. Proceeding with automatic layouting."))
                            LAYOUT_EXTERNAL <<- F
                            GEOGRAPHIC_LAYOUT <<- F
                    
                            print(paste("Nodes' labels for layer",l,"not specified correctly. Proceeding with automatic labeling."))
                            #Assign labels to nodes
                            nodesLabel[[l]] <<- 1:Nodes
                        }
                    }
                }else{                    
                    #just one layout file, let's fix l and work with that
                    l <- 1
                    if(layerLayoutFile[[l]][1] !="" && (!is.na(layerLayoutFile[[l]][1]))){
                        layerTable <- read.table(layerLayoutFile[[l]][1], header=T, sep=as.character(input$txtEdgeListFileSep))
                        if(input$chkEdgeListLabel) layerTable$nodeID <- 1:nrow(layerTable)
                            
                        layerLayout[[l]] <<- matrix(c(1),nrow=Nodes,ncol=2)
                        
                        if(length(layerTable$nodeLat)==Nodes && length(layerTable$nodeLong)==Nodes){
                            print(paste("Layout for is geographic. Converting."))
                            #Get boundaries
                            longBounds = c(min(layerTable$nodeLong,na.rm=T),max(layerTable$nodeLong,na.rm=T))
                            latBounds = c(min(layerTable$nodeLat,na.rm=T),max(layerTable$nodeLat,na.rm=T))
                
                            if(min(layerTable$nodeLong,na.rm=T) < LONGMIN) LONGMIN <<- min(layerTable$nodeLong,na.rm=T)
                            if(min(layerTable$nodeLat,na.rm=T) < LATMIN) LATMIN <<- min(layerTable$nodeLat,na.rm=T)
                            if(max(layerTable$nodeLong,na.rm=T) > LONGMAX) LONGMAX <<- max(layerTable$nodeLong,na.rm=T)
                            if(max(layerTable$nodeLat,na.rm=T) > LATMAX) LATMAX <<- max(layerTable$nodeLat,na.rm=T)
                                                                            
                            print(paste("  Latitude boundaries: ",LATMIN,LATMAX))
                            print(paste("  Longitude boundaries: ",LONGMIN,LONGMAX))
                            
                            #The input layout is geographic, we must convert it to cartesian
                            sphCoordinates <- list()
                            sphCoordinates$x <- layerTable$nodeLong
                            sphCoordinates$y <- layerTable$nodeLat
                            cartCoordinates <- mapproject(sphCoordinates,proj="mercator")
                            
                            layerTable$nodeX <- cartCoordinates$x
                            layerTable$nodeY <- cartCoordinates$y
                        }else{
                            GEOGRAPHIC_LAYOUT <<- F
                        }
                        
                        if(length(layerTable$nodeX)==Nodes && length(layerTable$nodeY)==Nodes){
                            layerLayout[[l]][layerTable$nodeID + offsetNode,1:2] <<- cbind(layerTable$nodeX,layerTable$nodeY)
                
                            if(min(layerTable$nodeX,na.rm=T) < XMIN) XMIN <<- min(layerTable$nodeX,na.rm=T)
                            if(min(layerTable$nodeY,na.rm=T) < YMIN) YMIN <<- min(layerTable$nodeY,na.rm=T)
                            if(max(layerTable$nodeX,na.rm=T) > XMAX) XMAX <<- max(layerTable$nodeX,na.rm=T)
                            if(max(layerTable$nodeY,na.rm=T) > YMAX) YMAX <<- max(layerTable$nodeY,na.rm=T)
                            
                            GEOGRAPHIC_LAYOUT <<- GEOGRAPHIC_LAYOUT && T
                            print(paste("Layout specified correctly from external file",layerLayoutFile[[l]][1]))
                        }else{
                            print(paste("Layout not specified correctly. Proceeding with automatic layouting."))
                            LAYOUT_EXTERNAL <<- F
                            GEOGRAPHIC_LAYOUT <<- F
                        }
                
                        if(length(layerTable$nodeLabel)==Nodes){
                            print(paste("Nodes' labels specified correctly from external file",layerLayoutFile[[l]][1]))            
                            #Assign labels to nodes
                            nodesLabel[[l]][layerTable$nodeID + offsetNode] <<- as.character(layerTable$nodeLabel)
                            print("Assigned labels.")
                        }else{
                            print(paste("Nodes' labels not specified correctly. Proceeding with automatic labeling."))
                            nodesLabel[[l]] <<- 1:Nodes
                        }
                    }else{
                        print(paste("Layout not specified correctly. Proceeding with automatic layouting."))
                        LAYOUT_EXTERNAL <<- F
                        GEOGRAPHIC_LAYOUT <<- F
                
                        print(paste("Nodes' labels not specified correctly. Proceeding with automatic labeling."))
                        #Assign labels to nodes
                        nodesLabel[[l]] <<- 1:Nodes
                    }
                    
                    for(l in 2:LAYERS){
                        nodesLabel[[l]] <<- nodesLabel[[1]]
                        layerLayout[[l]] <<- layerLayout[[1]]
                    }
                }
                                
                #giving the layout of the aggregate from external file makes no sense if it is different from other layers
                #and it is also annoying to be constrained to specify the aggregate, if one does not want to show it.
                #Therefore, here I prefer to assign manually the layout of the first layer to the aggregate.
                #So far, I accept this possibility just for sake of completeness, but a correct use of muxViz should avoid
                #situations like this..
                layerLayout[[LAYERS+1]] <<- layerLayout[[1]]
                nodesLabel[[LAYERS+1]] <<- nodesLabel[[1]]                
            }
        }

        #Dynamically create the selectInput for queries
        output$selQueryEdgesLayersOutputID <- renderUI({
            if (input$btnImportNetworks == 0 || length(input$project_file)==0)
                return(NULL)

            layerLabels <- c()
            for(l in 1:LAYERS){
                layerLabels <- c(layerLabels, layerLabel[[l]])
            }
            tmpChoice <- paste0( 1:LAYERS, " (", as.character(layerLabels) ,")" )    
            selectInput("selQueryEdgesLayerID", HTML("For the following layer(s) (multiple selections allowed):"), 
                choices = tmpChoice, multiple=TRUE
                )
        })

        output$selQueryNodesLayersOutputID <- renderUI({
            if (input$btnImportNetworks == 0 || length(input$project_file)==0)
                return(NULL)

            layerLabels <- c()
            for(l in 1:LAYERS){
                layerLabels <- c(layerLabels, layerLabel[[l]])
            }
            tmpChoice <- paste0( 1:LAYERS, " (", as.character(layerLabels) ,")" )    
            selectInput("selQueryNodesLayerID", HTML("For the following layer(s) (multiple selections allowed):"), 
                choices = tmpChoice, multiple=TRUE
                )
        })
        
        output$selQueryNodesOutputID <- renderUI({
            if (input$btnImportNetworks == 0 || length(input$project_file)==0)
                return(NULL)

            tmpChoice <- paste0( 1:Nodes, " (", as.character(nodesLabel[[1]]) ,")" )    
            selectInput("selQueryNodesNodeID", HTML("Retrieve the ego-network of the following node(s) (multiple selections allowed):"), 
                choices = tmpChoice, multiple=TRUE
                )
        })

        output$selQueryEdgesNodesToOutputID <- renderUI({
            if (input$btnImportNetworks == 0 || length(input$project_file)==0)
                return(NULL)

            tmpChoice <- paste0( 1:Nodes, " (", as.character(nodesLabel[[1]]) ,")" )    
            selectInput("selQueryEdgesNodesToID", HTML("To the following node(s) (multiple selections allowed):"), 
                choices = tmpChoice, multiple=TRUE
                )
        })

        output$selQueryEdgesNodesFromOutputID <- renderUI({
            if (input$btnImportNetworks == 0 || length(input$project_file)==0)
                return(NULL)

            tmpChoice <- paste0( 1:Nodes, " (", as.character(nodesLabel[[1]]) ,")" )    
            selectInput("selQueryEdgesNodesFromID", HTML("Retrieve edges from the following node(s) (multiple selections allowed):"), 
                choices = tmpChoice, multiple=TRUE
                )
        })



        #Dynamically create the selectInput after the community have been calculated
        output$selVizNodeColorCommunityTypeOutputID <- renderUI({
            if (input$btnImportNetworks == 0 || input$btnCalculateCommunityDiagnostics == 0 || length(input$project_file)==0)
                return(NULL)
            
            tmpChoice <- c()
            if(communityMultiplexOK){ tmpChoice <- c(tmpChoice, "Multilayer") }            
            if(communitySingleLayerOK){ tmpChoice <- c(tmpChoice, "Single-Layer") }
                        
            selectInput("selVizNodeColorCommunityType", HTML("Use the following analysis:"), 
                choices = tmpChoice
                )
        })

        #Dynamically create the selectInput after the components have been calculated
        output$selVizNodeColorComponentTypeOutputID <- renderUI({
            if (input$btnImportNetworks == 0 || input$btnCalculateComponentsDiagnostics == 0 || length(input$project_file)==0)
                return(NULL)
            
            tmpChoice <- c()
            if(componentsMultiplexOK){ tmpChoice <- c(tmpChoice, "Multilayer") }            
            if(componentsSingleLayerOK){ tmpChoice <- c(tmpChoice, "Single-Layer") }
                        
            selectInput("selVizNodeColorComponentType", HTML("Use the following analysis:"), 
                choices = tmpChoice
                )
        })


        #Dynamically create the selectInput after the networks have been imported
        output$selOutputLayerID <- renderUI({
            if (input$btnImportNetworks == 0 || length(input$project_file)==0)
                return(NULL)
    
            if(LAYERS<=0){
                importNetworksFromConfigurationFile()
            }
            tmpChoice <- c("None")
    
            for(l in 1:LAYERS){
                tmpChoice <- c(tmpChoice,paste("Layer",l,layerLabel[l]))
            }
            selectInput("selInputLayerID", HTML("Layer ID (valid only if type By_LayerID is selected):"), 
                choices = tmpChoice
                )
        })
    
        #Dynamically create the selectInput after the networks have been imported
        output$selAnularVizOutputLayerID <- renderUI({
            if (input$btnImportNetworks == 0 || length(input$project_file)==0)
                return(NULL)
    
            if(LAYERS<=0){
                importNetworksFromConfigurationFile()
            }
            tmpChoice <- c("Multiplex")
            tmpChoice <- c(tmpChoice,"Aggregate")
            tmpChoice <- c(tmpChoice,"Max entropy")
    
            for(l in 1:LAYERS){
                tmpChoice <- c(tmpChoice,paste("Layer",l,layerLabel[l]))
            }
            
            selectInput("selAnularVizInputLayerID", HTML("Sort nodes according to layer ID in single-descriptor visualization:"), 
                choices = tmpChoice
                )
        })

        #Dynamically create the selectInput after the diagnostics have been calculated
        output$selVizNodeSizeOutputID <- renderUI({
            if (input$btnImportNetworks == 0 || input$btnCalculateCentralityDiagnostics == 0 || length(input$project_file)==0)
                return(NULL)
            
            tmpChoice <- c("Uniform","External")
            
            if(diagnosticsMultiplexOK){
                for( attrib in attributes(listDiagnostics[[1]])$names ){        
                    if( (attrib!="Node" && attrib!="Label" && attrib!="Layer") && length(unique(listDiagnostics[[1]][,attrib]))>1 ) tmpChoice <- c(tmpChoice,paste0("Multi-",attrib))
                }
            }
            
            if(diagnosticsSingleLayerOK){
                for( attrib in attributes(listDiagnosticsSingleLayer[[1]])$names ){        
                    if( (attrib!="Node" && attrib!="Label" && attrib!="Layer") && length(unique(listDiagnosticsSingleLayer[[1]][,attrib]))>1 ) tmpChoice <- c(tmpChoice,attrib)
                }                
            }
                        
            selectInput("selVizNodeSizeID", HTML("Node size proportional to:"), 
                choices = tmpChoice
                )
        })

        #Dynamically create the selectInput after the diagnostics have been calculated
        output$selVizNodeColorOutputID <- renderUI({
            if (input$btnImportNetworks == 0 || input$btnCalculateCentralityDiagnostics == 0 || length(input$project_file)==0)
                return(NULL)
    
            tmpChoice <- NULL

            if(diagnosticsMultiplexOK){
                for( attrib in attributes(listDiagnostics[[1]])$names ){        
                    if( (attrib!="Node" && attrib!="Label" && attrib!="Layer") && length(unique(listDiagnostics[[1]][,attrib]))>1 ) tmpChoice <- c(tmpChoice,paste0("Multi-",attrib))
                }
            }
            
            if(diagnosticsSingleLayerOK){
                for( attrib in attributes(listDiagnosticsSingleLayer[[1]])$names ){        
                    if( (attrib!="Node" && attrib!="Label" && attrib!="Layer") && length(unique(listDiagnosticsSingleLayer[[1]][,attrib]))>1 ) tmpChoice <- c(tmpChoice,attrib)
                }                
            }
            
            selectInput("selVizNodeColorID", HTML("Node color proportional to:"), 
                choices = tmpChoice
                )
        })

        #Dynamically create the selectInput after the diagnostics have been calculated
        output$selVizNodeColorTopOutputID <- renderUI({
            if (input$btnImportNetworks == 0 || input$btnCalculateCentralityDiagnostics == 0 || length(input$project_file)==0)
                return(NULL)
    
            tmpChoice <- NULL
            
            if(diagnosticsMultiplexOK){
                for( attrib in attributes(listDiagnostics[[1]])$names ){        
                    if( (attrib!="Node" && attrib!="Label" && attrib!="Layer") && length(unique(listDiagnostics[[1]][,attrib]))>1 ) tmpChoice <- c(tmpChoice,paste0("Multi-",attrib))
                }
            }
            
            if(diagnosticsSingleLayerOK){
                for( attrib in attributes(listDiagnosticsSingleLayer[[1]])$names ){        
                    if( (attrib!="Node" && attrib!="Label" && attrib!="Layer") && length(unique(listDiagnosticsSingleLayer[[1]][,attrib]))>1 ) tmpChoice <- c(tmpChoice,attrib)
                }                
            }
            
            selectInput("selVizNodeColorTopID", HTML("Rank calculated from:"), 
                choices = tmpChoice
                )
        })
    
        #Dynamically create the selectInput after the diagnostics have been calculated
        output$selAnularVizOutputFeatureID <- renderUI({
            if (input$btnImportNetworks == 0 || input$btnCalculateCentralityDiagnostics == 0 || length(input$project_file)==0)
                return(NULL)
    
            tmpChoice <- NULL
            
            for( attrib in attributes(listDiagnostics[[1]])$names ){        
                if( (attrib!="Node" && attrib!="Label" && attrib!="Layer") && length(unique(listDiagnostics[[1]][,attrib]))>1 ) tmpChoice <- c(tmpChoice,attrib)
            }
            
            selectInput("selAnularVizInputFeatureID", HTML("Sort nodes according to centrality in multiplex visualization:"), 
                choices = tmpChoice
                )
        })

        #Dynamically create the selectInput after the diagnostics have been calculated
        output$selDiagnosticsCentralityVizOutputID <- renderUI({
            if (input$btnImportNetworks == 0 || input$btnCalculateCentralityDiagnostics == 0 || length(input$project_file)==0)
                return(NULL)
    
            tmpChoice <- NULL
            
            if(diagnosticsMultiplexOK){
                for( attrib in attributes(listDiagnostics[[1]])$names ){        
                    if( (attrib!="Node" && attrib!="Label" && attrib!="Layer") && length(unique(listDiagnostics[[1]][,attrib]))>1 ) tmpChoice <- c(tmpChoice,attrib)
                }
            }else{
                for( attrib in attributes(listDiagnosticsSingleLayer[[1]])$names ){        
                    if( (attrib!="Node" && attrib!="Label" && attrib!="Layer") && length(unique(listDiagnosticsSingleLayer[[1]][,attrib]))>1 ) tmpChoice <- c(tmpChoice,attrib)
                }                
            }
            
            selectInput("selDiagnosticsCentralityVizID", HTML("Select the centrality descriptor to analyze:"), 
                choices = tmpChoice
                )
        })

        output$selDiagnosticsCentralityVizScatterOutputID <- renderUI({
            if (input$btnImportNetworks == 0 || input$btnCalculateCentralityDiagnostics == 0 || length(input$project_file)==0)
                return(NULL)
    
            tmpChoice <- NULL
            
            if(diagnosticsMultiplexOK){
                for( attrib in attributes(listDiagnostics[[1]])$names ){        
                    if( (attrib!="Node" && attrib!="Label" && attrib!="Layer") && length(unique(listDiagnostics[[1]][,attrib]))>1 ) tmpChoice <- c(tmpChoice,attrib)
                }
            }else{
                for( attrib in attributes(listDiagnosticsSingleLayer[[1]])$names ){        
                    if( (attrib!="Node" && attrib!="Label" && attrib!="Layer") && length(unique(listDiagnosticsSingleLayer[[1]][,attrib]))>1 ) tmpChoice <- c(tmpChoice,attrib)
                }                
            }

            selectInput("selDiagnosticsCentralityVizScatterID", HTML("Select the centrality descriptor to analyze:"), 
                choices = tmpChoice
                )
        })


        output$selDiagnosticsCentralityVizScatterSizeOutputID <- renderUI({
            if (input$btnImportNetworks == 0 || input$btnCalculateCentralityDiagnostics == 0 || length(input$project_file)==0)
                return(NULL)
    
            tmpChoice <- "Uniform"
            
            if(diagnosticsMultiplexOK){
                for( attrib in attributes(listDiagnostics[[1]])$names ){        
                    if( (attrib!="Node" && attrib!="Label" && attrib!="Layer") && length(unique(listDiagnostics[[1]][,attrib]))>1 ) tmpChoice <- c(tmpChoice,attrib)
                }
            }else{
                for( attrib in attributes(listDiagnosticsSingleLayer[[1]])$names ){        
                    if( (attrib!="Node" && attrib!="Label" && attrib!="Layer") && length(unique(listDiagnosticsSingleLayer[[1]][,attrib]))>1 ) tmpChoice <- c(tmpChoice,attrib)
                }                
            }
                        
            selectInput("selDiagnosticsCentralityVizScatterSizeID", HTML("Circle radius proportional to:"), 
                choices = tmpChoice
                )
        })
                        
        #Create a summary of the config file and the networks, when the Import button is pushed
        output$projectSummaryHTML <- reactive({
            if (input$btnImportNetworks == 0 || length(input$project_file)==0)
                return(list())
                
            if(DIRECTED){
                layerType <- "Directed"
            }else{
                layerType <- "Undirected"
            }
            if(WEIGHTED){
                layerType <- paste(layerType,"and Weighted")
            }
    
            if(LAYOUT_EXTERNAL){
                extLayout <- "TRUE"
            }else{
                extLayout <- "FALSE"
            }
            if(GEOGRAPHIC_LAYOUT){
                geoLayout <- paste("TRUE","( Lat bounds:",LATMIN,"/",LATMAX,"Long bounds:",LONGMIN,"/",LONGMAX,")")
            }else{
                geoLayout <- "FALSE"
            }
    
            #be careful: "=" is required instead of "<-"
            return(list(
                sumLayers = as.character(LAYERS),
                sumLayerType = layerType,
                sumNodes = as.character(Nodes),
                sumMinNodeID = as.character(minNodeID),
                sumMaxNodeID = as.character(maxNodeID),
                sumEdges = as.character(Edges),
                sumIsLayoutExternal = extLayout,
                sumIsLayoutGeographic = geoLayout
            ))
        })
    
        #Fill the table summarizing the config file
        output$layersTable <- renderTable({
            input$btnImportNetworks        
            inFile <- input$project_file
            
            if (is.null(inFile))
                return(NULL)
    
            tmplayerTable <- read.csv(inFile$datapath, header=input$chkConfigFileHeader, sep=input$txtConfigFileSep)
            
            if(input$radMultiplexModel=="MULTIPLEX_IS_EDGECOLORED"){
                colnames(tmplayerTable) <- c("EdgeListPath", "Label", "LayoutPath")
            }else{
                colnames(tmplayerTable) <- c("EdgeListPath", "LayerInfoPath", "LayoutPath")   
            }
            
            return(tmplayerTable)
        })
    
        #Create a summary of the timeline file and the networks, when the Import button is pushed
        output$projectTimelineHTML <- reactive({
            if (input$btnImportTimeline == 0 || input$btnRenderNetworks == 0 || length(input$project_file)==0)
                return(list())

            #be careful: "=" is required instead of "<-"
            return(list(
                timelineTimesteps = max(dfTimeline$timeStep)-min(dfTimeline$timeStep)+1,
                timelineAffectNodes = sum(dfTimeline$entity=="node"),
                timelineAffectEdges = sum(dfTimeline$entity=="edge")
            ))
        })


      	################################################
      	# Create the graph objects
      	################################################
        
        #This should be called AFTER importNetworksFromConfigurationFile, where the 
        #relevant variables are filled with the data

        buildNetworks <- function(){
            if (input$btnImportNetworks == 0 || LAYERS<=0) return(NULL)
              
            g <<- vector("list",LAYERS+1)
            AdjMatrix <<- vector("list",LAYERS+1)
            
            #Adj_aggr <- matrix(0,ncol=Nodes,nrow=Nodes)
            AdjMatrix[[LAYERS+1]] <<- Matrix::Matrix(0, ncol=Nodes,nrow=Nodes)
            
            for(l in 1:LAYERS){    
                #account for the possibility of having layers with no intra-links
                if(nrow(layerEdges[[l]])==0){
                    if(ncol(layerEdges[[l]])==3){ colnames(layerEdges[[l]]) <<- c("from","to","weight") }
                    if(ncol(layerEdges[[l]])>3){
                        print("ERROR! More than 3 columns whereas equal or smaller than 3 expected.")
                        return(NULL)
                    }                
                    #generate the network object
                    g[[l]] <<- graph.empty(directed=DIRECTED, n=Nodes)
                    AdjMatrix[[l]] <<- get.adjacency(g[[l]]) #I use this to avoid class incompatibility 
                }else{
                    if(ncol(layerEdges[[l]])==3){ colnames(layerEdges[[l]]) <<- c("from","to","weight") }
                    if(ncol(layerEdges[[l]])>3){
                        print("ERROR! More than 3 columns whereas equal or smaller than 3 expected.")
                        return(NULL)
                    }                
                    
                    if(offsetNode>0){
                        layerEdges[[l]][,1] <<- layerEdges[[l]][,1] + offsetNode
                        layerEdges[[l]][,2] <<- layerEdges[[l]][,2] + offsetNode
                    }
                
                    if(WEIGHTED){
                        if(input$chkRESCALE_WEIGHT){
                            print("Rescaling weights...")
                            layerEdges[[l]][,3] <<- layerEdges[[l]][,3]/min(layerEdges[[l]][,3],na.rm=T)
                        }
                    }
                
                    #generate the network object
                    g[[l]] <<- graph.data.frame(layerEdges[[l]], directed=DIRECTED, vertices=1:Nodes)
                    
                    #remove multiple edges if analysis must be unweighted
                    if(!WEIGHTED){
                        g[[l]] <<- simplify(g[[l]], remove.multiple=T, remove.loops=F)
                    }
                    
                    AdjMatrix[[l]] <<- get.adjacency(g[[l]], attr="weight")
    
                    if(!WEIGHTED){                    
                        AdjMatrix[[l]] <<- binarizeMatrix(AdjMatrix[[l]])
                    }
                    
                    #update the aggregate
                    AdjMatrix[[LAYERS+1]] <<- AdjMatrix[[LAYERS+1]] + AdjMatrix[[l]]
                }
                
                print(paste("Layer ",l,": ",fileName[[l]][1],"   Name:",layerLabel[[l]][1]))
                print(paste("Layer ",l," Directed: ",DIRECTED))
                print(paste("Layer ",l," Weighted: ",WEIGHTED))
                print(paste(nrow(layerEdges[[l]]),"Edges in layer (excluding inter-links): ",l))
            }

            #Build the supra adjacency
            if(input$radMultiplexModel!="MULTIPLEX_IS_EDGECOLORED"){
                SupraAdjacencyMatrix <<- BuildSupraAdjacencyMatrixFromExtendedEdgelist(multilayerEdges, LAYERS, Nodes, DIRECTED)
            }else{
                MultisliceType <- ""
                if(input$radMultiplexType=="MULTIPLEX_IS_ORDERED"){
                    MultisliceType <- "ordered"
                }else if(input$radMultiplexType=="MULTIPLEX_IS_CATEGORICAL"){
                    MultisliceType <- "categorical"
                }    
                
                LayerTensor <- BuildLayersTensor(LAYERS, as.numeric(input$txtOmega), MultisliceType)
                SupraAdjacencyMatrix <<- BuildSupraAdjacencyMatrixFromEdgeColoredMatrices(AdjMatrix[1:LAYERS], LayerTensor, LAYERS, Nodes)
            }

            if(!WEIGHTED){
                SupraAdjacencyMatrix <<- binarizeMatrix(SupraAdjacencyMatrix)
            }

            #the aggregate
            
            #only if the network is interdependent
            if(input$radMultiplexModel=="MULTIPLEX_IS_INTERDEPENDENT"){
                #in this case the aggregate is just the input network itself
                g[[LAYERS+1]] <<- graph.data.frame( data.frame(from=multilayerEdges[,1], to=multilayerEdges[,3], weight=multilayerEdges[,5]) , directed=DIRECTED, vertices=1:Nodes)
                
                #sum parallel edges and remove 
                g[[LAYERS+1]] <<- simplify(g[[LAYERS+1]], edge.attr.comb=list(weight="sum"), remove.loops=F)
                
                if(!WEIGHTED){
                    #the interdependent network coincides with the aggregate, so it can't be weighted by the
                    #sum of overlapping links (like in the case below). We need to set the weight attribute to 1
                    E(g[[LAYERS+1]])$weight <<- 1
                }
            }else{
                g[[LAYERS+1]] <<- graph.adjacency(AdjMatrix[[LAYERS+1]],weighted=T)
            }

            #if the network is non-edge colored (ie, there are interlinks)
            if(input$radMultiplexModel!="MULTIPLEX_IS_EDGECOLORED"){
                #the trick is to build a huge network where the number of nodes is NxL

                #For interdependent networks we will apply the layout to each layer separately
                #For interconnected multiplex and general multilayer we will apply the layout to the aggregate
                #Finally, we will assign nodes and edges properties to their corresponding elements in the huge network
                #and we will use that for plotting
                
                multilayerEdges.tmp <- multilayerEdges
                
                #the idea is to relabel nodes from 1 to NL
                multilayerEdges.tmp[,1] <- multilayerEdges.tmp[,1] + Nodes*(multilayerEdges.tmp[,2]-1)
                multilayerEdges.tmp[,3] <- multilayerEdges.tmp[,3] + Nodes*(multilayerEdges.tmp[,4]-1)
                multilayerEdges.tmp[,4] <- NULL
                multilayerEdges.tmp[,2] <- NULL
                colnames(multilayerEdges.tmp) <- c("from", "to", "weight")
                
                g.multi <<- graph.data.frame(multilayerEdges.tmp, directed=DIRECTED, vertices=1:(Nodes*LAYERS))
                
                #print(V(g.multi))
                #print(E(g.multi))
                
                multilayerEdges.tmp <- NULL
            }

            #build heatmaps for matrix explorer
            
            #multilayer
            #mux.df <- as.data.frame.sp(t(SupraAdjacencyMatrix))
            
            if(Nodes*LAYERS < 100){
                #print(paste0(sort(rep(1:LAYERS, Nodes)), "-", rep(nodesLabel[[1]], LAYERS)))
                
                mux.df <- as.data.frame(t(as.matrix(SupraAdjacencyMatrix)))
                colnames(mux.df) <- paste0(sort(rep(1:LAYERS, Nodes)), "-", rep(nodesLabel[[1]], LAYERS))
                rownames(mux.df) <- paste0(sort(rep(1:LAYERS, Nodes)), "-", rep(nodesLabel[[1]], LAYERS))
    
                output$matrixExplorerHeatmapMultilayerUI <- renderUI({
                    d3heatmapOutput("matrixExplorerMultilayerHeatmap", width = "700", height="700")
                })
                
                output$matrixExplorerMultilayerHeatmap <- renderD3heatmap({                    
                    if(input$chkShowMatrixExplorer){
                        d3heatmap(
                            mux.df,
                            color = input$selMatrixEplorerHeatmapColorPalette,
                            dendrogram = if (input$chkMatrixEplorerHeatmapShowDendrogram){"both"}else{"none"}
                            )
                    }
                })
    
                #aggregate
                #agg.df <- as.data.frame(t(as.matrix(AdjMatrix[[LAYERS+1]])))

                agg.adj <- t(AdjMatrix[[LAYERS+1]])
                dimnames(agg.adj) = list(NULL, nodesLabel[[1]]) 
                agg.df <- as.data.frame(as.matrix(agg.adj))                
                colnames(agg.df) <- nodesLabel[[1]]
                rownames(agg.df) <- nodesLabel[[1]]

                output$matrixExplorerHeatmapAggregateUI <- renderUI({
                    d3heatmapOutput("matrixExplorerAggregateHeatmap", width = "700", height="700")
                })
    
                output$matrixExplorerAggregateHeatmap <- renderD3heatmap({                    
                    if(input$chkShowMatrixExplorer){
                        d3heatmap(
                            agg.df,
                            color = input$selMatrixEplorerHeatmapColorPalette,
                            dendrogram = if (input$chkMatrixEplorerHeatmapShowDendrogram){"both"}else{"none"}
                            )
                    }
                })
            }
            
            btnImportNetworksValue <<- input$btnImportNetworks
        }
    
        observe({
            if(input$btnImportNetworks==0)
                return()
    
            isolate({
                progress <- shiny::Progress$new()
                on.exit(progress$close())
                progress$set(message = 'Importing edges lists...', value = 0.2)
                Sys.sleep(1)
                res <- importNetworksFromConfigurationFile()
                if(is.null(res)){
                    progress$set(message = 'Errors occurred while reading input...', value = 0.2)
                    Sys.sleep(5)
                    return()
                }

                progress$set(detail = 'Building the network...', value = 0.6)
                Sys.sleep(1)
                buildNetworks()

                #Fill the table summarizing the config file
                output$edgelistTable <- renderGvis({
                    if(input$btnImportNetworks==0 || LAYERS==0 || input$chkOutputEdgelistTable==FALSE)
                        return(NULL)
        
                    progress$set(detail = 'Creating tables...', value = 0.9)
                    #Sys.sleep(2)

                    listEdgelistMerge <- NULL
                    if(input$radMultiplexModel=="MULTIPLEX_IS_EDGECOLORED"){
                        for(l in 1:LAYERS){
                            thisEdge <- cbind(get.edgelist(g[[l]]), E(g[[l]])$weight)
                            for(n in 1:nrow(thisEdge)){
                                listEdgelistMerge <- rbind(listEdgelistMerge,data.frame(cbind(Layer = l, nodeID1 = thisEdge[n,1],nodeID2 = thisEdge[n,2], Node1 = nodesLabel[[l]][thisEdge[n,1]], Node2 = nodesLabel[[l]][thisEdge[n,2]], Weight = thisEdge[n,3])))
                            }
                        }
                        #print(listEdgelistMerge)
                    }else{
                        listEdgelistMerge <- multilayerEdges
                        colnames(listEdgelistMerge) <- c("Node1", "Layer1", "Node2", "Layer2", "Weight")
                    }

                    gvisTable(listEdgelistMerge,options=googleVisEdgelistTableOptions())
                })   
        
                btnImportNetworksValue <<- input$btnImportNetworks
                
                progress$set(detail = 'Import Completed!', value = 1)
                Sys.sleep(2)
            })
        })

        observeEvent(input$btnResetLights, {
            if(input$btnRenderNetworks==0 || input$btnApplyLayout==0 || input$btnImportNetworks == 0 || LAYERS<=0) return()

            progress <- shiny::Progress$new(session)
            on.exit(progress$close())
            progress$set(message = 'Resetting lights...', value = 0.2)
            Sys.sleep(1)

            flag <- F
            while(!flag){
                tryCatch({
                    print("Popping lights...")
                    rgl.pop("lights")},
                    error=function(e){
                        print("Warning: no more lights to pop")
                        },
                    finally={flag=T}
                    )
            }
            #rgl.light(theta = 0, phi = 0, viewpoint.rel = TRUE, ambient = "#FFFFFF", 
            #            diffuse = "#FFFFFF", specular = "#FFFFFF")

            progress$set(detail = 'Done!', value = 1)
            Sys.sleep(2)
        })
        
        observeEvent(input$btnImportTimeline, {
            if(input$btnRenderNetworks==0 || input$btnApplyLayout==0 || input$btnImportNetworks == 0 || LAYERS<=0) return()
            
            if(is.null(input$timeline_file$datapath)) return()
    
            isolate({
                progress <- shiny::Progress$new(session)
                on.exit(progress$close())
                progress$set(message = 'Importing timeline...', value = 0.2)
                Sys.sleep(1)
                res <- importTimelineFromFile()
                if(is.null(res)){
                    progress$set(message = 'Errors occurred while reading input...', value = 0.2)
                    Sys.sleep(5)
                    return()
                }
                
                progress$set(detail = 'Import Completed!', value = 1)
                Sys.sleep(2)
            })
        })
        
        importTimelineFromFile <- function(){
            if(length(input$timeline_file)>0){
                if(!file.exists(input$timeline_file$datapath)){
                    progress <- shiny::Progress$new(session)
                    on.exit(progress$close())
                    progress$set(message = paste('ERROR! File',input$timeline_file$datapath,'does not exist.'), value = 0.01)
                    Sys.sleep(10)
                    return(NULL)
                }
            }

            fileTimeline <<- input$timeline_file$datapath
            dfTimeline <<-  read.table(fileTimeline, header=TRUE, sep=as.character(input$txtTimelineFileSep))
            
            if(!(all(as.integer(as.character(dfTimeline$layerID)) >= 1) & all(as.integer(as.character(dfTimeline$layerID)) <= (LAYERS+1)))){
                progress <- shiny::Progress$new(session)
                on.exit(progress$close())
                progress$set(message = paste('ERROR! There are unknown layers in the timeline file'), value = 0.01)
                Sys.sleep(10)
                return(NULL)
            }

            if(!(all(as.integer(as.character(dfTimeline[dfTimeline$entity=="node",]$nodeID)) >= minNodeID) & all(as.integer(as.character(dfTimeline[dfTimeline$entity=="node",]$nodeID)) <= maxNodeID))){
                progress <- shiny::Progress$new(session)
                on.exit(progress$close())
                progress$set(message = paste('ERROR! There are unknown nodes in the timeline file'), value = 0.01)
                Sys.sleep(10)
                return(NULL)
            }
            
            print(paste("Timeline imported! Found",nrow(dfTimeline),"entries"))
        }
        
        #Export the visualization for each snapshot of the timeline
        observeEvent(input$btnRenderDynamicsSnapshots, {            
            if(input$btnImportTimeline==0 || input$btnRenderNetworks==0 || length(dfTimeline)==0) return()

            isolate({
                progress <- shiny::Progress$new(session)
                on.exit(progress$close())
    
                progress$set(detail = 'Building the dynamics...', value = 0.1)
                Sys.sleep(1)
                
                #order the timeline by timestep
                #dfTimeline[order(dfTimeline$timeStep),]
                                
                #get the list of all (unique) timesteps
                timestepsList <- sort(unique(dfTimeline$timeStep))

                #re-set the default color and size
                if(length(input$colTimelineDefaultNodesColor)>0){
                    for(l in 1:(LAYERS+1)) V(g[[l]])$color <- input$colTimelineDefaultNodesColor
                }else{
                    for(l in 1:(LAYERS+1)) V(g[[l]])$color <- defaultVcolor[[l]]
                }
                
                if(length(input$txtTimelineDefaultNodesSize)>0){
                    for(l in 1:(LAYERS+1)) V(g[[l]])$size <- as.numeric(input$txtTimelineDefaultNodesSize)
                }else{
                    for(l in 1:(LAYERS+1)) V(g[[l]])$size <- defaultVsize[[l]]
                }

                if(!input$chkNODE_LABELS_SHOW){
                    for(l in 1:(LAYERS+1)) V(g[[l]])$label <- ""
                }else{
                    for(l in 1:(LAYERS+1)) V(g[[l]])$label <- nodesLabel[[l]]
                }

                if(input$chkNODE_ISOLATED_HIDE){
                    #this piece of code must be executed after the above one, to change the size of isolated
                    #nodes to zero, and also their label to ""
                    
                    if(input$radMultiplexModel == "MULTIPLEX_IS_EDGECOLORED"){
                        for(l in 1:(LAYERS+1)){
                            arrayStrength <- graph.strength(g[[l]],mode="total")
                            V(g[[l]])[arrayStrength==0.]$size <- 0
                            V(g[[l]])[arrayStrength==0.]$label <- ""
                        }
                    }else{
                        if(input$chkNODE_ISOLATED_HIDE_INTERLINKS){
                            #account for degree in the multiplex
                            
                            arrayStrength <- graph.strength(g.multi,mode="total")
                            idxtohide <- which(arrayStrength==0.)
                            inlayers <- floor((idxtohide-1)/Nodes) + 1
                            innodes <- (idxtohide-1) %% Nodes + 1
                            
                            for(l in 1:LAYERS){
                                idxs <- which(inlayers==l)
                                nodes2hide <- which(V(g[[l]]) %in% innodes[idxs])
                                V(g[[l]])[nodes2hide]$size <- 0
                                V(g[[l]])[nodes2hide]$label <- ""
                            }
                            
                            #aggregate must be done separately
                            arrayStrength <- graph.strength(g[[LAYERS+1]],mode="total")
                            V(g[[LAYERS+1]])[arrayStrength==0.]$size <- 0
                            V(g[[LAYERS+1]])[arrayStrength==0.]$label <- ""
                        }else{
                            #do not account for interlinks, just intralinks
                            for(l in 1:(LAYERS+1)){
                                arrayStrength <- graph.strength(g[[l]],mode="total")
                                V(g[[l]])[arrayStrength==0.]$size <- 0
                                V(g[[l]])[arrayStrength==0.]$label <- ""
                            }
                        }
                    }
                }

                if(length(input$colTimelineDefaultEdgesColor)>0){
                    for(l in 1:(LAYERS+1)) E(g[[l]])$color <- input$colTimelineDefaultEdgesColor
                }else{
                    for(l in 1:(LAYERS+1)) E(g[[l]])$color <- defaultEcolor[[l]]
                }

                if(length(input$txtTimelineDefaultEdgesSize)>0){
                    for(l in 1:(LAYERS+1)) E(g[[l]])$size <- as.numeric(input$txtTimelineDefaultEdgesSize)
                }else{
                    for(l in 1:(LAYERS+1)) E(g[[l]])$size <- defaultEsize[[l]]
                }


                print("Rendering dynamics")

                for(timestep in min(timestepsList):max(timestepsList)){                    
                    #extract all the rows corresponding to this timestep and create a new dataframe
                    tmpdfTimeline <- dfTimeline[dfTimeline$timeStep==timestep,]
                                        
                    print(paste("  Timeline (",100*which(timestepsList==timestep)/(max(timestepsList)-min(timestepsList)+1),"%",") -> timestep:",timestep))

                    xmin.tmp <- 1e100
                    xmax.tmp <- -1e100
                    ymin.tmp <- 1e100
                    ymax.tmp <- -1e100

                    if(input$chkPLOT_WITH_RGL){
                        rgl.clear()
                        tryCatch(rgl.pop("lights"),error=function(e) print("Warning: no lights to pop"))
                        rgl.light(theta = 0, phi = 0, viewpoint.rel = TRUE, ambient = "#FFFFFF", 
                       diffuse = "#FFFFFF", specular = "#FFFFFF")
                    }else{                        
                        for(l in 1:LAYERS){
                            xmin.tmp <- min(xmin.tmp, min(layouts[[l]][,1]))
                            xmax.tmp <- max(xmax.tmp, max(layouts[[l]][,1]))
                            ymin.tmp <- min(ymin.tmp, min(layouts[[l]][,2]))
                            ymax.tmp <- max(ymax.tmp, max(layouts[[l]][,2]))
                        }
                        
                        if(input$chkAGGREGATE_SHOW){
                            l <- LAYERS+1
                            xmin.tmp <- min(xmin.tmp, min(layouts[[l]][,1]))
                            xmax.tmp <- max(xmax.tmp, max(layouts[[l]][,1]))
                            ymin.tmp <- min(ymin.tmp, min(layouts[[l]][,2]))
                            ymax.tmp <- max(ymax.tmp, max(layouts[[l]][,2]))
                        }
                        
                        xmin.tmp <- min(xmin.tmp*0.95, xmin.tmp*1.05)
                        xmax.tmp <- max(xmax.tmp*0.95, xmax.tmp*1.05)
                        ymin.tmp <- min(ymin.tmp*0.95, ymin.tmp*1.05)
                        ymax.tmp <- max(ymax.tmp*0.95, ymax.tmp*1.05)                        
                    }

                    #for each time step, we have to modify the state of the network 
                    #nodes
                    tmpdfTimelineNode <- tmpdfTimeline[tmpdfTimeline$entity=="node",]
                    #print(tmpdfTimelineNode)
                    if(nrow(tmpdfTimelineNode)>0){
                        for(r in 1:nrow(tmpdfTimelineNode)){
                            l <- as.integer(as.character(tmpdfTimelineNode[r,]$layerID))
                            n <- which(V(g[[l]])==as.integer(as.character(tmpdfTimelineNode[r,]$nodeID)))

                            #we have to rescale node's default size, not the previous one..
                            defNodeSize <- 1
                            if(length(input$txtTimelineDefaultNodesSize)>0){
                                defNodeSize <- as.numeric(input$txtTimelineDefaultNodesSize)
                            }else{
                                defNodeSize <- defaultVsize[[l]][n]
                            }

                            V(g[[l]])$size[n] <- defNodeSize * as.double(as.character(tmpdfTimelineNode[r,]$sizeFactor))
                            V(g[[l]])$color[n] <- paste("#",tmpdfTimelineNode[r,]$color,sep='')
                        }
                    }else{
                        #no changes in the state of the nodes
                    }
                             
                    #edges
                    tmpdfTimelineEdge <- tmpdfTimeline[tmpdfTimeline$entity=="edge",]
                    #print(tmpdfTimelineNode)
                    if(nrow(tmpdfTimelineEdge)>0){
                        for(r in 1:nrow(tmpdfTimelineEdge)){
                            l <- as.integer(as.character(tmpdfTimelineEdge[r,]$layerID))
                            pair <- as.character(tmpdfTimelineEdge[r,]$nodeID)
                            n1 <- which(V(g[[l]])==as.integer( unlist(strsplit(pair,"-"))[1] ))
                            n2 <- which(V(g[[l]])==as.integer( unlist(strsplit(pair,"-"))[2] ))

                            #we have to rescale node's default size, not the previous one..
                            defEdgeSize <- 1
                            if(length(input$txtTimelineDefaultEdgesSize)>0){
                                defEdgeSize <- as.numeric(input$txtTimelineDefaultEdgesSize)
                            }else{
                                defEdgeSize <- AdjMatrix[[l]][n1,n2]
                            }

                            E(g[[l]])[n1 %--% n2]$size <- defEdgeSize * as.double(as.character(tmpdfTimelineEdge[r,]$sizeFactor))
                            E(g[[l]])[n1 %--% n2]$color <- paste("#",tmpdfTimelineEdge[r,]$color,sep='')
                        }
                    }else{
                        #no changes in the state of the edges
                    }

                    vecInactiveLayers <- as.numeric(strsplit(input$txtLAYERS_ACTIVE, ",")[[1]])

                    timelineFolder <- concatenatePath( concatenatePath("export","timeline"), input$txtProjectName)

                    #create the folder if it does not exist
                    dir.create(buildPath("export","timeline"), showWarnings = FALSE)
                    dir.create(timelineFolder, showWarnings = FALSE)

                    FILE_RGL_SNAPSHOT <- buildPath(timelineFolder,paste0(input$txtProjectName,"_",sprintf("%05d",timestep),".png"))
                    
                    if(!input$chkPLOT_WITH_RGL){
                        width <- as.numeric(input$txtExportRenderingClassicPNGWidth)
                        height <- as.numeric(input$txtExportRenderingClassicPNGHeight)
                        dpi <- as.numeric(input$txtExportRenderingClassicPNGResolution)
        
                        if(input$chkTIMELINE_RENDER_TO_FILE){
                            png(filename=FILE_RGL_SNAPSHOT, width=width, height=height, res=dpi)
                        }

                        par(mar=c(0, 0, 0, 0), xaxs='i', yaxs='i') 
                        par(oma=c(0, 0, 0, 0))

                        plot(x=NULL, y=NULL, type="n", 
                            xlim=c(xmin.tmp,xmax.tmp), ylim=c(ymin.tmp,ymax.tmp)
                            )
                        rect(par("usr")[1], par("usr")[3], par("usr")[2], par("usr")[4], col =input$colBACKGROUND_COLOR)

                    }

                    #now render the network
                    for(l in 1:(LAYERS+1)){
                        if( l %in% vecInactiveLayers ){
                            #skip layers set to be inactive
                            next
                        }

                        #progress$set(message = paste('Layer',l,'...'), value = 0.05 + 0.85*l/(LAYERS+1))
        
                        if(l==(LAYERS+1)){
                            if((!input$chkAGGREGATE_SHOW || LAYERS==1) || (input$chkPLOT_AS_EDGE_COLORED && LAYOUT_DIMENSION==3)){
                                #if we don't want to show the aggregate, we must skip the rest
                                #we must skip also if the layers is just 1
                                next
                            }
                        }
                        if(l<LAYERS+1){
                            print(paste("    Timeline Layer: ",l))
                        }else{
                            print(paste("    Timeline Layer: Aggregate"))    
                        }

                        #other assignments                        
                        V(g[[l]])$vertex.label.color <- input$colNODE_LABELS_FONT_COLOR
                        E(g[[l]])$curve<- as.numeric(input$txtEDGE_BENDING)
                        V(g[[l]])$shape <- "circle"
                        V(g[[l]])$shape[V(g[[l]])$size==0] <- "none"                    
                        V(g[[l]])$framecolor <- input$txtNODE_FRAME_COLOR
                        if(input$txtNODE_FRAME_COLOR==""){ V(g[[l]])$framecolor <- V(g[[l]])$color }
                        
                        if(input$chkNODE_LABELS_SHOW_WRAP){
                            V(g[[l]])$label2 <- lapply(lapply(V(g[[l]])$label, function(x) strwrap(x,as.numeric(input$txtNODE_LABELS_WRAP))), function(x) paste(x, collapse='\n'))
                        }else{
                            V(g[[l]])$label2 <- V(g[[l]])$label
                        }
        
                        if(input$chkPLOT_WITH_RGL){
                            print("      openGL phase...")
                            #plot the graph with openGL    
                            #print(layouts[[l]])
                            #V(g[[l]])$label <- ""
    
                            rglplot(g[[l]], layout=layouts[[l]],
                                                vertex.size=V(g[[l]])$size, 
                                                vertex.color=V(g[[l]])$color,
                                                vertex.label="",#V(g[[l]])$label,
                                                vertex.label.dist=as.numeric(input$txtNODE_LABELS_DISTANCE), #,+ 0.01*V(g[[l]])$size,
                                                vertex.label.font=2,
                                                vertex.label.cex=0, 
                                                vertex.label.color=V(g[[l]])$vertex.label.color,
                                                edge.width=E(g[[l]])$size, 
                                                edge.color=E(g[[l]])$color, 
                                                edge.arrow.size=as.numeric(input$txtLAYER_ARROW_SIZE), 
                                                edge.arrow.width=as.numeric(input$txtLAYER_ARROW_WIDTH), 
                                                edge.curved=E(g[[l]])$curve,
                                                rescale=F)
                        }else{
                            print("  Standard device output...")
        
                            #plot the graph with openGL    
                            #print(layouts[[l]])
                            plot.igraph(g[[l]], layout=layouts[[l]],
                                            vertex.size=V(g[[l]])$size, 
                                            vertex.shape=V(g[[l]])$shape,
                                            vertex.color=V(g[[l]])$color,
                                            vertex.frame.color=V(g[[l]])$framecolor,
                                            vertex.label=V(g[[l]])$label2,
                                            vertex.label.dist=as.numeric(input$txtNODE_LABELS_DISTANCE), #,+ 0.01*V(g[[l]])$size,
                                            vertex.label.font=2,
                                            vertex.label.cex=as.numeric(input$txtNODE_LABELS_FONT_SIZE), 
                                            vertex.label.color=V(g[[l]])$vertex.label.color,
                                            edge.width=E(g[[l]])$size, 
                                            edge.color=E(g[[l]])$color, 
                                            edge.arrow.size=as.numeric(input$txtLAYER_ARROW_SIZE), 
                                            edge.arrow.width=as.numeric(input$txtLAYER_ARROW_WIDTH), 
                                            edge.curved=E(g[[l]])$curve,
                                            rescale=F, add=T)
                                            
                                title(main=input$txtPLOT_TITLE, sub=input$txtPLOT_SUBTITLE)
                        }
                                                                                        
                        print(paste("    Layout of layer: finished."))
                    }

                    if(input$chkINTERLINK_SHOW && LAYERS>1){
                        if(input$radMultiplexModel!="MULTIPLEX_IS_EDGECOLORED"){
                            print("Adding interlayer links.")
    
                            #set to 0 the width of intra-layer links
                            E(g.multi)$width <- as.numeric(input$txtINTERLINK_WIDTH)*E(g.multi)$weight
                            E(g.multi)[which(multilayerEdges[,2]==multilayerEdges[,4])]$width <- 0
                            
                            #the same for interlinks from and to inactive layers 
                            for(l in vecInactiveLayers){
                                E(g.multi)[which(multilayerEdges[,2]==l | multilayerEdges[,4]==l)]$width <- 0
                            }
                            
                            #setup the layout for g.multi by merging the layout of each layer, in order
                            layout.multi <<- matrix(0, ncol=3, nrow=Nodes*LAYERS)

                            for(l in 1:LAYERS){
                                layout.multi[ (1 + (l-1)*Nodes):(l*Nodes), 1] <<- layouts[[l]][, 1]
                                layout.multi[ (1 + (l-1)*Nodes):(l*Nodes), 2] <<- layouts[[l]][, 2]
                                layout.multi[ (1 + (l-1)*Nodes):(l*Nodes), 3] <<- layouts[[l]][, 3]
                            }
        
                            if(input$chkPLOT_WITH_RGL){
                                #Print the interlinks by superimposing the g.multi
                                rglplot(g.multi, layout=layout.multi,
                                                    vertex.size=0, 
                                                    vertex.label="",
                                                    vertex.label.cex=0,
                                                    edge.width=E(g.multi)$width, 
                                                    edge.color=input$colINTERLINK_COLOR, 
                                                    edge.arrow.size=as.numeric(input$txtLAYER_ARROW_SIZE), 
                                                    edge.arrow.width=as.numeric(input$txtLAYER_ARROW_WIDTH), 
                                                    edge.curved=as.numeric(input$txtEDGE_BENDING),
                                                    edge.lty = input$selINTERLINK_TYPE,
                                                    rescale=F)
                                #edge/node transparancy not yet supported by rglplot
                                #alpha=as.numeric(input$txtINTERLINK_TRANSP))
                            }else{
                                plot.igraph(g.multi, layout=layout.multi,
                                            vertex.size=0, 
                                            vertex.shape="none",
                                            vertex.label="",
                                            vertex.label.cex=0,
                                            edge.width=E(g.multi)$width, 
                                            edge.color=addalpha(input$colINTERLINK_COLOR,as.numeric(input$txtINTERLINK_TRANSP)), 
                                            edge.arrow.size=as.numeric(input$txtLAYER_ARROW_SIZE), 
                                            edge.arrow.width=as.numeric(input$txtLAYER_ARROW_WIDTH), 
                                            edge.curved=as.numeric(input$txtEDGE_BENDING),
                                            edge.lty = input$selINTERLINK_TYPE,
                                            rescale=F, add=T)
                            }
                        }                
                    }                

                    if(input$chkPLOT_WITH_RGL){
                        #Call the visualization of other graphics
                        FinalizeRenderingMultiplex(progress)
                    
                        if(!is.na(tmpdfTimeline$labelStep[1])){
                            #assuming that all labels for this timestep are identical, as it should be..
                            title3d(input$txtPLOT_TITLE, tmpdfTimeline$labelStep[1],'','','')
        
                            print(paste("    Exporting snapshot",tmpdfTimeline$labelStep[1],"..."))
                        }
                    
                        rgl.snapshot(FILE_RGL_SNAPSHOT) 
                    }else{
                        if(input$chkTIMELINE_RENDER_TO_FILE){
                            dev.off()
                        }
                    }
                    #Sys.sleep(1)
                }
                
                progress$set(message = 'Rendering Completed!', value = 1)
                Sys.sleep(2)
            })
        })


      	################################################
      	# Motifs
      	################################################
        observeEvent(input$btnCalculateMotifs, {
            if(input$btnImportNetworks == 0 ||  LAYERS<=1)
                return()
    
            isolate({
                progress <- shiny::Progress$new(session)
                on.exit(progress$close())
                
                #to output full numbers
                options(scipen=999)
                                
                progress$set(message = 'Setting up the algorithms...', value = 0.2)                
                inputFile <- paste0(input$txtProjectName,"_fanmod.edges")
                
                #fanmod format assume 0-starting labeling for nodes, 1-starting for layers
                mergedEdgelist <- data.frame()
                layerLabels <- NULL
                for(l in 1:LAYERS){
                    mergedEdgelist <- rbind(mergedEdgelist, data.frame(from=layerEdges[[l]][,1]-1, to=layerEdges[[l]][,2]-1, layer=l))
                    layerLabels <- c(layerLabels, layerLabel[[l]])
                }
                write.table(file=inputFile, mergedEdgelist, row.names=F, col.names=F, quote=F)     
                resultFile <- paste0(input$txtProjectName,"_fanmod.csv")

                progress$set(message = 'Calculating motifs...', value = 0.5)

                nullModelID <- 2
                if(input$selMotifNullModel=="Local const"){
                    nullModelID <- 2
                }else if(input$selMotifNullModel=="Global const"){
                    nullModelID <- 1
                }else if(input$selMotifNullModel=="No regard"){
                    nullModelID <- 0
                }
                
                exePath <- getExecutablePath("fanmod")
                exeFlags <- paste(as.numeric(input$selMotifSize),
                                              as.numeric(input$txtMotifSamples),
                                              1,
                                              inputFile,
                                              as.numeric(DIRECTED),
                                              0,
                                              1,
                                              nullModelID,
                                              0,
                                              1,
                                              0,
                                              as.numeric(input$txtMotifRandomNetworks),
                                              as.numeric(input$txtMotifRandomExchangePerEdges),
                                              as.numeric(input$txtMotifRandomExchangeAttempts),
                                              resultFile,
                                              0,
                                              0
                                    )
                
                #call fanmod
                #print( paste(exePath,exeFlags) )
                system(paste(exePath,exeFlags),intern=T)
                Sys.sleep(3)
                
                #read output. Here I could redirect the output inside the R environment.. but
                #for compatibility with the rest of the code I prefer to read a file
                #ID,Adj-Matrix,Frequency,Mean-Freq,Standard-Dev,Z-Score,p-Value
                motifsTable <- read.table(resultFile, header=T, sep=",", colClasses=c("character","character",rep("numeric",5)))
                
                progress$set(message = 'Rendering the results...', value = 0.8)
                
                #sorting
                print("Sorting results from motifs analysis...")
                if(input$selMotifResultsSortBy=="Frequency"){
                    motifsTable <- motifsTable[order(-motifsTable[,3]),]
                }else if(input$selMotifResultsSortBy=="Z-score"){
                    motifsTable <- motifsTable[order(-motifsTable[,6]),]
                }else if(input$selMotifResultsSortBy=="p-value"){
                    motifsTable <- motifsTable[order(motifsTable[,7]),]
                }
                
                #cutting
                print("Applying cuts to motifs table...")
                if(input$chkMotifAbsZscore){
                    motifsTable <- motifsTable[ abs(motifsTable[,6])> as.numeric(input$txtMotifAbsZscore), ]
                }
                if(input$chkMotifPvalue){
                    motifsTable <- motifsTable[ abs(motifsTable[,7])< as.numeric(input$txtMotifPvalue), ]
                }
                if(input$chkMotifFrequency){
                    motifsTable <- motifsTable[ abs(motifsTable[,3])> as.numeric(input$txtMotifFrequency), ]
                }

                motifsTable$Mean.Freq <- NULL
                motifsTable$Standard.Dev <- NULL
                
                #creating figures
                rgb.palette <- colorRampPalette(brewer.pal(brewer.pal.info$maxcolors[row.names(brewer.pal.info)==input$selMotifColorPalette],input$selMotifColorPalette))(LAYERS)
                
                unloadNamespace("shinyjs")  #todo: this must be removed when new igraph will solve issue with shinyjs
                
                motifsTable$Motif <- rep("",nrow(motifsTable))
                for(r in 1:nrow(motifsTable)){
                    motif_name <- motifsTable[r,]$Adj.Matrix
                    outpng <- gsub("\"", "", concatenatePath(concatenatePath(buildPath("www","img"),"motifs"), paste0(motif_name,".png")))

                    #print(motif_name)
                    #print(t(matrix(as.numeric(strsplit(motif_name,"")[[1]]),ncol=as.numeric(input$selMotifSize))))
                    g.motif <- graph.adjacency( t(matrix(as.numeric(strsplit(motif_name,"")[[1]]),ncol=as.numeric(input$selMotifSize))) )
                    E(g.motif)$color <- 1
                    g.motif <- simplify(g.motif,edge.attr.comb=list(color="sum"))
                    g.layout <- layout.circle(g.motif)
                    g.layout[,1] <- 0.95*(g.layout[,1] - min(g.layout[,1]))/(max(g.layout[,1])-min(g.layout[,1])) - 0.95/2
                    g.layout[,2] <- 0.95*(g.layout[,2] - min(g.layout[,2]))/(max(g.layout[,2])-min(g.layout[,2])) - 0.95/2
                        
                    png(outpng,width=128,height=128)
                    par(mar=c(0, 0, 0, 0), xaxs='i', yaxs='i') 
                    par(oma=c(0, 0, 0, 0))
                    plot(x=NULL, y=NULL, type="n",
                        xlim=c(-1,1), ylim=c(-1,1)
                        )
                    
                    plot.igraph(g.motif, layout=g.layout,
                        vertex.label="",
                        vertex.color="#A0A0A0",
                        vertex.frame.color=NA,
                        edge.color=rgb.palette[E(g.motif)$color], 
                        edge.arrow.size=1, 
                        edge.arrow.width=1.5,
                        edge.width=3,
                        rescale=F
                        )
                    dev.off()

                    motifsTable[r,]$Motif <- paste0("<img src=\"img/motifs/",motif_name,".png\" width='128' height='128'>")
                }

                loadNamespace("shinyjs")  #todo: this must be removed when new igraph will solve issue with shinyjs
                listMotifs <<- motifsTable
                
                #to reset output options
                options(scipen=0)
                progress$set(message = 'Calculation Completed!', value = 1)
                Sys.sleep(2)

                if(file.exists(paste0(input$txtProjectName,"_fanmod.edges"))) file.remove(paste0(input$txtProjectName,"_fanmod.edges"))
                if(file.exists(paste0(input$txtProjectName,"_fanmod.csv"))) file.remove(paste0(input$txtProjectName,"_fanmod.csv"))
                if(file.exists(paste0(input$txtProjectName,"_fanmod.csv.log"))) file.remove(paste0(input$txtProjectName,"_fanmod.csv.log"))
            })
        }, ignoreInit = TRUE)

                
        output$motifsColorLegend <- renderPlot({
            if(input$btnCalculateMotifs==0 || input$btnImportNetworks == 0 ||  LAYERS<=1)
                return()
            
            rgb.palette <- colorRampPalette(brewer.pal(brewer.pal.info$maxcolors[row.names(brewer.pal.info)==input$selMotifColorPalette],input$selMotifColorPalette))(LAYERS)

            layerLabels <- c()
            for(l in 1:LAYERS){ layerLabels <- c(layerLabels, layerLabel[[l]]) }

            mydf <- data.frame(layer=1:LAYERS, fake=1, color=rgb.palette)
            p <- ggplot(mydf, aes(x=layer, y=fake, fill=layer)) + geom_tile() + 
                    scale_fill_gradientn(colours = rgb.palette) +  
                    ylab("") + 
                    xlab("") + 
                    scale_x_discrete(breaks=c(1:LAYERS), limits=c(1:LAYERS), labels=layerLabels) + 
                    scale_y_discrete(breaks=NULL, limits=c(0,1)) + 
                    guides(fill=FALSE) + 
                    theme_bw() + 
                    theme( plot.background = element_blank(),
                        panel.grid.major = element_blank() ,
                        panel.grid.minor = element_blank() ,
                        panel.border = element_blank() ,
                        panel.background = element_blank(),
                        axis.line = element_blank(),
                        text = element_text(size=25),
                        axis.text.x = element_text(angle=90, vjust=1)
                        )
            print(p)
        }, height = 250)
        
        output$motifsGvisTable <- renderGvis({
            if(input$btnCalculateMotifs==0 || input$btnImportNetworks == 0 ||  LAYERS<=1)
                return()
            
            gvisTable(listMotifs,options=googleVisMotifsSummaryTableOptions())
        })   


      	################################################
      	# Query
      	################################################

        observeEvent(input$btnQuery, {
            if(input$btnImportNetworks == 0 ||  LAYERS==0)
                return()
    
            isolate({
                progress <- shiny::Progress$new(session)
                on.exit(progress$close())
                progress$set(message = 'Running query...', value = 0.1)
                
                query.tab <- data.frame()

                if(input$selQueryType=="Nodes"){
                    for(layer.str in input$selQueryNodesLayerID){
                        l <- as.numeric(strsplit(layer.str, " ")[[1]][1])
                        for(node.str in input$selQueryNodesNodeID){
                            n <- as.numeric(strsplit(node.str, " ")[[1]][1])
                            neighs <- as.numeric(neighbors(g[[l]], mode="all", v=n))
                            if(length(neighs)>0){
                                if(input$chkQueryShowLabels){
                                    query.tab <- rbind(query.tab, data.frame(Layer=l, 
                                                                                                    Node=nodesLabel[[l]][n], 
                                                                                                    Neighbor=nodesLabel[[l]][neighs]
                                                                                                    ))
                                }else{
                                    query.tab <- rbind(query.tab, data.frame(Layer=l, 
                                                                                                    Node=n, 
                                                                                                    Neighbor=neighs
                                                                                                    ))                                
                                }
                            }
                        }
                    }                    
                }
                if(input$selQueryType=="Edges"){
                    for(layer.str in input$selQueryEdgesLayerID){
                        l <- as.numeric(strsplit(layer.str, " ")[[1]][1])
                        
                        nodes.from <- as.numeric(unlist(lapply(strsplit(input$selQueryEdgesNodesFromID, " "), 
                                                                                       function(x) return(x[[1]][1]))))
                        nodes.to <- as.numeric(unlist(lapply(strsplit(input$selQueryEdgesNodesToID, " "), 
                                                                                  function(x) return(x[[1]][1]))))

                        g.sub <- induced_subgraph( g[[l]], as.numeric(union(nodes.from, nodes.to)) )
                        g.edges <- get.edges(g.sub, E(g.sub))

                        if(nrow(g.edges)>0){
                            if(input$chkQueryShowLabels){
                                query.tab <- rbind(query.tab, data.frame(Layer=l, 
                                                                                                NodeFrom=nodesLabel[[l]][g.edges[,1]], 
                                                                                                NodeTo=nodesLabel[[l]][g.edges[,2]]
                                                                                                ))
                            }else{
                                query.tab <- rbind(query.tab, data.frame(Layer=l, 
                                                                                                NodeFrom=g.edges[,1], 
                                                                                                NodeTo=g.edges[,2]
                                                                                                ))                                
                            }
                        }
                    }                    

                }
                
                
                output$queryNodesTable <- renderGvis({
                    gvisTable(query.tab, options= list(page='enable', pageSize=50, width=550))
                })                                       

                progress$set(message = 'Completed!', value = 1)
                Sys.sleep(1)
                
                listQueryResult <<- query.tab
            })
        }, ignoreInit = TRUE)

      	################################################
      	# Calculate diagnostics
      	################################################

        observeEvent(input$btnCalculateCorrelationDiagnostics, {
            if(input$btnImportNetworks == 0 ||  LAYERS==0)
                return()
    
            isolate({
                progress <- shiny::Progress$new(session)
                on.exit(progress$close())
    
                #cell LAYERS+1 contains the data for the multiplex
    
                if(input$chkEXPORT_MATRIX_PLOT){
                    progress$set(message = 'Exporting matrices as images...', value = 0.05)
                    Sys.sleep(2)
                    for(l in 1:(LAYERS+1)){
                        if(l<=LAYERS){
                            outfilem <- buildPath("export",paste0(input$txtProjectName,"_layer",l,".png"))
                            
                            progress$set(message = paste('Exporting image for layer ',l,'...',sep=""), value = 0.05 + 0.9*l/(LAYERS+1))  
                        }else{
                            outfilem <- buildPath("export",paste0(input$txtProjectName,"_layer","aggr",".png"))
                        progress$set(message = paste('Exporting image for aggregated...',sep=""), value = 0.05 + 0.9*l/(LAYERS+1))  
                        }
    
                        #Build the plot 
                        #rgb.palette <- colorRampPalette(c("white", "blue", "red"), space = "rgb") 
                        rgb.palette <- colorRampPalette(brewer.pal(brewer.pal.info$maxcolors[row.names(brewer.pal.info)==input$selAssortativityTypeColorPalette],input$selAssortativityTypeColorPalette))
                        
                        png(outfilem, width=550, height=400)
                        myImagePlot(as.matrix(AdjMatrix[[l]]), xLabels=rep("",Nodes), yLabels=rep("",Nodes), ColorRamp=rgb.palette(120)) #,title=c("")
                        #levelplot(AdjMatrix[[l]], main="", xlab="", ylab="", col.regions=rgb.palette(120), cuts=100, at=seq(0,1,0.01))
                        dev.off()
                    }
                    
                    progress$set(message = 'Exporting Completed!', value = 1)
                    Sys.sleep(1)
                }
                
                ###############
                ## Global
                ###############            
                
                if(input$chkMULTIPLEX_OVERLAPPING && LAYERS>1){
                    progress$set(message = 'Calculating edge overlapping...', value = 0.05)
                                        
                    avgGlobalOverlapping <<- GetAverageGlobalOverlapping(SupraAdjacencyMatrix, LAYERS, Nodes)
                    avgGlobalOverlapping <<- paste(round(as.numeric(avgGlobalOverlapping)*100,3)," %")
                                                            
                    Layer <- NULL
                    for(l in 1:LAYERS) Layer = c(Layer,as.character(layerLabel[[l]]))
        
                    avgGlobalOverlappingMatrix <<- GetAverageGlobalOverlappingMatrix(SupraAdjacencyMatrix,LAYERS,Nodes)

                    avgGlobalOverlappingMatrix.df <<- as.data.frame(t(as.matrix(avgGlobalOverlappingMatrix)))
                    colnames(avgGlobalOverlappingMatrix.df) <<- Layer
                    rownames(avgGlobalOverlappingMatrix.df) <<- Layer

                    overlapMatrix <- data.frame(as.matrix(avgGlobalOverlappingMatrix))
                    colnames(overlapMatrix) <- Layer
                    overlapMatrix <- cbind(data.frame(Layer),overlapMatrix)
                    listOverlap <<- overlapMatrix    
                }

                if(input$chkMULTIPLEX_NODEOVERLAPPING && LAYERS>1){
                    progress$set(message = 'Calculating node overlapping...', value = 0.05)
                                        
                    Layer <- NULL
                    for(l in 1:LAYERS) Layer = c(Layer,as.character(layerLabel[[l]]))

                    avgGlobalNodeOverlappingMatrix <<- GetAverageGlobalNodeOverlappingMatrix(SupraAdjacencyMatrix,LAYERS,Nodes)
    

                    avgGlobalNodeOverlappingMatrix.df <<- as.data.frame(t(as.matrix(avgGlobalNodeOverlappingMatrix)))
                    colnames(avgGlobalNodeOverlappingMatrix.df) <<- Layer
                    rownames(avgGlobalNodeOverlappingMatrix.df) <<- Layer
    
                    overlapMatrix2 <- data.frame(as.matrix(avgGlobalNodeOverlappingMatrix))
                    colnames(overlapMatrix2) <- Layer
                    overlapMatrix2 <- cbind(data.frame(Layer),overlapMatrix2)
                    listNodeOverlap <<- overlapMatrix2    
                }
    
                if(input$chkMULTIPLEX_INTERASSORTATIVITY_PEARSON && LAYERS>1){
                    progress$set(message = 'Calculating Pearson...', value = 0.05)

                    Layer <- NULL
                    for(l in 1:LAYERS) Layer = c(Layer,as.character(layerLabel[[l]]))

                    interPearson <- GetInterAssortativityTensor(SupraAdjacencyMatrix,LAYERS,Nodes,
                                                                                        DIRECTED,input$selAssortativityType)$InterPearson
    
                    interPearson.df <<- as.data.frame(t(as.matrix(interPearson)))
                    colnames(interPearson.df) <<- Layer
                    rownames(interPearson.df) <<- Layer
    
                    interPearson <- data.frame(as.matrix(interPearson))
                    colnames(interPearson) <- Layer
                    interPearson <- cbind(data.frame(Layer),interPearson)
                    listInterPearson <<- interPearson                    
                }
    
                if(input$chkMULTIPLEX_INTERASSORTATIVITY_SPEARMAN && LAYERS>1){
                    progress$set(message = 'Calculating Spearman...', value = 0.05)
                        
                    Layer <- NULL
                    for(l in 1:LAYERS) Layer = c(Layer,as.character(layerLabel[[l]]))


                    interSpearman <- GetInterAssortativityTensor(SupraAdjacencyMatrix,LAYERS,Nodes,
                                                                                    DIRECTED,input$selAssortativityType)$InterSpearman
    
                    interSpearman.df <<- as.data.frame(t(as.matrix(interSpearman)))
                    colnames(interSpearman.df) <<- Layer
                    rownames(interSpearman.df) <<- Layer
                    
                    interSpearman <- data.frame(as.matrix(interSpearman))
                    colnames(interSpearman) <- Layer
                    interSpearman <- cbind(data.frame(Layer),interSpearman)
                    listInterSpearman <<- interSpearman                    
                }            

                if(input$chkMULTIPLEX_SHORTESTPATH && LAYERS>1){
                    progress$set(message = 'Calculating SP Distances...', value = 0.05)
                        
                    Layer <- NULL
                    distanceList <- vector("list", LAYERS)
                    for(l in 1:LAYERS){
                        Layer = c(Layer,as.character(layerLabel[[l]]))
                        
                        distanceList[[l]] <- shortest.paths(g[[l]])
                        distanceList[[l]][ is.infinite(distanceList[[l]]) ] <- 1e8
                    }

                    frobeniusNorm <- matrix(0, ncol=LAYERS, nrow=LAYERS)
                    for(l1 in 1:(LAYERS-1)){
                        for(l2 in (l1+1):LAYERS){
                            frobeniusNorm[l1,l2] <- sqrt(sum((distanceList[[l1]] - distanceList[[l2]])^2))
                            frobeniusNorm[l2,l1] <- frobeniusNorm[l1,l2]
                        }
                    }
                    frobeniusNorm <- 1 - frobeniusNorm/max(frobeniusNorm)
    
                    colnames(frobeniusNorm) <- Layer
                    rownames(frobeniusNorm) <- Layer
                    frobeniusNorm.df <<- as.data.frame(t(frobeniusNorm))
                    colnames(frobeniusNorm.df) <<- Layer
                    rownames(frobeniusNorm.df) <<- Layer

                    frobeniusNorm <- data.frame(frobeniusNorm)
                    frobeniusNorm <- cbind(data.frame(Layer),frobeniusNorm)
                    listDistanceSimilarity <<- frobeniusNorm
                }            

                progress$set(message = 'Correlation Diagnostics Completed!', value = 1)
                Sys.sleep(2)
            })
        }, ignoreInit = TRUE)
        
        output$overlappingEdgeHeatmapUI <- renderUI({
            d3heatmapOutput("overlappingEdgeHeatmap", width = "100%")
        })

        output$globalDiagnosticsOverlapping <- reactive({
            if (input$btnCalculateCorrelationDiagnostics == 0 || input$btnImportNetworks == 0 || length(input$project_file)==0)
                return(list())

            #be careful: "=" is required instead of "<-"
            return(list(
                sumAvgGlobalOverlapping = as.character(avgGlobalOverlapping)
            ))
        })

        output$overlappingEdgeHeatmap <- renderD3heatmap({
            if(input$btnCalculateCorrelationDiagnostics==0 || input$btnImportNetworks == 0 || LAYERS==0)
                return(NULL)
                
            d3heatmap(
                avgGlobalOverlappingMatrix.df,
                color = input$selAssortativityTypeColorPalette,
                dendrogram = if (input$chkOverlappingEdgeHeatmapShowDendrogram){"both"}else{"none"}
                )
        })
        
        output$overlappingSummaryTable <- renderGvis({
            if(input$btnCalculateCorrelationDiagnostics==0 || input$btnImportNetworks == 0 || LAYERS==0)
                return(NULL)
            
            gvisTable(listOverlap,options=googleVisOverlapMatrixSummaryTableOptions())
        })   

        output$overlappingNodeHeatmapUI <- renderUI({
            d3heatmapOutput("overlappingNodeHeatmap", width = "100%")
        })

        output$overlappingNodeHeatmap <- renderD3heatmap({
            if(input$btnCalculateCorrelationDiagnostics==0 || input$btnImportNetworks == 0 || LAYERS==0)
                return(NULL)
                
            d3heatmap(
                avgGlobalNodeOverlappingMatrix.df,
                color = input$selAssortativityTypeColorPalette,
                dendrogram = if (input$chkOverlappingNodeHeatmapShowDendrogram){"both"}else{"none"}
                )
        })

        output$overlappingNodeSummaryTable <- renderGvis({
            if(input$btnCalculateCorrelationDiagnostics==0 || input$btnImportNetworks == 0 || LAYERS==0)
                return(NULL)
            
            gvisTable(listNodeOverlap,options=googleVisNodeOverlapMatrixSummaryTableOptions())
        })   

        output$interPearsonHeatmapUI <- renderUI({
            d3heatmapOutput("interPearsonHeatmap", width = "100%")
        })

        output$interPearsonHeatmap <- renderD3heatmap({
            if(input$btnCalculateCorrelationDiagnostics==0 || input$btnImportNetworks == 0 || LAYERS==0)
                return(NULL)
                
            d3heatmap(
                interPearson.df,
                color = input$selAssortativityTypeColorPalette,
                dendrogram = if (input$chkInterPearsonHeatmapShowDendrogram){"both"}else{"none"}
                )
        })

        output$interPearsonSummaryTable <- renderGvis({
            if(input$btnCalculateCorrelationDiagnostics==0 || input$btnImportNetworks == 0 || LAYERS==0)
                return(NULL)
            
            gvisTable(listInterPearson,options=googleVisInterPearsonSummaryTableOptions())
        })   

        output$interSpearmanHeatmapUI <- renderUI({
            d3heatmapOutput("interSpearmanHeatmap", width = "100%")
        })

        output$interSpearmanHeatmap <- renderD3heatmap({
            if(input$btnCalculateCorrelationDiagnostics==0 || input$btnImportNetworks == 0 || LAYERS==0)
                return(NULL)
                
            d3heatmap(
                interSpearman.df,
                color = input$selAssortativityTypeColorPalette,
                dendrogram = if (input$chkInterSpearmanHeatmapShowDendrogram){"both"}else{"none"}
                )
        })

        output$interSpearmanSummaryTable <- renderGvis({
            if(input$btnCalculateCorrelationDiagnostics==0 || input$btnImportNetworks == 0 || LAYERS==0)
                return(NULL)
            
            gvisTable(listInterSpearman,options=googleVisInterSpearmanSummaryTableOptions())
        })   

        output$distanceSimilarityHeatmapUI <- renderUI({
            d3heatmapOutput("distanceSimilarityHeatmap", width = "100%")
        })

        output$distanceSimilarityHeatmap <- renderD3heatmap({
            if(input$btnCalculateCorrelationDiagnostics==0 || input$btnImportNetworks == 0 || LAYERS==0)
                return(NULL)
                
            d3heatmap(
                frobeniusNorm.df,
                color = input$selAssortativityTypeColorPalette,
                dendrogram = if (input$chkDistanceSimilarityHeatmapShowDendrogram){"both"}else{"none"}
                )
        })

        output$distanceSimilaritySummaryTable <- renderGvis({
            if(input$btnCalculateCorrelationDiagnostics==0 || input$btnImportNetworks == 0 || LAYERS==0)
                return(NULL)
            
            gvisTable(listDistanceSimilarity,options=googleVisDistanceSimilaritySummaryTableOptions())
        })   
        
        observeEvent(input$btnCalculateCentralityDiagnostics, {
            if(input$btnImportNetworks == 0 ||  LAYERS==0)
                return()
    
            isolate({
                progress <- shiny::Progress$new(session)
                on.exit(progress$close())
                   
                ###############
                ## Centrality
                ###############
                if(input$chkNODE_CENTRALITY_DEGREE || input$chkNODE_CENTRALITY_STRENGTH || input$chkNODE_CENTRALITY_PAGERANK || input$chkNODE_CENTRALITY_EIGENVECTOR || input$chkNODE_CENTRALITY_HUB || input$chkNODE_CENTRALITY_AUTHORITY || input$chkNODE_CENTRALITY_KATZ || input$chkNODE_CENTRALITY_KCORE || input$chkNODE_CENTRALITY_MULTIPLEXITY){

                    progress$set(message = 'Calculating centrality...', value = 0.05)
                    diagnosticsOK <<- T
                    #do not reinitialize the following, because it would delete previous calculations and this is a problem
                    #if one wants to work with both single-layer and multiplex calculations
                    #listDiagnostics <<- NULL
                    #listDiagnosticsMerge <<- NULL
                    #listDiagnosticsMergeSingleLayer <<- NULL
                    
                    if(input$radCentralityAlgorithm=="CENTRALITY_MULTILAYER"){
                        #the output will be stored in [[l]] for the multiplex and [[LAYERS+1]] for the aggregated.
                        listDiagnostics <<- GetCentralityDataFrameArray("Multiplex") 
                        diagnosticsMultiplexOK <<- T
                        listDiagnosticsMerge <<- NULL
                        
                        for(l in 1:(LAYERS+1)){
                            listDiagnosticsMerge <<- rbind(listDiagnosticsMerge,listDiagnostics[[l]])
                        }             

                    }else{
                        #calculation per layer. No needs to specify the weight attribute because the g objects
                        #are built assuming weighted input (where weight is 1 for binary networks), and each measure
                        #assume by default the weight attribute of E(g)
                        
                        listDiagnosticsSingleLayer <<- GetCentralityDataFrameArray("SingleLayer")
                        diagnosticsSingleLayerOK <<- T
                        listDiagnosticsMergeSingleLayer <<- NULL
                        
                        for(l in 1:(LAYERS+1)){
                            listDiagnosticsMergeSingleLayer <<- rbind(listDiagnosticsMergeSingleLayer,listDiagnosticsSingleLayer[[l]])
                        }             

                    }
                }
                
                progress$set(message = 'Centrality Completed!', value = 1)
                Sys.sleep(2)
            })
        }, ignoreInit = TRUE)
        
        #Fill the table summarizing centrality 
        output$centralityTable <- renderGvis({
            if(diagnosticsMultiplexOK){
                return(gvisTable(listDiagnosticsMerge, options=list(page='enable', pageSize=Nodes)))
                #googleVisCentralityTableOptions()))
            }
        })   
            
        #Fill the table summarizing centrality 
        output$centralityTableSingleLayer <- renderGvis({
            if(diagnosticsSingleLayerOK){
                return(gvisTable(listDiagnosticsMergeSingleLayer, options=list(page='enable', pageSize=Nodes)))
                #googleVisCentralityTableOptions()))
            }
        }) 
        
        observeEvent(input$btnCalculateCommunityDiagnostics, {
            if(input$btnImportNetworks == 0 ||  LAYERS==0)
                return()
            
            isolate({
                progress <- shiny::Progress$new(session)
                on.exit(progress$close())
                
                ###############
                ## Community
                ###############

                progress$set(message = 'Calculating community structure...', value = 0.05)
                print("  Detecting communities...")    
                if(is.null(input$radCommunityAlgorithm)) return()

                communityOK <<- T
                    
                if(input$radCommunityAlgorithm=="COMMUNITY_MULTIPLEX_MODMAX" || input$radCommunityAlgorithm=="COMMUNITY_MULTIPLEX_INFOMAP"){
                    #calculation in the multiplex
                    listCommunities <<- NULL
                    sumCommunities <- NULL
                    listCommunitiesMerge <<- NULL
                    sumCommunitiesMerge <<- NULL
    
                    if(input$radCommunityAlgorithm=="COMMUNITY_MULTIPLEX_MODMAX"){
                        #the output will be stored in [[l]] for the multiplex and [[LAYERS+1]] for the aggregated.
    
#                        for(l in 1:LAYERS){
#                            listCommunities[[l]] <<- data.frame(Layer = rep(paste(l,"Multi",sep="-"),Nodes))
#                            listCommunities[[l]] <<- cbind(listCommunities[[l]],data.frame(Node = 1:Nodes))
#                            listCommunities[[l]] <<- cbind(listCommunities[[l]],data.frame(Label=nodesLabel[[l]]))
#                        }
#                        l <- LAYERS+1
#                        listCommunities[[l]] <<- data.frame(Layer = rep("Aggr",Nodes))
#                        listCommunities[[l]] <<- cbind(listCommunities[[l]],data.frame(Node = 1:Nodes))
#                        listCommunities[[l]] <<- cbind(listCommunities[[l]],data.frame(Label=nodesLabel[[l]]))
#                        
#                        sumCommunities[[1]] <- data.frame(Layer = "Multi")
#                        sumCommunities[[2]] <- data.frame(Layer = "Aggr")
#    
#                        createOctaveConfigFile()
#                        #call octave
#                        #system("octave -qf octave/muxMultisliceCommunity.m",intern=T)
#                        octave.call("octave/muxMultisliceCommunity.m")
#                        Sys.sleep(3)
#                        
#                        #read output.
#                        resultFile <- paste0(input$txtProjectName,"_community_membership.txt")
#                        wmemb_membership <- matrix(scan(resultFile, n = Nodes*LAYERS), ncol=LAYERS, nrow=Nodes, byrow = TRUE)   
#                        if(file.exists(resultFile)) file.remove(resultFile)             
#                        resultFile <- paste0(input$txtProjectName,"_community_modularity.txt")
#                        wtmod <- as.numeric(read.table(resultFile)[1,1])
#                        if(file.exists(resultFile)) file.remove(resultFile)
#    
#                        print(paste("  Modularity: ",wtmod))
#                        maxCom <- max(wmemb_membership)
#                        numComms <- maxCom
#    
#                        resultFile <- paste0(input$txtProjectName,"_community_membership_aggregate.txt")
#                        wmemb_membership_aggregate <- matrix(scan(resultFile, n = Nodes), ncol=1, nrow=Nodes, byrow = TRUE)        
#                        if(file.exists(resultFile)) file.remove(resultFile)        
#                        resultFile <- paste0(input$txtProjectName,"_community_modularity_aggregate.txt")
#                        wtmod_aggregate <- as.numeric(read.table(resultFile)[1,1])
#                        if(file.exists(resultFile)) file.remove(resultFile)
#    
#                        print(paste("  Modularity aggregate: ",wtmod_aggregate))
#                        maxComAggr <- max(wmemb_membership_aggregate)
#                        numCommsAggr <- maxComAggr
#
#                              
#                        #eventual community merging, if any, here.
#                        #todo: this can be improved by finding isolated nodes at the very beginning
#                        isolatedNodes <- 0
#                        for(l in 1:LAYERS){
#                            final.memb <- rep(0, Nodes)
#                            idx.nonisolated <- which(degree(g[[l]], mode="total")>0)
#                            isolatedNodes <- isolatedNodes + Nodes - length(idx.nonisolated)
#                            final.memb[ idx.nonisolated ] <- wmemb_membership[idx.nonisolated,l]
#                            
#                            listCommunities[[l]] <<- cbind(listCommunities[[l]],data.frame(Community=final.memb))
#                            listCommunitiesMerge <<- rbind(listCommunitiesMerge,listCommunities[[l]])
#                        }
#                        listCommunities[[LAYERS+1]] <<- cbind(listCommunities[[LAYERS+1]],data.frame(Community=wmemb_membership_aggregate))
#                        listCommunitiesMerge <<- rbind(listCommunitiesMerge,listCommunities[[LAYERS+1]])
#                        #print(listCommunities)
#                        
#                        if(as.numeric(input$txtOmega)==0){
#                            numComms <- numComms - isolatedNodes
#                        }
#                        
#    
#                        #Multiplex
#                        l <- 1
#                        sumCommunities[[l]] <- cbind(sumCommunities[[l]],data.frame(Communities = numComms))                        
#                        sumCommunities[[l]] <- cbind(sumCommunities[[l]],data.frame(Modularity = round(wtmod,3)))
#                        sumCommunitiesMerge <<- rbind(sumCommunitiesMerge,sumCommunities[[l]])
#                        #Aggregate: change numcoms and modularity here
#                        l <- 2
#                        sumCommunities[[l]] <- cbind(sumCommunities[[l]],data.frame(Communities = numCommsAggr))                        
#                        sumCommunities[[l]] <- cbind(sumCommunities[[l]],data.frame(Modularity = round(wtmod_aggregate,3)))
#                        sumCommunitiesMerge <<- rbind(sumCommunitiesMerge,sumCommunities[[l]])
#    
#                        #print(listCommunitiesMerge)
                    }else if(input$radCommunityAlgorithm=="COMMUNITY_MULTIPLEX_INFOMAP"){
                        for(l in 1:LAYERS){
                            listCommunities[[l]] <<- data.frame(Layer = rep(paste(l,"Multi",sep="-"),Nodes))
                            listCommunities[[l]] <<- cbind(listCommunities[[l]],data.frame(Node = 1:Nodes))
                            listCommunities[[l]] <<- cbind(listCommunities[[l]],data.frame(Label=nodesLabel[[l]]))
                        }
                        l <- LAYERS+1
                        listCommunities[[l]] <<- data.frame(Layer = rep("Aggr",Nodes))
                        listCommunities[[l]] <<- cbind(listCommunities[[l]],data.frame(Node = 1:Nodes))
                        listCommunities[[l]] <<- cbind(listCommunities[[l]],data.frame(Label=nodesLabel[[l]]))
                        
                        sumCommunities[[1]] <- data.frame(Layer = "Multi")
                        sumCommunities[[2]] <- data.frame(Layer = "Aggr")

                        #generate adequate input for multimap
                        progress$set(message = 'Setting up the algorithm...', value = 0.2)                
                        inputFile <- paste0(input$txtProjectName,"_multimap.edges")
                        if(file.exists(inputFile)) file.remove(inputFile)
                        fileConn <- file(inputFile, open="at")
                
                        if(input$radMultiplexModel=='MULTIPLEX_IS_EDGECOLORED'){
                            writeLines(c("*Intra","#level node node weight"), fileConn)
                            out.edgeslist <- data.frame()
                            for(l in 1:LAYERS){
                                tmp.edges <- get.edgelist(g[[l]])
                                out.edgeslist <- rbind(out.edgeslist, data.frame(level=l, 
                                                                                                           nodeA=tmp.edges[,1], 
                                                                                                           nodeB=tmp.edges[,2], 
                                                                                                           weight=E(g[[l]])$weight)
                                                                )

                                if(!DIRECTED){
                                    #this is because multimap requires both directions specified, even for undirected networks
                                    out.edgeslist <- rbind(out.edgeslist, data.frame(level=l, 
                                                                                                               nodeA=tmp.edges[,2], 
                                                                                                               nodeB=tmp.edges[,1], 
                                                                                                               weight=E(g[[l]])$weight)
                                                                )                                    
                                }
                            }
                            write.table(out.edgeslist, file=fileConn, row.names=F, col.names=F, quote=F)
                        }else{
                            writeLines(c("*Intra","#level node node weight"), fileConn)
                            out.edgeslist <- data.frame()
                            
                            submulti <- multilayerEdges[ multilayerEdges$V2==multilayerEdges$V4, ]
                            out.edgeslist <- rbind(out.edgeslist, data.frame(level=submulti$V2, 
                                                                                                      nodeA=submulti$V1, 
                                                                                                      nodeB=submulti$V3, 
                                                                                                      weight=submulti$V5)
                                                            )
                            if(!DIRECTED){
                                #this is because multimap requires both directions specified, even for undirected networks
                                out.edgeslist <- rbind(out.edgeslist, data.frame(level=submulti$V2, 
                                                                                                          nodeA=submulti$V3, 
                                                                                                          nodeB=submulti$V1, 
                                                                                                          weight=submulti$V5)
                                                                )    
                            }
                            write.table(out.edgeslist, file=fileConn, row.names=F, col.names=F, quote=F)

                            writeLines(c("*Inter","#node level level weight"), fileConn)
                            out.edgeslist <- data.frame()
                            submulti <- multilayerEdges[ multilayerEdges$V1==multilayerEdges$V3, ]
                            out.edgeslist <- rbind(out.edgeslist, data.frame(node=submulti$V1, 
                                                                                                      levelA=submulti$V2, 
                                                                                                      levelB=submulti$V4, 
                                                                                                      weight=submulti$V5)
                                                            )
                            if(!DIRECTED){
                                #this is because multimap requires both directions specified, even for undirected networks
                                out.edgeslist <- rbind(out.edgeslist, data.frame(node=submulti$V1, 
                                                                                                          levelA=submulti$V4, 
                                                                                                          levelB=submulti$V2, 
                                                                                                          weight=submulti$V5)
                                                                )
                            }
                            write.table(out.edgeslist, file=fileConn, row.names=F, col.names=F, quote=F)
                        }
                        close(fileConn) 
                        
                        #setup the adequate flags
                        exePath <- getExecutablePath("multiplex-infomap")
                        exeFlags <- ""
                        
                        if(input$radMultiplexModel=='MULTIPLEX_IS_EDGECOLORED'){
                            exeFlags <- paste("-s", floor(runif(1)*1e7), 
                                                          "-N", as.numeric(input$txtMultimapTries),
                                                          "-multiplex -physical -smartinit -proportionalswitch",
                                                          "-switchrate", as.numeric(input$txtMultimapRelaxRate),
                                                          inputFile)
                        }else{
                            exeFlags <- paste("-s", floor(runif(1)*1e7), 
                                                          "-N", as.numeric(input$txtMultimapTries),
                                                          "-multiplex -physical -smartinit",
                                                          "-switchrate", as.numeric(input$txtMultimapRelaxRate),
                                                          inputFile)
                        }                        

                        #make the external call
                        progress$set(message = 'Finding communities...', value = 0.5)
                        #print( paste(exePath,exeFlags) )
                        system(paste(exePath,exeFlags),intern=T)
                        
                        #import the results (clu and modularity value)
                        resultFile <- paste0(input$txtProjectName,"_multimap_Multiplex_Physical.clu")
                        wmemb_membership <- read.table(resultFile, header=F, sep=" ")
                        if(file.exists(resultFile)) file.remove(resultFile)             
                        resultFile <- paste0(input$txtProjectName,"_multimap_Multiplex_Physical.tree") 
                        wtcod <- as.numeric(strsplit(readLines(resultFile, n=1), " ")[[1]][4])
                        if(file.exists(resultFile)) file.remove(resultFile)

                        print(paste("  Code length: ",wtcod))
                        maxCom <- max(wmemb_membership$V3)
                        numComms <- maxCom

                        #calculate same things for the aggregate using R infomap
                        infocom <- infomap.community(g[[LAYERS+1]],modularity=TRUE)                                
                        wmemb_membership_aggregate <- as.numeric(membership(infocom))
                        wtcod_aggregate <- code_len(infocom)
                        
                        print(paste("  Code length aggregate: ",wtcod_aggregate))
                        maxComAggr <- max(wmemb_membership_aggregate)
                        numCommsAggr <- maxComAggr

                        #update the global variables
                        #eventual community merging, if any, here.
                        for(l in 1:LAYERS){                     
                            final.memb <- rep(0, Nodes)
                            tmp.memb <- wmemb_membership[ wmemb_membership$V1==l, ]
                            final.memb[ tmp.memb$V2 ] <- tmp.memb$V3
                                                                               
                            listCommunities[[l]] <<- cbind(listCommunities[[l]],data.frame(Community=final.memb))
                            listCommunitiesMerge <<- rbind(listCommunitiesMerge,listCommunities[[l]])
                        }
                        listCommunities[[LAYERS+1]] <<- cbind(listCommunities[[LAYERS+1]],data.frame(Community=wmemb_membership_aggregate))
                        listCommunitiesMerge <<- rbind(listCommunitiesMerge,listCommunities[[LAYERS+1]])
                        #print(listCommunities)
    
                        #Multiplex
                        l <- 1
                        sumCommunities[[l]] <- cbind(sumCommunities[[l]],data.frame(Communities = numComms))                        
                        sumCommunities[[l]] <- cbind(sumCommunities[[l]],data.frame(CodeLength = round(wtcod,3)))
                        sumCommunitiesMerge <<- rbind(sumCommunitiesMerge,sumCommunities[[l]])
                        #Aggregate: change numcoms and CodeLength here
                        l <- 2
                        sumCommunities[[l]] <- cbind(sumCommunities[[l]],data.frame(Communities = numCommsAggr))                        
                        sumCommunities[[l]] <- cbind(sumCommunities[[l]],data.frame(CodeLength = round(wtcod_aggregate,3)))
                        sumCommunitiesMerge <<- rbind(sumCommunitiesMerge,sumCommunities[[l]])
                    }
                    
                    communityMultiplexOK <<- T
                    values$communityMultiplexOK <- T

                    if(input$chkMultimapBatchExploration){ 
                        print("  Entering multiscale mode")
                        
                        r.min <- as.numeric(input$txtMultimapMinRelaxRate)
                        r.max <- as.numeric(input$txtMultimapMaxRelaxRate)
                        r.step <- (r.max - r.min)/as.numeric(input$txtMultimapStepsRelaxRate)
                        r.rates <- seq(r.min, r.max, r.step)

                        df.batch_memb <- data.frame(node=1:nrow(wmemb_membership), stringsAsFactors = FALSE)
                        df.batch_data <- data.frame()

                        #setup the adequate flags
                        exePath <- getExecutablePath("multiplex-infomap")

                        #progress' step
                        prog <- 0.5
                        
                        cnt <- 1
                        for(relax.rate in r.rates){
                            print(paste("    Relax rate: ", relax.rate))
                            progress$set(message = paste('Batch exploration, rate:', relax.rate, '...'), value = prog)     
                            exeFlags <- ""
                        
                            if(input$radMultiplexModel=='MULTIPLEX_IS_EDGECOLORED'){
                                exeFlags <- paste("-s", floor(runif(1)*1e7), 
                                                              "-N", as.numeric(input$txtMultimapTries),
                                                              "-multiplex -physical -smartinit -proportionalswitch",
                                                              "-switchrate", relax.rate,
                                                              inputFile)
                            }else{
                                exeFlags <- paste("-s", floor(runif(1)*1e7), 
                                                              "-N", as.numeric(input$txtMultimapTries),
                                                              "-multiplex -physical -smartinit",
                                                              "-switchrate", relax.rate,
                                                              inputFile)
                            }                        

                            #make the external call
                            system(paste(exePath,exeFlags),intern=T)

                            #import the results (clu and modularity value)
                            resultFile <- paste0(input$txtProjectName,"_multimap_Multiplex_Physical.clu")
                            wmemb_membership <- read.table(resultFile, header=F, sep=" ")
                            if(file.exists(resultFile)) file.remove(resultFile)             
                            resultFile <- paste0(input$txtProjectName,"_multimap_Multiplex_Physical.tree") 
                            wtcod.tmp <- as.numeric(strsplit(readLines(resultFile, n=1), " ")[[1]][4])
                            if(file.exists(resultFile)) file.remove(resultFile)

                            numComms.tmp <- max(wmemb_membership$V3)
                            
                            df.batch_memb <- cbind(df.batch_memb, paste0("R", cnt, "_", wmemb_membership$V3))
                                                                            
                            df.batch_data <- rbind(df.batch_data, data.frame(Rate=relax.rate,
                                                                                                            Multi.Communities=numComms.tmp,
                                                                                                            Multi.CodeLength=wtcod.tmp,
                                                                                                            Aggr.Communities=numCommsAggr,
                                                                                                            Aggr.CodeLength=wtcod_aggregate
                                                                                                        ))

                            cnt <- cnt + 1    
                            prog <- prog + 0.4/as.numeric(input$txtMultimapStepsRelaxRate)
                        }
                        
                        df.batch_memb$node <- NULL
                        colnames(df.batch_memb) <- paste0("R", 1:(cnt-1))

                        #update the global variables
                        communityBatchMembership <<- df.batch_memb
                        communityBatchData <<- df.batch_data
                        
                        communityMultiplexBatchOK <<- T
                        values$communityMultiplexBatchOK <- T
                    }

                    #keep this here, to delete the input file even if the batch exploration is not used
                    Sys.sleep(3)
                    if(file.exists(inputFile)) file.remove(inputFile)
                }else{                    
                    #calculation per layer. No needs to specify the weight attribute because the g objects
                    #are built assuming weighted input (where weight is 1 for binary networks), and each measure
                    #assume by default the weight attribute of E(g)

                    listCommunitiesSingleLayer <<- NULL
                    sumCommunitiesSingleLayer <- NULL
                    listCommunitiesMergeSingleLayer <<- NULL
                    sumCommunitiesMergeSingleLayer <<- NULL
                    wt <- NULL
                    wmemb <- list()

                    for(l in 1:(LAYERS)){
                        listCommunitiesSingleLayer[[l]] <<- data.frame(Layer = rep(l,Nodes))
                        listCommunitiesSingleLayer[[l]] <<- cbind(listCommunitiesSingleLayer[[l]],data.frame(Node = 1:Nodes))
                        listCommunitiesSingleLayer[[l]] <<- cbind(listCommunitiesSingleLayer[[l]],data.frame(Label=nodesLabel[[l]]))

                        sumCommunitiesSingleLayer[[l]] <- data.frame(Layer = as.character(l))
                    }

                    listCommunitiesSingleLayer[[LAYERS+1]] <<- data.frame(Layer = rep("Aggr",Nodes))
                    listCommunitiesSingleLayer[[LAYERS+1]] <<- cbind(listCommunitiesSingleLayer[[LAYERS+1]],data.frame(Node = 1:Nodes))
                    listCommunitiesSingleLayer[[LAYERS+1]] <<- cbind(listCommunitiesSingleLayer[[LAYERS+1]],data.frame(Label=nodesLabel[[LAYERS+1]]))

                    sumCommunitiesSingleLayer[[LAYERS+1]] <- data.frame(Layer = "Aggr")
                        
                    for(l in 1:(LAYERS+1)){
                        if(input$radCommunityAlgorithm=="COMMUNITY_LOUVAIN"){
                            wt <- multilevel.community(as.undirected(g[[l]]))
                        }

                        if(input$radCommunityAlgorithm=="COMMUNITY_INFOMAP"){                            
                            wt <- infomap.community(g[[l]],modularity=TRUE)
                        }
                        
                        if(input$radCommunityAlgorithm=="COMMUNITY_RANDOM_WALK_TRAP"){
                            wt <- walktrap.community(g[[l]],modularity=TRUE)
                        }
                        
                        if(input$radCommunityAlgorithm=="COMMUNITY_EDGE_BETWEENNESS"){
                            wt <- edge.betweenness.community(g[[l]],modularity=TRUE)
                        }

                        #wmemb$modularity <- modularity(wt)
                        wmemb$membership <- as.numeric(membership(wt) )
                        wmemb$csize <- as.numeric(sizes(wt))
                        #comList <- communities(wt)

                        print(paste("  Modularity: ",modularity(wt)))
                        isolated.nodes <- sum(wmemb$csize==1)
                        maxCom <- max(wmemb$membership) - isolated.nodes
                        numComms <- maxCom
                        
                        #Merge isolated nodes to 0 community 
                        mergedNodes <- 0                            
                        idx.co <- which(wmemb$csize == 1)
                            
                        for(co in idx.co){
                            idx.nodes <- which(wmemb$membership==co)
                            wmemb$membership[idx.nodes]  <- -1
                            mergedNodes <- mergedNodes + length(idx.nodes)
                        }                            
                        
                        #Transform community ids to sequential integers
                        com.ids <- sort(unique(wmemb$membership[wmemb$membership!=-1]))
                        com.ids.map <- setNames(1:length(com.ids), com.ids)
                        wmemb$membership[wmemb$membership!=-1] <- com.ids.map[as.character(wmemb$membership[wmemb$membership!=-1])]

                        print(paste("  There are", mergedNodes, "isolated nodes."))
                                
                        maxCom <- max(wmemb$membership) 
                        mergeComID <-  0
                        wmemb$membership[wmemb$membership==-1] <- mergeComID
#                        wmemb$csize[mergeComID] <- mergedNodes
#                        wmemb$csize <- wmemb$csize[1:mergeComID]
                        
                        print(paste("  Communities with more than 1 node:",maxCom))
                        
                        listCommunitiesSingleLayer[[l]] <<- cbind(listCommunitiesSingleLayer[[l]],data.frame(Community=wmemb$membership))

                        sumCommunitiesSingleLayer[[l]] <- cbind(sumCommunitiesSingleLayer[[l]],data.frame(Communities = numComms))                        
                        sumCommunitiesSingleLayer[[l]] <- cbind(sumCommunitiesSingleLayer[[l]],data.frame(Modularity = round(modularity(wt),3)))
                    }
    
                    for(l in 1:(LAYERS+1)){
                        listCommunitiesMergeSingleLayer <<- rbind(listCommunitiesMergeSingleLayer,listCommunitiesSingleLayer[[l]])
                        sumCommunitiesMergeSingleLayer <<- rbind(sumCommunitiesMergeSingleLayer,sumCommunitiesSingleLayer[[l]])
                    }
                    
                    communitySingleLayerOK <<- T
                    values$communitySingleLayerOK <- T
                }
                    
                progress$set(message = 'Community Detection Completed!', value = 1)
                Sys.sleep(2)
            })  
        }, ignoreInit = TRUE)

        observe({
            values$communityMultiplexOK
            input$btnRefreshCommunityDiagnostics
            
            #todo: cannot do reactive on global variable, unless it's a reactiveValues
            
            if(communityMultiplexOK){
                matComm <- matrix(nrow=LAYERS, ncol=Nodes, 0)
                Layer <- NULL
                for(l in 1:LAYERS) Layer = c(Layer,as.character(layerLabel[[l]]))
                for(l in 1:(LAYERS)){
                    matComm[l,] <- listCommunities[[l]]$Community
                }


                #not used: possibly removable
                #rgb.palette <- colorRampPalette(brewer.pal(brewer.pal.info$maxcolors[row.names(brewer.pal.info)==input$selCommunityColorPalette],input$selCommunityColorPalette))(max(matComm))
                #rgb.palette <- c("white", rgb.palette)

                matComm.df <- as.data.frame(t(matComm))
                colnames(matComm.df) <- Layer
                

                output$communityHeatmapUI <- renderUI({
                    d3heatmapOutput("communityHeatmap",
                                                    width = "100%",
                                                    height = paste0(max(Nodes*3,600),"px")
                        )
                })
                
                output$communityHeatmap <- renderD3heatmap({
                    if(input$communityHeatmapShowLabels){
                        rownames(matComm.df) <- nodesLabel[[1]]
                    }else{
                        rownames(matComm.df) <- paste0("n",1:Nodes)
                    }


                    d3heatmap(
                        matComm.df,
                        color = input$selCommunityHeatmapColorPalette,
                        dendrogram = if (input$chkCommunityHeatmapShowDendrogram){"both"}else{"none"}
                        )
                })

                output$communityDistributionPlot <- renderChart2({
                    X <- data.frame()
                    categs.tmp <- unique(as.numeric(matComm))
                    for(l in 1:LAYERS){
                        distr.tmp <- as.data.frame(table(matComm.df[,l]),stringsAsFactors=F)
                        distr.tmp <- distr.tmp[as.character(distr.tmp$Var1)!="0",]
                        missing.tmp <- categs.tmp[which(!categs.tmp %in% distr.tmp$Var1)]
                        missing.tmp <- missing.tmp[as.character(missing.tmp)!="0"]
                        if(length(missing.tmp)>0){
                            missing.tmp <- data.frame(Var1=missing.tmp, Freq=0,stringsAsFactors=F)
                            #add zero-counts categories. See https://github.com/ramnathv/rCharts/issues/545
                            distr.tmp <- rbind(distr.tmp, missing.tmp)
                        }

                        distr.tmp <- distr.tmp[order(as.numeric(distr.tmp$Var1)),]

                        X <- rbind(X, data.frame(Layer=l, 
                                                                Community=distr.tmp$Var1, 
                                                                Nodes=distr.tmp$Freq)
                                                                )
                    }


                    rplot <- nPlot(Nodes ~ Community, 
                                data = X, group="Layer", type = "multiBarChart")
                    
                    rplot$chart(reduceXTicks = FALSE)
                    rplot$xAxis(staggerLabels = T)
                    rplot$xAxis(axisLabel = 'Community ID')
                    rplot$yAxis(axisLabel = '# Nodes')
                    return(rplot)
                })        
        
                
                #Fill the table summarizing the community
                output$communitySummaryTable <- renderGvis({                    
                    gvisTable(sumCommunitiesMerge,options=list(page='enable',pageSize=2))
                })
                
                #Fill the table summarizing the community
                output$communityTable <- renderGvis({                    
                    gvisTable(listCommunitiesMerge,options=list(page='enable',pageSize=Nodes))
                })
            }      
        })


        observe({
            values$communityMultiplexBatchOK
            input$btnRefreshCommunityDiagnostics
            
            #todo: cannot do reactive on global variable, unless it's a reactiveValues
            
            if(communityMultiplexBatchOK){
                #Fill the table summarizing the community batch
                output$communityBatchTable <- renderGvis({                    
                    gvisTable(communityBatchData,options=list(page='enable',pageSize=5))
                })
                
                output$batchMultiplexCommunityData1 <- renderChart2({
                    data <- data.frame(Rate=communityBatchData$Rate, Multi.Communities=communityBatchData$Multi.Communities)
    
                    linechart <- nPlot(Multi.Communities ~ Rate, data = data, type = 'lineChart')
                    linechart$addParams(width = 400, height = 400, title="Multiscale analysis") 
                    linechart$xAxis(axisLabel="Relax rate")
                    linechart$yAxis(axisLabel="Number of Communities")
        
                    linechart$chart(forceY=c(floor(min(data$Multi.Communities))-1,floor(max(data$Multi.Communities))+1), 
                                        forceX=c(-0.05, 1.05))
        
                    return(linechart)
                })    
        
                output$batchMultiplexCommunityData2 <- renderChart2({
                    data <- data.frame(Rate=communityBatchData$Rate, Multi.CodeLength=communityBatchData$ Multi.CodeLength)
    
                    linechart <- nPlot(Multi.CodeLength ~ Rate, data = data, type = 'lineChart')
                    linechart$addParams(width = 400, height = 400, title="Multiscale analysis") 
                    linechart$xAxis(axisLabel="Relax rate")
                    linechart$yAxis(axisLabel="Code Length")
        
                    linechart$chart(forceY=c(0.95*min(data$Multi.CodeLength),1.05*max(data$Multi.CodeLength)), 
                                        forceX=c(-0.05,1.05))
        
                    return(linechart)
                })    
            }
        })

        observeEvent(input$btnPrintCommunityMultiplexChord, {
            if(communityMultiplexOK){
                if(WEIGHTED){
                    g.chord <- graph.adjacency(SupraAdjacencyMatrix, weighted=WEIGHTED)
                }else{
                    g.chord <- graph.adjacency(SupraAdjacencyMatrix)
                }
                memb.chord <- c()
                lapply(1:LAYERS, function(x) memb.chord <<- c(memb.chord, listCommunities[[x]]$Community))
                g.mod.chord <- getCommunityNetwork(g.chord, memb.chord)                
    
                A.mod.tmp <- as.matrix(get.adjacency(g.mod.chord, attr="weight"))

                colorPalette <- colorRampPalette(brewer.pal(brewer.pal.info$maxcolors[row.names(brewer.pal.info)==input$selCommunityHeatmapColorPalette],input$selCommunityHeatmapColorPalette))(length(V(g.mod.chord))+1)

                print( chordNetwork(A.mod.tmp, useTicks = 0, 
                                                labels = c(as.character(V(g.mod.chord))),
                                                colourScale=colorPalette
                                                ) )
            }
        })




        observeEvent(input$btnPrintCommunityMultiplexSankey, {
            if(communityMultiplexBatchOK){
                muxSankey <- buildSankeyFromCommunity(communityBatchMembership)

                print(sankeyNetwork(Links = muxSankey$links, Nodes = muxSankey$nodes, Source = "source",
                  Target = "target", Value = "sum", NodeID = "name", fontSize = 12, nodeWidth = 30)
                  )
            }
        })


        observe({
            values$communitySingleLayerOK 
            input$btnRefreshCommunityDiagnostics
            
            if(communitySingleLayerOK){
                matComm <- matrix(nrow=LAYERS, ncol=Nodes, 0)
                Layer <- NULL
                for(l in 1:LAYERS) Layer = c(Layer,as.character(layerLabel[[l]]))
                for(l in 1:(LAYERS)){
                    matComm[l,] <- listCommunitiesSingleLayer[[l]]$Community
                }


                #not used: possibly removable
                #rgb.palette <- colorRampPalette(brewer.pal(brewer.pal.info$maxcolors[row.names(brewer.pal.info)==input$selCommunityColorPalette],input$selCommunityColorPalette))(max(matComm))
                #rgb.palette <- c("white", rgb.palette)

                matComm.df <- as.data.frame(t(matComm))
                colnames(matComm.df) <- Layer

                output$communityHeatmapSingleLayerUI <- renderUI({
                    d3heatmapOutput("communityHeatmapSingleLayer",
                                                    width = "100%",
                                                    height = paste0(max(Nodes*3,600),"px")
                        )
                    })
                
                output$communityHeatmapSingleLayer <- renderD3heatmap({
                    if(input$communityHeatmapShowLabels){
                        rownames(matComm.df) <- nodesLabel[[1]]
                    }else{
                        rownames(matComm.df) <- paste0("n",1:Nodes)
                    }

                    d3heatmap(
                        matComm.df,
                        color = input$selCommunityHeatmapColorPalette,
                        dendrogram = if (input$chkCommunityHeatmapShowDendrogram){"both"}else{"none"}
                        )
                })

                output$communityDistributionPlotSingleLayer <- renderChart2({
                    X <- data.frame()
                    categs.tmp <- unique(as.numeric(matComm))
                    for(l in 1:LAYERS){
                        distr.tmp <- as.data.frame(table(matComm.df[,l]),stringsAsFactors=F)
                        distr.tmp <- distr.tmp[as.character(distr.tmp$Var1)!="0",]
                        missing.tmp <- categs.tmp[which(!categs.tmp %in% distr.tmp$Var1)]
                        missing.tmp <- missing.tmp[as.character(missing.tmp)!="0"]
                        if(length(missing.tmp)>0){
                            missing.tmp <- data.frame(Var1=missing.tmp, Freq=0,stringsAsFactors=F)
                            #add zero-counts categories. See https://github.com/ramnathv/rCharts/issues/545
                            distr.tmp <- rbind(distr.tmp, missing.tmp)
                        }

                        distr.tmp <- distr.tmp[order(as.numeric(distr.tmp$Var1)),]

                        X <- rbind(X, data.frame(Layer=l, 
                                                                Community=distr.tmp$Var1, 
                                                                Nodes=distr.tmp$Freq)
                                                                )
                    }


                    rplot <- nPlot(Nodes ~ Community, 
                                data = X, group="Layer", type = "multiBarChart")
                    
                    rplot$chart(reduceXTicks = FALSE)
                    rplot$xAxis(staggerLabels = T)
                    rplot$xAxis(axisLabel = 'Community ID')
                    rplot$yAxis(axisLabel = '# Nodes')
                    return(rplot)
                })                

                #Fill the table summarizing the community
                output$communityTableSingleLayer <- renderGvis({
                    gvisTable(listCommunitiesMergeSingleLayer,options=list(page='enable',pageSize=Nodes))
                })
                
                #Fill the table summarizing the community
                output$communitySummaryTableSingleLayer <- renderGvis({
                    gvisTable(sumCommunitiesMergeSingleLayer,options=list(page='enable',pageSize=Nodes))
                })

            }
        })

        ###############
        ## Components
        ###############


        observeEvent(input$btnCalculateComponentsDiagnostics, {
            if(input$btnImportNetworks == 0 ||  LAYERS==0)
                return()
                
            if( as.numeric(input$txtOmega)==0 && input$radMultiplexModel=='MULTIPLEX_IS_EDGECOLORED' ) return()
    
            isolate({
                progress <- shiny::Progress$new(session)
                on.exit(progress$close())
                
                ###############
                ## Connected Components
                ###############
                progress$set(message = 'Calculating connected components...', value = 0.05)
                print("  Finding connected clusters...")    
                componentsOK <<- T

                if(input$radConnectedComponentsAlgorithm=="CONNECTED_COMPONENTS_MULTILAYER"){
                    listComponents <<- NULL
                    sumComponents <- NULL
                    listComponentsMerge <<- NULL
                    sumComponentsMerge <<- NULL

                    #the output will be stored in [[l]] for the multiplex and [[LAYERS+1]] for the aggregated.

                    for(l in 1:LAYERS){
                        listComponents[[l]] <<- data.frame(Layer = rep(paste(l,"Multi",sep="-"),Nodes))
                        listComponents[[l]] <<- cbind(listComponents[[l]],data.frame(Node = 1:Nodes))
                        listComponents[[l]] <<- cbind(listComponents[[l]],data.frame(Label=nodesLabel[[l]]))
                    }
                    l <- LAYERS+1
                    listComponents[[l]] <<- data.frame(Layer = rep("Aggr",Nodes))
                    listComponents[[l]] <<- cbind(listComponents[[l]],data.frame(Node = 1:Nodes))
                    listComponents[[l]] <<- cbind(listComponents[[l]],data.frame(Label=nodesLabel[[l]]))
                    
                    sumComponents[[1]] <- data.frame(Layer = "Multi")
                    sumComponents[[2]] <- data.frame(Layer = "Aggr")
                    
                    wmemb_membership <- GetConnectedComponents(SupraAdjacencyMatrix,LAYERS,Nodes)    
                    wmemb_membership <- t(matrix(rep(wmemb_membership,LAYERS), nrow=LAYERS, ncol=Nodes, byrow=T))
                        
                    maxCom <- max(wmemb_membership)
                    numComms <- maxCom

                    #todo: when weak/strong method available for the multiplex, uncomment the line below
                    #wmemb_membership_aggregate <- components(g[[LAYERS+1]], mode=input$selConnectedComponentsSingleLayerType)$membership
                    wmemb_membership_aggregate <- components(as.undirected(g[[LAYERS+1]]))$membership
                    
                    maxComAggr <- max(wmemb_membership_aggregate)
                    numCommsAggr <- maxComAggr

                    #todo: this can be improved by finding isolated nodes at the very beginning
                    isolatedNodes <- 0
                    for(l in 1:LAYERS){
                        final.memb <- rep(0, Nodes)
                        idx.nonisolated <- which(degree(g[[l]], mode="total")>0)
                        isolatedNodes <- isolatedNodes + Nodes - length(idx.nonisolated)
                        final.memb[ idx.nonisolated ] <- wmemb_membership[idx.nonisolated,l]
                        
                        listComponents[[l]] <<- cbind(listComponents[[l]],data.frame(Component=final.memb))
                        listComponentsMerge <<- rbind(listComponentsMerge,listComponents[[l]])
                    }
                    listComponents[[LAYERS+1]] <<- cbind(listComponents[[LAYERS+1]],data.frame(Component=wmemb_membership_aggregate))
                    listComponentsMerge <<- rbind(listComponentsMerge,listComponents[[LAYERS+1]])
                    #print(listComponents)
                                        
                    #Multiplex
                    l <- 1
                    sumComponents[[l]] <- cbind(sumComponents[[l]],data.frame(Components = numComms)) 
                    sumComponentsMerge <<- rbind(sumComponentsMerge,sumComponents[[l]])
                    #Aggregate: change numcoms here
                    l <- 2
                    sumComponents[[l]] <- cbind(sumComponents[[l]],data.frame(Components = numCommsAggr))                        
                    sumComponentsMerge <<- rbind(sumComponentsMerge,sumComponents[[l]])
                    
                    componentsMultiplexOK <<- T 
                    values$componentsMultiplexOK <- T 
                    #print(listComponentsMerge)
                    #mdebug(listComponentsMerge)    
                }else{                    
                    #calculation per layer. 
                    listComponentsSingleLayer <<- NULL
                    sumComponentsSingleLayer <- NULL
                    listComponentsMergeSingleLayer <<- NULL
                    sumComponentsMergeSingleLayer <<- NULL

                    for(l in 1:(LAYERS)){
                        listComponentsSingleLayer[[l]] <<- data.frame(Layer = rep(l,Nodes))
                        listComponentsSingleLayer[[l]] <<- cbind(listComponentsSingleLayer[[l]],data.frame(Node = 1:Nodes))
                        listComponentsSingleLayer[[l]] <<- cbind(listComponentsSingleLayer[[l]],data.frame(Label=nodesLabel[[l]]))

                        sumComponentsSingleLayer[[l]] <- data.frame(Layer = as.character(l))
                    }

                    listComponentsSingleLayer[[LAYERS+1]] <<- data.frame(Layer = rep("Aggr",Nodes))
                    listComponentsSingleLayer[[LAYERS+1]] <<- cbind(listComponentsSingleLayer[[LAYERS+1]],data.frame(Node = 1:Nodes))
                    listComponentsSingleLayer[[LAYERS+1]] <<- cbind(listComponentsSingleLayer[[LAYERS+1]],data.frame(Label=nodesLabel[[LAYERS+1]]))

                    sumComponentsSingleLayer[[LAYERS+1]] <- data.frame(Layer = "Aggr")
                    
                    for(l in 1:(LAYERS+1)){      
                        #todo: when weak/strong method available for the multiplex, uncomment the line below
                        #wmemb <- components(g[[l]], mode=input$selConnectedComponentsSingleLayerType)
                        wmemb <- components(as.undirected(g[[l]]))$membership

                        maxCom <- max(wmemb)
                        numComms <- maxCom

                        #eventual community merging, if any, here.
                        #todo: this can be improved by finding isolated nodes at the very beginning

                        final.memb <- rep(0, Nodes)
                        idx.nonisolated <- which(degree(g[[l]], mode="total")>0)
                        isolatedNodes <- Nodes - length(idx.nonisolated)
                        final.memb[ idx.nonisolated ] <- wmemb[idx.nonisolated]
                        listComponentsSingleLayer[[l]] <<- cbind(listComponentsSingleLayer[[l]],data.frame(Component=final.memb))

                        numComms <- numComms - isolatedNodes

                        sumComponentsSingleLayer[[l]] <- cbind(sumComponentsSingleLayer[[l]],data.frame(Components = numComms)) 
                    }

                    for(l in 1:(LAYERS+1)){
                        listComponentsMergeSingleLayer <<- rbind(listComponentsMergeSingleLayer,listComponentsSingleLayer[[l]])
                        sumComponentsMergeSingleLayer <<- rbind(sumComponentsMergeSingleLayer,sumComponentsSingleLayer[[l]])
                    }
                    #print(listComponentsMerge)
                    componentsSingleLayerOK <<- T
                    values$componentsSingleLayerOK <- T
                }
    
                progress$set(message = 'Connected Components Completed!', value = 1)
                Sys.sleep(2)
            })  
        }, ignoreInit = TRUE)

        observe({
            values$componentsMultiplexOK
            input$btnRefreshComponentsDiagnostics
            
            if(componentsMultiplexOK){
                matComm <- matrix(nrow=LAYERS, ncol=Nodes, 0)
                Layer <- NULL
                for(l in 1:LAYERS) Layer = c(Layer,as.character(layerLabel[[l]]))
                
                for(l in 1:(LAYERS)){
                    matComm[l,] <- listComponents[[l]]$Component
                }
                
                matComm.df <- as.data.frame(t(matComm))
                colnames(matComm.df) <- Layer

                output$componentsHeatmapUI <- renderUI({
                    d3heatmapOutput("componentsHeatmap",
                                                    width = "100%",
                                                    height = paste0(max(Nodes*3,600),"px")
                        )
                    })
                
                output$componentsHeatmap <- renderD3heatmap({
                    #if(input$btnCalculateComponentsDiagnostics==0 || input$btnImportNetworks == 0 || LAYERS==0)
                    #    return(NULL)

                    if(input$componentsHeatmapShowLabels){
                        rownames(matComm.df) <- nodesLabel[[1]]
                    }else{
                        rownames(matComm.df) <- paste0("n",1:Nodes)
                    }

                    d3heatmap(
                        matComm.df,
                        color = input$selComponentsHeatmapColorPalette,
                        dendrogram = if (input$chkComponentsHeatmapShowDendrogram){"both"}else{"none"}
                        )
                    })

                output$componentsDistributionPlot <- renderChart2({
                    X <- data.frame()
                    categs.tmp <- unique(as.numeric(matComm))
                    for(l in 1:LAYERS){
                        distr.tmp <- as.data.frame(table(matComm.df[,l]),stringsAsFactors=F)
                        distr.tmp <- distr.tmp[as.character(distr.tmp$Var1)!="0",]
                        missing.tmp <- categs.tmp[which(!categs.tmp %in% distr.tmp$Var1)]
                        missing.tmp <- missing.tmp[as.character(missing.tmp)!="0"]                            
                        if(length(missing.tmp)>0){
                            missing.tmp <- data.frame(Var1=missing.tmp, Freq=0,stringsAsFactors=F)
                            #add zero-counts categories. See https://github.com/ramnathv/rCharts/issues/545
                            distr.tmp <- rbind(distr.tmp, missing.tmp)
                        }

                        distr.tmp <- distr.tmp[order(as.numeric(distr.tmp$Var1)),]

                        X <- rbind(X, data.frame(Layer=l, 
                                                                Component=distr.tmp$Var1, 
                                                                Nodes=distr.tmp$Freq)
                                                                )
                    }
                    
                    rplot <- nPlot(Nodes ~ Component, 
                                data = X, group="Layer", type = "multiBarChart")
                    
                    rplot$chart(reduceXTicks = FALSE)
                    rplot$xAxis(staggerLabels = T)
                    rplot$xAxis(axisLabel = 'Component ID')
                    rplot$yAxis(axisLabel = '# Nodes')
                    return(rplot)
                })                

                #Fill the table summarizing the components
                output$componentsTable <- renderGvis({
                    #if(input$btnCalculateComponentsDiagnostics==0 || input$btnImportNetworks == 0 || LAYERS==0)
                    #    return(NULL)
                    
                    gvisTable(listComponentsMerge,options=list(page='enable',pageSize=Nodes))
                    #googleVisCommunityTableOptions())
                })
                
                #Fill the table summarizing the components
                output$componentsSummaryTable <- renderGvis({
                    #if(input$btnCalculateComponentsDiagnostics==0 || input$btnImportNetworks == 0 || LAYERS==0)
                    #    return(NULL)
                    
                    gvisTable(sumComponentsMerge,options=list(page='enable',pageSize=Nodes))
                    #googleVisComponentsSummaryTableOptions())
                })

            }
        })

        observe({
            values$componentsSingleLayerOK
            input$btnRefreshComponentsDiagnostics
            
            if(componentsSingleLayerOK){
                matComm <- matrix(nrow=LAYERS, ncol=Nodes, 0)
                Layer <- NULL
                for(l in 1:LAYERS) Layer = c(Layer,as.character(layerLabel[[l]]))
                
                for(l in 1:(LAYERS)){
                    matComm[l,] <- listComponentsSingleLayer[[l]]$Component
                }
                
                matComm.df <- as.data.frame(t(matComm))
                colnames(matComm.df) <- Layer

                output$componentsHeatmapSingleLayerUI <- renderUI({
                    d3heatmapOutput("componentsHeatmapSingleLayer",
                                                    width = "100%",
                                                    height = paste0(max(Nodes*3,600),"px")
                        )
                    })
                
                output$componentsHeatmapSingleLayer <- renderD3heatmap({
                    #if(input$btnCalculateComponentsDiagnostics==0 || input$btnImportNetworks == 0 || LAYERS==0)
                    #    return(NULL)

                    if(input$componentsHeatmapShowLabels){
                        rownames(matComm.df) <- nodesLabel[[1]]
                    }else{
                        rownames(matComm.df) <- paste0("n",1:Nodes)
                    }

                    d3heatmap(
                        matComm.df,
                        color = input$selComponentsHeatmapColorPalette,
                        dendrogram = if (input$chkComponentsHeatmapShowDendrogram){"both"}else{"none"}
                        )
                    })

                output$componentsDistributionPlotSingleLayer <- renderChart2({
                    X <- data.frame()
                    categs.tmp <- unique(as.numeric(matComm))
                    for(l in 1:LAYERS){
                        distr.tmp <- as.data.frame(table(matComm.df[,l]),stringsAsFactors=F)
                        distr.tmp <- distr.tmp[as.character(distr.tmp$Var1)!="0",]
                        missing.tmp <- categs.tmp[which(!categs.tmp %in% distr.tmp$Var1)]
                        missing.tmp <- missing.tmp[as.character(missing.tmp)!="0"]
                        if(length(missing.tmp)>0){
                            missing.tmp <- data.frame(Var1=missing.tmp, Freq=0,stringsAsFactors=F)
                            #add zero-counts categories. See https://github.com/ramnathv/rCharts/issues/545
                            distr.tmp <- rbind(distr.tmp, missing.tmp)
                        }

                        distr.tmp <- distr.tmp[order(as.numeric(distr.tmp$Var1)),]

                        X <- rbind(X, data.frame(Layer=l, 
                                                                Component=distr.tmp$Var1, 
                                                                Nodes=distr.tmp$Freq)
                                                                )
                    }
                    
                    rplot <- nPlot(Nodes ~ Component, 
                                data = X, group="Layer", type = "multiBarChart")
                    
                    rplot$chart(reduceXTicks = FALSE)
                    rplot$xAxis(staggerLabels = T)
                    rplot$xAxis(axisLabel = 'Component ID')
                    rplot$yAxis(axisLabel = '# Nodes')
                    return(rplot)
                })                

                #Fill the table summarizing the components
                output$componentsTableSingleLayer <- renderGvis({
                    #if(input$btnCalculateComponentsDiagnostics==0 || input$btnImportNetworks == 0 || LAYERS==0)
                     #   return(NULL)
                    
                    gvisTable(listComponentsMergeSingleLayer,options=list(page='enable',pageSize=Nodes))
                    #googleVisCommunityTableOptions())
                })
                
                #Fill the table summarizing the components
                output$componentsSummaryTableSingleLayer <- renderGvis({
                    #if(input$btnCalculateComponentsDiagnostics==0 || input$btnImportNetworks == 0 || LAYERS==0)
                    #    return(NULL)
                    
                    gvisTable(sumComponentsMergeSingleLayer,options=list(page='enable',pageSize=Nodes))
                    #googleVisComponentsSummaryTableOptions())
                })
            }
        })

       ################
       # Triads
       ################ 

        observeEvent(input$btnCalculateTriadsDiagnostics, {
            if(input$btnImportNetworks == 0 ||  LAYERS==0)
                return()
    
            isolate({
                progress <- shiny::Progress$new(session)
                on.exit(progress$close())
                
                ###############
                ## Triadic Closure
                ###############
                progress$set(message = 'Calculating triadic closure...', value = 0.05)
                print("  Finding triangles...")    
                triadsOK <<- T

                if(input$radTriadicClosureAlgorithm=="TRIADIC_CLOSURE_MULTILAYER"){
                    listTriads <<- NULL
                    listTriadsMerge <<- NULL
                    sumTriads <- NULL
                    sumTriadsMerge <<- NULL

                    #the output will be stored in [[l]] for the multiplex and [[LAYERS+1]] for the aggregated.

                    for(l in 1:LAYERS){
                        listTriads[[l]] <<- data.frame(Layer = rep(paste(l,"Multi",sep="-"),Nodes))
                        listTriads[[l]] <<- cbind(listTriads[[l]],data.frame(Node = 1:Nodes))
                        listTriads[[l]] <<- cbind(listTriads[[l]],data.frame(Label=nodesLabel[[l]]))
                    }
                    l <- LAYERS+1
                    listTriads[[l]] <<- data.frame(Layer = rep("Aggr",Nodes))
                    listTriads[[l]] <<- cbind(listTriads[[l]],data.frame(Node = 1:Nodes))
                    listTriads[[l]] <<- cbind(listTriads[[l]],data.frame(Label=nodesLabel[[l]]))

                    sumTriads[[1]] <- data.frame(Layer = "Multi")
                    sumTriads[[2]] <- data.frame(Layer = "Aggr")
                    
                    wclus <- c(GetLocalClustering(SupraAdjacencyMatrix,LAYERS,Nodes))
                    wclus <- t(matrix(rep(wclus,LAYERS), nrow=LAYERS, ncol=Nodes, byrow=T))
                        
                    maxClus <- max(wclus)
                    numTriads <- GetGlobalNumberTriangles(SupraAdjacencyMatrix,Layers,Nodes)

                    wclus_aggregate <- transitivity(g[[LAYERS+1]], type="local")
                    
                    maxClusAggr <- max(wclus_aggregate)
                    numTriadsAggr <- GetGlobalNumberTriangles(AdjMatrix[[LAYERS+1]],1,Nodes)

                    #todo: this can be improved by finding isolated nodes at the very beginning
                    isolatedNodes <- 0
                    for(l in 1:LAYERS){
                        final.val <- rep(0, Nodes)
                        idx.nonisolated <- which(degree(g[[l]], mode="total")>0)
                        isolatedNodes <- isolatedNodes + Nodes - length(idx.nonisolated)
                        final.val[ idx.nonisolated ] <- wclus[idx.nonisolated,l]
                        
                        listTriads[[l]] <<- cbind(listTriads[[l]],data.frame(Transitivity=final.val))
                        listTriadsMerge <<- rbind(listTriadsMerge,listTriads[[l]])
                    }
                    listTriads[[LAYERS+1]] <<- cbind(listTriads[[LAYERS+1]],data.frame(Transitivity=wclus_aggregate))
                    listTriadsMerge <<- rbind(listTriadsMerge,listTriads[[LAYERS+1]])
                    #print(listTriads)
                                        
                    #Multiplex
                    l <- 1
                    sumTriads[[l]] <- cbind(sumTriads[[l]],data.frame(Triangles = numTriads)) 
                    sumTriadsMerge <<- rbind(sumTriadsMerge,sumTriads[[l]])
                    #Aggregate: change numtriads here
                    l <- 2
                    sumTriads[[l]] <- cbind(sumTriads[[l]],data.frame(Triangles = numTriadsAggr))                        
                    sumTriadsMerge <<- rbind(sumTriadsMerge,sumTriads[[l]])
                    
                    triadsMultiplexOK <<- T 
                    values$triadsMultiplexOK <- T 

                    #mdebug(listTriadsMerge)
                }else{                    
                    #calculation per layer. 
                    listTriadsSingleLayer <<- NULL
                    sumTriadsSingleLayer <- NULL
                    listTriadsMergeSingleLayer <<- NULL
                    sumTriadsMergeSingleLayer <<- NULL

                    for(l in 1:(LAYERS)){
                        listTriadsSingleLayer[[l]] <<- data.frame(Layer = rep(l,Nodes))
                        listTriadsSingleLayer[[l]] <<- cbind(listTriadsSingleLayer[[l]],data.frame(Node = 1:Nodes))
                        listTriadsSingleLayer[[l]] <<- cbind(listTriadsSingleLayer[[l]],data.frame(Label=nodesLabel[[l]]))

                        sumTriadsSingleLayer[[l]] <- data.frame(Layer = as.character(l))
                    }

                    listTriadsSingleLayer[[LAYERS+1]] <<- data.frame(Layer = rep("Aggr",Nodes))
                    listTriadsSingleLayer[[LAYERS+1]] <<- cbind(listTriadsSingleLayer[[LAYERS+1]],data.frame(Node = 1:Nodes))
                    listTriadsSingleLayer[[LAYERS+1]] <<- cbind(listTriadsSingleLayer[[LAYERS+1]],data.frame(Label=nodesLabel[[LAYERS+1]]))

                    sumTriadsSingleLayer[[LAYERS+1]] <- data.frame(Layer = "Aggr")
                    
                    for(l in 1:(LAYERS+1)){      

                        wclus <- transitivity(g[[l]], type="local")

                        maxClus <- max(wclus)
                        numTriangles <- GetGlobalNumberTriangles(AdjMatrix[[l]],1,Nodes)

                        #todo: this can be improved by finding isolated nodes at the very beginning

                        final.val <- rep(0, Nodes)
                        idx.nonisolated <- which(degree(g[[l]], mode="total")>0)
                        isolatedNodes <- Nodes - length(idx.nonisolated)
                        final.val[ idx.nonisolated ] <- wclus[idx.nonisolated]
                        listTriadsSingleLayer[[l]] <<- cbind(listTriadsSingleLayer[[l]],data.frame(Transitivity=final.val))

                        sumTriadsSingleLayer[[l]] <- cbind(sumTriadsSingleLayer[[l]],data.frame(Triangles = numTriangles)) 
                    }

                    for(l in 1:(LAYERS+1)){
                        listTriadsMergeSingleLayer <<- rbind(listTriadsMergeSingleLayer,listTriadsSingleLayer[[l]])
                        sumTriadsMergeSingleLayer <<- rbind(sumTriadsMergeSingleLayer,sumTriadsSingleLayer[[l]])
                    }

                    triadsSingleLayerOK <<- T
                    values$triadsSingleLayerOK <- T
                }
    
                progress$set(message = 'Triadic Closure Completed!', value = 1)
                Sys.sleep(2)
            })  
        }, ignoreInit = TRUE)

        observe({
            values$triadsMultiplexOK
            input$btnRefreshTriadsDiagnostics
            
            if(triadsMultiplexOK){
                matTriads <- matrix(nrow=LAYERS, ncol=Nodes, 0)
                Layer <- NULL
                for(l in 1:LAYERS) Layer = c(Layer,as.character(layerLabel[[l]]))
                
                for(l in 1:(LAYERS)){
                    matTriads[l,] <- listTriads[[l]]$Transitivity
                }
                
                matTriads.df <- as.data.frame(t(matTriads))
                colnames(matTriads.df) <- Layer

                output$triadsHeatmapUI <- renderUI({
                    d3heatmapOutput("triadsHeatmap",
                                                    width = "100%",
                                                    height = paste0(max(Nodes*3,600),"px")
                        )
                    })
                
                output$triadsHeatmap <- renderD3heatmap({
                    #if(input$btnCalculateTriadsDiagnostics==0 || input$btnImportNetworks == 0 || LAYERS==0)
                    #    return(NULL)

                    if(input$triadsHeatmapShowLabels){
                        rownames(matTriads.df) <- nodesLabel[[1]]
                    }else{
                        rownames(matTriads.df) <- paste0("n",1:Nodes)
                    }

                    d3heatmap(
                        matTriads.df,
                        color = input$selTriadsHeatmapColorPalette,
                        dendrogram = if (input$chkTriadsHeatmapShowDendrogram){"both"}else{"none"}
                        )
                    })

                output$triadsDistributionPlot <- renderChart2({
                    X <- data.frame()

                    min.val <- round(min(as.numeric(matTriads), na.rm=T),2)
                    max.val <- round(max(as.numeric(matTriads), na.rm=T),2)
                    categs.tmp <- round(seq(min.val,max.val,(max.val-min.val)/15),2)

                    for(l in 1:LAYERS){
                        histog <- hist(matTriads.df[,l], plot=F, breaks=categs.tmp)
                        distr.tmp <- data.frame( Var1=histog$mids, Freq=histog$counts )

                        distr.tmp <- distr.tmp[as.character(distr.tmp$Var1)!="0",]
                        missing.tmp <- categs.tmp[which(!categs.tmp %in% distr.tmp$Var1)]
                        missing.tmp <- missing.tmp[as.character(missing.tmp)!="0"]                            
                        if(length(missing.tmp)>0){
                            missing.tmp <- data.frame(Var1=missing.tmp, Freq=0,stringsAsFactors=F)
                            #add zero-counts categories. See https://github.com/ramnathv/rCharts/issues/545
                            distr.tmp <- rbind(distr.tmp, missing.tmp)
                        }

                        distr.tmp <- distr.tmp[order(as.numeric(distr.tmp$Var1)),]

                        X <- rbind(X, data.frame(Layer=l, 
                                                                Transitivity=distr.tmp$Var1, 
                                                                Nodes=distr.tmp$Freq)
                                                                )
                    }

                    rplot <- nPlot(Nodes ~ Transitivity, 
                                data = X, group="Layer", type = "multiBarChart")
                    
                    rplot$chart(reduceXTicks = FALSE)
                    rplot$xAxis(staggerLabels = T)
                    rplot$xAxis(axisLabel = 'Transitivity')
                    rplot$yAxis(axisLabel = '# Nodes')
                    return(rplot)
                })                

                #Fill the table summarizing the triads
                output$triadsTable <- renderGvis({
                    #if(input$btnCalculateTriadsDiagnostics==0 || input$btnImportNetworks == 0 || LAYERS==0)
                    #    return(NULL)
                    
                    gvisTable(listTriadsMerge,options=list(page='enable',pageSize=Nodes))
                    #googleVisTriadsTableOptions())
                })
                
                #Fill the table summarizing the triads
                output$triadsSummaryTable <- renderGvis({
                    #if(input$btnCalculateTriadsDiagnostics==0 || input$btnImportNetworks == 0 || LAYERS==0)
                    #    return(NULL)
                    
                    gvisTable(sumTriadsMerge,options=list(page='enable',pageSize=Nodes))
                    #googleVisTriadsSummaryTableOptions())
                })

            }
        })

        observe({
            values$triadsSingleLayerOK
            input$btnRefreshTriadsDiagnostics
            
            if(triadsSingleLayerOK){
                matTriads <- matrix(nrow=LAYERS, ncol=Nodes, 0)
                Layer <- NULL
                for(l in 1:LAYERS) Layer = c(Layer,as.character(layerLabel[[l]]))
                
                for(l in 1:(LAYERS)){
                    matTriads[l,] <- listTriadsSingleLayer[[l]]$Transitivity
                }
                
                matTriads.df <- as.data.frame(t(matTriads))
                colnames(matTriads.df) <- Layer

                output$triadsHeatmapSingleLayerUI <- renderUI({
                    d3heatmapOutput("triadsHeatmapSingleLayer",
                                                    width = "100%",
                                                    height = paste0(max(Nodes*3,600),"px")
                        )
                    })
                
                output$triadsHeatmapSingleLayer <- renderD3heatmap({
                    #if(input$btnCalculateTriadsDiagnostics==0 || input$btnImportNetworks == 0 || LAYERS==0)
                    #    return(NULL)

                    if(input$triadsHeatmapShowLabels){
                        rownames(matTriads.df) <- nodesLabel[[1]]
                    }else{
                        rownames(matTriads.df) <- paste0("n",1:Nodes)
                    }

                    d3heatmap(
                        matTriads.df,
                        color = input$selTriadsHeatmapColorPalette,
                        dendrogram = if (input$chkTriadsHeatmapShowDendrogram){"both"}else{"none"}
                        )
                    })

                output$triadsDistributionPlotSingleLayer <- renderChart2({
                    X <- data.frame()
                    min.val <- round(min(as.numeric(matTriads), na.rm=T),2)
                    max.val <- round(max(as.numeric(matTriads), na.rm=T),2)
                    categs.tmp <- round(seq(min.val,max.val,(max.val-min.val)/15),2)

                    for(l in 1:LAYERS){
                        histog <- hist(matTriads.df[,l], plot=F, breaks=categs.tmp)
                        distr.tmp <- data.frame( Var1=histog$mids, Freq=histog$counts )

                        distr.tmp <- distr.tmp[as.character(distr.tmp$Var1)!="0",]
                        missing.tmp <- categs.tmp[which(!categs.tmp %in% distr.tmp$Var1)]
                        missing.tmp <- missing.tmp[as.character(missing.tmp)!="0"]
                        if(length(missing.tmp)>0){
                            missing.tmp <- data.frame(Var1=missing.tmp, Freq=0,stringsAsFactors=F)
                            #add zero-counts categories. See https://github.com/ramnathv/rCharts/issues/545
                            distr.tmp <- rbind(distr.tmp, missing.tmp)
                        }

                        distr.tmp <- distr.tmp[order(as.numeric(distr.tmp$Var1)),]

                        X <- rbind(X, data.frame(Layer=l, 
                                                                Transitivity=distr.tmp$Var1, 
                                                                Nodes=distr.tmp$Freq)
                                                                )
                    }
                    
                    rplot <- nPlot(Nodes ~ Transitivity, 
                                data = X, group="Layer", type = "multiBarChart")
                    
                    rplot$chart(reduceXTicks = FALSE)
                    rplot$xAxis(staggerLabels = T)
                    rplot$xAxis(axisLabel = 'Transitivity')
                    rplot$yAxis(axisLabel = '# Nodes')
                    return(rplot)
                })                

                #Fill the table summarizing the triads
                output$triadsTableSingleLayer <- renderGvis({
                    #if(input$btnCalculateTriadsDiagnostics==0 || input$btnImportNetworks == 0 || LAYERS==0)
                    #    return(NULL)
                    
                    gvisTable(listTriadsMergeSingleLayer,options=list(page='enable',pageSize=Nodes))
                    #googleVisTriadsTableOptions())
                })
                
                #Fill the table summarizing the triads
                output$triadsSummaryTableSingleLayer <- renderGvis({
                    #if(input$btnCalculateTriadsDiagnostics==0 || input$btnImportNetworks == 0 || LAYERS==0)
                    #    return(NULL)
                    
                    gvisTable(sumTriadsMergeSingleLayer,options=list(page='enable',pageSize=Nodes))
                    #googleVisTriadsSummaryTableOptions())
                })
            }
        })
    
      	################################################
      	# Annular Visualization
      	################################################
    
        observeEvent(input$btnAnularViz, {
            if(input$btnCalculateCentralityDiagnostics==0 || input$btnImportNetworks == 0 || input$btnAnularViz==0 ||LAYERS==0)
                return()
        
            isolate({
                progress <- shiny::Progress$new(session)
                on.exit(progress$close())
                muxFeatureDataFrame <- NULL
                monoxFeatureDataFrame <- NULL
                muxFeatureDataFrameArray <- NULL
                monoxFeatureDataFrameArray <- NULL

                if(length(listDiagnosticsSingleLayer)>0 && length(listDiagnostics)>0){
                    for( attrib in colnames(listDiagnostics[[1]]) ){
                        if( (attrib!="Node" && attrib!="Label" && attrib!="Layer") ){
                            if( all(listDiagnosticsSingleLayer[[1]][,attrib]==rep("-",Nodes)) && all(listDiagnostics[[1]][,attrib]!=rep("-",Nodes)) ){
                                diagnosticsSingleLayerOK <<- FALSE
                                break
                            }
                        }
                    }
                }

                if(!diagnosticsSingleLayerOK){
                    print("  Calculation of single-layer descriptors...")
                    listDiagnosticsSingleLayer <<- GetCentralityDataFrameArray("SingleLayer")
                    diagnosticsSingleLayerOK <<- TRUE
                }
                monoxFeatureDataFrameArray <- listDiagnosticsSingleLayer
                #print(monoxFeatureDataFrameArray)

                if(!diagnosticsMultiplexOK){
                    print("  Calculation of multiplex descriptors...")
                    listDiagnostics <<- GetCentralityDataFrameArray("Multiplex")
                    diagnosticsMultiplexOK <<- TRUE
                }
                muxFeatureDataFrameArray <- listDiagnostics
                                
                ####################################
                #Multiplex viz: each ring is a feature
                ####################################
                
                print("  New set: each ring represents a diagnostics")
                
                #we can use just the l = 1 data for the multiplex and l = LAYERS+1 for the aggregate
                
                numFeatures <- length(attributes(muxFeatureDataFrameArray[[1]])$names)
    
                progress$set(message = 'Creating tables...', value = 0.25)
                Bins <- as.numeric(input$txtANULAR_VIZ_BINS)
                
                k <- 1
                sortByFeatureID <- 1
                
                for( attrib in attributes(muxFeatureDataFrameArray[[1]])$names ){
                    if( (attrib!="Node" && attrib!="Label" && attrib!="Layer") && length(unique(muxFeatureDataFrameArray[[1]][,attrib]))>1 ){
                        print(paste("  Processing",attrib,"..."))
                        if(attrib==input$selAnularVizInputFeatureID){
                            print(paste("    Ordering by",attrib))
                            sortByFeatureID <- k
                        }
                        
                        #rescale feature
                        x <- muxFeatureDataFrameArray[[1]][,attrib]
                        x <- x + 1e-3

                        if(!input$chkANULAR_VIZ_LOG){
                            if(max(x,na.rm=T) != min(x,na.rm=T)){
                                x <- floor( 1 + Bins * (x - min(x,na.rm=T))/(max(x,na.rm=T) - min(x,na.rm=T)) )
                            }else{
                                x <- rep(1,Nodes)
                            }
                        }else{
                            if(max(x,na.rm=T) != min(x,na.rm=T)){
                                x <- floor( 1 + Bins * (log(x) - log(min(x,na.rm=T)))/(log(max(x,na.rm=T)) - log(min(x,na.rm=T))) )
                            }else{
                                x <- rep(1,Nodes)
                            }
                        }
                        
                        muxFeatureDataFrame <- rbind(
                                                                    muxFeatureDataFrame,
                                                                    data.frame(
                                                                        feature=rep(k,Nodes),  
                                                                        node=muxFeatureDataFrameArray[[1]][,"Node"],
                                                                        cluster=x,
                                                                        featurelabel=attrib
                                                                        )
                                                                    )
                        k <- k + 1
                    }
                }                            
    
                if(input$radAnularVizCorrelationMethod=="ANULAR_VIZ_CORRELATION_SPEARMAN"){
                    CorrelationMethod <- "spearman"
                }else if(input$radAnularVizCorrelationMethod=="ANULAR_VIZ_CORRELATION_PEARSON"){
                    CorrelationMethod <- "pearson"
                }else if(input$radAnularVizCorrelationMethod=="ANULAR_VIZ_CORRELATION_JSD"){                
                    CorrelationMethod <- "jsd"
                }
                
                if( input$chkANULAR_VIZ_CELL_BORDER==0 ) Border <- NA
                                
                progress$set(message = 'Creating figures...', value = 0.5)
                outfileX <- buildTmpPath("image_annular_multiplex.png")
                
                png(outfileX, width=1200, height=1200)
    
                plotAnularViz(
                            muxFeatureDataFrame,
                            as.numeric(input$txtANULAR_VIZ_RCORE),
                            as.numeric(input$txtANULAR_VIZ_RING_DISPLACEMENT),
                            Border,
                            sortByFeatureID,
                            CorrelationMethod, 
                            input$chkANULAR_VIZ_SHOW_NODE_LABEL,
                            "Multiplex"
                            )
    
                dev.off()
    
                output$anularVizSummaryMuxImage <- renderImage({
                    list(src = outfileX,
                        contentType = 'image/png',
                        width = 550,
                        height = 550,
                        alt = "")
                        }, 
                        deleteFile = FALSE
                    )
                #if(file.exists(outfile2)) file.remove(outfile2)
    
    
                ####################################
                #Layer viz: each ring is a layer, each plot is for a single descriptor
                ####################################
                
                print("  New set: each ring represents a layer")
                
                # l = LAYERS+1 is for the aggregate
    
                progress$set(message = 'Creating tables...', value = 0.75)
                max_plots <- 0
                for( attrib in attributes(monoxFeatureDataFrameArray[[1]])$names ){
                    if( (attrib!="Node" && attrib!="Label" && attrib!="Layer") && length(unique(monoxFeatureDataFrameArray[[1]][,attrib]))>1 ){
                        print(paste("  Processing",attrib,"..."))
                        monoxFeatureDataFrame <- NULL
                        max_plots <- max_plots + 1
                        
                        #we have a feature, let's read the values for each layer
                        for(l in 1:(LAYERS+1)){
                            #rescale feature

                            x <- monoxFeatureDataFrameArray[[l]][,attrib]
                            x <- x + 1e-3

                            if(!input$chkANULAR_VIZ_LOG){
                                if(max(x,na.rm=T) != min(x,na.rm=T)){
                                    x <- floor( 1 + Bins * (x - min(x,na.rm=T))/(max(x,na.rm=T) - min(x,na.rm=T)) )
                                }else{
                                    x <- rep(1,Nodes)
                                }
                            }else{
                                if(max(x,na.rm=T) != min(x,na.rm=T)){
                                    x <- floor( 1 + Bins * (log(x) - log(min(x,na.rm=T)))/(log(max(x,na.rm=T)) - log(min(x,na.rm=T))) )
                                }else{
                                    x <- rep(1,Nodes)
                                }
                            }
                        
                            if(l<LAYERS+1){
                                thislabel <- layerLabel[[l]]#paste("Layer",l)
                            }else{
                                thislabel <- "Aggregate"
                            }
                        
                            monoxFeatureDataFrame <- rbind(
                                                                        monoxFeatureDataFrame,
                                                                        data.frame(
                                                                            feature=rep(l,Nodes),  
                                                                            node=monoxFeatureDataFrameArray[[1]][,"Node"],
                                                                            cluster=x,
                                                                            featurelabel=thislabel
                                                                            )
                                                                        )
                        }
                        
                        #add the values from the multiplex
                        #rescale feature
                        x <- muxFeatureDataFrameArray[[1]][,attrib]
                        x <- x + 1e-3
            
                        if(!input$chkANULAR_VIZ_LOG){
                            if(max(x,na.rm=T) != min(x,na.rm=T)){
                                x <- floor( 1 + Bins * (x - min(x,na.rm=T))/(max(x,na.rm=T) - min(x,na.rm=T)) )
                            }else{
                                x <- rep(1,Nodes)
                            }
                        }else{
                            if(max(x,na.rm=T) != min(x,na.rm=T)){
                                x <- floor( 1 + Bins * (log(x) - log(min(x,na.rm=T)))/(log(max(x,na.rm=T)) - log(min(x,na.rm=T))) )
                            }else{
                                x <- rep(1,Nodes)
                            }
                        }
                    
                        thislabel <- "Multiplex"
                    
                        monoxFeatureDataFrame <- rbind(
                                                                    monoxFeatureDataFrame,
                                                                    data.frame(
                                                                        feature=rep(LAYERS+2,Nodes),  
                                                                        node=monoxFeatureDataFrameArray[[1]][,"Node"],
                                                                        cluster=x,
                                                                        featurelabel=thislabel
                                                                        )
                                                                    )                    
                        
                        #print(monoxFeatureDataFrame)
                        
                        #build the plot                    
                        progress$set(message = 'Creating figures...', value = 0.9)
                        outfileY <- buildTmpPath(paste("image_annular_",attrib,".png",sep=""))
                        
                        png(outfileY, width=1200, height=1200)
                        
                        Title <- attrib
                        if(input$selAnularVizInputLayerID=="Multiplex"){
                            sortByLayerID <- LAYERS+2
                            print(paste("    Ordering by","Multiplex"))
                        }else if(input$selAnularVizInputLayerID=="Aggregate"){
                            sortByLayerID <- LAYERS+1
                            print(paste("    Ordering by","Aggregate"))
                        }else if(input$selAnularVizInputLayerID=="Max entropy"){
                            sortByLayerID <- 0
                            print(paste("    Ordering by","Max entropy"))
                        }else{
                            #it is by one of the layers
                            sortByLayerID <- strsplit(input$selAnularVizInputLayerID," ")[[1]][2]
                            print(paste("    Ordering by layer",sortByLayerID))
                        }
                        
                        plotAnularViz(
                                    monoxFeatureDataFrame,
                                    as.numeric(input$txtANULAR_VIZ_RCORE),
                                    as.numeric(input$txtANULAR_VIZ_RING_DISPLACEMENT),
                                    Border,
                                    sortByLayerID,
                                    CorrelationMethod, 
                                    input$chkANULAR_VIZ_SHOW_NODE_LABEL,
                                    Title
                                    )
            
                        dev.off()
                    }
                } 
                
                #http://stackoverflow.com/questions/15875786/dynamically-add-plots-to-web-page-using-shiny
                output$outputAnularVizImages <- renderUI({                        
                    plot_output_list <- lapply(1:max_plots, function(i) {
                        plotname <- paste("plot", i, sep="")
                        imageOutput(plotname,width = "100%", height = "100%")
                    })
                    
                    # Convert the list to a tagList - this is necessary for the list of items
                    # to display properly.
                    do.call(tagList, plot_output_list)
                })
    
                i <- 1
                for( attrib in attributes(monoxFeatureDataFrameArray[[1]])$names ){
                    if( (attrib!="Node" && attrib!="Label" && attrib!="Layer") && length(unique(monoxFeatureDataFrameArray[[1]][,attrib]))>1 ){
                        local({
                            #print(i)
                            my_i <- i
                            outfileY <- buildTmpPath(paste("image_annular_",attrib,".png",sep=""))
                            plotname <- paste("plot", my_i, sep="")
                            
                            output[[plotname]] <- renderImage({
                                list(src = outfileY,
                                    contentType = 'image/png',
                                    width = 550,
                                    height = 550,
                                    alt = "")
                                    }, 
                                    deleteFile = FALSE
                                )
                        })
                        i <- i + 1
                    }
                }
                
                #output$anularVizSummaryDescriptorImage <- renderImage({
                #    list(src = outfileY,
                #        contentType = 'image/png',
                #        width = 550,
                #        height = 550,
                #        alt = "")
                #        }, 
                #        deleteFile = FALSE
                #    )
                #if(file.exists(outfile2)) file.remove(outfile2)
    
                ###############################################
    
                progress$set(message = 'Annular Viz Completed!', value = 1)
                Sys.sleep(2)
            })  
        }, ignoreInit = TRUE)

      	################################################
      	# Statistics plots
      	################################################

        observeEvent(input$btnMeanPathLengthStatistics, {
            if(input$btnImportNetworks == 0 ||  LAYERS==0)
                return()
    
            isolate({
                progress <- shiny::Progress$new(session)
                on.exit(progress$close())
    
                progress$set(message = 'Calculating...', value = 0.05)

                Bins <- LAYERS

                v.x <- c()
                v.y <- c()
                for(l in 1:LAYERS){
                    v.y <- c(v.y, average.path.length(g[[l]], directed=DIRECTED, unconnected=T) )
                    v.x <- c(v.x, layerLabel[[l]])
                }

                if(input$meanPathLengthStatisticsLogy){
                    v.y <- log10(v.y)
                    if(any(is.infinite(v.y))){
                        v.y[is.infinite(v.y)] <- 0
                    }
                }
                
                X <- data.frame(Var1 = v.x, MeanPathLength = v.y)
        
                output$meanPathLengthStatisticsPlot <- renderChart2({
                    rplot <- nPlot(MeanPathLength ~ Var1, 
                                data = X, type = "multiBarChart")
                 
                    rplot$chart(reduceXTicks = FALSE)
                    rplot$xAxis(staggerLabels = F, rotateLabels=-90)
                    if(input$meanPathLengthStatisticsLogy){
                        rplot$yAxis(axisLabel="log10 Mean Path Length")
                    }else{
                        rplot$yAxis(axisLabel="Mean Path Length")
                    }

                    return(rplot)
                })                

                progress$set(message = 'Completed!', value = 1)
                Sys.sleep(2)
            })
        }, ignoreInit = TRUE)
        
        observeEvent(input$btnDiameterStatistics, {
            if(input$btnImportNetworks == 0 ||  LAYERS==0)
                return()
    
            isolate({
                progress <- shiny::Progress$new(session)
                on.exit(progress$close())
    
                progress$set(message = 'Calculating...', value = 0.05)

                Bins <- LAYERS

                v.x <- c()
                v.y <- c()
                for(l in 1:LAYERS){
                    v.y <- c(v.y, diameter(g[[l]], directed=DIRECTED, unconnected=T) )
                    v.x <- c(v.x, layerLabel[[l]])
                }

                if(input$diameterStatisticsLogy){
                    v.y <- log10(v.y)
                    if(any(is.infinite(v.y))){
                        v.y[is.infinite(v.y)] <- 0
                    }
                }
                
                X <- data.frame(Var1 = v.x, Diameter = v.y)
        
                output$diameterStatisticsPlot <- renderChart2({
                    rplot <- nPlot(Diameter ~ Var1, 
                                data = X, type = "multiBarChart")
                 
                    rplot$chart(reduceXTicks = FALSE)
                    rplot$xAxis(staggerLabels = F, rotateLabels=-90)
                    if(input$diameterStatisticsLogy){
                        rplot$yAxis(axisLabel="log10 Diameter")
                    }else{
                        rplot$yAxis(axisLabel="Diameter")
                    }

                    return(rplot)
                })                

                progress$set(message = 'Completed!', value = 1)
                Sys.sleep(2)
            })
        }, ignoreInit = TRUE)

        observeEvent(input$btnComponentsStatistics, {
            if(input$btnImportNetworks == 0 ||  LAYERS==0)
                return()
    
            isolate({
                progress <- shiny::Progress$new(session)
                on.exit(progress$close())
    
                progress$set(message = 'Calculating...', value = 0.05)

                Bins <- LAYERS

                v.x <- c()
                v.y <- c()
                v.y2 <- c()
                for(l in 1:LAYERS){
                    v.y <- c(v.y, count_components(g[[l]], mode="weak") )
                    v.y2 <- c(v.y2, count_components(g[[l]], mode="strong") )
                    v.x <- c(v.x, layerLabel[[l]])
                }

                if(input$componentsStatisticsLogy){
                    v.y <- log10(v.y)
                    if(any(is.infinite(v.y))){
                        v.y[is.infinite(v.y)] <- 0
                    }
                    v.y2 <- log10(v.y2)
                    if(any(is.infinite(v.y2))){
                        v.y2[is.infinite(v.y2)] <- 0
                    }
                }
                
                X <- data.frame(Var1 = v.x, Type = "Weak", Components=v.y)
                X <- rbind(X, data.frame(Var1 = v.x, Type = "Strong", Components=v.y2))
                
        
                output$componentsStatisticsPlot <- renderChart2({
                    rplot <- nPlot(Components ~ Var1, 
                                data = X, group="Type", type = "multiBarChart")
                 
                    rplot$chart(reduceXTicks = FALSE)
                    rplot$xAxis(staggerLabels = F, rotateLabels=-90)
                    if(input$diameterStatisticsLogy){
                        rplot$yAxis(axisLabel="log10 Components")
                    }else{
                        rplot$yAxis(axisLabel="Components")
                    }

                    return(rplot)
                })                

                progress$set(message = 'Completed!', value = 1)
                Sys.sleep(2)
            })
        }, ignoreInit = TRUE)

        observeEvent(input$btnDensityStatistics, {
            if(input$btnImportNetworks == 0 ||  LAYERS==0)
                return()
    
            isolate({
                progress <- shiny::Progress$new(session)
                on.exit(progress$close())
    
                progress$set(message = 'Calculating...', value = 0.05)

                Bins <- LAYERS

                v.x <- c()
                v.y <- c()
                for(l in 1:LAYERS){
                    tmp.deg <- degree(g[[l]], mode="all")
                    v.y <- c(v.y, length(E(g[[l]]))/sum(tmp.deg>0))
                    v.x <- c(v.x, layerLabel[[l]])
                }

                if(input$densityStatisticsLogy){
                    v.y <- log10(v.y)
                    if(any(is.infinite(v.y))){
                        v.y[is.infinite(v.y)] <- 0
                    }
                }
                
                X <- data.frame(Var1 = v.x, Density = v.y)
        
                output$densityStatisticsPlot <- renderChart2({
                    rplot <- nPlot(Density ~ Var1, 
                                data = X, type = "multiBarChart")
                 
                    rplot$chart(reduceXTicks = FALSE)
                    rplot$xAxis(staggerLabels = F, rotateLabels=-90)
                    if(input$densityStatisticsLogy){
                        rplot$yAxis(axisLabel="log10 Density")
                    }else{
                        rplot$yAxis(axisLabel="Density")
                    }

                    return(rplot)
                })                

                progress$set(message = 'Completed!', value = 1)
                Sys.sleep(2)
            })
        }, ignoreInit = TRUE)

        observeEvent(input$btnNodeStatistics, {
            if(input$btnImportNetworks == 0 ||  LAYERS==0)
                return()
    
            isolate({
                progress <- shiny::Progress$new(session)
                on.exit(progress$close())
    
                progress$set(message = 'Calculating...', value = 0.05)

                Bins <- LAYERS

                v.x <- c()
                v.y <- c()
                for(l in 1:LAYERS){
                    tmp.deg <- degree(g[[l]], mode="all")
                    v.y <- c(v.y, sum(tmp.deg>0))
                    v.x <- c(v.x, layerLabel[[l]])
                }

                if(input$nodeStatisticsLogy){
                    v.y <- log10(v.y)
                    if(any(is.infinite(v.y))){
                        v.y[is.infinite(v.y)] <- 0
                    }
                }
                
                X <- data.frame(Var1 = v.x, Counts = v.y)
        
                output$nodeStatisticsPlot <- renderChart2({
                    rplot <- nPlot(Counts ~ Var1, 
                                data = X, type = "multiBarChart")
                 
                    rplot$chart(reduceXTicks = FALSE)
                    rplot$xAxis(staggerLabels = F, rotateLabels=-90)
                    if(input$nodeStatisticsLogy){
                        rplot$yAxis(axisLabel="log10 Counts")
                    }else{
                        rplot$yAxis(axisLabel="Counts")
                    }

                    return(rplot)
                })                

                progress$set(message = 'Completed!', value = 1)
                Sys.sleep(2)
            })
        }, ignoreInit = TRUE)

        observeEvent(input$btnEdgeStatistics, {
            if(input$btnImportNetworks == 0 ||  LAYERS==0)
                return()
    
            isolate({
                progress <- shiny::Progress$new(session)
                on.exit(progress$close())
    
                progress$set(message = 'Calculating...', value = 0.05)

                Bins <- LAYERS

                v.x <- c()
                v.y <- c()
                for(l in 1:LAYERS){
                    v.y <- c(v.y, length(E(g[[l]])))
                    v.x <- c(v.x, layerLabel[[l]])
                }

                if(input$edgeStatisticsLogy){
                    v.y <- log10(v.y)
                    if(any(is.infinite(v.y))){
                        v.y[is.infinite(v.y)] <- 0
                    }
                }
                
                X <- data.frame(Var1 = v.x, Counts = v.y)
        
                output$edgeStatisticsPlot <- renderChart2({
                    rplot <- nPlot(Counts ~ Var1, 
                                data = X, type = "multiBarChart")
                 
                    rplot$chart(reduceXTicks = FALSE)
                    rplot$xAxis(staggerLabels = F, rotateLabels=-90)
                    if(input$edgeStatisticsLogy){
                        rplot$yAxis(axisLabel="log10 Counts")
                    }else{
                        rplot$yAxis(axisLabel="Counts")
                    }

                    return(rplot)
                })                

                progress$set(message = 'Completed!', value = 1)
                Sys.sleep(2)
            })
        }, ignoreInit = TRUE)
    
      	################################################
      	# Diagnostics centrality plots
      	################################################

        observeEvent(input$btnCentralityDiagnosticsAnalysis, {
            if(input$btnCalculateCentralityDiagnostics == 0 || input$btnImportNetworks == 0 ||  LAYERS==0)
                return()
    
            isolate({
                progress <- shiny::Progress$new(session)
                on.exit(progress$close())
    
                progress$set(message = 'Calculating...', value = 0.05)

                this.descriptor <- input$selDiagnosticsCentralityVizID

                featureDataFrameList <- list()
                layersToInclude <- as.numeric(strsplit(input$txtDiagnosticsCentralityStructureLayer,",")[[1]])

                if(length(listDiagnosticsSingleLayer)>0 && length(listDiagnostics)>0){
                    for( attrib in colnames(listDiagnostics[[1]]) ){

                        if( (attrib!="Node" && attrib!="Label" && attrib!="Layer") ){
                            if( all(listDiagnosticsSingleLayer[[1]][,attrib]==rep("-",Nodes)) && all(listDiagnostics[[1]][,attrib]!=rep("-",Nodes)) ){
                                #print(listDiagnosticsSingleLayer[[1]][,attrib]) 
                                #print(listDiagnostics[[1]][,attrib])
                                diagnosticsSingleLayerOK <<- FALSE
                                break
                            }
                        }
                    }
                }

                if(input$chkCentralityAnalysisStructureMultiplex){
                    if(!diagnosticsMultiplexOK){
                        print("  Calculation of multiplex descriptors...")
                        listDiagnostics <<- GetCentralityDataFrameArray("Multiplex")
                        diagnosticsMultiplexOK <<- TRUE
                    }
                    #same for all layers, just pick the 1
                    featureDataFrameList[["Multi"]] <- listDiagnostics[[1]]
                }

                if(input$chkCentralityAnalysisStructureLayer){
                    if(!diagnosticsSingleLayerOK){
                        print("  Calculation of single-layer descriptors...")
                        listDiagnosticsSingleLayer <<- GetCentralityDataFrameArray("SingleLayer")
                        diagnosticsSingleLayerOK <<- TRUE
                    }
                    
                    for(l in layersToInclude){
                        featureDataFrameList[[ paste0("L",l) ]] <- listDiagnosticsSingleLayer[[ l ]]
                    }
                }
    
                if(input$chkCentralityAnalysisStructureAggregate){
                    if(!diagnosticsMultiplexOK){
                        print("  Calculation of multiplex descriptors...")
                        listDiagnostics <<- GetCentralityDataFrameArray("Multiplex")
                        diagnosticsMultiplexOK <<- TRUE
                    }
                    featureDataFrameList[["Aggr"]] <- listDiagnostics[[LAYERS+1]]
                }
                
                if(input$radDiagnosticsCentralityType=="DIAGNOSTICS_ANALYSIS_TOPRANKED"){
                    orderingIdx <- 1:Nodes
                    X <- data.frame()
                    Bins <- as.numeric(input$txtDiagnosticsCentralityTopRankedBins)

                    if(input$chkCentralityAnalysisStructureMultiplex){
                        v.x <- as.numeric(featureDataFrameList[["Multi"]][this.descriptor][,1])
                        if(input$centralityAnalysisTopRankedLog){
                            v.x <- log10(v.x)
                            if( any(is.infinite(v.x)) ){
                                v.x[is.infinite(v.x)] <- 0
                            }
                        }
                        
                        ordering <- sort(v.x, decreasing=T, index.return=T)
                        v.x <- ordering$x
                        orderingIdx <- ordering$ix
                        X <- rbind(X, data.frame(Node=nodesLabel[[1]][orderingIdx[1:Bins]], Var1=v.x[1:Bins], Type="Multilayer"))
                    }
                    if(input$chkCentralityAnalysisStructureAggregate){
                        v.x <- as.numeric(featureDataFrameList[["Aggr"]][this.descriptor][,1])
                        if(input$centralityAnalysisTopRankedLog){
                            v.x <- log10(v.x)
                            if( any(is.infinite(v.x)) ){
                                v.x[is.infinite(v.x)] <- 0
                            }
                        }

                        v.x <- v.x[orderingIdx]
                        X <- rbind(X, data.frame(Node=nodesLabel[[1]][orderingIdx[1:Bins]], Var1=v.x[1:Bins], Type="Aggregate"))
                    }                    
                    if(input$chkCentralityAnalysisStructureLayer){
                        for(l in layersToInclude){
                            v.x <- as.numeric(featureDataFrameList[[ paste0("L",l) ]][this.descriptor][,1])
                            if(input$centralityAnalysisTopRankedLog){
                                v.x <- log10(v.x)
                                if( any(is.infinite(v.x)) ){
                                    v.x[is.infinite(v.x)] <- 0
                                }
                            }
                            v.x <- v.x[orderingIdx]
                            X <- rbind(X, data.frame(Node=nodesLabel[[1]][orderingIdx[1:Bins]], Var1=v.x[1:Bins], Type=layerLabel[[l]]))
                        }
                    }                    
                    
                    #print(X)

                    output$centralityAnalysisPlot <- renderChart2({
                        rplot <- nPlot(Var1 ~ Node, 
                                 group = "Type",
                                 data = X, type = "multiBarHorizontalChart")

                        rplot$xAxis(axisLabel="")

                        if(input$centralityAnalysisTopRankedLog){
                            rplot$yAxis(axisLabel=paste0("log10 ", this.descriptor), width = 40)
                        }else{
                            rplot$yAxis(axisLabel=this.descriptor, width = 40)
                        }
                        rplot$chart(margin = list(left = 120))
                        
                        #rplot$chart(reduceXTicks = FALSE)
                        #rplot$xAxis(staggerLabels = TRUE)

                        return(rplot)
                    })
                }
                
                if(input$radDiagnosticsCentralityType=="DIAGNOSTICS_ANALYSIS_DISTRIBUTION"){

                    X <- data.frame()
                    Bins <- as.numeric(input$txtDiagnosticsCentralityDistributionBins)
                    
                    if(input$chkCentralityAnalysisStructureMultiplex){
                        v.x <- as.numeric(featureDataFrameList[["Multi"]][this.descriptor][,1])
                        if(input$centralityAnalysisDistributionLogx){
                            v.x <- log10(v.x)
                            if( any(is.infinite(v.x)) ){
                                v.x[is.infinite(v.x)] <- 0
                            }
                        }
                        x.min <- min(v.x,na.rm=T)
                        x.max <- max(v.x,na.rm=T)
                        x.step <- (x.max-x.min)/Bins
                        freq <- as.data.frame(table(cut(v.x, breaks=seq(x.min,x.max,x.step))))
                        if(input$centralityAnalysisDistributionLogy){
                            freq$Freq <- log10(freq$Freq)
                            if(any(is.infinite(freq$Freq))){
                                freq[is.infinite(freq$Freq),]$Freq <- 0
                            }
                        }
                        freq$Var1 <- seq(x.min,x.max,x.step)[1:Bins]
                        freq$Type <- "Multilayer"
                        X <- rbind(X, freq)
                    }
                    if(input$chkCentralityAnalysisStructureAggregate){
                        v.x <- as.numeric(featureDataFrameList[["Aggr"]][this.descriptor][,1])
                        if(input$centralityAnalysisDistributionLogx){
                            v.x <- log10(v.x)
                            if( any(is.infinite(v.x)) ){
                                v.x[is.infinite(v.x)] <- 0
                            }
                        }
                        x.min <- min(v.x,na.rm=T)
                        x.max <- max(v.x,na.rm=T)
                        x.step <- (x.max-x.min)/Bins
                        freq <- as.data.frame(table(cut(v.x, breaks=seq(x.min,x.max,x.step))))
                        if(input$centralityAnalysisDistributionLogy){
                            freq$Freq <- log10(freq$Freq)
                            if(any(is.infinite(freq$Freq))){
                                freq[is.infinite(freq$Freq),]$Freq <- 0
                            }
                        }
                        freq$Var1 <- seq(x.min,x.max,x.step)[1:Bins]
                        freq$Type <- "Aggregate"
                        X <- rbind(X, freq)
                    }                    
                    if(input$chkCentralityAnalysisStructureLayer){
                        for(l in layersToInclude){
                            v.x <- as.numeric(featureDataFrameList[[ paste0("L",l) ]][this.descriptor][,1])
                            if(input$centralityAnalysisDistributionLogx){
                                v.x <- log10(v.x)
                                if( any(is.infinite(v.x)) ){
                                    v.x[is.infinite(v.x)] <- 0
                                }
                            }
                            x.min <- min(v.x,na.rm=T)
                            x.max <- max(v.x,na.rm=T)
                            x.step <- (x.max-x.min)/Bins
                            freq <- as.data.frame(table(cut(v.x, breaks=seq(x.min,x.max,x.step))))
                            if(input$centralityAnalysisDistributionLogy){
                                freq$Freq <- log10(freq$Freq)
                                if(any(is.infinite(freq$Freq))){
                                    freq[is.infinite(freq$Freq),]$Freq <- 0
                                }
                            }
                            freq$Var1 <- seq(x.min,x.max,x.step)[1:Bins]
                            freq$Type <- layerLabel[[l]]
                            X <- rbind(X, freq)    
                        }
                    }                    
                    
                    #print(X)
            
                    output$centralityAnalysisPlot <- renderChart2({
                        rplot <- nPlot(Freq ~ Var1, 
                                 group = "Type",
                                 data = X, type = "multiBarChart")

                        if(input$centralityAnalysisDistributionLogy){
                            rplot$yAxis(axisLabel="log10 Counts")
                        }else{
                            rplot$yAxis(axisLabel="Counts")
                        }
                        if(input$centralityAnalysisDistributionLogx){
                            rplot$xAxis(axisLabel=paste0("log10 ", this.descriptor))
                        }else{
                            rplot$xAxis(axisLabel=this.descriptor)
                        }
                        rplot$chart(reduceXTicks = FALSE)
                        rplot$xAxis(staggerLabels = F, rotateLabels=-90)

                        return(rplot)
                    })

                }
                
                if(input$radDiagnosticsCentralityType=="DIAGNOSTICS_ANALYSIS_SCATTER"){
                    this.descriptor.x <- this.descriptor
                    this.descriptor.y <- input$selDiagnosticsCentralityVizScatterID                    
                    this.descriptor.color <- input$selDiagnosticsCentralityVizScatterColorID
                    this.descriptor.size <- input$selDiagnosticsCentralityVizScatterSizeID
                    X <- data.frame()
                    if(input$chkCentralityAnalysisStructureMultiplex){
                        v.x <- as.numeric(featureDataFrameList[["Multi"]][this.descriptor.x][,1])
                        v.y <- as.numeric(featureDataFrameList[["Multi"]][this.descriptor.y][,1])

                        if(input$centralityAnalysisScatterLogx){
                            v.x <- log10(v.x)
                            if( any(is.infinite(v.x)) ){
                                v.x[is.infinite(v.x)] <- 0
                            }
                        }
                        if(input$centralityAnalysisScatterLogy){
                            v.y <- log10(v.y)
                            if( any(is.infinite(v.y)) ){
                                v.y[is.infinite(v.y)] <- 0
                            }
                        }

                        v.radius <- 1
                        if(input$selDiagnosticsCentralityVizScatterSizeID!="Uniform"){
                            this.descriptor.radius <- input$selDiagnosticsCentralityVizScatterSizeID
                            v.radius <- as.numeric(featureDataFrameList[["Multi"]][this.descriptor.radius][,1])
    
                            if(input$centralityAnalysisScatterLogRadius){
                                v.radius <- log10(v.radius)
                                if( any(is.infinite(v.radius)) ){
                                    v.radius[is.infinite(v.radius)] <- 0
                                }
                            }
                        }
                        
                        X <- rbind(X, data.frame(Node=nodesLabel[[1]], Var1=v.x, Var2=v.y, Radius=v.radius, Type="Multilayer"))
                    }
                    if(input$chkCentralityAnalysisStructureAggregate){
                        v.x <- as.numeric(featureDataFrameList[["Aggr"]][this.descriptor.x][,1])
                        v.y <- as.numeric(featureDataFrameList[["Aggr"]][this.descriptor.y][,1])

                        if(input$centralityAnalysisScatterLogx){
                            v.x <- log10(v.x)
                            if( any(is.infinite(v.x)) ){
                                v.x[is.infinite(v.x)] <- 0
                            }
                        }
                        if(input$centralityAnalysisScatterLogy){
                            v.y <- log10(v.y)
                            if( any(is.infinite(v.y)) ){
                                v.y[is.infinite(v.y)] <- 0
                            }
                        }

                        v.radius <- 1
                        if(input$selDiagnosticsCentralityVizScatterSizeID!="Uniform"){
                            this.descriptor.radius <- input$selDiagnosticsCentralityVizScatterSizeID
                            v.radius <- as.numeric(featureDataFrameList[["Aggr"]][this.descriptor.radius][,1])
    
                            if(input$centralityAnalysisScatterLogRadius){
                                v.radius <- log10(v.radius)
                                if( any(is.infinite(v.radius)) ){
                                    v.radius[is.infinite(v.radius)] <- 0
                                }
                            }
                        }
                        
                        X <- rbind(X, data.frame(Node=nodesLabel[[1]], Var1=v.x, Var2=v.y, Radius=v.radius, Type="Aggregate"))
                    }
                    if(input$chkCentralityAnalysisStructureLayer){
                        for(l in layersToInclude){
                            v.x <- as.numeric(featureDataFrameList[[ paste0("L",l) ]][this.descriptor.x][,1])
                            v.y <- as.numeric(featureDataFrameList[[ paste0("L",l) ]][this.descriptor.y][,1])

                            if(input$centralityAnalysisScatterLogx){
                                v.x <- log10(v.x)
                                if( any(is.infinite(v.x)) ){
                                    v.x[is.infinite(v.x)] <- 0
                                }
                            }
                            if(input$centralityAnalysisScatterLogy){
                                v.y <- log10(v.y)
                                if( any(is.infinite(v.y)) ){
                                    v.y[is.infinite(v.y)] <- 0
                                }
                            }

                            v.radius <- 1
                            if(input$selDiagnosticsCentralityVizScatterSizeID!="Uniform"){
                                this.descriptor.radius <- input$selDiagnosticsCentralityVizScatterSizeID
                                v.radius <- as.numeric(featureDataFrameList[[ paste0("L",l) ]][this.descriptor.radius][,1])
        
                                if(input$centralityAnalysisScatterLogRadius){
                                    v.radius <- log10(v.radius)
                                    if( any(is.infinite(v.radius)) ){
                                        v.radius[is.infinite(v.radius)] <- 0
                                    }
                                }
                            }
                            
                            X <- rbind(X, data.frame(Node=nodesLabel[[1]], Var1=v.x, Var2=v.y, Radius=v.radius, Type=layerLabel[[l]]))
                        }
                    }

        
                    output$centralityAnalysisPlot <- renderChart2({
                        rplot <- nPlot(Var2 ~ Var1, 
                                 group = "Type", opacity=list(const=as.numeric(input$txtDiagnosticsCentralityVizScatterColorTransparency)), 
                                 data = X, type = "scatterChart")

                        if(input$centralityAnalysisScatterLogy){
                            rplot$yAxis(axisLabel=paste0("log10 ", this.descriptor.y))
                        }else{
                            rplot$yAxis(axisLabel=this.descriptor.y)
                        }
                        if(input$centralityAnalysisScatterLogx){
                            rplot$xAxis(axisLabel=paste0("log10 ", this.descriptor.x))
                        }else{
                            rplot$xAxis(axisLabel=this.descriptor.x)
                        }

                        rplot$chart(size = '#! function(d){return d.Radius} !#')
                        rplot$chart(tooltipContent = "#! function(key, x, y, e){ return  '<h3>' + e.point.Node + '</h3> ' + '<br><b>Type:</b> ' + key } !#")
                        rplot$chart(forceY=c(0.9*floor(min(X$Var2)),1.1*floor(max(X$Var2))), 
                                          forceX=c(0.9*floor(min(X$Var1)),1.1*floor(max(X$Var1))))
                                          
                        return(rplot)
                    })

                }
    
                progress$set(message = 'Diagnostics analysis Completed!', value = 1)
                Sys.sleep(2)
            })  
        }, ignoreInit = TRUE)

                
      	################################################
      	# Reducibility
      	################################################
    
        observeEvent(input$btnCalculateReducibility, {
            if(input$btnImportNetworks == 0 ||  LAYERS==0)
                return()
    
            isolate({
                progress <- shiny::Progress$new(session)
                on.exit(progress$close())
    
                progress$set(message = 'Calculating Redundancy...', value = 0.05)
                
                if(sum(SupraAdjacencyMatrix-t(SupraAdjacencyMatrix))>0){
                    cat("  Network is directed. Transforming to undirected to apply structural reducibility.\n")
                }
                
                muxType <- ""
                if(input$radMultiplexType=="MULTIPLEX_IS_ORDERED"){
                    muxType <- "Ordinal"
                }
                if(input$radMultiplexType=="MULTIPLEX_IS_CATEGORICAL"){
                    muxType <- "Categorical"
                }
                
                listReducibility <<- GetMultilayerReducibility(SupraAdjacencyMatrix,LAYERS,Nodes,input$selReducibilityClusterMethod, muxType)
                
                progress$set(message = 'Hierarchical clustering...', value = 0.6)


#            Layer <- c()
#            for(l in 1:LAYERS) Layer = c(Layer,as.character(layerLabel[[l]]))
#
#                outfile7 <- buildTmpPath("image_dendrogram.png")
#                png(outfile7, width=650, height=650)
#                plot(hclust(as.dist(listReducibility$JSD),
#                       method=input$selReducibilityClusterMethod),
#                       col = "#1F77B4", col.main = "#1F77B4", col.lab = "#E08400", 
#                       col.axis = "#E08400", lwd = 2, 
#                       labels=Layer,
#                       cex=as.numeric(input$txtREDUCIBILITY_HEATMAP_FONT_SIZE),
#                       main="Reducibility Dendrogram",
#                       sub="", 
#                       xlab="")
#                dev.off()
#    
#                output$reducibilityDendrogramSummaryImage <- renderImage({
#                    list(src = outfile7,
#                        contentType = 'image/png',
#                        width = 650,
#                        height = 650,
#                        alt = "")
#                        }, 
#                        deleteFile = FALSE
#                    )
    
                progress$set(message = 'Reducibility analysis Completed!', value = 1)
                Sys.sleep(2)
            })  
        }, ignoreInit = TRUE)

        output$reducibilityHeatmapUI <- renderUI({
            d3heatmapOutput("reducibilityHeatmap",
                                            width = "650px",
                                            height = "650px"
                )
            })

        output$reducibilityQualityFunction <- renderChart2({
            data <- data.frame(Step=0:(LAYERS-1), Q=listReducibility$gQualityFunction)

            linechart <- nPlot(Q ~ Step, data = data, type = 'lineChart')
            linechart$addParams(width = 600, height = 400, title="Quality function") 
            linechart$xAxis(axisLabel="Step")
            linechart$yAxis(axisLabel="Q")

            linechart$chart(forceY=c(floor(min(data$Q)),floor(max(data$Q))+1), 
                                forceX=c(floor(min(data$Step)),floor(max(data$Step))+1))

            return(linechart)
        })    

        output$reducibilityHeatmap <- renderD3heatmap({
            if(input$btnCalculateReducibility==0 || input$btnImportNetworks == 0 || LAYERS==0)
                return(NULL)

            Layer <- c()
            for(l in 1:LAYERS) Layer = c(Layer,as.character(layerLabel[[l]]))

            distanceMatrix <- listReducibility$JSD
                
            colnames(distanceMatrix) <- Layer
            rownames(distanceMatrix) <- Layer
            distanceMatrix.df <- as.data.frame(as.matrix(distanceMatrix))
                
            d3heatmap(
                distanceMatrix.df,
                color = input$selReducibilityColorPalette,
                labRow=Layer,
                labCol=Layer,
                cexRow=as.numeric(input$txtREDUCIBILITY_HEATMAP_FONT_SIZE),
                cexCol=as.numeric(input$txtREDUCIBILITY_HEATMAP_FONT_SIZE),
                hclustfun=function(x) hclust(x,method=input$selReducibilityClusterMethod),
                symm=T,
                dendrogram="both",
                )
            })

    
      	################################################
      	# Apply layout
      	################################################
    
        observeEvent(input$btnApplyLayout, {
            if(input$btnApplyLayout==0 || input$btnImportNetworks == 0)
                return()
    
            isolate({
                progress <- shiny::Progress$new(session)
                on.exit(progress$close())
                
                #First check if the layout is wrt Multiplex, a layer or independent
                if(input$radLayoutType == 'LAYOUT_BY_LAYER_ID' && input$selInputLayerID!='None'){
                    LAYOUT_BY_LAYER_ID <<- as.numeric(strsplit(input$selInputLayerID," ")[[1]][2])
                }else{
                    LAYOUT_BY_LAYER_ID <<- 0
                }
                
                if(input$radLayoutType == 'LAYOUT_INDEPENDENT'){
                    LAYOUT_INDEPENDENT <<- TRUE
                }else{
                    LAYOUT_INDEPENDENT <<- FALSE
                }
    
                if(input$radLayoutDimension=="LAYOUT_DIMENSION_2D"){
                    LAYOUT_DIMENSION <<- 2
                }else{
                    LAYOUT_DIMENSION <<- 3
                }
    
                layouts <<- vector("list",LAYERS+1)
                
                print(paste("Layout dimension:",LAYOUT_DIMENSION))
                
                #Check if the layouts are specified by external files, otherwise proceed with the automatic ones
                if(!LAYOUT_EXTERNAL || (GEOGRAPHIC_LAYOUT && input$chkPLOT_AS_EDGE_COLORED)){
                    if(!LAYOUT_INDEPENDENT){    
                        print("Constrained layout option.")
                        progress$set(message = 'Constrained layout...', value = 0.05)
                        Sys.sleep(0.5)
                        if(LAYOUT_BY_LAYER_ID>0){
                            #It will use the first layer to layout the others
                            if(WEIGHTED){
                                gAggr <- graph.adjacency(AdjMatrix[[LAYOUT_BY_LAYER_ID]], weighted=T)
                            }else{
                                gAggr <- graph.adjacency(AdjMatrix[[LAYOUT_BY_LAYER_ID]], weighted=NULL)    
                            }
                        }else{
                            #Aggregate graph from aggregate adjacency matrix
                            progress$set(message = 'Calculating aggregated...', value = 0.1)
                            
                            #here the code for the three template methods
                            if(input$radLayoutTypeMultiplex=="LAYOUT_MULTIPLEX_AGGREGATE"){
                                gAggr <- graph.adjacency(AdjMatrix[[LAYERS+1]], weighted=T)
                            }else if(input$radLayoutTypeMultiplex=="LAYOUT_MULTIPLEX_UNION"){
                                gAggr <- graph.adjacency(AdjMatrix[[LAYERS+1]], weighted=NULL)
                            }else if(input$radLayoutTypeMultiplex=="LAYOUT_MULTIPLEX_INTERSECTION"){
                                Adj.tmp <- AdjMatrix[[1]]
                                for(l2 in 2:LAYERS){
                                    Adj.tmp <- pmin( Adj.tmp, AdjMatrix[[l2]] )
                                }
                                gAggr <- graph.adjacency(Adj.tmp, weighted=NULL)                                
                            }
                            
                            
                            print("Aggregate network created. Proceeding with layout to obtain coordinates for each layer.")
                        }
                        
                        #Note that here, gAggr does not correspond to the aggregate when LAYOUT_BY_LAYER_ID is T 
                        #But this is only confusing in this piece of code. I am too lazy now to change the name of this
                        #variable, I keep this note for the future. The aggregate is in g[[LAYERS+1]]
                        
                        progress$set(message = 'Applying layout...', value = 0.2)
                        #Choose a layout and apply it to the aggregate network
                        if(input$radLayoutAlgorithm=="LAYOUT_FRUCHTERMAN_REINGOLD"){
                            lAggr <- layout.fruchterman.reingold(gAggr,weights=E(gAggr)$weight,niter=as.numeric(input$txtLAYOUT_MAXITER),dim=LAYOUT_DIMENSION)
                        }
                        if(input$radLayoutAlgorithm=="LAYOUT_LGL"){
                            lAggr <- layout.lgl(gAggr,maxiter=as.numeric(input$txtLAYOUT_MAXITER))
                        }
                        if(input$radLayoutAlgorithm=="LAYOUT_DRL"){
                            lAggr <- layout.drl(gAggr,options=list(simmer.attraction=0,simmer.iterations=floor(as.numeric(input$txtLAYOUT_MAXITER)*0.15),crunch.iterations=floor(as.numeric(input$txtLAYOUT_MAXITER)*0.1),cooldown.iterations=floor(as.numeric(input$txtLAYOUT_MAXITER)*0.25),expansion.iterations=floor(as.numeric(input$txtLAYOUT_MAXITER)*0.25),liquid.iterations=floor(as.numeric(input$txtLAYOUT_MAXITER)*0.25)),dim=LAYOUT_DIMENSION)
                        }
                        
                        if(input$radLayoutAlgorithm=="LAYOUT_REINGOLD_TILFORD"){
                            lAggr <- layout.reingold.tilford(gAggr)
                        }
                        
                        if(input$radLayoutAlgorithm=="LAYOUT_KAMADA_KAWAI"){
                            lAggr <- layout.kamada.kawai(gAggr, niter=as.numeric(input$txtLAYOUT_MAXITER),dim=LAYOUT_DIMENSION)
                        }
                        if(input$radLayoutAlgorithm=="LAYOUT_SPRING"){
                            lAggr <- layout.spring(gAggr,repulse=T)
                        }
                        
                        if(input$radLayoutAlgorithm=="LAYOUT_COMBINED"){
                            #We try to use the DRL to scale and we use it as seed for a Kamada-Kawai with few iterations
                            ltmp <- layout.drl(gAggr,options=list(simmer.attraction=0,simmer.iterations=floor(as.numeric(input$txtLAYOUT_MAXITER)*0.15),crunch.iterations=floor(as.numeric(input$txtLAYOUT_MAXITER)*0.1),cooldown.iterations=floor(as.numeric(input$txtLAYOUT_MAXITER)*0.25),expansion.iterations=floor(as.numeric(input$txtLAYOUT_MAXITER)*0.25),liquid.iterations=floor(as.numeric(input$txtLAYOUT_MAXITER)*0.25)),dim=LAYOUT_DIMENSION)
    
                            lAggr <- layout.kamada.kawai(gAggr, niter=as.numeric(input$txtLAYOUT_MAXITER),start=ltmp,dim=LAYOUT_DIMENSION)
                        }

                        #For compatibility with the other option, we assign lAggr to each layout, aggregate included
                        for(l in 1:(LAYERS+1)){
                            layouts[[l]] <<- lAggr
                        }
                    }else{
                        progress$set(message = 'Independent layout option...', value = 0.05)
                        Sys.sleep(0.5)
                        print("Independent layout option.")
                        for(l in 1:(LAYERS+1)){
                            layouts[[l]] <<- matrix(c(1),ncol=3,nrow=Nodes)
                            
                            progress$set(message = paste('Layout for layer',l,"..."), value = 0.05 + 0.85* l / (LAYERS+1))
                            
                            print(paste("  Layout for layer",l,"..."))
                            #Each layout is calculated separately    
                            if(input$radLayoutAlgorithm=="LAYOUT_FRUCHTERMAN_REINGOLD"){
                                layouts[[l]] <<- layout.fruchterman.reingold(g[[l]],weights=E(g[[l]])$weight,niter=as.numeric(input$txtLAYOUT_MAXITER),dim=LAYOUT_DIMENSION)
                            }
                            if(input$radLayoutAlgorithm=="LAYOUT_LGL"){
                                layouts[[l]] <<- layout.lgl(g[[l]],maxiter=as.numeric(input$txtLAYOUT_MAXITER))
                            }
                            if(input$radLayoutAlgorithm=="LAYOUT_DRL"){
                                layouts[[l]] <<- layout.drl(g[[l]],options=list(simmer.attraction=0,simmer.iterations=floor(as.numeric(input$txtLAYOUT_MAXITER)*0.15),crunch.iterations=floor(as.numeric(input$txtLAYOUT_MAXITER)*0.1),cooldown.iterations=floor(as.numeric(input$txtLAYOUT_MAXITER)*0.25),expansion.iterations=floor(as.numeric(input$txtLAYOUT_MAXITER)*0.25),liquid.iterations=floor(as.numeric(input$txtLAYOUT_MAXITER)*0.25)),dim=LAYOUT_DIMENSION)
                            }
                            
                            if(input$radLayoutAlgorithm=="LAYOUT_REINGOLD_TILFORD"){
                                layouts[[l]] <<- layout.reingold.tilford(g[[l]])
                            }
                            
                            if(input$radLayoutAlgorithm=="LAYOUT_KAMADA_KAWAI"){
                                layouts[[l]] <<- layout.kamada.kawai(g[[l]], niter=as.numeric(input$txtLAYOUT_MAXITER),dim=LAYOUT_DIMENSION)
                            }
                            if(input$radLayoutAlgorithm=="LAYOUT_SPRING"){
                                layouts[[l]] <<- layout.spring(g[[l]],repulse=T)
                            }
                            
                            if(input$radLayoutAlgorithm=="LAYOUT_COMBINED"){
                                #We try to use the DRL to scale and we use it as seed for a Kamada-Kawai with few iterations
                                ltmp <- layout.drl(g[[l]],options=list(simmer.attraction=0,simmer.iterations=floor(as.numeric(input$txtLAYOUT_MAXITER)*0.15),crunch.iterations=floor(as.numeric(input$txtLAYOUT_MAXITER)*0.1),cooldown.iterations=floor(as.numeric(input$txtLAYOUT_MAXITER)*0.25),expansion.iterations=floor(as.numeric(input$txtLAYOUT_MAXITER)*0.25),liquid.iterations=floor(as.numeric(input$txtLAYOUT_MAXITER)*0.25)),dim=LAYOUT_DIMENSION)
                                
                                layouts[[l]] <<- layout.kamada.kawai(g[[l]], niter=as.numeric(input$txtLAYOUT_MAXITER),start=ltmp,dim=LAYOUT_DIMENSION)
                            }
                        }
                    }
                }else{
                    print("Layouting: external files.")
                    for(l in 1:LAYERS){
                        progress$set(message = paste('Layout for layer',l,"..."), value = 0.05 + 0.85* l / (LAYERS+1))
                        #print(paste("DEBUG Layer",l))
                        #print(Nodes)
                                                
                        layouts[[l]] <<- matrix(c(1),nrow=Nodes,ncol=2)
                        layouts[[l]] <<- layerLayout[[l]]
                    }
                    
                    #giving the layout of the aggregate from external file makes no sense if it is different from the other layers
                    #and it is also annoying to be constrained to specify the aggregate, if one does not want to show it.
                    #Therefore, here I prefer to assign manually the layout of the first layer to the aggregate.
                    #So far, I accept this possibility just for sake of completeness, but a correct use of muxViz should avoid
                    #situations like this..
                    progress$set(message = paste('Layout for layer',l,"..."), value = 0.05 + 0.85)
                    layouts[[LAYERS+1]] <<- layouts[[1]]
                }
    
                if(LAYOUT_DIMENSION==2 || dim(layouts[[1]])[2]==2){
                    #Make it a 3-columns object
                    for(l in 1:(LAYERS+1)){
                        layouts[[l]] <<- cbind(layouts[[l]][,1:2],0)
                    }
                }

                if(!LAYOUT_EXTERNAL && !GEOGRAPHIC_LAYOUT){
                    XMAX <<- -1e10
                    YMAX <<- -1e10
                    ZMAX <<- -1e10
                    XMIN <<- 1e10
                    YMIN <<- 1e10
                    ZMIN <<- 1e10

                    for(l in 1:(LAYERS+1)){
                        if(min(layouts[[l]][,1],na.rm=T) < XMIN) XMIN <<- min(layouts[[l]][,1],na.rm=T)
                        if(min(layouts[[l]][,2],na.rm=T) < YMIN) YMIN <<- min(layouts[[l]][,2],na.rm=T)
                        if(min(layouts[[l]][,3],na.rm=T) < ZMIN) ZMIN <<- min(layouts[[l]][,3],na.rm=T)
                        if(max(layouts[[l]][,1],na.rm=T) > XMAX) XMAX <<- max(layouts[[l]][,1],na.rm=T)
                        if(max(layouts[[l]][,2],na.rm=T) > YMAX) YMAX <<- max(layouts[[l]][,2],na.rm=T)
                        if(max(layouts[[l]][,3],na.rm=T) > ZMAX) ZMAX <<- max(layouts[[l]][,3],na.rm=T)
                    }
                }
    

                if(GEOGRAPHIC_LAYOUT && LAYOUT_EXTERNAL){
                    #we check if the boundaries are given by the user
                    if(input$txtGEOGRAPHIC_LAT_MIN != "" && input$txtGEOGRAPHIC_LAT_MAX != "" && input$txtGEOGRAPHIC_LONG_MIN != "" && input$txtGEOGRAPHIC_LONG_MAX != ""){
                        #we are sure here that each layout is specified correctly
                        for(l in 1:LAYERS){
                            layerTable <- read.table(layerLayoutFile[[l]][1], header=T, sep=as.character(input$txtEdgeListFileSep))
                            
                            if(input$chkEdgeListLabel) layerTable$nodeID <- 1:nrow(layerTable)
                            
                            layerLayout[[l]] <<- matrix(c(1),nrow=Nodes,ncol=2)
                            
                            print(paste("Layout for layer",l,"is geographic and user-defined. Converting."))
                            #Get boundaries
                            thisLATMIN <- as.numeric(input$txtGEOGRAPHIC_LAT_MIN)
                            thisLATMAX <- as.numeric(input$txtGEOGRAPHIC_LAT_MAX)
                            thisLONGMIN <- as.numeric(input$txtGEOGRAPHIC_LONG_MIN)
                            thisLONGMAX <- as.numeric(input$txtGEOGRAPHIC_LONG_MAX)
                            
                            #mapproject has problems in converting |latitudes| > 80, therefore we have to constrain
                            if(thisLATMIN < -80){
                                thisLATMIN <- -80
                                print("Warning! Min Latitude smaller than -80, changing to -80.")
                            }
                            if(thisLATMAX > 80){
                                thisLATMAX <- 80
                                print("Warning! Max Latitude larger than 80, changing to 80.") 
                            }
                            #the geographical maps give error for extremal longitudes. Constrain here too..
                            if(thisLONGMIN < -179){
                                thisLONGMIN <- -179
                                print("Warning! Min Longitude smaller than -179, changing to -179.")
                            }
                            if(thisLONGMAX>179){
                                thisLONGMAX <- 179
                                print("Warning! Max Longitude larger than 179, changing to 179.") 
                            }
                                            
                            layerTable$nodeLong[ layerTable$nodeLong < thisLONGMIN ] <- thisLONGMIN
                            layerTable$nodeLong[ layerTable$nodeLong > thisLONGMAX ] <- thisLONGMAX
                            layerTable$nodeLat[ layerTable$nodeLat < thisLATMIN ] <- thisLATMIN
                            layerTable$nodeLat[ layerTable$nodeLat > thisLATMAX ] <- thisLATMAX
        
                            longBounds = c(min(layerTable$nodeLong,na.rm=T),max(layerTable$nodeLong,na.rm=T))
                            latBounds = c(min(layerTable$nodeLat,na.rm=T),max(layerTable$nodeLat,na.rm=T))
                                         
                            print(paste("  Latitude new boundaries: ",latBounds[1],latBounds[2]))
                            print(paste("  Longitude new boundaries: ",longBounds[1],longBounds[2]))
                            
                            #The input layout is geographic, we must convert it to cartesian
                            sphCoordinates <- list()
                            sphCoordinates$x <- layerTable$nodeLong
                            sphCoordinates$y <- layerTable$nodeLat
                            cartCoordinates <- mapproject(sphCoordinates,proj="mercator")
                            
                            layerTable$nodeX <- cartCoordinates$x
                            layerTable$nodeY <- cartCoordinates$y
                            
                            layerLayout[[l]][layerTable$nodeID + offsetNode,1:2] <<- cbind(layerTable$nodeX,layerTable$nodeY)
                            layouts[[l]] <<- layerLayout[[l]]
                            layouts[[l]] <<- cbind(layouts[[l]][,1:2],0)
                
                            sphCoordinates <- list()
                            sphCoordinates$x <- c(thisLONGMIN,thisLONGMAX)
                            sphCoordinates$y <- c(thisLATMIN,thisLATMAX)
                            cartCoordinates <- mapproject(sphCoordinates,proj="mercator")
                            XMIN <<- cartCoordinates$x[1]
                            XMAX <<- cartCoordinates$x[2]
                            YMIN <<- cartCoordinates$y[1]
                            YMAX <<- cartCoordinates$y[2]  
                        }            
                        layerLayout[[LAYERS+1]] <<- layerLayout[[1]]
                        layouts[[LAYERS+1]] <<- layerLayout[[1]]
                        layouts[[LAYERS+1]] <<- cbind(layouts[[LAYERS+1]][,1:2],0)
                    }
                }

                print(paste("LIMITS:", XMIN, XMAX, YMIN, YMAX))
                            
                progress$set(message = 'Normalizing coordinates...', value = 0.95)
    
                print("  Normalizing coordinates...")    
    
                if(input$radNetworkOfLayersLayoutType=="NETWORK_LAYERS_LAYOUT_ONELINE"){
                    #rescale the layout to allow superposition with shift along z-axis
                    for(l in 1:(LAYERS+1)){
                        #not quite sure about this piece of code, waiting for empirical problems
                        #deltaX <- min(layouts[[l]][,1],na.rm=T) - XMIN
                        #if(XMIN > min(layouts[[l]][,1],na.rm=T)){
                        #    deltaX <- -deltaX
                        #}
                        #deltaY <- min(layouts[[l]][,2],na.rm=T) - YMIN
                        #if(YMIN > min(layouts[[l]][,2],na.rm=T)){
                        #    deltaY <- -deltaY
                        #}
                        deltaX <- 0
                        deltaY <- 0
                        layouts[[l]][,1] <<- as.numeric(input$txtLAYER_SCALE)*(layouts[[l]][,1] - XMIN + deltaX)/(XMAX-XMIN) - 1 + (l-1)*as.numeric(input$txtLAYER_SHIFT_X)
                        layouts[[l]][,2] <<- as.numeric(input$txtLAYER_SCALE)*(layouts[[l]][,2] - YMIN + deltaY)/(YMAX-YMIN) - 1 + (l-1)*as.numeric(input$txtLAYER_SHIFT_Y)
        
                        if(LAYERS>1){
                            if(input$chkPLOT_AS_EDGE_COLORED){
                                layouts[[l]][,1] <<- ((layouts[[LAYERS+1]][,1]- XMIN)/(XMAX-XMIN))*runif(1,1.005,1.01)
                                layouts[[l]][,2] <<- ((layouts[[LAYERS+1]][,2] - YMIN)/(YMAX-YMIN))*runif(1,1.005,1.01)
        
                                if(LAYOUT_DIMENSION==3){
                                    layouts[[l]][,3] <<- ((layouts[[LAYERS+1]][,3] - ZMIN)/(ZMAX-ZMIN))*runif(1,1.005,1.01)
                                }else{
                                    layouts[[l]][,3] <<- 0
                                }
                                
                            }else{
                                if(input$chkAGGREGATE_SHOW){
                                    layouts[[l]][,3] <<- -1 + as.numeric(input$txtLAYER_SCALE)*as.numeric(input$txtLAYER_SPACE)*l/(LAYERS+1)
                                }else{
                                    layouts[[l]][,3] <<- -1 + as.numeric(input$txtLAYER_SCALE)*as.numeric(input$txtLAYER_SPACE)*l/LAYERS
                                }
                            }
                        }else{
                            #We allow this only if the layer is one
                            if(LAYOUT_DIMENSION==3 && !GEOGRAPHIC_LAYOUT){
                                layouts[[l]][,1] <<- (layouts[[l]][,1]- XMIN)/(XMAX-XMIN)
                                layouts[[l]][,2] <<- (layouts[[l]][,2] -YMIN)/(YMAX-YMIN)
                                layouts[[l]][,3] <<- (layouts[[l]][,3] - ZMIN)/(ZMAX-ZMIN) - 1 
                            }else{
                                layouts[[l]][,3] <<- 1
                            }
                        }
                    }
                }else{
                    #new network of layers layouts
                    if(input$radNetworkOfLayersLayoutType=="NETWORK_LAYERS_LAYOUT_MULTILINE"){
                        if(LAYERS>1){
                            rows <- as.numeric(input$txtNetworkLayersMultilineRows)
                            cols <- as.numeric(input$txtNetworkLayersMultilineCols)
                            
                            #estimate the number of levels in the third dimension
                            multilevels <- floor(LAYERS/(rows*cols)) + 1
                            if( LAYERS %% (rows*cols)==0 ){
                                #if the ratio is exact, we don't need one more level in the worst case
                                multilevels <- multilevels-1
                            }

                            scal <- as.numeric(input$txtLAYER_SCALE)
                            rescx <- scal/(XMAX-XMIN)
                            rescy <- scal/(YMAX-YMIN)
                            #shift <- as.numeric(input$txtLAYER_SHIFT_X) #useless for this layout
                            space <- as.numeric(input$txtLAYER_SPACE)

                            shiftx <- (cols*scal + cols*space)/2
                            shifty <- (rows*scal + rows*space)/2

                            rowcnt <- 1
                            colcnt <- 1
                            levelcnt <- 1
                            for(l in 1:LAYERS){
                                layouts[[l]][,1] <<- rescx*(layouts[[l]][,1] - XMIN) - shiftx + (colcnt-1)*scal + (colcnt-1)*space - 1
                                layouts[[l]][,2] <<- rescy*(layouts[[l]][,2] - YMIN) + shifty - (rowcnt-1)*scal - (rowcnt-1)*space - 1
                                
                                #change z accordinly
                                layouts[[l]][,3] <<- 1 - scal*2*space*(levelcnt-1)/LAYERS
                                
                                colcnt <- colcnt + 1
                                if(colcnt==(cols+1)){
                                    rowcnt <- rowcnt + 1
                                    
                                    if(rowcnt==(rows+1)){
                                        levelcnt <- levelcnt + 1
                                        rowcnt <- 1
                                    }
                                    
                                    colcnt <- 1
                                }
                            }
                            
                            
                        }else{
                            progress$set(message = 'This layout require more than one layer!', value = 0.9)
                        }
                    }
                    if(input$radNetworkOfLayersLayoutType=="NETWORK_LAYERS_LAYOUT_FORCEDIRECTED"){ 
                        if(LAYERS>1){
                            scal <- as.numeric(input$txtLAYER_SCALE)
                            rescx <- scal/(XMAX-XMIN)
                            rescy <- scal/(YMAX-YMIN)

                            #this is not the optimal approach.. but for the networks handled by muxViz shoudl be enough
                            #alternatively: use igraph to create the weighted network, then convert again to data.frame
                            #see http://lists.nongnu.org/archive/html/igraph-help/2013-02/msg00079.html
                            dfNoN <- data.frame()
                            
                            sub.multi <- multilayerEdges #multilayerEdges[ multilayerEdges[,2]!=multilayerEdges[,4], ]
                            sub.multi$V1 <- NULL
                            sub.multi$V3 <- NULL
                            #print(sub.multi)
                            
                            colnames(sub.multi) <- c("from", "to", "weight")
                            g.non <- graph.data.frame(sub.multi, directed=DIRECTED)
                            g.non <- simplify(g.non, edge.attr.comb="sum")

                            layout.non <<- layout.auto(g.non, dim=3)
                            layout.non[,1] <<- (layout.non[,1] - min(layout.non[,1]))/(max(layout.non[,1])-min(layout.non[,1]))
                            layout.non[,2] <<- (layout.non[,2] - min(layout.non[,2]))/(max(layout.non[,2])-min(layout.non[,2]))
                            layout.non[,3] <<- (layout.non[,3] - min(layout.non[,3]))/(max(layout.non[,3])-min(layout.non[,3]))
                            layout.non <<- scal*scal*layout.non + scal
                            #print(layout.non)
                            
                            for(l in 1:LAYERS){
                                layouts[[l]][,1] <<- rescx*(layouts[[l]][,1] - XMIN) - 1 - layout.non[l,1]
                                layouts[[l]][,2] <<- rescy*(layouts[[l]][,2] - YMIN) - 1 - layout.non[l,2]
                                layouts[[l]][,3] <<-  layout.non[l,3]
                            }
                        }else{
                            progress$set(message = 'This layout require more than one layer!', value = 0.9)
                        }
                    }
                    if(input$radNetworkOfLayersLayoutType=="NETWORK_LAYERS_LAYOUT_MATRIX"){

                        if(LAYERS>1){                            
                            rows <- as.numeric(input$txtNetworkLayersMatrixRows)
                            cols <- as.numeric(input$txtNetworkLayersMatrixCols)

                            if(rows*cols<LAYERS){
                                progress$set(message = 'ERROR! Rows x Columns < # Layers ...', value = 0.9)
                            }else{
                                scal <- as.numeric(input$txtLAYER_SCALE)
                                rescx <- scal/(XMAX-XMIN)
                                rescy <- scal/(YMAX-YMIN)
                                #shift <- as.numeric(input$txtLAYER_SHIFT_X) #useless for this layout
                                space <- as.numeric(input$txtLAYER_SPACE)

                                shiftx <- (cols*scal + cols*space)/2
                                shifty <- (rows*scal + rows*space)/2

                                rowcnt <- 1
                                colcnt <- 1
                                for(l in 1:LAYERS){
                                    layouts[[l]][,1] <<- rescx*(layouts[[l]][,1] - XMIN) - shiftx + (colcnt-1)*scal + (colcnt-1)*space - 1
                                    layouts[[l]][,2] <<- rescy*(layouts[[l]][,2] - YMIN) + shifty - (rowcnt-1)*scal - (rowcnt-1)*space - 1
                                    
                                    #keep the same z
                                    #layouts[[l]][,3] <<- -1 + scal*space*l/LAYERS
                                    layouts[[l]][,3] <<- 0
                                    
                                    colcnt <- colcnt + 1
                                    if(colcnt==(cols+1)){
                                        colcnt = 1
                                        rowcnt <- rowcnt + 1
                                    }
                                }
                            }
                        }else{
                            progress$set(message = 'This layout require more than one layer!', value = 0.9)
                        }                        
                    }
                }            

                if(!input$chkPLOT_WITH_RGL){
                    #rotate the view, if needed
                    thx <- as.numeric(input$txtPLOT_ROTX)
                    thy <- as.numeric(input$txtPLOT_ROTY)
                    thz <- as.numeric(input$txtPLOT_ROTZ)
                    
                    for(l in 1:(LAYERS+1)){
                        layouts[[l]] <<- t( Rotx(thx) %*% t(layouts[[l]]) ) 
                        layouts[[l]] <<- t( Roty(thy) %*% t(layouts[[l]]) ) 
                        layouts[[l]] <<- t( Rotz(thz) %*% t(layouts[[l]]) ) 
                    }                          
                }

                progress$set(message = 'Layout Completed!', value = 1)
                Sys.sleep(2)
                print("Layouting finished. Proceeding with openGL plot of each layer.")
            })
        }, ignoreInit = TRUE)
    
    
      	################################################
      	# Plot
      	################################################
      	makeRendering <- function(){
            progress <- shiny::Progress$new(session)
            on.exit(progress$close())

            progress$set(message = 'Start rendering...', value = 0.05)
            Sys.sleep(1)

            if(input$chkPLOT_WITH_RGL){
                #save orientation for later user, if needed
                orientationRGL <<- par3d(no.readonly=TRUE)
                
                rgl.clear()
                tryCatch(rgl.pop("lights"),error=function(e) print("Warning: no lights to pop"))
                rgl.light(theta = 0, phi = 0, viewpoint.rel = TRUE, ambient = "#FFFFFF", 
                diffuse = "#FFFFFF", specular = "#FFFFFF")
                #print(rgl.ids())
                #if ( length(rgl.ids) )
                #rgl.pop(type="lights")
            }else{
                #plot.new()
                par(mar=c(0, 0, 0, 0), xaxs='i', yaxs='i') 
                par(oma=c(0, 0, 0, 0))
                
                xmin.tmp <- 1e100
                xmax.tmp <- -1e100
                ymin.tmp <- 1e100
                ymax.tmp <- -1e100
                for(l in 1:LAYERS){
                    xmin.tmp <- min(xmin.tmp, min(layouts[[l]][,1]))
                    xmax.tmp <- max(xmax.tmp, max(layouts[[l]][,1]))
                    ymin.tmp <- min(ymin.tmp, min(layouts[[l]][,2]))
                    ymax.tmp <- max(ymax.tmp, max(layouts[[l]][,2]))
                }
                
                if(input$chkAGGREGATE_SHOW){
                    l <- LAYERS+1
                    xmin.tmp <- min(xmin.tmp, min(layouts[[l]][,1]))
                    xmax.tmp <- max(xmax.tmp, max(layouts[[l]][,1]))
                    ymin.tmp <- min(ymin.tmp, min(layouts[[l]][,2]))
                    ymax.tmp <- max(ymax.tmp, max(layouts[[l]][,2]))
                }
                
                xmin.tmp <- min(xmin.tmp*0.9, xmin.tmp*1.1)
                xmax.tmp <- max(xmax.tmp*0.9, xmax.tmp*1.1)
                ymin.tmp <- min(ymin.tmp*0.9, ymin.tmp*1.1)
                ymax.tmp <- max(ymax.tmp*0.9, ymax.tmp*1.1)
                
                plot(x=NULL, y=NULL, type="n", 
                    xlim=c(xmin.tmp,xmax.tmp), ylim=c(ymin.tmp,ymax.tmp)
                    )
                rect(par("usr")[1], par("usr")[3], par("usr")[2], par("usr")[4], col =input$colBACKGROUND_COLOR)
            }
            
            #layer color
            layer.color <- strsplit(input$colLAYER_COLOR,",")[[1]]
            if(length(layer.color)==LAYERS){
                for(l in 1:LAYERS){
                    layerColor[[l]] <<- layer.color[l]
                }
            }else{
                for(l in 1:LAYERS){
                    layerColor[[l]] <<- input$colLAYER_COLOR
                }                    
            }

            #layer alpha
            layer.alpha <- strsplit(input$txtLAYER_TRANSP,",")[[1]]
            if(length(layer.alpha)==LAYERS){
                for(l in 1:LAYERS){
                    layerColorAlpha[[l]] <<- as.numeric(layer.alpha[l])
                }
            }else{
                for(l in 1:LAYERS){
                    layerColorAlpha[[l]] <<- as.numeric(input$txtLAYER_TRANSP)
                }                    
            }                

            #create the vector for inactive layers 
            vecInactiveLayers <- as.numeric(strsplit(input$txtLAYERS_ACTIVE, ",")[[1]])

            for(l in 1:(LAYERS+1)){
                if( l %in% vecInactiveLayers ){
                    #skip layers set to be inactive
                    next
                }
                progress$set(message = paste('Layer',l,'...'), value = 0.05 + 0.85*l/(LAYERS+1))

                if(l==(LAYERS+1)){
                    if( (!input$chkAGGREGATE_SHOW || LAYERS==1) || 
                        (input$chkPLOT_AS_EDGE_COLORED && LAYOUT_DIMENSION==3) ||
                        (input$radNetworkOfLayersLayoutType!="NETWORK_LAYERS_LAYOUT_ONELINE")
                        ){
                        #if we don't want to show the aggregate, we must skip the rest
                        #we must skip also if the layers is just 1
                        next
                    }
                }
                if(l<LAYERS+1){
                    print(paste("Layer: ",l))
                }else{
                    print(paste("Layer: Aggregate"))    
                }
                #V(g[[l]])$vertex.label.color <- rgb(47,47,47,0,maxColorValue = 255)
                V(g[[l]])$vertex.label.color <- input$colNODE_LABELS_FONT_COLOR

                #this set the transparency level of edges and nodes.. it can be customized
                #E(g[[l]])$alpha <- floor(as.numeric(input$txtEDGE_TRANSP)*255)
                #V(g[[l]])$alpha <- floor(as.numeric(input$txtNODE_TRANSP)*255)
            

                print("Other graphic options...")
                
                #other assignments
                E(g[[l]])$curve<- as.numeric(input$txtEDGE_BENDING)
            
                if(!input$chkNODE_LABELS_SHOW){
                    V(g[[l]])$label <- ""
                }else{
                    V(g[[l]])$label <- nodesLabel[[l]]
                }
            
                #NODE SIZE
            
                arrayDiagnostics <- 1
                #if the GUI shows only the UNIFORM and EXTERNAL option
                if(input$radNodeSizeType=="NODE_SIZE_PROPORTIONAL_TO_UNIFORM"){
                    arrayDiagnostics <- rep(1,Nodes)
                }
                if(input$radNodeSizeType=="NODE_SIZE_PROPORTIONAL_TO_EXTERNAL"){
                    if(externalNodeSizeFlag){
                        #setting default size for all nodes
                        arrayDiagnostics <- rep(1,Nodes)
                        
                        #set the size for nodes specified in the external table
                        size.set <- externalNodeColorTable[ externalNodeColorTable$layerID==l, ]

                        if(nrow(size.set)>0){
                            arrayDiagnostics[ size.set$nodeID ] <- size.set$size
                        }
                    }
                }
                #overwrite arrayDiagnostics if centrality have been calculated and attrib is not Uniform or External
                if(diagnosticsOK){
                    if(input$btnCalculateCentralityDiagnostics>0 && !is.null(input$selVizNodeSizeID)){
                        #the GUI is visualizing the list of possibilities
                        attrib <- input$selVizNodeSizeID
                        if(attrib=="Uniform"){
                            arrayDiagnostics <- rep(1,Nodes)
                            V(g[[l]])$size <- as.numeric(input$txtNODE_DEFAULT_SIZE)
                        }else if(attrib=="External"){
                            if(externalNodeSizeFlag){
                                #setting default size for all nodes
                                arrayDiagnostics <- rep(1,Nodes)
        
                                #set the size for nodes specified in the external table
                                size.set <- externalNodeColorTable[ externalNodeColorTable$layerID==l, ]
                                if(nrow(size.set)>0){
                                    arrayDiagnostics[ size.set$nodeID ] <- size.set$size
                                }
                            }
                        }else{
                            if( length(grep("Multi-",attrib))>0 ){
                                arrayDiagnostics <- as.numeric(listDiagnostics[[l]][ gsub("Multi-","",attrib) ][,1])
                            }else{
                                arrayDiagnostics <- as.numeric(listDiagnosticsSingleLayer[[l]][ attrib ][,1])
                            }
                        }
                    }
                }

                if(input$radNodeSizeType2=="NODE_SIZE_PROPORTIONAL_TYPE_NORMAL"){
                    V(g[[l]])$size <- as.numeric(input$txtNODE_DEFAULT_SIZE)*arrayDiagnostics
                }else if(input$radNodeSizeType2=="NODE_SIZE_PROPORTIONAL_TYPE_LOG"){
                    V(g[[l]])$size <- as.numeric(input$txtNODE_DEFAULT_SIZE)*(1+2*log(1+arrayDiagnostics))
                }else if(input$radNodeSizeType2=="NODE_SIZE_PROPORTIONAL_TYPE_LOGLOG"){
                    V(g[[l]])$size <- as.numeric(input$txtNODE_DEFAULT_SIZE)*log(1+log(1+arrayDiagnostics));
                }


                #EDGE SIZE
                
                if(input$radEdgeSizeType=="EDGE_SIZE_PROPORTIONAL_TO_UNIFORM"){                
                    E(g[[l]])$size <- as.numeric(input$txtEDGE_DEFAULT_SIZE);
                }else if(input$radEdgeSizeType=="EDGE_SIZE_PROPORTIONAL_TO_WEIGHT"){
                    if(WEIGHTED){
                        if(input$radEdgeSizeType2=="EDGE_SIZE_PROPORTIONAL_TYPE_NORMAL"){
                            E(g[[l]])$size <- E(g[[l]])$weight
                        }else if(input$radEdgeSizeType2=="EDGE_SIZE_PROPORTIONAL_TYPE_LOG"){
                            E(g[[l]])$size <- as.numeric(input$txtEDGE_DEFAULT_SIZE)*log(1+E(g[[l]])$weight)
                        }else if(input$radEdgeSizeType2=="EDGE_SIZE_PROPORTIONAL_TYPE_LOGLOG"){
                            E(g[[l]])$size <- as.numeric(input$txtEDGE_DEFAULT_SIZE)*log(1+log(1+E(g[[l]])$weight))
                        }
                    }else{
                        E(g[[l]])$size <- as.numeric(input$txtEDGE_DEFAULT_SIZE)
                    }
                }else if(input$radEdgeSizeType=="EDGE_SIZE_PROPORTIONAL_TO_EXTERNAL"){
                    if(externalEdgeSizeFlag){
                        #Here we set only intra-layer links. Inter-links are considered later, if they are shown
                        
                        E(g[[l]])$size <- as.numeric(input$txtEDGE_DEFAULT_SIZE)
                        
                        size.set <- externalEdgeColorTable[ externalEdgeColorTable$layerID.from==l & externalEdgeColorTable$layerID.to==l, ]

                        if(nrow(size.set)>0){
                            edges.tmp <- igraph::get.edges(g[[l]], E(g[[l]]))

                            E(g[[l]])[which(edges.tmp[,1] %in% size.set$nodeID.from & edges.tmp[,2] %in% size.set$nodeID.to)]$size <- size.set$size
                        }
                    }
                }

                #EDGE COLOR
                if(input$radEdgeColor=="EDGE_COLOR_EXTERNAL"){
                    #Here we set only intra-layer links. Inter-links are considered later, if they are shown

                    E(g[[l]])$color <- as.character(input$colEDGE_COLOR)
                    
                    if(externalEdgeColorFlag){
                        color.set <- externalEdgeColorTable[ externalEdgeColorTable$layerID.from==l & externalEdgeColorTable$layerID.to==l, ]

                        if(nrow(color.set)>0){
                            edges.tmp <- igraph::get.edges(g[[l]], E(g[[l]]))

                            E(g[[l]])[which(edges.tmp[,1] %in% color.set$nodeID.from & edges.tmp[,2] %in% color.set$nodeID.to)]$color <- color.set$color
                        }
                    }
                }else if(input$radEdgeColor=="EDGE_COLOR_UNIFORM"){
                    E(g[[l]])$color <- input$colEDGE_COLOR
                }else if(input$radEdgeColor=="EDGE_COLOR_RANDOM"){
                    #colorset for the multiplex                
                    if( input$selMultiplexEdgeColorPalette=="random" ){
                        Rcolor <- sample(0:255, 1, replace=T)
                        Gcolor <- sample(0:255, 1, replace=T)
                        Bcolor <- sample(0:255, 1, replace=T)

                        #assign the color to the layer
                        E(g[[l]])$red <- Rcolor
                        E(g[[l]])$green <- Gcolor
                        E(g[[l]])$blue <- Bcolor
                    
                        E(g[[l]])$color<-rgb(red=E(g[[l]])$red, 
                                                        green=E(g[[l]])$green, 
                                                        blue=E(g[[l]])$blue, 
                                                        maxColorValue=255)
                    }else{
                        colorPalette <- colorRampPalette(brewer.pal(brewer.pal.info$maxcolors[row.names(brewer.pal.info)==input$selMultiplexEdgeColorPalette],input$selMultiplexEdgeColorPalette))(LAYERS+1)
                        E(g[[l]])$color <- colorPalette[l]
                    }
                }

                #NODE COLOR
                if(input$radNodeColor=="NODE_COLOR_EXTERNAL"){
                    if(externalNodeColorFlag){
                        #setting default color for all nodes
                        V(g[[l]])$color <- as.character(input$colNodeColorFileDefaultNodesColor)

                        #set the color for nodes specified in the external table
                        color.set <- externalNodeColorTable[ externalNodeColorTable$layerID==l, ]
                        if(nrow(color.set)>0){
                            V(g[[l]])$color[ as.numeric(color.set$nodeID) ] <- color.set$color                        
                        }
                    }
                }else if(input$radNodeColor=="NODE_COLOR_COMMUNITY"){
                    tmpColor <- rep("#959595", Nodes)
                                                
                    if(communityOK){
                        if(input$btnCalculateCommunityDiagnostics>0){
                            if(input$selVizNodeColorCommunityType=="Single-Layer"){
                                #color-code by single-layer community
                                idx.tmp <- which(listCommunitiesSingleLayer[[l]]$Community>0)
                                
                                if( input$selCommunityColorPalette=="random" ){
                                    colorPalette <- rainbow( max(listCommunitiesSingleLayer[[l]]$Community) + 2, 
                                                                      alpha=as.numeric(input$txtNODE_TRANSP), 
                                                                      start=runif(1))    
                                }else{
                                    #use a palette
                                    colorPalette <- colorRampPalette(brewer.pal(brewer.pal.info$maxcolors[row.names(brewer.pal.info)==input$selCommunityColorPalette],input$selCommunityColorPalette))(max(listCommunitiesSingleLayer[[l]]$Community))
                                }

                                tmpColor[idx.tmp] <- colorPalette[ listCommunitiesSingleLayer[[l]]$Community[idx.tmp] ]
                            }else{
                                #color-code by multilayer community
                                idx.tmp <- which(listCommunities[[l]]$Community>0)
                                
                                if( input$selCommunityColorPalette=="random" ){
                                    #for the multiplex we want exactly the opposite behavior
                                    colorPalette <- rainbow( max(listCommunitiesMerge$Community) + 2, 
                                                                      alpha=as.numeric(input$txtNODE_TRANSP), 
                                                                      start=commonRunif)
                                }else{
                                    #use a palette
                                    colorPalette <- colorRampPalette(brewer.pal(brewer.pal.info$maxcolors[row.names(brewer.pal.info)==input$selCommunityColorPalette],input$selCommunityColorPalette))(max(listCommunitiesMerge$Community))
                                }
                                
                                tmpColor[idx.tmp] <- colorPalette[ listCommunities[[l]]$Community[idx.tmp] ]
                            }                            
                        }
                    }
                    #print(tmpColor)
                    V(g[[l]])$color <- tmpColor
                }else if(input$radNodeColor=="NODE_COLOR_COMPONENT"){
                    tmpColor <- rep("#959595", Nodes)
                    
                    if(componentsOK){
                        if(input$btnCalculateComponentsDiagnostics>0){

                            if(input$selVizNodeColorComponentType=="Single-Layer"){
                                #color-code by single-layer component
                                idx.tmp <- which(listComponentsSingleLayer[[l]]$Component>0)
                                
                                if( input$selComponentColorPalette=="random" ){
                                    colorPalette <- rainbow( max(listComponentsSingleLayer[[l]]$Component) + 2, 
                                                                      alpha=as.numeric(input$txtNODE_TRANSP), 
                                                                      start=runif(1))    
                                }else{
                                    #use a palette
                                    colorPalette <- colorRampPalette(brewer.pal(brewer.pal.info$maxcolors[row.names(brewer.pal.info)==input$selComponentColorPalette],input$selComponentColorPalette))(max(listComponentsSingleLayer[[l]]$Component))
                                }

                                tmpColor[idx.tmp] <- colorPalette[ listComponentsSingleLayer[[l]]$Component[idx.tmp] ]
                            }else{
                                #color-code by multilayer component
                                idx.tmp <- which(listComponents[[l]]$Component>0)
                                
                                if( input$selComponentColorPalette=="random" ){
                                    #for the multiplex we want exactly the opposite behavior
                                    colorPalette <- rainbow( max(listComponentsMerge$Component) + 2, 
                                                                      alpha=as.numeric(input$txtNODE_TRANSP), 
                                                                      start=commonRunif)
                                }else{
                                    #use a palette
                                    colorPalette <- colorRampPalette(brewer.pal(brewer.pal.info$maxcolors[row.names(brewer.pal.info)==input$selComponentColorPalette],input$selComponentColorPalette))(max(listComponentsMerge$Component))
                                }
                                
                                tmpColor[idx.tmp] <- colorPalette[ listComponents[[l]]$Component[idx.tmp] ]
                            }
                        }
                    }
                    V(g[[l]])$color <- tmpColor
                }else if(input$radNodeColor=="NODE_COLOR_RANDOM"){
                    #colorset for the multiplex                
                    if( input$selMultiplexColorPalette=="random" ){
                        Rcolor <- sample(0:255, 1, replace=T)
                        Gcolor <- sample(0:255, 1, replace=T)
                        Bcolor <- sample(0:255, 1, replace=T)

                        #assign the color to the layer
#                        E(g[[l]])$red <- Rcolor
#                        E(g[[l]])$green <- Gcolor
#                        E(g[[l]])$blue <- Bcolor
                        V(g[[l]])$red <- Rcolor
                        V(g[[l]])$green <- Gcolor
                        V(g[[l]])$blue <- Bcolor
                    
#                        E(g[[l]])$color<-rgb(red=E(g[[l]])$red, 
#                                                        green=E(g[[l]])$green, 
#                                                        blue=E(g[[l]])$blue, 
#                                                        maxColorValue=255)
                        V(g[[l]])$color <- rgb(red=V(g[[l]])$red, 
                                                            green=V(g[[l]])$green, 
                                                            blue=V(g[[l]])$blue, 
                                                            maxColorValue=255)
                    }else{
                        colorPalette <- colorRampPalette(brewer.pal(brewer.pal.info$maxcolors[row.names(brewer.pal.info)==input$selMultiplexColorPalette],input$selMultiplexColorPalette))(LAYERS+1)
#                        E(g[[l]])$color <- colorPalette[l]
                        V(g[[l]])$color <- colorPalette[l]
                    }
                }else if(input$radNodeColor=="NODE_COLOR_UNIFORM"){
                    #E(g[[l]])$color <- input$colNODE_COLOR_UNIFORM_COLOR
                    V(g[[l]])$color <- input$colNODE_COLOR_UNIFORM_COLOR
                }else if(input$radNodeColor=="NODE_COLOR_CENTRALITY"){
                    if(diagnosticsOK){
                        bins <- as.numeric(input$txtNODE_COLOR_CENTRALITY_BINS)
                        colorPalette <- colorRampPalette(brewer.pal(brewer.pal.info$maxcolors[row.names(brewer.pal.info)==input$selCentralityColorPalette],input$selCentralityColorPalette))(bins)

                        attrib <- input$selVizNodeColorID
                        values <- NULL

                        if( length(grep("Multi-",attrib))>0 ){
                            values <- as.numeric(listDiagnostics[[l]][ gsub("Multi-","",attrib) ][,1])
                        }else{
                            values <- as.numeric(listDiagnosticsSingleLayer[[l]][ attrib ][,1])
                        }
                        
                        if(input$radNodeColorType2=="NODE_COLOR_PROPORTIONAL_TYPE_LOG"){
                            values <- 1+2*log(1+values)
                        }else if(input$radNodeColorType2=="NODE_COLOR_PROPORTIONAL_TYPE_LOGLOG"){
                            values <- log(1+log(1+values))
                        }

                        values <- 1 + (bins-1)*(values - min(values, na.rm=T))/(max(values, na.rm=T) - min(values, na.rm=T))
                        values <- floor(values)
                        
                        #E(g[[l]])$color <- as.character(input$colEDGE_COLOR)
                        V(g[[l]])$color <- colorPalette[ values ]
                    }
                }else if(input$radNodeColor=="NODE_COLOR_TOPRANK"){
                    if(diagnosticsOK){
                        if(input$btnCalculateCentralityDiagnostics>0 && as.numeric(input$txtNODE_COLOR_TOP)>0){
                            numTop <- as.numeric(input$txtNODE_COLOR_TOP)
                            attrib <- input$selVizNodeColorTopID
                            values <- NULL
                            
                            if( length(grep("Multi-",attrib))>0 ){
                                values <- as.numeric(listDiagnostics[[l]][ gsub("Multi-","",attrib) ][,1])
                            }else{
                                values <- as.numeric(listDiagnosticsSingleLayer[[l]][ attrib ][,1])
                            }

                            topNodes <- head(rev(order(values)),numTop)
                            #V(g[[l]])$color <- rgb(169,169,169, V(g[[l]])$alpha, maxColorValue=255)
                            #E(g[[l]])$color <- rgb(169,169,169, V(g[[l]])$alpha, maxColorValue=255)
                            V(g[[l]])$color <- input$colNODE_COLOR_TOP_COLOR_OTHERS
                            E(g[[l]])$color <- input$colNODE_COLOR_TOP_COLOR_OTHERS
                            #V(g[[l]])[topNodes]$color <- rgb(255, 0, 0, V(g[[l]])[topNodes]$alpha, maxColorValue=255)
                            V(g[[l]])[topNodes]$color <- input$colNODE_COLOR_TOP_COLOR_TOP
                            
                            if(input$chkNODE_LABELS_SHOW_ONLY_TOP){
                                V(g[[l]])$label <- ""
                                V(g[[l]])[topNodes]$label <- nodesLabel[[l]][topNodes]
                                V(g[[l]])[topNodes]$vertex.label.color <- input$colNODE_COLOR_TOP_LABELS_FONT_COLOR
                            }
                        }
                    }
                }else if(input$radNodeColor=="NODE_COLOR_QUERY"){
                    V(g[[l]])$color <- input$colQUERY_NODES_NODE_OTHER_COLOR
                    
                    if(input$btnQuery>0 && input$selQueryType=="Nodes"){                        
                        sub.nodes <- c()
                        sub.neighs <- c()
                        
                        for(layer.str in input$selQueryNodesLayerID){
                            l.tmp <- as.numeric(strsplit(layer.str, " ")[[1]][1])
                            if(l==l.tmp){
                                for(node.str in input$selQueryNodesNodeID){
                                    n <- as.numeric(strsplit(node.str, " ")[[1]][1])
                                    sub.nodes <- union(sub.nodes, n)

                                    neighs <- as.numeric(neighbors(g[[l]], mode="all", v=n))
                                    if(length(neighs)>0){
                                        sub.neighs <- union(sub.neighs, neighs)
                                    }
                                }
                                
                                #we now know all nodes of this layer to be colored
                                
                                if(length(sub.neighs)>0){
                                    V(g[[l]])$color[sub.neighs] <- input$colQUERY_NODES_NODE_NEIGH_COLOR
                                }
                                if(length(sub.nodes)>0){
                                    V(g[[l]])$color[sub.nodes] <- input$colQUERY_NODES_NODE_COLOR
                                }
                                
                                union.nodes <- union(sub.neighs,sub.nodes)
                                if(input$chkNODE_LABELS_SHOW_ONLY_QUERY && length(union.nodes)>0){
                                    V(g[[l]])[union.nodes]$label <- nodesLabel[[l]][union.nodes]
                                    V(g[[l]])[union.nodes]$vertex.label.color <- input$colNODE_COLOR_QUERY_LABELS_FONT_COLOR
                                }
                            }
                        }
                    }
                }

                if(input$chkNODE_ISOLATED_HIDE){
                    #this piece of code must be executed after the above one, to change the size of isolated
                    #nodes to zero, and also their label to ""
                    
                    if(input$radMultiplexModel == "MULTIPLEX_IS_EDGECOLORED"){
                        arrayStrength <- graph.strength(g[[l]],mode="total")
                        V(g[[l]])[arrayStrength==0.]$size <- 0
                        V(g[[l]])[arrayStrength==0.]$label <- ""
                    }else{
                        if(input$chkNODE_ISOLATED_HIDE_INTERLINKS){
                            #account for degree in the multiplex
                            
                            arrayStrength <- graph.strength(g.multi,mode="total")
                            idxtohide <- which(arrayStrength==0.)
                            inlayers <- floor((idxtohide-1)/Nodes) + 1
                            innodes <- (idxtohide-1) %% Nodes + 1

                            idxs <- which(inlayers==l)
                            nodes2hide <- which(V(g[[l]]) %in% innodes[idxs])
                            V(g[[l]])[nodes2hide]$size <- 0
                            V(g[[l]])[nodes2hide]$label <- ""
                        }else{
                            #do not account for interlinks, just intralinks
                            arrayStrength <- graph.strength(g[[l]],mode="total")
                            V(g[[l]])[arrayStrength==0.]$size <- 0
                            V(g[[l]])[arrayStrength==0.]$label <- ""
                        }
                    }
                }

                V(g[[l]])$shape <- "circle"
                V(g[[l]])$shape[V(g[[l]])$size==0] <- "none"                    
                E(g[[l]])$color <- addalpha(E(g[[l]])$color, as.numeric(input$txtEDGE_TRANSP))
                V(g[[l]])$color <- addalpha(V(g[[l]])$color, as.numeric(input$txtNODE_TRANSP))
                V(g[[l]])$framecolor <- input$txtNODE_FRAME_COLOR

                if(input$txtNODE_FRAME_COLOR==""){ V(g[[l]])$framecolor <- V(g[[l]])$color }

                if(input$chkNODE_LABELS_SHOW_WRAP){
                    V(g[[l]])$label2 <- lapply(lapply(V(g[[l]])$label, function(x) strwrap(x,as.numeric(input$txtNODE_LABELS_WRAP))), function(x) paste(x, collapse='\n'))
                }else{
                    V(g[[l]])$label2 <- V(g[[l]])$label
                }


                #saving default values for later usage
                defaultVsize[[l]] <<- V(g[[l]])$size
                defaultVcolor[[l]] <<- V(g[[l]])$color
                defaultEsize[[l]] <<- E(g[[l]])$size
                defaultEcolor[[l]] <<- E(g[[l]])$color
                nodesLabel2[[l]] <<- V(g[[l]])$label2
                
                if(input$chkPLOT_WITH_RGL){
                    print("  openGL phase...")

#print(V(g[[l]])$size)
#print(V(g[[l]])$color)
#print(V(g[[l]])$vertex.label.color)


                    #plot the graph with openGL    
                    #print(layouts[[l]])
                    rglplot(g[[l]], layout=layouts[[l]],
                                        vertex.size=V(g[[l]])$size, 
                                        vertex.color=V(g[[l]])$color,
                                        vertex.label="",#V(g[[l]])$label,
                                        vertex.label.dist=as.numeric(input$txtNODE_LABELS_DISTANCE), #,+ 0.01*V(g[[l]])$size,
                                        vertex.label.font=2,
                                        vertex.label.cex=0, 
                                        vertex.label.color=V(g[[l]])$vertex.label.color,
                                        edge.width=E(g[[l]])$size, 
                                        edge.color=E(g[[l]])$color, 
                                        edge.arrow.size=as.numeric(input$txtLAYER_ARROW_SIZE), 
                                        edge.arrow.width=as.numeric(input$txtLAYER_ARROW_WIDTH), 
                                        edge.curved=E(g[[l]])$curve,
                                        rescale=F)
                }else{
                    print("  Standard device output...")

                    #plot the graph with openGL    
                    #print(layouts[[l]])
                    plot.igraph(g[[l]], layout=layouts[[l]],
                                    vertex.size=V(g[[l]])$size, 
                                    vertex.shape=V(g[[l]])$shape,
                                    vertex.color=V(g[[l]])$color,
                                    vertex.frame.color=V(g[[l]])$framecolor,
                                    vertex.label=V(g[[l]])$label2,
                                    vertex.label.dist=as.numeric(input$txtNODE_LABELS_DISTANCE), #,+ 0.01*V(g[[l]])$size,
                                    vertex.label.font=2,
                                    vertex.label.cex=as.numeric(input$txtNODE_LABELS_FONT_SIZE), 
                                    vertex.label.color=V(g[[l]])$vertex.label.color,
                                    edge.width=E(g[[l]])$size, 
                                    edge.color=E(g[[l]])$color, 
                                    edge.arrow.size=as.numeric(input$txtLAYER_ARROW_SIZE), 
                                    edge.arrow.width=as.numeric(input$txtLAYER_ARROW_WIDTH), 
                                    edge.curved=E(g[[l]])$curve,
                                    rescale=F, add=T)
                                    
                        title(main=input$txtPLOT_TITLE, sub=input$txtPLOT_SUBTITLE)
                }
                print(paste("  Layout of layer: finished."))
            }
            
            if(input$chkINTERLINK_SHOW && LAYERS>1){
                if(input$radMultiplexModel!="MULTIPLEX_IS_EDGECOLORED"){
                    print("Adding interlayer links.")

                    #set default color
                    E(g.multi)$color <- input$colINTERLINK_COLOR

                    #set to 0 the width of intra-layer links
                    E(g.multi)$width <- as.numeric(input$txtINTERLINK_WIDTH)*E(g.multi)$weight
                    E(g.multi)[which(multilayerEdges[,2]==multilayerEdges[,4])]$width <- 0
                    
                    #the same for interlinks from and to inactive layers 
                    for(l in vecInactiveLayers){
                        E(g.multi)[which(multilayerEdges[,2]==l | multilayerEdges[,4]==l)]$width <- 0
                    }

                    if(input$radEdgeSizeType=="EDGE_SIZE_PROPORTIONAL_TO_EXTERNAL"){
                        if(externalEdgeSizeFlag){
                            #Here we set only intra-layer links. Inter-links are considered later, if they are shown

                            #inter-links set
                            size.set <- externalEdgeColorTable[ externalEdgeColorTable$layerID.from != externalEdgeColorTable$layerID.to, ]

                            if(nrow(size.set)>0){
                                #relabeling
                                size.set$nodeID.from <- size.set$nodeID.from + Nodes*(size.set$layerID.from-1)
                                size.set$nodeID.to <- size.set$nodeID.to + Nodes*(size.set$layerID.to-1)

                                idxs <- get.edge.ids(g.multi, as.vector(rbind(size.set$nodeID.from, size.set$nodeID.to)), directed=DIRECTED)
                                E(g.multi)[idxs]$width <- size.set$size
                            }
                        }
                    }

                    if(input$radEdgeColor=="EDGE_COLOR_EXTERNAL"){
                        #Here we set only inter-layer links. Intra-links are considered above
                    
                        if(externalEdgeColorFlag){
                            E(g.multi)$color <- input$colEdgeColorFileDefaultEdgesColor
                            
                            color.set <- externalEdgeColorTable[ externalEdgeColorTable$layerID.from != externalEdgeColorTable$layerID.to, ]
    
                            if(nrow(color.set)>0){
                                #relabeling
                                color.set$nodeID.from <- color.set$nodeID.from + Nodes*(color.set$layerID.from-1)
                                color.set$nodeID.to <- color.set$nodeID.to + Nodes*(color.set$layerID.to-1)

                                idxs <- get.edge.ids(g.multi, as.vector(rbind(color.set$nodeID.from, color.set$nodeID.to)), directed=DIRECTED)
                                E(g.multi)[idxs]$color <- color.set$color
                            }
                        }
                    }

                    #setup the layout for g.multi by merging the layout of each layer, in order
                    layout.multi <<- matrix(0, ncol=3, nrow=Nodes*LAYERS)
                    
                    for(l in 1:LAYERS){
                        layout.multi[ (1 + (l-1)*Nodes):(l*Nodes), 1] <<- layouts[[l]][, 1]
                        layout.multi[ (1 + (l-1)*Nodes):(l*Nodes), 2] <<- layouts[[l]][, 2]
                        layout.multi[ (1 + (l-1)*Nodes):(l*Nodes), 3] <<- layouts[[l]][, 3]
                    }

                    #Print the interlinks by superimposing the g.multi
                    if(input$chkPLOT_WITH_RGL){
                        rglplot(g.multi, layout=layout.multi,
                                            vertex.size=0, 
                                            vertex.label="",
                                            vertex.label.cex=0,
                                            edge.width=E(g.multi)$width, 
                                            edge.color=E(g.multi)$color, 
                                            edge.arrow.size=as.numeric(input$txtLAYER_ARROW_SIZE), 
                                            edge.arrow.width=as.numeric(input$txtLAYER_ARROW_WIDTH), 
                                            edge.curved=as.numeric(input$txtEDGE_BENDING),
                                            edge.lty = input$selINTERLINK_TYPE,
                                            rescale=F)
                    }else{
                        #edge/node transparancy not yet supported by rglplot
                        #alpha=as.numeric(input$txtINTERLINK_TRANSP))
                        E(g.multi)$color <- addalpha(E(g.multi)$color, as.numeric(input$txtINTERLINK_TRANSP))

                        plot.igraph(g.multi, layout=layout.multi,
                                    vertex.size=0, 
                                    vertex.shape="none",
                                    vertex.label="",
                                    vertex.label.cex=0,
                                    edge.width=E(g.multi)$width, 
                                    edge.color=E(g.multi)$color, 
                                    edge.arrow.size=as.numeric(input$txtLAYER_ARROW_SIZE), 
                                    edge.arrow.width=as.numeric(input$txtLAYER_ARROW_WIDTH), 
                                    edge.curved=as.numeric(input$txtEDGE_BENDING),
                                    edge.lty = input$selINTERLINK_TYPE,
                                    rescale=F, add=T)
                    }
                }                
            }                

                                
            if(input$chkPLOT_WITH_RGL){
                #Call the visualization of other RGL graphics
                FinalizeRenderingMultiplex(progress)
            }
            
            progress$set(message = 'Rendering Completed!', value = 1)
            Sys.sleep(2)
    	 }
    	 
        observeEvent(input$btnRenderNetworks, {
            if(input$btnApplyLayout==0 || input$btnImportNetworks == 0 || LAYERS<=0) return()
            
            isolate({    
                makeRendering()
            })
        }, ignoreInit = TRUE)
        
        ############################################
        ## Global functions
        ############################################
        
        FinalizeRenderingMultiplex <- function(progress){
            #Towards the end.. add layers, textures if any, etc
            #Create the hash of the properties of the map, to avoid downloading multiple times
            #the same area 
            thisLATMIN <- LATMIN
            thisLATMAX <- LATMAX
            thisLONGMIN <- LONGMIN
            thisLONGMAX <- LONGMAX
            if(input$txtGEOGRAPHIC_LAT_MIN != "" && input$txtGEOGRAPHIC_LAT_MAX != "" && input$txtGEOGRAPHIC_LONG_MIN != "" && input$txtGEOGRAPHIC_LONG_MAX != ""){
                thisLATMIN <- as.numeric(input$txtGEOGRAPHIC_LAT_MIN)
                thisLATMAX <- as.numeric(input$txtGEOGRAPHIC_LAT_MAX)
                thisLONGMIN <- as.numeric(input$txtGEOGRAPHIC_LONG_MIN)
                thisLONGMAX <- as.numeric(input$txtGEOGRAPHIC_LONG_MAX)
            }
            #print(paste(thisLATMIN,thisLATMAX,thisLONGMIN,thisLONGMAX))
            #mapproject has problems in converting |latitudes| > 80, therefore we have to constrain
            if(thisLATMIN < -80){
                thisLATMIN <- -80
                print("Warning! Min Latitude smaller than -80, changing to -80.")
            }
            if(thisLATMAX > 80){
                thisLATMAX <- 80
                print("Warning! Max Latitude larger than 80, changing to 80.") 
            }
            #the geographical maps give error for extremal longitudes. Constrain here too..
            if(thisLONGMIN < -179){
                thisLONGMIN <- -179
                print("Warning! Min Longitude smaller than -179, changing to -179.")
            }
            if(thisLONGMAX>179){
                thisLONGMAX <- 179
                print("Warning! Max Longitude larger than 179, changing to 179.") 
            }
            
            hash <- digest( c(thisLATMIN,thisLATMAX,thisLONGMIN,thisLONGMAX,input$selOSMType) , algo="md5" )
            fileNamePNG <- buildTmpPath(paste(hash,".png",sep=""))
            #print(paste("COORDS:",LATMIN,LATMAX,LONGMIN,LONGMAX,XMIN,XMAX,YMIN,YMAX))
            if(!file.exists(fileNamePNG)){
                if(GEOGRAPHIC_LAYOUT && (input$chkGEOGRAPHIC_BOUNDARIES_SHOW || input$chkGEOGRAPHIC_BOUNDARIES_AGGREGATE_SHOW)){
                    progress$set(message = 'Downloading map...', value = 0.95)
                    Sys.sleep(1)
                    print(paste("  Downloading geographic area..."))
                    #create a map with openstreetmap and save to a file for later use
                    rescaleFactor <- (YMAX-YMIN)/(XMAX-XMIN)
                    #H0/W0 = H/W  --> H = W/W0 * H0 = W*rescaleFactor in terms of Cartesian coords
                    pngWidth = 720
                    pngHeight = pngWidth*rescaleFactor
                    png(filename=fileNamePNG,width=pngWidth,height=pngHeight)
                    map = openmap(c(lat=thisLATMAX,   lon=thisLONGMIN), c(lat=thisLATMIN,   lon=thisLONGMAX), minNumTiles=18,type=input$selOSMType)
                    plot(map)
                    dev.off()
                }
            }
                        
            progress$set(message = 'Finalizing rendering...', value = 0.95)

            #inactive layers
            vecInactiveLayers <- as.numeric(strsplit(input$txtLAYERS_ACTIVE,",")[[1]])
    
            if(input$chkLAYER_SHOW && !input$chkPLOT_AS_EDGE_COLORED){
                if(input$radNetworkOfLayersLayoutType=="NETWORK_LAYERS_LAYOUT_ONELINE"){
                    #the standard one-line visualization made my muxViz, no changes wrt previous versions
                    for(l in 1:(LAYERS+1)){
                        if(l %in% vecInactiveLayers){
                            #skip inactive layers
                            next
                        }
                        
                        if(LAYERS>1){
                            #This draws a plan to be used as layer
                            if(input$chkAGGREGATE_SHOW){
                                d <- -1 + as.numeric(input$txtLAYER_SCALE)*as.numeric(input$txtLAYER_SPACE)*l/(LAYERS+1)
                            }else{
                                d <- -1 + as.numeric(input$txtLAYER_SCALE)*as.numeric(input$txtLAYER_SPACE)*l/LAYERS
                            }
                        }else{
                            #to allow drawing single-layer networks
                            d <- 1
                        }
                
                        x <- c(-1,-1,-1+as.numeric(input$txtLAYER_SCALE),-1+as.numeric(input$txtLAYER_SCALE)) + (l-1)*as.numeric(input$txtLAYER_SHIFT_X)
                        y <- c(-1+as.numeric(input$txtLAYER_SCALE),-1,-1,-1+as.numeric(input$txtLAYER_SCALE)) + (l-1)*as.numeric(input$txtLAYER_SHIFT_Y)
                        z <- c(d,d,d,d)

                        if(LAYOUT_DIMENSION==2){
                            if(l<LAYERS+1){
                                #planes3d(0,0,1, -d , alpha=LAYER_TRANSP, col=LAYER_COLOR)
                                if(GEOGRAPHIC_LAYOUT && input$chkGEOGRAPHIC_BOUNDARIES_SHOW){
                                    quads3d(x,y,z, alpha=layerColorAlpha[[l]], col=layerColor[[l]],texcoords=cbind(c(0,0,1,1), -c(0,1,1,0)), texture=fileNamePNG)
                                }else{
                                    quads3d(x,y,z, alpha=layerColorAlpha[[l]], col=layerColor[[l]])
                                }
                            }else{
                                if(input$chkAGGREGATE_SHOW && LAYERS>1){
                                    #planes3d(0,0,1, -d , alpha=LAYER_AGGREGATE_TRANSP, col=LAYER_AGGREGATE_COLOR)
                                    if(GEOGRAPHIC_LAYOUT && input$chkGEOGRAPHIC_BOUNDARIES_AGGREGATE_SHOW){
                                        quads3d(x,y,z, alpha=as.numeric(input$txtLAYER_AGGREGATE_TRANSP), col=input$colLAYER_AGGREGATE_COLOR,texcoords=cbind(c(0,0,1,1), -c(0,1,1,0)), texture=fileNamePNG)
                                    }else{
                                        quads3d(x,y,z, alpha=as.numeric(input$txtLAYER_AGGREGATE_TRANSP), col=input$colLAYER_AGGREGATE_COLOR)                    
                                    }
                                }else{
                                    next
                                }
                            }
                                            
                            if(input$chkLAYER_ID_SHOW_BOTTOMLEFT){
                                text3d(-1+(l-1)*as.numeric(input$txtLAYER_SHIFT_X), -1+(l-1)*as.numeric(input$txtLAYER_SHIFT_Y), d+0.1,text=layerLabel[[l]][1],adj = 0.2, color="black", family="sans", cex=as.numeric(input$txtLAYER_ID_FONTSIZE))
                            }
                            if(input$chkLAYER_ID_SHOW_TOPLEFT){
                                text3d(-1+(l-1)*as.numeric(input$txtLAYER_SHIFT_X), -1 +(l-1)*as.numeric(input$txtLAYER_SHIFT_Y) + as.numeric(input$txtLAYER_SCALE), d+0.1,text=layerLabel[[l]][1],adj = 0.2, color="black", family="sans", cex=as.numeric(input$txtLAYER_ID_FONTSIZE))
                            }
                            if(input$chkLAYER_ID_SHOW_BOTTOMRIGHT){
                                text3d(-1+(l-1)*as.numeric(input$txtLAYER_SHIFT_X)+as.numeric(input$txtLAYER_SCALE), -1 +(l-1)*as.numeric(input$txtLAYER_SHIFT_Y), d+0.1,text=layerLabel[[l]][1],adj = 0.2, color="black", family="sans", cex=as.numeric(input$txtLAYER_ID_FONTSIZE))
                            }
                            if(input$chkLAYER_ID_SHOW_TOPRIGHT){
                                text3d(-1+(l-1)*as.numeric(input$txtLAYER_SHIFT_X)+as.numeric(input$txtLAYER_SCALE), -1 + as.numeric(input$txtLAYER_SCALE) +(l-1)*as.numeric(input$txtLAYER_SHIFT_Y), d+0.1,text=layerLabel[[l]][1],adj = 0.2, color="black", family="sans", cex=as.numeric(input$txtLAYER_ID_FONTSIZE))
                            }
                        }
                    }
                }else{
                    #new network of layers layouts
                    if(input$radNetworkOfLayersLayoutType=="NETWORK_LAYERS_LAYOUT_MULTILINE"){
                        if(LAYERS>1){                            
                            rows <- as.numeric(input$txtNetworkLayersMultilineRows)
                            cols <- as.numeric(input$txtNetworkLayersMultilineCols)

                            #estimate the number of levels in the third dimension
                            multilevels <- floor(LAYERS/(rows*cols)) + 1
                            if( LAYERS %% (rows*cols)==0 ){
                                #if the ratio is exact, we don't need one more level in the worst case
                                multilevels <- multilevels-1
                            }


                            scal <- as.numeric(input$txtLAYER_SCALE)
                            rescx <- scal/(XMAX-XMIN)
                            rescy <- scal/(YMAX-YMIN)
                            #shift <- as.numeric(input$txtLAYER_SHIFT_X) #useless for this layout
                            space <- as.numeric(input$txtLAYER_SPACE)

                            shiftx <- cols*(scal + space)/2
                            shifty <- rows*(scal + space)/2

                            rowcnt <- 1
                            colcnt <- 1
                            levelcnt <- 1
                            for(l in 1:LAYERS){
                                d <- 1 - scal*2*space*(levelcnt-1)/LAYERS
                                #when scal=1 x ranges in [-1,0]   y [-1,0]
                                #try: rgl.clear();axes3d();quads3d(c(-1,-1,0,0),c(0,-1,-1,0),c(0,0,0,0),col='green')
                                x <- c(-1,-1,-1+scal,-1+scal) - shiftx + (colcnt-1)*(scal + space)
                                y <- c(-1+scal,-1,-1,-1+scal) + shifty - (rowcnt-1)*(scal + space)
                                z <- c(d,d,d,d)

                                if(!l %in% vecInactiveLayers){
                                    #skip inactive layers
                                    if(GEOGRAPHIC_LAYOUT && input$chkGEOGRAPHIC_BOUNDARIES_SHOW){
                                        quads3d(x,y,z, alpha=layerColorAlpha[[l]], col=layerColor[[l]], 
                                                        texcoords=cbind(c(0,0,1,1), -c(0,1,1,0)), texture=fileNamePNG)
                                    }else{
                                        quads3d(x,y,z, alpha=layerColorAlpha[[l]], col=layerColor[[l]])
                                    }
                                                                        
                                    if(input$chkLAYER_ID_SHOW_TOPLEFT){
                                        text3d(-1- shiftx + (colcnt-1)*(scal + space), 
                                                    -1 + scal + shifty - (rowcnt-1)*(scal + space), 
                                                    d, 
                                                    text=layerLabel[[l]][1], 
                                                    adj = 0.2, 
                                                    color="black", family="sans", cex=as.numeric(input$txtLAYER_ID_FONTSIZE))
                                    }
                                    if(input$chkLAYER_ID_SHOW_BOTTOMLEFT){
                                        text3d(-1- shiftx + (colcnt-1)*(scal + space), 
                                                    -1 + shifty - (rowcnt-1)*(scal + space), 
                                                    d, 
                                                    text=layerLabel[[l]][1], 
                                                    adj = 0.2, 
                                                    color="black", family="sans", cex=as.numeric(input$txtLAYER_ID_FONTSIZE))
                                    }
                                    if(input$chkLAYER_ID_SHOW_BOTTOMRIGHT){
                                        text3d(-1 + scal - shiftx + (colcnt-1)*(scal + space), 
                                                    -1 + shifty - (rowcnt-1)*(scal + space), 
                                                    d, 
                                                    text=layerLabel[[l]][1], 
                                                    adj = 0.2, 
                                                    color="black", family="sans", cex=as.numeric(input$txtLAYER_ID_FONTSIZE))
                                    }
                                    if(input$chkLAYER_ID_SHOW_TOPRIGHT){
                                        text3d(-1 + scal - shiftx + (colcnt-1)*(scal + space), 
                                                    -1 + scal + shifty - (rowcnt-1)*(scal + space), 
                                                    d, 
                                                    text=layerLabel[[l]][1], 
                                                    adj = 0.2, 
                                                    color="black", family="sans", cex=as.numeric(input$txtLAYER_ID_FONTSIZE))
                                    }
                                }

                                colcnt <- colcnt + 1
                                if(colcnt==(cols+1)){
                                    rowcnt <- rowcnt + 1
                                    
                                    if(rowcnt==(rows+1)){
                                        levelcnt <- levelcnt + 1
                                        rowcnt <- 1
                                    }
                                    
                                    colcnt <- 1
                                }
                            }
                        }else{
                            progress$set(message = 'This layout require more than one layer!', value = 0.9)
                        }                        
                    }
                    if(input$radNetworkOfLayersLayoutType=="NETWORK_LAYERS_LAYOUT_FORCEDIRECTED"){
                        if(LAYERS>1){                            

                            scal <- as.numeric(input$txtLAYER_SCALE)
                            #shift <- as.numeric(input$txtLAYER_SHIFT_X) #useless for this layout
                            #space <- as.numeric(input$txtLAYER_SPACE)
                              
                            #this should have been already calculated and stored..  
                            #layout.non <<- layout.auto(g.non, dim=3)
                            #layout.non[,1] <<- (layout.non[,1] - min(layout.non[,1]))/(max(layout.non[,1])-min(layout.non[,1]))
                            #layout.non[,2] <<- (layout.non[,2] - min(layout.non[,2]))/(max(layout.non[,2])-min(layout.non[,2]))
                            #layout.non[,3] <<- (layout.non[,3] - min(layout.non[,3]))/(max(layout.non[,3])-min(layout.non[,3]))
                            #layout.non <<- scal*layout.non + scal
                            #print(layout.non)
                            

                            for(l in 1:LAYERS){
                                d <- layout.non[l,3]
                                shiftx <- layout.non[l,1]
                                shifty <- layout.non[l,2]
                                #when scal=1 x ranges in [-1,0]   y [-1,0]
                                #try: rgl.clear();axes3d();quads3d(c(-1,-1,0,0),c(0,-1,-1,0),c(0,0,0,0),col='green')
                                x <- c(-1,-1,-1+scal,-1+scal) - shiftx
                                y <- c(-1+scal,-1,-1,-1+scal) - shifty
                                z <- c(d,d,d,d)

                                if(!l %in% vecInactiveLayers){
                                    #skip inactive layers
                                    if(GEOGRAPHIC_LAYOUT && input$chkGEOGRAPHIC_BOUNDARIES_SHOW){
                                        quads3d(x,y,z, alpha=layerColorAlpha[[l]], col=layerColor[[l]], 
                                                        texcoords=cbind(c(0,0,1,1), -c(0,1,1,0)), texture=fileNamePNG)
                                    }else{
                                        quads3d(x,y,z, alpha=layerColorAlpha[[l]], col=layerColor[[l]])
                                    }
                                                                        
                                    if(input$chkLAYER_ID_SHOW_TOPLEFT){
                                        text3d(-1 - shiftx, 
                                                    -1 + scal - shifty, 
                                                    d, 
                                                    text=layerLabel[[l]][1], 
                                                    adj = 0.2, 
                                                    color="black", family="sans", cex=as.numeric(input$txtLAYER_ID_FONTSIZE))
                                    }
                                    if(input$chkLAYER_ID_SHOW_BOTTOMLEFT){
                                        text3d(-1 - shiftx, 
                                                    -1 - shifty, 
                                                    d, 
                                                    text=layerLabel[[l]][1], 
                                                    adj = 0.2, 
                                                    color="black", family="sans", cex=as.numeric(input$txtLAYER_ID_FONTSIZE))
                                    }
                                    if(input$chkLAYER_ID_SHOW_BOTTOMRIGHT){
                                        text3d(-1 + scal  - shiftx, 
                                                    -1  - shifty, 
                                                    d, 
                                                    text=layerLabel[[l]][1], 
                                                    adj = 0.2, 
                                                    color="black", family="sans", cex=as.numeric(input$txtLAYER_ID_FONTSIZE))
                                    }
                                    if(input$chkLAYER_ID_SHOW_TOPRIGHT){
                                        text3d(-1 + scal  - shiftx, 
                                                    -1 + scal - shifty, 
                                                    d, 
                                                    text=layerLabel[[l]][1], 
                                                    adj = 0.2, 
                                                    color="black", family="sans", cex=as.numeric(input$txtLAYER_ID_FONTSIZE))
                                    }
                                }
                            }
                        }else{
                            progress$set(message = 'This layout require more than one layer!', value = 0.9)
                        }                        
                    }
                    if(input$radNetworkOfLayersLayoutType=="NETWORK_LAYERS_LAYOUT_MATRIX"){
                        if(LAYERS>1){                            
                            rows <- as.numeric(input$txtNetworkLayersMatrixRows)
                            cols <- as.numeric(input$txtNetworkLayersMatrixCols)

                            if(rows*cols<LAYERS){
                                progress$set(message = 'ERROR! Rows x Columns < # Layers ...', value = 0.9)
                            }else{
                                scal <- as.numeric(input$txtLAYER_SCALE)
                                rescx <- scal/(XMAX-XMIN)
                                rescy <- scal/(YMAX-YMIN)
                                #shift <- as.numeric(input$txtLAYER_SHIFT_X) #useless for this layout
                                space <- as.numeric(input$txtLAYER_SPACE)

                                shiftx <- cols*(scal + space)/2
                                shifty <- rows*(scal + space)/2

                                rowcnt <- 1
                                colcnt <- 1
                                for(l in 1:LAYERS){
                                    d <- 0
                                    #when scal=1 x ranges in [-1,0]   y [-1,0]
                                    #try: rgl.clear();axes3d();quads3d(c(-1,-1,0,0),c(0,-1,-1,0),c(0,0,0,0),col='green')
                                    x <- c(-1,-1,-1+scal,-1+scal) - shiftx + (colcnt-1)*(scal + space)
                                    y <- c(-1+scal,-1,-1,-1+scal) + shifty - (rowcnt-1)*(scal + space)
                                    z <- c(d,d,d,d)

                                    if(!l %in% vecInactiveLayers){
                                        #skip inactive layers
                                        if(GEOGRAPHIC_LAYOUT && input$chkGEOGRAPHIC_BOUNDARIES_SHOW){
                                            quads3d(x,y,z, alpha=layerColorAlpha[[l]], col=layerColor[[l]], 
                                                          texcoords=cbind(c(0,0,1,1), -c(0,1,1,0)), texture=fileNamePNG)
                                        }else{
                                            quads3d(x,y,z, alpha=layerColorAlpha[[l]], col=layerColor[[l]])
                                        }
                                                                            
                                        if(input$chkLAYER_ID_SHOW_TOPLEFT){
                                            text3d(-1- shiftx + (colcnt-1)*(scal + space), 
                                                       -1 + scal + shifty - (rowcnt-1)*(scal + space), 
                                                       d, 
                                                       text=layerLabel[[l]][1], 
                                                       adj = 0.2, 
                                                       color="black", family="sans", cex=as.numeric(input$txtLAYER_ID_FONTSIZE))
                                        }
                                        if(input$chkLAYER_ID_SHOW_BOTTOMLEFT){
                                            text3d(-1- shiftx + (colcnt-1)*(scal + space), 
                                                       -1 + shifty - (rowcnt-1)*(scal + space), 
                                                       d, 
                                                       text=layerLabel[[l]][1], 
                                                       adj = 0.2, 
                                                       color="black", family="sans", cex=as.numeric(input$txtLAYER_ID_FONTSIZE))
                                        }
                                        if(input$chkLAYER_ID_SHOW_BOTTOMRIGHT){
                                            text3d(-1 + scal - shiftx + (colcnt-1)*(scal + space), 
                                                       -1 + shifty - (rowcnt-1)*(scal + space), 
                                                       d, 
                                                       text=layerLabel[[l]][1], 
                                                       adj = 0.2, 
                                                       color="black", family="sans", cex=as.numeric(input$txtLAYER_ID_FONTSIZE))
                                        }
                                        if(input$chkLAYER_ID_SHOW_TOPRIGHT){
                                            text3d(-1 + scal - shiftx + (colcnt-1)*(scal + space), 
                                                       -1 + scal + shifty - (rowcnt-1)*(scal + space), 
                                                       d, 
                                                       text=layerLabel[[l]][1], 
                                                       adj = 0.2, 
                                                       color="black", family="sans", cex=as.numeric(input$txtLAYER_ID_FONTSIZE))
                                        }
                                    }
                                    
                                    colcnt <- colcnt + 1
                                    if(colcnt==(cols+1)){
                                        colcnt = 1
                                        rowcnt <- rowcnt + 1
                                    }

                                }
                            }
                            
                            flag <- F
                            while(!flag){
                                tryCatch({
                                    print("Popping lights...")
                                    rgl.pop("lights")},
                                    error=function(e){
                                        print("Warning: no more lights to pop")
                                        },
                                    finally={flag=T}
                                    )
                            }
                            rgl.light(theta = 30, phi = 20, viewpoint.rel = TRUE, ambient = "#FFFFFF", 
                                        diffuse = "#FFFFFF", specular = "#FFFFFF")
                        }else{
                            progress$set(message = 'This layout require more than one layer!', value = 0.9)
                        }                        

                    }
                }            
            }
            
            #add labels as text3d because the vertex.label attribute of rgl.igraph does not work
            #if(input$chkNODE_LABELS_SHOW){
                for(l in 1:LAYERS){    
                    if(!l %in% vecInactiveLayers){
                        this.labels <- nodesLabel2[[l]]
                        if(input$chkNODE_ISOLATED_HIDE){
                            #this is to account for nodes isolated wrt intra-layer links, but not wrt inter-layer links
                            if(input$radMultiplexModel=="MULTIPLEX_IS_EDGECOLORED"){
                                arrayStrength <- graph.strength(g[[l]],mode="total")
                                this.labels[arrayStrength==0.] <- ""    
                            }else{
                                nodesOK <- union(multilayerEdges[ multilayerEdges$V2==l, ]$V1, multilayerEdges[ multilayerEdges$V4==l, ]$V3)                                
                                this.labels[-nodesOK] <- ""                                    
                            }
                        }
                        this.labels[ is.na(this.labels) ] <- ""

                        if(input$chkNODE_LABELS_SHOW_WRAP){
                            if(as.numeric(input$txtNODE_LABELS_WRAP)>0){
                                text3dwrap(layouts[[l]],
                                           this.labels,
                                           as.numeric(input$txtNODE_LABELS_WRAP),
                                           as.numeric(input$txtNODE_LABELS_WRAP_OFFSET),
                                           as.numeric(input$txtLAYER_SCALE),
                                           as.numeric(input$txtNODE_LABELS_DISTANCE), 
                                           input$colNODE_LABELS_FONT_COLOR, 
                                           "sans", 
                                           as.numeric(input$txtNODE_LABELS_FONT_SIZE)
                                           )
                            }
                        }else{
                            text3d(layouts[[l]],
                                       text=this.labels,
                                       adj = as.numeric(input$txtNODE_LABELS_DISTANCE), 
                                       color=input$colNODE_LABELS_FONT_COLOR, 
                                       family="sans", 
                                       cex=as.numeric(input$txtNODE_LABELS_FONT_SIZE)
                                       )
                        }
                    }
                }
            #}
                        
#            if(!LAYOUT_INDEPENDENT){
#                if(input$chkINTERLINK_SHOW && as.numeric(input$txtINTERLINK_SHOW_FRACTION)>0 && LAYERS>1){
#                    print("Adding interlayer links.")
#                    #to be generalized to allow cross-interlink and absence of interlinks for some nodes
#                    for( l in 1:(LAYERS-1) ){
#                        layerLinesX <- matrix(c(0),nrow=Nodes,ncol=2)
#                        layerLinesY <- matrix(c(0),nrow=Nodes,ncol=2)
#                        layerLinesZ <- matrix(c(0),nrow=Nodes,ncol=2)
#            
#                        layerLinesX <- cbind(layouts[[l]][,1] + (l-1)*as.numeric(input$txtLAYER_SHIFT_X),layouts[[l+1]][,1] + l*as.numeric(input$txtLAYER_SHIFT_X))
#                        layerLinesY <- cbind(layouts[[l]][,2],layouts[[l+1]][,2])
#                        layerLinesZ <- cbind(layouts[[l]][,3],layouts[[l+1]][,3])
#            
#                        for(i in 1:Nodes){
#                            if(runif(1)>1-as.numeric(input$txtINTERLINK_SHOW_FRACTION)){ 
#                                segments3d(
#                                    layerLinesX[i,],
#                                    layerLinesY[i,],
#                                    layerLinesZ[i,],
#                                    lwd=as.numeric(input$txtINTERLINK_WIDTH), 
#                                    col=input$colINTERLINK_COLOR, 
#                                    lty=input$selINTERLINK_TYPE,
#                                    alpha=as.numeric(input$txtINTERLINK_TRANSP))
#                            }
#                        }
#                    }
#                }
#            }
            
            if(!input$chkPLOT_AS_EDGE_COLORED){
                if(!input$chkPLOT_REMEMBER_ORIENTATION){
                    #forget current orientation and use the default one
                    M <- matrix(0, ncol=4,nrow=4)
                    M[1,] <- c(0.54,0,0.84,0)
                    M[2,] <- c(0.33,0.92,-0.22,0)
                    M[3,] <- c(-0.77,0.39,0.5,0)
                    M[4,] <- c(0,0,0,1)
                    
                    par3d(FOV=as.numeric(input$txtPLOT_FOV), userMatrix=M)
                }else{
                    #if orientation must be remembered but it's the first rendering:
                    if(input$btnRenderNetworks==1){
                        M <- matrix(0, ncol=4,nrow=4)
                        M[1,] <- c(0.54,0,0.84,0)
                        M[2,] <- c(0.33,0.92,-0.22,0)
                        M[3,] <- c(-0.77,0.39,0.5,0)
                        M[4,] <- c(0,0,0,1)
                        
                        par3d(FOV=as.numeric(input$txtPLOT_FOV), userMatrix=M)
                    }
                }
                
                #not really needed
                #par3D(orientationRGL)
            }
                        
            bg3d(input$colBACKGROUND_COLOR)
            title3d(input$txtPLOT_TITLE, input$txtPLOT_SUBTITLE,'','','')
            
            if(input$chkPLOT_LIGHT){
                #add a light
                rgl.light(phi=as.numeric(input$txtPLOT_LIGHT_PHI),theta=as.numeric(input$txtPLOT_LIGHT_THETA))
            }

            if(input$chkPLOT_AXES3D){
                #add a light
                axes3d()
            }

            print("Finalizing rendering...")    
        }
        
    
        GetCentralityDataFrameArray <- function(Type){
            tmplistDiagnostics <- NULL
            
            if(Type=="Multiplex"){
                #the output will be stored in [[l]] for the multiplex and [[LAYERS+1]] for the aggregated.
                
                for(l in 1:LAYERS){
                    tmplistDiagnostics[[l]] <- data.frame(Layer = rep(paste(l,"Multi",sep="-"),Nodes))
                    tmplistDiagnostics[[l]] <- cbind(tmplistDiagnostics[[l]],data.frame(Node = 1:Nodes))
                    tmplistDiagnostics[[l]] <- cbind(tmplistDiagnostics[[l]],data.frame(Label = nodesLabel[[l]]))
                }
                l <- (LAYERS+1)
                tmplistDiagnostics[[l]] <- data.frame(Layer = rep("Aggr",Nodes))
                tmplistDiagnostics[[l]] <- cbind(tmplistDiagnostics[[l]],data.frame(Node = 1:Nodes))
                tmplistDiagnostics[[l]] <- cbind(tmplistDiagnostics[[l]],data.frame(Label = nodesLabel[[l]]))

                if(input$chkNODE_CENTRALITY_STRENGTH){
                    progress2 <- shiny::Progress$new(session)
                    on.exit(progress2$close())
                    progress2$set(message = paste('Current: Strength...'), value = 0.5)
                    
                    if(input$radMultiplexModel!="MULTIPLEX_IS_INTERDEPENDENT"){

                        centralityVector <- GetMultiStrengthSum(SupraAdjacencyMatrix,LAYERS,Nodes,DIRECTED)
                                
                        for(l in 1:LAYERS){
                            tmplistDiagnostics[[l]] <- cbind(tmplistDiagnostics[[l]],data.frame(Strength = centralityVector))
                        }

                        l <- LAYERS+1        
                        centralityVector <- GetMultiStrengthSum(AdjMatrix[[l]],1,Nodes,DIRECTED);
                        
                        tmplistDiagnostics[[l]] <- cbind(tmplistDiagnostics[[l]],data.frame(Strength = centralityVector))
                    }else{
                        #for an interdependent network, it is enough to calculate centrality in the aggregate
                        #http://igraph.sourceforge.net/doc/R/graph.strength.html
                        centralityVector <- graph.strength(g[[LAYERS+1]],mode="total")
                        for(l in 1:(LAYERS+1)){
                            tmplistDiagnostics[[l]] <- cbind(tmplistDiagnostics[[l]],data.frame(Strength = centralityVector))
                        }
                    }
                    
                    progress2$set(message = paste('Current: Strength... Done!'), value = 1)
                    Sys.sleep(1)
                    progress2$close()
                }else{
                    for(l in 1:LAYERS){
                        tmplistDiagnostics[[l]] <- cbind(tmplistDiagnostics[[l]],data.frame(Strength = rep("-",Nodes)))
                    }
                    l <- (LAYERS+1)
                    tmplistDiagnostics[[l]] <- cbind(tmplistDiagnostics[[l]],data.frame(Strength = rep("-",Nodes)))
                }
    
                if(input$chkNODE_CENTRALITY_STRENGTH){
                    progress2 <- shiny::Progress$new(session)
                    on.exit(progress2$close())
                    progress2$set(message = paste('Current: In-Strength...'), value = 0.5)
                    
                    if(DIRECTED){
                        if(input$radMultiplexModel!="MULTIPLEX_IS_INTERDEPENDENT"){
                            centralityVector <- GetMultiInStrengthSum(SupraAdjacencyMatrix,LAYERS,Nodes,DIRECTED)
    
                            for(l in 1:LAYERS){
                                tmplistDiagnostics[[l]] <- cbind(tmplistDiagnostics[[l]],data.frame(StrengthIn = centralityVector))
                            }

                            l <- LAYERS+1            
                            centralityVector <- GetMultiInStrengthSum(AdjMatrix[[l]],1,Nodes,DIRECTED)

                            tmplistDiagnostics[[l]] <- cbind(tmplistDiagnostics[[l]],data.frame(StrengthIn = centralityVector))
                        }else{
                            #for an interdependent network, it is enough to calculate centrality in the aggregate
                            #http://igraph.sourceforge.net/doc/R/graph.strength.html
                            centralityVector <- graph.strength(g[[LAYERS+1]],mode="in")
                            for(l in 1:(LAYERS+1)){
                                tmplistDiagnostics[[l]] <- cbind(tmplistDiagnostics[[l]],data.frame(StrengthIn = centralityVector))
                            }

                        }
                    }else{
                        for(l in 1:(LAYERS+1)){
                            tmplistDiagnostics[[l]] <- cbind(tmplistDiagnostics[[l]],data.frame(StrengthIn = tmplistDiagnostics[[l]]$Strength))
                        }
                    }

                    progress2$set(message = paste('Current: In-Strength... Done!'), value = 1)
                    Sys.sleep(1)
                    progress2$close()

                }else{
                    for(l in 1:LAYERS){
                        tmplistDiagnostics[[l]] <- cbind(tmplistDiagnostics[[l]],data.frame(StrengthIn = rep("-",Nodes)))
                    }
                    l <- (LAYERS+1)
                    tmplistDiagnostics[[l]] <- cbind(tmplistDiagnostics[[l]],data.frame(StrengthIn = rep("-",Nodes)))
                }
    
                if(input$chkNODE_CENTRALITY_STRENGTH){
                    progress2 <- shiny::Progress$new(session)
                    on.exit(progress2$close())
                    progress2$set(message = paste('Current: Out-Strength...'), value = 0.5)

                    if(DIRECTED){
                        if(input$radMultiplexModel!="MULTIPLEX_IS_INTERDEPENDENT"){
                            centralityVector <- GetMultiOutStrengthSum(SupraAdjacencyMatrix,LAYERS,Nodes,DIRECTED)

                            for(l in 1:LAYERS){
                                tmplistDiagnostics[[l]] <- cbind(tmplistDiagnostics[[l]],data.frame(StrengthOut = centralityVector))
                            }

                            l <- LAYERS+1            
                            centralityVector <- GetMultiOutStrengthSum(AdjMatrix[[l]],1,Nodes,DIRECTED)
                            
                            tmplistDiagnostics[[l]] <- cbind(tmplistDiagnostics[[l]],data.frame(StrengthOut = centralityVector))
                        }else{
                            #for an interdependent network, it is enough to calculate centrality in the aggregate
                            #http://igraph.sourceforge.net/doc/R/graph.strength.html
                            centralityVector <- graph.strength(g[[LAYERS+1]],mode="out")
                            for(l in 1:(LAYERS+1)){
                                tmplistDiagnostics[[l]] <- cbind(tmplistDiagnostics[[l]],data.frame(StrengthOut = centralityVector))
                            }
                        }
                    }else{
                        for(l in 1:(LAYERS+1)){
                            tmplistDiagnostics[[l]] <- cbind(tmplistDiagnostics[[l]],data.frame(StrengthOut = tmplistDiagnostics[[l]]$Strength))
                        }
                    }

                    progress2$set(message = paste('Current: Out-Strength... Done!'), value = 1)
                    Sys.sleep(1)
                    progress2$close()
                }else{
                    for(l in 1:LAYERS){
                        tmplistDiagnostics[[l]] <- cbind(tmplistDiagnostics[[l]],data.frame(StrengthOut = rep("-",Nodes)))
                    }
                    l <- (LAYERS+1)
                    tmplistDiagnostics[[l]] <- cbind(tmplistDiagnostics[[l]],data.frame(StrengthOut = rep("-",Nodes)))
                }

                if(input$chkNODE_CENTRALITY_DEGREE){
                    progress2 <- shiny::Progress$new(session)
                    on.exit(progress2$close())
                    progress2$set(message = paste('Current: Degree...'), value = 0.5)
                    
                    if(input$radMultiplexModel!="MULTIPLEX_IS_INTERDEPENDENT"){

                        centralityVector <- GetMultiDegreeSum(SupraAdjacencyMatrix,LAYERS,Nodes,DIRECTED)
                        
                        for(l in 1:LAYERS){
                            tmplistDiagnostics[[l]] <- cbind(tmplistDiagnostics[[l]],data.frame(Degree = centralityVector))
                        }
        
                        l <- LAYERS+1
                        centralityVector <- GetMultiDegreeSum(AdjMatrix[[l]],1,Nodes,DIRECTED)
                        
                        tmplistDiagnostics[[l]] <- cbind(tmplistDiagnostics[[l]],data.frame(Degree = centralityVector))
                    }else{
                        #for an interdependent network, it is enough to calculate centrality in the aggregate
                        #http://igraph.sourceforge.net/doc/R/degree.html
                        centralityVector <- degree(g[[LAYERS+1]],mode="total")
                        for(l in 1:(LAYERS+1)){
                            tmplistDiagnostics[[l]] <- cbind(tmplistDiagnostics[[l]],data.frame(Degree = centralityVector))
                        }
                    }
                    
                    progress2$set(message = paste('Current: Degree... Done!'), value = 1)
                    Sys.sleep(1)
                    progress2$close()
                }else{
                    for(l in 1:LAYERS){
                        tmplistDiagnostics[[l]] <- cbind(tmplistDiagnostics[[l]],data.frame(Degree = rep("-",Nodes)))
                    }
                    l <- (LAYERS+1)
                    tmplistDiagnostics[[l]] <- cbind(tmplistDiagnostics[[l]],data.frame(Degree = rep("-",Nodes)))
                }
    
                if(input$chkNODE_CENTRALITY_DEGREE){
                    progress2 <- shiny::Progress$new(session)
                    on.exit(progress2$close())
                    progress2$set(message = paste('Current: In-Degree...'), value = 0.5)
                    
                    if(DIRECTED){
                        if(input$radMultiplexModel!="MULTIPLEX_IS_INTERDEPENDENT"){

                            centralityVector <- GetMultiInDegreeSum(SupraAdjacencyMatrix,LAYERS,Nodes,DIRECTED)
                            
                            for(l in 1:LAYERS){
                                tmplistDiagnostics[[l]] <- cbind(tmplistDiagnostics[[l]],data.frame(DegreeIn = centralityVector))
                            }

                            l <- LAYERS+1
                            centralityVector <- GetMultiInDegreeSum(AdjMatrix[[l]],1,Nodes,DIRECTED)

                            tmplistDiagnostics[[l]] <- cbind(tmplistDiagnostics[[l]],data.frame(DegreeIn = centralityVector))
                        }else{
                            #for an interdependent network, it is enough to calculate centrality in the aggregate
                            #http://igraph.sourceforge.net/doc/R/degree.html
                            centralityVector <- degree(g[[LAYERS+1]],mode="in")
                            for(l in 1:(LAYERS+1)){
                                tmplistDiagnostics[[l]] <- cbind(tmplistDiagnostics[[l]],data.frame(DegreeIn = centralityVector))
                            }

                        }
                    }else{
                        for(l in 1:(LAYERS+1)){
                            tmplistDiagnostics[[l]] <- cbind(tmplistDiagnostics[[l]],data.frame(DegreeIn = tmplistDiagnostics[[l]]$Degree))
                        }
                    }

                    progress2$set(message = paste('Current: In-Degree... Done!'), value = 1)
                    Sys.sleep(1)
                    progress2$close()

                }else{
                    for(l in 1:LAYERS){
                        tmplistDiagnostics[[l]] <- cbind(tmplistDiagnostics[[l]],data.frame(DegreeIn = rep("-",Nodes)))
                    }
                    l <- (LAYERS+1)
                    tmplistDiagnostics[[l]] <- cbind(tmplistDiagnostics[[l]],data.frame(DegreeIn = rep("-",Nodes)))
                }
    
                if(input$chkNODE_CENTRALITY_DEGREE){
                    progress2 <- shiny::Progress$new(session)
                    on.exit(progress2$close())
                    progress2$set(message = paste('Current: Out-Degree...'), value = 0.5)

                    if(DIRECTED){
                        if(input$radMultiplexModel!="MULTIPLEX_IS_INTERDEPENDENT"){
                            
                            centralityVector <- GetMultiOutDegreeSum(SupraAdjacencyMatrix,LAYERS,Nodes,DIRECTED)
                            
                            for(l in 1:LAYERS){
                                tmplistDiagnostics[[l]] <- cbind(tmplistDiagnostics[[l]],data.frame(DegreeOut = centralityVector))
                            }

                            l <- LAYERS+1            
                            centralityVector <- GetMultiOutDegreeSum(AdjMatrix[[l]],1,Nodes,DIRECTED)

                            tmplistDiagnostics[[l]] <- cbind(tmplistDiagnostics[[l]],data.frame(DegreeOut = centralityVector))
                        }else{
                            #for an interdependent network, it is enough to calculate centrality in the aggregate
                            #http://igraph.sourceforge.net/doc/R/degree.html
                            centralityVector <- degree(g[[LAYERS+1]],mode="out")
                            for(l in 1:(LAYERS+1)){
                                tmplistDiagnostics[[l]] <- cbind(tmplistDiagnostics[[l]],data.frame(DegreeOut = centralityVector))
                            }
                        }
                    }else{
                        for(l in 1:(LAYERS+1)){
                            tmplistDiagnostics[[l]] <- cbind(tmplistDiagnostics[[l]],data.frame(DegreeOut = tmplistDiagnostics[[l]]$Degree))
                        }
                    }

                    progress2$set(message = paste('Current: Out-Degree... Done!'), value = 1)
                    Sys.sleep(1)
                    progress2$close()
                }else{
                    for(l in 1:LAYERS){
                        tmplistDiagnostics[[l]] <- cbind(tmplistDiagnostics[[l]],data.frame(DegreeOut = rep("-",Nodes)))
                    }
                    l <- (LAYERS+1)
                    tmplistDiagnostics[[l]] <- cbind(tmplistDiagnostics[[l]],data.frame(DegreeOut = rep("-",Nodes)))
                }
                
                if(input$chkNODE_CENTRALITY_PAGERANK){
                    progress <- shiny::Progress$new(session)
                    on.exit(progress$close())
                    progress$set(message = paste('Current: PageRank...'), value = 0.5)

                    if(input$radMultiplexModel!="MULTIPLEX_IS_INTERDEPENDENT"){

                        centralityVector <- GetMultiPageRankCentrality(SupraAdjacencyMatrix,LAYERS,Nodes)
                        
                        centralityVector <- centralityVector/max(centralityVector)
                        for(l in 1:LAYERS){
                            tmplistDiagnostics[[l]] <- cbind(tmplistDiagnostics[[l]],data.frame(PageRank = centralityVector))
                        }

                        l <- (LAYERS+1)        
                        centralityVector <- GetMultiPageRankCentrality(AdjMatrix[[l]],1,Nodes)
                        centralityVector <- centralityVector/max(centralityVector)

                        tmplistDiagnostics[[l]] <- cbind(tmplistDiagnostics[[l]],data.frame(PageRank = centralityVector))
                    }else{
                        #http://igraph.sourceforge.net/doc/R/page.rank.html
                        #for an interdependent network, it is enough to calculate centrality in the aggregate                        
                        centralityVector <- page.rank(g[[LAYERS+1]],directed=DIRECTED)$vector
                        centralityVector <- centralityVector/max(centralityVector)
                        for(l in 1:(LAYERS+1)){
                            tmplistDiagnostics[[l]] <- cbind(tmplistDiagnostics[[l]],data.frame(PageRank = centralityVector))
                        }
                    }                    
                    
                    progress$set(message = paste('Current: PageRank... Done!'), value = 1)
                    Sys.sleep(1)
                    progress$close()

                }else{
                    for(l in 1:LAYERS){
                        tmplistDiagnostics[[l]] <- cbind(tmplistDiagnostics[[l]],data.frame(PageRank = rep("-",Nodes)))
                    }
                    l <- (LAYERS+1)
                    tmplistDiagnostics[[l]] <- cbind(tmplistDiagnostics[[l]],data.frame(PageRank = rep("-",Nodes)))
                }
    
                if(input$chkNODE_CENTRALITY_EIGENVECTOR){
                    progress <- shiny::Progress$new(session)
                    on.exit(progress$close())
                    progress$set(message = paste('Current: Eigenvector...'), value = 0.5)

                    if(input$radMultiplexModel!="MULTIPLEX_IS_INTERDEPENDENT"){

                        centralityVector <- GetMultiEigenvectorCentrality(SupraAdjacencyMatrix,LAYERS,Nodes)
                        centralityVector <- centralityVector/max(centralityVector)
                        if(any(centralityVector<0)){
                            print(paste("WARNING! Eigenvector centralities cannot be calculated. Assigning the same centrality to all nodes."))
                            progress$set(message = paste('Graph directed and acyclic or other problems...'), value = 0.5)
                            Sys.sleep(5)
                            centralityVector <- rep(1,Nodes)
                        }
                        for(l in 1:LAYERS){
                            tmplistDiagnostics[[l]] <- cbind(tmplistDiagnostics[[l]],data.frame(Eigenvector = centralityVector))
                        }

                        l <- (LAYERS+1)
                        centralityVector <- GetMultiEigenvectorCentrality(AdjMatrix[[l]],1,Nodes)
                        centralityVector <- centralityVector/max(centralityVector)
                        if(any(centralityVector<0)){
                            print(paste("WARNING! Eigenvector centralities cannot be calculated. Assigning the same centrality to all nodes."))
                            progress$set(message = paste('Graph directed and acyclic or other problems...'), value = 0.5)
                            Sys.sleep(5)
                            centralityVector <- rep(1,Nodes)
                        }

                        tmplistDiagnostics[[l]] <- cbind(tmplistDiagnostics[[l]],data.frame(Eigenvector = centralityVector))
                    }else{
                        #http://igraph.sourceforge.net/doc/R/evcent.html
                        #for an interdependent network, it is enough to calculate centrality in the aggregate
                        centralityVector <- evcent(g[[LAYERS+1]],directed=DIRECTED)$vector
                        centralityVector <- centralityVector/max(centralityVector)
                        if(any(is.null(centralityVector)) || any(is.nan(centralityVector)) || length(centralityVector)==0){
                            centralityVector <- rep(0,Nodes)
                        } 
                        for(l in 1:(LAYERS+1)){
                            tmplistDiagnostics[[l]] <- cbind(tmplistDiagnostics[[l]],data.frame(Eigenvector = centralityVector))
                        }
                    }
                                        
                    progress$set(message = paste('Current: Eigenvector... Done!'), value = 1)
                    Sys.sleep(1)
                    progress$close()
                }else{
                    for(l in 1:LAYERS){
                        tmplistDiagnostics[[l]] <- cbind(tmplistDiagnostics[[l]],data.frame(Eigenvector = rep("-",Nodes)))
                    }
                    l <- (LAYERS+1)
                    tmplistDiagnostics[[l]] <- cbind(tmplistDiagnostics[[l]],data.frame(Eigenvector = rep("-",Nodes)))
                }
    
                if(input$chkNODE_CENTRALITY_HUB){
                    progress <- shiny::Progress$new(session)
                    on.exit(progress$close())
                    progress$set(message = paste('Current: Hub...'), value = 0.5)

                    if(input$radMultiplexModel!="MULTIPLEX_IS_INTERDEPENDENT"){

                        centralityVector <- GetMultiHubCentrality(SupraAdjacencyMatrix,LAYERS,Nodes)
                        centralityVector <- centralityVector/max(centralityVector)
                        for(l in 1:LAYERS){
                            tmplistDiagnostics[[l]] <- cbind(tmplistDiagnostics[[l]],data.frame(Hub = centralityVector))
                        }

                        l <- (LAYERS+1)        
                        centralityVector <- GetMultiHubCentrality(AdjMatrix[[l]],1,Nodes)
                        centralityVector <- centralityVector/max(centralityVector)

                        tmplistDiagnostics[[l]] <- cbind(tmplistDiagnostics[[l]],data.frame(Hub = centralityVector))
                    }else{
                        #http://igraph.sourceforge.net/doc/R/kleinberg.html
                        #for an interdependent network, it is enough to calculate centrality in the aggregate
                        centralityVector <- hub.score(g[[LAYERS+1]],scale = TRUE)$vector
                        centralityVector <- centralityVector/max(centralityVector)
                        for(l in 1:(LAYERS+1)){
                            tmplistDiagnostics[[l]] <- cbind(tmplistDiagnostics[[l]],data.frame(Hub = centralityVector))
                        }
                    }                    
                    
                    progress$set(message = paste('Current: Hub... Done!'), value = 1)
                    Sys.sleep(1)
                    progress$close()
                }else{
                    for(l in 1:LAYERS){
                        tmplistDiagnostics[[l]] <- cbind(tmplistDiagnostics[[l]],data.frame(Hub = rep("-",Nodes)))
                    }
                    l <- LAYERS+1
                    tmplistDiagnostics[[l]] <- cbind(tmplistDiagnostics[[l]],data.frame(Hub = rep("-",Nodes)))
                }
    
                if(input$chkNODE_CENTRALITY_AUTHORITY){
                    progress <- shiny::Progress$new(session)
                    on.exit(progress$close())
                    progress$set(message = paste('Current: Authority...'), value = 0.5)

                    if(input$radMultiplexModel!="MULTIPLEX_IS_INTERDEPENDENT"){
                        
                        centralityVector <- GetMultiAuthCentrality(SupraAdjacencyMatrix,LAYERS,Nodes)
                        centralityVector <- centralityVector/max(centralityVector)
                        for(l in 1:LAYERS){
                            tmplistDiagnostics[[l]] <- cbind(tmplistDiagnostics[[l]],data.frame(Authority = centralityVector))
                        }
        
                        l <- (LAYERS+1)
                        centralityVector <- GetMultiAuthCentrality(AdjMatrix[[l]],1,Nodes)
                        centralityVector <- centralityVector/max(centralityVector)

                        tmplistDiagnostics[[l]] <- cbind(tmplistDiagnostics[[l]],data.frame(Authority = centralityVector))
                    }else{
                        #http://igraph.sourceforge.net/doc/R/kleinberg.html
                        #for an interdependent network, it is enough to calculate centrality in the aggregate
                        centralityVector <- authority.score(g[[LAYERS+1]],scale = TRUE)$vector
                        centralityVector <- centralityVector/max(centralityVector)
                        for(l in 1:(LAYERS+1)){
                            tmplistDiagnostics[[l]] <- cbind(tmplistDiagnostics[[l]],data.frame(Authority = centralityVector))
                        }
                    }                    
                    
                    progress$set(message = paste('Current: Authority... Done!'), value = 1)
                    Sys.sleep(1)
                    progress$close()
                }else{
                    for(l in 1:LAYERS){
                        tmplistDiagnostics[[l]] <- cbind(tmplistDiagnostics[[l]],data.frame(Authority = rep("-",Nodes)))
                    }
                    l <- (LAYERS+1)
                    tmplistDiagnostics[[l]] <- cbind(tmplistDiagnostics[[l]],data.frame(Authority = rep("-",Nodes)))
                }
    
                if(input$chkNODE_CENTRALITY_KATZ){
                    progress <- shiny::Progress$new(session)
                    on.exit(progress$close())
                    progress$set(message = paste('Current: Katz...'), value = 0.5)

                    if(input$radMultiplexModel!="MULTIPLEX_IS_INTERDEPENDENT"){

                        centralityVector <- GetMultiKatzCentrality(SupraAdjacencyMatrix,LAYERS,Nodes)
                        centralityVector <- centralityVector/max(centralityVector)
                        for(l in 1:LAYERS){
                            tmplistDiagnostics[[l]] <- cbind(tmplistDiagnostics[[l]],data.frame(Katz = centralityVector))
                        }
        
                        l <- (LAYERS+1)
                        centralityVector <- GetMultiKatzCentrality(AdjMatrix[[l]],1,Nodes)
                        centralityVector <- centralityVector/max(centralityVector)

                        tmplistDiagnostics[[l]] <- cbind(tmplistDiagnostics[[l]],data.frame(Katz = centralityVector))
                    }else{
                        #http://igraph.sourceforge.net/doc/R/alpha.centrality.html
                        #for an interdependent network, it is enough to calculate centrality in the aggregate
                        #It is easy to show that Katz centrality can be obtained from Bonacich centrality:
                        # v(katz) = v(bonacich) - vec(1)
                        #calculate the eigenvector centrality to obtain the leading eigenvalue

                        lambda <- evcent(g[[LAYERS+1]],directed=DIRECTED)$value
                        if(is.null(lambda) || is.nan(lambda) || is.infinite(lambda) || abs(lambda)<1e-8){
                            #use a lower bound:
                            #http://files.ele-math.com/articles/jmi-04-36.pdf
                            lambda <- sqrt(max(graph.strength(g[[LAYERS+1]],mode="total")))
                        }

                        centralityVector <- alpha.centrality(g[[LAYERS+1]], exo=1, alpha=0.99999/lambda) - 1
                        centralityVector <- centralityVector/max(centralityVector)
                        for(l in 1:(LAYERS+1)){
                            tmplistDiagnostics[[l]] <- cbind(tmplistDiagnostics[[l]],data.frame(Katz = centralityVector))
                        }
                    }                    
                    progress$set(message = paste('Current: Katz... Done!'), value = 1)
                    Sys.sleep(1)
                    progress$close()
                }else{
                    for(l in 1:LAYERS){
                        tmplistDiagnostics[[l]] <- cbind(tmplistDiagnostics[[l]],data.frame(Katz = rep("-",Nodes)))
                    }
                    l <- (LAYERS+1)
                    tmplistDiagnostics[[l]] <- cbind(tmplistDiagnostics[[l]],data.frame(Katz = rep("-",Nodes)))
                }

                if(input$chkNODE_CENTRALITY_MULTIPLEXITY){
                    progress <- shiny::Progress$new(session)
                    on.exit(progress$close())
                    progress$set(message = paste('Current: Multiplexity...'), value = 0.5)

                    if(input$radMultiplexModel!="MULTIPLEX_IS_INTERDEPENDENT"){

                        centralityVector <- GetMultiplexityCentrality(SupraAdjacencyMatrix,LAYERS,Nodes)
                        #centralityVector <- centralityVector/max(centralityVector)
                        for(l in 1:LAYERS){
                            tmplistDiagnostics[[l]] <- cbind(tmplistDiagnostics[[l]],data.frame(Multiplexity = centralityVector))
                        }
        
                        l <- (LAYERS+1)
                        centralityVector <- rep(0, Nodes)
                        tmplistDiagnostics[[l]] <- cbind(tmplistDiagnostics[[l]],data.frame(Multiplexity = centralityVector))
                    }else{
                        centralityVector <- as.numeric(rowSums(table(  rbind(multilayerEdges[,1],multilayerEdges[,3]), rbind(multilayerEdges[,2],multilayerEdges[,4])  )>0))
                        
                        #for an interdependent network, node multiplexity should be 1
                        if(!all( centralityVector==1 )){
                            progress <- shiny::Progress$new(session)
                            on.exit(progress$close())
                            progress$set(message = 'Error! Multiplexity expected to be 1/# Layers for all nodes, but different values have been found. Are you sure your network is interdependent?', value = 0.5)
                            Sys.sleep(10)
                        }
                        centralityVector <- centralityVector/LAYERS

                        for(l in 1:(LAYERS+1)){
                            tmplistDiagnostics[[l]] <- cbind(tmplistDiagnostics[[l]],data.frame(Multiplexity = centralityVector))
                        }
                    }
                                        
                    progress$set(message = paste('Current: Multiplexity... Done!'), value = 1)
                    Sys.sleep(1)
                    progress$close()
                }else{
                    for(l in 1:LAYERS){
                        tmplistDiagnostics[[l]] <- cbind(tmplistDiagnostics[[l]],data.frame(Multiplexity = rep("-",Nodes)))
                    }
                    l <- (LAYERS+1)
                    tmplistDiagnostics[[l]] <- cbind(tmplistDiagnostics[[l]],data.frame(Multiplexity = rep("-",Nodes)))
                }
                
                if(input$chkNODE_CENTRALITY_KCORE){
                    progress <- shiny::Progress$new(session)
                    on.exit(progress$close())
                    progress$set(message = paste('Current: K-core...'), value = 0.5)

                    if(input$radMultiplexModel!="MULTIPLEX_IS_INTERDEPENDENT"){
                                                
                        centralityVector <- GetMultiKCoreCentrality(SupraAdjacencyMatrix,LAYERS,Nodes)
                        #centralityVector <- centralityVector/max(centralityVector)
                        for(l in 1:LAYERS){
                            tmplistDiagnostics[[l]] <- cbind(tmplistDiagnostics[[l]],data.frame(Kcore = centralityVector))
                        }
            
                        l <- (LAYERS+1)
                        centralityVector <- graph.coreness(g[[l]], mode="all")
                        #centralityVector <- centralityVector/max(centralityVector)
                        tmplistDiagnostics[[l]] <- cbind(tmplistDiagnostics[[l]],data.frame(Kcore = centralityVector))
                    }else{
                        #http://www.inside-r.org/packages/cran/igraph/docs/graph.coreness
                        #for an interdependent network, it is enough to calculate centrality in the aggregate

                        centralityVector <- graph.coreness(g[[LAYERS+1]], mode="all")
                        #centralityVector <- centralityVector/max(centralityVector)
                        for(l in 1:(LAYERS+1)){
                            tmplistDiagnostics[[l]] <- cbind(tmplistDiagnostics[[l]],data.frame(Kcore = centralityVector))
                        }
                    }                    
                    progress$set(message = paste('Current: K-core... Done!'), value = 1)
                    Sys.sleep(1)
                    progress$close()
                }else{
                    for(l in 1:LAYERS){
                        tmplistDiagnostics[[l]] <- cbind(tmplistDiagnostics[[l]],data.frame(Kcore = rep("-",Nodes)))
                    }
                    l <- (LAYERS+1)
                    tmplistDiagnostics[[l]] <- cbind(tmplistDiagnostics[[l]],data.frame(Kcore = rep("-",Nodes)))
                }

            }else{
                #calculation per layer. No needs to specify the weight attribute because the g objects
                #are built assuming weighted input (where weight is 1 for binary networks), and each measure
                #assume by default the weight attribute of E(g)
                for(l in 1:(LAYERS)){
                    tmplistDiagnostics[[l]] <- data.frame(Layer = rep(l,Nodes))
                    tmplistDiagnostics[[l]] <- cbind(tmplistDiagnostics[[l]],data.frame(Node = 1:Nodes))
                    tmplistDiagnostics[[l]] <- cbind(tmplistDiagnostics[[l]],data.frame(Label = nodesLabel[[l]]))
                }
    
                tmplistDiagnostics[[LAYERS+1]] <- data.frame(Layer = rep("Aggr",Nodes))
                tmplistDiagnostics[[LAYERS+1]] <- cbind(tmplistDiagnostics[[LAYERS+1]],data.frame(Node = 1:Nodes))
                tmplistDiagnostics[[LAYERS+1]] <- cbind(tmplistDiagnostics[[LAYERS+1]],data.frame(Label = nodesLabel[[LAYERS+1]]))

                                                        
                for(l in 1:(LAYERS+1)){
                    if(input$chkNODE_CENTRALITY_DEGREE){
                        progress <- shiny::Progress$new(session)
                        on.exit(progress$close())
                        progress$set(message = paste('Current: Degree  for layer',l,'...'), value = 0.5)

                        #http://igraph.sourceforge.net/doc/R/degree.html
                        tmplistDiagnostics[[l]] <- cbind(tmplistDiagnostics[[l]],data.frame(Degree = degree(g[[l]],mode="total")))
                        if(DIRECTED){
                            tmplistDiagnostics[[l]] <- cbind(tmplistDiagnostics[[l]],data.frame(DegreeIn = degree(g[[l]],mode="in")))
                            tmplistDiagnostics[[l]] <- cbind(tmplistDiagnostics[[l]],data.frame(DegreeOut = degree(g[[l]],mode="out")))
                        }else{
                            tmplistDiagnostics[[l]] <- cbind(tmplistDiagnostics[[l]],data.frame(DegreeIn = tmplistDiagnostics[[l]]$Degree))
                            tmplistDiagnostics[[l]] <- cbind(tmplistDiagnostics[[l]],data.frame(DegreeOut = tmplistDiagnostics[[l]]$Degree))   
                        }
                        progress$set(message = paste('Current: Degree  for layer',l,'... Done!'), value = 1)
                        Sys.sleep(1)
                        progress$close()
                    }else{
                        tmplistDiagnostics[[l]] <- cbind(tmplistDiagnostics[[l]],data.frame(Degree = rep("-",Nodes)))
                        tmplistDiagnostics[[l]] <- cbind(tmplistDiagnostics[[l]],data.frame(DegreeIn = rep("-",Nodes)))
                        tmplistDiagnostics[[l]] <- cbind(tmplistDiagnostics[[l]],data.frame(DegreeOut = rep("-",Nodes)))
                    }
                    
                    if(input$chkNODE_CENTRALITY_STRENGTH){
                        progress <- shiny::Progress$new(session)
                        on.exit(progress$close())
                        progress$set(message = paste('Current: Strength  for layer',l,'...'), value = 0.5)

                        #http://igraph.sourceforge.net/doc/R/graph.strength.html
                        tmplistDiagnostics[[l]] <- cbind(tmplistDiagnostics[[l]],data.frame(Strength = graph.strength(g[[l]],mode="total")))
                        if(DIRECTED){
                            tmplistDiagnostics[[l]] <- cbind(tmplistDiagnostics[[l]],data.frame(StrengthIn = graph.strength(g[[l]],mode="in")))
                            tmplistDiagnostics[[l]] <- cbind(tmplistDiagnostics[[l]],data.frame(StrengthOut = graph.strength(g[[l]],mode="out")))
                        }else{
                            tmplistDiagnostics[[l]] <- cbind(tmplistDiagnostics[[l]],data.frame(StrengthIn = tmplistDiagnostics[[l]]$Strength))
                            tmplistDiagnostics[[l]] <- cbind(tmplistDiagnostics[[l]],data.frame(StrengthOut = tmplistDiagnostics[[l]]$Strength))   
                        }
                        progress$set(message = paste('Current: Strength  for layer',l,'... Done!'), value = 1)
                        Sys.sleep(1)
                        progress$close()
                    }else{
                        tmplistDiagnostics[[l]] <- cbind(tmplistDiagnostics[[l]],data.frame(Strength = rep("-",Nodes)))
                        tmplistDiagnostics[[l]] <- cbind(tmplistDiagnostics[[l]],data.frame(StrengthIn = rep("-",Nodes)))
                        tmplistDiagnostics[[l]] <- cbind(tmplistDiagnostics[[l]],data.frame(StrengthOut = rep("-",Nodes)))
                    }
    
                    if(input$chkNODE_CENTRALITY_PAGERANK){
                        progress <- shiny::Progress$new(session)
                        on.exit(progress$close())
                        progress$set(message = paste('Current: PageRank  for layer',l,'...'), value = 0.5)

                        #http://igraph.sourceforge.net/doc/R/page.rank.html
                        tmplistDiagnostics[[l]] <- cbind(tmplistDiagnostics[[l]],data.frame(PageRank = page.rank(g[[l]],directed=DIRECTED)$vector))
                        tmplistDiagnostics[[l]]$PageRank <- tmplistDiagnostics[[l]]$PageRank/max(tmplistDiagnostics[[l]]$PageRank)
                        
                        progress$set(message = paste('Current: PageRank  for layer',l,'... Done!'), value = 1)
                        Sys.sleep(1)
                        progress$close()
                    }else{
                        tmplistDiagnostics[[l]] <- cbind(tmplistDiagnostics[[l]],data.frame(PageRank = rep("-",Nodes)))
                    }
    
                    if(input$chkNODE_CENTRALITY_EIGENVECTOR){
                        progress <- shiny::Progress$new(session)
                        on.exit(progress$close())
                        progress$set(message = paste('Current: Eigenvector  for layer',l,'...'), value = 0.5)

                        #http://igraph.sourceforge.net/doc/R/evcent.html
                        tmplistDiagnostics[[l]] <- cbind(tmplistDiagnostics[[l]],data.frame(Eigenvector = evcent(g[[l]],directed=DIRECTED)$vector))
                        tmplistDiagnostics[[l]]$Eigenvector <- tmplistDiagnostics[[l]]$Eigenvector/max(tmplistDiagnostics[[l]]$Eigenvector)
                        if(any(is.null(tmplistDiagnostics[[l]]$Eigenvector)) || any(is.nan(tmplistDiagnostics[[l]]$Eigenvector)) || length(tmplistDiagnostics[[l]]$Eigenvector)==0){
                            tmplistDiagnostics[[l]]$Eigenvector <- rep(0,Nodes)
                        } 
                        
                        progress$set(message = paste('Current: Eigenvector  for layer',l,'... Done!'), value = 1)
                        Sys.sleep(1)
                        progress$close()
                    }else{
                        tmplistDiagnostics[[l]] <- cbind(tmplistDiagnostics[[l]],data.frame(Eigenvector = rep("-",Nodes)))
                    }
    
                    if(input$chkNODE_CENTRALITY_HUB){
                        progress <- shiny::Progress$new(session)
                        on.exit(progress$close())
                        progress$set(message = paste('Current: Hub  for layer',l,'...'), value = 0.5)

                        #http://igraph.sourceforge.net/doc/R/kleinberg.html
                        tmplistDiagnostics[[l]] <- cbind(tmplistDiagnostics[[l]],data.frame(Hub = hub.score(g[[l]],scale = TRUE)$vector))
                        tmplistDiagnostics[[l]]$Hub <- tmplistDiagnostics[[l]]$Hub/max(tmplistDiagnostics[[l]]$Hub)
                        
                        progress$set(message = paste('Current: Hub  for layer',l,'... Done!'), value = 1)
                        Sys.sleep(1)
                        progress$close()
                    }else{
                        tmplistDiagnostics[[l]] <- cbind(tmplistDiagnostics[[l]],data.frame(Hub = rep("-",Nodes)))
                    }
    
                    if(input$chkNODE_CENTRALITY_AUTHORITY){
                        progress <- shiny::Progress$new(session)
                        on.exit(progress$close())
                        progress$set(message = paste('Current: Authority  for layer',l,'...'), value = 0.5)

                        #http://igraph.sourceforge.net/doc/R/kleinberg.html
                        tmplistDiagnostics[[l]] <- cbind(tmplistDiagnostics[[l]],data.frame(Authority = authority.score(g[[l]],scale = TRUE)$vector))
                        tmplistDiagnostics[[l]]$Authority <- tmplistDiagnostics[[l]]$Authority/max(tmplistDiagnostics[[l]]$Authority)
                        
                        progress$set(message = paste('Current: Authority  for layer',l,'... Done!'), value = 1)
                        Sys.sleep(1)
                        progress$close()
                    }else{
                        tmplistDiagnostics[[l]] <- cbind(tmplistDiagnostics[[l]],data.frame(Authority = rep("-",Nodes)))
                    }
    
                    if(input$chkNODE_CENTRALITY_KATZ){
                        progress <- shiny::Progress$new(session)
                        on.exit(progress$close())
                        progress$set(message = paste('Current: Katz  for layer',l,'...'), value = 0.5)

                        #http://igraph.sourceforge.net/doc/R/alpha.centrality.html
                        #It is easy to show that Katz centrality can be obtained from Bonacich centrality:
                        # v(katz) = v(bonacich) - vec(1)
                        #calculate the eigenvector centrality to obtain the leading eigenvalue
                        lambda <- evcent(g[[l]],directed=DIRECTED)$value
                        
                        if(is.null(lambda) || is.nan(lambda) || is.infinite(lambda) || abs(lambda)<1e-8){
                            #use a lower bound:
                            #http://files.ele-math.com/articles/jmi-04-36.pdf
                            lambda <- sqrt(max(graph.strength(g[[l]],mode="total")))
                        }
                        
                        tmplistDiagnostics[[l]] <- cbind(tmplistDiagnostics[[l]],data.frame(Katz = alpha.centrality(g[[l]],exo=1,alpha=0.99999/lambda) - 1))
                        tmplistDiagnostics[[l]]$Katz <- tmplistDiagnostics[[l]]$Katz/max(tmplistDiagnostics[[l]]$Katz)
                        
                        progress$set(message = paste('Current: Katz  for layer',l,'... Done!'), value = 1)
                        Sys.sleep(1)
                        progress$close()
                    }else{
                        tmplistDiagnostics[[l]] <- cbind(tmplistDiagnostics[[l]],data.frame(Katz = rep("-",Nodes)))
                    }

                    if(input$chkNODE_CENTRALITY_MULTIPLEXITY){
                        progress <- shiny::Progress$new(session)
                        on.exit(progress$close())
                        progress$set(message = paste('Current: Multiplexity  for layer',l,'...'), value = 0.5)

                        tmplistDiagnostics[[l]] <- cbind(tmplistDiagnostics[[l]],data.frame(Multiplexity = rep(0,Nodes)))
                        
                        progress$set(message = paste('Current: Multiplexity  for layer',l,'... Done!'), value = 1)
                        Sys.sleep(1)
                        progress$close()
                    }else{
                        tmplistDiagnostics[[l]] <- cbind(tmplistDiagnostics[[l]],data.frame(Multiplexity = rep("-",Nodes)))
                    }

                    if(input$chkNODE_CENTRALITY_KCORE){
                        progress <- shiny::Progress$new(session)
                        on.exit(progress$close())
                        progress$set(message = paste('Current: Kcore  for layer',l,'...'), value = 0.5)

                        centralityVector <- graph.coreness(g[[l]], mode="all")
                        #centralityVector <- centralityVector/max(centralityVector)
                        tmplistDiagnostics[[l]] <- cbind(tmplistDiagnostics[[l]],data.frame(Kcore = centralityVector))
                        
                        progress$set(message = paste('Current: Kcore  for layer',l,'... Done!'), value = 1)
                        Sys.sleep(1)
                        progress$close()
                    }else{
                        tmplistDiagnostics[[l]] <- cbind(tmplistDiagnostics[[l]],data.frame(Kcore = rep("-",Nodes)))
                    }

                }
                #progress$close()
            }
            
            return(tmplistDiagnostics)
        }

        #######################################
        ## Console
        #######################################
        observeEvent(input$btnRunConsole, {
            #if(input$btnImportNetworks == 0 || LAYERS<=0) return()
        
            isolate({
                progress <- shiny::Progress$new(session)
                on.exit(progress$close())
    
                progress$set(message = 'Running...', value = 0.05)
                Sys.sleep(1)

                console.result <- tryCatch({
                    eval(parse(text=input$Console))
                }, warning = function(war) {
                  # warning handler picks up where error was generated
                    print(paste("Warning:  ",war))
                    progress <- shiny::Progress$new(session)
                    on.exit(progress$close())
                    progress$set(message = paste('Warning! ',war), value = 0.5)
                    Sys.sleep(10)
                }, error = function(err) {
                  # error handler picks up where error was generated
                  print(paste("Error:  ",err))
                    progress <- shiny::Progress$new(session)
                    on.exit(progress$close())
                    progress$set(message = paste('Error! ',err), value = 0.5)
                    Sys.sleep(10)
                }, finally = { 
                })
                                                
                progress$set(message = "OK!", value = 1)
                Sys.sleep(2)
            })
        }, ignoreInit = TRUE)

        
        #######################################
        ## Export / Import
        #######################################
#        observe({
#            if(btnOpenSessionValue==input$btnOpenSession) return()
#
#            isolate({
#                if(length(input$session_file)>0){
#                    if(!file.exists(input$session_file$datapath)){
#                        progress <- shiny::Progress$new(session)
#                        on.exit(progress$close())
#                        progress$set(message = paste('ERROR! File',input$session_file$datapath,'does not exist.'), value = 0.01)
#                        Sys.sleep(10)
#                        return(NULL)
#                    }
#                    
#                    progress <- shiny::Progress$new(session)
#                    on.exit(progress$close())
#                    progress$set(message = 'Loading muxViz session...', value = 0.2)
#                    Sys.sleep(1)
#
#                    values <- readRDS(input$session_file$datapath)
#                    
#                    globalList.names <- names(values$global)
#
#                    print(input$btnImportNetworks)
#
#                    sapply(globalList.names, function(x){
#                                                        if(x!="values$global"){
#                                                            assign(x, values$global[[x]], envir=.GlobalEnv)
#                                                        }}, simplify=F, USE.NAMES=T
#                    )
#                                        
##                    lapply(names(values$input),
##                          function(x) session$sendInputMessage(x, list(value = values$input[[x]]))
##                      )
#                    
#                    inputList.names <- names(values$input)
#                    for(i in 1:length(inputList.names)){
#                        x <- inputList.names[i]
#
#                            session$sendInputMessage(x, shiny:::dropNulls(list(value = values$input[[x]])))
#                            print(x)
#                            print(values$input[[x]])
#
#                    }
#                    print("------------")
#                    print(input$btnImportNetworks)
#
#                    
#                    progress$set(detail = paste('Session correctly loaded'), value = 1)
#                    Sys.sleep(2)
#                }                
#                btnOpenSessionValue <<- input$btnOpenSession
#            })
#        })

#        observeEvent(input$btnSaveSession, {
#            if(input$btnImportNetworks == 0 || LAYERS<=0) return()
#
#            isolate({
#                progress <- shiny::Progress$new(session)
#                on.exit(progress$close())
#                progress$set(message = 'Saving muxViz session...', value = 0.2)
#                Sys.sleep(1)
#
#                
#                outfile <- concatenatePath("sessions", paste0(input$txtProjectName,".mux"))
#                
#                #http://stackoverflow.com/questions/28166730/how-to-convert-shiny-input-values-into-a-shiny-output-table
##                elementList <- NULL
##                for(el in list(reactiveValuesToList(input))){
##                    elementList <- attributes(el)
##                }
##        
##                df.input <- data.frame(id=rep("",length(elementList$names)), 
##                                                   type=rep("",length(elementList$names)), 
##                                                   value=rep("",length(elementList$names)))
##                
##                df.input$id <- as.character(df.input$id)
##                df.input$value <- as.character(df.input$value)
##
##                for(r in 1:length(elementList$names)){
##                    df.input[r,]$id <- as.character(elementList$names[r])
##                    df.input[r,]$value <- as.character(reactiveValuesToList(input)[ df.input[r,]$id ])
##                }
#                #updateCheckboxInput(session, "chkMULTIPLEX_OVERLAPPING", value=F)
#                #updateSelectInput(session, "selMotifColorPalette", selected="Spectral")
##                df.input$id <- as.character(df.input$id)
##                df.input$type <- as.character(df.input$type)
##                df.input$value <- as.character(df.input$value)
#
#
#                #http://stackoverflow.com/questions/32922190/saving-state-of-shiny-app-to-be-restored-later
#                #inputList <- lapply(reactiveValuesToList(input), unclass)
#                inputList <- reactiveValuesToList(input)
#                globalList <- as.list(.GlobalEnv)
#
#                resout <- list(input=inputList, global=globalList)
#                saveRDS(resout, file = outfile)
#                progress$set(detail = paste('Session correctly saved to',outfile), value = 1)
#                Sys.sleep(2)
#            })
#        })

        observeEvent(input$btnLoadSession, {
            if(is.null(input$selSavedSessionsList)) return()
            
            stateID <- strsplit(input$selSavedSessionsList, "|", fixed=T)[[1]][1]
            showModal(modalDialog(title = "Load session", HTML(paste0("<a href='/?_state_id_=",stateID,"' target='_blank'>Click here to load session ",stateID,"</a>")), easyClose = TRUE))
        })

        observeEvent(input$btnDeleteSavedSession, {
            if(is.null(input$selSavedSessionsList)) return()
            
            stateID <- strsplit(input$selSavedSessionsList, "|", fixed=T)[[1]][1]

            if(input$chkConfirmDeleteSavedSession){
                unlink(concatenatePath("shiny_bookmarks", stateID), recursive=T)
                updateSavedSessionsList()
                showModal(modalDialog(title = "Delete session", "Done!", easyClose = TRUE))
            }
        })


        updateSavedSessionsList <- function(){
            output$selOutputSavedSessionsList <- renderUI({
                dirs <- list.dirs(path = "shiny_bookmarks", full.names = F, recursive = F)
                details <- file.info(unlist(lapply(dirs, function(x) concatenatePath("shiny_bookmarks", x))))
                details <- details[with(details, rev(order(as.POSIXct(mtime)))), ]
    
                sessions <- paste0(basename(rownames(details)), "|", details$mtime)
                session_list <- list()
                res <- lapply(sessions, function(x) {session_list[x] <<- x})
    
                cat("Loading saved sessions list...\n")
                cat("   + Done!\n")
    
                return(
                    selectInput("selSavedSessionsList", label = getText("selSavedSessionsList"), 
                            choices = session_list, 
                            selected = 1)
                        )
            })
        }   
        updateSavedSessionsList()     
        
        observeEvent(input$btnExportRendering, {
            if(input$btnExportRendering==0 || input$btnRenderNetworks==0 || input$btnApplyLayout==0 || input$btnImportNetworks == 0 || LAYERS<=0) return()
    
            isolate({
                progress <- shiny::Progress$new(session)
                on.exit(progress$close())
    
                progress$set(message = 'Start exporting...', value = 0.05)
                Sys.sleep(1)
    
                FILE_RGL_SNAPSHOT <- buildPath("export",paste0(input$txtProjectName,"_",as.character(format(Sys.time(), "%d-%m-%Y_%H%M%S")),".png"))
                rgl.snapshot(FILE_RGL_SNAPSHOT) 
                                
                progress$set(message = paste('Image exported to',FILE_RGL_SNAPSHOT), value = 1)
                Sys.sleep(5)
            })
        }, ignoreInit = TRUE)
        
        observeEvent(input$btnExportRenderingPDF, {
            if(input$btnExportRenderingPDF==0 || input$btnRenderNetworks==0 || input$btnApplyLayout==0 || input$btnImportNetworks == 0 || LAYERS<=0) return()
    
            isolate({
                progress <- shiny::Progress$new(session)
                on.exit(progress$close())
    
                progress$set(message = 'Start exporting...', value = 0.05)
                Sys.sleep(1)
    
                FILE_RGL_SNAPSHOT <- buildPath("export",paste0(input$txtProjectName,"_",as.character(format(Sys.time(), "%d-%m-%Y_%H%M%S")),".pdf"))
                rgl.postscript(FILE_RGL_SNAPSHOT,"pdf",drawText=TRUE)

                progress$set(message = paste('Image exported to',FILE_RGL_SNAPSHOT), value = 1)
                Sys.sleep(5)
            })
        }, ignoreInit = TRUE)

        observeEvent(input$btnExportRenderingSVG, {
            if(input$btnExportRenderingSVG==0 || input$btnRenderNetworks==0 || input$btnApplyLayout==0 || input$btnImportNetworks == 0 || LAYERS<=0) return()
    
            isolate({
                progress <- shiny::Progress$new(session)
                on.exit(progress$close())
    
                progress$set(message = 'Start exporting...', value = 0.05)
                Sys.sleep(1)
    
                FILE_RGL_SNAPSHOT <- buildPath("export",paste0(input$txtProjectName,"_",as.character(format(Sys.time(), "%d-%m-%Y_%H%M%S")),".svg"))
                rgl.postscript(FILE_RGL_SNAPSHOT,"svg",drawText=TRUE)

                progress$set(message = paste('Image exported to',FILE_RGL_SNAPSHOT), value = 1)
                Sys.sleep(5)
            })
        }, ignoreInit = TRUE)

    
        observeEvent(input$btnExportRenderingWeb, {
            if(input$btnExportRenderingWeb==0 || input$btnRenderNetworks==0 || input$btnApplyLayout==0 || input$btnImportNetworks == 0 || LAYERS<=0) return()
    
            isolate({
                progress <- shiny::Progress$new(session)
                on.exit(progress$close())
    
                progress$set(message = 'Start exporting webGL...', value = 0.05)
                Sys.sleep(1)
    
                #browseURL(paste("file://", writeWebGL(dir=file.path("/path/", "webGL"), width=700), sep=""))
                writeWebGL(dir=buildPath("export", paste("webGL_",input$txtProjectName,sep="")), width=945)
                progress$set(message = 'webGL exported. See export folder.', value = 1)
                Sys.sleep(5)
            })
        }, ignoreInit = TRUE)

        observeEvent(input$btnExportRenderingClassicPDF, {
            if(input$btnExportRenderingClassicPDF==0 || input$btnRenderNetworks==0 || input$btnApplyLayout==0 || input$btnImportNetworks == 0 || LAYERS<=0) return()
    
            isolate({
                progress <- shiny::Progress$new(session)
                on.exit(progress$close())
    
                progress$set(message = 'Start exporting...', value = 0.05)
                Sys.sleep(1)
    
                FILE_OUTPUT <- buildPath("export",paste0(input$txtProjectName,"_",as.character(format(Sys.time(), "%d-%m-%Y_%H%M%S")),".pdf"))

                width <- as.numeric(input$txtExportRenderingClassicPNGWidth)
                height <- as.numeric(input$txtExportRenderingClassicPNGHeight)

                pdf(file=FILE_OUTPUT, width=width, height=height)
                makeRendering()
                dev.off()
                
                progress$set(message = paste('Image exported to',FILE_OUTPUT), value = 1)
                Sys.sleep(5)
            })
        }, ignoreInit = TRUE)

        observeEvent(input$btnExportRenderingClassicPNG, {
            if(input$btnExportRenderingClassicPNG==0 || input$btnRenderNetworks==0 || input$btnApplyLayout==0 || input$btnImportNetworks == 0 || LAYERS<=0) return()
    
            isolate({
                progress <- shiny::Progress$new(session)
                on.exit(progress$close())
    
                progress$set(message = 'Start exporting...', value = 0.05)
                Sys.sleep(1)
    
                FILE_OUTPUT <- buildPath("export",paste0(input$txtProjectName,"_",as.character(format(Sys.time(), "%d-%m-%Y_%H%M%S")),".png"))

                width <- as.numeric(input$txtExportRenderingClassicPNGWidth)
                height <- as.numeric(input$txtExportRenderingClassicPNGHeight)
                dpi <- as.numeric(input$txtExportRenderingClassicPNGResolution)

                png(filename=FILE_OUTPUT, width=width, height=height, res=dpi)
                makeRendering()
                dev.off()

                progress$set(message = paste('Image exported to',FILE_OUTPUT), value = 1)
                Sys.sleep(5)
            })
        }, ignoreInit = TRUE)


        #this is to setup the pageable table output
        googleVisMotifsSummaryTableOptions <- reactive({
            #other options here:
            #http://www.inside-r.org/packages/cran/googleVis/docs/gvisTable
            if(input$btnCalculateMotifs==0 || input$btnImportNetworks == 0 || LAYERS<=1)
                return()
                
            list(
                page='enable',
                width=750,
                alternatingRowStyle = FALSE
            )
        })

        googleVisOverlapMatrixSummaryTableOptions <- reactive({
            #other options here:
            #http://www.inside-r.org/packages/cran/googleVis/docs/gvisTable
            if(input$btnCalculateCorrelationDiagnostics==0 || input$btnImportNetworks == 0 || LAYERS==0 || !input$chkMULTIPLEX_OVERLAPPING)
                return()
                
            list(
                page='disable',
                width=550,
                height=150
            )
        })

        googleVisNodeOverlapMatrixSummaryTableOptions <- reactive({
            #other options here:
            #http://www.inside-r.org/packages/cran/googleVis/docs/gvisTable
            if(input$btnCalculateCorrelationDiagnostics==0 || input$btnImportNetworks == 0 || LAYERS==0 || !input$chkMULTIPLEX_OVERLAPPING)
                return()
                
            list(
                page='disable',
                width=550,
                height=150
            )
        })

        #this is to setup the pageable table output
        googleVisDistanceSimilaritySummaryTableOptions <- reactive({
            #other options here:
            #http://www.inside-r.org/packages/cran/googleVis/docs/gvisTable
            if(input$btnCalculateCorrelationDiagnostics==0 || input$btnImportNetworks == 0 || LAYERS==0 || !input$chkMULTIPLEX_SHORTESTPATH)
                return()
                
            list(
                page='disable',
                width=550,
                height=150
            )
        })
        
        #this is to setup the pageable table output
        googleVisInterPearsonSummaryTableOptions <- reactive({
            #other options here:
            #http://www.inside-r.org/packages/cran/googleVis/docs/gvisTable
            if(input$btnCalculateCorrelationDiagnostics==0 || input$btnImportNetworks == 0 || LAYERS==0 || !input$chkMULTIPLEX_INTERASSORTATIVITY_PEARSON)
                return()
                
            list(
                page='disable',
                width=550,
                height=150
            )
        })
    
        #this is to setup the pageable table output
        googleVisInterSpearmanSummaryTableOptions <- reactive({
            #other options here:
            #http://www.inside-r.org/packages/cran/googleVis/docs/gvisTable
            if(input$btnCalculateCorrelationDiagnostics==0 || input$btnImportNetworks == 0 || LAYERS==0 || !input$chkMULTIPLEX_INTERASSORTATIVITY_SPEARMAN)
                return()
                
            list(
                page='disable',
                width=550,
                height=150
            )
        })
        
        #this is to setup the pageable table output
        googleVisCommunitySummaryTableOptions <- reactive({
            #other options here:
            #http://www.inside-r.org/packages/cran/googleVis/docs/gvisTable
            if(input$btnCalculateCommunityDiagnostics==0 || input$btnImportNetworks == 0 || LAYERS==0)
                return()
                
            list(
                page='disable',
                width=550,
                height=150
    #            pageSize=10
            )
        })

        #this is to setup the pageable table output
        output$numOutputComponentsTableNodesPerPage <- renderUI({
            numericInput(inputId = "componentsTablePageSize",label = "Nodes per page",Nodes)
        })
        
        #this is to setup the pageable table output
        output$numOutputCommunityTableNodesPerPage <- renderUI({
            numericInput(inputId = "communityTablePageSize",label = "Nodes per page",Nodes)
        })

        googleVisCommunityTableOptions <- reactive({
            #other options here:
            #http://www.inside-r.org/packages/cran/googleVis/docs/gvisTable
            if(input$btnCalculateCommunityDiagnostics==0 || input$btnImportNetworks == 0 || LAYERS==0)
                return()
                
            if(is.null(input$communityTablePageSize)){
                list(
                    page=ifelse(input$communityTablePageable==TRUE,'enable','disable'),
                    pageSize=Nodes,
                    width=550
                )
            }else{
                list(
                    page=ifelse(input$communityTablePageable==TRUE,'enable','disable'),
                    pageSize=as.numeric(input$communityTablePageSize),
                    width=550
                )
            }
        })
    
        #this is to setup the pageable table output
        output$numOutputCentralityTableNodesPerPage <- renderUI({
            numericInput(inputId = "centralityTablePageSize",label = "Nodes per page",Nodes)
        })

        googleVisCentralityTableOptions <- reactive({
            #other options here:
            #http://www.inside-r.org/packages/cran/googleVis/docs/gvisTable
            if(input$btnCalculateCentralityDiagnostics==0 || input$btnImportNetworks == 0 || LAYERS==0)
                return()
            
            if(is.null(input$centralityTablePageSize)){
                list(
                    #page=ifelse(input$centralityTablePageable==TRUE,'enable','disable'),
                    pageSize=Nodes,
                    width=550
                )
            }else{
                list(
                    #page=ifelse(input$centralityTablePageable==TRUE,'enable','disable'),
                    pageSize=as.numeric(input$centralityTablePageSize),
                    width=550
                )                
            }
        })

        googleVisCentralityTableSingleLayerOptions <- reactive({
            #other options here:
            #http://www.inside-r.org/packages/cran/googleVis/docs/gvisTable
            if(input$btnCalculateCentralityDiagnostics==0 || input$btnImportNetworks == 0 || LAYERS==0)
                return()
            
            if(is.null(input$centralityTablePageSize)){
                list(
                    #page=ifelse(input$centralityTablePageable==TRUE,'enable','disable'),
                    pageSize=Nodes,
                    width=550
                )
            }else{
                list(
                    #page=ifelse(input$centralityTablePageable==TRUE,'enable','disable'),
                    pageSize=as.numeric(input$centralityTablePageSize),
                    width=550
                )                
            }
        })
    
        #this is to setup the pageable table output
        googleVisEdgelistTableOptions <- reactive({
            #other options here:
            #http://www.inside-r.org/packages/cran/googleVis/docs/gvisTable
            if(input$btnImportNetworks==0 || LAYERS==0)
                return()
                
            list(
                page=ifelse(input$edgelistTablePageable==TRUE,'enable','disable'),
                pageSize=input$edgelistTablePageSize,
                width=550
            )
        })

        #######################################
        ## Download handlers
        #######################################

        # downloadHandler() takes two arguments, both functions.
        #output$downSaveAs <- downloadHandler(    
        #    filename = function() { paste(input$txtProjectName, "csv", sep = ".") },
        #    content = function(file) { write.table(myData, file, sep = ";", row.names = FALSE) }
        #)

        output$downTriadsSummaryTableSingleLayer <- downloadHandler(    
            filename = function() { paste0(input$txtProjectName, "_triads_summary_perLayer_table.csv") },
            content = function(file) { 
                if(input$btnCalculateTriadsDiagnostics==0 || input$btnImportNetworks == 0 ||  LAYERS<=1){
                    return(NULL)
                }else{
                    write.table(sumTriadsMergeSingleLayer, file, sep = ";", row.names = FALSE) 
                }
            }
        )

        output$downTriadsTableSingleLayer <- downloadHandler(    
            filename = function() { paste0(input$txtProjectName, "_triads_perLayer_table.csv") },
            content = function(file) { 

                if(input$btnCalculateTriadsDiagnostics==0 || input$btnImportNetworks == 0 ||  LAYERS<=1){
                    return(NULL)
                }else{
                    write.table(listTriadsMergeSingleLayer, file, sep = ";", row.names = FALSE) 
                }
            }
        )

        output$downTriadsTable <- downloadHandler(    
            filename = function() { paste0(input$txtProjectName, "_triads_table.csv") },
            content = function(file) { 
                if(input$btnCalculateTriadsDiagnostics==0 || input$btnImportNetworks == 0 ||  LAYERS<=1){
                    return(NULL)
                }else{
                    write.table(listTriadsMerge, file, sep = ";", row.names = FALSE) 
                }
            }
        )

        output$downTriadsSummaryTable <- downloadHandler(    
            filename = function() { paste0(input$txtProjectName, "_triads_summary_table.csv") },
            content = function(file) { 

                if(input$btnCalculateTriadsDiagnostics==0 || input$btnImportNetworks == 0 ||  LAYERS<=1){
                    return(NULL)
                }else{
                    write.table(sumTriadsMerge, file, sep = ";", row.names = FALSE) 
                }
            }
        )


        output$downMotifsTable <- downloadHandler(    
            filename = function() { paste0(input$txtProjectName, "_motifs_table.csv") },
            content = function(file) { 

                if(input$btnCalculateMotifs==0 || input$btnImportNetworks == 0 ||  LAYERS<=1){
                    return(NULL)
                }else{
                    tmp <- listMotifs
                    tmp$Motif <- NULL
                    write.table(tmp, file, sep = ";", row.names = FALSE) 
                }
            }
        )
        
        output$downCentralityTable <- downloadHandler(    
            filename = function() { paste0(input$txtProjectName, "_centrality_table.csv") },
            content = function(file) { 

                if(input$btnCalculateCentralityDiagnostics==0 || input$btnImportNetworks == 0 || LAYERS==0 || !diagnosticsMultiplexOK){
                    return(NULL)
                }else{
                    write.table(listDiagnosticsMerge, file, sep = ";", row.names = FALSE) 
                }
            }
        )

        output$downCentralityTableSingleLayer <- downloadHandler(    
            filename = function() { paste0(input$txtProjectName, "_centrality_perLayer_table.csv") },
            content = function(file) { 

                if(input$btnCalculateCentralityDiagnostics==0 || input$btnImportNetworks == 0 || LAYERS==0 || !diagnosticsSingleLayerOK){
                    return(NULL)
                }else{
                    write.table(listDiagnosticsMergeSingleLayer, file, sep = ";", row.names = FALSE) 
                }
            }
        )


        output$downComponentsSummaryTable <- downloadHandler(    
            filename = function() { paste0(input$txtProjectName, "_components_summary_table.csv") },
            content = function(file) { 

                if(input$btnCalculateComponentsDiagnostics==0 || input$btnImportNetworks == 0 || LAYERS==0 || !componentsMultiplexOK){
                    return(NULL)
                }else{
                    write.table(sumComponentsMerge, file, sep = ";", row.names = FALSE) 
                }
            }
        )

        output$downComponentsTable <- downloadHandler(    
            filename = function() { paste0(input$txtProjectName, "_components_table.csv") },
            content = function(file) { 

                if(input$btnCalculateComponentsDiagnostics==0 || input$btnImportNetworks == 0 || LAYERS==0 || !componentsMultiplexOK){
                    return(NULL)
                }else{
                    write.table(listComponentsMerge, file, sep = ";", row.names = FALSE) 
                }
            }
        )
        
        output$downComponentsSummaryTableSingleLayer <- downloadHandler(    
            filename = function() { paste0(input$txtProjectName, "_components_summary_perLayer_table.csv") },
            content = function(file) { 

                if(input$btnCalculateComponentsDiagnostics==0 || input$btnImportNetworks == 0 || LAYERS==0 || !componentsSingleLayerOK){
                    return(NULL)
                }else{
                    write.table(sumComponentsMergeSingleLayer, file, sep = ";", row.names = FALSE) 
                }
            }
        )

        output$downComponentsTableSingleLayer <- downloadHandler(    
            filename = function() { paste0(input$txtProjectName, "_components_perLayer_table.csv") },
            content = function(file) { 

                if(input$btnCalculateComponentsDiagnostics==0 || input$btnImportNetworks == 0 || LAYERS==0 || !componentsSingleLayerOK){
                    return(NULL)
                }else{
                    write.table(listComponentsMergeSingleLayer, file, sep = ";", row.names = FALSE) 
                }
            }
        )

        output$downCommunitySummaryTable <- downloadHandler(
            filename = function() { paste0(input$txtProjectName, "_community_summary_table.csv") },
            content = function(file) { 
                if(input$btnCalculateCommunityDiagnostics==0 || input$btnImportNetworks == 0 || LAYERS==0 || !communityMultiplexOK){
                    return(NULL)
                }else{
                    write.table(sumCommunitiesMerge, file, sep = ";", row.names = FALSE) 
                }
            }
        )

        output$downCommunityBatchTable <- downloadHandler(    
            filename = function() { paste0(input$txtProjectName, "_community_multiscale_table.csv") },
            content = function(file) { 

                if(input$btnCalculateCommunityDiagnostics==0 || input$btnImportNetworks == 0 || LAYERS==0 || !communityMultiplexBatchOK){
                    return(NULL)
                }else{
                    write.table(communityBatchData, file, sep = ";", row.names = FALSE) 
                }
            }
        )

        output$downCommunityTable <- downloadHandler(    
            filename = function() { paste0(input$txtProjectName, "_community_table.csv") },
            content = function(file) { 

                if(input$btnCalculateCommunityDiagnostics==0 || input$btnImportNetworks == 0 || LAYERS==0 || !communityMultiplexOK){
                    return(NULL)
                }else{
                    write.table(listCommunitiesMerge, file, sep = ";", row.names = FALSE) 
                }
            }
        )
        
        output$downCommunitySummaryTableSingleLayer <- downloadHandler(    
            filename = function() { paste0(input$txtProjectName, "_community_summary_perLayer_table.csv") },
            content = function(file) { 

                if(input$btnCalculateCommunityDiagnostics==0 || input$btnImportNetworks == 0 || LAYERS==0 || !communitySingleLayerOK){
                    return(NULL)
                }else{
                    write.table(sumCommunitiesMergeSingleLayer, file, sep = ";", row.names = FALSE) 
                }
            }
        )

        output$downCommunityTableSingleLayer <- downloadHandler(    
            filename = function() { paste0(input$txtProjectName, "_community_perLayer_table.csv") },
            content = function(file) { 

                if(input$btnCalculateCommunityDiagnostics==0 || input$btnImportNetworks == 0 || LAYERS==0 || !communitySingleLayerOK){
                    return(NULL)
                }else{
                    write.table(listCommunitiesMergeSingleLayer, file, sep = ";", row.names = FALSE) 
                }
            }
        )

        
        output$downOverlappingSummaryTable <- downloadHandler(    
            filename = function() { paste0(input$txtProjectName, "_overlap_table.csv") },
            content = function(file) { 

                if(input$btnCalculateCorrelationDiagnostics==0 || input$btnImportNetworks == 0 || LAYERS==0){
                    return(NULL)
                }else{
                    write.table(listOverlap, file, sep = ";", row.names = FALSE) 
                }
            }
        )

        output$downOverlappingNodeSummaryTable <- downloadHandler(    
            filename = function() { paste0(input$txtProjectName, "_node-overlap_table.csv") },
            content = function(file) { 

                if(input$btnCalculateCorrelationDiagnostics==0 || input$btnImportNetworks == 0 || LAYERS==0){
                    return(NULL)
                }else{
                    write.table(listNodeOverlap, file, sep = ";", row.names = FALSE) 
                }
            }
        )

        output$downDistanceSimilaritySummaryTable <- downloadHandler(    
            filename = function() { paste0(input$txtProjectName, "_interdistance_table.csv") },
            content = function(file) { 

                if(input$btnCalculateCorrelationDiagnostics==0 || input$btnImportNetworks == 0 || LAYERS==0){
                    return(NULL)
                }else{
                    write.table(listDistanceSimilarity, file, sep = ";", row.names = FALSE) 
                }
            }
        )
        
        output$downInterPearsonSummaryTable <- downloadHandler(    
            filename = function() { paste0(input$txtProjectName, "_interpearson_table.csv") },
            content = function(file) { 

                if(input$btnCalculateCorrelationDiagnostics==0 || input$btnImportNetworks == 0 || LAYERS==0){
                    return(NULL)
                }else{
                    write.table(listInterPearson, file, sep = ";", row.names = FALSE) 
                }
            }
        )

        output$downInterSpearmanSummaryTable <- downloadHandler(    
            filename = function() { paste0(input$txtProjectName, "_interspearman_table.csv") },
            content = function(file) { 

                if(input$btnCalculateCorrelationDiagnostics==0 || input$btnImportNetworks == 0 || LAYERS==0){
                    return(NULL)
                }else{
                    write.table(listInterSpearman, file, sep = ";", row.names = FALSE) 
                }
            }
        )

        output$downQueryNodesTable <- downloadHandler(    
            filename = function() { paste0(input$txtProjectName, "_query_table.csv") },
            content = function(file) { 

                if(input$btnImportNetworks == 0 || LAYERS==0){
                    return(NULL)
                }else{
                    write.table(listQueryResult, file, sep = ";", row.names = FALSE) 
                }
            }
        )

        observeEvent(input$btnImportNodeColor, {
    
            isolate({
                progress <- shiny::Progress$new()
                on.exit(progress$close())
                progress$set(message = 'Importing external attributes for nodes...', value = 0.2)
                Sys.sleep(1)

                if(length(input$nodecolor_file)>0){
                    if(!file.exists(input$nodecolor_file$datapath)){
                        progress2 <- shiny::Progress$new(session)
                        on.exit(progress2$close())
                        progress2$set(message = paste('ERROR! File',input$nodecolor_file$datapath,'does not exist.'), value = 0.01)
                        Sys.sleep(10)
                        return(NULL)
                    }
                    externalNodeColorTable <<- read.table(input$nodecolor_file$datapath, sep=as.character(input$txtNodeColorFileSep), header=T)

                    if(!"nodeID" %in% colnames(externalNodeColorTable)){
                        progress2 <- shiny::Progress$new(session)
                        on.exit(progress2$close())
                        progress2$set(message = paste('ERROR! Missing nodeID in external file.'), value = 0.01)
                        Sys.sleep(10)
                        return(NULL)                        
                    }
                    if(!"layerID" %in% colnames(externalNodeColorTable)){
                        progress2 <- shiny::Progress$new(session)
                        on.exit(progress2$close())
                        progress2$set(message = paste('ERROR! Missing layerID in external file.'), value = 0.01)
                        Sys.sleep(10)
                        return(NULL)                                                
                    }
                    
                    externalNodeColorTable$layerID <<- as.numeric(externalNodeColorTable$layerID)
                    
                    if("color" %in% colnames(externalNodeColorTable)){
                        externalNodeColorFlag <<- TRUE
                    }else{
                        externalNodeColorFlag <<- FALSE
                    }
                    if("size" %in% colnames(externalNodeColorTable)){
                        externalNodeSizeFlag <<- TRUE
                    }else{
                        externalNodeSizeFlag <<- FALSE
                    }
                    
                    externalNodeColorTable$size <<- as.numeric(externalNodeColorTable$size)
                    externalNodeColorTable$color <<- as.character(externalNodeColorTable$color)
                                        
                    if(input$chkEdgeListLabel){
                        externalNodeColorTable$nodeLabel <<- externalNodeColorTable$nodeID
                        externalNodeColorTable$nodeID <<- nodeLabelSeqIdConvTable[ externalNodeColorTable$nodeLabel ]
                    }

                    externalNodeColorTable$nodeID <<- as.numeric(externalNodeColorTable$nodeID)
                    print(externalNodeColorTable)
                    
                    #progress$set(detail = 'Setting the colors...', value = 0.6)
                    #Sys.sleep(1)
                }else{
                    return()
                }
                
                progress$set(detail = 'Import Completed!', value = 1)
                Sys.sleep(2)
            })
        })


        observeEvent(input$btnImportEdgeColor, {
    
            isolate({
                progress <- shiny::Progress$new()
                on.exit(progress$close())
                progress$set(message = 'Importing external attributes for edges...', value = 0.2)
                Sys.sleep(1)

                if(length(input$edgecolor_file)>0){
                    if(!file.exists(input$edgecolor_file$datapath)){
                        progress2 <- shiny::Progress$new(session)
                        on.exit(progress2$close())
                        progress2$set(message = paste('ERROR! File',input$edgecolor_file$datapath,'does not exist.'), value = 0.01)
                        Sys.sleep(10)
                        return(NULL)
                    }
                    externalEdgeColorTable <<- read.table(input$edgecolor_file$datapath, sep=as.character(input$txtEdgeColorFileSep), header=T)

                    if(!"nodeID.from" %in% colnames(externalEdgeColorTable)){
                        progress2 <- shiny::Progress$new(session)
                        on.exit(progress2$close())
                        progress2$set(message = paste('ERROR! Missing nodeID.from in external file.'), value = 0.01)
                        Sys.sleep(10)
                        return(NULL)                        
                    }
                    if(!"layerID.from" %in% colnames(externalEdgeColorTable)){
                        progress2 <- shiny::Progress$new(session)
                        on.exit(progress2$close())
                        progress2$set(message = paste('ERROR! Missing layerID.from in external file.'), value = 0.01)
                        Sys.sleep(10)
                        return(NULL)                                                
                    }
                    if(!"nodeID.to" %in% colnames(externalEdgeColorTable)){
                        progress2 <- shiny::Progress$new(session)
                        on.exit(progress2$close())
                        progress2$set(message = paste('ERROR! Missing nodeID.to in external file.'), value = 0.01)
                        Sys.sleep(10)
                        return(NULL)                        
                    }
                    if(!"layerID.to" %in% colnames(externalEdgeColorTable)){
                        progress2 <- shiny::Progress$new(session)
                        on.exit(progress2$close())
                        progress2$set(message = paste('ERROR! Missing layerID.to in external file.'), value = 0.01)
                        Sys.sleep(10)
                        return(NULL)                                                
                    }

                    externalEdgeColorTable$layerID.to <<- as.numeric(externalEdgeColorTable$layerID.to)
                    externalEdgeColorTable$layerID.from <<- as.numeric(externalEdgeColorTable$layerID.from)
                    
                    if("color" %in% colnames(externalEdgeColorTable)){
                        externalEdgeColorFlag <<- TRUE
                    }else{
                        externalEdgeColorFlag <<- FALSE
                    }
                    if("size" %in% colnames(externalEdgeColorTable)){
                        externalEdgeSizeFlag <<- TRUE
                    }else{
                        externalEdgeSizeFlag <<- FALSE
                    }
                    
                    externalEdgeColorTable$size <<- as.numeric(externalEdgeColorTable$size)
                    externalEdgeColorTable$color <<- as.character(externalEdgeColorTable$color)
                                        
                    if(input$chkEdgeListLabel){
                        externalEdgeColorTable$nodeLabel.from <<- externalEdgeColorTable$nodeID.from
                        externalEdgeColorTable$nodeID.from <<- nodeLabelSeqIdConvTable[ externalEdgeColorTable$nodeLabel.from ]
                        externalEdgeColorTable$nodeLabel.to <<- externalEdgeColorTable$nodeID.to
                        externalEdgeColorTable$nodeID.to <<- nodeLabelSeqIdConvTable[ externalEdgeColorTable$nodeLabel.to ]
                    }

                    externalEdgeColorTable$nodeID.from <<- as.numeric(externalEdgeColorTable$nodeID.from)
                    externalEdgeColorTable$nodeID.to <<- as.numeric(externalEdgeColorTable$nodeID.to)
                    print(externalEdgeColorTable)
                    
                    #progress$set(detail = 'Setting the colors...', value = 0.6)
                    #Sys.sleep(1)
                }else{
                    return()
                }
                
                progress$set(detail = 'Import Completed!', value = 1)
                Sys.sleep(2)
            })
        })


                    
        #######################################
        ## Simple interface with octave (DEPRECATED)
        #######################################
    
#        createOctaveConfigFile <- function(){
#            if(LAYERS==0) return(NULL)
#            
#            octaveConfigFile <- "octave/muxOctaveConfig.m"
#            
#            write(paste("AnalysisName = \"",input$txtProjectName,"\";",sep=""),
#                file=octaveConfigFile,append=F)
#    
#            if(input$radMultiplexModel=="MULTIPLEX_IS_EDGECOLORED"){
#                write(paste0("isExtendedEdgesList = 0;"), file=octaveConfigFile,append=T)
#                for(l in 1:LAYERS){
#                    if(input$chkEdgeListLabel){
#                        write(paste0("LayersList{",l,"}=\"",normalizePath(paste0(fileName[[l]][1],".rel"), winslash = "/"),"\";"),
#                            file=octaveConfigFile,append=T)                    
#                    }else{
#                        write(paste0("LayersList{",l,"}=\"",normalizePath(fileName[[l]][1], winslash = "/"),"\";"),
#                            file=octaveConfigFile,append=T)
#                    }
#                }
#            }else{
#                write(paste0("isExtendedEdgesList = 1;"), file=octaveConfigFile,append=T) 
#                if(input$chkEdgeListLabel){
#                    write(paste0("MultiLayerEdgesListFile = \"",normalizePath(paste0(fileName[[1]][1],".rel"),winslash = "/"),"\";"),
#                            file=octaveConfigFile,append=T)
#                }else{
#                    write(paste0("MultiLayerEdgesListFile = \"",normalizePath(fileName[[1]][1],winslash = "/"),"\";"),
#                            file=octaveConfigFile,append=T)
#                }
#            }
#                
#            if(!DIRECTED){
#                flag <- "U"
#                if(!input$chkEdgeListUndirectedBoth) flag <- "UD"
#            }else{
#                flag <- "D"
#            }
#            if(WEIGHTED) flag <- paste(flag,"W",sep="")
#            write(paste("Flags = \"",flag,"\";",sep=""),
#                file=octaveConfigFile,append=T)
#    
#            write(paste("MaxNodes = ",Nodes,";"),
#                file=octaveConfigFile,append=T)
#    
#            write(paste("FirstNodeLabel = ",minNodeID,";"),
#                file=octaveConfigFile,append=T)
#
#            if(!is.null(input$txtGamma) && input$txtGamma!=""){
#                #trick necessary because this value is hidden at the beginning and it would recognize its value
#                #only if you pass from multislice community detection panel.. 
#                write(paste("GammaParameter = ",input$txtGamma,";"),
#                    file=octaveConfigFile,append=T)
#            }else{
#                write(paste("GammaParameter = ",1,";"),
#                    file=octaveConfigFile,append=T)                
#            }
#                
#            write(paste("OmegaParameter = ",input$txtOmega,";"),
#                file=octaveConfigFile,append=T)
#    
#            write(paste("InterAssortativityType = \"",input$selAssortativityType,"\";",sep=""),
#                file=octaveConfigFile,append=T)
#
#            write(paste("ConnectedComponentsType = \"","simple","\";",sep=""),
#                file=octaveConfigFile,append=T)
#    
#            if(input$radMultiplexType=="MULTIPLEX_IS_ORDERED"){
#                write(paste("MultisliceType = \"ordered\";"),
#                    file=octaveConfigFile,append=T)
#            }else if(input$radMultiplexType=="MULTIPLEX_IS_CATEGORICAL"){
#                write(paste("MultisliceType = \"categorical\";"),
#                    file=octaveConfigFile,append=T)                    
#            }    
#            
#            return(TRUE)
#        }
    
        #######################################
        ## External function for plots
        #######################################
        
        #to plot a matrix, as for the interassortativity, I use a piece of code apparently available for free from
        #http://www.phaget4.org/R/image_matrix.html
        #Author: Chris Seidel
        # ----- Define a function for plotting a matrix ----- #
        myImagePlot <- function(x, ...){
             min <- min(x,na.rm=T)
             max <- max(x,na.rm=T)
             yLabels <- rownames(x)
             xLabels <- colnames(x)
             title <-c()
             ColorRamp <- c()
          # check for additional function arguments
          if( length(list(...)) ){
            Lst <- list(...)
            if( !is.null(Lst$zlim) ){
               min <- Lst$zlim[1]
               max <- Lst$zlim[2]
            }
            if( !is.null(Lst$yLabels) ) yLabels <- c(Lst$yLabels)
            if( !is.null(Lst$xLabels) ) xLabels <- c(Lst$xLabels)
            if( !is.null(Lst$title) ) title <- Lst$title
            if( !is.null(Lst$ColorRamp) ) ColorRamp <- Lst$ColorRamp
          }
        # check for null values
        if( is.null(xLabels) ) xLabels <- c(1:ncol(x))
        if( is.null(yLabels) ) yLabels <- c(1:nrow(x))
        
        layout(matrix(data=c(1,2), nrow=1, ncol=2), widths=c(4,1), heights=c(1,1))
    
        if( is.null(ColorRamp) ){    
             # Red and green range from 0 to 1 while Blue ranges from 1 to 0
             ColorRamp <- rgb( seq(0,1,length=256),  # Red
                               seq(0,1,length=256),  # Green
                               seq(1,0,length=256))  # Blue
         }
         ColorLevels <- seq(min, max, length=length(ColorRamp))
        
         # Reverse Y axis
         reverse <- nrow(x) : 1
         yLabels <- yLabels[reverse]
         x <- x[reverse,]
        
         # Data Map
         par(mar = c(3,5,2.5,2))
         image(1:length(xLabels), 1:length(yLabels), t(x), col=ColorRamp, xlab="",
         ylab="", axes=FALSE, zlim=c(min,max))
         if( !is.null(title) ){
            title(main=title)
         }
         axis(BELOW<-1, at=1:length(xLabels), labels=xLabels, cex.axis=1)
         axis(LEFT <-2, at=1:length(yLabels), labels=yLabels, las= HORIZONTAL<-1,
         cex.axis=1)
        
         # Color Scale
         par(mar = c(3,2.5,2.5,2))
         image(1, ColorLevels,
              matrix(data=ColorLevels, ncol=length(ColorLevels),nrow=1),
              col=ColorRamp,
              xlab="",ylab="",
              xaxt="n")
    
         layout(1)
        }
        # ----- END plot function ----- #
    
    
        NormalizedShannonEntropy <- function(featureArray){
            #this assume as the number of bins the max value in featureArray
            B <- max(featureArray)
            N <- length(featureArray)
            H <- 0
            for(i in 1:B){
                p <- length(featureArray[featureArray==i])/N
                if(p>0) H <- H - p*log(p)
            }
            
            return(H/log(N))
        }
        
        KullbackLeiblerDivergence <- function(x,y){
            #this code is valid for this application, because I am assuming that the number of bins is
            #given by the max value in the arrays, which are integers and binned during pre-processing
            #note that the function is not symmetric!
            if(length(x)!=length(y)){
                stop("KLD: mismatching length of the two arrays")
            }
            B <- max(max(x,na.rm=T),max(y))
            N <- length(x)
            KLD <- 0
        
            for(i in 1:B){
                px <- length(x[x==i])/N
                py <- length(y[y==i])/N
                if(px>0 && py>0) KLD <- KLD + px*log(px/py)
            }
            
            return(KLD)
        }
        
        JensenShannonDivergence <- function(x,y){
            if(length(x)!=length(y)){
                stop("JSD: mismatching length of the two arrays")
            }
            B <- max(max(x,na.rm=T),max(y))
            N <- length(x)
            JSD <- 0
        
            for(i in 1:B){
                px <- length(x[x==i])/N
                py <- length(y[y==i])/N
                m <- 0.5*(px+py)
                if(px>0 && m>0) JSD <- JSD + 0.5*px*log(px/m)
                if(py>0 && m>0) JSD <- JSD + 0.5*py*log(py/m)
            }
        
            return( JSD )
        }
        
        GetCorrelationMatrix <- function(FeaturesDataFrame,method="spearman"){
            plotFeatures <- max(FeaturesDataFrame$feature)
        
            #build the correlation matrix for the measures
            correlationMatrix <- matrix(0,nrow=plotFeatures,ncol=plotFeatures)
                
            for(l in 1:plotFeatures){
                thisCluster <- FeaturesDataFrame$cluster[FeaturesDataFrame$feature==l]
        
                for(m in l:plotFeatures){
                    if(method=="pearson"){
                        correlationMatrix[l,m] <- cor(thisCluster,FeaturesDataFrame$cluster[FeaturesDataFrame$feature==m],use="all.obs", method="pearson")
                    }else if(method=="spearman"){
                        correlationMatrix[l,m] <- cor(thisCluster,FeaturesDataFrame$cluster[FeaturesDataFrame$feature==m],use="all.obs", method="spearman")                
                    }else if(method=="jsd"){
                        correlationMatrix[l,m] <- JensenShannonDivergence(thisCluster,FeaturesDataFrame$cluster[FeaturesDataFrame$feature==m]) 
                    }
                    correlationMatrix[m,l] <- correlationMatrix[l,m]
                }
        
                if(method=="pearson" || method=="spearman"){
                    correlationMatrix[l,l] <- 1
                }else if (method=="jsd"){
                    correlationMatrix[l,l] <- 0
                }
            }
            
            correlationMatrix[is.na(correlationMatrix)] <- 0
            
            return(correlationMatrix)
        }
        
        GetDistanceMatrix <- function(FeaturesDataFrame,method="spearman"){
            correlationMatrix <- GetCorrelationMatrix(FeaturesDataFrame,method)
            
            if(method=="pearson" || method=="spearman"){
                return( 1-correlationMatrix )
            }else if(method=="jsd"){
                return( sqrt(correlationMatrix) )
            }
        }
                
        annular_chart_ <- function (Nodes,Clusters, r1=1, r2=2, Border = NA, colorPalette) {
           stopifnot(Nodes>=0, r1 >= 0, r2 > 0, r1 < r2)
           #nodeOffset <- floor(max(Nodes)*0.05) + 1
           #x <- Nodes / (max(Nodes)+nodeOffset)
           #x <- c(0,x,rep(0,nodeOffset))
           #Clusters <- c(0,Clusters,rep(0,nodeOffset))
           x <- Nodes / (max(Nodes))
           x <- c(0,x)
           Clusters <- c(0,Clusters)
        
           for (i in 2:length(x)) {
             theta <- 2*pi*seq(x[i-1], x[i], length=100)
        
             polygon( c(r1 * cos(theta), r2 * cos(rev(theta))),
                      c(r1 * sin(theta), r2 * sin(rev(theta))),
                      col = colorPalette[Clusters[i]], border = Border )
           }
        }
        
        
        plotAnularViz <- function (FeaturesDataFrame, rCore=0.4,DisplacementFactor=0.05, Border = NA, sortbyFeatureID = 1, correlationMethod = "spearman", ShowLabels = T, Title = "") {
            myFontSize <- as.numeric(input$txtANULAR_VIZ_FONT_SIZE)
            
            print("### Annular Viz")
            
            #print(FeaturesDataFrame)
            
            #Expected format for data.frame: feature node cluster
            plotFeatures <- max(FeaturesDataFrame$feature)
            plotNodes <- max(FeaturesDataFrame$node)
            plotClusters <- max(FeaturesDataFrame$cluster)
            Displacement <- DisplacementFactor/plotFeatures
            
            plot.new()
            plot.window(xlim = c(-1,1), ylim = c(-1,1))


            #Set the palette
            #colorPalette <- rainbow(plotClusters+1,start=0.,end=0.8,alpha=0.9)[1:plotClusters]
            colorPalette <- colorRampPalette(brewer.pal(brewer.pal.info$maxcolors[row.names(brewer.pal.info)==input$selAnularColorPalette],input$selAnularColorPalette))(plotClusters)
               
            #plot the core
            #annular_chart_(1:plotNodes, AggregateCommunityDataFrame$cluster, rCore - 5*Displacement - ((1-rCore)/plotFeatures), rCore - 5*Displacement, Border, colorPalette)

            #plot the single-features
            if(nrow(FeaturesDataFrame)>0){
                image.plot(legend.only=TRUE, 1, 1:length(colorPalette),
                    z=matrix(data=1:length(colorPalette), ncol=length(colorPalette),nrow=1),
                    col=colorPalette,
                    xlab="",ylab="", xaxt="n", horizontal=T, legend.mar=4.1, cex=2*myFontSize)   

                entropyFactor <- vector()
                featureLabels <- vector()
                        
                for(l in 1:plotFeatures){
                    #calculate also the entropy of the distribution in the meanwhile
                    entropyFactor[l] <- 1-NormalizedShannonEntropy( FeaturesDataFrame$cluster[FeaturesDataFrame$feature==l] )

                    #assign the label
                    featureLabels[l] <- as.character(unique(FeaturesDataFrame$featurelabel[FeaturesDataFrame$feature==l]))
                }

                if(correlationMethod!="none"){
                    distanceMatrix <- GetDistanceMatrix(FeaturesDataFrame,correlationMethod)
                    myClustering <- hclust(as.dist(distanceMatrix),method="centroid")
                    featureOrdering <- myClustering$order
                }else{
                    featureOrdering <- 1:plotFeatures
                }

                #print(featureOrdering)
                if(sortbyFeatureID>0){
                    for(l in 1:plotFeatures){
                        if(l==sortbyFeatureID){
                            print(paste(" = Order by feature",l," ( ring",which(featureOrdering==l),")"))
                            idx <- sort.int(FeaturesDataFrame$cluster[FeaturesDataFrame$feature==l],index.return=T)$ix

                        }
                    }
                }else{
                    #we order by the feature with largest entropy, that is the min entropy factor
                    featureIndex <- which.min(entropyFactor)
                    print(paste(" = Order by maximum entropy feature",featureIndex," ( ring",which(featureOrdering==featureIndex),")"))
                    
                    idx <- sort.int(FeaturesDataFrame$cluster[FeaturesDataFrame$feature==featureIndex],index.return=T)$ix
                }
             
                for(l in 1:plotFeatures){
                    l2 <- featureOrdering[l]
                    thisCluster <- FeaturesDataFrame$cluster[FeaturesDataFrame$feature==l2]
                    #we reduce the thickness of the ring according to its information entropy: 
                    #smaller the entropy, smaller the thickness
                    print(paste(" == Feature",l2,": entropy =",1-entropyFactor[l2]))

                    annular_chart_(1:plotNodes, thisCluster[idx], rCore + ((1-rCore)/plotFeatures)*(l-1), rCore + ((1-rCore)/plotFeatures)*(l -0.5*entropyFactor[l2]) - Displacement, Border, colorPalette)
                    
                    if(l==plotFeatures && ShowLabels==T){
                        #show nodes label
                        x <- (1:plotNodes)/max(plotNodes)
                        x <- c(0,x)
                        for (i in 2:length(x)) {
                           theta <- 2*pi*(x[i-1] + x[i])*0.5
                           r <- (rCore + ((1-rCore)/plotFeatures)*(l -0.5*entropyFactor[l2])- Displacement)*1.04
                           text( r*cos(theta), r*sin(theta), label=as.character(idx[i-1]),cex = myFontSize, col="black" )
                        }
                    }
                }
        
                for(l in 1:plotFeatures){
                    l2 <- featureOrdering[l]
                    #print(paste(featureLabels[l],"-->",featureLabels[l2]))
                    mtext(paste(featureLabels[l2],l,""), side=3, line=2.2 - (l-1)*1.5*myFontSize, adj=1.0, cex=2*myFontSize, col="black")
                }
                mtext(Title, side=3, line=1., cex=3*myFontSize, col="black")       
            }    
        }
    }, warning = function(war) {
      # warning handler picks up where error was generated
      print(paste("Warning:  ",war))
        progress <- shiny::Progress$new(session)
        on.exit(progress$close())
        progress$set(message = paste('Warning! ',war), value = 0.5)
        Sys.sleep(10)
    }, error = function(err) {
      # error handler picks up where error was generated
      print(paste("Error:  ",err))
        progress <- shiny::Progress$new(session)
        on.exit(progress$close())
        progress$set(message = paste('Error! ',err), value = 0.5)
        Sys.sleep(10)
    }, finally = {
    }) #end tryCatch

})