#' @name SORM
#' @title Second-Order Reliability Method (SORM)
#' @description
#' Second-Order Reliability Method (SORM) for the approximation
#' of failure probabilities in structural reliability analysis.
#'
#' The SORM extends the First-Order Reliability Method (FORM)
#' by incorporating second-order information of the limit-state function
#' at the design point. By accounting for local curvature effects,
#' SORM generally provides more accurate failure probability estimates
#' for nonlinear limit-state functions.
#'
#' The method evaluates the principal curvatures of the limit-state surface
#' in standard normal space and applies an asymptotic correction
#' to the FORM probability of failure.
#'
#' @param lsf objective function with limit state function in form of
#'   \code{function(x) \{ x[1] + x[2] + ... \}}.
#' @param lDistr list ob distribiutions regarding the distribution object of TesiproV
#' @param debug.level If 0 no additional info if 2 high output during calculation
#'
#' @return SORM returns an object containing the following elements:
#' \itemize{
#'   \item \code{beta}: Hasofer-Lind reliability index.
#'   \item \code{pf}: Estimated probability of failure.
#'   \item \code{u_points}: Design point(s) in the standard normal space (\eqn{u}-space).
#'   \item \code{dy}: Gradient(s) of the limit-state function at the design point.
#' }
#' @import pracma
#' @author (C) 2021-2026 K. Nille-Hauf, T. Feiri, M. Ricker, T. Lux -- Hochschule Biberach (until 2022),
#' TU Dortmund University - Chair of Structural Concrete (since 2023)
#' @references
#' Breitung, K. (1989).
#' Asymptotic approximations for probability integrals.
#' \emph{Probabilistic Engineering Mechanics}, 4(4), 187-190.
#'
#' Cai, G. Q., & Elishakoff, I. (1994).
#' Refined second-order reliability analysis.
#' \emph{Structural Safety}, 14(4), 267-276.
#'
#' Hohenbichler, M., Gollwitzer, S., Kruse, W., & Rackwitz, R. (1987).
#' New light on first- and second-order reliability methods.
#' \emph{Structural Safety}, 4, 267-284.
#'
#' Tvedt, L. (1990).
#' Distribution of quadratic forms in normal space -
#' Applications to structural reliability.
#' \emph{Journal of Engineering Mechanics}, 116(6), 1183-1197.
#'
#' Marelli, S., & Sudret, B. (2014).
#' UQLab: A framework for uncertainty quantification in Matlab.
#' In \emph{Proceedings of the 2nd International Conference on
#' Vulnerability, Risk Analysis and Management (ICVRAM2014)},
#' Liverpool, United Kingdom, 2554-2563.
#'
#' Lacaze, S., & Missoum, S. (2015).
#' CODES: A toolbox for computational design (Version 1.0).
#' Retrieved from \url{http://www.codes.arizona.edu/toolbox/}
#'
#' Wu, X. Z. (2017).
#' Implementing statistical fitting and reliability analysis
#' for geotechnical engineering problems in R.
#' \emph{Georisk: Assessment and Management of Risk for Engineered Systems and Geohazards},
#' 11(2), 173-188.
#' @export

