package scientific.stats.continuous

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

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

/*
 * Log of Probability density function
 */
public func cosineLogPDF(x: Float64, loc!: Float64 = 0.0, scale!: Float64 = 1.0): Float64 {
    let y = (x - loc) / scale

    if (y < -Float64.getPI() || y > Float64.getPI()) {
        throw IllegalArgumentException("cosineLogPDF: input value x out of bound.")
    }

    return log((1.0 + cos(y)) / 2.0 / Float64.getPI())
}

/*
 * Probability density function
 */
public func cosinePDF(x: Float64, loc!: Float64 = 0.0, scale!: Float64 = 1.0): Float64 {
    let y = (x - loc) / scale

    if (y < -Float64.getPI() || y > Float64.getPI()) {
        throw IllegalArgumentException("cosinePDF: input value x out of bound.")
    }

    return (1.0 + cos(y)) / 2.0 / Float64.getPI()
}

/*
 * Mean
 */
public func cosineMean(loc!: Float64 = 0.0, scale!: Float64 = 1.0): Float64 {
    return loc
}

/*
 * Var
 */
public func cosineVar(loc!: Float64 = 0.0, scale!: Float64 = 1.0): Float64 {
    let temp = Float64.getPI() * Float64.getPI() / 3.0 - 2.0
    return temp * scale * scale
}

/*
 * Std
 */
public func cosineStd(loc!: Float64 = 0.0, scale!: Float64 = 1.0): Float64 {
    let temp = cosineVar(loc: loc, scale: scale)

    if (temp < 0.000001) {
        throw IllegalArgumentException("cosineStd: return-value too small.")
    }

    return sqrt(temp)
}

@Test
public class TestCosine {
    @TestCase
    func testCosine(): Unit {
        @Assert(approxEqual(cosineLogPDF(2.0, loc: 1.0, scale: 2.0), -1.207892, atol:1e-6))
        @Assert(approxEqual(cosineMean(loc: 1.0, scale: 2.0),         1.0,      atol:1e-6)) 
        @Assert(approxEqual(cosineVar(loc: 1.0, scale: 2.0),          5.159472, atol:1e-6)) 
    }
}