library(Matrix)
library(igraph)
source('C:\\RDS\\code\\RDS3.R')

#Function to exponentiate a matrix
# r-help, https://stat.ethz.ch/pipermail/r-help/2006-September/113542.html
# This one has trouble with numerical stability.
# expM <- function(X,e) { v <- La.svd(X); v$u %*% diag(v$d^e) %*% v$vt }

# Slower but more numerically stable
mat_pow = function(G, e) {
	result = matrix(G, nrow=nrow(G), ncol=ncol(G))
	if(e == 1) { return(result)}
	for (i in 2:e) {
		result = result %*% G
		#print("EXP Mat")
	}
    return( result )
}

var_rw = function(g, steps, covar_fn='RW') {
	# covar_fn is
	#  'RW'  for random walk
	#  'RDS' for RDS weighted
	#cov_mat = matrix(NA, nrow=steps, ncol=steps)
	#cov_mat = outer(1:steps, 1:steps, FUN=covar_fn, g=g, cv_type=covar_fn )

	# Set up a matrix for results:
	node_count = vcount(g)
	cov_mat = matrix(NA, nrow=steps, ncol=steps)

	# Precompute some values:
	W = outer(degree(g), degree(g))^-1
	sum_W = sum(W)
	#norm_W = sum_W/W
	norm_W = W / sum_W

	val_Y = V(g)$aids   ## This depends on name of attibute

	P_s = degree(g)/sum(degree(g))   # assume node is selected at random proportional to degree
	mean_Y = t(P_s) %*% val_Y
	Y = outer(val_Y-mean_Y, val_Y-mean_Y)

	M_tran_probs = get.adjacency(g) / degree(g)

	#cov_mat = norm_W * P_s * P_q * Y

	diag(cov_mat) = P_s %*% (val_Y - mean_Y)^2
	# cov_mat_pre = norm_W * P_s * Y #( P_q * Y )
	if( identical(covar_fn, 'RDS_WRONG') ){
        cov_mat_pre = norm_W * P_s * Y
	} else {  #Use the RW - random walk model
		cov_mat_pre = P_s * Y
	}

	step_diff = abs(outer(1:steps, 1:steps, '-'))
	mat_pow_res = M_tran_probs

	for(i in 1:(steps-1)) {
		P_q = diag(1, node_count, node_count) %*% mat_pow_res
		cov_mat[which(step_diff==i)] = sum( cov_mat_pre * P_q )
		if( i != (steps-1) ) {
			mat_pow_res = mat_pow_res %*% M_tran_probs
		}
	}

	# for(i in 1:steps){
		# for(j in i:steps){
			# if(i!=j) {
				# # P_q[i,j] gives probability of reaching j at q-s steps starting at node i
				# P_q = diag(1, node_count, node_count) %*% mat_pow( M_tran_probs, abs(j-i) )
				# cov_mat[i,j] = sum( cov_mat_pre * P_q)
				# cov_mat[j,i] = cov_mat[i,j]
			# }
		# }
	# }

	return( sum(cov_mat)/(steps^2) )
}

var_rw_srs = function(g, steps) {
	#cov_mat = matrix(NA, nrow=steps, ncol=steps)
	#cov_mat = outer(1:steps, 1:steps, FUN=covar_fn, g=g, cv_type=covar_fn )

	# Set up a matrix for results:
	node_count = vcount(g)
	cov_mat = matrix(NA, nrow=steps, ncol=steps)

	# Precompute some values:
	# W = outer(degree(g), degree(g))^-1
	# sum_W = sum(W)
	# norm_W = sum_W/W

	val_Y = V(g)$aids   ## This depends on name of attibute

	#P_s = as.vector(rep(1/vcount(g), vcount(g)))   # assume node is selected at random (NOT proportional to degree)
	P_s = degree(g)/sum(degree(g))   # assume node is selected at random proportional to degree
	mean_Y = t(P_s) %*% val_Y
	Y = outer(val_Y-mean_Y, val_Y-mean_Y)

	M_tran_probs = get.adjacency(g) / degree(g)
	##M_tran_probs = matrix(P_s, vcount(g), vcount(g), byrow=TRUE)

	#cov_mat = norm_W * P_s * P_q * Y

	diag(cov_mat) = P_s %*% (val_Y - mean_Y)^2
	#cov_mat_pre = norm_W * P_s * Y
	cov_mat_pre = P_s * Y

	step_diff = abs(outer(1:steps, 1:steps, '-'))
	mat_pow_res = M_tran_probs

	for(i in 1:(steps-1)) {
		P_q = diag(1, node_count, node_count) %*% mat_pow_res
		cov_mat[which(step_diff==i)] = sum( cov_mat_pre * P_q )
		if( i != (steps-1) ) {
			mat_pow_res = mat_pow_res %*% M_tran_probs
		}
	}

	# for(i in 1:steps){
		# for(j in i:steps){
			# if(i!=j) {
				# # P_q[i,j] gives probability of reaching j at q-s steps starting at node i
				# P_q = diag(1, node_count, node_count) %*% mat_pow( M_tran_probs, abs(j-i) )
				# cov_mat[i,j] = sum( cov_mat_pre * P_q)
				# cov_mat[j,i] = cov_mat[i,j]
			# }
		# }
	# }

	return( sum(cov_mat)/(steps^2) )
}

var_rds = function(g, steps, covar_fn='RDS') {
	# covar_fn is
	#  'RW'  for random walk
	#  'RDS' for RDS weighted
	#cov_mat = matrix(NA, nrow=steps, ncol=steps)
	#cov_mat = outer(1:steps, 1:steps, FUN=covar_fn, g=g, cv_type=covar_fn )

	# Set up a matrix for results:
	node_count = vcount(g)
	cov_mat = matrix(NA, nrow=steps, ncol=steps)

	# Precompute some values:
	W = degree(g)^-1
	sum_W = sum(W)
	#norm_W = sum_W/W
	norm_W = 1 / sum_W

	val_Y = V(g)$aids   ## This depends on name of attibute

	P_s = degree(g)/sum(degree(g))   # assume node is selected at random proportional to degree
	mean_Y = mean( norm_W*(val_Y/degree(g)) )
	Y = outer((norm_W*val_Y/degree(g))-mean_Y, (norm_W*val_Y/degree(g))-mean_Y)

	M_tran_probs = get.adjacency(g) / degree(g)

	#cov_mat = norm_W * P_s * P_q * Y

	diag(cov_mat) = mean((val_Y - mean_Y)^2)
	# cov_mat_pre = norm_W * P_s * Y #( P_q * Y )
	if( identical(covar_fn, 'RDS') ){
        cov_mat_pre = P_s * Y
	} else {  #Use the RW - random walk model
		cov_mat_pre = P_s * Y
	}

	step_diff = abs(outer(1:steps, 1:steps, '-'))
	mat_pow_res = M_tran_probs

	for(i in 1:(steps-1)) {
		P_q = diag(1, node_count, node_count) %*% mat_pow_res
		cov_mat[which(step_diff==i)] = sum( cov_mat_pre * P_q )
		if( i != (steps-1) ) {
			mat_pow_res = mat_pow_res %*% M_tran_probs
		}
	}

	# for(i in 1:steps){
		# for(j in i:steps){
			# if(i!=j) {
				# # P_q[i,j] gives probability of reaching j at q-s steps starting at node i
				# P_q = diag(1, node_count, node_count) %*% mat_pow( M_tran_probs, abs(j-i) )
				# cov_mat[i,j] = sum( cov_mat_pre * P_q)
				# cov_mat[j,i] = cov_mat[i,j]
			# }
		# }
	# }

	return( mean(cov_mat) )
}

