# ver 2.0 -- marc, 11, 2020
# naiv, egyerszeru modell a magyar COVID-19 fertozott betegek szamanak rovidtavu alakulasara

# a modell azért naiv, mert NEM veszi figyelembe
#   kezdo gocpontok es azok tavolsaganak hatasak
#   a mar valamennyire tisztazott parametereket: onset delay, incubation period (5-8nap), reprodukcios szam (2,5), overdispersion stb.
#   orszagok egymasra hatasat, infrastrukturalis valtozokat es a beavatkozasok hatasat
#   SIR modell korlatait. stb.
#   valamint determinisztikus.

#memoria uritese
rm(list = ls())

# parameterek
min_fert <- 19 #honnantol veszi figyelembe az adatsort
min_hossz <- 4 #milyen hosszo adatsort tekint elemezhetonek min
shossz <- 21  # hany napra vetitse elore az eredmenyeket a modell

#BETOLTES
#adatok betoltese Johns Hopkins egyetem folyamatos frissitesu online adataibol
rawcovid <- read.csv("https://raw.githubusercontent.com/CSSEGISandData/COVID-19/master/csse_covid_19_data/csse_covid_19_time_series/time_series_19-covid-Confirmed.csv", header=TRUE, as.is=T, sep=",", fileEncoding = "UTF-8")

#OECD orszagok alapadatai
oecdraw <- read.csv("https://raw.githubusercontent.com/bricoler/naiv_COVID19_HU/R_code/OECD_2019.csv", header=TRUE, as.is=T, sep=",", fileEncoding = "UTF-8")

#AGGREGALAS
#aggregalas orszagokra
#E: itt eldobjuk a GEO-t, de a jovoben lehetne kezdeni valamit a tavolsagi adatokkal, ketdimenzioban (ido, ter)
l2 <- dim(rawcovid)[2] # napok hossza

rcovid <- aggregate(rawcovid[5:l2], by = list(rawcovid$Country.Region), FUN = sum) # orszagok szerinti aggregalas rcovid
rownames(rcovid) <- rcovid[,1] #sornevek javitasa rcovidban
colnames(rcovid)[1] <- "cname"

rownames(oecdraw) <- oecdraw[,1] #sornevek oecdrawban is

# OECD orszagok vizsgalata csak

rrcovid <- rcovid[(rcovid$cname %in% oecdraw$cname),] ##rrcovid: csak OECD orszagok
rrcovid <- rrcovid[,-1] # elso oszlop a cname torlese rrcovidban

#dif matrix eloallitasa (delta elozo nap)
d_covid <- rrcovid[1:dim(rrcovid)[1],2:dim(rrcovid)[2]] - rrcovid[1:dim(rrcovid)[1],(1:(dim(rrcovid)[2]-1))]

#az OECD lakossagszam leosztas
#covid : lakossagszamaranyos fertozottek aranya OECD orszagokban

covid <- rrcovid

for (i in 1:dim(rrcovid)[1])
{
covid[i,] <- rrcovid[i,] / oecdraw[rownames(rrcovid)[i],"pop"]
}

#SZAMITAS
# kulonbozo orszagok metaadatok szamitas, exp es log fuggvenyillesztes
# hianyzik: orszaglakossag es lakossagaranyos emelkedesszamitas

covid[covid==0]<-NA #0-kbol NA

cname <- NULL
chossz <-NULL
cmin <- NULL
cmax <- NULL
cmean <- NULL
cmedian <-NULL
csd <- NULL

tempv <-NULL
tempv2 <- NULL

c_expC <- NULL
c_expbeta <- NULL
c_expR2 <- NULL

c_logC <- NULL
c_logbeta <- NULL
c_logR2 <- NULL

c_tend <- NULL

for (i in 1:dim(covid)[1])
{
cname[i] <- rownames(covid)[i] # orszag neve
cmin[i] <- min(as.numeric(covid[i,]), na.rm=T) * oecdraw[rownames(rrcovid)[i],"pop"] # minimum fertozottszam emberben
cmax[i] <- max(as.numeric(covid[i,]), na.rm=T) * oecdraw[rownames(rrcovid)[i],"pop"] # maximum fertozottszam
cmean[i] <- mean(as.numeric(covid[i,]), na.rm=T) * oecdraw[rownames(rrcovid)[i],"pop"] # atlagos fertozottszam
cmedian[i] <- median(as.numeric(covid[i,]), na.rm=T) * oecdraw[rownames(rrcovid)[i],"pop"] #median fertozottszam
csd[i] <- sd(as.numeric(covid[i,]), na.rm=T) * oecdraw[rownames(rrcovid)[i],"pop"] #sd fertozottszam emberben

tempv <- NULL
tempv2 <- NULL

tempv <- na.omit(as.numeric(covid[i,])) # atmeneti vektor a nullak kidobalasa

tempv2 <- tempv[which( tempv * oecdraw[rownames(rrcovid)[i],"pop"] > min_fert) ] # 17-nel kisebbek kidobalasa - (tempv * vissza kell szorozni lakossagszamra)
chossz[i] <- length(tempv2) # hany nap van tobb mint min_fert fertozott

#ha minhossznak kevesebb a megfigyeles akkor nem szamolunk
#utana exp illesztes
#utana log illesztes

if (length(tempv2)>min_hossz) {
#exp
c_expC[i] <- coef(summary(lm(log(tempv2)~c(1:(length(tempv2))))))[1,1]
c_expbeta[i] <- coef(summary(lm(log(tempv2)~c(1:(length(tempv2))))))[2,1]
c_expR2[i] <- summary(lm(log(tempv2)~c(1:(length(tempv2)))))$adj.r.squared
#log
c_logC[i] <- coef(summary(lm((tempv2)~log(c(1:(length(tempv2)))))))[1,1]
c_logbeta[i] <- coef(summary(lm((tempv2)~log(c(1:(length(tempv2)))))))[2,1]
c_logR2[i] <- summary(lm((tempv2)~log(c(1:(length(tempv2))))))$adj.r.squared

if (c_logR2[i]<c_expR2[i]) {c_tend[i] <- "exp"} else {c_tend[i] <- "log"}

}
else
{
c_expC[i] <- 0
c_expbeta[i] <- 0
c_expR2[i] <- 0
c_logC[i] <- 0
c_logbeta[i] <- 0
c_logR2[i] <- 0
c_tend[i]<- "ned2c" # not enough data to calculate
}

}

cmeta <- data.frame(cname,chossz,cmin,cmax,cmean,cmedian,csd, c_expC, c_expbeta, c_expR2, c_logC, c_logbeta, c_logR2, c_tend)

#cmeta, orszagokrol fertozesmetaadatok

View(cmeta)

#Ahol meg keves adat volt fuggvenyszamitashoz, annak kidobasa

cmeta_exp <- cmeta[as.numeric(which(cmeta$c_tend!="ned2c")),]

#oecd orszagadatok hozzafuzzese a szamitott adatokhoz

cmeta_exp_oecd <- merge(cmeta_exp, oecdraw) # orszagok metadatai exp es log fuggveny es OECD adatok

#tesztek fuggvenyparameterek eloszlasara

shapiro.test(cmeta_exp_oecd$c_expC)[2]
shapiro.test(cmeta_exp_oecd$c_expbeta)[2]
shapiro.test(cmeta_exp_oecd$c_expR2)[2]

shapiro.test(cmeta_exp_oecd$c_logC)[2]
shapiro.test(cmeta_exp_oecd$c_logbeta)[2]
shapiro.test(cmeta_exp_oecd$c_logR2)[2]

#exp_beta
HU_beta <- mean(cmeta_exp_oecd$c_expbeta)
error_beta <- qnorm(0.95)*sd(cmeta_exp_oecd$c_expbeta)/sqrt(dim(cmeta_exp_oecd)[2]-1) # 90% CI

HU_betamax <- HU_beta + error_beta
HU_betamin <- HU_beta - error_beta

#exp_C
HU_C <- mean(cmeta_exp_oecd$c_expC)
error_c <- qnorm(0.95)*sd(cmeta_exp_oecd$c_expC)/sqrt(dim(cmeta_exp_oecd)[2]-1) # 90% CI

HU_Cmax <- HU_C + error_c
HU_Cmin <- HU_C - error_c

#expR2
HU_R2 <- mean(cmeta_exp_oecd$c_expR2)
error_R2 <- qnorm(0.975)*sd(cmeta_exp_oecd$c_expR2)/sqrt(dim(cmeta_exp_oecd)[2]-1)

HU_R2max <- HU_R2 + error_R2
HU_R2min <- HU_R2 - error_R2

# hazai adatok szimulalasa shossz n
pnap <- c(1:shossz)

HU_p <- NULL
HU_pmin <- NULL
HU_pmax <- NULL

# e kitevojeben a josolt beta kiszamitasa

for (i in 1:shossz) {
HU_p[i] <- exp(1)**(HU_C+(HU_beta*i))
HU_pmin[i] <- exp(1)**(HU_Cmin+(HU_betamin*i))
HU_pmax[i] <- exp(1)**(HU_Cmax+(HU_betamax*i))
}

#R2 bizonytalansag fapados hozzaszamitasa
HU_pmin <- (HU_pmin) * HU_R2
HU_pmax <- (HU_pmax) * (1+ (1-HU_R2))

#Kerekites egesz emberre, hazai populacioval felszorzas
HU_pi <- ceiling((HU_p) * oecdraw["Hungary","pop"])
HU_pmini <- ceiling((HU_pmin) * oecdraw["Hungary","pop"])
HU_pmaxi <- ceiling((HU_pmax) * oecdraw["Hungary","pop"])

# eredmenyek dataframe

results <- data.frame(pnap, HU_p, HU_pmin, HU_pmax, HU_pi, HU_pmini, HU_pmaxi)

#vizualizacio
par(mfrow=c(1,1))

#kirajzolas2 : szimulalalt adatok
#hany napot rajzoljon ki : kir

plot(pnap, HU_pmax[1:shossz], xlab = "Napok, X. beteg megjelenése után", ylab = "Igazolt fertőzött betegek aránya, becslés (CI 0.82 - 0.86)", col=8, lty = 2, pch = 16, cex=0)
points (HU_pmin[1:shossz], col =8, pch = 16, cex=0)
polygon(c(pnap[1:shossz], rev(pnap[1:shossz])), c(HU_pmax[1:shossz] ,rev(HU_pmin[1:shossz])), col = rgb(1, 0, 0,0.5) )
points(pnap, HU_p[1:shossz], col=1, lty = 2, pch = 16, cex=1, type="b")

#eredmenyek kiiratasa
View(results)
#technikai reszletek kiirasa
#write.csv(cmeta_exp_oecd, file = "D:/R/Data/covid19_hu_tec_res1.csv", fileEncoding = "UTF-8")
#joslas kiirasa
#write.csv(results, file = "D:/R/Data/covid19_hu_res1.csv",fileEncoding = "UTF-8")

#SIR kod egy resze nem az en szellemi termekem, ezert nem oszthatom meg nyilvanosan, de ha vki ir emailt, kuldom


### Advanced Factor Functions V1.05  2013/03/20
### 2013 James H. Steiger
packageLoaded <- function(name) 0 != length(grep(paste("^package:",
                                                       name, "$", sep=""), search()))

if(!packageLoaded("plotrix")) library(plotrix)
if(!packageLoaded("sem")) library(sem)

# This code block includes R code for
#
# Gradient Projection Algorithms and Software for Arbitrary
# Rotation Criteria in Factor Analysis.
#
# by:
#
# Coen A. Bernaards and Robert I. Jennrich.
#
# Website: http://www.stat.ucla.edu/research
#

# GPForth is the main GP algorithm for orthogonal rotation.
# GPFoblq is the main GP algorithm for oblique rotation.
# For both algorithms is required: a loadings matrix A. Optional
# a initial rotation matrix Tmat. By default this is the identity matrix.
# Optional: the rotation method to be used. Between quation marks have to
# be the last part of the name of the vgQ function, e.g. for vgQ.varimax
# the argument is "varimax". Identical arguments can be used for oblique
# rotation. Some rotation criteria (including simplimax, pst, procrustes,
# cf,...) require one or more additional arguments. For example, simplimax
# needs the number of 'close to zero loadings'. This is given included as
# the extra argument k=27. Check out the rotation methods for details.
# When a new rotation method is implemented, and it needs an additional
# argument then this is the easiest way to pass it to the function.
#
# New rotation methods need to be programmed as vgQ.newmethod. The only
# inputs are the matrix L, and potential additional arguments. The
# output consists of the value f  of the criterion, its gradient Gq at L,
# and the name of the method.

GPForth <- function(A,Tmat=diag(ncol(A)),method="varimax",...){
 al <- 1
  L  <- A %*% Tmat
  VgQ <- get(paste("vgQ",method,sep="."))(L,...)
  G <- crossprod(A,VgQ$Gq)
  f <- VgQ$f
  Table <- NULL
  for (iter in 0:500){
    M <- crossprod(Tmat,G)
    S <- (M+t(M))/2
    Gp <- G - Tmat %*% S
    s <- sqrt(sum(diag(crossprod(Gp))))
    Table <- rbind(Table,c(iter,f,log10(s),al))
    if (s < 1e-5)
      break
    al <- 2*al
    for (i in 0:10){
      X <- Tmat - al * Gp
      UDV <- svd(X)
      Tmatt <- UDV$u %*% t(UDV$v)
      L <- A %*% Tmatt
      VgQt <- get(paste("vgQ",method,sep="."))(L,...)
      if (VgQt$f < (f-.5*s^2*al))
        break
      al <- al/2
    }
    Tmat <- Tmatt
    f <- VgQt$f
    G <- crossprod(A,VgQt$Gq)
  }
  Th <- Tmat
  Lh <- L
  method <- VgQ$Method
  orthogonal <- T
  ## Added by Steiger
  colnames(Table) <- c("Iteration", "Function", "Log10(s)","Alpha")
  ##
  return(list(Lh=Lh,Th=Th,Table=Table,method=method,orthogonal=orthogonal))
}

