# https://www.r-bloggers.com/2016/07/round-values-while-preserve-their-rounded-sum-in-r/
# E.G.
# > sum(c(0.333, 0.333, 0.334))
# [1] 1
# > sum(round(c(0.333, 0.333, 0.334), 2))
# [1] 0.99
# > sum(round_preserve_sum(c(0.333, 0.333, 0.334), 2))
# [1] 1.00
round_preserve_sum <- function(x, digits = 0) {
  up <- 10 ^ digits
  x <- x * up
  y <- floor(x)
  indices <- tail(order(x-y), round(sum(x)) - sum(y))
  y[indices] <- y[indices] + 1
  y / up
}

ca <-
function(N)
{
  n <- sum(sum(N))
  P <- (1/n) * N
  r <- rowSums(P)
  c <- colSums(P)
  R <- sweep(P, MARGIN = 1, 1/r, '*')
  C <- sweep(t(P), MARGIN = 1, 1/c, '*')
  scaleR <- 1/sqrt(r)
  scaleC <- 1/sqrt(c)
  S <- sweep(P - r %*% t(c), 1, scaleR, FUN = "*")
  S <- sweep(S, 2, scaleC, FUN = "*")

  dec <- svd(S)
  Phi <- sweep(dec$u, 1, scaleR, FUN="*")
  F <- sweep(Phi, 2, dec$d, FUN="*")
  Gam <- sweep(dec$v, 1, scaleC, FUN="*")
  G <- sweep(Gam, 2, dec$d, FUN="*")

  temp <- sweep(F^2, 1, r, FUN = "*")
  CTRI <- sweep(temp, 2, dec$d^2, FUN = "/")
  temp <- sweep(G^2, 1, c, FUN = "*")
  CTRJ <- sweep(temp, 2, dec$d^2, FUN = "/")

  temp <- temp <- F^2
  CORI <- sweep(temp, 1, rowSums(temp), FUN = "/")
  temp <- temp <- G^2
  CORJ <- sweep(temp, 1, rowSums(temp), FUN = "/")

  INRI <- rowSums(sweep(F^2, 1, r, FUN="*"))
  INRI <- INRI / sum(INRI)
  INRJ <- rowSums(sweep(G^2, 1, c, FUN="*"))
  INRJ <- INRJ / sum(INRJ)

  r <- list(P=P, r=r, c=c, F=F, G=G, Phi=Phi, Gam=Gam, sv=dec$d,
            CTRI=CTRI, CTRJ=CTRJ, CORI=CORI, CORJ=CORJ,
            INRI=INRI, INRJ=INRJ)
  class(r) <- "ca"
  return(r)
}

plot.ca <-
function(o, d1 = NULL, d2 = NULL)
{
  if(class(o) != "ca") {
    warning("Object is not of class 'ca'")
    UseMethod("plot")
    return(invisible(NULL))
  }
  if(is.null(d1)) d1<-1
  if(is.null(d2)) d2<-2

  # Part de l'inertie du plan factoriel d1-d2 expliquée par chaque profil ligne
  rowcon <- o$r * (o$F[,d1]^2 + o$F[,d2]^2) / (o$sv[d1]^2 + o$sv[d2]^2)
  rnames <- rownames(o$P)
  rnames[rowcon < 0.01] <- "."
  rsize <- log(1 + exp(1) * rowcon^0.3)
  rsize[rowcon < 0.01] <- 1

  # Part de l'inertie du plan factoriel d1-d2 expliquée par chaque profil colonne
  colcon <- o$c * (o$G[,d1]^2 + o$G[,d2]^2) / (o$sv[d1]^2 + o$sv[d2]^2)
  cnames <- colnames(o$P)
  cnames[colcon < 0.01] <- "."
  csize <- log(1 + exp(1) * colcon^0.3)
  csize[colcon < 0.01] <- 1

  plot(c(o$F[,d1], o$G[,d1]), c(o$F[,d2], o$G[,d2]), type = "n",
     xlab="", ylab="", asp = 1, xaxt = "n", yaxt = "n")
  text(c(o$F[,d1], o$G[,d1]), c(o$F[,d2], o$G[,d2]), c(rnames, cnames),
     adj = 0, cex = c(rsize, csize))
  points(0, 0, pch = 3)
}

tblTotalInr.ca <-
function(o)
{
  if(class(o) != "ca") {
    warning("Object is not of class 'ca'")
    return(invisible(NULL))
  }
  tblTotalInr <- cumsum(round_preserve_sum(1000 * (o$sv^2 / sum(o$sv^2))))
  tblTotalInr <- matrix(tblTotalInr, nrow=1)
  colnames(tblTotalInr) <- 1:length(o$sv)
  r <- list(tbl=tblTotalInr, title="Proportion de l'inertie cumulée exprimée \
par les axes factoriels successifs (en millièmes)")
  return(r)
}

printTbl <-
function(o)
{
  kbl(o$tbl, caption = o$title, booktabs = TRUE)
}

printTblWithHeaders <-
function(o)
{
  kbl(o$tbl, caption = o$title, booktabs = TRUE) %>%
    add_header_above(o$headers) %>%
    kable_styling(latex_options = c("striped", "scale_down"))
}

tblRowProfiles.ca <-
function(o, nbFact)
{
  if(class(o) != "ca") {
    warning("Object is not of class 'ca'")
    return(invisible(NULL))
  }
  tempFactData <- c()
  tempFactNames <- c()
  subHeaders <- c(" " = 3) # first empty subheader for row names, PDS and INR
  for (k in 1:nbFact) {
    tempFactData <- c(tempFactData,
                      round_preserve_sum(1000*o$F[,k]),
                      round_preserve_sum(1000*o$CORI[,k]),
                      round_preserve_sum(1000*o$CTRI[,k]))
    factIdStr <- paste('F#', k, sep="")
    tempFactNames <- c(tempFactNames, factIdStr, 'COR', 'CTR')
    subHeaders[[factIdStr]] <- 3
  }
  tblI <- matrix( c(round_preserve_sum(1000*o$r),
                    round_preserve_sum(1000*o$INRI),
                    tempFactData),
                  ncol = 2 + nbFact * 3)
  rownames(tblI) <- rownames(o$P)
  colnames(tblI) <- c('PDS', 'INR', tempFactNames)
  r <- list(tbl=tblI, title="Synthèse des profils lignes", headers=subHeaders)
}

tblColProfiles.ca <-
function(o, nbFact)
{
  if(class(o) != "ca") {
    warning("Object is not of class 'ca'")
    return(invisible(NULL))
  }
  tempFactData <- c()
  tempFactNames <- c()
  subHeaders <- c(" " = 3) # first empty subheader for row names, PDS and INR
  for (k in 1:nbFact) {
    tempFactData <- c(tempFactData,
                      round_preserve_sum(1000*o$G[,k]),
                      round_preserve_sum(1000*o$CORJ[,k]),
                      round_preserve_sum(1000*o$CTRJ[,k]))
    factIdStr <- paste('F#', k, sep="")
    tempFactNames <- c(tempFactNames, factIdStr, 'COR', 'CTR')
    subHeaders[[factIdStr]] <- 3
  }
  tblJ <- matrix( c(round_preserve_sum(1000*o$c),
                    round_preserve_sum(1000*o$INRJ),
                    tempFactData),
                  ncol = 2 + nbFact * 3)
  rownames(tblJ) <- colnames(o$P)
  colnames(tblJ) <- c('PDS', 'INR', tempFactNames)
  r <- list(tbl=tblJ, title="Synthèse des profils colonnes", headers=subHeaders)
}
