#' Calculation of Weighted Distance Matrices
#'
#' @param X  n x r matrix
#' @param W  nG x r maatrix
#' @param array = 0 to make the result a list
#'
#' @details
#' This function calculates the weighted distance among the rows of
#' \code{X} matrix  by applying the k-th row of \code{W} matrix
#' as a set of dimensional weights as:
#' \deqn{ d_{ijk} = \sqrt( \sum_{a=1}^r   w[k,a] (x[i,a]-x[j,a])^2 ) }
#' or
#' \deqn{ d_{ijk} = \sqrt( (x[i,]-x[j,])' diag(W[k,]) (x[i,]-x[j,]) ) }
#' \cr
#' Note that \code{W} matrix will NOT be squared when calculating distance.
#' \cr
#'
#' @return
#' An array or a list of nG matrices of size n x n
#'
#' @examples
#' X <- matrix( c(0,0,  3,0,  0,4), 3,2,  byrow=1 )
#' distancew( X )
#' W <- matrix( c(2,1, 1,2, 1,1), 3,2, byrow=1 )
#' distancew( X, W )
#'
#' @export
#'

distancew <- function( X, W=matrix(1,1,ncol(X)), array=1 ){
 # weighted Euclidean distance between the rows of X matrices
 # Shin-ichi Mayekawa
 # 111122
 # This version allows negative elements for W
 # error check: 20150609
 # roxygen: 20210421
 #
 # Args:
 #  X      n x r matrix
 #  W      s x r matrix or subject weight
 #  array  = 0 to store the result in a list
 #
 # Value:
 #  D  array or list of s n x n matrices
 #
 # distance is calculated as follows using function distancew0:
 #   dijk = sqrt( sum_{a=1}^r   w[k,a] (x[i,a]-x[j,a])^2 )
 #   dijk = sqrt( (x[i,]-x[j,])' diag(W[k,]) (x[i,]-x[j,]) )
 #
 #
 # Needs:
 #  distancew0, Diag
 #

 # error
 if( !is.matrix(X) | !is.matrix(W) ){
  cat("\n\nerror1:(distancew) Input must be a matrix.\n")
  return()
 }

 n=nrow(X)
 nr=ncol(X)
 ns=nrow(W)

 rname=rownames(X)

 if ( length(rownames(W)) == 0 )
  rnw=paste("s",1:ns,sep="")
 else
  rnw=rownames(W)

 if ( array == 1 ){
  # as array
  D=array(0,c(n,n,ns))
  dimnames(D)[[1]]=rownames(X)
  dimnames(D)[[2]]=rownames(X)
  if( ns > 1 ) dimnames(D)[[3]]=rnw
  for ( k in 1:ns ){
   D[,,k]=distancew0(X,diag(W[k,]))
  }
 }
 else{
  # as list
  D=list(distancew0(X,diag(W[1,])))
  if ( ns >= 2 ) {
   for ( k in 2:ns ){
    D=append(D,list(distancew0(X,diag(W[k,]))))
   }
  }
  names(D)=rnw
 }

 return( D )

} #  end of distancew



#' Calculation of Weighted Distance Matrix
#'
#' @param X  n x r matrix
#' @param W  r x r diagonal maatrix
#'
#' @details
#' This function calculates the weighted distance among the rows of
#' \code{X} matrix  by applying the a-th diagonal element of \code{W} matrix
#' as the dimensional weights as:
#' \deqn{ d_{ij} = \sqrt( \sum_{a=1}^r   w[a,a] (x[i,a]-x[j,a])^2 ) }
#' or
#' \deqn{ d_{ij} = \sqrt( (x[i,]-x[j,])' W (x[i,]-x[j,]) ) }
#' \cr
#' Note that \code{W} matrix will NOT be squared when calculating distance.
#' \cr
#'
#' @return
#' A matrices of size n x n
#'
#' @examples
#' X <- matrix( c(0,0,  3,0,  0,4), 3,2,  byrow=1 )
#' distancew0( X )
#' W <- diag(c(2,1))
#' distancew0( X,W )
#'
#' @export
#'
distancew0 <- function( X, W=diag(ncol(X)) ){
 # Weighted Euclidean distance between the rows of X matrix
 # Shin-ichi Mayekawa
 # 111122,120130
 # error check: 20150609
 # roxygen: 20210421
 #
 # Args:
 #  X  n x r matrix
 #  W  r x r diagonal matrix of dimensional weight
 #
 # Value:
 #  D  n x n matrix
 #
 # distance is calculated as:
 #   dij = sqrt( sum_{k=1}^r w_{k,k}( x_{ik}-x_{jk} )^2 )
 #   dij = sqrt( (x[i,]-x[j,])' W (x[i,]-x[j,]) )
 #
 #

 # error
 if( !is.matrix(X) | !is.matrix(W) ){
  cat("\n\nerror1:(distancew0) Input must be a matrix.\n")
  return()
 }

 n=nrow(X)
 nr=ncol(X)

 XX=rowSums(X*X%*%W)
 XX11=XX%*%matrix(1,1,n); XX11=XX11+t(XX11)
 D=sqrt( abs( XX11-2*X%*%W%*%t(X) ) )

 rownames(D)=rownames(X)
 colnames(D)=rownames(X)

 return( D )

} # end of distancew0