GPFoblq <- function(A,Tmat=diag(ncol(A)),method="quartimin",...){
  al <- 1
  L <- A %*% t(solve(Tmat))
  VgQ <- get(paste("vgQ",method,sep="."))(L,...)
  G <- -t(t(L) %*% VgQ$Gq %*% solve(Tmat))
  f <- VgQ$f
  Table <- NULL
  for (iter in 0:500){
    Gp <- G-Tmat %*% diag(apply(Tmat*G,2,sum))
    s <- sqrt(sum(diag(crossprod(Gp))))
    Table <- rbind(Table,c(iter,f,log10(s),al))
    if (s < 1e-5)
      break
    al <- 2*al
    for (i in 0:10){
      X <- Tmat-al*Gp
      v <- 1/sqrt(apply(X^2,2,sum))
      Tmatt <- X %*% diag(v)
      L <- A %*% t(solve(Tmatt))
      VgQt <- get(paste("vgQ",method,sep="."))(L,...)
      if (VgQt$f < (f-.5*s^2*al))
        break
      al <- al/2
    }
    Tmat <- Tmatt
    f <- VgQt$f
    G <- -t(t(L) %*% VgQt$Gq %*% solve(Tmatt))

  }
  Th <- Tmat
  Lh <- L
  Phi <- t(Tmat) %*% Tmat
  method <- VgQ$Method
  orthogonal <- F
  return(list(Lh=Lh,Phi=Phi,Th=Th,Table=Table,method=method,orthogonal=orthogonal))
}

vgQ.quartimin <- function(L){
  Method="Quartimin"
  L2 <- L^2
  k <- ncol(L)
  M <- matrix(1,k,k)-diag(k)
  f <- sum(L2 * (L2 %*% M))/4
  Gq <- L * (L2 %*% M)
  return(list(Gq=Gq,f=f,Method=Method))
}

vgQ.oblimin <- function(L,gam=0){
  Method <- paste("Oblimin g=",gam,sep="")
  if (gam == 0) Method <- "Oblimin Quartimin"
  if (gam == .5) Method <- "Oblimin Biquartimin"
  if (gam == 1) Method <- "Oblimin Covarimin"
  k <- ncol(L)
  p <- nrow(L)
  N <- matrix(1,k,k)-diag(k)
  f <- sum(L^2 * (diag(p)-gam*matrix(1/p,p,p)) %*% L^2 %*% N)/4
  Gq <- L * ((diag(p)-gam*matrix(1/p,p,p)) %*% L^2 %*% N)
  return(list(Gq=Gq,f=f,Method=Method))
}

vgQ.target <- function(L,Target){
  Method <- "Target rotation"
  # Needs Target matrix, e.g.  Target <- matrix(c(rep(9,4),rep(0,8),rep(9,4)),8)
  f <- sum((L-Target)^2)
  Gq <- 2*(L-Target)
  return(list(Gq=Gq,f=f,Method=Method))
}

vgQ.pst <- function(L,W,Target){
  Method <- "Partially specified target"
  # Needs weight matrix W with 1's at specified values, 0 otherwise
  # e.g. W = matrix(c(rep(1,4),rep(0,8),rep(1,4)),8).
  # When W has only 1's this is procrustes rotation
  # Needs a Target matrix Target with hypothesized factor loadings.
  # e.g. Target = matrix(0,8,2)
  Btilde <- W * Target
  f <- sum((W*L-Btilde)^2)
  Gq <- 2*(W*L-Btilde)
  return(list(Gq=Gq,f=f,Method=Method))
}

vgQ.oblimax <- function(L){
  Method <- "Oblimax"
  f <- -(log(sum(L^4))-2*log(sum(L^2)))
  Gq <- -(4*L^3/(sum(L^4))-4*L/(sum(L^2)))
  return(list(Gq=Gq,f=f,Method=Method))
}

vgQ.entropy <- function(L){
  Method <- "Minimum entropy"
  f <- -sum(L^2 * log(L^2 + (L^2==0)))/2
  Gq <- -(L * log(L^2 + (L^2==0)) + L)
  return(list(Gq=Gq,f=f,Method=Method))
}

vgQ.quartimax <- function(L){
  Method <- "Quartimax"
  f <- -sum(diag(crossprod(L^2)))/4
  Gq <- -L^3
  return(list(Gq=Gq,f=f,Method=Method))
}

vgQ.varimax <- function(L){
  Method <- "Varimax"
  QL <- sweep(L^2,2,apply(L^2,2,mean),"-")
  f <- -sqrt(sum(diag(crossprod(QL))))^2/4
  Gq <- -L * QL
  return(list(Gq=Gq,f=f,Method=Method))
}

vgQ.simplimax <- function(L,k=nrow(L)){
  Method <- "Simplimax"
  # m: Number of close to zero loadings
  Imat <- sign(L^2 <= sort(L^2)[k])
  f <- sum(Imat*L^2)
  Gq <- 2*Imat*L
  return(list(Gq=Gq,f=f,Method=Method))
}

vgQ.bentler <- function(L){
  Method <- "Bentler's criterion"
  L2 <- L^2
  M <- crossprod(L2)
  D <- diag(diag(M))
  f <- -(log(det(M))-log(det(D)))/4
  Gq <- -L * (L2 %*% (solve(M)-solve(D)))
  return(list(Gq=Gq,f=f,Method=Method))
}

vgQ.tandemI <- function(L){  # Tandem Criterion, Comrey, 1967.
  Method <- "Tandem I"
  LL <- (L %*% t(L))
  LL2 <- LL^2
  f <- -sum(diag(crossprod(L^2, LL2 %*% L^2)))
  Gq1 <- 4 * L *(LL2 %*% L^2)
  Gq2 <- 4 * (LL * (L^2 %*% t(L^2))) %*% L
  Gq <- -Gq1 - Gq2
  return(list(Gq=Gq,f=f,Method=Method))
}

vgQ.tandemII <- function(L){  # Tandem Criterion, Comrey, 1967.
  Method <- "Tandem II"
  LL <- (L %*% t(L))
  LL2 <- LL^2
  f <- sum(diag(crossprod(L^2, (1-LL2) %*% L^2)))
  Gq1 <- 4 * L *((1-LL2) %*% L^2)
  Gq2 <- 4 * (LL * (L^2 %*% t(L^2))) %*% L
  Gq <- Gq1 - Gq2
  return(list(Gq=Gq,f=f,Method=Method))
}

vgQ.geomin <- function(L,eps=.01){
  Method <- "Geomin"
  k <- ncol(L)
  p <- nrow(L)
  L2 <- L^2+eps
  pro <- exp(apply(log(L2),1,sum)/k)
  f <- sum(pro)
  Gq <- (2/k)*(L/L2)*matrix(rep(pro,k),p)
  return(list(Gq=Gq,f=f,Method=Method))
}

vgQ.cf <- function(L,kappa=0){
  k <- ncol(L)
  p <- nrow(L)
  # kappa <- 0 # Quartimax
  # kappa <- 1/p # Varimax
  # kappa <- m/(2*p) # Equamax
  # kappa <- (m-1)/(p+m-2) # Parsimax
  # kappa <- 1 # Factor parsimony
  Method <- paste("Crawford-Ferguson:k=",kappa,sep="")
  N <- matrix(1,k,k)-diag(k)
  M <- matrix(1,p,p)-diag(p)
  L2 <- L^2
  f1 <- (1-kappa)*sum(diag(crossprod(L2,L2 %*% N)))/4
  f2 <- kappa*sum(diag(crossprod(L2,M %*% L2)))/4
  f <- f1 + f2
  Gq <- (1-kappa) * L * (L2 %*% N) + kappa * L * (M %*% L2)
  return(list(Gq=Gq,f=f,Method=Method))
}

vgQ.infomax <- function(L){
  Method <- "Infomax"
  k <- ncol(L)
  p <- nrow(L)
  S <- L^2
  s <- sum(S)
  s1 <- apply(S, 1, sum)
  s2 <- apply(S, 2, sum)
  E <- S/s
  e1 <- s1/s
  e2 <- s2/s
  Q0 <- sum(-E * log(E))
  Q1 <- sum(-e1 * log(e1))
  Q2 <- sum(-e2 * log(e2))
  f <- log(k) + Q0 - Q1 - Q2
  H <- -(log(E) + 1)
  alpha <- sum(S * H)/s^2
  G0 <- H/s - alpha * matrix(1, p, k)
  h1 <- -(log(e1) + 1)
  alpha1 <- s1 %*% h1/s^2
  G1 <- matrix(rep(h1,k), p)/s - as.vector(alpha1) * matrix(1, p, k)
  h2 <- -(log(e2) + 1)
  alpha2 <- h2 %*% s2/s^2
  G2 <- matrix(rep(h2,p), ncol=k, byrow=T)/s - as.vector(alpha2) * matrix(1, p, k)
  Gq <- 2 * L * (G0 - G1 - G2)
  return(list(Gq=Gq,f=f,Method=Method))
}

vgQ.mccammon <- function(L){
  Method <- "McCammon entropy"
  k <- ncol(L)
  p <- nrow(L)
  S <- L^2
  M <- matrix(1,p,p)
  s2 <- apply(S, 2, sum)
  P <- S / matrix(rep(s2,p),ncol=k,byrow=T)
  Q1 <- -sum(P * log(P))
  H <- -(log(P) + 1)
  R <- M %*% S
  G1 <- H/R - M %*% (S*H/R^2)
  s <- sum(S)
  p2 <- s2/s
  Q2 <- -sum(p2 * log(p2))
  h <- -(log(p2) + 1)
  alpha <- h %*% p2
  G2 <- rep(1,p) %*% t(h)/s - as.vector(alpha)*matrix(1,p,k)
  Gq <- 2*L*(G1/Q1 - G2/Q2)
  Q <- log(Q1) - log(Q2)
  return(list(Gq=Gq,f=Q,Method=Method))
}

#
# GPromax is a separate function!!!
# Call directly from command prompt.
# R code only.
#
GPromax <- function(A,pow=3){
  method <- "Promax"
  # Initial rotation: Standardized Varimax
  #require(mva)
  xx <- promax(A,pow)
  Lh <- xx$loadings
  Th <- xx$rotmat
  orthogonal <- F
  Table <- NULL
  return(list(Lh=Lh,Th=Th,Table=NULL,method,orthogonal=orthogonal))
}

##
## UTILITY FUNCTIONS

##

# Kaisers standardized variables

# Can be used for standardization and de-standardization.

# For standardization: only provide 1 argument: the matrix A

# output: standardized A and weights W

# For destandardization: provide 2 arguments: matrix A

# and weights W.

# output: destandardized A and weights W.

Standardize <- function(A,W){

  if (nargs()==1)

    W <- sqrt(apply(A^2,1,sum))
  else

    W <- 1/W

  A <- sweep(A,1,W,"/")

  return(A,W)
}

# Random Start

# k: number of dimensions

# orthogonal random start? (Yes, by default).

Random.Start <- function(k,orthogonal=T){

  mat <- matrix(rnorm(k*k),k)

  if (orthogonal){
    ans <- qr.Q(qr(mat))
  }

  else{

    ans <- mat %*% diag(sqrt(1/diag(t(mat) %*% mat)))

  }

  ans

}

########## Following routines added by Steiger

vgQ.bifactor <- function(L){
  Method <- "bifactor"
  k <- dim(L)[2]
  p <- dim(L)[1]
  L <- L[,2:k]
  Lsq <- L * L
  M <- matrix(1,k-1,k-1) - diag(k-1)
  f <- sum(Lsq * (Lsq %*% M))
  zero <- matrix(0,p,1)
  Gq <- 4*cbind(zero, L * (Lsq %*% M))
  return(list(Gq=Gq,f=f,Method=Method))
}

################  End of Augmented Jennrich-Bentler Functions ############

################ The following code block augments some service
################ functions for Exploratory Factor Analysis with R
##########################################################################

sx2 <- function(df, lambda)
{
  x <- 2 * df + 4 * lambda
  if(x<0)x<-0
  return(sqrt(x))
}

