All Downloads are FREE. Search and download functionalities are using the official Maven repository.

jvmMain.io.kotest.matchers.stats.matchers.kt Maven / Gradle / Ivy

package io.kotest.matchers.stats

import io.kotest.matchers.Matcher
import io.kotest.matchers.MatcherResult
import io.kotest.matchers.should
import io.kotest.matchers.shouldNot
import java.math.BigDecimal
import java.math.MathContext
import java.math.RoundingMode
import kotlin.math.sqrt

/**
 * Asserts that mean of the Collection elements equals to [value] with default
 * or specific [precision]. Default precision equals 4 digits after decimal point.
 *
 * Opposite of [shouldNotHaveMean]
 *
 * Example:
 * ```
 *
 * val collection = listOf(1, 1, 3)
 * val firstMean   = BigDecimal("1.66667")
 * val secondMean = BigDecimal("1.6667")
 *
 * collection.shouldHaveMean(firstMean, 5)      // Assertion passes
 * collection.shouldHaveMean(secondMean)       // Assertion passes
 *
 * collection.shouldHaveMean(firstMean)         // Assertion fails
 * collection.shouldHaveMean(secondMean, 5)    // Assertion fails
 * ```
 *
 * @param value - expected mean value
 * @param precision - precision, by default - 4 digits after decimal point
 *
 */
fun  Collection.shouldHaveMean(value: BigDecimal, precision: Int = 4) = this should haveMean(value, precision)

/**
 * Asserts that mean of the Collection elements equals to [value] with default
 * or specific [precision]. Default precision equals 4 digits after decimal point.
 *
 * Opposite of [shouldNotHaveMean]
 *
 * Example:
 * ```
 *
 * val collection = listOf(1, 1, 3)
 * val firstMean   = 1.66667
 * val secondMean = 1.6667
 *
 * collection.shouldHaveMean(firstMean, 5)      // Assertion passes
 * collection.shouldHaveMean(secondMean)       // Assertion passes
 *
 * collection.shouldHaveMean(firstMean)         // Assertion fails
 * collection.shouldHaveMean(secondMean, 5)    // Assertion fails
 * ```
 *
 * @param value - expected mean value
 * @param precision - precision, by default - 4 digits after decimal point
 *
 */
fun  Collection.shouldHaveMean(value: Double, precision: Int = 4) = this should haveMean(value, precision)

/**
 * Asserts that mean of the Collection elements doesn't equal to [value] with default
 * or specific [precision]. Default precision equals 4 digits after decimal point.
 *
 * Opposite of [shouldHaveMean]
 *
 * Example:
 * ```
 *
 * val collection = listOf(1, 1, 3)
 * val firstMean   = BigDecimal("2.0")
 * val secondMean = BigDecimal("1.6666667")
 *
 * collection.shouldNotHaveMean(firstMean)                // Assertion passes
 * collection.shouldNotHaveMean(secondMean, 5)           // Assertion passes
 *
 * collection.shouldNotHaveMean(BigDecimal("1.6667"))    // Assertion fails
 * collection.shouldNotHaveMean(BigDecimal("1.6667"), 4) // Assertion fails
 * ```
 *
 * @param value - not expected mean value
 * @param precision - precision, by default - 4 digits after decimal point
 *
 */
fun  Collection.shouldNotHaveMean(value: BigDecimal, precision: Int = 4) = this shouldNot haveMean(value, precision)

/**
 * Asserts that mean of the Collection elements doesn't equal to [value] with default
 * or specific [precision]. Default precision equals 4 digits after decimal point.
 *
 * Opposite of [shouldHaveMean]
 *
 * Example:
 * ```
 *
 * val collection = listOf(1, 1, 3)
 *
 * collection.shouldNotHaveMean(2.0)          // Assertion passes
 * collection.shouldNotHaveMean(1.67, 5)      // Assertion passes
 *
 * collection.shouldNotHaveMean(1.6667)       // Assertion fails
 * collection.shouldNotHaveMean(1.6667, 4)    // Assertion fails
 * ```
 *
 * @param value - not expected mean value
 * @param precision - precision, by default - 4 digits after decimal point
 *
 */
