package scientific.stats.continuous

import std.math.*
import std.unittest.*
import std.unittest.testmacro.*

import scientific.numbers.*
import scientific.stats.random.*

/*
 * Probability density function for exponential distribution
 */
public func exponentialPDF(x: Float64, lambda: Float64): Float64 {
    return lambda * pow(Float64.getE(), -lambda * x)
}

/*
 * Cumulative density function for exponential distribution
 */
public func exponentialCDF(x: Float64, lambda: Float64): Float64 {
    if (x < 0.0) {
        return 0.0
    }

    return 1.0 - pow(Float64.getE(), -lambda * x)
}

/*
 * Compute cumulative density between x1, x2
 */
public func exponentialCDF(lambda: Float64, x1: Float64, x2: Float64): Float64{
    if (x1 >= x2) {
        return 0.0  // Require input to satisfy x1 < x2
    }

    if (x2 <= 0.0) {
        return 0.0
    }

    if (x1 < 0.0 && x2 > 0.0) {
        return exponentialCDF(lambda, x2)
    }

    return exponentialCDF(lambda, x2) - exponentialCDF(lambda, x1)
}

/*
 * Sampling from the exponential distribution
 */
public func exponentialSample(m: Random, lambda: Float64): Float64{
    let x = m.nextFloat64()
    return - 1.0 / lambda * log(x) 
}


@Test
public class TestExponential {
    @TestCase
    func testExponential(): Unit {
        @Assert(approxEqual(exponentialPDF(1.0, 1.0), 0.36787944117144233,  atol:1e-13))
        @Assert(approxEqual(exponentialPDF(2.0, 1.0), 0.1353352832366127,   atol:1e-13))
        @Assert(approxEqual(exponentialPDF(3.0, 1.0), 0.049787068367863944, atol:1e-13))
        @Assert(approxEqual(exponentialPDF(4.0, 1.0), 0.01831563888873418,  atol:1e-13))
    }
}