All files / src/ops numeric.ts

100% Statements 45/45
100% Branches 15/15
100% Functions 12/12
100% Lines 45/45

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 1611x 1x 1x 1x 1x                                             1x 1x 1x 1x             1x             1x 4x               1x 42x             1x 11x             1x 16x             1x 16x             1x 1x             1x 1x                     1x 2x                     1x 2x             1x 4x 4x 4x             1x 1x 1x 1x             1x 4x 4x 4x             1x 1x 1x 1x 1x  
import {map, monoidFold, treeCata, type Tree, type TreeFolder} from '#tree'
import {flow, pipe, type Function} from '#util'
import {Monoid} from '@effect/typeclass'
import * as NumberData from '@effect/typeclass/data/Number'
import {
  MonoidMax,
  MonoidMin,
  MonoidMultiply,
  MonoidSum,
} from '@effect/typeclass/data/Number'
 
/**
 * A running average encoded as the _sum_ of all values encountered and the
 * _count_ of all values encountered.
 * @category ops
 * @function
 */
export interface RunningAverage {
  numerator: number
  denominator: number
}
 
/**
 * A monoid for running a running average.
 * @category ops
 * @function
 */
export const MonoidAverage: Monoid.Monoid<RunningAverage> = Monoid.struct({
  numerator: NumberData.MonoidSum,
  denominator: NumberData.MonoidSum,
})
 
/**
 * A starting value for a {@link RunningAverage}.
 * @category ops
 * @function
 */
export const RunningAverage: RunningAverage = {numerator: 0, denominator: 0}
 
/**
 * Compute a running average for a single level in a numeric tree.
 * @category fold
 * @function
 */
export const averageFold: TreeFolder<RunningAverage, RunningAverage> = self =>
  pipe(self, monoidFold(MonoidAverage))
 
/**
 * Compute a running average for a single level in a numeric tree.
 * Sum the nodes of a single level in a numeric tree.
 * @category fold
 * @function
 */
export const numericSumFold: TreeFolder<number, number> = self =>
  pipe(self, monoidFold(MonoidSum))
 
/**
 * Multiply the node values of a single level in a numeric tree.
 * @category fold
 * @function
 */
export const numericProductFold: TreeFolder<number, number> = self =>
  pipe(self, monoidFold(MonoidMultiply))
 
/**
 * Find maximum node value in a level of a numeric tree.
 * @category fold
 * @function
 */
export const numericMaxFold: TreeFolder<number, number> = self =>
  pipe(self, monoidFold(MonoidMax))
 
/**
 * Find minimum node value in a level of a numeric tree.
 * @category fold
 * @function
 */
export const numericMinFold: TreeFolder<number, number> = self =>
  pipe(self, monoidFold(MonoidMin))
 
/**
 * Sum all node values in a numeric tree.
 * @category ops
 * @function
 */
export const sum: (self: Tree<number>) => number = self =>
  pipe(self, treeCata(numericSumFold))
 
/**
 * Multiply all node values in a numeric tree and return the product.
 * @category ops
 * @function
 */
export const multiply = (tree: Tree<number>): number =>
  pipe(tree, treeCata(numericProductFold))
 
/**
 * Find max node value in a numeric tree.
 * @example
 * import {max, from, of} from 'effect-tree'
 *
 * expect(max(from(1, of(2), from(3, of(4), of(5))))).toBe(5)
 * @category ops
 * @function
 */
export const max = (tree: Tree<number>): number =>
  pipe(tree, treeCata(numericMaxFold))
 
/**
 * Find min node value in a numeric tree.
 * @example
 * import {min, from, of} from 'effect-tree'
 *
 * expect(min(from(1, of(2), from(3, of(4), of(5))))).toBe(1)
 * @category ops
 * @function
 */
export const min = (tree: Tree<number>): number =>
  pipe(tree, treeCata(numericMinFold))
 
/**
 * Collapse the sum and count of a running average into the average value.
 * @category ops
 * @function
 */
export const computeRunningAverage = ({
  numerator,
  denominator,
}: RunningAverage) => (denominator === 0 ? 0 : numerator / denominator)
 
/**
 * Update the running average with a new value.
 * @category ops
 * @function
 */
export const updateRunningAverage =
  (newValue: number): Function.EndoOf<RunningAverage> =>
  average =>
    MonoidAverage.combine(average, {numerator: newValue, denominator: 1})
 
/**
 * Create a new running average from a single sample.
 * @category ops
 * @function
 */
export const toRunningAverage = (numerator: number): RunningAverage => ({
  numerator,
  denominator: 1,
})
 
/**
 * Compute the arithmetic mean of all node values in a numeric tree.
 * @category ops
 * @function
 */
export const average: (self: Tree<number>) => number = flow(
  map(toRunningAverage),
  treeCata(averageFold),
  computeRunningAverage,
)