FindLambda <- function(X2Observed,Df,CumP)
{

##should get symbolic integer codes for droot_c
##compute rough approximation to the start value
  ErrorCondition <- FALSE
  if (pchisq(X2Observed, Df) < CumP)
  {
    print("No non-negative value of Lambda will produce the desired cumulative probability.")
    return(NA)
  }
  zgoal <- qnorm(CumP)
  df <- Df
  lambda1 <- X2Observed - df
  if(lambda1 < 0) lambda1 <- 0
  s1 <- sx2(df, lambda1)
  lambda2 <- lambda1 - s1 * zgoal  ##temporary guess for lambda2
  s2 <- sx2(df, lambda2)
  s_est <- (s1 + s2) / 2
  lambda2 <- lambda1 - zgoal * s_est
  if(lambda2 < 0) lambda2 <- 0
  pp2<-pchisq(X2Observed, Df, lambda2)-CumP
  s1 <- sx2(df,lambda2)
  z <-( X2Observed - df - lambda2)/s1
  lambda3 <- lambda2 - s1 * (z-zgoal)  ##temporary guess for lambda2
  s2 <- sx2(df, lambda3)
  s_est <- (s1 + s2) / 2
  lambda3 <- lambda2 - (z-zgoal) * s_est
  if(lambda3 < 0)
  {
    lambda3 <- 0
  }
  pp3<-pchisq(X2Observed, Df, lambda3)-CumP
  ##   should be in a linear range by now
  if((pp2-pp3)!= 0)
  {
    xstart <- lambda3 - pp3 * ((lambda2-lambda3)/(pp2-pp3))
  }
  n<-1
  maxit<-10000
  step<-1.e-4
  eps<-1.e-10

  xstart<-max(xstart,1)
  if(abs(pchisq(X2Observed, Df)-CumP) <= eps)
  {
    return(0)
  }
  xold<-0
  xc<-xstart
  fc<-pchisq(X2Observed, Df, xc)-CumP
  if(ErrorCondition)
  {
    return(NA)
  }
  hc<-max(step*xc,eps)
  xnew<-xc-hc
  fnew<-pchisq(X2Observed, Df, xnew)-CumP
  if(ErrorCondition)
  {
    return(NA)
  }
  ac<-(fc-fnew)/hc
  while ( abs(xnew-xold) > eps && n < maxit && abs(fc) > eps && abs(ac) >= 1.e-35)
  {
    n<-n+1
    xnew<-xc-fc/ac
    xold<-xc
    fold<-fc
    xc<-xnew
    fc<-pchisq(X2Observed, Df, xnew)-CumP
    ac<-(fold-fc)/(xold-xc)
    if(ErrorCondition)
    {
      return(NA)
    }

  }
  ##iteration complete
  if( (n >= maxit)||(abs(fc) > eps))
  {
    ErrorCondition <- TRUE
    print("Percentage point convergence failure.")
    return(NA)
  }
  else
  {
    return(xc)
  }
}
ChiSquare.Lambda.CI <- function(X2Observed,Df,Confidence=.90){
  p <- (1. - Confidence)/2.
  if( pchisq(X2Observed, Df) <= p)
{
    Upper <- 0.
    Lower <- 0.
    return(c(Lower,Upper))
  }
     Upper <- FindLambda(X2Observed,Df,p)
     if(pchisq(X2Observed, Df) <= 1.-p)
     {
       Lower  <- 0
       return(c(Lower,Upper))
     }
     Lower <- FindLambda(X2Observed,Df,1-p)
     return(c(Lower,Upper))
}

rmsea.ci <- function(chisq,df,n,conf=.90){
  options(warn=-1)
  sample.rmsea <- sqrt(max(((chisq-df)/(n-1)),0)/df)
#  out <- conf.limits.nc.chisq(Chi.Square=chisq,conf.level=conf,df=df,Jumping.Prop=0.001)
  out<-ChiSquare.Lambda.CI(chisq,df,conf)
  lower <- if(is.na(out[1])) 0 else out[1]
  upper <- if(is.na(out[2])) 0 else out[2]
  lower <- sqrt(lower/(n-1)/df)
  upper <- sqrt(upper/(n-1)/df)
  results <- list(Lower.Limit= lower,
                  Point.Estimate = sample.rmsea,
                  Upper.Limit = upper,
                  Confidence.Level = conf)
  return((results))
}

compute_CFI <- function(chi_null,df_null,chi_model,df_model){
  d_null <- chi_null - df_null
  d_model <- chi_model - df_model
  (cfi <- (d_null - d_model) / d_null)
  return(cfi)
}

compute_TLI <- function(chi_null,df_null,chi_model,df_model){
  (r_null <- chi_null/df_null)
  (r_model <- chi_model/df_model)
  (tli <- (r_null - r_model)/((r_null)-1))
  return(tli)
}

fa_stats <- function(Correlation.Matrix,n.obs,n.factors,conf=.90,
                     maxit=1000,RMSEA.cutoff=NULL,
                     main="RMSEA Plot",sub=NULL){
  # browser()
  runs <- length(n.factors)
  R <- Correlation.Matrix
  maxfac <- max(n.factors)
  res <- matrix(NA,runs,10)
  roots <- eigen(R)$values
  for(i in 1:runs){
    # i <- 3
    # R <- R0
    # n.obs = 643
    # n.factors=3:5
    # maxit = 1000
    output <- factanal(covmat=R,n.obs=n.obs,factors=n.factors[i],maxit=maxit)
    fit <- psych::fa(R,nfactors=n.factors[i],n.obs=n.obs,fm="ml")
    X2 <- output$STATISTIC
    df <- output$dof
    ci <- rmsea.ci(X2,df,n.obs,conf)
    pvar <- sum(roots[1:n.factors[i]])
    # cfi  <- compute_CFI(fit$null.chisq,fit$null.dof,fit$chi, fit$dof)
    # tli  <- compute_TLI(fit$null.chisq,fit$null.dof,fit$chi, fit$dof)
    cfi  <- compute_CFI(fit$null.chisq,fit$null.dof,X2, df)
    tli  <- compute_TLI(fit$null.chisq,fit$null.dof,X2, df)
    v <- c(n.factors[i],pvar,X2,df,1-pchisq(X2,df),ci$Point.Estimate,
           ci$Lower.Limit,ci$Upper.Limit, cfi,tli)

    res[i,] <- v
  }
  colnames(res)=c("Factors","Cum.Eigen","Chi-Square","Df","p.value",
                  "RMSEA.Pt","RMSEA.Lo","RMSEA.Hi","CFI","TLI")
  plotCI(n.factors,res[,6],li = res[,7],ui=res[,8],
         ylab="RMSEA",xlab="Number of Factors",main=main,
         sub=sub)
  lines(n.factors,res[,6],col="blue")
  abline(h=0,col="black",lty=3)
  abline(h=RMSEA.cutoff,col="red",lty=2)

  return(res)
}

FA.Stats <- function(
  Correlation.Matrix,
  n.obs,
  n.factors,
  conf=.90,
  maxit=1000,
  RMSEA.cutoff=NULL,
  main="RMSEA Plot",
  sub=NULL
){
  # browser()
  runs <- length(n.factors)
  R <- Correlation.Matrix
  maxfac <- max(n.factors)
  res <- matrix(NA,runs,8)
  roots <- eigen(R)$values
  for(i in 1:runs){
     output <- stats::factanal(
       covmat=R,
       n.obs=n.obs,
       factors=n.factors[i],
       maxit=maxit
     )
     X2 <- output$STATISTIC
     df <- output$dof
     ci <- rmsea.ci(X2,df,n.obs,conf)
     pvar <- sum(roots[1:n.factors[i]])
     v <- c(
       n.factors[i],
       pvar,
       X2,
       df,
       1-pchisq(X2,df),
       ci$Point.Estimate,
       ci$Lower.Limit,
       ci$Upper.Limit
     )
     res[i,] <- v
  }
  colnames(res)=c("Factors","Cum.Eigen","Chi-Square","Df","p.value",
                      "RMSEA.Pt","RMSEA.Lo","RMSEA.Hi")
  plotCI(n.factors,res[,6],li = res[,7],ui=res[,8],
         ylab="RMSEA",xlab="Number of Factors",main=main,
         sub=sub)
  lines(n.factors,res[,6],col="blue")
  abline(h=0,col="black",lty=3)
  abline(h=RMSEA.cutoff,col="red",lty=2)
  return(res)
}

Scree.Plot <- function(R,main="Scree Plot",sub=NULL){
  roots <- eigen(R)$values
  x <- 1:dim(R)[1]
  plot(x,roots,type="b",col='blue',ylab="Eigenvalue",
       xlab="Component Number",main=main,sub=sub)
  abline(h=1,lty=2,col="red")

}
# Scree.PlotGG <- function(R, main="Scree Plot",sub=NULL){
#   roots <- eigen(R)$values
#   x <- 1:dim(R)[1]
# #   plot(x,roots,type="b",col='blue',ylab="Eigenvalue",
# #        xlab="Component Number",main=main,sub=sub)
# #   abline(h=1,lty=2,col="red")
#
#   ds <- data.frame(x=x, roots=roots)
#   g <- ggplot(ds, aes(x=x, y=roots)) +
#     geom_rect(ymax=1, ymin=-Inf, xmin=-Inf, xmax=Inf, fill="red") +
#     geom_line(size=1.5, color="blue", na.rm = TRUE) +
#     geom_point(size=5, color="darkblue", na.rm = TRUE) +
#     labs(title=main, x="Component Number", y="Eigenvalue") +
#     theme_bw()
#
#   print(g)
# }

FindBifactor <- function(A,reps){
  warn=-1
  results <- rep( list(list()), reps )
  criterion <- rep(NA,reps)
  m <- dim(A)[2]
  for(i in 1:reps) {
    x <- GPForth(A,method="bifactor",Tmat = Random.Start(m))
    criterion[i] <- min(x$Table[,2])
    results[[i]] <- x
  }
  j <- order(criterion)[1]
  warn=1
  return(results[[j]])
}

FindBestPattern <- function(A,method,is.oblique=FALSE,reps=15){
  results <- rep( list(list()), reps )
  criterion <- rep(NA,reps)
  m <- dim(A)[2]
  for(i in 1:reps) {
    if(!is.oblique)x <- GPForth(A,method=method,Tmat = Random.Start(m))else
      x <- GPFoblq(A,method=method,Tmat=Random.Start(m))
    criterion[i] <- min(x$Table[,2])
    results[[i]] <- x
  }
  j <- order(criterion)[1]
  return(results[[j]])
}

FixPattern <- function(res,sort=TRUE){
  F <- res$Lh
  p <- dim(F)[1]
  m <- dim(F)[2]
  factor.names <- paste("Factor",1:m,sep="")
  one.prime <- matrix(1,1,p)
  F.sign <- as.vector(sign(one.prime %*% F))
  #  cat("\nLoadings:\n")
#  fx <- format(round(Lambda, digits))
#  names(fx) <- NULL
#  nc <- nchar(fx[1L], type="c")
#  fx[abs(Lambda) < cutoff] <- paste(rep(" ", nc), collapse = "")
#  print(fx, quote = FALSE, ...)
#  F <- Lambda
#fix signs
  Lambda <- F
  Lambda <- Lambda %*% diag(F.sign)
#sort columns by SS loadings
  mx <- diag(t(Lambda)%*%Lambda)
  ind <- order(mx,decreasing=TRUE)
  Lambda <- Lambda[,ind]
#  browser()
#  print(res$orthogonal)
#  print(res$Phi)
  if(!res$orthogonal)
  {
#   fix up Phi
   Phi <- diag(F.sign) %*% res$Phi %*% diag(F.sign)
   Phi <- Phi[ind,]
   Phi <- Phi[,ind]
  }
  if (sort) {
    mx <- max.col(abs(Lambda))
    ind <- cbind(1L:p, mx)
    mx[abs(Lambda[ind]) < 0.4] <- m + 1
    Lambda <- Lambda[order(mx, 1L:p),]
  }
  colnames(Lambda) <- factor.names
  res$Lh <- Lambda
  if(!res$orthogonal)
  {
  colnames(Phi) <- factor.names
  rownames(Phi) <- factor.names
  res$Phi <- Phi
  }
  return(res)
}

# FindBifactorPattern is not used anywhere else in this code
FindBifactorPattern <- function(A,reps,digits=2) {
  options(warn=-1)
  p<-dim(A)[1]
  out <- FindBifactor(A,reps)
  F <- out$Lh
  one.prime <- matrix(1,1,p)
  F.sign <- as.vector(sign(one.prime %*% F))
  F <- F %*% diag(F.sign)
  options(warn=1)
  return(round(F,digits))
}

GPromax <- function(A,pow=3){
  method <- "Promax"
  # Initial rotation: Standardized Varimax
  xx <- promax(A,pow)
  Lh <- xx$loadings
  Th <- xx$rotmat
  orthogonal <- F
  Table <- NULL
  Tinv <- solve(Th)
  Phi <- Tinv %*% t(Tinv)
  return(list(Lh=Lh,Phi=Phi,Th=Th,Table=NULL,method,orthogonal=orthogonal))
}

print.FLS <- function(x, sort=TRUE, num.digits=3, cutoff=.25, ...)
{
  Lambda <- unclass(x$F)

  p <- nrow(Lambda)
  factors <- ncol(Lambda)
  if (sort) {
    mx <- max.col(abs(Lambda))
    ind <- cbind(1L:p, mx)
    mx[abs(Lambda[ind]) < 0.5] <- factors + 1
    Lambda <- Lambda[order(mx, 1L:p),]
  }
  fx <- format(round(Lambda, num.digits))
  names(fx) <- NULL
  nc <- nchar(fx[1L], type="c")
  fx[abs(Lambda) < cutoff] <- paste(rep(" ", nc), collapse = "")
  print(fx, quote = FALSE, ...)
  vx <- colSums(Lambda^2)
  varex <- rbind("SS loadings" = vx)
  if(is.null(attr(x, "covariance"))) {
    varex <- rbind(varex, "Proportion Var" = vx/p)
    if(factors > 1)
      varex <- rbind(varex, "Cumulative Var" = cumsum(vx/p))
  }
  cat("\n")
  print(round(varex, num.digits))

if(!is.null(x$Phi)){
  cat("\nFactor Intercorrelations\n")
  cat("------------------------\n")
  print(round(unclass(x$Phi),num.digits))
  }
 invisible(x)
}

