///|
/// Lognormal distribution for reliability modeling.
/// The parameters `mu` and `sigma` are the mean and standard deviation of the variable's natural logarithm.
pub struct Lognormal {
  mu : Double
  sigma : Double
}

///|
/// Create a new Lognormal distribution.
/// Panics if sigma is not strictly positive.
pub fn Lognormal::new(mu : Double, sigma : Double) -> Lognormal {
  if sigma <= 0.0 {
    abort("sigma must be strictly positive")
  }
  { mu, sigma }
}

///|
/// Probability density function (PDF) at time t.
pub fn Lognormal::pdf(self : Lognormal, t : Double) -> Double {
  if t <= 0.0 {
    0.0
  } else {
    let ln_t = @math.ln(t)
    let z = (ln_t - self.mu) / self.sigma
    1.0 / (t * self.sigma * (2.0 * @math.PI).sqrt()) * @math.exp(-0.5 * z * z)
  }
}

///|
/// Cumulative distribution function (CDF) at time t.
pub fn Lognormal::cdf(self : Lognormal, t : Double) -> Double {
  if t <= 0.0 {
    0.0
  } else {
    let z = (@math.ln(t) - self.mu) / self.sigma
    standard_normal_cdf(z)
  }
}

///|
/// Reliability function R(t) = 1 - CDF(t).
pub fn Lognormal::reliability(self : Lognormal, t : Double) -> Double {
  if t <= 0.0 {
    1.0
  } else {
    let z = (@math.ln(t) - self.mu) / self.sigma
    1.0 - standard_normal_cdf(z)
  }
}

///|
/// Mean Time Between Failures (MTBF).
pub fn Lognormal::mtbf(self : Lognormal) -> Double {
  @math.exp(self.mu + self.sigma * self.sigma / 2.0)
}

///|
/// Quantile function (inverse CDF).
pub fn Lognormal::quantile(self : Lognormal, p : Double) -> Double {
  if p <= 0.0 || p >= 1.0 {
    abort("p must be in (0, 1)")
  }
  let z = standard_normal_inv(p)
  @math.exp(self.mu + self.sigma * z)
}