var_srs = function(g, steps) {
	#cov_mat = matrix(NA, nrow=steps, ncol=steps)
	#cov_mat = outer(1:steps, 1:steps, FUN=covar_fn, g=g, cv_type=covar_fn )

	# Set up a matrix for results:
	node_count = vcount(g)
	cov_mat = matrix(NA, nrow=steps, ncol=steps)

	# Precompute some values:
	# W = outer(degree(g), degree(g))^-1
	# sum_W = sum(W)
	# norm_W = sum_W/W

	val_Y = V(g)$aids   ## This depends on name of attibute

	P_s = as.vector(rep(1/vcount(g), vcount(g)))   # assume node is selected at random (NOT proportional to degree)
	mean_Y = t(P_s) %*% val_Y
	Y = outer(val_Y-mean_Y, val_Y-mean_Y)

	#M_tran_probs = get.adjacency(g) / degree(g)
	M_tran_probs = matrix(P_s, vcount(g), vcount(g), byrow=TRUE)

	#cov_mat = norm_W * P_s * P_q * Y

	diag(cov_mat) = P_s %*% (val_Y - mean_Y)^2
	#cov_mat_pre = norm_W * P_s * Y
	cov_mat_pre = P_s * Y

	step_diff = abs(outer(1:steps, 1:steps, '-'))
	mat_pow_res = M_tran_probs

	for(i in 1:(steps-1)) {
		P_q = diag(1, node_count, node_count) %*% mat_pow_res
		cov_mat[which(step_diff==i)] = sum( cov_mat_pre * P_s )
		if( i != (steps-1) ) {
			mat_pow_res = mat_pow_res %*% M_tran_probs
		}
	}

	# for(i in 1:steps){
		# for(j in i:steps){
			# if(i!=j) {
				# # P_q[i,j] gives probability of reaching j at q-s steps starting at node i
				# P_q = diag(1, node_count, node_count) %*% mat_pow( M_tran_probs, abs(j-i) )
				# cov_mat[i,j] = sum( cov_mat_pre * P_q)
				# cov_mat[j,i] = cov_mat[i,j]
			# }
		# }
	# }

	return( sum(cov_mat)/(steps^2) )
}

covar_fn = function(s, q, g, cv_type='covar_fn') {
	# Set up a matrix for results:
	node_count = vcount(g)
	cov_mat = matrix(NA, nrow= node_count, ncol= node_count)

	# Precompute some values:
	W = outer(degree(g), degree(g))^-1
	sum_W = sum(W)
	norm_W = sum_W/W

	val_Y = V(g)$aids   ## This depends on name of attibute
	mean_Y = mean(Y)
	Y = outer(val_Y-mean_Y, val_Y-mean_Y)

	P_s = degree(g)/sum(degree(g))   # assume node is selected at random
	M_tran_probs = get.adjacency(g) / degree(g)
	# P_q[i,j] gives probability of reaching j at q-s steps starting at node i
	P_q = diag(1, node_count, node_count) %*% mat_pow( M_tran_probs, abs(q-s) )

	#for(i in 1:node_count){
	#	for(j in 1:node_count){
	#		cov_mat[i,j] = norm_W[i,j] * P_s[i] * P_q[i,j] * Y[i,j]
	#	}
	#}

	cov_mat = norm_W * P_s * P_q * Y
	return(sum(cov_mat))
}

generate_node_seq = function( M_trans_probs,  seq_length = 10, initial_node = NA ) {
	ret_seq = rep(0, seq_length)
	rand_list = runif(seq_length)
	if (is.na(initial_node[1])) { ret_seq[1]=sample.int(nrow(M_trans_probs), 1) }
	else{
		if(length(initial_node)>1){ ret_seq[1]=sample(nrow(M_trans_probs), 1, prob=initial_node)    }
		else{ ret_seq[1] = initial_node }
	}
	cum_prob = t(apply(M_trans_probs, 1, cumsum))
	cum_prob[,nrow(cum_prob)] = 1  #Set the last column to 1 to avoid rounding errors
	for (i in 2: seq_length){
		 ret_seq[i] = 1+sum(cum_prob[ret_seq[i-1],] <= rand_list[i] )
	}
	# Could add code to replace the state_index with the state code (as.factor)
	return(ret_seq)

}

sim_rds_var = function(ggraph, trials=10, seq_length = 10 ) {
	M_trans_probs = get.adjacency(ggraph)/degree(ggraph)
	results=matrix(nrow=trials,ncol=2)
	for( i in seq(trials) ) {
		sample_nodes = generate_node_seq(M_trans_probs, seq_length, initial_node=degree(ggraph)/sum(degree(ggraph)))
		sample_attr = V(ggraph)$aids[sample_nodes]
		sample_deg = degree(ggraph)[sample_nodes]
		sample_rid = c(NA, head(sample_nodes, -1))

		results[i,] = rds.set_groups_estimate(sample_attr, sample_nodes, sample_rid, sample_deg, groups=c('0','1'), est.only=TRUE, minXrec=0)$P.hat
	}
	#return(diag(var(results)))
	return(results)
}

sim_rw_var = function(ggraph, trials=10, seq_length = 10 ) {
	M_trans_probs = get.adjacency(ggraph)/degree(ggraph)
	results=matrix(nrow=trials,ncol=2)
	for( i in seq(trials) ) {
		sample_nodes = generate_node_seq(M_trans_probs, seq_length, initial_node=degree(ggraph)/sum(degree(ggraph)))
		sample_attr = V(ggraph)$aids[sample_nodes]
		#sample_deg = degree(ggraph)[sample_nodes]
		#sample_rid = c(NA, tail(sample_nodes, -1))

		results[i,] = prop.table(table(factor(sample_attr, levels=c(0,1))))
	}
	#return(diag(var(results)))
	return(results)
}

sim_srs_var = function(ggraph, trials=10, seq_length = 10 ) {
	#M_trans_probs = get.adjacency(ggraph)/degree(ggraph)
	results=matrix(nrow=trials,ncol=2)
	for( i in seq(trials) ) {
		sample_nodes = sample(V(ggraph), size= seq_length, replace=TRUE)
		sample_attr = V(ggraph)$aids[sample_nodes]
		#sample_deg = degree(ggraph)[sample_nodes]
		#sample_rid = c(NA, tail(sample_nodes, -1))

		results[i,] = prop.table(table(factor(sample_attr, levels=c(0,1))))
	}
	return(diag(var(results)))
	#return(results)
}

sim_vh_var = function(ggraph, trials=10, seq_length = 10 ) {
	M_trans_probs = get.adjacency(ggraph)/degree(ggraph)
	results=matrix(nrow=trials,ncol=2)
	for( i in seq(trials) ) {
		sample_nodes = generate_node_seq(M_trans_probs, seq_length, initial_node=degree(ggraph)/sum(degree(ggraph)))
		sample_attr = V(ggraph)$aids[sample_nodes]
		sample_deg = degree(ggraph)[sample_nodes]
		sample_rid = c(NA, head(sample_nodes, -1))

		results[i,] = rds.VH.estimate(sample_attr, sample_nodes, sample_rid, sample_deg, groups=c('0','1'))$RDS.VH
	}
	#return(diag(var(results, na.rm=TRUE)))
	return(results)
}

a_edge_list = c(
1,2,
1,3,
1,5,
2,1,
2,4,
2,6,
3,1,
3,5,
4,2,
4,6,
5,1,
5,3,
5,6,
5,7,
6,2,
6,4,
6,5,
7,5,
7,8,
7,9,
7,12,
8,7,
8,10,
8,11,
9,7,
9,10,
9,11,
10,8,
10,9,
10,12,
11,8,
11,9,
11,12,
12,7,
12,10,
12,11,
12,14,
13,14,
13,15,
13,17,
14,12,
14,13,
14,16,
14,18,
15,13,
15,17,
16,14,
16,18,
17,13,
17,15,
17,18,
18,14,
18,16,
18,17
)

#edge_list_a = matrix(a_edge_list, ncol=2, byrow=TRUE)

attrib_a = rep(0,18)
attrib_a[c(8,9,12:18)] = 1

g_a = graph.empty(directed=FALSE)
g_a = add.vertices( g_a, 18, aids=attrib_a)

g_a = add.edges(g_a, a_edge_list)

g_a = simplify(g_a)

b_edge_list = c(
1,2,
1,9,
1,10,
2,1,
2,3,
2,11,
3,2,
3,5,
3,12,
4,5,
4,13,
5,3,
5,7,
5,14,
6,7,
6,15,
7,5,
7,6,
7,8,
7,16,
8,7,
8,9,
8,17,
9,1,
9,8,
9,18,
10,1,
10,11,
10,18,
11,2,
11,10,
11,12,
12,3,
12,11,
12,14,
13,4,
13,12,
14,5,
14,12,
14,15,
14,16,
15,6,
15,14,
16,7,
16,17,
17,8,
17,16,
17,18,
18,9,
18,10,
18,17
)

attrib_b = rep(0,18)
attrib_b[10:18] = 1

g_b = graph.empty(directed=FALSE)
g_b = add.vertices( g_b, 18, aids=attrib_b)
g_b = add.edges(g_b, b_edge_list)
g_b = simplify(g_b)

comparison_plot_data = function(ggraph, steps_list=c(10, 20), trials=1000 ){
	compare_list = c("sim_rw", "sim_vh", "sim_rds", "var_srs", "var_rw", "var_vh", "var_rds")
	results = matrix(ncol=length(steps_list), nrow=7, dimnames=list(compare_list, steps_list) )
	for (i in seq(from=length(steps_list)) ){
		results[1:3,i] = sim_all_var(ggraph, trials, steps_list[i] )
		results[4,i] = var_srs(ggraph, steps_list[i])
		results[5,i] = var_rw(ggraph, steps_list[i])
		results[6,i] = var_rds(ggraph, steps_list[i])
		results[7,i] = var_rds(ggraph, steps_list[i])
	}
	return(results)
}