print.MLFA<-function(x,num.digits=3,cutoff=0.25,sort_=TRUE,...)
{
cat("\n## Unrotated\n------------------\n")
print.FLS(x$Unrotated,num.digits=num.digits,cutoff=cutoff,sort=sort_)
cat("\n## Varimax (T)\n------------------\n")
print.FLS(x$Varimax,num.digits=num.digits,cutoff=cutoff,sort=sort_)
cat("\n## Promax (Q)\n-----------------\n")
print.FLS(x$Promax,num.digits=num.digits,cutoff=cutoff,sort=sort_)
cat("\n## Quartimin (Q)\n-----------------\n")
print.FLS(x$Quartimin,num.digits=num.digits,cutoff=cutoff,sort=sort_)
cat("\n## Bifactor (T)Loadings\n---------------------------\n")
print.FLS(x$Bifactor,num.digits=num.digits,cutoff=cutoff,sort=sort_)
cat("\n## Bifactor (Q)\n-------------------------\n")
# print.FLS(x$BifactorOblique,num.digits=num.digits,cutoff=cutoff,sort=sort_)
# # the last element must be invisible for proper output in R
invisible(print.FLS(x$BifactorOblique,num.digits=num.digits,cutoff=cutoff,sort=sort_))

# # Crawford-Ferguson added by Koval
# cat("\nCF Loadings\n-------------------------\n")
# print.FLS(x$CF,num.digits=num.digits,cutoff=cutoff,sort=sort_)
# cat("\nCFQ Loadings\n-------------------------\n")
# # the last element must be invisible for proper output in R
# invisible(print.FLS(x$CFQ,num.digits=num.digits,cutoff=cutoff,sort=sort_))

}

MLFA <- function(Correlation.Matrix=NULL,
                 n.factors=NA,
                 n.obs=NA,
                 data=NULL,
                 Factor.Pattern=NULL,
                 random.starts=15,
                 maxit=1000,
                 num.digits=3,
                 cutoff=0.30,
                 promax.m=3,
                 sort = TRUE)
{
cat("This will take a moment.")
if(!is.null(Correlation.Matrix)&&is.null(data)){
        R <- Correlation.Matrix
        p <- dim(R)[1]
        A <- factanal(covmat=R,n.obs=n.obs,factors=n.factors,maxit=maxit,rotation="none")$loadings[1:p,]
  }
if(!is.null(data)){
        R <- cor(data)
    n.obs <- dim(data)[1]
        p <- dim(R)[1]
        A <- factanal(covmat=R,n.obs=n.obs,factors=n.factors,maxit=maxit,rotation="none")$loadings[1:p,]
}
if(is.null(data)&&is.null(Correlation.Matrix)){
        A <- Factor.Pattern
        p <- dim(A)[1]
}

m <- dim(A)[2]
factor.labels <- paste("Factor",1:m,sep="")
# browser()
# Variamx
A.varimax <- varimax(A)$loadings[1:p,]
res <- list(Lh=A.varimax,orthogonal=TRUE)
res <- FixPattern(res, sort = sort)
A.varimax <- list(F=res$Lh)
# Promax
res <- GPromax(A,pow=promax.m)
res <- list(Lh=res$Lh,Phi=res$Phi,orthogonal=FALSE)
res <- FixPattern(res, sort = sort)
Phi.promax <- res$Phi
A.promax <- list(F=res$Lh,Phi=Phi.promax,orthogonal=FALSE)
cat(".")
# Quartimin
res <- FindBestPattern(A,"quartimin",reps=random.starts,is.oblique=TRUE)
res <- list(Lh=res$Lh,Phi=res$Phi,orthogonal=FALSE)
res <- FixPattern(res, sort = sort)
Phi.quartimin <- res$Phi
A.quartimin <- list(F=res$Lh,Phi = Phi.quartimin)
cat(".")
# Bifactor
res <- FindBestPattern(A,"bifactor",reps=random.starts)
orthogonal=TRUE
res <- list(Lh=res$Lh,Phi=res$Phi,orthogonal=orthogonal)
res <- FixPattern(res, sort = sort)
Phi=NULL
A.bifactor <- list(F=res$Lh,Phi=Phi)
cat(".")
# Bifactor Oblique
res <- FindBestPattern(A,"bifactor",reps=random.starts,is.oblique=TRUE)
res <- list(Lh=res$Lh,Phi=res$Phi,orthogonal=FALSE)
res <- FixPattern(res, sort = sort)
cat(".")
Phi.bifactor.oblique <- res$Phi
A.bifactor.oblique <- list(F=res$Lh,Phi=Phi.bifactor.oblique)
cat(".")

#### ADDitional rotations

# Geomin (orthogonal)
res <- FindBestPattern(A,"geomin",reps=random.starts)
orthogonal=TRUE
res <- list(Lh=res$Lh,Phi=res$Phi,orthogonal=orthogonal)
res <- FixPattern(res, sort = sort)
Phi=NULL
A.geominT <- list(F=res$Lh,Phi=Phi)
cat(".")
# Geomin (Oblique)
res <- FindBestPattern(A,"geomin",reps=random.starts,is.oblique=TRUE)
res <- list(Lh=res$Lh,Phi=res$Phi,orthogonal=FALSE)
res <- FixPattern(res, sort = sort)
cat(".")
Phi.bifactor.oblique <- res$Phi
A.geominQ <- list(F=res$Lh,Phi=Phi.bifactor.oblique)
cat(".")

# # Oblimin
# res <- FindBestPattern(A,"oblimin",reps=random.starts,is.oblique=TRUE)
# res <- list(Lh=res$Lh,Phi=res$Phi,orthogonal=FALSE)
# res <- FixPattern(res, sort = sort)
# Phi.oblimin <- res$Phi
# A.oblimin <- list(F=res$Lh,Phi = Phi.oblimin)
# cat(".")

# # CF added by Koval
# # Crawford-Ferguson
# res <- FindBestPattern(A,"cf",reps=random.starts,is.oblique=FALSE)
# res <- list(Lh=res$Lh,Phi=res$Phi,orthogonal=TRUE)
# res <- FixPattern(res, sort = sort)
# Phi=NULL
# A.cf <- list(F=res$Lh,Phi=Phi)
# cat(".")
# # Crawford-Ferguson Oblique
# res <- FindBestPattern(A,"cf",reps=random.starts,is.oblique=TRUE)
# res <- list(Lh=res$Lh,Phi=res$Phi,orthogonal=FALSE)
# res <- FixPattern(res, sort = sort)
# cat(".")
# Phi.cfq <- res$Phi
# A.cfq <- list(F=res$Lh,Phi=Phi.cfq)
# cat(".")
# browser()
# reconstruct the unrotated solution
A <- list(F=A,Phi=NULL) # line moved from end of the BifactorOblique section
# cat(".")
#
class(A)="FLS"
class(A.varimax)="FLS"
class(A.promax)="FLS"
class(A.quartimin)="FLS"
cat(".exiting\n") # what is this for?
class(A.bifactor)="FLS"
class(A.bifactor.oblique)="FLS"
class(A.geominT) ="FLS"
class(A.geominQ) ="FLS"

# additional rotations
# class(A.cf)="FLS"
# class(A.cfq)="FLS"
# class(A.oblimin) = "FLS"

output<-list(
  Unrotated       = A,
  Varimax         = A.varimax,
  GeominT         = A.geominT,
  Bifactor        = A.bifactor,
  Promax          = A.promax,
  Quartimin       = A.quartimin,
  GeominQ         = A.geominQ,
  BifactorOblique = A.bifactor.oblique
  # Oblimin = A.oblimin
  # CF=A.cf,
  # CFQ=A.cfq
       )
class(output) = "MLFA"
return(output)
}
# end of MLFA function definition

Loadings <- function(x,num.digits=3,cutoff=.25){
  invisible(print.MLFA(x,cutoff=cutoff,num.digits=num.digits))
}

FAtoCFA <-  function(
  x,
  model.name           = "fit",
  cutoff               = 0.30,
  factor.names         = colnames(x$F),
  reference.indicators = FALSE,
  covs                 = paste(factor.names, collapse=",")
){
  # browser()
  F <- x$F
  fname <- paste(model.name,".r",sep="")
  file.create(fname)
  rn <- rownames(F)
  p <- dim(F)[1]
  m <- dim(F)[2]
  line <- rep(" ",m)
  for (j in 1:m) {
    prefix <- paste(factor.names[j],":",sep="")
    count <- sum(abs(F[,j]>cutoff))
    postfix <- rep(" ",count)
    i.postfix <- 0
    for(i in 1:p){
      if(abs(F[i,j])> cutoff){
        i.postfix <- i.postfix + 1
        if(i.postfix<count) postfix[i.postfix] <- paste(rn[i],", ",sep="")else postfix[i.postfix] <- paste(rn[i],"\n",sep="")
      }
    }
    postfix <- paste(postfix,collapse='')
    line[j] <- paste(prefix,postfix)
    cat(line[j],file=fname,append=TRUE)
  }
  cat("\n",file=fname,append=TRUE)
  cstring <- paste(  c("cfa(reference.indicators=reference.indicators,covs=covs,
                       file=\"",fname,"\")"),collapse="")
  cfa.model <- eval(parse(text=cstring))
  return(cfa.model)
}

FAtoREF <-  function(x,R,model.name="fit",factor.names=colnames(x$F),
                     make.start.values=TRUE,cov.matrix=TRUE,num.digits=4){
  F <- round(x$F,num.digits)
  fname <- paste(model.name,".r",sep="")
  file.create(fname)
  Phi <- x$Phi
  rn <- rownames(F)
  p <- dim(F)[1]
  m <- dim(F)[2]
  #  cat(paste(model.name,"<- cfa(reference.indicators=FALSE)\n"))
  line <- rep(" ",p-(m-1))
  ref.position <- rep(NA,m)
  par.number <- 0
  for (j in 1:m)ref.position[j] <- order(F[,j])[p]
## Got reference positions -- if start values are needed, compute them
## Now, construct the transformation matris
## First, pull out F1
  F1 <- F[ref.position[1],]
  for (j in 2:m) F1 <- rbind(F1,F[ref.position[j],])
## Got F1, now compute T and then rotate F and compute Phi
  if(make.start.values){
  T <- solve(F1) %*% diag ( sqrt( diag(F1 %*% t(F1) ) ) )
  F <- F %*% T
  Tinv <- solve(T)
  Phi <- Tinv %*% t(Tinv)
  }
  for (j in 1:m) {
    prefix <- paste(factor.names[j]," -> ",sep="")
    for(i in 1:p){
      postfix <- rn[i]
      non.fixed=TRUE
      line.out=FALSE
      for(jj in 1:m)
      {
        if(i == ref.position[jj]&&jj==j)
        {
          non.fixed=TRUE
          line.out=TRUE}
        if(i == ref.position[jj]&&jj!=j)
          non.fixed=FALSE
      }
      if(non.fixed){

        par.number <- par.number + 1
        par.name <- paste("Theta",as.character(par.number),sep="")
        if(make.start.values)
          sv<-paste(c(", ",as.character(round(F[i,j],num.digits)),"\n"),collapse="")else  sv <- ", NA\n"
        postfix <- paste(c(postfix,", ",par.name,sv),collapse="")
        line.out=TRUE
      }

      if(line.out){
        line <- paste(c(prefix,postfix),collapse="")
        cat(line,file=fname,append=TRUE)
      }
    }
  }
  for(j in 1:m)
  {
    cat(paste(c(factor.names[j]," <-> ",factor.names[j],", NA, 1\n"),collapse=""),file=fname,append=TRUE)
  }
  if(cov.matrix && m>1 )
  {
    ok <- !is.null(Phi)
    for(i in 2:m)
      for(j in 1:(i-1))
      {
        par.number <- par.number + 1
        prefix <- paste(factor.names[i], "<-> ",sep="")
        postfix <- factor.names[j]
        if(make.start.values&&ok)
          sv<-paste(c(", ",as.character(round(Phi[i,j],num.digits)),"\n"),collapse="") else  sv <- ", NA\n"
        par.name <- paste("Theta",as.character(par.number),sep="")
        postfix <- paste(c(postfix,", ",par.name,sv),collapse="")
        cat(paste(c(prefix,postfix),collapse=""),file=fname,append=TRUE)
      }
  }
## add unique variances
  if(is.null(Phi))Phi <- diag(p)
  U2 <- diag(R - F %*% Phi %*% t(F))
  for(i in 1:p){
    par.number <- par.number+1
    prefix <- paste(c(rn[i]," <-> ",rn[i],", "),collapse="")
    par.name <- paste("Theta",as.character(par.number),sep="")
    if(make.start.values)
      sv<-paste(c(", ",as.character(round(U2[i],num.digits)),"\n"),collapse="") else  sv <- ", NA\n"
    cat(paste(c(prefix,par.name,sv),collapse=""),file=fname,append=TRUE)
    }

  cat("\n",file=fname,append=TRUE)
  cstring <- paste(  c("specifyModel(file=\"",fname,"\")"),collapse="")
  sem.model <- eval(parse(text=cstring))
  return(sem.model)
}

