#' Sigmoid activation function
#'
#' Computes the sigmoid transformation.
#'
#' @param x A numeric vector, matrix, or scalar.
#'
#' @return Transformed values in the interval (0, 1).
sigmoid <- function(x) {
  1 / (1 + exp(-x))
}

#' Clip values to lower and upper bounds
#'
#' Restricts each element of a vector to the given bounds.
#'
#' @param x Numeric vector.
#' @param lower Lower bound vector.
#' @param upper Upper bound vector.
#'
#' @return A bounded numeric vector.
clip_bounds <- function(x, lower, upper) {
  pmin(pmax(x, lower), upper)
}

#' Lévy flight step generator
#'
#' Generates Lévy-distributed random steps.
#'
#' @param d Dimension of the step vector.
#' @param beta Stability parameter.
#'
#' @return A numeric vector of length \code{d}.
#' @importFrom stats rnorm
levy_flight <- function(d, beta = 1.5) {
  sigma <- (gamma(1 + beta) * sin(pi * beta / 2) /
              (gamma((1 + beta) / 2) * beta * 2^((beta - 1) / 2)))^(1 / beta)
  u <- rnorm(d) * sigma
  v <- rnorm(d)
  u / abs(v)^(1 / beta)
}

#' Min-max normalization
#'
#' Scales each column of a matrix to the interval from 0 to 1.
#'
#' @param x Numeric vector or matrix.
#'
#' @return A list containing scaled data and min-max values.
normalize_minmax <- function(x) {
  x <- as.matrix(x)
  mins <- apply(x, 2, min)
  maxs <- apply(x, 2, max)
  ranges <- maxs - mins
  ranges[ranges == 0] <- 1

  x_scaled <- sweep(x, 2, mins, "-")
  x_scaled <- sweep(x_scaled, 2, ranges, "/")

  list(
    x_scaled = x_scaled,
    min = mins,
    max = maxs
  )
}

#' Apply min-max normalization using existing bounds
#'
#' @param x Numeric vector or matrix.
#' @param mins Column minima.
#' @param maxs Column maxima.
#'
#' @return Scaled matrix.
apply_minmax <- function(x, mins, maxs) {
  x <- as.matrix(x)
  ranges <- maxs - mins
  ranges[ranges == 0] <- 1

  x_scaled <- sweep(x, 2, mins, "-")
  sweep(x_scaled, 2, ranges, "/")
}

#' Reverse min-max normalization
#'
#' @param x_scaled Scaled numeric vector or matrix.
#' @param mins Column minima.
#' @param maxs Column maxima.
#'
#' @return Values on the original scale.
denormalize_minmax <- function(x_scaled, mins, maxs) {
  x_scaled <- as.matrix(x_scaled)
  ranges <- maxs - mins
  sweep(sweep(x_scaled, 2, ranges, "*"), 2, mins, "+")
}