SORM <- function(lsf, # the limit-state function.
                 lDistr,
                 debug.level=0){ #  description of distribution of random variables

  debug.TAG <- "SORM_RABU"
  debug.print(debug.level,debug.TAG,c(TRUE), msg="SORM started...")
  tic <- proc.time()

  # Isoprobabilistic transformation to the standard Gaussian space U
  # Tlsf, convenient for users and required by mistral::FORM. AZC-style Tinv could be an alternative, e.g. Tinv <- function(X) { lsf(qnorm(pnorm(X),0.25,1)) }
  Tlsf <- function(X) {
    for(i in 1:length(X)){
      XU <- stats::pnorm(X[i])
      if(XU == 1){ XU = 1-1E-9}
      X[i] <- lDistr[[i]][[1]]$q(XU)
    }
    return(lsf(X))
  }


  # Orthonormal rotation matrix (implementation inspired by uq_gram_schmidt)
  GRAM_RABU <- function(alphas) {
    dimension <- length(alphas)
    Base<-diag(dimension)
    Base[,dimension] <- alphas
    NewBase <- Base
    Rot <- matrix( rep( 0, dimension*dimension), nrow = dimension)
    Rot[dimension,dimension] <- 1
    for (k in dimension:1){
      Z <- matrix( rep( 0, dimension), nrow = dimension)
      j <- k+1
      while(j<=dimension) {
        Z <- Z + pracma::dot(Base[,k],NewBase[,j]) * NewBase[,j]
        j <- j+1
      }
      NewBase[,k] <- Base[,k] - Z;
      Rot[k,k] <- pracma::Norm(NewBase[,k],2);
      NewBase[,k] <- NewBase[,k] / Rot[k,k];
      if (k<dimension) {
        Rot[k,(k+1):dimension] <- t(Base[,k]) %*% NewBase[,(k+1):dimension];
      }
    }
    return(t(NewBase))
  }




  # FORM
  res <- TesiproV::FORM(lsf, lDistr,loctol = 0.01,n_optim=10)

  # Alpha's (direction cosines) as components of the unit gradient vector:
  # The alphas can be determined through the gradient vector in the U space at the design-point u*
  gradient <- res$dy
  alphas <- gradient/pracma::Norm(gradient)

  # Matrix R
  R <- GRAM_RABU(alphas)

  # Hessian Matrix H
  H <- pracma::hessian(Tlsf, res$x_points) # Second-derivative Hessian matrix at the design-point u*


  # Matrix A as a product of R,H,R'divided by the lenght of the gradient vector at the design-point u*
  A <- R %*% H %*% t(R)
  A <- A/pracma::Norm(gradient)

  # Curvatures (found through the eigenvalues of the reduced matrix A
  # Fixed by MR 19-01-2026: older R versions silently accepted vector conditions in 'if()',
  # but newer versions require a single logical value (length == 1).
  # !any() and any() added inside the if() conditions.
  if(!any(!is.nan(A))){
    curvatures <- eigen(A[1:nrow(A)-1,1:nrow(A)-1])$values
    warning("No corvature found. Maybe bad designpoints of FORM.")
  }else{
    curvatures <- rep(0,length(gradient))
  }
  if (any(curvatures >= 1)) {warning("FORM did not converge to the design point")}
  debug.print(0,debug.TAG,c("curvatures"),curvatures)

  # SORM: Reliability Index (Beta) and Probability of failure (Pf)
  # Breitung's formula
  SORMpfBreitung <- res$pf/sqrt(prod(1+res$beta*curvatures)) # Breitung's Formula
  SORMbetaBreitung <- -stats::qnorm(SORMpfBreitung) # Beta_SORM   norminv() in AZC, icdf() in UQL
  # NOTE: Check if the starting point was negative or not based on the origin point from FORM (as in the UQLab)

  # HohenBichler's formula
  CDFminusBeta <- stats::pnorm(-res$beta, 0, 1);
  RatioCDFPDF <- stats::dnorm(-res$beta, 0, 1)/CDFminusBeta;
  HohenbichlerVec <- 1./sqrt(1 + RatioCDFPDF*curvatures);
  SORMpfHB <- CDFminusBeta*prod(HohenbichlerVec);
  SORMbetaHB <- (-1)*stats::qnorm(SORMpfHB)

  # Tvedt's formula
  A11 <- 1/sqrt(prod(1+res$beta*curvatures))
  A12 <- res$beta*res$pf-stats::dnorm(-res$beta)
  A1 <- res$pf*A11
  A2 <- A12*(A11-1/sqrt(prod(1+(res$beta+1)*curvatures)))
  A3 <- (res$beta+1)*A12*(A11-Re(1/sqrt(prod(1+(res$beta+1i)*curvatures))));
  SORMpfTvedt <- A1+A2+A3;
  SORMbetaTvedt <- -stats::qnorm(SORMpfTvedt)


  cat("\n")
  duration<-proc.time()-tic

  # Return the results
  output <- list(
            "method"="SORM",
            "beta"=SORMbetaBreitung,
            "pf"=SORMpfBreitung,
            "FORM_pf"= res$pf,
            "FORMbeta"=res$beta,
            "design.point_X"=res$x_points,
            "SORMpfTvedt"=SORMpfTvedt,
            "SORMbetaTvedt"=SORMbetaTvedt,
            "SORMpfHB"=SORMpfHB,
            "SORMbetaHB"=SORMbetaHB,
            "runtime"=duration[1:5]
            )

  debug.print(debug.level,debug.TAG,c(duration), msg="SORM finished in [s]: ")
  return(output)
  }



