open Jest
open Expect
open TestHelpers
open FastCheck
open Arbitrary
open Property.Sync

describe("dotSubtract", () => {
  test("mean of normal minus exponential (unit)", () => {
    let mean = 0.0
    let rate = 10.0
    exception MeanFailed
    let dotDifference = DistributionOperation.Constructors.pointwiseSubtract(
      ~env,
      mkNormal(mean, 1.0),
      mkExponential(rate),
    )
    let meanResult = E.R2.bind(DistributionOperation.Constructors.mean(~env), dotDifference)
    let meanAnalytical =
      mean -.
      SymbolicDist.Exponential.mean({rate: rate})->E.R2.toExn(
        "On trusted input this should never happen",
      )
    switch meanResult {
    | Ok(meanValue) => meanValue->expect->toBeCloseTo(meanAnalytical)
    | Error(_) => raise(MeanFailed)
    }
  })
  /*
    It seems like this test should work, and it's plausible that
    there's some bug in `pointwiseSubtract`
 */
  Skip.test("mean of normal minus exponential (property)", () => {
    assert_(
      property2(float_(), floatRange(1e-5, 1e5), (mean, rate) => {
        // We limit ourselves to stdev=1 so that the integral is trivial
        let dotDifference = DistributionOperation.Constructors.pointwiseSubtract(
          ~env,
          mkNormal(mean, 1.0),
          mkExponential(rate),
        )
        let meanResult = E.R2.bind(DistributionOperation.Constructors.mean(~env), dotDifference)
        // according to algebra or random variables,
        let meanAnalytical =
          mean -.
          SymbolicDist.Exponential.mean({rate: rate})->E.R2.toExn(
            "On trusted input this should never happen",
          )
        switch meanResult {
        | Ok(meanValue) => abs_float(meanValue -. meanAnalytical) /. abs_float(meanValue) < 1e-2 // 1% relative error
        | Error(err) => err === DistributionTypes.OperationError(DivisionByZeroError)
        }
      }),
    )
    pass
  })
})