#' Calculation of Weighted Distance Matrix between the rows of F and A
#'
#'
#' @param F  n x r matrix
#' @param A  p x r matrix
#' @param W  p x r maatrix
#' @param R  p x (r x r) array
#'
#' @details
#' This program calculates the weighted distance between the rows of F matrix
#' and the rows of A matrix as\cr
#' \deqn{ d_{ij} = \sqrt( (f[i,]-a[j,])' diag(W[j,]) (f[i,]-a[j,]) )  } \cr
#' \deqn{ d_{ij} = \sqrt( (f[i,]-a[j,])' R[,,j] (f[i,]-a[j,]) )  }
#'
#' \cr
#' Note that \code{W} matrix will NOT be squared when calculating distance.
#' \cr
#'
#' @return
#' A matrices of size n x p
#'
#' @examples
#' F <- matrix( c(0,0,  3,0,  0,4), 3,2,  byrow=1 )
#' A <- matrix( c(0,0,  3,4 ), 2,2, byrow=1 )
#' distrectw( F,A )
#' Wa <- matrix( c(1,1,  1,2 ), 2,2, byrow=1 )
#' distrectw( F,A, Wa )
#' R <- matrix(c(1,0, 0,1, 1,0.5, 0.5,3 ), ,2, byrow=TRUE)
#' # R <- t(R)
#' # dim(R)=c(2,2,2)
#' distrectw( F,A, R=R )
#'
#' @export
#'

distrectw <- function( F, A, W=matrix(1, nrow(A), ncol(A)), R=NULL
                        , outD2=0 ){
 # weighted distance between F and A
 # Shin-ichi Mayekawa
 # 111122,120130
 # error check: 20150609
 # roxygen: 20210421
 # R: 20251226
 # 20251227
 # distrectw renamed as distrectWR: 20260111
 # outD2: 20260214
 # nr --> ndim: 20260423
 # renamed as distrectw: 20260514
 #
 # Args:
 #  F  n x r matrix
 #  A  nv x ndim matrix
 #  W  nv x ndim matrix
 #  R  nv * ndim x ndim matrix
 #  outD2 = 1 to return squared distance as D
 #
 # Value:
 #  D  n x nv matrix
 #
 # distance is calculated as:
 #   dij =sqrt( (f[i,]-a[j,])' diag(W[j,]) (f[i,]-a[j,]) )
 #   dij =sqrt( (f[i,]-a[j,])' R[(j-1)*ndim*(1:ndim),](f[i,]-a[j,]) )
 #
 #

 # error
 if( !is.matrix(F) | !is.matrix(A)  | !is.matrix(W) ){
  cat("\n\nerror1:(distrectWR) Input must be a matrix.\n")
  return()
 }

 ns=nrow(F)
 ndim=ncol(F)
 nv=nrow(A)

 if( !is.null(R) ){
  if( nrow(R) != nv*ndim ){
   cat("\nwarning1:(distrectWR) R must be ncol(A)*nrow(A) x nrow(A).\n")
   stop()
  }
 }

 if( is.null(R) ){
  FF=F^2
  AA=apply((A^2)*W,1,sum)
  # Print(F,A,W,Ws,FF,AA)
  D=FF%*%t(W)-2*F%*%t(A*W)+matrix(1,ns,1)%*%t(AA)
 }
 else{
  D=matrix(0,ns,nv)
   for( j in 1:nv ){
    FmA=F-matrix(A[j,],ns,ndim, byrow=1)
    Rj=R[((j-1)*ndim+1):(j*ndim), , drop = FALSE]
    D[,j]=rowSums((FmA %*% Rj) * FmA)
   }
 }

 if( outD2 == 0 ) D=sqrt(D)
 rownames(D)=rownames(F)
 colnames(D)=rownames(A)

 return( D )

} # distrectw