FAtoSEM <-  function(
  x,
  model.name          = "fit",
  cutoff              = 0.30,
  factor.names        = colnames(x$F),
  make.start.values   = FALSE,
  cov.matrix          = FALSE,
  num.digits          = 4,
  par.prefix          = c("Theta","Theta"),
  reference.variables = FALSE
){
  F <- round(x$F,num.digits)
  fname <- paste(model.name,".r",sep="")
  file.create(fname)
  cat("\n")
  Phi <- x$Phi
  rn <- rownames(F)
  p <- dim(F)[1]
  m <- dim(F)[2]
  #  cat(paste(model.name,"<- cfa(reference.indicators=FALSE)\n"))
  line <- rep(" ",p-(m-1))
  par.number <- 0
  for (j in 1:m) {
    prefix <- paste(factor.names[j]," -> ",sep="")
    var.count <- 0
    for(i in 1:p){
      postfix <- rn[i]
      if(abs(F[i,j])>cutoff)
      { var.count <- var.count + 1
        if(var.count>1 || !reference.variables){
        par.number <- par.number + 1
        par.name <- paste(par.prefix[1],as.character(par.number),sep="")
        if(make.start.values)
        {sv<-paste(c(", ",as.character(F[i,j]),"\n"),collapse="")}else sv <- ", NA\n"
        postfix <- paste(c(postfix,", ",par.name,sv),collapse="")
        }else{
          postfix <- paste(postfix,",NA,1\n",sep="")
        }
        line <- paste(c(prefix,postfix),collapse="")
        cat(line,file=fname,append=TRUE)
      }
    }
  }
  for(j in 1:m)
  {
    if(!reference.variables){
      cat(paste(c(factor.names[j]," <-> ",factor.names[j],", NA, 1\n"),collapse=""),
           file=fname,append=TRUE)}
}
if(cov.matrix && m>1 )
  {
    ok <- !is.null(Phi)
    for(i in 2:m)
      for(j in 1:(i-1))
      {
        par.number <- par.number + 1
        prefix <- paste(factor.names[i], "<-> ",sep="")
        postfix <- factor.names[j]
        if(make.start.values&&ok)sv<-paste(c(", ",as.character(Phi[i,j]),"\n"),collapse="") else  sv <- ", NA\n"
        par.name <- paste(par.prefix[2],as.character(par.number),sep="")
        postfix <- paste(c(postfix,", ",par.name,sv),collapse="")
        cat(paste(c(prefix,postfix),collapse=""),file=fname,append=TRUE)
      }
  }
  cat("\n",file=fname,append=TRUE)
  cstring <- paste(  c("specifyModel(file=\"",fname,"\")"),collapse="")
  sem.model <- eval(parse(text=cstring))
  return(sem.model)
}

GetPattern <- function(sem.object){
  A <- sem.object$A
  P <- sem.object$P
  p <- sem.object$n
  m <- sem.object$m - p
  F <- A[1:p,(p+1):sem.object$m]
  Phi <- P[(p+1):sem.object$m,(p+1):sem.object$m]
  return(list(F=F,Phi=Phi))
}

SetupCFAPattern <- function(
  R,
  n.factors    = NA,
  factor.names = NULL
){
  p <- dim(R)[1]
  m <- n.factors
  Fp <- matrix(0,p,m)
  rownames(Fp) <- colnames(R)
  if(is.null(factor.names))factor.names <- paste("Factor",1:m,sep="")
  colnames(Fp) <- factor.names
  Fp <- edit(Fp)
  return(list(F=Fp))
} # return Factor pattern that was manually set up in the editor

UseMod <- function(sem.object,loadings.only=TRUE){
  options(warn=-1)
new.path <- CheckMod(sem.object,row.form=TRUE,loadings.only=loadings.only)$NewPath
old.model <- sem.object$semmod
R <- sem.object$S
n.obs <- sem.object$N
new.model <- rbind(old.model,new.path)
class(new.model) <- class(old.model)
fit.object <- sem(new.model,R,n.obs)
return(fit.object)
}

CFAModel <- function(
  R,
  model.name           = "fit",
  n.factors            = NULL,
  factor.names         = NULL,
  cutoff               = 0.30,
  covs                 = TRUE,
  reference.indicators = FALSE
){
 if(is.null(n.factors))n.factors=length(factor.names)
 x <- SetupCFAPattern(R,n.factors,factor.names)
 factor.names <- colnames(x$F)
 if(covs){
   covariances=paste(factor.names, collapse=",")
 }else{covariances=NULL}
 # browser()
model <- FAtoCFA(
  x                    = x,
  model.name           = model.name,
  cutoff               = cutoff,
  factor.names         = factor.names,
  reference.indicators = reference.indicators,
  covs                 = covariances
)
return(model)
}

QuickCFA <- function(
  R,
  n.factors            = NULL,
  n.obs,
  model.name           = "Model0",
  factor.names         = NULL,
  cutoff               =0.30,
  factor.correlations  = FALSE,
  reference.indicators = FALSE)
{
  if(is.null(factor.names))factor.names <- paste("Factor",1:n.factors,sep="")
  model <- CFAModel(
    R,
    model.name,
    n.factors,
    factor.names,
    cutoff,
    covs=factor.correlations,
    reference.indicators
  )
  fit.object <- sem(model,R,n.obs)
  return(fit.object)
}

CheckMod <- function(sem.object,row.form=FALSE,loadings.only=TRUE){
  options(warn=-1)
  mi <- modIndices(sem.object)
  A <- mi$mod.A
  P <- mi$mod.P
  p <- sem.object$n
  m <- sem.object$m - p
  F <- A[1:p,(p+1):sem.object$m]
  par.number <- sem.object$t + 1
  par.name <- paste("AddedTheta",par.number,sep="")
  largest.F <- max(F,na.rm=TRUE)
  largest.P <- 0
  largest.Q <- 0
  if(!loadings.only){
  Q <- P
  P <- P[(p+1):sem.object$m,(p+1):sem.object$m]
  Q <- Q[1:p,1:p]
  largest.P <- max(P,na.rm=TRUE)
  largest.Q <- max(Q,na.rm=TRUE)
  }
  maxes <- c(largest.F,largest.P,largest.Q)
  for(i in 1:3)if(maxes[i]==-Inf)maxes[i]<-0
  max.mod.pos <- order(maxes,decreasing=TRUE)[1]
  if(max.mod.pos == 1){
    arrow <- "->"
    F.pos <- which(F==largest.F,arr.ind=TRUE)
    if(length(F.pos[,1])>1)F.pos <- F.pos[1,]
    postfix <- rownames(F)[F.pos[1]]
    prefix <- colnames(F)[F.pos[2]]
    value <- F[F.pos[1],F.pos[2]]
  }
  if(max.mod.pos==2){
        arrow="<->"
        P.pos <- which(P==largest.P,arr.ind=TRUE)
        if(length(P.pos[,1])>1)P.pos <- P.pos[1,]
        prefix <- colnames(P)[P.pos[1]]
        postfix <- colnames(P)[P.pos[2]]
        value <- P[P.pos[1],P.pos[2]]
  }
  if(max.mod.pos==3){
    arrow="<->"
    Q.pos <- which(Q==largest.Q,arr.ind=TRUE)
    if(length(Q.pos[,1])>1)Q.pos <- Q.pos[1,]
    prefix <- colnames(Q)[Q.pos[1]]
    postfix <- colnames(Q)[Q.pos[2]]
    value <- Q[Q.pos[1],Q.pos[2]]
  }
  path <- paste(c(prefix,arrow,postfix),collapse="")
  if(!row.form)line <- paste(c(path,",",par.name,", NA"),collapse="")else line <- c(path,par.name,", NA")
  fname="added.r"
  file.create(fname)
  cat(line,file=fname)
  output  <- list(NewPath=line,ModIndex=value)
  return(output)
}
CullModel<-function(fit.object,alpha,cull.Phi=FALSE){
  model <- fit.object$semmod
  zcrit <- qnorm(1-alpha/2)
  zval <- summary(fit.object)$coeff$'z value'
  sig <- abs(zval)>zcrit
  par.posn <- fit.object$par.posn
  new.model <- model
  model.length <- dim(model)[1]
  pos<-1
  to.cull <- 0
  is.ok <- rep(TRUE,model.length)
  if(!cull.Phi){
    do.not.cull <- grep("<->",model)
    for(i in 1:length(do.not.cull)){
    is.ok[do.not.cull[i]]<-FALSE
    }
  }
  for(i in 1:length(sig)){
    if(!sig[i]&&is.ok[par.posn[i]]){
      to.cull[pos] <- par.posn[i]
      pos <- pos+1
    }
  }
new.model <- new.model[-to.cull,]
class(new.model) <- class(model)
return(new.model)
}

QuickJoreskog <- function(R,n.factors,n.obs,model.name="model.0",
                          alpha=0.05,use.promax=TRUE,promax.m=3){
  mlfa.object <- MLFA(R,n.factors,n.obs,promax.m=promax.m)
  if(use.promax)x <- mlfa.object$Promax else x <- mlfa.object$Varimax
  ref.model <- FAtoREF(x,R,model.name)
  ref.fit <- sem(ref.model,R,n.obs)
  culled.model <- CullModel(ref.fit,alpha)
  culled.fit <- sem(culled.model,R,n.obs)
  return(culled.fit)
}

GetPrettyPattern <- function(fit.object,cutoff=0.10){
  print.FLS(GetPattern(fit.object),cutoff=cutoff)}

RMSEA <- function(fit.object,conf=0.90){
  n.obs <- fit.object$N
  X2 <- (n.obs-1)*fit.object$criterion
  p <- fit.object$n
  t <- fit.object$t
  df <- p*(p+1)/2 - t
  rmsea.ci(X2,df,n.obs,conf)
}
QuickEFAtoCFA <- function(
  R,
  n.factors,
  n.obs,
  rotation="Varimax",
  model.name="model.0",
  cutoff=0.30,
  alpha=0.05,
  make.start.values=TRUE,
  cov.matrix=TRUE,
  num.digits=4,
  promax.m=3
){
  rotation.list <- c("Varimax","Promax","Quartimin","Bifactor", "BifactorOblique")
  rot <- 0
  for(i in 1:5)
    if(rotation==rotation.list[i]){rot<-i;break}
  if(rot==0){print("Illegal Rotation Method");return()}
  x <- MLFA(R,n.factors,n.obs,cutoff=cutoff,num.digits=num.digits,promax.m=promax.m)
  cstring <- paste(  c("rot.pattern <-x$",rotation),collapse="")
  eval(parse(text=cstring))
  model <- FAtoSEM(rot.pattern,model.name=model.name,cutoff=cutoff,
                   factor.names=colnames(rot.pattern$F),
  make.start.values=make.start.values,cov.matrix=cov.matrix,num.digits=num.digits)
  fit <- sem(model,R,n.obs)
  return(fit)
}
QuickSEM <- function(S = NULL,n.obs=NULL,max.latents = dim(S)[1]){
x <- data.frame(Latent.Variable.Names = rep("",max.latents))
x <- edit(x)
p <- dim(S)[1]
LV <- x$Latent.Variable.Names
LV <- as.character(LV[LV!=""])
n.LV <- length(LV)
F <- matrix(0,n.LV,n.LV)
Lx <-NULL
Ln <-NULL
rownames(F) <- LV
colnames(F) <- LV
F <- edit(F)
cs <- colSums(F)
for(i in 1:n.LV)if(cs[i]==0)F <- F[1:n.LV,-i]
rs <- rowSums(F)
nc <- dim(F)[2]
for(i in 1:n.LV)if(rs[i]==0){
  Lx <- c(Lx,rownames(F)[i])
  F <- F[-i,1:nc]
}
Ln <- rownames(F)
n.Ln <- length(Ln)
n.Lx <- length(Lx)
Lambda.x <- matrix(0,p,n.Lx)
rownames(Lambda.x) <- colnames(S)
colnames(Lambda.x) <- Lx
Lambda.x <- edit(Lambda.x)
cly <- colnames(S)
rs <- rowSums(Lambda.x)
rn <-NULL
for(i in 1:p)if(rs[i]==0)rn <- c(rn,cly[i])
rows.Lambda.y <- length(rn)
Lambda.y <- matrix(0,rows.Lambda.y,n.Ln)
rownames(Lambda.y) <- rn
colnames(Lambda.y) <- Ln
Lambda.y <- edit(Lambda.y)
q <- dim(Lambda.x)[2]
rs <- rowSums(Lambda.x)
LL<-NULL
RR<-NULL
RN<-rownames(Lambda.x)
for(i in 1:p)if(rs[i]>0){
  LL <- rbind(LL,Lambda.x[i,])
  RR <- c(RR,RN[i])
}
Lambda.x <- LL
colnames(Lambda.x)<- Lx
rownames(Lambda.x)<- RR
xxx <- list(F=Lambda.x)
yyy <- list(F=Lambda.y)
mm1 <- FAtoSEM(xxx,"xxx",par.prefix=c("Lamdax","Phi"))
mm2 <- FAtoSEM(yyy,"yyy",reference.variables=TRUE,par.prefix=c("Lambday","Delta"))
struc.model <- FtoSEM(F,Lx,Ln,"sss")
my.model <- rbind(mm1,mm2,struc.model)
class(my.model) <- class(mm1)
sem(my.model,S,n.obs)
}

FtoSEM <- function(F,Lx,Ln,model.name){
  fname <- paste(model.name,".r",sep="")
  file.create(fname)
  cat("\n")
#Get Betas
  #Are there any?
  if(dim(F)[2]>length(Lx)){
  par.prefix <- "Beta"
  lnlist <- colnames(F)
  for(k in 1:length(Lx)){
    for(j in 1:length(lnlist))
      if(Lx[k]==lnlist[j])lnlist[k]<-" "}
  lns <- lnlist[lnlist!=" "]
  bbb=0
## List of prefix names for Betas is in lns
  for(j in 1:length(lns)){
    for(i in 1:length(Ln)){
#      browser()
      if(F[Ln[i],lns[j]]>0){bbb<-bbb+1
      prefix <- paste(c(lns[j],"->",Ln[i]),collapse="")
      par.name <- paste(c(",",par.prefix,as.character(bbb)),collapse="")
      postfix <- ",NA\n"
      }
    }
    line <- paste(c(prefix,par.name,postfix),collapse="")
    cat(line,file=fname,append=TRUE)
  }
  }
#Get Gammas
  gamma<-0
  par.prefix <- "Gamma"
  for(i in 1:length(Lx)){
    for(j in 1:length(Ln))
  {
      if(F[Ln[j],Lx[i]]>0){gamma<-gamma+1
      prefix <- paste(Lx[i],"->",sep="")
      prefix <- paste(prefix,Ln[j],sep="")
      par.name <- paste(c(",",par.prefix,as.character(gamma)),sep="")
      postfix <- ",NA\n"

        line <- paste(c(prefix,par.name,postfix),collapse="")
        cat(line,file=fname,append=TRUE)
  }}}
  cat("\n",file=fname,append=TRUE)
  cstring <- paste(  c("specifyModel(file=\"",fname,"\")"),collapse="")
  sem.model <- eval(parse(text=cstring))
  return(sem.model)

}