sim_all_var = function(ggraph, trials=10, seq_length = 10 ) {
	M_trans_probs = get.adjacency(ggraph)/degree(ggraph)
	compare_list = c("sim_rw", "sim_vh", "sim_rds")
	results=matrix(nrow=trials,ncol=3)
	for( i in seq(trials) ) {
		sample_nodes = generate_node_seq(M_trans_probs, seq_length, initial_node=degree(ggraph)/sum(degree(ggraph)))
		sample_attr = V(ggraph)$aids[sample_nodes]
		sample_deg = degree(ggraph)[sample_nodes]
		sample_rid = c(NA, head(sample_nodes, -1))

		results[i,3] = rds.set_groups_estimate(sample_attr, sample_nodes, sample_rid, sample_deg, groups=c('0','1'), est.only=TRUE, minXrec=0)$P.hat[1]
		results[i,2] = rds.VH.estimate(sample_attr, sample_nodes, sample_rid, sample_deg, groups=c('0','1'))$RDS.VH[1]
		results[i,1] = prop.table(table(factor(sample_attr, levels=c(0,1))))[1]
	}
	return(apply(results, 2, var, na.rm=TRUE))
	#return(results)
}

comparison_plot_data = function(ggraph, steps_list=c(10, 20), trials=1000 ){
	compare_list = c("sim_rw", "sim_vh", "sim_rds", "var_srs", "var_rw", "var_vh", "var_rds")
	results = matrix(ncol=length(steps_list), nrow=7, dimnames=list(compare_list, steps_list) )
	for (i in seq(from=length(steps_list)) ){
		results[1:3,i] = sim_all_var(ggraph, trials, steps_list[i] )
		results[4,i] = var_srs(ggraph, steps_list[i])
		results[5,i] = var_rw(ggraph, steps_list[i])
		results[6,i] = var_rds(ggraph, steps_list[i])
		results[7,i] = var_rds(ggraph, steps_list[i])
	}
	return(results)
}

plot(x=1, type='n',xlim=c(0,100), ylim=c(0,.15), xlab="Sequence Length", ylab="Variance")
plot_lines = function(plot_data) {
	x_cords = as.numeric(dimnames(plot_data)[[2]])
	for (i in 1:nrow(plot_data)){
		lines(x_cords, plot_data[i,], col=c("lightblue", "blue", "darkblue", "red", "lightgreen", "green", "darkgreen")[i])
	}
}

plot_cf_lines = function(plot_data_a, plot_data_b) {
	titles = dimnames(plot_data_a)[[1]]
	x_cords = as.numeric(dimnames(plot_data_b)[[2]])
	for (i in 1:nrow(plot_data_a)){
		plot(x=1, type='n',xlim=c(0,200), ylim=c(0,.25), xlab="Sequence Length", ylab="Variance", main=titles[i])
		lines(x_cords, plot_data_a[i,], col="blue")
		lines(x_cords, plot_data_b[i,], col="lightblue")
	}
}


library(orthopolynom)
library(igraph)
library(parallel)
legendre_Pl_array<-function(m,y){
  pf<-polynomial.functions(legendre.polynomials(m,normalized=T))
  lv<-sapply(X=1:(m+1),FUN=function(x){
    pf[[x]](y) #*sqrt(2*(x-1)+1)
  })
  return(t(lv)*sqrt(2))
}

len_deriv<-function(m,y){
	#y varies from [0,1]
	#returns x(1-x)deriv
	out=matrix(0,m+1,length(y))
	out[1,]=0
	lpa<-legendre_Pl_array(m,2*y-1)[,,drop=F]
	for(i in 2:(m+1)){
		k=i-1
		out[i,]<- -(k/2)*( (2*y-1)*lpa[i,]-sqrt((2*k+1)/(2*k-1))*lpa[i-1,] )
	}
	return(out)
}

len_deriv2<-function(m,y){
	#y varies from [0,1]
	#x(1-x)deriv2
	out=matrix(0,m+1,length(y))
	out[1,]=0
	out[2,]=0
	if(m==1) return(out)
	#lpa<-legendre_Pl_array(m,2*y-1)
	ld = len_deriv(m,y)[,,drop=F]
	for(i in 3:(m+1)){
		k=i-1
		#out[i,]<- sqrt(2*(i-1)+1)*(out[i-2,]/sqrt(2*(i-3)+1)+(1/2)*(2*(i-2)+1)*ld[i-1,]/sqrt(2*(i-2)+1) )
		out[i,] = out[i-2,]*sqrt((2*k+1)/(2*k-3)) + 2*(2*k-1)*sqrt(2*k+1)*ld[i-1,]/sqrt(2*k-1)
	}
	return(out)
}

Kvec.node<-function(dat,m){
	d=dim(dat)[2]
	out=matrix(0,d,m)
	out=lapply(1:d,function(i){
		ld1= t(-2*len_deriv(m,dat[,i])[-1,,drop=F])*(2*dat[,i]-1)
		ld2= dat[,i]*(1-dat[,i])*t(len_deriv2(m,dat[,i])[-1,,drop=F])
		rowMeans(t(ld1+ld2))
	})
	t(simplify2array(out))
}

Kvec.edge<-function(dat,m,edgelist=NULL){
	d=dim(dat)[2]
	g=graph.full(d)
	if(is.null(edgelist)) edgelist=get.edgelist(g)
	e=dim(edgelist)[1]
	if(d>=100){
		cores=detectCores()
	}else{
		cores=1
	}
	ret=mclapply(1:e,function(i){
		s=edgelist[i,1]
		t=edgelist[i,2]
		#out= crossprod(2*len_deriv(m,dat[,s])[-1,]*(2*dat[,s]-1) ,legendre_Pl_array(m,2*dat[,t]-1)[-1,] )
		#out = out + crossprod(legendre_Pl_array(m,2*dat[,s]-1)[-1,], 2*len_deriv(m,dat[,t])[-1,]*(2*dat[,t]-1) )
		#out= out+ crossprod(2*len_deriv2(m,dat[,s])[-1,]*(2*dat[,s]-1) ,legendre_Pl_array(m,2*dat[,t]-1)[-1,] )
		#out = out + crossprod(legendre_Pl_array(m,2*dat[,s]-1)[-1,], 2*len_deriv2(m,dat[,t])[-1,]*(2*dat[,t]-1) )
		out1= crossprod(-2*t(len_deriv(m,dat[,s])[-1,,drop=F])*(2*dat[,s]-1) , t(legendre_Pl_array(m,2*dat[,t]-1)[-1,,drop=F]) )
		out2 = legendre_Pl_array(m,2*dat[,s]-1)[-1,,drop=F] %*% (-2*t(len_deriv(m,dat[,t])[-1,,drop=F])*(2*dat[,t]-1) )
		out1= out1+ crossprod(dat[,s]*(1-dat[,s])*t(len_deriv2(m,dat[,s])[-1,,drop=F]) ,t(legendre_Pl_array(m,2*dat[,t]-1)[-1,,drop=F]) )
		out2 = out2 + legendre_Pl_array(m,2*dat[,s]-1)[-1,,drop=F] %*% (dat[,t]*(1-dat[,t])*t(len_deriv2(m,dat[,t])[-1,,drop=F]) )
		return(list(out1=out1/dim(dat)[1],out2=out2/dim(dat)[1]))
	},mc.cores=cores)
	return(list(array(unlist(lapply(ret,function(x)x$out1)),dim=c(m,m,e)),
		array(unlist(lapply(ret,function(x)x$out2)),dim=c(m,m,e))))
}