fun  Collection.shouldNotHaveMean(value: Double, precision: Int = 4) = this shouldNot haveMean(value, precision)

/**
 * Asserts that variance of the Collection elements equals to [value] with default
 * or specific [precision]. Default precision equals 4 digits after decimal point.
 *
 * Opposite of [shouldNotHaveVariance]
 *
 * Example:
 * ```
 *
 * val collection = listOf(1, 2, 3)
 * val firstVariance   = BigDecimal("0.66667")
 * val secondVariance = BigDecimal("0.6667")
 *
 * collection.shouldHaveVariance(firstVariance, 5)      // Assertion passes
 * collection.shouldHaveVariance(secondVariance)       // Assertion passes
 *
 * collection.shouldHaveVariance(firstVariance)         // Assertion fails
 * collection.shouldHaveVariance(secondVariance, 5)    // Assertion fails
 * ```
 *
 * @param value - expected variance value
 * @param precision - precision, by default - 4 digits after decimal point
 *
 */
fun  Collection.shouldHaveVariance(value: BigDecimal, precision: Int = 4) = this should haveVariance(value, precision)

/**
 * Asserts that variance of the Collection elements equals to [value] with default
 * or specific [precision]. Default precision equals 4 digits after decimal point.
 *
 * Opposite of [shouldNotHaveVariance]
 *
 * Example:
 * ```
 *
 * val collection = listOf(1, 2, 3)
 *
 * collection.shouldHaveVariance(0.66667, 5)      // Assertion passes
 * collection.shouldHaveVariance(0.6667)          // Assertion passes
 *
 * collection.shouldHaveVariance(0.67)         // Assertion fails
 * collection.shouldHaveVariance(0.6667, 5)    // Assertion fails
 * ```
 *
 * @param value - expected variance value
 * @param precision - precision, by default - 4 digits after decimal point
 *
 */
fun  Collection.shouldHaveVariance(value: Double, precision: Int = 4) = this should haveVariance(value, precision)

/**
 * Asserts that variance of the Collection elements doesn't equal to [value] with default
 * or specific [precision]. Default precision equals 4 digits after decimal point.
 *
 * Opposite of [shouldHaveVariance]
 *
 * Example:
 * ```
 *
 * val collection = listOf(1, 2, 3)
 *
 * collection.shouldNotHaveVariance(BigDecimal("1.01"), 5)        // Assertion passes
 * collection.shouldNotHaveVariance(BigDecimal("0.666667"))       // Assertion passes
 *
 * collection.shouldNotHaveVariance(BigDecimal("0.6667"))         // Assertion fails
 * collection.shouldNotHaveVariance(BigDecimal("0.66667"), 5)     // Assertion fails
 * ```
 *
 * @param value - not expected variance value
 * @param precision - precision, by default - 4 digits after decimal point
 *
 */
fun  Collection.shouldNotHaveVariance(value: BigDecimal, precision: Int = 4) = this shouldNot haveVariance(value, precision)

/**
 * Asserts that variance of the Collection elements doesn't equal to [value] with default
 * or specific [precision]. Default precision equals 4 digits after decimal point.
 *
 * Opposite of [shouldHaveVariance]
 *
 * Example:
 * ```
 *
 * val collection = listOf(1, 2, 3)
 *
 * collection.shouldNotHaveVariance(1.01, 5)        // Assertion passes
 * collection.shouldNotHaveVariance(0.666667)       // Assertion passes
 *
 * collection.shouldNotHaveVariance(0.6667)         // Assertion fails
 * collection.shouldNotHaveVariance(0.66667, 5)     // Assertion fails
 * ```
 *
 * @param value - not expected variance value
 * @param precision - precision, by default - 4 digits after decimal point
 *
 */
fun  Collection.shouldNotHaveVariance(value: Double, precision: Int = 4) = this shouldNot haveVariance(value, precision)