EditSEM<- function(sem.object){
  old.model <- sem.object$semmod
  new.model <- edit(old.model)
  return(sem(new.model,sem.object$S,sem.object$N))
}


[[[[[
   Show that a real r is Solovay random
   iff it is strong Chaitin random.

   An effective covering A_k of k is a function
   of k that enumerates bit strings s,
   which are the initial bits of the covered
   reals.  We assume that no s in A_k is a
   proper prefix or extension of another.
   Thus the measure of the cover A_k of k is
   exactly Sum_{s in A_k} of 2^{-|s|},
   where |s| is the length of the bit string s.
]]]]]

[Second part: not Ch random ===> not Sol random]

define (is-in? x l) [is x an element of list l?]
   if atom l     false
   if = x car l  true
   (is-in? x cdr l)

define      is-in?
value       (lambda (x l) (if (atom l) false (if (= x (car l))
             true (is-in? x (cdr l)))))

define (union x y) [set-theoretic union of two sets x y]
   if atom x y
   if (is-in? car x y) (union cdr x y)
   cons car x (union cdr x y)

define      union
value       (lambda (x y) (if (atom x) y (if (is-in? (car x) y
            ) (union (cdr x) y) (cons (car x) (union (cdr x) y
            )))))

define (is-bit-string? x) [is x a list of 0's and 1's?]
   if = x nil   true
   if atom x    false
   if = 0 car x (is-bit-string? cdr x)
   if = 1 car x (is-bit-string? cdr x)
   false

define      is-bit-string?
value       (lambda (x) (if (= x nil) true (if (atom x) false
            (if (= 0 (car x)) (is-bit-string? (cdr x)) (if (=
            1 (car x)) (is-bit-string? (cdr x)) false)))))

define C [test computer---real thing is eval read-exp]
   let (loop x y) [xx yy zz 01 ===> xyz]
     if = x y cons x (loop read-bit read-bit)
     nil
   (loop read-bit read-bit)

define      C
value       ((' (lambda (loop) (loop (read-bit) (read-bit))))
            (' (lambda (x y) (if (= x y) (cons x (loop (read-b
            it) (read-bit))) nil))))

[
 The hypothesis that
 the real number r is not Chaitin random
 means that there is a K such that
 for infinitely many values of n
   H(r_n) < n + K,
 where r_n is the first n bits of r.

 For this example, let's suppose that K = 5.
]

define K 5

define      K
value       5

[
 Our proof depends on the fact that there is a c such that
 the probability that an n-bit string s has
  H(s) < n + K
 is less than 2^{-H(n) + K + c}.
]

[
 Now let's do stage N of A_n = n-bit strings s with H(s) < |s| + K.
 At stage N we look at all programs p less than n + K bits in size for time up to N.
]

define (quasi-compressible N n)
   (look-at nil)

define      quasi-compressible
value       (lambda (N n) (look-at nil))

[this routine has free parameters N, n, K, C]

define (look-at p) [produces quasi-compressible strings of length n]
   if = length p + n K [p too long?]
      nil
   let v try N C ['eval read-exp] p
   if = success car v
      let w cadr v
      if (is-bit-string? w)
         if = n length w
            cons w nil
         nil
      nil
   [
    Also works with append below instead of union
    because duplicates are removed later by (process interval).
   ]
   (union (look-at append p cons 0 nil)
          (look-at append p cons 1 nil))

define      look-at
value       (lambda (p) (if (= (length p) (+ n K)) nil ((' (la
            mbda (v) (if (= success (car v)) ((' (lambda (w) (
            if (is-bit-string? w) (if (= n (length w)) (cons w
             nil) nil) nil))) (car (cdr v))) (union (look-at (
            append p (cons 0 nil))) (look-at (append p (cons 1
             nil))))))) (try N C p))))

[
 List of intervals in covering so far.
 used to avoid overlapping intervals in covering.

 This is easy to do because here because
 all intervals are the same length.
]
define intervals ()

define      intervals
value       ()

define (process-all x) [process list of intervals x]
   if atom x  intervals
   let intervals append (process car x) intervals
   (process-all cdr x)

define      process-all
value       (lambda (x) (if (atom x) intervals ((' (lambda (in
            tervals) (process-all (cdr x)))) (append (process
            (car x)) intervals))))

define (process interval) [process individual interval]
   if (is-in? interval intervals)
      [then don't need to repeat it]
      nil
      [else interval is fine as is]
      cons display interval nil

define      process
value       (lambda (interval) (if (is-in? interval intervals)
             nil (cons (display interval) nil)))

[
 Put it all together---Here is cover A_n
 covering all reals r having n-bit prefix r_n
 with H(r_n) < n + K.

 And we have measure \mu A_n <= 2^{-H(n)+K+c}
 so that Sum_n \mu A_n <= \Omega 2^{K+c} <= 2^{K+c} < infinity .

 Hence a real r which is not strongly Chaitin random
 will be in infinitely many of the A_n,
 which have convergent total measure,
 and hence will not be Solovay random.
]
define (A n)
   let intervals nil
   let (stage N)
      if = N 7 stop! [to stop test run---remove if real thing]
      let quasi-compressible-strings (quasi-compressible N n)
      let intervals (process-all quasi-compressible-strings)
      (stage + 1 N)
   [go!!!!!]
   (stage 0)

define      A
value       (lambda (n) ((' (lambda (intervals) ((' (lambda (s
            tage) (stage 0))) (' (lambda (N) (if (= N 7) stop!
             ((' (lambda (quasi-compressible-strings) ((' (lam
            bda (intervals) (stage (+ 1 N)))) (process-all qua
            si-compressible-strings)))) (quasi-compressible N
            n)))))))) nil))

[n = 2, i.e., quasi-compressible 2-bit strings]
(A 2)

expression  (A 2)
display     (0 0)
display     (0 1)
display     (1 0)
display     (1 1)
value       stop!


#' IREQ
#'
#' Calculation of Required Clothing Insulation IREQ.
#'
#' @param t numeric    Air temperature in degC.
#' @param rh numeric   Relative humidity in percentage.
#' @param wind numeric Windspeed in meter per second.
#' @param tr numeric Mean radiant temperature in degC.
#' @param M numeric    Metabolic work in Watt per mq of the subject.
#' @param W   numeric  Mechanical work rate in Watt per mq of the subject.
#' @param clo numeric  Available basic clothing insulation in clo.
#' @param p  numeric   Air permeability of clothing ensemble lt on meters per second.
#' @param w  numeric   Walking speed in meters per second.
#' @param param  character Parameter of IREQ ISO 11079: "IREQ_neutral","IREQ_min","ICL_neutral","ICL_min","DLE_neutral","DLE_min","ICL_ISO11079"
#' @return IREQ value
#' @references ISO 11079, 2007-12-15, ERGONOMICS OF THE THERMAL ENVIRONMENT - DETERMINATION AND INTERPRETATION OF COLD STRESS WHEN USING REQUIRED CLOTHING INSULATION (IREQ) AND LOCAL COOLING EFFECTS.
#' @author    Istituto per la Bioeconomia CNR Firenze Italy  Alfonso Crisci \email{alfonso.crisci@@ibe.cnr.it}
#' @keywords IREQ
#' @export
#'
#'
#'
#'

IREQ=function(t,rh,wind,tr,M,W,clo,p,w,param="IREQ_neutral") {
                         if ( length(param)==1) {param=rep(param,length(t))}
                         ct$assign("t", as.array(t))
                         ct$assign("rh", as.array(rh))
                         ct$assign("v", as.array(wind))
                         ct$assign("Tr", as.array(tr))
                         ct$assign("M", as.array(M))
                         ct$assign("W", as.array(W))
                         ct$assign("Icl", as.array(clo))
                         ct$assign("p", as.array(p))
                         ct$assign("w", as.array(w))
                         ct$assign("param", as.array(param))
                         ct$eval("var res=[]; for(var i=0, len=t.length; i < len; i++){ res[i]=IREQ(t[i],rh[i],v[i],Tr[i],M[i],W[i],Icl[i],p[i],w[i],param[i])};")
                         res=ct$get("res")
                         return(ifelse(res==9999,NA,res))
}


################################################################################
#
# Simplified script for example analysis for "A Simple Way to
# Incorporate Uncertainty and Risk into Forest Harvest Scheduling".
#
# Andrew Robinson  22 September 2015
#
################################################################################

## Set run parameters

set.seed(1)

n.coupes <- 1000

nsim <- 10000

################################################################################

## Call libraries

library(ggplot2)
library(gridExtra)
library(splines)
library(gamlss)
library(lpSolveAPI)
library(parallel)

################################################################################

## Generate fake albeit realistic data

coupes <-
    data.frame(HARVYEAR = sample(
                   2003:2013,
                   size = n.coupes,
                   replace = TRUE),
               ForestQualityClass = sample(
                   c("MAT_hq","MAT_lq","MIX_hq","MIX_lq"),
                   size = n.coupes,
                   replace = TRUE),
               grossArea = rweibull(n = n.coupes,
                   shape = 1.74,
                   scale = 70))

parameters <-
    data.frame(ForestQualityClass = c("MAT_hq","MAT_lq","MIX_hq","MIX_lq"),
               x.shapes = c(1.70, 1.45, 1.7, 0.90),
               x.scales = c(6800, 2400, 6000, 2600),
               recovery = c(0.5, 0.3, 0.5, 0.3),
               y.scales = c(1, 1.5, 2, 1.2))

coupes <- merge(coupes, parameters, all.x = TRUE)

coupes$grossHQSLest <- rweibull(n = nrow(coupes),
                                shape = coupes$x.shapes,
                                scale = coupes$x.scales)

coupes$SalesHQSL.s <-
    with(coupes, 200 * y.scales)
coupes$SalesHQSL.a <-
    with(coupes, grossHQSLest * recovery / SalesHQSL.s)

coupes$SalesHQSL <- rgamma(n = nrow(coupes),
                           shape = coupes$SalesHQSL.a,
                           scale = coupes$SalesHQSL.s)

pdf(file = "Figure 1 ex.pdf", height = 4, width = 4)
qplot(x = grossHQSLest / 1000,
      y = SalesHQSL / 1000,
      geom = c("point","smooth"),
      se = FALSE,
      xlab = expression(paste("Assessed HQ Sawlog ('000 ", m^3, ")")),
      ylab = expression(paste("Actual HQ Sawlog ('000 ", m^3, ")")),
      method = "lm",
      alpha = I(0.4),
      data = coupes) +
  geom_abline(aes(slope = 1, intercept = 0), linetype = 4, colour = "darkgrey") +
  geom_smooth(se = FALSE, linetype = 2)
dev.off()

str(coupes)

table(coupes$HARVYEAR)
table(coupes$ForestQualityClass)

coupes$fit <- TRUE
coupes$fit[coupes$HARVYEAR > 2007] <- FALSE

################################################################################
################################################################################
################################################################################

## Generate helper functions

summary.lpExtPtr <- function(x, target, ...) {
  cut.me <- get.variables(x)
  num.coupes.cut <- sum(cut.me)
  area.cut <- sum(cut.me * target$grossArea)
  volume.cut <- sum(cut.me * target$SalesHQSL)
  volume.cut.hat <- sum(cut.me * target$mu.hat)
  out <- list(cut.me = cut.me,
              num.coupes.cut = num.coupes.cut,
              area.cut = area.cut,
              volume.cut = volume.cut,
              volume.cut.hat = volume.cut.hat)
  class(out) <- "summary.lpExtPtr"
  return(out)
}

simulate.summary.lpExtPtr <-
  function(object, nsim = 1, seed = NULL, ...) {
    sim.volume <-
      lapply(1:nsim,
             function(x)
             with(to.cut[object$cut.me == 1, ],
                  sum(rGA(sum(object$cut.me),
                          mu = mu.hat,
                          sigma = sigma.hat))))
    return(unlist(sim.volume))
  }

################################################################################
################################################################################
################################################################################

## Fit and compare some models

test.0 <- gamlss(SalesHQSL ~ log(grossHQSLest),
                 sigma.formula = ~ 1,
                 sigma.link = "log",
                 family = GA(),
                 data = coupes)

test.1 <- gamlss(SalesHQSL ~ log(grossHQSLest),
                 sigma.formula = ~ log(grossHQSLest),
                 sigma.link = "log",
                 family = GA(),
                 data = coupes)

LR.test(test.0, test.1)

summary(test.1)

test.2 <- gamlss(SalesHQSL ~ pb(log(grossHQSLest)),
                 sigma.formula = ~ 1,
                 sigma.link = "log",
                 family = GA(),
                 data = coupes)

LR.test(test.0, test.2)

test.3 <- gamlss(SalesHQSL ~ log(grossHQSLest) * ForestQualityClass,
                 sigma.formula = ~ 1,
                 sigma.link = "log",
                 family = GA(),
                 data = coupes)