# Gamma.ret<-function(dat,m1,m2){
# 	d=dim(dat)[2]
# 	g=graph.full(d)
# 	ge=get.edgelist(g)
# 	nedge=dim(ge)[1]
# 	out=mclapply(1:d,function(i){
# 		eout=mclapply(1:nedge,function(e){
# 			s=ge[e,1];t=ge[e,2]
# 			if(s==i){
# 				l1= len_deriv(m1,dat[,i])[-1,]
# 			}else{
# 				l1=numeric()
# 				lpa=legendre_Pl_array(m2,2*dat[,s]-1)[-1,]
# 				for(l in 1:m2){
# 					if(i<s){
# 						l1=rbind(
# 							l1,
# 							t(t(len_deriv(m2,dat[,i])[-1,]) * lpa[l,])
# 							#t(apply(len_deriv(m2,dat[,i])[-1,],1,function(x) x*lpa[l,,drop=F]))
# 						)
# 					}else{
# 						l1=rbind(
# 								l1,
# 								t(t(lpa) * len_deriv(m2,dat[,i])[-1,][l,])
# 								#t(apply(lpa,1,function(x) x*len_deriv(m2,dat[,i])[-1,][l,,drop=F]))
# 							)
# 					}
# 				}
# 			}
# 			if(t==i){
# 				l2= len_deriv(m1,dat[,i])[-1,]
# 			}else{
# 				l2=numeric()
# 				lpa=legendre_Pl_array(m2,2*dat[,t]-1)[-1,]
# 				for(l in 1:m2){
# 				if(i<t){
# 					l2=rbind(
# 						l2,
# 						t(t(len_deriv(m2,dat[,i])[-1,]) * lpa[l,])
#
# 						#t(apply(len_deriv(m2,dat[,i])[-1,],1,function(x) x*lpa[l,,drop=F]))
# 						)
# 				}else{
# 					l2=rbind(
# 						l2,
# 						t(t(lpa) * len_deriv(m2,dat[,i])[-1,][l,])
# 						#t(apply(lpa,1,function(x) x*len_deriv(m2,dat[,i])[-1,][l,,drop=F]))
# 						)
# 				}
# 				}
# 			}
# 			tcrossprod(l1,l2)/dim(dat)[1]
# 		},mc.cores=detectCores())
# 		return(eout)
# 	},mc.cores=detectCores())
# 	return(out)
#
# }

# Gamma.node<-function(dat,m1,m2){
# 	d=dim(dat)[2]
# 	out=mclapply(1:d,function(i){
# 		eout=mclapply(1:d,function(e){
# 			if(i==e){
# 				l1= len_deriv(m1,dat[,e])[-1,]
# 			}else{
# 				l1=numeric()
# 				lpa=legendre_Pl_array(m2,2*dat[,e]-1)[-1,]
# 				for(l in 1:m2){
# 				if(i<e){
# 					l1=rbind(
# 						l1,
# 						t(apply(len_deriv(m2,dat[,i])[-1,],1,function(x) x*lpa[l,,drop=F]))
# 					)
# 				}else{
# 					l1=rbind(
# 						l1,
# 						t(apply(lpa,1,function(x) x*len_deriv(m2,dat[,i])[-1,]))
# 					)
# 				}
# 				}
# 			}
# 			tcrossprod(l1,l1)/dim(dat)[1]
# 		},mc.cores=1)
# 		return(eout)
# 	},mc.cores=detectCores())
# 	return(out)
# }

Gamma.ret2<-function(dat,m1,m2,cores=1,ge=NULL){
	d=dim(dat)[2]
	if(is.null(ge))ge=get.edgelist(graph.full(d))
	g=graph.edgelist(ge,directed=F)
	nedge=dim(ge)[1]
	out=mclapply(1:d,function(i){
		XX=numeric()
		for(j in sort(c(i,neighbors(g,i)))){
				if(i==j){
					ld=len_deriv(m1,dat[,i])[-1,,drop=F]
					XX=cbind(XX, t(ld) )
				}else if(i<j){
					e=which(ge[,1]%in%c(i,j) & ge[,2]%in%c(i,j))
					ld=len_deriv(m2,dat[,i])[-1,,drop=F]
					lpa=legendre_Pl_array(m2,2*dat[,j]-1)[-1,,drop=F]
					for(l in 1:m2){
						XX=cbind(XX, t(ld) * lpa[l,])
					}
				}else{
					e=which(ge[,1]%in%c(i,j) & ge[,2]%in%c(i,j))
					ld=len_deriv(m2,dat[,i])[-1,,drop=F]
					lpa=legendre_Pl_array(m2,2*dat[,j]-1)[-1,,drop=F]
					for(l in 1:m2){
						XX=cbind(XX, t(lpa) * ld[l,])
					}
				}
		}
		#return(crossprod(XX,XX)/dim(dat)[1])
		#return(XX)
		return(svd(XX,nu=0))
 	},mc.cores=cores)
	return(out)

}

Kvec2<-function(dat,m1,m2,elist=NULL){
	if(is.null(elist)) elist=get.edgelist(graph.full(dim(dat)[2]))
	g=graph.edgelist(elist,directed=F)
	kvn=Kvec.node(dat,m1)
	kve=Kvec.edge(dat,m2,elist)
	#YY=numeric()
	YY=list()
	for(i in 1:d){
		XX=numeric()
		for(j in sort(c(i,neighbors(g,i)))){
			if(i==j){
				XX=c(XX,kvn[i,])
			}else if(i<j){
				e=which(elist[,1]%in% c(i,j) & elist[,2]%in%c(i,j))
				XX= c(XX, kve[[1]][,,e])
			}else{
				e=which(elist[,1]%in% c(i,j) & elist[,2]%in%c(i,j))
				XX= c(XX, kve[[2]][,,e])
			}
		}
		##YY=cbind(YY,XX)
		YY[[i]]=XX
	}
	return(YY)
}

#
# Gamma.node(dat,3,3)
# d=5;n=40
# dat=matrix(runif(d*n),n,d)
# system.time()
# gr=Gamma.ret(dat,3,3)
# kvn=Kvec.node(dat,3)
# kve=Kvec.edge(dat,3)
#
#
# #Gamma: edges: d* d*(d-1)/2 m_2^2 x m_2^2 matrices
# #diag: d * d * matrices
#
# Kvec.node(matrix(runif(100),10,10),5)
#
# g=graph.full(10)
# Kvec.edge(matrix(runif(100),10,10),5,get.edgelist(g))
#
# len_deriv(5,seq(from=0,to=1,length.out=50))
#
# #for(i in 1:4) plot(legendre_Pl_array(10,2*seq(from=0,to=1,length.out=1000)-1)[i,])
# for(i in 1:20){
# plot(
# 	len_deriv2(25,seq(from=0,to=1,length.out=1000))[i,]
# )}
# #plot(
# #	len_deriv2(25,seq(from=0,to=1,length.out=1000))[5,]
# #)
# dev.off()
#


## Functions for Spatial objects

## Function that create a buffer around a centroid
makePoly <- function(centroid.lon, centroid.lat, buffer){
  ## determine buffer for longitude
  latinkm <-  lat2km(centroid.lat)
  res <- 1/latinkm*buffer
  xmin = centroid.lon - res
  xmax = centroid.lon + res
  ymin = centroid.lat - 1/111*buffer
  ymax = centroid.lat + 1/111*buffer
  cornerCoords <- matrix(c(xmin, ymin, ## South-west Corner
                           xmin, ymax, ## North-west Corner
                           xmax, ymax, ## North-East Corner
                           xmax, ymin, ## South-east Corner
                           xmin, ymin), ## South-west Corner Close
                         ncol=2, byrow=T)
  polytemp <- Polygon(cornerCoords)
  polytemp <- Polygons(list(polytemp), ID="A")
  Ps1 <- SpatialPolygons(list(polytemp), proj4string=CRS("+proj=longlat +ellps=WGS84 +datum=WGS84 +no_defs"))
  return(Ps1)
}

## Create function that takes city coordinates and generated transects
cityTransect <- function(citylon, citylat, nquadrat, buffer, distance){ ## nquadrat is the number of quadrats, resolution is the km in decimal degrees, and distance is the distance between points
  distbtwbuff <- buffer*2 + distance
  disKm <- 1/111*distbtwbuff
  transect <- data.frame(lon  = seq(citylon, by = 0, length.out=nquadrat), ## keep longitude unchanged
                         lat = seq(citylat, by = disKm, length.out=nquadrat)) ## move latitude up by distance
  quadratList <- lapply(1:nquadrat, FUN=function(i){
    buff <- makePoly(transect[i,"lon"], transect[i,"lat"], buffer) ## make polygons for each position along transect
    return(buff)
  })
  return(do.call(bind, quadratList) ) ## combine polygons
}


pdf('last.pdf')
#Task a

#read csv file in the working directory
gauss_R = read.csv(file="/home/stelios/training_set_1.csv", header=TRUE, sep=";")
gauss_G = read.csv(file="/home/stelios/training_set_2.csv", header=TRUE, sep=";")
gauss_B = read.csv(file="/home/stelios/training_set_3.csv", header=TRUE, sep=";")

typeof(gauss_B) #type of gauss_R,gauss_B,gauss_G is list

label_class = c("r","g","b");

mean_R = c(0,0)
mean_G = c(0,0)
mean_B = c(0,0)
label_gauss = c("-","-","-");

Nk = c(0,0,0);
#finding mean of each class

#first Mean

label_gauss[1] = levels(gauss_R[[3]][1])
length_of_gauss = lengths(gauss_R);
Nk[1] = length_of_gauss[[1]][1];