/**
 * Asserts that standard deviation of the Collection elements equals to [value] with default
 * or specific [precision]. Default precision equals 4 digits after decimal point.
 *
 * Opposite of [shouldNotHaveStandardDeviation]
 *
 * Example:
 * ```
 *
 * val collection = listOf(1, 2, 3)
 *
 * collection.shouldHaveStandardDeviation(BigDecimal("0.82"), 2)      // Assertion passes
 * collection.shouldHaveStandardDeviation(BigDecimal("0.8165"))       // Assertion passes
 *
 * collection.shouldHaveStandardDeviation(BigDecimal("0.82"))         // Assertion fails
 * collection.shouldHaveStandardDeviation(BigDecimal("0.8165"), 5)    // Assertion fails
 * ```
 *
 * @param value - expected standard deviation value
 * @param precision - precision, by default - 4 digits after decimal point
 *
 */
fun  Collection.shouldHaveStandardDeviation(value: BigDecimal, precision: Int = 4) = this should haveStandardDeviation(value, precision)

/**
 * Asserts that standard deviation of the Collection elements equals to [value] with default
 * or specific [precision]. Default precision equals 4 digits after decimal point.
 *
 * Opposite of [shouldNotHaveStandardDeviation]
 *
 * Example:
 * ```
 *
 * val collection = listOf(1, 2, 3)
 *
 * collection.shouldHaveStandardDeviation(0.82, 2)      // Assertion passes
 * collection.shouldHaveStandardDeviation(0.8165)       // Assertion passes
 *
 * collection.shouldHaveStandardDeviation(0.82)         // Assertion fails
 * collection.shouldHaveStandardDeviation(0.8165, 5)    // Assertion fails
 * ```
 *
 * @param value - expected standard deviation value
 * @param precision - precision, by default - 4 digits after decimal point
 *
 */
fun  Collection.shouldHaveStandardDeviation(value: Double, precision: Int = 4) = this should haveStandardDeviation(value, precision)

/**
 * Asserts that standard deviation of the Collection elements doesn't equal to [value] with default
 * or specific [precision]. Default precision equals 4 digits after decimal point.
 *
 * Opposite of [shouldHaveStandardDeviation]
 *
 * Example:
 * ```
 *
 * val collection = listOf(1, 2, 3)
 *
 * collection.shouldNotHaveStandardDeviation(BigDecimal("0.8165"), 5)    // Assertion passes
 * collection.shouldNotHaveStandardDeviation(BigDecimal("0.8333"))       // Assertion passes
 *
 * collection.shouldNotHaveStandardDeviation(BigDecimal("0.8165"))       // Assertion fails
 * collection.shouldNotHaveStandardDeviation(BigDecimal("0.82"), 2)      // Assertion fails
 * ```
 *
 * @param value - not expected standard deviation value
 * @param precision - precision, by default - 4 digits after decimal point
 *
 */
fun  Collection.shouldNotHaveStandardDeviation(value: BigDecimal, precision: Int = 4) = this shouldNot haveStandardDeviation(value, precision)

/**
 * Asserts that standard deviation of the Collection elements doesn't equal to [value] with default
 * or specific [precision]. Default precision equals 4 digits after decimal point.
 *
 * Opposite of [shouldHaveStandardDeviation]
 *
 * Example:
 * ```
 *
 * val collection = listOf(1, 2, 3)
 *
 * collection.shouldNotHaveStandardDeviation(0.8165, 5)    // Assertion passes
 * collection.shouldNotHaveStandardDeviation(0.8333)       // Assertion passes
 *
 * collection.shouldNotHaveStandardDeviation(0.8165)       // Assertion fails
 * collection.shouldNotHaveStandardDeviation(0.82, 2)      // Assertion fails
 * ```
 *
 * @param value - not expected standard deviation value
 * @param precision - precision, by default - 4 digits after decimal point
 *
 */
fun  Collection.shouldNotHaveStandardDeviation(value: Double, precision: Int = 4) = this shouldNot haveStandardDeviation(value, precision)