summary(test.3)
LR.test(test.0, test.3)

test.5 <- gamlss(SalesHQSL ~ poly(log(grossHQSLest), 2) * ForestQualityClass,
                 sigma.formula = ~ ForestQualityClass,
                 sigma.link = "log",
                 family = GA(),
                 data = coupes)

summary(test.5)
LR.test(test.0, test.5)

## Modest difference.

trajectory.5 <-
  with(coupes,
       expand.grid(grossHQSLest =
                   seq(from = min(grossHQSLest),
                       to = max(grossHQSLest),
                       length.out = 100),
                   ForestQualityClass = levels(coupes$ForestQualityClass)))

new.dist.5 <- predictAll(test.5, newdata = trajectory.5)

trajectory.5$mu <- new.dist.5$mu
trajectory.5$sigma <- new.dist.5$sigma
trajectory.5$upper.2 <- with(new.dist.5, qGA(0.95, mu = mu, sigma = sigma))
trajectory.5$lower.2 <- with(new.dist.5, qGA(0.05, mu = mu, sigma = sigma))
trajectory.5$upper.1 <- with(new.dist.5, qGA(0.67, mu = mu, sigma = sigma))
trajectory.5$lower.1 <- with(new.dist.5, qGA(0.33, mu = mu, sigma = sigma))

# setEPS()
# postscript("model.eps", height=6, width=6)
pdf("Figure 2 ex.pdf", height=6, width=6)

p.5 <-
  ggplot(coupes,
         aes(x = grossHQSLest / 1000,
             y = SalesHQSL / 1000)) +
#  geom_point(aes(shape = fit)) +
  geom_point(alpha=0.4) +
#  scale_colour_discrete(name = "Source", labels = c("Test","Fit")) +
  facet_wrap(~ ForestQualityClass) +
  xlab(expression(paste("Assessed HQ Sawlog ('000 ", m^3, ")"))) +
  ylab(expression(paste("Actual HQ Sawlog ('000 ", m^3, ")"))) +
#  geom_smooth(aes(colour = fit),
#              method = "lm", formula = y ~ pb(x), se = FALSE) +
  geom_line(aes(y = mu / 1000, x = grossHQSLest / 1000),
            data = trajectory.5) +
  geom_line(aes(y = lower.2 / 1000, x = grossHQSLest / 1000),
            linetype = "dashed", data = trajectory.5) +
  geom_line(aes(y = upper.2 / 1000, x = grossHQSLest / 1000),
            linetype = "dashed", data = trajectory.5) +
  geom_line(aes(y = lower.1 / 1000, x = grossHQSLest / 1000),
            linetype = "dotted", data = trajectory.5) +
  geom_line(aes(y = upper.1 / 1000, x = grossHQSLest / 1000),
            linetype = "dotted", data = trajectory.5) +
  ylim(c(0,10))
plot(p.5)

dev.off()

################################################################################
################################################################################

# Settle on model 5.  Obtain predictions for withheld stands

to.cut <-
  droplevels(subset(coupes, !fit &
                    ForestQualityClass %in%
                    levels(coupes$ForestQualityClass)))

to.cut.hat.cut <- predictAll(test.5, newdata = to.cut)

to.cut$mu.hat <- to.cut.hat.cut$mu
to.cut$sigma.hat <- to.cut.hat.cut$sigma

par(mfrow=c(1,3))
plot(SalesHQSL ~ mu.hat, data = to.cut)
plot(SalesHQSL ~ grossHQSLest, data = to.cut)
plot(mu.hat ~ grossHQSLest, data = to.cut)

################################################################################
################################################################################

### Now use LP to optimize. This script portion benefits from prior
### work at
### http://fishyoperations.com/r/linear-programming-in-r-an-lpsolveapi-example
### see also
### http://www.r-bloggers.com/linear-programming-in-r-an-lpsolveapi-example/

## Here's an example that cuts le 15 stands, total area le 1000 ha,
## one year, max volume.

## ops creates the annual constraints

ops <- data.frame(year = c('y1'),
                  area = c(1000),
                  equip = c(30))

lpmodel.harvest <- make.lp(2*NROW(ops) + NROW(to.cut), NROW(ops) * NROW(to.cut))

column <- 0
row <- 0

# build the model column by column

for(wg in ops$year){
  row <- row + 1
  for(coupe in seq(1, NROW(to.cut))){
    column <- column + 1
# this takes the arguments 'column','values' & 'indices' (as in where
# these values should be placed in the column)
    set.column(lpmodel.harvest,
               column,
               c(1, to.cut[coupe,'grossArea'], 1),
               indices = c(row, NROW(ops) + row, NROW(ops)*2 + coupe))
  }}

# set rhs weight constraints - first triplet
set.constr.value(lpmodel.harvest,                    # The lp model
                 rhs = ops$equip,                    # The values
                 constraints = seq(1, NROW(ops)))    # Where to put them

# set rhs volume constraints - second triplet
set.constr.value(lpmodel.harvest,
                 rhs = ops$area,
                 constraints = seq(NROW(ops)+1, NROW(ops)*2))

# set rhs availability constraints - last quadruplet

set.constr.value(lpmodel.harvest,
                 rhs = rep(1, NROW(to.cut)),
                 constraints = seq(NROW(ops)*2+1, NROW(ops)*2+NROW(to.cut)))

# set objective coefficients - here the simulated average

set.objfn(lpmodel.harvest, rep(to.cut$mu.hat, NROW(ops)))

# set objective direction

lp.control(lpmodel.harvest, sense = 'max')

# Ensure integers - works!

get.type(lpmodel.harvest)
set.type(lpmodel.harvest, 1:length(get.type(lpmodel.harvest)), "integer")

# solve the model; if this returns 0 then an optimal solution has been found

solve(lpmodel.harvest)

# this returns the proposed solution

get.objective(lpmodel.harvest)

# Coupe choices

(base <- summary(lpmodel.harvest, target = to.cut))

par(mfrow=c(1,3))
plot(SalesHQSL ~ mu.hat, data = to.cut,
     pch=c(1,19)[base$cut.me + 1])
plot(SalesHQSL ~ grossHQSLest, data = to.cut,
     pch=c(1,19)[base$cut.me + 1])
plot(mu.hat ~ grossHQSLest, data = to.cut,
     pch=c(1,19)[base$cut.me + 1])

#########################################################################################
#
# FTM I have a stand prescription.  I should generate a bunch of random volumes
# corresponding to these predictions.
#
#########################################################################################

base.sim.volume <-
  sapply(1:10000,
         function(x)
         with(to.cut[base$cut.me == 1,],
              sum(rGA(sum(base$cut.me), # if n = 1 then randoms are correlated!
                      mu = mu.hat,
                      sigma = sigma.hat)))) / 1000

## Graphical summary.

par(mfrow=c(1,2), mar=c(5,4,3,2), las=1)
plot(density(base.sim.volume), xlim = c(50,300), main="Distribution",
     xlab = expression(paste("Assessed Sawlog ('000 ", m^3, ")")))
abline(v = base$volume.cut/1000, lty = 2)
abline(v = base$volume.cut.hat/1000)
abline(v = quantile(base.sim.volume, p = c(0.05, 0.95)), col = "darkgrey")
#qqnorm(base.sim.volume); qqline(base.sim.volume)
plot(ecdf(base.sim.volume), xlim = c(0,500), main="CDF",
     xlab = expression(paste("Assessed Sawlog ('000 ", m^3, ")")))
abline(v = base$volume.cut/1000, lty = 2)
abline(v = base$volume.cut.hat/1000)
abline(v = quantile(base.sim.volume, p = c(0.05, 0.95)), col = "darkgrey")

## This provides us with, as hoped for, probabilistic information
## about the expected return.

## For example, what is the mean expected return?

mean(base.sim.volume)

## What is the probability of achieving the mean objective?

mean(base.sim.volume > base$volume.cut.hat / 1000)

## What is the 90% prediction interval?

quantile(base.sim.volume, p = c(0.05, 0.95))

## How can this be related back to headroom?  10%? 20%? 30%?

mean(base.sim.volume > base$volume.cut.hat * 0.9 / 1000) # 10% HR
mean(base.sim.volume > base$volume.cut.hat * 0.8 / 1000) # 20% HR
mean(base.sim.volume > base$volume.cut.hat * 0.7 / 1000) # 30% HR

## We could compare this with a clearly inadequate correction, such as
## a straight ratio, somehow.

## What is the proportion reduced relative to expectations?

(base$volume.cut.hat - base$volume.cut) / base$volume.cut.hat

################################################################################
################################################################################

# Now penalize optimization by uncertainty.  Start simply: use the 0.1
# quantile as the objective, and analyze as above.

set.objfn(lpmodel.harvest,
          rep(with(to.cut, qGA(0.1, mu = mu.hat, sigma = sigma.hat)),
              NROW(ops)))

solve(lpmodel.harvest)

get.objective(lpmodel.harvest)

# Coupe choices

(q10 <- summary(lpmodel.harvest, target = to.cut))

cor(base$cut.me, q10$cut.me)

q10.sim.volume <-
  sapply(1:10000,
         function(x)
         with(to.cut[q10$cut.me == 1,],
              sum(rGA(sum(q10$cut.me), # if n = 1 then randoms are correlated!
                      mu = mu.hat,
                      sigma = sigma.hat)))) / 1000

## Graphical summary.

par(mfrow=c(1,2), mar=c(5,4,3,2), las=1)
plot(density(q10.sim.volume), xlim = c(0,100), main="Distribution",
     xlab = expression(paste("Assessed Sawlog ('000 ", m^3, ")")))
lines(density(base.sim.volume), col = "darkgrey")
abline(v = q10$volume.cut/1000, lty = 2)
abline(v = q10$volume.cut.hat/1000)
abline(v = quantile(q10.sim.volume, p = c(0.05, 0.95)))
#qqnorm(base.sim.volume); qqline(base.sim.volume)
plot(ecdf(q10.sim.volume), xlim = c(0,100), main="CDF",
     xlab = expression(paste("Assessed Sawlog ('000 ", m^3, ")")))
lines(ecdf(base.sim.volume), col = "darkgrey")
abline(v = q10$volume.cut/1000, lty = 2)
abline(v = q10$volume.cut.hat/1000)
abline(v = quantile(q10.sim.volume, p = c(0.05, 0.95)))

## This provides us with, as hoped for, probabilistic information
## about the expected return.  For example, what is the probability of
## achieving the implied objective?

mean(q10.sim.volume > q10$volume.cut / 1000)

## How can this be related back to headroom?  10%? 20%? 30%?

mean(q10.sim.volume > q10$volume.cut * 0.9 / 1000) # 10% HR
mean(q10.sim.volume > q10$volume.cut * 0.8 / 1000) # 20% HR
mean(q10.sim.volume > q10$volume.cut * 0.7 / 1000) # 30% HR

quantile(q10.sim.volume, p = c(0.05, 0.95))

#### Compare with Base in Figure 3 (below)

################################################################################

## Try to minimize variance given constraints on volume

sum(to.cut$grossArea)

sum(to.cut$mu.hat)

to.cut$variance <- with(to.cut, mu.hat^2 * sigma.hat^2)

ops <- data.frame(year = c('y1'),
                  area = c(1000),
                  cut.out = c(17100))

# create an LP model

lpmodel.harvest <- make.lp(2*NROW(ops) + NROW(to.cut), NROW(ops) * NROW(to.cut))

# I used this to keep count within the loops, I admit that this could
# be done a lot neater

column <- 0
row <- 0

# build the model column per column

for(wg in ops$year){
  row <- row + 1
  for(coupe in seq(1, NROW(to.cut))){
    column <- column + 1
# this takes the arguments 'column','values' & 'indices' (as in where
# these values should be placed in the column)
    set.column(lpmodel.harvest,
               column,
               c(to.cut[coupe, 'mu.hat'], to.cut[coupe, 'grossArea'], 1),
               indices = c(row, NROW(ops) + row, NROW(ops)*2 + coupe))
  }}

# set rhs equipment constraints - first triplet
set.constr.value(lpmodel.harvest,                    # The lp model
                 rhs = ops$cut.out,                  # The values
                 constraints = seq(1, NROW(ops)))    # Where to put them

# set rhs area constraints - second triplet
set.constr.value(lpmodel.harvest,
                 rhs = ops$area,
                 constraints = seq(NROW(ops)+1, NROW(ops)*2))

# set rhs availability constraints - last quadruplet

set.constr.value(lpmodel.harvest,
                 rhs = rep(1, NROW(to.cut)),
                 constraints = seq(NROW(ops)*2+1, NROW(ops)*2+NROW(to.cut)))

set.constr.type(lpmodel.harvest,
                c(rep(">=", NROW(ops)),
                  rep("<=", NROW(ops)),
                  rep("<=", NROW(to.cut))))

# set objective coefficients - here the simulated average

set.objfn(lpmodel.harvest, rep(to.cut$variance, NROW(ops)))

# set objective direction

lp.control(lpmodel.harvest, sense='min')

# Ensure integers - works!

set.type(lpmodel.harvest, 1:length(get.type(lpmodel.harvest)), "integer")

# I in order to be able to visually check the model, I find it useful
# to write the model to a text file

write.lp(lpmodel.harvest, 'harvest.lp', type = 'lp')

# solve the model, if this return 0 an optimal solution is found

solve(lpmodel.harvest)

# this returns the proposed solution

get.objective(lpmodel.harvest)

# Coupe choices

(port.1 <- summary(lpmodel.harvest, target = to.cut))