for (i in 1:Nk[1]){
 mean_R[1] =  mean_R[1] + gauss_R[[1]][i];
 mean_R[2]  =  mean_R[2] + gauss_R[[2]][i];
}

mean_R[1]= mean_R[1]/length_of_gauss[[1]][1];
mean_R[2]= mean_R[2]/length_of_gauss[[2]][1];

#second Mean

label_gauss[2] = levels(gauss_G[[3]][1])
length_of_gauss = lengths(gauss_G)
Nk[2] = length_of_gauss[[1]][1];

for (i in 1:Nk[2]){
 mean_G[1] =  mean_G[1] + gauss_G[[1]][i]
 mean_G[2]  =  mean_G[2] + gauss_G[[2]][i]
}

mean_G[1]= mean_G[1]/length_of_gauss[[1]][1]
mean_G[2]= mean_G[2]/length_of_gauss[[2]][1]

#third Mean

label_gauss[3] = levels(gauss_B[[3]][1])
length_of_gauss = lengths(gauss_B);
Nk[3] = length_of_gauss[[1]][1];

for (i in 1:Nk[3]){
 mean_B[1] =  mean_B[1] + gauss_B[[1]][i];
 mean_B[2]  =  mean_B[2] + gauss_B[[2]][i];
}

mean_B[1]= mean_B[1]/length_of_gauss[[1]][1];
mean_B[2]= mean_B[2]/length_of_gauss[[2]][1];

#all means in a list
means_of_gaussians = list(mean_R,mean_G,mean_B) #All means in a list

cov_r=0;
cov_g=0;
cov_b=0;

# calculate the covariance matrix of the r class

length_of_gauss = lengths(gauss_R);

temp_a = matrix(c(0,0),nrow=1,ncol=2);
for (i in 1:Nk[1]){
	temp_a =  matrix(c(gauss_R[[1]][i]-mean_R[1], gauss_R[[2]][i]-mean_R[2]),nrow=2,ncol=1);
	temp_trans = t(temp_a)
	cov_r = cov_r + temp_a%*%temp_trans
}
cov_r = cov_r/length_of_gauss[[1]][1];

# calculate the covariance matrix of the g class
length_of_gauss = lengths(gauss_G);

vector_a = c(0,0);
for (i in 1:Nk[2]){
	vector_a =  c(gauss_G[[1]][i]-mean_G[1], gauss_G[[2]][i]-mean_G[2])
	vector_trans = t(vector_a)
	cov_g = cov_g + vector_a%*%vector_trans
}
cov_g = cov_g/length_of_gauss[[1]][1];

# calculate the covariance matrix of the b class
length_of_gauss = lengths(gauss_B);

vector_a = c(0,0);
for (i in 1:Nk[3]){
	vector_a =  c(gauss_B[[1]][i]-mean_B[1], gauss_B[[2]][i]-mean_B[2])
	vector_trans = t(vector_a)
	cov_b = cov_b + vector_a%*%vector_trans
}
cov_b = cov_b/length_of_gauss[[1]][1];

N = Nk[1] + Nk[2] + Nk[3]
S = (Nk[1]/N)*cov_r + (Nk[2]/N)*cov_g + (Nk[3]/N)*cov_b

#----------------------------------------------------------------
#task b

#calculating the prior
Pc_r = Nk[1] / N
Pc_g = Nk[2] / N
Pc_b = Nk[3] / N

#calculating posterior and classification training set 1
vector_a = c(0,0);
max_o = 0;
temp=0;
classification_r= matrix(nrow=3000,ncol=1) #classification of the first dataset
post_R = matrix(nrow=3000,ncol=3) #posterior for R dataset

for (i in 1:Nk[1]){

	vector_a =  c(gauss_R[[1]][i]-mean_R[1], gauss_R[[2]][i]-mean_R[2])
	vector_trans = t(vector_a)
	vector_trans= t(vector_a)
	dens_r = ( 1/( (2*pi)^(3/2) ) ) * (1/((det(S))^(1/2)) ) * exp(-0.5*vector_trans%*%(solve(S))%*%vector_a)

	vector_a =  c(gauss_R[[1]][i]-mean_G[1], gauss_R[[2]][i]-mean_G[2])
	vector_trans = t(vector_a)
	dens_g = ( 1/( (2*pi)^(3/2) ) ) * (1/((det(S))^(1/2)) ) * exp(-0.5*vector_trans%*%(solve(S))%*%vector_a)

	vector_a =  c(gauss_R[[1]][i]-mean_B[1], gauss_R[[2]][i]-mean_B[2])
	vector_trans = t(vector_a)
	dens_b = ( 1/( (2*pi)^(3/2) ) ) * (1/((det(S))^(1/2)) ) * exp(-0.5*vector_trans%*%(solve(S))%*%vector_a)

	temp = (dens_r)*Pc_r/(dens_r*Pc_r+dens_g*Pc_g+dens_b*Pc_b);
	post_R[[i,1]] = temp;
	max_o = temp;
	pos=1;

	temp = (dens_g)*Pc_g/(dens_r*Pc_r+dens_g*Pc_g+dens_b*Pc_b);
	post_R[[i,2]] = temp;
	if(temp>max_o){
	 	pos=2
		max_o =temp
	}

	temp = (dens_b*Pc_b)/(dens_r*Pc_r+dens_g*Pc_g+dens_b*Pc_b);
	post_R[[i,3]] = temp;
	if(temp>max_o){
	 	pos=3
		max_o =temp
	}

	classification_r[[i,1]] = label_class[pos];

}

#calculating posterior and classification training set 2
vector_a = c(0,0);
max_o = 0;
temp=0;
classification_g= matrix(nrow=3000,ncol=1)
post_G = matrix(nrow=3000,ncol=3) #posterior for G dataset

for (i in 1:Nk[2]){

	vector_a =  c(gauss_G[[1]][i]-mean_R[1], gauss_G[[2]][i]-mean_R[2])
	vector_trans = t(vector_a)
	vector_trans= t(vector_a)
	dens_r = ( 1/( (2*pi)^(3/2) ) ) * (1/((det(S))^(1/2)) ) * exp(-0.5*vector_trans%*%(solve(S))%*%vector_a)

	vector_a =  c(gauss_G[[1]][i]-mean_G[1], gauss_G[[2]][i]-mean_G[2])
	vector_trans = t(vector_a)
	dens_g = ( 1/( (2*pi)^(3/2) ) ) * (1/((det(S))^(1/2)) ) * exp(-0.5*vector_trans%*%(solve(S))%*%vector_a)

	vector_a =  c(gauss_G[[1]][i]-mean_B[1], gauss_G[[2]][i]-mean_B[2])
	vector_trans = t(vector_a)
	dens_b = ( 1/( (2*pi)^(3/2) ) ) * (1/((det(S))^(1/2)) ) * exp(-0.5*vector_trans%*%(solve(S))%*%vector_a)

	temp = (dens_r*Pc_r)/(dens_r*Pc_r+dens_g*Pc_g+dens_b*Pc_b);
	post_G[[i,1]] = temp;
	max_o = temp;
	pos=1;

	temp = (dens_g*Pc_g)/(dens_r*Pc_r+dens_g*Pc_g+dens_b*Pc_b);
	post_G[[i,2]] = temp;
	if(temp>max_o){
	 	pos=2
		max_o =temp
	}

	temp = (dens_b*Pc_b)/(dens_r*Pc_r+dens_g*Pc_g+dens_b*Pc_b);
	post_G[[i,3]] = temp;
	if(temp>max_o){
	 	pos=3
		max_o =temp
	}

	classification_g[[i,1]] = label_class[pos];

}

#calculating posterior and classification training set 3
vector_a = c(0,0);
max_o = 0;
temp=0;
classification_b= matrix(nrow=3000,ncol=1)
post_B = matrix(nrow=3000,ncol=3) #posterior for B dataset