private val defaultMathContext = MathContext(64, RoundingMode.HALF_UP)
private fun BigDecimal.round(precision: Int): BigDecimal = this.setScale(precision, RoundingMode.HALF_UP).stripTrailingZeros()

private fun  calculateMean(collection: Collection): BigDecimal {
   var sum: BigDecimal = BigDecimal.ZERO
   for (elem in collection) {
      sum += BigDecimal(elem.toString())
   }
   return sum.divide(BigDecimal(collection.size), defaultMathContext)
}

private fun  calculateVariance(collection: Collection): BigDecimal {
   val mean: BigDecimal = calculateMean(collection)
   var sumOfSquaredDifferences: BigDecimal = BigDecimal.ZERO
   for (elem in collection) {
      sumOfSquaredDifferences += (BigDecimal(elem.toString()) - mean).pow(2)
   }
   return sumOfSquaredDifferences.divide(BigDecimal(collection.size), defaultMathContext)
}

private fun  calculateStandardDeviation(collection: Collection): BigDecimal {
   val variance = calculateVariance(collection)
   val two = BigDecimal(2)
   var x0 = BigDecimal.ZERO
   var x1 = BigDecimal(sqrt(variance.toDouble()))
   while (x0 != x1) {
      x0 = x1
      x1 = variance.divide(x0, defaultMathContext)
      x1 = x1.add(x0)
      x1 = x1.divide(two, defaultMathContext)
   }
   return x1
}

private fun  testMean(collection: Collection, expectedValue: BigDecimal, precision: Int): MatcherResult {
   val expected = expectedValue.stripTrailingZeros()
   val actual = if (collection.isEmpty()) BigDecimal.ZERO else calculateMean(collection).round(precision)
   return MatcherResult(
      expected.compareTo(actual) == 0,
      { "Collection should have mean $expected but was $actual" },
      {
         "Collection should not have mean $expected but was $actual"
      })
}

private fun  testVariance(
   collection: Collection,
   expectedValue: BigDecimal,
   precision: Int
): MatcherResult {
   val expected = expectedValue.stripTrailingZeros()
   val actual = if (collection.isEmpty()) BigDecimal.ZERO else calculateVariance(collection).round(precision)
   return MatcherResult(
      expected.compareTo(actual) == 0,
      { "Collection should have variance $expected but was $actual" },
      {
         "Collection should not have variance $expected but was $actual"
      })
}

private fun  testStandardDeviation(
   collection: Collection,
   expectedValue: BigDecimal,
   precision: Int
): MatcherResult {
   val expected = expectedValue.stripTrailingZeros()
   val actual = if (collection.isEmpty()) BigDecimal.ZERO else calculateStandardDeviation(collection).round(precision)
   return MatcherResult(
      expected.compareTo(actual) == 0,
      { "Collection should have standard deviation $expected but was $actual" },
      {
         "Collection should not have standard deviation $expected but was $actual"
      })
}

fun  haveMean(expectedValue: BigDecimal, precision: Int = 4) = object :
   Matcher> {
   override fun test(value: Collection): MatcherResult = testMean(value, expectedValue, precision)
}

fun  haveMean(expectedValue: Double, precision: Int = 4) = object : Matcher> {
   override fun test(value: Collection): MatcherResult = testMean(value, expectedValue.toBigDecimal(), precision)
}

fun  haveVariance(expectedValue: BigDecimal, precision: Int) = object : Matcher> {
   override fun test(value: Collection): MatcherResult = testVariance(value, expectedValue, precision)
}

fun  haveVariance(expectedValue: Double, precision: Int) = object : Matcher> {
   override fun test(value: Collection): MatcherResult = testVariance(value, expectedValue.toBigDecimal(), precision)
}

fun  haveStandardDeviation(expectedValue: BigDecimal, precision: Int) = object : Matcher> {
   override fun test(value: Collection): MatcherResult = testStandardDeviation(value, expectedValue, precision)
}

fun  haveStandardDeviation(expectedValue: Double, precision: Int) = object : Matcher> {
   override fun test(value: Collection): MatcherResult = testStandardDeviation(value, expectedValue.toBigDecimal(), precision)
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy