library(igraph)
## 
## Attaching package: 'igraph'
## The following objects are masked from 'package:stats':
## 
##     decompose, spectrum
## The following object is masked from 'package:base':
## 
##     union
g <- sample_gnm(12, 20) 

plot(g) # A simple plot of the network - we'll talk more about plots later

dists = distances(g)

#Setting the defender node and states of all other nodes
d <- 2
n <- vcount(g) # sample size
state_vec <- sample(c(-1,0,1), replace=TRUE, size=n)
state_vec[d] <- 1 #Hardcoding the state of the defender to 1


#This is the function that takes as input the parent node and outputs
#the nodes in a 1-hop neighborhood i.e., the child nodes
theta1nodes <- function(x, y, z){
  
  #X is the parent node
  #Y is the distance matrix
  #Z is the defender node
  #W is the nodes in the current hop that must not be counted
  
  idx <- y[x,]
  
  idx_theta1 <- which(idx == 1)
  
  idx_theta1 <- idx_theta1[!idx_theta1 %in% z]
  
  #idx_theta1 <- idx_theta1[!idx_theta1 %in% w]
  
  return(idx_theta1)
}

#Function for calculating expected states
#Expected states for child nodes X
#Using probability conversion matrix information
#For parent node Z
#Under scenario s
exputil <- function(x, z, s, p, q){
  
  #Expected states for child nodes X
  #Using probability conversion matrix information
  #For parent node Z
  #Under scenario s
  
  s1 <- vector(mode = "numeric", length = length(x))
  
  #Scenario 1 - Parent induces child to have parent's opinion
  if(s==1){
    
    muc <- p
    mu2c <- q
    
    for (i in 1:length(x)) {
      #First term is tied to probability of change and second term is tied to probability of no change
      if(z ==1){
        
        if(x[i] == 1){
          
          s1[i] <- 1 
          
        }
        
        if(x[i] == 0){
          
          s1[i] <- muc*(z) + (1-muc)*(x[i])
          
        }
        
        if(x[i] == -1){
          
          s1[i] <- mu2c*(z) + (1-mu2c)*(x[i])
          
        }
        
      }
      
      if(z ==0){
        
        if(x[i] == 1){
          
          s1[i] <- (muc)*(z) + (1-muc)*x[i]
          
        }
        
        if(x[i] == 0){
          
          s1[i] <- 0
          
        }
        
        if(x[i] == -1){
          
          s1[i] <- mu2c*(0) + (1-mu2c)*(x[i])
          
        }
        
      }
      
      if(z ==-1){
        
        if(x[i] == 1){
          
          s1[i] <- mu2c*(z) + (1-mu2c)*x[i]
          
        }
        
        if(x[i] == 0){
          
          s1[i] <- muc*(z) + (1-muc)*(x[i])
          
        }
        
        if(x[i] == -1){
          
          s1[i] <- -1
          
        }
        
      }
      
    }
    
    
  }
  
  #Scenario 2 - Parent induces child to have a positive opinion
  if(s==2){
    
    muc <- p
    mu2c <- q
    
    for (i in 1:length(x)) {
      #First term is tied to probability of change and second term is tied to probability of no change
      if(z ==1){
        
        if(x[i] == 1){
          
          s1[i] <- 1 
          
        }
        
        if(x[i] == 0){
          
          s1[i] <- muc*(x[i]+1) + (1-muc)*(x[i])
          
        }
        
        if(x[i] == -1){
          
          s1[i] <- mu2c*(x[i]+1) + (1-mu2c)*(x[i])
          
        }
        
      }
      
      if(z==0){
        
        if(x[i] == 1){
          
          s1[i] <- 1
          
        }
        
        if(x[i] == 0){
          
          s1[i] <- 0
          
        }
        
        if(x[i] == -1){
          
          s1[i] <- mu2c*(x[i]+1) + (1-mu2c)*(x[i])
          
        }
        
      }
      
      if(z ==-1){
        
        if(x[i] == 1){
          
          s1[i] <- 1
          
        }
        
        if(x[i] == 0){
          
          s1[i] <- muc*(x[i]+1) + (1-muc)*(x[i])
          
        }
        
        if(x[i] == -1){
          
          s1[i] <- -1
          
        }
        
      }
      
    }
    
    
  }
  
  #Scenario 3 - Parent induces child to have a negative opinion
  if(s==3){
    
    muc <- p
    mu2c <- q
    
    for (i in 1:length(x)) {
      #First term is tied to probability of change and second term is tied to probability of no change
      if(z ==1){
        
        if(x[i] == 1){
          
          s1[i] <- 1 
          
        }
        
        if(x[i] == 0){
          
          s1[i] <- muc*(x[i]-1) + (1-muc)*(x[i])
          
        }
        
        if(x[i] == -1){
          
          s1[i] <- -1
          
        }
        
      }
      
      if(z ==0){
        
        if(x[i] == 1){
          
          s1[i] <- (muc)*(x[i]-1) + (1-muc)*x[i]
          
        }
        
        if(x[i] == 0){
          
          s1[i] <- 0
          
        }
        
        if(x[i] == -1){
          
          s1[i] <- -1
          
        }
        
      }
      
      if(z ==-1){
        
        if(x[i] == 1){
          
          s1[i] <- mu2c*(x[i]-1) + (1-mu2c)*x[i]
          
        }
        
        if(x[i] == 0){
          
          s1[i] <- muc*(x[i]-1) + (1-muc)*(x[i])
          
        }
        
        if(x[i] == -1){
          
          s1[i] <- -1
          
        }
        
      }
      
    }
    
    
  }

  return(s1)
  
}