for (i in 1:Nk[2]){

	vector_a =  c(gauss_B[[1]][i]-mean_R[1], gauss_B[[2]][i]-mean_R[2])
	vector_trans = t(vector_a)
	vector_trans= t(vector_a)
	dens_r = ( 1/( (2*pi)^(3/2) ) ) * (1/((det(S))^(1/2)) ) * exp(-0.5*vector_trans%*%(solve(S))%*%vector_a)

	vector_a =  c(gauss_B[[1]][i]-mean_G[1], gauss_B[[2]][i]-mean_G[2])
	vector_trans = t(vector_a)
	dens_g = ( 1/( (2*pi)^(3/2) ) ) * (1/((det(S))^(1/2)) ) * exp(-0.5*vector_trans%*%(solve(S))%*%vector_a)

	vector_a =  c(gauss_B[[1]][i]-mean_B[1], gauss_B[[2]][i]-mean_B[2])
	vector_trans = t(vector_a)
	dens_b = ( 1/( (2*pi)^(3/2) ) ) * (1/((det(S))^(1/2)) ) * exp(-0.5*vector_trans%*%(solve(S))%*%vector_a)

	temp = (dens_r*Pc_r)/(dens_r*Pc_r+dens_g*Pc_g+dens_b*Pc_b);
	post_B[[i,1]] = temp;
	max_o = temp;
	pos=1;

	temp = (dens_g*Pc_g)/(dens_r*Pc_r+dens_g*Pc_g+dens_b*Pc_b);
	post_B[[i,2]] = temp;
	if(temp>max_o){
	 	pos=2
		max_o =temp
	}

	temp = (dens_b*Pc_b)/(dens_r*Pc_r+dens_g*Pc_g+dens_b*Pc_b);
	post_B[[i,3]] = temp;
	if(temp>max_o){
	 	pos=3
		max_o =temp
	}

	classification_b[[i,1]] = label_class[pos];

}

#all data together
#final_class shows class of each spot
#final is the matrix with the corresponding spots

final_class=c(0,0);
final = matrix(nrow=N,ncol=2)
for (i in 1:Nk[1]){

final[i,1]=gauss_R[[1]][i]
final[i,2]=gauss_R[[2]][i]
final_class[i]=classification_r[[i,1]]

final[i+Nk[1],1]=gauss_G[[1]][i]
final[i+Nk[1],2]=gauss_G[[2]][i]
final_class[i+Nk[1]]=classification_g[[i,1]]

final[i+Nk[1]+Nk[2],1]=gauss_B[[1]][i]
final[i+Nk[1]+Nk[2],2]=gauss_B[[2]][i]
final_class[i+Nk[1]+Nk[2]]=classification_b[[i,1]]

}

#data with their max posteriors
post = matrix(nrow=9000,ncol=1)
for (i in 1:Nk[1]){
post[i,1]=max(post_R[[i,1]],post_R[[i,2]],post_R[[i,3]])
post[i+Nk[1],1]=max(post_G[[i,1]],post_G[[i,2]],post_G[[i,3]])
post[i+Nk[1]+Nk[2],1]=max(post_B[[i,1]],post_B[[i,2]],post_B[[i,3]])
}

#classifying the whole data from beginning to the 3 classes
#1->x,2->y,3->probability
a=0;
a=sum(final_class == "r") #sum of elements in r class
class_r=matrix(nrow=a,ncol=3)

a=0;
a=sum(final_class == "g") #sum of elements in g class
class_g=matrix(nrow=a,ncol=3)

a=0;
a=sum(final_class == "b") #sum of elements in b class
class_b=matrix(nrow=a,ncol=3)

counter1=1;
counter2=1;
counter3=1;
for (i in 1:N){
 	if(final_class[i]=="r"){
		class_r[counter1,1] = final[i,1];
		class_r[counter1,2] = final[i,2];
		class_r[counter1,3] = post[i,1];
		counter1=counter1+1;
	}
	if(final_class[i]=="g"){
		class_g[counter2,1] = final[i,1];
		class_g[counter2,2] = final[i,2];
		class_g[counter2,3] = post[i,1];
		counter2=counter2+1;
	}
	if(final_class[i]=="b"){
		class_b[counter3,1] = final[i,1];
		class_b[counter3,2] = final[i,2];
		class_b[counter3,3] = post[i,1];
		counter3=counter3+1;
	}

}

#calculation to find the outliers
e = which(class_r[,3]<0.4);
tr1=matrix(nrow=length(e),ncol=2)
counter1=1;
#spots in the boundaries
for (i in e){
tr1[counter1,1]=class_r[i,1]
tr1[counter1,2]=class_r[i,2]
counter1= counter1+1
}

#calculation to find the outliers
e = which((class_g[,3])<0.4);
tr2=matrix(nrow=length(e),ncol=2)
counter1=1;
#spots in the boundaries
for (i in e){
tr2[counter1,1]=class_g[i,1]
tr2[counter1,2]=class_g[i,2]
counter1= counter1+1
}

#calculation to find the outliers
e = which(class_b[,3]<0.4);
tr3=matrix(nrow=length(e),ncol=2)
counter1=1;
#spots in the boundaries
for (i in e){
tr3[counter1,1]=class_b[i,1]
tr3[counter1,2]=class_b[i,2]
counter1= counter1+1
}

plot(class_b[,1],class_b[,2],col="blue",xlim=c(-1.6,1.6),ylim=c(-1.7,1.7),pch=1,panel.first = grid(),xlab="x",ylab="y")
points(class_g[,1],class_g[,2],col="green",pch=1)
points(class_r[,1],class_r[,2],col="red",pch=1)

t=( colMeans(tr1) + colMeans(tr2) + colMeans(tr3) )/3
t2=( colMeans(tr1) + colMeans(tr2) )/2
lines( c(-1.7,t[1]),c(t[2],t[2]) ,lwd=1)

t=( colMeans(tr1) + colMeans(tr2) + colMeans(tr3) )/3
t2=(colMeans(tr2) + colMeans(tr3) )/2;
f= (t[2]-t2[2])/(t[1]-t2[1]) ;
x=f*10;
y=f*x
lines( c(t[1],x),c(t[2],y+2) ,lwd=1)

t=( colMeans(tr1) + colMeans(tr2) + colMeans(tr3) )/3
t2=(colMeans(tr1) + colMeans(tr3) )/2;
f= (-t[2]+t2[2])/(t[1]-t2[1]) ;
x=f*10;
y=f*x
lines( c(t[1],x),c(t[2],-y-8.5) ,lwd=1)

#------------------------------------------------------

#install.packages("plot3D") #package for plotting 2d and 3d
library("plot3D")

par(mfrow=c(2,2))
x = class_r[,1]
y = class_r[,2]
z = class_r[,3]
points3D(x,y,z)

x = class_g[,1]
y = class_g[,2]
z = class_g[,3]
points3D(x,y,z)

x = class_b[,1]
y = class_b[,2]
z = class_b[,3]
points3D(x,y,z)

e=which(final_class=="r");
x1= matrix(nrow=length(e),ncol=1)
y1= matrix(nrow=length(e),ncol=1)
z1= matrix(nrow=length(e),ncol=1)
counter1=1;
for (i in e){
x1[counter1,1] = final[i,1]
y1[counter1,1]  = final[i,2]
z1[counter1,1]  = post[i,1];
counter1= counter1+1
}
points3D(x1,y1,z1, col="red",xlim=c(-2.5,2.5),ylim=c(-2.5,2.5),zlim=c(0,1))

e=which(final_class=="g");
x2= matrix(nrow=length(e),ncol=1)
y2= matrix(nrow=length(e),ncol=1)
z2= matrix(nrow=length(e),ncol=1)
counter1=1;
for (i in e){
x2[counter1,1] = final[i,1]
y2[counter1,1]  = final[i,2]
z2[counter1,1]  = post[i,1];
counter1= counter1+1
}
points3D(x2,y2,z2, col="green",add = TRUE)

e=which(final_class=="b");
x1= matrix(nrow=length(e),ncol=1)
y1= matrix(nrow=length(e),ncol=1)
z1= matrix(nrow=length(e),ncol=1)
counter1=1;
for (i in e){
x1[counter1,1] = final[i,1]
y1[counter1,1]  = final[i,2]
z1[counter1,1]  = post[i,1];
counter1= counter1+1
}
points3D(x1,y1,z1, col="blue",add = TRUE)

par(mfrow=c(1,1))
plot(class_b[,1],class_b[,2],col="blue",xlim=c(-1.6,1.6),ylim=c(-1.7,1.7),pch=20,panel.first = grid())
points(class_g[,1],class_g[,2],col="green",pch=20)
points(class_r[,1],class_r[,2],col="red",pch=20)

#==================================================================
#task3

#read csv file in the working directory
gauss_T = read.csv(file="/home/stelios/test_set.csv", header=TRUE, sep=";")

t=length(gauss_T[,1])

#calculating posterior and classification of test set
vector_a = c(0,0);
max_o = 0;
temp=0;
classification_t= matrix(nrow=t,ncol=1) #classification of the first dataset
post_T = matrix(nrow=t,ncol=1) #Max posterior for test dataset
post_T_all = matrix(nrow=t,ncol=3) #Max posterior for test dataset