out.1 <- simulate(port.1, nsim = 1000)

plot(density(out.1))

#########################################################################################
#########################################################################################
#########################################################################################
#########################################################################################

## Write a portfolio function

portfolio <- function(min.vol, target = to.cut, area = 2000) {
  ops <- data.frame(year = c('y1'),
                    area = c(area),
                    cut.out = c(min.vol))
  lpmodel.harvest <- make.lp(2*NROW(ops) + NROW(to.cut), NROW(ops) * NROW(to.cut))
  column <- 0
  row <- 0
  for(wg in ops$year){
    row <- row + 1
    for(coupe in seq(1, NROW(to.cut))){
      column <- column + 1
      set.column(lpmodel.harvest,
                 column,
                 c(to.cut[coupe, 'mu.hat'], to.cut[coupe, 'grossArea'], 1),
                 indices = c(row, NROW(ops) + row, NROW(ops)*2 + coupe))
    }}
  set.constr.value(lpmodel.harvest,                    # The lp model
                   rhs = ops$cut.out,                  # The values
                   constraints = seq(1, NROW(ops)))    # Where to put them
  set.constr.value(lpmodel.harvest,
                   rhs = ops$area,
                   constraints = seq(NROW(ops)+1, NROW(ops)*2))
  set.constr.value(lpmodel.harvest,
                   rhs = rep(1, NROW(to.cut)),
                   constraints = seq(NROW(ops)*2+1, NROW(ops)*2+NROW(to.cut)))
  set.constr.type(lpmodel.harvest,
                  c(rep(">=", NROW(ops)),
                    rep("<=", NROW(ops)),
                    rep("<=", NROW(to.cut))))
  set.objfn(lpmodel.harvest, rep(to.cut$variance, NROW(ops)))
  lp.control(lpmodel.harvest, sense='min')
  set.type(lpmodel.harvest, 1:length(get.type(lpmodel.harvest)), "integer")
  solve(lpmodel.harvest)
  return(summary(lpmodel.harvest, target = target))
}

################################################################################

minima <- (15:25)*1000

returns <- mclapply(minima, portfolio, mc.cores = 4)

lapply(returns, function(x) sum(x$cut.me))

sim.returns <- mclapply(returns, simulate, nsim = nsim, mc.cores = 4)

simexp <- data.frame(volume.cut = do.call(c, sim.returns),
                     minima = rep(minima, each = nsim))

ggplot(simexp,
       aes(x = factor(minima), y = volume.cut))  +
  geom_violin() +
  xlab(expression(paste("Minimum prescribed cut (", m^3, ")"))) +
  ylab(expression(paste("Achieved cut (", m^3, ")"))) +
  geom_point(aes(x = factor(minima), y = minima))

ggplot(simexp,
       aes(x = factor(minima), y = volume.cut))  +
  geom_boxplot() +
  xlab(expression(paste("Minimum prescribed cut (", m^3, ")"))) +
  ylab(expression(paste("Achieved cut (", m^3, ")")))

lapply(sim.returns, function (x) (var(x)) / mean(x))

cv <- function (x) (sd(x)) / mean(x)

sapply(sim.returns, cv)

sapply(sim.returns, sd)

sapply(sim.returns, mean)

sim.results <- data.frame(minima = minima,
                          mean = sapply(sim.returns, mean),
                          sd = sapply(sim.returns, sd),
                          cv = sapply(sim.returns, cv))

write.csv(sim.results, file = "simresults.csv", row.names = FALSE)

################################################################################

## What if we now try to achieve the same volume with min variance?

cf.base <- portfolio(mean(base.sim.volume)*1000)

cf.sim.volume <-
  sapply(1:10000,
         function(x)
         with(to.cut[cf.base$cut.me == 1,],
              sum(rGA(sum(cf.base$cut.me), # if n = 1 then randoms are correlated!
                      mu = mu.hat,
                      sigma = sigma.hat)))) / 1000

## Graphical summary.

par(mfrow=c(1,2), mar=c(5,4,3,2), las=1)
plot(density(base.sim.volume), xlim = c(20, 90), main="Distribution",
     xlab = expression(paste("Assessed Sawlog ('000 ", m^3, ")")))
lines(density(cf.sim.volume), col = "darkgrey")
abline(v = base$volume.cut/1000, lty = 2)
abline(v = base$volume.cut.hat/1000)
abline(v = quantile(base.sim.volume, p = c(0.05, 0.95)), lty = 3)
#qqnorm(base.sim.volume); qqline(base.sim.volume)
plot(ecdf(base.sim.volume), xlim = c(20, 90), main="CDF",
     xlab = expression(paste("Assessed Sawlog ('000 ", m^3, ")")))
lines(ecdf(q10.sim.volume), col = "grey")
lines(ecdf(cf.sim.volume), col = "darkgrey")
#abline(v = base$volume.cut/1000, lty = 2)
#abline(v = base$volume.cut.hat/1000)
#abline(v = quantile(base.sim.volume, p = c(0.05, 0.95)))

qq.base <- qqnorm(base.sim.volume, plot.it = FALSE)
qq.q10 <- qqnorm(q10.sim.volume, plot.it = FALSE)
qq.cf <- qqnorm(cf.sim.volume, plot.it = FALSE)

grain <- 1000

x.range <- (min(base.sim.volume) * 10):(max(base.sim.volume) * 10) / 10

flip <- function(object) return(list(x = object$y, y = object$x))

pdf(file = "Figure 3 ex.pdf", height = 3.5, width = 8)

par(mfrow=c(1,2), mar=c(5,4,1,2), las=1)
plot(density(base.sim.volume), main = "",
     xlab = expression(paste("Assessed HQ Sawlog ('000 ", m^3, ")")))
#lines(density(q10.sim.volume), col = "darkgrey")
abline(v = base$volume.cut/1000, lty = 2)
abline(v = base$volume.cut.hat/1000)
abline(v = quantile(base.sim.volume, p = c(0.05, 0.95)), lty = 3)

plot(with(flip(qq.base), predict(smooth.spline(x, y), x = x.range)),
     type = "l", axes = FALSE,
     ylim = c(-4, 4),
     xlab = expression(paste("Assessed HQ Sawlog ('000 ", m^3, ")")),
     ylab = expression(paste(F[x](X))))
box()
axis(1)
axis(2, at = c(-4:4), label = c("0", formatC(pnorm(-3:4))), cex.axis=0.8)
lines(with(flip(qq.q10), predict(smooth.spline(x, y), x = x.range)),
      type="l", lty = 2)
lines(with(flip(qq.cf), predict(smooth.spline(x, y), x = x.range)),
      type="l", lty = 3)
legend("bottomright", lty = 1:3, bty = "n", cex = 0.9,
       legend=c("Maximize Mean","Maximize Q10","Minimize Variance"))

dev.off()


model_leafnumber <- function (deltaTT = 23.5895677277199,
         phyllochron_t1 = 0.0,
         hasFlagLeafLiguleAppeared = 0,
         leafNumber_t1 = 0.0,
         phase = 1.0){
    #'- Name: LeafNumber -Version: 1.0, -Time step: 1
    #'- Description:
    #'            * Title: CalculateLeafNumber Model
    #'            * Author: Pierre MARTRE
    #'            * Reference: Modeling development phase in the
    #'                Wheat Simulation Model SiriusQuality.
    #'                See documentation at http://www1.clermont.inra.fr/siriusquality/?page_id=427
    #'            * Institution: INRA Montpellier
    #'            * Abstract: calculate leaf number. LeafNumber increase is caped at one more leaf per day
    #'- inputs:
    #'            * name: deltaTT
    #'                          ** description : daily delta TT
    #'                          ** variablecategory : auxiliary
    #'                          ** datatype : DOUBLE
    #'                          ** min : -20
    #'                          ** max : 100
    #'                          ** default : 23.5895677277199
    #'                          ** unit : °C d
    #'                          ** inputtype : variable
    #'            * name: phyllochron_t1
    #'                          ** description : phyllochron
    #'                          ** variablecategory : state
    #'                          ** inputtype : variable
    #'                          ** datatype : DOUBLE
    #'                          ** min : 0
    #'                          ** max : 1000
    #'                          ** default : 0
    #'                          ** unit : °C d leaf-1
    #'            * name: hasFlagLeafLiguleAppeared
    #'                          ** description : true if flag leaf has appeared (leafnumber reached finalLeafNumber)
    #'                          ** variablecategory : state
    #'                          ** datatype : INT
    #'                          ** min : 0
    #'                          ** max : 1
    #'                          ** default : 0
    #'                          ** unit :
    #'                          ** inputtype : variable
    #'            * name: leafNumber_t1
    #'                          ** description :  Actual number of phytomers
    #'                          ** variablecategory : state
    #'                          ** datatype : DOUBLE
    #'                          ** min : 0
    #'                          ** max : 25
    #'                          ** default : 0
    #'                          ** unit : leaf
    #'                          ** inputtype : variable
    #'            * name: phase
    #'                          ** description :  the name of the phase
    #'                          ** variablecategory : state
    #'                          ** datatype : DOUBLE
    #'                          ** min : 0
    #'                          ** max : 7
    #'                          ** default : 1
    #'                          ** unit :
    #'                          ** uri : some url
    #'                          ** inputtype : variable
    #'- outputs:
    #'            * name: leafNumber
    #'                          ** description : Actual number of phytomers
    #'                          ** variablecategory : state
    #'                          ** datatype : DOUBLE
    #'                          ** min : 0
    #'                          ** max : 10000
    #'                          ** unit : leaf
    #'                          ** uri : some url
    leafNumber <- leafNumber_t1
    if (phase >= 1.0 && phase < 4.0)
    {
        if (hasFlagLeafLiguleAppeared == 0)
        {
            if (phyllochron_t1 == 0.0)
            {
                phyllochron_ <- 0.0000001
            }
            else
            {
                phyllochron_ <- phyllochron_t1
            }
            leafNumber <- leafNumber_t1 + min(deltaTT / phyllochron_, 0.999)
        }
    }
    return (list('leafNumber' = leafNumber))
}


# MIT License
# Copyright (c) 2020 Abhinav Prakash, Vijay Panchang, Yu Ding, and Lewis Ntaimo

# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:

# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.

# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.

source("function.r")
inputfile = "hackensack.csv"
outputfile = "Results/hackensack_constrained_MCMC_"
outputplot = "Hackensack River"
data_col = 3
year_col = 1
ReferenceYear = 2018
ReturnPeriod = 100
burn_in = 500
CSVFile = read.csv(file = inputfile, header = T)
Data = CSVFile[,data_col]
Year = CSVFile[,year_col]
time_index = time.index(Year,ReferenceYear)
parameters = draw.MCMC.samples(Data,time_index,InitialValues = c(50,1,20,0.8),LocationInterceptPrior = c(0,200),LocationSlopePrior = c(-5,5),ScalePrior = c(-50,50),ShapePrior = c(-1,1),ProposalWidth = c(2,0.15,1,0.05),n_samples=10000)
parameters = parameters[-c(1:burn_in),]
mu_post = parameters[,1]
muslope_post = parameters[,2]
scale_post = parameters[,3]
shape_post = parameters[,4]

### Expected and MAP Values of the parameters and the return event
bw = c(2,0.02,0.75,0.020,10)
d_mu = density(mu_post, bw = bw[1])
d_muslope = density(muslope_post, bw = bw[2])
d_scale = density(scale_post, bw= bw[3])
d_shape = density(shape_post, bw = bw[4])
mu_exp = mean(mu_post)
mu_map = d_mu$x[which.max(d_mu$y)]
muslope_exp = mean(muslope_post)
muslope_map = d_muslope$x[which.max(d_muslope$y)]
scale_exp = mean(scale_post)
scale_map = d_scale$x[which.max(d_scale$y)]
shape_exp =mean(shape_post)
shape_map = d_shape$x[which.max(d_shape$y)]
NLLH_exp = likelihood.val(Data,time_index,mu_exp,muslope_exp,scale_exp,shape_exp)
NLLH_map = likelihood.val(Data,time_index,mu_map,muslope_map,scale_map,shape_map)
cat("Expected Values of the Parameters:",'\n')
cat("Location Intercept:", mu_exp, '\n')
cat("Location Slope:", muslope_exp, '\n')
cat("Scale:",scale_exp, '\n')
cat("Shape:", shape_exp, '\n')
cat("NLLH:", NLLH_exp, '\n')
cat('\n',"MAP values of the Parameters:",'\n')
cat("Location Intercept:", mu_map, '\n')
cat("Location Slope:", muslope_map, '\n')
cat("Scale:",scale_map, '\n')
cat("Shape:", shape_map, '\n')
cat("NLLH:", NLLH_map, '\n')


# 산술연산

tot1 <- -5
tot2 <- 10

tot1+tot2
tot1-tot2
tot1*tot2
tot1/tot2
tot1^tot2

# 마지막 것은 제곱 **로 해도 됨


subroutine crossutil(i,j,k,x,y,ntot,eps,collin)
implicit double precision(a-h,o-z)
dimension x(-3:ntot), y(-3:ntot)
dimension xt(3), yt(3)
logical collin

xt(1) = x(i)
yt(1) = y(i)
xt(2) = x(j)
yt(2) = y(j)
xt(3) = x(k)
yt(3) = y(k)

# Create indicator telling which of i, j, and k are ideal points.
# The point being added, i, is never ideal.
i1 = 0
if(j<=0) j1 = 1
else j1 = 0
if(k<=0) k1 = 1
else k1 = 0
ijk = i1*4+j1*2+k1

call cross(xt,yt,ijk,cprd)
collin = (abs(cprd) < eps)
return
end