#Function for enumerating the neighborhoods (k=1,2,3...) and their nodes
#This function outputs a list with: (1) max number of hops given graph and defender node, (2) List of nodes per hop, (3) Total 
#number of nodes per hop
khop <- function(d,dmat){
  #d is the defender node
  #dmat is the distances matrix
  
  #First establishing the max k - limit of neighborhoods
  idx <- dmat[d,]
  midx <- max(idx) #The maximum distance from the defender - the k in the k-hop
  
  s1 <- vector(mode = "numeric", length = midx) #Vector for holding number of nodes per hop
  s2 <- list() #List for holding the exact nodes per hop
  
  for (i in 1:midx) {
    
    if(length(which(idx==i))>0){
      
      s2[[i]] <- which(idx==i)
      
      s1[i] <- length(which(idx==i))
      
    }
    
  }
  
  #Creating easy return list
  rs <- list()
  
  rs[["hops"]] <- midx #Max number of possible hops
  
  rs[["nodesperhop"]] <- s2 #List of nodes per hop - List
  
  rs[["numnodesperhop"]] <- s1 #Total number of nodes per hop - Vector
  
  return(rs)
  
}


#Main Script
#Getting the distance (hop) matrix and the adjacency matrix
hopmat <- distances(g)
adjmat <- data.matrix(get.adjacency(g))

#Starting the main loop
#First, finding out the number of hops and max hops
iter_data <- khop(d,hopmat)
nhops_d <- iter_data$hops

#Second, creating matrix to store the expected state data
expstate <- matrix(data = 0, nrow = nrow(hopmat), ncol = ncol(hopmat))

#Third, starting off with defender node
st_node <- d
istart <- st_node

if(length(istart) == 1 && istart == d){
  #Extracting child nodes for defender node
  t1 <- theta1nodes(istart, hopmat, d)
  #Calculate and store the expected state
  expstate[d, t1] <- expstate[d, t1] + exputil(state_vec[t1], state_vec[istart], 1, 0.75, 0.25)
  
  #Going on to next hop
  st_node <- t1
  
}

for (i in 1:(nhops_d-1)) {
  istart <- st_node
  if(length(istart)>1){
    for (j in 1:length(istart)) {
      #Extracting child nodes for each node in parent nodes set
      t1 <- theta1nodes(istart[j], hopmat, d)
      
      if(length(t1)>0){
        expstate[istart[j], t1] <- expstate[istart[j], t1] + exputil(state_vec[t1], state_vec[istart[j]], 1, 0.75, 0.25)
      }
      
    }
    st_node <- t1 #Initializing the next hop
  }
  
}


#Initializing Cost Matrix for immediate theta 1 neighborhood
cij <- vector(mode = "numeric", length = vcount(g))
cij[iter_data$nodesperhop[[1]]] <- runif(length(iter_data$nodesperhop[[1]]), 0, 1)


#All nodes except defender
d_nodes <- iter_data$nodesperhop[[1]]

#Enumerating the overall path of impact when selecting one or the other node
impact_list <- list()
path_list <- list()

#Computing sum within and across hops
sumutil <- function(x, y, z){
  s1 <- 0
  
  if(length(x) == 1){
    
    ix <- which(z[[1]] == x)
    
    s1 <- s1 + sum(y[z[[1]][ix], z[[2]]])
    
  }
  else{
    
    s1 <- s1 + sum(y[x, z[[2]]])
    
  }
  
  if(length(z)>2){
    
    for (j in 2:(length(z)-1)) {
      
      s1 <- s1 + sum(y[z[[j]]], z[[j+1]])
        
    }
    
  }
  
  
  return(s1)
  
}

#Power Set function
pset <- function(set) { 
  n <- length(set)
  masks <- 2^(1:n-1)
  lapply( 1:2^n-1, function(u) set[ bitwAnd(u, masks) != 0 ] )
}

#Generating the power set
all_possible_choices <- pset(d_nodes)
all_possible_choices <- all_possible_choices[-1]

#Matrix for benefits
decision_mat_utility <- matrix(data = 0, nrow = length(all_possible_choices), ncol = 1)

for (i in 1:length(all_possible_choices)) {
  
  decision_mat_utility[i,1] <- sumutil(all_possible_choices[[i]], expstate, iter_data$nodesperhop)
  
  
}

#Decision not considering cost
dk1 <- which(decision_mat_utility == max(decision_mat_utility))
for (i in 1:length(dk1)) {
  
  print("Choice")
  print(all_possible_choices[[dk1[i]]])
  print("Cost:")
  print(sum(cij[all_possible_choices[[dk1[i]]]]))
  print("________________")
  
}
## [1] "Choice"
## [1]  1 10 11
## [1] "Cost:"
## [1] 0.8303753
## [1] "________________"
## [1] "Choice"
## [1]  1  3 10 11
## [1] "Cost:"
## [1] 1.601867
## [1] "________________"