for (i in 1:t){

	vector_a =  c(gauss_T[[1]][i]-mean_R[1], gauss_T[[2]][i]-mean_R[2])
	vector_trans = t(vector_a)
	vector_trans= t(vector_a)
	dens_r = ( 1/( (2*pi)^(3/2) ) ) * (1/((det(S))^(1/2)) ) * exp(-0.5*vector_trans%*%(solve(S))%*%vector_a)

	vector_a =  c(gauss_T[[1]][i]-mean_G[1], gauss_T[[2]][i]-mean_G[2])
	vector_trans = t(vector_a)
	dens_g = ( 1/( (2*pi)^(3/2) ) ) * (1/((det(S))^(1/2)) ) * exp(-0.5*vector_trans%*%(solve(S))%*%vector_a)

	vector_a =  c(gauss_T[[1]][i]-mean_B[1], gauss_T[[2]][i]-mean_B[2])
	vector_trans = t(vector_a)
	dens_b = ( 1/( (2*pi)^(3/2) ) ) * (1/((det(S))^(1/2)) ) * exp(-0.5*vector_trans%*%(solve(S))%*%vector_a)

	temp = (dens_r)*Pc_r/(dens_r*Pc_r+dens_g*Pc_g+dens_b*Pc_b);
	post_T_all[[i,1]] = temp;
	max_o = temp;
	pos=1;

	temp = (dens_g)*Pc_g/(dens_r*Pc_r+dens_g*Pc_g+dens_b*Pc_b);
	post_T_all[[i,2]] = temp;

	if(temp>max_o){
	 	pos=2
		max_o =temp
	}

	temp = (dens_b*Pc_b)/(dens_r*Pc_r+dens_g*Pc_g+dens_b*Pc_b);
	post_T_all[[i,3]] = temp;

	if(temp>max_o){
	 	pos=3
		max_o =temp
	}

	post_T[[i,1]] = max_o ;
	classification_t[[i,1]] = label_class[pos];

}

#---------------plotting classified training data ---------------------

par(mfrow=c(1,1))
plot(c(0,0),c(0,0),col="white",xlim=c(-2.5,2.5),ylim=c(-2.5,2.5),pch=20,panel.first = grid(),xlab="x",ylab="y")

for (i in 1:t){
	if(classification_t[[i,1]]=="r"){
		points(gauss_T[[1]][i],gauss_T[[2]][i],col="red",pch=1)
	}
	if(classification_t[[i,1]]=="g"){
		points(gauss_T[[1]][i],gauss_T[[2]][i],col="green",pch=1)
	}
	if(classification_t[[i,1]]=="b"){
		points(gauss_T[[1]][i],gauss_T[[2]][i],col="blue",pch=1)
	}
}

plot(c(0,0),c(0,0),col="white",xlim=c(-2.5,2.5),ylim=c(-2.5,2.5),pch=20,panel.first = grid(),xlab="x",ylab="y")

for (i in 1:t){
if(classification_t[[i,1]]=="r"){
points(gauss_T[[1]][i],gauss_T[[2]][i],col="coral",pch=20)
}
if(classification_t[[i,1]]=="g"){
points(gauss_T[[1]][i],gauss_T[[2]][i],col="chartreuse1",pch=20)
}
if(classification_t[[i,1]]=="b"){
points(gauss_T[[1]][i],gauss_T[[2]][i],col="cadetblue2",pch=20)
}
}

#calculation to find the outliers
e = which((post_T)<0.5);
tr1=matrix(nrow=length(e),ncol=2)
counter1=1;
#spots in the boundaries
for (i in e){
tr1[counter1,1]=gauss_T[[1]][i];
tr1[counter1,2]=gauss_T[[2]][i];
counter1= counter1+1
}

points(tr1,col="black",pch=5)

#======================plotting test data=================

e=which(classification_t[,1]=="r");
x1= matrix(nrow=length(e),ncol=1)
y1= matrix(nrow=length(e),ncol=1)
z1= matrix(nrow=length(e),ncol=1)
counter1=1;
for (i in e){
x1[counter1,1] = gauss_T[i,1]
y1[counter1,1]  = gauss_T[i,2]
z1[counter1,1]  = post_T[i,1];
counter1= counter1+1
}
points3D(x1,y1,z1, col="red",xlim=c(-2.5,2.5),ylim=c(-2.5,2.5),zlim=c(0,1))

e=which(classification_t[,1]=="g");
x2= matrix(nrow=length(e),ncol=1)
y2= matrix(nrow=length(e),ncol=1)
z2= matrix(nrow=length(e),ncol=1)
counter1=1;
for (i in e){
x2[counter1,1] = gauss_T[i,1]
y2[counter1,1]  = gauss_T[i,2]
z2[counter1,1]  = post_T[i,1];
counter1= counter1+1
}
points3D(x2,y2,z2, col="green",add = TRUE)

e=which(classification_t[,1]=="b");
x1= matrix(nrow=length(e),ncol=1)
y1= matrix(nrow=length(e),ncol=1)
z1= matrix(nrow=length(e),ncol=1)
counter1=1;
for (i in e){
x1[counter1,1] = gauss_T[i,1]
y1[counter1,1]  = gauss_T[i,2]
z1[counter1,1]  = post_T[i,1];
counter1= counter1+1
}
points3D(x1,y1,z1, col="blue",add = TRUE)

#==========================================================

#calculation to find the outliers
e = which((post_T)<0.5);
tr1=matrix(nrow=length(e),ncol=3)
counter1=1;
#spots in the boundaries
for (i in e){
tr1[counter1,1]=gauss_T[[1]][i];
tr1[counter1,2]=gauss_T[[2]][i];
tr1[counter1,3]=post_T[i,1];
counter1= counter1+1
}

points3D(tr1[,1],tr1[,2],tr1[,3],pch=5,xlim=c(-1,1),ylim=c(-1,1),zlim=c(0,1))

dev.off()

#outlier The resulting decision boundaries, corresponding to the minimum misclassification rate, will occur when two of the posterior probabilities (the two largest) are equal, and so will be defined by linear functions of x, and so again we have a generalized linear model.


#' Unesco 1983. Algorithms for computation of fundamental properties of seawater, 1983

pressure2Depth <- function (pressure, lat = 45) {
    lat <- lat * 0.0174532925199433
    x <- sin(lat)^2
    gr <- 9.780318 * (1 + (0.0052788 + 2.36e-05 * x) * x) + 1.092e-06 * pressure
    d <- (((-1.82e-15 * pressure + 2.279e-10) * pressure - 2.2512e-05) *
        pressure + 9.72659) * pressure/gr

    return(d)
}


factIter <- function(n) {
  f = 1
  if (n > 1) {
    for (i in 2:n) f <- f * i
  }
  f
}


# This is the Script for 06_Lab Exercise 1
# By Ryan McWay
# Setting the directory
setwd("~/LiDAR-McWay/06_Class/LidarDataAnalysis")
# Importing the database
lidarData <- read.csv("~/LiDAR-McWay/06_Class/LidarDataAnalysis/BA_lidarData.csv")
# View the database
View(lidarData)
# Name the variables from the database
names(lidarData)
# Create the response variable as total Basel Area
ResponseV <- lidarData$BAtotal
# Mean for the response variable
mean(ResponseV)
# Plot Hieght Mean against the response variable
plot(lidarData$HMean, ResponseV, xlab = "Mean Height", ylab = "Response Variable")
# Learn more about plot
?plot
# correlation between mean hieght to basal area
cor(lidarData$HMean, lidarData$BAtotal)
# plot 10th percentile to the response variable
plot(lidarData$P10, ResponseV)
# correlation between 10th percentile to basal area
cor(lidarData$P10, lidarData$BAtotal)
# plot 95th percentile to response variable
plot(lidarData$P95, ResponseV)
# correlation between 95th percentile to basal area
cor(lidarData$P95, lidarData$BAtotal)
# plot 1st cover mean to response variable
plot(lidarData$all_1st_cover_mean, ResponseV)
# correlation between 1st cover mean to basal area
cor(lidarData$all_1st_cover_mean, lidarData$BAtotal)
# plot 1st cover 3 to response variable
plot(lidarData$all_1st_cover_3, ResponseV)
# correlation between 1st cover 3 to basal area
cor(lidarData$all_1st_cover_3, lidarData$BAtotal)
# creates correlation matrix for database expect the first column "Plots", because "That's Silly."
corrMat <- cor(lidarData[,-1])
# Print out the correlation matrix so it can be read
print(corrMat)
# range for Hieght max
range(lidarData$HMax)
# 5 statistic summary for every variable in the database
summary(lidarData)
# create histogram of basal area (frequency graph)
hist(lidarData$BAtotal)
# Learn more about histograms
?hist
# variance for basal area
var(lidarData$BAtotal)
# standard deviation for basal area
sd(lidarData$BAtotal)


# 4. faza: Analiza podatkov

#napoved št. priseljencev v Slovenijo

