time-to-botec

Benchmark sampling in different programming languages
Log | Files | Refs | README

XYShape.js (15013B)


      1 import sortBy from "lodash/sortBy.js";
      2 import { epsilon_float } from "./magicNumbers.js";
      3 import * as E_A from "./utility/E_A.js";
      4 import * as E_A_Floats from "./utility/E_A_Floats.js";
      5 import * as E_A_Sorted from "./utility/E_A_Sorted.js";
      6 import * as Result from "./utility/result.js";
      7 export const XYShapeError = {
      8     mapErrorArrayToError(errors) {
      9         if (errors.length === 0) {
     10             return undefined;
     11         }
     12         else if (errors.length === 1) {
     13             return errors[0];
     14         }
     15         else {
     16             return {
     17                 tag: "MultipleErrors",
     18                 errors,
     19             };
     20         }
     21     },
     22     toString(t) {
     23         switch (t.tag) {
     24             case "NotSorted":
     25                 return `${t.property} is not sorted`;
     26             case "IsEmpty":
     27                 return `${t.property} is empty`;
     28             case "NotFinite":
     29                 return `${t.property} is not finite. Example value: ${t.value}`;
     30             case "DifferentLengths":
     31                 return `${t.p1Name} and ${t.p2Name} have different lengths. ${t.p1Name} has length ${t.p1Length} and ${t.p2Name} has length ${t.p2Length}`;
     32             case "MultipleErrors":
     33                 return `Multiple Errors: ${t.errors
     34                     .map(XYShapeError.toString)
     35                     .map((r) => `[${r}]`)
     36                     .join(", ")}`;
     37         }
     38     },
     39 };
     40 const interpolate = (xMin, xMax, yMin, yMax, xIntended) => {
     41     const minProportion = (xMax - xIntended) / (xMax - xMin);
     42     const maxProportion = (xIntended - xMin) / (xMax - xMin);
     43     return yMin * minProportion + yMax * maxProportion;
     44 };
     45 const extImp = (value) => {
     46     if (value === undefined) {
     47         throw new Error("Tried to perform an operation on an empty XYShape.");
     48     }
     49     return value;
     50 };
     51 export const T = {
     52     length(t) {
     53         return t.xs.length;
     54     },
     55     empty: { xs: [], ys: [] },
     56     isEmpty(t) {
     57         return T.length(t) === 0;
     58     },
     59     minX(t) {
     60         return extImp(t.xs[0]);
     61     },
     62     maxX(t) {
     63         return extImp(t.xs[t.xs.length - 1]);
     64     },
     65     firstY(t) {
     66         return extImp(t.ys[0]);
     67     },
     68     lastY(t) {
     69         return extImp(t.ys[t.ys.length - 1]);
     70     },
     71     xTotalRange(t) {
     72         return T.maxX(t) - T.minX(t);
     73     },
     74     mapX(t, fn) {
     75         return { xs: t.xs.map(fn), ys: t.ys };
     76     },
     77     mapY(t, fn) {
     78         return { xs: t.xs, ys: t.ys.map(fn) };
     79     },
     80     mapYResult(t, fn) {
     81         const mappedYs = [];
     82         for (const y of t.ys) {
     83             const mappedY = fn(y);
     84             if (!mappedY.ok) {
     85                 return mappedY;
     86             }
     87             mappedYs.push(mappedY.value);
     88         }
     89         return Result.Ok({
     90             xs: t.xs,
     91             ys: mappedYs,
     92         });
     93     },
     94     square(t) {
     95         return T.mapX(t, (x) => x ** 2);
     96     },
     97     zip({ xs, ys }) {
     98         return E_A.zip(xs, ys);
     99     },
    100     fromArray([xs, ys]) {
    101         return { xs, ys };
    102     },
    103     fromArrays(xs, ys) {
    104         return { xs, ys };
    105     },
    106     accumulateYs(p, fn) {
    107         return T.fromArray([p.xs, E_A.accumulate(p.ys, fn)]);
    108     },
    109     concat(t1, t2) {
    110         const cxs = [...t1.xs, ...t2.xs];
    111         const cys = [...t1.ys, ...t2.ys];
    112         return { xs: cxs, ys: cys };
    113     },
    114     isEqual(t1, t2) {
    115         return E_A.isEqual(t1.xs, t2.xs) && E_A.isEqual(t1.ys, t2.ys);
    116     },
    117     fromZippedArray(pairs) {
    118         return T.fromArray(E_A.unzip(pairs));
    119     },
    120     equallyDividedXs(t, newLength) {
    121         return E_A_Floats.range(T.minX(t), T.maxX(t), newLength);
    122     },
    123     Validator: {
    124         notSortedError(p) {
    125             return {
    126                 tag: "NotSorted",
    127                 property: p,
    128             };
    129         },
    130         notFiniteError(p, exampleValue) {
    131             return {
    132                 tag: "NotFinite",
    133                 property: p,
    134                 value: exampleValue,
    135             };
    136         },
    137         isEmptyError(propertyName) {
    138             return { tag: "IsEmpty", property: propertyName };
    139         },
    140         differentLengthsError(t) {
    141             return {
    142                 tag: "DifferentLengths",
    143                 p1Name: "Xs",
    144                 p2Name: "Ys",
    145                 p1Length: t.xs.length,
    146                 p2Length: t.ys.length,
    147             };
    148         },
    149         areXsSorted(t) {
    150             return E_A_Floats.isSorted(t.xs);
    151         },
    152         areXsEmpty(t) {
    153             return t.xs.length === 0;
    154         },
    155         getNonFiniteXs(t) {
    156             return t.xs.find((v) => !Number.isFinite(v));
    157         },
    158         getNonFiniteYs(t) {
    159             return t.ys.find((v) => !Number.isFinite(v));
    160         },
    161         validate(t) {
    162             const errors = [];
    163             if (!T.Validator.areXsSorted(t)) {
    164                 errors.push(T.Validator.notSortedError("Xs"));
    165             }
    166             if (T.Validator.areXsEmpty(t)) {
    167                 errors.push(T.Validator.isEmptyError("Xs"));
    168             }
    169             if (t.xs.length !== t.ys.length) {
    170                 errors.push(T.Validator.differentLengthsError(t));
    171             }
    172             const nonFiniteX = T.Validator.getNonFiniteXs(t);
    173             if (nonFiniteX !== undefined) {
    174                 errors.push(T.Validator.notFiniteError("Xs", nonFiniteX));
    175             }
    176             const nonFiniteY = T.Validator.getNonFiniteYs(t);
    177             if (nonFiniteY !== undefined) {
    178                 errors.push(T.Validator.notFiniteError("Ys", nonFiniteY));
    179             }
    180             return XYShapeError.mapErrorArrayToError(errors);
    181         },
    182     },
    183     make(xs, ys) {
    184         const attempt = { xs, ys };
    185         const maybeError = T.Validator.validate(attempt);
    186         if (maybeError) {
    187             return Result.Err(maybeError);
    188         }
    189         else {
    190             return Result.Ok(attempt);
    191         }
    192     },
    193     makeFromZipped(values) {
    194         const [xs, ys] = E_A.unzip(values);
    195         return T.make(xs, ys);
    196     },
    197 };
    198 const Pairs = {
    199     first(t) {
    200         return [T.minX(t), T.firstY(t)];
    201     },
    202     last(t) {
    203         return [T.maxX(t), T.lastY(t)];
    204     },
    205     getBy(t, fn) {
    206         return T.zip(t).find(fn);
    207     },
    208     firstAtOrBeforeXValue(t, xValue) {
    209         const firstGreaterIndex = E_A_Sorted.firstGreaterIndex(t.xs, xValue);
    210         if (firstGreaterIndex === 0) {
    211             return;
    212         }
    213         return [t.xs[firstGreaterIndex - 1], t.ys[firstGreaterIndex - 1]];
    214     },
    215 };
    216 export const YtoX = {
    217     linear(t, y) {
    218         const firstHigherIndex = E_A_Sorted.firstGreaterIndex(t.ys, y);
    219         if (firstHigherIndex === t.ys.length) {
    220             return T.maxX(t);
    221         }
    222         else if (firstHigherIndex === 0) {
    223             return T.minX(t);
    224         }
    225         else {
    226             const lowerOrEqualIndex = firstHigherIndex - 1;
    227             if (t.ys[lowerOrEqualIndex] === y) {
    228                 return t.xs[lowerOrEqualIndex];
    229             }
    230             else {
    231                 return interpolate(t.ys[lowerOrEqualIndex], t.ys[firstHigherIndex], t.xs[lowerOrEqualIndex], t.xs[firstHigherIndex], y);
    232             }
    233         }
    234     },
    235 };
    236 export const XtoY = {
    237     stepwiseIncremental(t, x) {
    238         return Pairs.firstAtOrBeforeXValue(t, x)?.[1];
    239     },
    240     stepwiseIfAtX(t, f) {
    241         return Pairs.getBy(t, ([x]) => x === f)?.[1];
    242     },
    243     linear(t, x) {
    244         const firstHigherIndex = E_A_Sorted.firstGreaterIndex(t.xs, x);
    245         if (firstHigherIndex === t.xs.length) {
    246             return T.lastY(t);
    247         }
    248         else if (firstHigherIndex === 0) {
    249             return T.firstY(t);
    250         }
    251         else {
    252             const lowerOrEqualIndex = firstHigherIndex - 1;
    253             if (t.xs[lowerOrEqualIndex] === x) {
    254                 return t.ys[lowerOrEqualIndex];
    255             }
    256             else {
    257                 return interpolate(t.xs[lowerOrEqualIndex], t.xs[firstHigherIndex], t.ys[lowerOrEqualIndex], t.ys[firstHigherIndex], x);
    258             }
    259         }
    260     },
    261     continuousInterpolator(interpolation, extrapolation) {
    262         if (interpolation === "Linear" && extrapolation === "UseZero") {
    263             return (t, leftIndex, x) => {
    264                 if (leftIndex < 0) {
    265                     return 0;
    266                 }
    267                 else if (leftIndex >= T.length(t) - 1) {
    268                     return 0;
    269                 }
    270                 else {
    271                     const x1 = t.xs[leftIndex];
    272                     const x2 = t.xs[leftIndex + 1];
    273                     const y1 = t.ys[leftIndex];
    274                     const y2 = t.ys[leftIndex + 1];
    275                     const fraction = (x - x1) / (x2 - x1);
    276                     return y1 * (1 - fraction) + y2 * fraction;
    277                 }
    278             };
    279         }
    280         else if (interpolation === "Linear" &&
    281             extrapolation === "UseOutermostPoints") {
    282             return (t, leftIndex, x) => {
    283                 if (leftIndex < 0) {
    284                     return t.ys[0];
    285                 }
    286                 else if (leftIndex >= T.length(t) - 1) {
    287                     return t.ys[T.length(t) - 1];
    288                 }
    289                 else {
    290                     const x1 = t.xs[leftIndex];
    291                     const x2 = t.xs[leftIndex + 1];
    292                     const y1 = t.ys[leftIndex];
    293                     const y2 = t.ys[leftIndex + 1];
    294                     const fraction = (x - x1) / (x2 - x1);
    295                     return y1 * (1 - fraction) + y2 * fraction;
    296                 }
    297             };
    298         }
    299         else if (interpolation === "Stepwise" && extrapolation === "UseZero") {
    300             return (t, leftIndex, _x) => {
    301                 if (leftIndex < 0) {
    302                     return 0;
    303                 }
    304                 else if (leftIndex >= T.length(t) - 1) {
    305                     return 0;
    306                 }
    307                 else {
    308                     return t.ys[leftIndex];
    309                 }
    310             };
    311         }
    312         else if (interpolation === "Stepwise" &&
    313             extrapolation === "UseOutermostPoints") {
    314             return (t, leftIndex, _x) => {
    315                 if (leftIndex < 0) {
    316                     return t.ys[0];
    317                 }
    318                 else if (leftIndex >= T.length(t) - 1) {
    319                     return t.ys[T.length(t) - 1];
    320                 }
    321                 else {
    322                     return t.ys[leftIndex];
    323                 }
    324             };
    325         }
    326         else {
    327             throw new Error("Implementation error: invalid interpolation/extrapolation strategy combination");
    328         }
    329     },
    330     discreteInterpolator: (() => 0),
    331 };
    332 export const XsConversion = {
    333     _replaceWithXs(newXs, t) {
    334         const newYs = newXs.map((x) => XtoY.linear(t, x));
    335         return { xs: newXs, ys: newYs };
    336     },
    337     equallyDivideXByMass(integral, newLength) {
    338         return E_A_Floats.range(0, 1, newLength).map((y) => YtoX.linear(integral, y));
    339     },
    340     proportionEquallyOverX(t, newLength) {
    341         return XsConversion._replaceWithXs(T.equallyDividedXs(t, newLength), t);
    342     },
    343     proportionByProbabilityMass(t, newLength, integral) {
    344         return XsConversion._replaceWithXs(XsConversion.equallyDivideXByMass(integral, newLength), t);
    345     },
    346 };
    347 export const Zipped = {
    348     sortByY(t) {
    349         return sortBy(t, [([x, y]) => y]);
    350     },
    351     sortByX(t) {
    352         return sortBy(t, [([x, y]) => x]);
    353     },
    354     filterByX(t, testFn) {
    355         return t.filter(([x]) => testFn(x));
    356     },
    357 };
    358 export const PointwiseCombination = {
    359     combine(interpolator, fn, t1, t2) {
    360         const t1n = t1.xs.length;
    361         const t2n = t2.xs.length;
    362         const outX = [];
    363         const outY = [];
    364         let i1 = 0, i2 = 0;
    365         while (i1 < t1n || i2 < t2n) {
    366             let x, y1, y2;
    367             if (i1 > 0 && i1 === t1n && i2 === 0) {
    368                 x = t1.xs[i1 - 1] + Number.EPSILON * t1.xs[i1 - 1];
    369                 y1 = interpolator(t1, t1n, x);
    370                 y2 = interpolator(t2, -1, x);
    371                 i1++;
    372             }
    373             else if (i1 === t1n + 1 && i2 === 0) {
    374                 x = t2.xs[0] - Number.EPSILON * t2.xs[0];
    375                 y1 = interpolator(t1, t1n, x);
    376                 y2 = interpolator(t2, -1, x);
    377                 i1++;
    378             }
    379             else if (i2 > 0 && i2 === t2n && i1 === 0) {
    380                 x = t2.xs[i2 - 1] + Number.EPSILON * t2.xs[i2 - 1];
    381                 y1 = interpolator(t1, -1, x);
    382                 y2 = interpolator(t2, t2n, x);
    383                 i2++;
    384             }
    385             else if (i2 === t2n + 1 && i1 === 0) {
    386                 x = t1.xs[0] - Number.EPSILON * t1.xs[0];
    387                 y1 = interpolator(t1, -1, x);
    388                 y2 = interpolator(t2, t2n, x);
    389                 i2++;
    390             }
    391             else if (i2 >= t2n || (i1 < t1n && t1.xs[i1] < t2.xs[i2])) {
    392                 x = t1.xs[i1];
    393                 y1 = t1.ys[i1];
    394                 y2 = interpolator(t2, i2 - 1, x);
    395                 i1++;
    396             }
    397             else if (i1 >= t1n || (i2 < t2n && t1.xs[i1] > t2.xs[i2])) {
    398                 x = t2.xs[i2];
    399                 y1 = interpolator(t1, i1 - 1, x);
    400                 y2 = t2.ys[i2];
    401                 i2++;
    402             }
    403             else if (i1 < t1n && i2 < t2n && t1.xs[i1] === t2.xs[i2]) {
    404                 x = t1.xs[i1];
    405                 y1 = t1.ys[i1];
    406                 y2 = t2.ys[i2];
    407                 i1++;
    408                 i2++;
    409             }
    410             else {
    411                 throw new Error(`PointwiseCombination error: ${i1}, ${i2}`);
    412             }
    413             outX.push(x);
    414             const newY = fn(y1, y2);
    415             if (!newY.ok) {
    416                 return newY;
    417             }
    418             outY.push(newY.value);
    419         }
    420         return Result.Ok({ xs: outX, ys: outY });
    421     },
    422     addCombine(interpolator, t1, t2) {
    423         const result = PointwiseCombination.combine(interpolator, (a, b) => Result.Ok(a + b), t1, t2);
    424         if (!result.ok) {
    425             throw new Error("Add operation should never fail");
    426         }
    427         return result.value;
    428     },
    429 };
    430 export const Range = {
    431     integrateWithTriangles({ xs, ys }) {
    432         const length = xs.length;
    433         const cumulativeY = new Array(length).fill(0);
    434         for (let x = 0; x <= length - 2; x++) {
    435             cumulativeY[x + 1] =
    436                 (xs[x + 1] - xs[x]) * ((ys[x] + ys[x + 1]) / 2) + cumulativeY[x];
    437         }
    438         return { xs, ys: cumulativeY };
    439     },
    440     stepwiseToLinear({ xs, ys }) {
    441         const length = xs.length;
    442         const newXs = new Array(2 * length);
    443         const newYs = new Array(2 * length);
    444         newXs[0] = xs[0] - epsilon_float;
    445         newYs[0] = 0;
    446         newXs[1] = xs[0];
    447         newYs[1] = ys[0];
    448         for (let i = 1; i <= length - 1; i++) {
    449             newXs[i * 2] = xs[i] - epsilon_float;
    450             newYs[i * 2] = ys[i - 1];
    451             newXs[i * 2 + 1] = xs[i];
    452             newYs[i * 2 + 1] = ys[i];
    453         }
    454         return { xs: newXs, ys: newYs };
    455     },
    456 };
    457 export const Analysis = {
    458     getVarianceDangerously(t, mean, getMeanOfSquares) {
    459         const meanSquared = mean(t) ** 2;
    460         const meanOfSquares = getMeanOfSquares(t);
    461         return meanOfSquares - meanSquared;
    462     },
    463 };
    464 //# sourceMappingURL=XYShape.js.map