skupno_leta <- tabela1 %>% group_by(Leto) %>% summarise(Stevilo_priseljenih_iz_tujine=sum(Priseljeni_iz_tujine))
skupno_leta$Vrsta <- "Meritev"
model <- lm(Stevilo_priseljenih_iz_tujine~Leto, data=skupno_leta)
pr <- predict(model, data.frame(Leto=seq.int(2021, 2025, 1)))
napoved_preseljevanja <- data.frame(Leto = c(2021, 2022, 2023, 2024, 2025), Stevilo_priseljenih_iz_tujine = c(pr[1], pr[2], pr[3], pr[4], pr[5]))
napoved_preseljevanja$Vrsta <- "Napoved"
skupna_tabela <- rbind(skupno_leta, napoved_preseljevanja)

graf_napoved <- ggplot(skupna_tabela, aes(x=Leto, y=Stevilo_priseljenih_iz_tujine, shape=Vrsta)) +
  geom_smooth(formula = y ~ x, method=lm, se=TRUE, fullrange = TRUE, color="#ff6d44") +
  geom_point(data=skupno_leta, aes(x=Leto, y=Stevilo_priseljenih_iz_tujine), color="blue", size=1) +
  labs(title="Napoved števila priseljenih ljudi v Slovenijo", y="Število priseljenih", x="Leto") + geom_point(color="blue")

#napoved št. izseljencev iz Slovenije

skupno_leta_iz <- tabela2 %>% group_by(Leto) %>% summarise(Stevilo_odseljenih_v_tujino=sum(Odseljeni_v_tujino))
skupno_leta_iz$Vrsta <- "Meritev"
model2 <- lm(Stevilo_odseljenih_v_tujino~Leto, data=skupno_leta_iz)
pr2 <- predict(model2, data.frame(Leto=seq.int(2020, 2025, 1)))
napoved_izseljevanja <- data.frame(Leto = c(2020, 2021, 2022, 2023, 2024, 2025), Stevilo_odseljenih_v_tujino = c(pr2[1], pr2[2], pr2[3], pr2[4], pr2[5], pr2[6]))
napoved_izseljevanja$Vrsta <- "Napoved"
skupna_tabela2 <- rbind(skupno_leta_iz, napoved_izseljevanja)

graf_napoved2 <- ggplot(skupna_tabela2, aes(x=Leto, y=Stevilo_odseljenih_v_tujino, shape=Vrsta)) +
  geom_smooth(formula = y ~ x, method=lm, se=TRUE, fullrange = TRUE, color="#01c659") +
  geom_point(data=skupno_leta_iz, aes(x=Leto, y=Stevilo_odseljenih_v_tujino), color="orange", size=1) +
  labs(title="Napoved števila odseljenih iz Slovenije", y="Število odseljenih", x="Leto") + geom_point(color="yellow")

#Razvrščanje v skupine na zemljevidu - Evropa
#združevanje tabel
tabela11skupno <- tabela11 %>% filter(between(Leto,2016,2019)) %>% group_by(Leto,Drzava) %>% summarise("Stevilo_priseljenih"=sum(Stevilo))

tabela12skupno <- tabela12 %>% filter(between(Leto,2016,2019)) %>% group_by(Leto,Drzava) %>% summarise("Stevilo_izseljenih"=sum(Stevilo))
t1 <- inner_join(tabela11skupno, tabela12skupno)
t2 <- inner_join(t1, tabela15)
evropa_master <- inner_join(t2, tabela10)

povprecjeevropa <- evropa_master %>% group_by(Drzava) %>%
  summarise(emigracija = mean(Stevilo_izseljenih, na.rm = TRUE),
            imigracija = mean(Stevilo_priseljenih, na.rm = TRUE),
            populacija = mean(Prebivalstvo, na.rm = TRUE),
            GDP = mean(GDP_per_capita_dolarji, na.rm = TRUE)) %>%
  mutate(emigracija = emigracija / populacija, imigracija = imigracija / populacija)

#clustering emigracija (izseljeni)
skupineEmigracija <- kmeans(povprecjeevropa$emigracija, 4, nstart = 1500)
centersEmi <- sort(skupineEmigracija$centers)
skupineEmigracija <- kmeans(povprecjeevropa$emigracija, centers = centersEmi, nstart = 1500)

#zemljevid glede na skupine - izseljevanje
zemljevidskupineemi <- tm_shape(merge(zemljevid, data.frame(Drzava = povprecjeevropa$Drzava,
                                                skupina = factor(skupineEmigracija$cluster)),
                               by.x = "SOVEREIGNT", by.y = "Drzava"), xlim=c(-20,32), ylim=c(32,80)) +
  tmap_options(max.categories = 4) +
  tm_polygons("skupina", title = "Skupina") +
  tm_layout(main.title = "Države razdeljene glede na \n povprečno emigracijo", main.title.size = 1, legend.title.size = 1) +
  tm_legend(position = c("left", "bottom"))

#clustering imigracija (priseljeni)
skupineImigracija <- kmeans(povprecjeevropa$imigracija, 4, nstart = 1500)
centersImi <- sort(skupineImigracija$centers)
skupineImigracija <- kmeans(povprecjeevropa$imigracija, centers = centersImi, nstart = 1500)

#clustering imigracija (priseljeni)
zemljevidskupineimi <- tm_shape(merge(zemljevid, data.frame(Drzava = povprecjeevropa$Drzava,
                                                            skupina = factor(skupineImigracija$cluster)),
                                      by.x = "SOVEREIGNT", by.y = "Drzava"), xlim=c(-20,32), ylim=c(32,80)) +
  tmap_options(max.categories = 4) +
  tm_polygons("skupina", title = "Skupina") +
  tm_layout(main.title = "Države razdeljene glede na \n povprečno imigracijo", main.title.size = 1, legend.title.size = 1) +
  tm_legend(position = c("left", "bottom"))

#Še eno razvrščanje: REGIJE_____________________
#združimo tabele:
tab14 <- tabela14 %>% group_by(Leto,Regija) %>% summarise("Stevilo_priseljenih"=sum(Stevilo_priseljenih_iz_tujine))
tab13 <- tabela13 %>% group_by(Leto,Regija) %>% summarise("Stevilo_odseljenih"=sum(Stevilo_odseljenih_v_tujino))
ta1 <- inner_join(tab13,tab14)
ta2 <- inner_join(tabela16,tabela17)
ta3 <- inner_join(ta2,vsezares)
master_regije <- inner_join(ta3,ta1)

povprecjeregije <- master_regije %>% group_by(Regija) %>%
  summarise(emigracija = mean(Stevilo_odseljenih, na.rm = TRUE),
            imigracija = mean(Stevilo_priseljenih, na.rm = TRUE),
            populacija = mean(Prebivalstvo, na.rm = TRUE),
            BDP = mean(BDP_na_prebivalca, na.rm = TRUE),
            SS = mean(razSS, na.rm = TRUE),
            VS = mean(razVS, na.rm = TRUE)) %>%
  mutate(emigracija = emigracija / populacija, imigracija = imigracija / populacija)

#clustering emigracija -regije
skupineEmigracijaReg <- kmeans(povprecjeregije$emigracija, 4, nstart = 1500)
centersEmiReg <- sort(skupineEmigracijaReg$centers)
skupineEmigracijaReg <- kmeans(povprecjeregije$emigracija, centers = centersEmiReg, nstart = 1500)

zemljevid_reg_skup_emi <- tm_shape(merge(Slovenija, data.frame(Regija = povprecjeregije$Regija,
                                                            skupina = factor(skupineEmigracijaReg$cluster)),
                                      by.x = "NAME_1", by.y = "Regija")) +
  tmap_options(max.categories = 4) +
  tm_polygons("skupina", title = "Skupina") +
  tm_layout(main.title="Skupine glede na povprečno število izseljenih ljudi v regijah", legend.position = c(0.75,0.1)) +
  tm_text(text='NAME_1', size=0.6)

#clustering imigracija - regije
skupineImigracijaReg <- kmeans(povprecjeregije$imigracija, 4, nstart = 1500)
centersImiReg <- sort(skupineImigracijaReg$centers)
skupineImigracijaReg <- kmeans(povprecjeregije$imigracija, centers = centersImiReg, nstart = 1500)

zemljevid_reg_skup_imi <- tm_shape(merge(Slovenija, data.frame(Regija = povprecjeregije$Regija,
                                                               skupina = factor(skupineImigracijaReg$cluster)),
                                         by.x = "NAME_1", by.y = "Regija")) +
  tmap_options(max.categories = 4) +
  tm_polygons("skupina", title = "Skupina") +
  tm_layout(main.title="Skupine glede na povprečno število priseljenih ljudi v regijah", legend.position = c(0.75,0.1)) +
  tm_text(text='NAME_1', size=0.6)