/*---------------------------------------------------------------------------------------------
* Copyright (c) Bentley Systems, Incorporated. All rights reserved.
* See LICENSE.md in the project root for license terms and full copyright notice.
*--------------------------------------------------------------------------------------------*/
import { Vector2d } from "./geometry3d/Point2dVector2d";
import { Vector3d } from "./geometry3d/Point3dVector3d";
import { XAndY, XYAndZ } from "./geometry3d/XYZProps";
/* eslint-disable @typescript-eslint/naming-convention */
/**
 * Enumeration of the 6 possible orderings of XYZ axis order
 * * AxisOrder encodes handedness as well. There are 3 right-handed axis orderings (XYZ, YZX, ZXY) and 3 left-handed orderings (XZY, YXZ, ZYX).
 * * Given an axis ordering, the cross product of axis _i_ with axis _i+1_ yields axis _i+2_.
 * @public
 */
export var AxisOrder;
(function (AxisOrder) {
    /** Right handed system, X then Y then Z */
    // eslint-disable-next-line @typescript-eslint/no-shadow
    AxisOrder[AxisOrder["XYZ"] = 0] = "XYZ";
    /** Right handed system, Y then Z then X */
    AxisOrder[AxisOrder["YZX"] = 1] = "YZX";
    /** Right handed system, Z then X then Y */
    AxisOrder[AxisOrder["ZXY"] = 2] = "ZXY";
    /** Left handed system, X then Z then Y */
    AxisOrder[AxisOrder["XZY"] = 4] = "XZY";
    /** Left handed system, Y then X then Z */
    AxisOrder[AxisOrder["YXZ"] = 5] = "YXZ";
    /** Left handed system, Z then Y then X */
    AxisOrder[AxisOrder["ZYX"] = 6] = "ZYX";
})(AxisOrder || (AxisOrder = {}));
/**
 * Enumeration of numeric indices of 3 axes AxisIndex.X, AxisIndex.Y, AxisIndex.Z
 * @public
 */
export var AxisIndex;
(function (AxisIndex) {
    /** x axis is index 0 */
    AxisIndex[AxisIndex["X"] = 0] = "X";
    /** y axis is index 1 */
    AxisIndex[AxisIndex["Y"] = 1] = "Y";
    /** 2 axis is index 2 */
    AxisIndex[AxisIndex["Z"] = 2] = "Z";
})(AxisIndex || (AxisIndex = {}));
/**
 * Standard views. Used in `Matrix3d.createStandardViewAxes(index: StandardViewIndex, invert: boolean)`
 * @public
 */
export var StandardViewIndex;
(function (StandardViewIndex) {
    /** X to right, Y up */
    StandardViewIndex[StandardViewIndex["Top"] = 1] = "Top";
    /** X to right, negative Y up */
    StandardViewIndex[StandardViewIndex["Bottom"] = 2] = "Bottom";
    /** Negative Y to right, Z up */
    StandardViewIndex[StandardViewIndex["Left"] = 3] = "Left";
    /** Y to right, Z up */
    StandardViewIndex[StandardViewIndex["Right"] = 4] = "Right";
    /** X to right, Z up */
    StandardViewIndex[StandardViewIndex["Front"] = 5] = "Front";
    /** Negative X to right, Z up */
    StandardViewIndex[StandardViewIndex["Back"] = 6] = "Back";
    /** Isometric: view towards origin from (-1,-1,1) */
    StandardViewIndex[StandardViewIndex["Iso"] = 7] = "Iso";
    /** Right isometric: view towards origin from (1,-1,1) */
    StandardViewIndex[StandardViewIndex["RightIso"] = 8] = "RightIso";
})(StandardViewIndex || (StandardViewIndex = {}));
/**
 * Enumeration among choice for how a coordinate transformation should incorporate scaling.
 * @public
 */
export var AxisScaleSelect;
(function (AxisScaleSelect) {
    /** All axes of unit length. */
    AxisScaleSelect[AxisScaleSelect["Unit"] = 0] = "Unit";
    /** On each axis, the vector length matches the longest side of the range of the data. */
    AxisScaleSelect[AxisScaleSelect["LongestRangeDirection"] = 1] = "LongestRangeDirection";
    /** On each axis, the vector length matches he length of the corresponding edge of the range. */
    AxisScaleSelect[AxisScaleSelect["NonUniformRangeContainment"] = 2] = "NonUniformRangeContainment";
})(AxisScaleSelect || (AxisScaleSelect = {}));
/**
 * Enumeration of possible locations of a point in the plane of a polygon.
 * @public
 */
export var PolygonLocation;
(function (PolygonLocation) {
    /** No location specified. */
    PolygonLocation[PolygonLocation["Unknown"] = 0] = "Unknown";
    /** Point is at a vertex. */
    PolygonLocation[PolygonLocation["OnPolygonVertex"] = 1] = "OnPolygonVertex";
    /** Point is on an edge (but not a vertex). */
    PolygonLocation[PolygonLocation["OnPolygonEdgeInterior"] = 2] = "OnPolygonEdgeInterior";
    /** Point is strictly inside the polygon with unknown projection. */
    PolygonLocation[PolygonLocation["InsidePolygon"] = 3] = "InsidePolygon";
    /** Point is strictly inside the polygon and projects to a vertex. */
    PolygonLocation[PolygonLocation["InsidePolygonProjectsToVertex"] = 4] = "InsidePolygonProjectsToVertex";
    /** Point is strictly inside the polygon and projects to an edge (but not a vertex). */
    PolygonLocation[PolygonLocation["InsidePolygonProjectsToEdgeInterior"] = 5] = "InsidePolygonProjectsToEdgeInterior";
    /** Point is strictly outside the polygon with unknown projection. */
    PolygonLocation[PolygonLocation["OutsidePolygon"] = 6] = "OutsidePolygon";
    /** Point is strictly outside the polygon and projects to a vertex. */
    PolygonLocation[PolygonLocation["OutsidePolygonProjectsToVertex"] = 7] = "OutsidePolygonProjectsToVertex";
    /** Point is strictly outside the polygon and projects to an edge (but not a vertex). */
    PolygonLocation[PolygonLocation["OutsidePolygonProjectsToEdgeInterior"] = 8] = "OutsidePolygonProjectsToEdgeInterior";
})(PolygonLocation || (PolygonLocation = {}));
/**
 * Class containing static methods for typical numeric operations.
 * * Experimentally, methods like Geometry.hypotenuse are observed to be faster than the system intrinsics.
 * * This is probably due to
 *    * Fixed length arg lists
 *    * strongly typed parameters
 * @public
 */
export class Geometry {
    /** Tolerance for small distances in metric coordinates. */
    static smallMetricDistance = 1.0e-6;
    /** Square of [[smallMetricDistance]]. */
    static smallMetricDistanceSquared = 1.0e-12;
    /** Tolerance for small angle measured in radians. */
    static smallAngleRadians = 1.0e-12;
    /** Square of [[smallAngleRadians]]. */
    static smallAngleRadiansSquared = 1.0e-24;
    /** Tolerance for small angle measured in degrees. */
    static smallAngleDegrees = 5.7e-11;
    /** Tolerance for small angle measured in arc-seconds. */
    static smallAngleSeconds = 2e-7;
    /** Numeric value that may be considered zero for fractions between 0 and 1. */
    static smallFraction = 1.0e-10;
    /** Tight tolerance near machine precision (unitless). Useful for snapping values, e.g., to 0 or 1. */
    static smallFloatingPoint = 1.0e-15;
    /** Radians value for full circle 2PI radians minus [[smallAngleRadians]]. */
    static fullCircleRadiansMinusSmallAngle = 2.0 * Math.PI - Geometry.smallAngleRadians;
    /**
     * Numeric value that may be considered large for a ratio of numbers.
     * * Note that the allowed result value is vastly larger than 1.
     */
    static largeFractionResult = 1.0e10;
    /**
     * Numeric value that may considered large for numbers expected to be coordinates.
     * * This allows larger results than [[largeFractionResult]].
     */
    static largeCoordinateResult = 1.0e13;
    /**
     * Numeric value that may considered infinite for metric coordinates.
     * @deprecated in 4.9.0 - will not be removed until after 2026-06-13. Use [[largeCoordinateResult]].
     * * This coordinate should be used only as a placeholder indicating "at infinity" -- computing actual
     * points at this coordinate invites numerical problems.
     */
    static hugeCoordinate = 1.0e12;
    /** Test if the absolute value of x is at least [[largeCoordinateResult]]. */
    static isLargeCoordinateResult(x) {
        return x >= this.largeCoordinateResult || x <= -this.largeCoordinateResult;
    }
    /**
     * Test if the absolute value of x is at least [[largeCoordinateResult]].
     * @deprecated in 4.9.0 - will not be removed until after 2026-06-13. Use [[isLargeCoordinateResult]].
     */
    static isHugeCoordinate(x) {
        return Geometry.isLargeCoordinateResult(x);
    }
    /** Test if a number is odd */
    static isOdd(x) {
        return (x & (0x01)) === 1; // bitwise operation
    }
    /**
     * Correct a small distance.
     * @param distance metric value to test
     * @param replacement value to return if `distance` is too small. Default is zero.
     * @returns `distance` if it is defined and has absolute value greater than [[smallMetricDistance]];
     * otherwise returns `replacement`
     */
    static correctSmallMetricDistance(distance, replacement = 0.0) {
        if (distance === undefined || Math.abs(distance) <= Geometry.smallMetricDistance) {
            return replacement;
        }
        return distance;
    }
    /**
     * Correct a small fraction.
     * @param fraction fraction to test
     * @param replacement value to return if `fraction` is too small. Default is zero.
     * @returns `fraction` if it is defined and has absolute value greater than [[Geometry.smallFraction]];
     * otherwise returns `replacement`
     */
    static correctSmallFraction(fraction, replacement = 0.0) {
        if (fraction === undefined || Math.abs(fraction) <= Geometry.smallFraction) {
            return replacement;
        }
        return fraction;
    }
    /**
     * Compute the inverse of `distance`, checking for safe division.
     * @returns `1/distance` if the absolute value of `distance` exceeds [[smallMetricDistance]];
     * otherwise returns `undefined`.
     */
    static inverseMetricDistance(distance) {
        return (Math.abs(distance) <= Geometry.smallMetricDistance) ? undefined : 1.0 / distance;
    }
    /**
     * Return the inverse of `distanceSquared`, checking for safe division.
     * @returns `1/distanceSquared` if the absolute value of `distanceSquared` exceeds [[smallMetricDistanceSquared]];
     * otherwise returns `undefined`.
     */
    static inverseMetricDistanceSquared(distanceSquared) {
        return (Math.abs(distanceSquared) <= Geometry.smallMetricDistanceSquared) ? undefined : 1.0 / distanceSquared;
    }
    /**
     * Boolean test for metric coordinate near-equality (i.e., if `x` and `y` are almost equal) using `tolerance`.
     * * [[smallMetricDistance]] is used if tolerance is `undefined`.
     */
    static isSameCoordinate(x, y, tolerance = Geometry.smallMetricDistance) {
        let d = x - y;
        if (d < 0)
            d = -d;
        return d <= tolerance;
    }
    /**
     * Boolean test for metric coordinate near-equality (i.e., if `x` and `y` are almost equal) using
     * `tolerance = toleranceFactor * smallMetricDistance`
     * */
    static isSameCoordinateWithToleranceFactor(x, y, toleranceFactor) {
        return Geometry.isSameCoordinate(x, y, toleranceFactor * Geometry.smallMetricDistance);
    }
    /**
     * Boolean test for metric coordinate pair near-equality (i.e., if `x0` and `x1` are almost equal
     * and `y0` and `y1` are almost equal) using `tolerance`.
     * * [[smallMetricDistance]] is used if tolerance is `undefined`.
     */
    static isSameCoordinateXY(x0, y0, x1, y1, tolerance = Geometry.smallMetricDistance) {
        let d = x1 - x0;
        if (d < 0)
            d = -d;
        if (d > tolerance)
            return false;
        d = y1 - y0;
        if (d < 0)
            d = -d;
        return d <= tolerance;
    }
    /**
     * Boolean test for squared metric coordinate near-equality (i.e., if `sqrt(x)` and `sqrt(y)` are
     * almost equal) using `tolerance`.
     * * [[smallMetricDistance]] is used if tolerance is `undefined`.
     */
    static isSameCoordinateSquared(x, y, tolerance = Geometry.smallMetricDistance) {
        return Math.abs(Math.sqrt(x) - Math.sqrt(y)) <= tolerance;
    }
    /**
     * Boolean test for small `dataA.distance(dataB)` within `tolerance`.
     * * [[smallMetricDistance]] is used if tolerance is `undefined`.
     */
    static isSamePoint3d(dataA, dataB, tolerance = Geometry.smallMetricDistance) {
        return dataA.distance(dataB) <= tolerance;
    }
    /**
     * Boolean test for small xyz-distance within `tolerance`.
     * * [[smallMetricDistance]] is used if tolerance is `undefined`.
     * * Note that Point3d and Vector3d are both derived from XYZ, so this method tolerates mixed types.
     */
    static isSameXYZ(dataA, dataB, tolerance = Geometry.smallMetricDistance) {
        return dataA.distance(dataB) <= tolerance;
    }
    /**
     * Boolean test for small xy-distance (ignoring z) within `tolerance`.
     * * [[smallMetricDistance]] is used if tolerance is `undefined`.
     */
    static isSamePoint3dXY(dataA, dataB, tolerance = Geometry.smallMetricDistance) {
        return dataA.distanceXY(dataB) <= tolerance;
    }
    /**
     * Boolean test for small xyz-distance within `tolerance`.
     * * [[smallMetricDistance]] is used if tolerance is `undefined`.
     */
    static isSameVector3d(dataA, dataB, tolerance = Geometry.smallMetricDistance) {
        return dataA.distance(dataB) <= tolerance;
    }
    /**
     * Boolean test for small xy-distance within `tolerance`.
     * * [[smallMetricDistance]] is used if tolerance is `undefined`.
     */
    static isSamePoint2d(dataA, dataB, tolerance = Geometry.smallMetricDistance) {
        return dataA.distance(dataB) <= tolerance;
    }
    /**
     * Boolean test for small xy-distance within `tolerance`.
     * * [[smallMetricDistance]] is used if tolerance is `undefined`.
     */
    static isSameVector2d(dataA, dataB, tolerance = Geometry.smallMetricDistance) {
        return dataA.distance(dataB) <= tolerance;
    }
    /**
     * Lexical comparison of (a.x, a.y) and (b.x, b.y) with x as first test and y as second (z is ignored).
     * * This is appropriate for a horizontal sweep in the plane.
     */
    static lexicalXYLessThan(a, b) {
        if (a.x < b.x)
            return -1;
        else if (a.x > b.x)
            return 1;
        if (a.y < b.y)
            return -1;
        else if (a.y > b.y)
            return 1;
        return 0;
    }
    /**
     * Lexical comparison of (a.x, a.y) and (b.x, b.y) with y as first test and x as second (z is ignored).
     * * This is appropriate for a vertical sweep in the plane.
     */
    static lexicalYXLessThan(a, b) {
        if (a.y < b.y)
            return -1;
        else if (a.y > b.y)
            return 1;
        if (a.x < b.x)
            return -1;
        else if (a.x > b.x)
            return 1;
        return 0;
    }
    /** Lexical comparison of (a.x, a.y, a.z) and (b.x, b.y, b.z) with x as first test, y as second, and z as third. */
    static lexicalXYZLessThan(a, b) {
        if (a.x < b.x)
            return -1;
        else if (a.x > b.x)
            return 1;
        if (a.y < b.y)
            return -1;
        else if (a.y > b.y)
            return 1;
        if (a.z < b.z)
            return -1;
        else if (a.z > b.z)
            return 1;
        return 0;
    }
    /**
     * Constructor for a lexical comparison with tolerance (x then y then z).
     * @param distanceTol tolerance for comparing coordinates. Default value is [[Geometry.smallMetricDistance]].
     * @returns comparison function useful for ordered map callbacks.
     */
    static compareXYZ(distanceTol = Geometry.smallMetricDistance) {
        return (p0, p1) => {
            if (XYAndZ.almostEqual(p0, p1, distanceTol))
                return 0;
            if (!Geometry.isSameCoordinate(p0.x, p1.x, distanceTol)) {
                if (p0.x < p1.x)
                    return -1;
                if (p0.x > p1.x)
                    return 1;
            }
            if (!Geometry.isSameCoordinate(p0.y, p1.y, distanceTol)) {
                if (p0.y < p1.y)
                    return -1;
                if (p0.y > p1.y)
                    return 1;
            }
            if (!Geometry.isSameCoordinate(p0.z, p1.z, distanceTol)) {
                if (p0.z < p1.z)
                    return -1;
                if (p0.z > p1.z)
                    return 1;
            }
            return 0;
        };
    }
    /**
     * Constructor for a lexical comparison with tolerance (x then y).
     * @param distanceTol tolerance for comparing coordinates. Default value is [[Geometry.smallMetricDistance]].
     * @returns comparison function useful for ordered map callbacks.
     */
    static compareXY(distanceTol = Geometry.smallMetricDistance) {
        return (p0, p1) => {
            if (XAndY.almostEqual(p0, p1, distanceTol))
                return 0;
            if (!Geometry.isSameCoordinate(p0.x, p1.x, distanceTol)) {
                if (p0.x < p1.x)
                    return -1;
                if (p0.x > p1.x)
                    return 1;
            }
            if (!Geometry.isSameCoordinate(p0.y, p1.y, distanceTol)) {
                if (p0.y < p1.y)
                    return -1;
                if (p0.y > p1.y)
                    return 1;
            }
            return 0;
        };
    }
    /**
     * Test if `value` is at most [[smallFraction]] in absolute value.
     * * This is appropriate if `value` is known to be a fraction.
     */
    static isSmallRelative(value) {
        return Math.abs(value) <= Geometry.smallFraction;
    }
    /** Test if `value` is at most [[smallAngleRadians]] in absolute value. */
    static isSmallAngleRadians(value) {
        return Math.abs(value) <= Geometry.smallAngleRadians;
    }
    /** Test if `value` is at most [[smallAngleRadiansSquared]] in absolute value. */
    static isSmallAngleRadiansSquared(value) {
        return Math.abs(value) <= Geometry.smallAngleRadiansSquared;
    }
    /**
     * Returns `true` if both values are `undefined` or if both are defined and almost equal within tolerance.
     * If one is `undefined` and the other is not, then `false` is returned.
     */
    static isAlmostEqualOptional(a, b, tolerance) {
        if (a !== undefined && b !== undefined) {
            if (Math.abs(a - b) > tolerance)
                return false;
        }
        else {
            if (a !== undefined || b !== undefined)
                return false;
        }
        return true;
    }
    /**
     * Toleranced equality test.
     * @param tolerance _relative_ tolerance. Default value is [[smallAngleRadians]].
     * @returns true if and only if `a` and `b` are almost equal.
     */
    static isAlmostEqualNumber(a, b, tolerance = Geometry.smallAngleRadians) {
        const sumAbs = 1.0 + Math.abs(a) + Math.abs(b);
        return Math.abs(a - b) <= tolerance * sumAbs;
    }
    /**
     * Toleranced test for equality to at least one of two numbers.
     * @param tolerance _relative_ tolerance. Default value is [[smallAngleRadians]].
     * @returns true if and only if `a` and `b` are almost equal, or `a` and `c` are almost equal.
     */
    static isAlmostEqualEitherNumber(a, b, c, tolerance = Geometry.smallAngleRadians) {
        return this.isAlmostEqualNumber(a, b, tolerance) || this.isAlmostEqualNumber(a, c, tolerance);
    }
    /**
     * Toleranced test for equality to any value in `values`.
     * @param a value to test
     * @param values array of values to test against, or an object that provides the i_th value, where 0 <= i < length.
     * @param tolerance _relative_ tolerance. Default value is [[smallAngleRadians]].
     * @returns true if and only if `a` is almost equal to at least one value supplied by `iterator`.
     */
    static isAlmostEqualAnyNumber(a, values, tolerance = Geometry.smallAngleRadians) {
        const value = Array.isArray(values) ? (i) => values[i] : values.iter;
        for (let i = 0; i < values.length; i++)
            if (this.isAlmostEqualNumber(a, value(i), tolerance))
                return true;
        return false;
    }
    /**
     * Toleranced equality test for xy points.
     * @param a first point
     * @param b second point
     * @param tolerance _relative_ coordinate tolerance. Default value is [[smallAngleRadians]].
     */
    static isAlmostEqualXAndY(a, b, tolerance = Geometry.smallAngleRadians) {
        const tol = tolerance * (1.0 + Math.abs(a.x) + Math.abs(b.x) + Math.abs(a.y) + Math.abs(b.y));
        return (Math.abs(a.x - b.x) <= tol) && (Math.abs(a.y - b.y) <= tol);
    }
    /**
     * Test if a distance is smaller than `tolerance` (or equal).
     * @param tolerance distance tolerance. Default value is [[smallMetricDistance]].
     */
    static isDistanceWithinTol(distance, tolerance = Geometry.smallMetricDistance) {
        return Math.abs(distance) <= tolerance;
    }
    /** Test if a distance is smaller than [[smallMetricDistance]] (or equal). */
    static isSmallMetricDistance(distance) {
        return Math.abs(distance) <= Geometry.smallMetricDistance;
    }
    /** Test if a squared distance is smaller than [[smallMetricDistanceSquared]] (or equal). */
    static isSmallMetricDistanceSquared(distanceSquared) {
        return Math.abs(distanceSquared) <= Geometry.smallMetricDistanceSquared;
    }
    /**
     * Return `axis modulo 3` with proper handling of negative indices
     * ..., -3:x, -2:y, -1:z, 0:x, 1:y, 2:z, 3:x, 4:y, 5:z, 6:x, 7:y, 8:z, ...
     */
    static cyclic3dAxis(axis) {
        /* Direct test for the most common cases to avoid more expensive modulo operation */
        if (axis >= 0) {
            if (axis < 3)
                return axis;
            if (axis < 6)
                return axis - 3;
            return axis % 3;
        }
        const j = axis + 3;
        if (j >= 0)
            return j;
        return 2 - ((-axis - 1) % 3);
    }
    /**
     * Return the `AxisOrder` for which `axisIndex` is the first named axis.
     * * `axisIndex === 0` returns `AxisOrder.XYZ`
     * * `axisIndex === 1` returns `AxisOrder.YZX`
     * * `axisIndex === 2` returns `AxisOrder.ZXY`
     */
    static axisIndexToRightHandedAxisOrder(axisIndex) {
        if (axisIndex === 0)
            return AxisOrder.XYZ;
        if (axisIndex === 1)
            return AxisOrder.YZX;
        if (axisIndex === 2)
            return AxisOrder.ZXY;
        return Geometry.axisIndexToRightHandedAxisOrder(Geometry.cyclic3dAxis(axisIndex));
    }
    /** Return the largest signed value among `a`, `b`, and `c` */
    static maxXYZ(a, b, c) {
        let max = a;
        if (b > max)
            max = b;
        if (c > max)
            max = c;
        return max;
    }
    /** Return the smallest signed value among `a`, `b`, and `c` */
    static minXYZ(a, b, c) {
        let min = a;
        if (b < min)
            min = b;
        if (c < min)
            min = c;
        return min;
    }
    /** Return the largest signed value among `a` and `b` */
    static maxXY(a, b) {
        let max = a;
        if (b > max)
            max = b;
        return max;
    }
    /** Return the smallest signed value among `a` and `b` */
    static minXY(a, b) {
        let min = a;
        if (b < min)
            min = b;
        return min;
    }
    /** Return the largest absolute value among `x`, `y`, and `z` */
    static maxAbsXYZ(x, y, z) {
        return Geometry.maxXYZ(Math.abs(x), Math.abs(y), Math.abs(z));
    }
    /** Return the largest absolute value among `x` and `y` */
    static maxAbsXY(x, y) {
        return Geometry.maxXY(Math.abs(x), Math.abs(y));
    }
    /** Return the largest absolute distance from `a` to either of `b0` or `b1` */
    static maxAbsDiff(a, b0, b1) {
        return Math.max(Math.abs(a - b0), Math.abs(a - b1));
    }
    /**
     * Examine the sign of `x`.
     * * If `x` is negative, return `outNegative`
     * * If `x` is true zero, return `outZero`
     * * If `x` is positive, return `outPositive`
     */
    static split3WaySign(x, outNegative, outZero, outPositive) {
        if (x < 0)
            return outNegative;
        if (x > 0.0)
            return outPositive;
        return outZero;
    }
    /**
     * Examine the value (particularly sign) of x.
     * * If x is negative, return -1
     * * If x is true zero, return 0
     * * If x is positive, return 1
     */
    static split3Way01(x, tolerance = Geometry.smallMetricDistance) {
        if (x > tolerance)
            return 1;
        if (x < -tolerance)
            return -1;
        return 0;
    }
    /** Return the square of x */
    static square(x) {
        return x * x;
    }
    /**
     * Return the hypotenuse (i.e., `sqrt(x*x + y*y)`).
     * * This is much faster than `Math.hypot(x,y)`.
     */
    static hypotenuseXY(x, y) {
        return Math.sqrt(x * x + y * y);
    }
    /** Return the squared hypotenuse (i.e., `x*x + y*y`). */
    static hypotenuseSquaredXY(x, y) {
        return x * x + y * y;
    }
    /**
     * Return the hypotenuse (i.e., `sqrt(x*x + y*y + z*z)`).
     * * This is much faster than `Math.hypot(x,y,z)`.
     */
    static hypotenuseXYZ(x, y, z) {
        return Math.sqrt(x * x + y * y + z * z);
    }
    /** Return the squared hypotenuse (i.e., `x*x + y*y + z*z`). */
    static hypotenuseSquaredXYZ(x, y, z) {
        return x * x + y * y + z * z;
    }
    /**
     * Return the full 4d hypotenuse (i.e., `sqrt(x*x + y*y + z*z + w*w)`).
     * * This is much faster than `Math.hypot(x,y,z,w)`.
     */
    static hypotenuseXYZW(x, y, z, w) {
        return Math.sqrt(x * x + y * y + z * z + w * w);
    }
    /** Return the squared hypotenuse (i.e., `x*x + y*y + z*z + w*w`). */
    static hypotenuseSquaredXYZW(x, y, z, w) {
        return x * x + y * y + z * z + w * w;
    }
    /**
     * Return the distance between xy points given as numbers.
     * @param x0 x coordinate of point 0
     * @param y0 y coordinate of point 0
     * @param x1 x coordinate of point 1
     * @param y1 y coordinate of point 1
     */
    static distanceXYXY(x0, y0, x1, y1) {
        return Geometry.hypotenuseXY(x1 - x0, y1 - y0);
    }
    /**
     * Return the squared distance between xy points given as numbers.
     * @param x0 x coordinate of point 0
     * @param y0 y coordinate of point 0
     * @param x1 x coordinate of point 1
     * @param y1 y coordinate of point 1
     */
    static distanceSquaredXYXY(x0, y0, x1, y1) {
        return Geometry.hypotenuseSquaredXY(x1 - x0, y1 - y0);
    }
    /**
     * Return the distance between xyz points given as numbers.
     * @param x0 x coordinate of point 0
     * @param y0 y coordinate of point 0
     * @param z0 z coordinate of point 0
     * @param x1 x coordinate of point 1
     * @param y1 y coordinate of point 1
     * @param z1 z coordinate of point 1
     */
    static distanceXYZXYZ(x0, y0, z0, x1, y1, z1) {
        return Geometry.hypotenuseXYZ(x1 - x0, y1 - y0, z1 - z0);
    }
    /**
     * Returns the triple product of 3 vectors provided as x,y,z number sequences.
     * * The triple product is the determinant of the 3x3 matrix with the 9 numbers (3 vectors placed in 3 rows).
     * * The triple product is positive if the 3 vectors form a right handed coordinate system.
     * * The triple product is negative if the 3 vectors form a left handed coordinate system.
     * * Treating the 9 numbers as 3 vectors U, V, W, any of these formulas gives the same result:
     *     * U dot (V cross W)
     *     * V dot (W cross U)
     *     * W dot (U cross V)
     *     * -U dot (W cross V)
     *     * -V dot (U cross W)
     *     * -W dot (V cross U)
     * * Note the negative in the last 3 formulas. Reversing cross product order changes the sign.
     * * The triple product is 6 times the (signed) volume of the tetrahedron with the three vectors as edges from a
     * common vertex.
     */
    static tripleProduct(ux, uy, uz, vx, vy, vz, wx, wy, wz) {
        return ux * (vy * wz - vz * wy)
            + uy * (vz * wx - vx * wz)
            + uz * (vx * wy - vy * wx);
    }
    /** Returns the determinant of the 4x4 matrix unrolled as the 16 parameters */
    static determinant4x4(xx, xy, xz, xw, yx, yy, yz, yw, zx, zy, zz, zw, wx, wy, wz, ww) {
        return xx * this.tripleProduct(yy, yz, yw, zy, zz, zw, wy, wz, ww)
            - yx * this.tripleProduct(xy, xz, xw, zy, zz, zw, wy, wz, ww)
            + zx * this.tripleProduct(xy, xz, xw, yy, yz, yw, wy, wz, ww)
            - wx * this.tripleProduct(xy, xz, xw, yy, yz, yw, zy, zz, zw);
    }
    /**
     * Returns the determinant of 3x3 matrix with first and second rows created from the 3 xy points and the third
     * row created from the 3 numbers:
     *      [columnA.x   columnB.x   columnC.x]
     *      [columnA.y   columnB.y   columnC.y]
     *      [ weightA     weightB     weightC ]
     */
    static tripleProductXYW(columnA, weightA, columnB, weightB, columnC, weightC) {
        return Geometry.tripleProduct(columnA.x, columnB.x, columnC.x, columnA.y, columnB.y, columnC.y, weightA, weightB, weightC);
    }
    /**
     * Returns the determinant of 3x3 matrix columns created by the given `Point4d` ignoring the z part:
     *      [columnA.x   columnB.x   columnC.x]
     *      [columnA.y   columnB.y   columnC.y]
     *      [columnA.w   columnB.w   columnC.w]
     */
    static tripleProductPoint4dXYW(columnA, columnB, columnC) {
        return Geometry.tripleProduct(columnA.x, columnB.x, columnC.x, columnA.y, columnB.y, columnC.y, columnA.w, columnB.w, columnC.w);
    }
    /**
     * 2D cross product of vectors with the vectors presented as numbers.
     * * Sign of 2d cross product is positive <=> sweeping from first vector to second vector is ccw orientation.
     * * Sign of 2d cross product is negative <=> sweeping from first vector to second vector is clockwise orientation.
     * * 2d cross product is 0 <=> parallel/antiparallel vectors.
     */
    static crossProductXYXY(ux, uy, vx, vy) {
        return ux * vy - uy * vx;
    }
    /** 3D cross product of vectors with the vectors presented as numbers. */
    static crossProductXYZXYZ(ux, uy, uz, vx, vy, vz, result) {
        return Vector3d.create(uy * vz - uz * vy, uz * vx - ux * vz, ux * vy - uy * vx, result);
    }
    /**
     * 2D cross product of vectors with the vectors presented with common origin point, and two target points.
     * @see crossProductXYXY for interpretations of the result.
     */
    static crossProductToPointsXY(origin, target0, target1) {
        return this.crossProductXYXY(target0.x - origin.x, target0.y - origin.y, target1.x - origin.x, target1.y - origin.y);
    }
    /** Magnitude of 3D cross product of vectors with the vectors presented as numbers. */
    static crossProductMagnitude(ux, uy, uz, vx, vy, vz) {
        return Geometry.hypotenuseXYZ(uy * vz - uz * vy, uz * vx - ux * vz, ux * vy - uy * vx);
    }
    /**
     * 2D dot product of vectors with the vectors presented as numbers.
     * * Sign of dot product is positive <=> vectorA points into the same half-space as vectorB.
     * * Sign of dot product is negative <=> vectorA points into opposite half-space as vectorB.
     * * Dot product is 0 <=> perpendicular vectors.
     * * **Note:** half-space is defined in terms of a vector, by the perpendicular plane at its origin (it splits
     * the universe into two halves).
     */
    static dotProductXYXY(ux, uy, vx, vy) {
        return ux * vx + uy * vy;
    }
    /** 3D dot product of vectors with the vectors presented as numbers. */
    static dotProductXYZXYZ(ux, uy, uz, vx, vy, vz) {
        return ux * vx + uy * vy + uz * vz;
    }
    /**
     * Return the mean curvature for two radii.
     * * Curvature is the reciprocal of radius.
     * * 0 radius implies 0 curvature.
     * @param r0 first radius
     * @param r1 second radius
     */
    static meanCurvatureOfRadii(r0, r1) {
        return 0.5 * (this.safeDivideFraction(1, r0, 0) + this.safeDivideFraction(1, r1, 0));
    }
    /**
     * Returns curvature from the first and second derivative vectors.
     * * If U is the first derivative and V is the second derivative, the curvature is defined as:
     *     * `|| U x V || / || U ||^3`.
     * * Math details can be found at https://en.wikipedia.org/wiki/Curvature#General_expressions
     * @param ux first derivative x component
     * @param uy first derivative y component
     * @param uz first derivative z component
     * @param vx second derivative x component
     * @param vy second derivative y component
     * @param vz second derivative z component
     */
    static curvatureMagnitude(ux, uy, uz, vx, vy, vz) {
        let q = uy * vz - uz * vy;
        let sum = q * q;
        q = uz * vx - ux * vz;
        sum += q * q;
        q = ux * vy - uy * vx;
        sum += q * q;
        const magUxV = Math.sqrt(sum);
        const magU = Math.sqrt(ux * ux + uy * uy + uz * uz);
        const magUCubed = magU * magU * magU;
        if (magUCubed > Geometry.smallAngleRadians * magUxV)
            return magUxV / magUCubed;
        return 0;
    }
    /**
     * Clamp to (min(a,b), max(a,b)).
     * * Always returns a number between `a` and `b`.
     * @param value value to clamp
     * @param a smallest allowed output if `a < b` or largest allowed output if `a > b`
     * @param b largest allowed output if `a < b` or smallest allowed output if `a > b`
     */
    static clampToStartEnd(value, a, b) {
        if (a > b)
            return Geometry.clampToStartEnd(value, b, a);
        if (value < a)
            return a;
        if (b < value)
            return b;
        return value;
    }
    /**
     * Clamp value to (min, max) with no test for order of (min, max).
     * * Always returns a number between `min` and `max`.
     * @param value value to clamp
     * @param min smallest allowed output
     * @param max largest allowed output
     */
    static clamp(value, min, max) {
        return Math.max(min, Math.min(max, value));
    }
    /** If given a `value`, return it. If given `undefined`, return `defaultValue`. */
    static resolveNumber(value, defaultValue = 0) {
        return value !== undefined ? value : defaultValue;
    }
    /** If given a `value`, return it. If given `undefined`, return `defaultValue`. */
    static resolveValue(value, defaultValue) {
        return value !== undefined ? value : defaultValue;
    }
    /** If given `value` matches the `targetValue`, return `undefined`. Otherwise return the `value`. */
    static resolveToUndefined(value, targetValue) {
        return value === targetValue ? undefined : value;
    }
    /**
     * Simple interpolation between values `a` and `b` with fraction `f`.
     * * If `f = 0`, then `a` is returned and if `f = 1`, then `b` is returned.
     * * For maximum accuracy, we choose `a` or `b` as starting point based on fraction `f`.
     */
    static interpolate(a, f, b) {
        return f <= 0.5 ? a + f * (b - a) : b - (1.0 - f) * (b - a);
    }
    /**
     * Interpolate the specified byte of two integers (e.g., colors).
     * * Extract a single byte from each integer by shifting to the right by `shiftBits`, then masking off the low 8 bits.
     * * Interpolate the number, truncate to floor, and mask off the low 8 bits.
     * * Move interpolated byte back into position by shifting to the left by `shiftBits`.
     * @internal
     */
    static interpolateByte(color0, fraction, color1, shiftBits) {
        color0 = (color0 >>> shiftBits) & 0xFF;
        color1 = (color1 >>> shiftBits) & 0xFF;
        const color = Math.floor(color0 + fraction * (color1 - color0)) & 0xFF; // in range [0,255]
        return color << shiftBits;
    }
    /**
     * Interpolate each byte of color0 and color1 as integers.
     * @param color0 32-bit RGBA color0
     * @param fraction fractional position. This is clamped to 0..1 to prevent byte values outside their 0..255 range.
     * @param color1 32-bit RGBA color1
     */
    static interpolateColor(color0, fraction, color1) {
        fraction = Geometry.clamp(fraction, 0, 1); // do not allow fractions outside the individual byte ranges
        const byte0 = this.interpolateByte(color0, fraction, color1, 0); // red
        const byte1 = this.interpolateByte(color0, fraction, color1, 8); // green
        const byte2 = this.interpolateByte(color0, fraction, color1, 16); // blue
        const byte3 = this.interpolateByte(color0, fraction, color1, 24); // alpha
        return (byte0 | byte1 | byte2 | byte3);
    }
    /**
     * Given an `axisOrder` (e.g. XYZ, YZX, etc) and an `index`, return the `axis` at the given index.
     * * For example, if `axisOrder = XYZ`, then for index 0 return `X` (or axis 0), for index 1 return
     * `Y` (or axis 1), and for index 2 return `Z` (or axis 2).
     * * Another example: if `axisOrder = ZXY`, then for index 0 return `Z` (or axis 2), for index 1 return
     * `X` (or axis 0), and for index 2 return `Y` (or axis 1).
     * * For indexes greater than 2 or smaller than 0, it return cyclic axis. See [[Geometry.cyclic3dAxis]]
     * for more info.
     */
    static axisOrderToAxis(order, index) {
        const axis = order <= AxisOrder.ZXY ? order + index : (order - AxisOrder.XZY) - index;
        return Geometry.cyclic3dAxis(axis);
    }
    /**
     * Return `a` modulo `period`.
     * * Both `a` and `period` can be negative.
     * * This function can be faster than the `%` operator for the common case when `p > 0` and `-p < a < 2p`.
     */
    static modulo(a, period) {
        // period is negative
        if (period <= 0) {
            if (period === 0)
                return a;
            return -Geometry.modulo(-a, -period);
        }
        // period is positive
        if (a >= 0) {
            if (a < period) // "0 < a < period"
                return a;
            if (a < 2 * period) // "0 < period < a < 2*period"
                return a - period;
        }
        else { // "-period < a < 0"
            a += period;
            if (a > 0)
                return a;
        }
        // "0 < 2*period < a" or "a < -period < 0"
        const m = Math.floor(a / period);
        return a - m * period;
    }
    /** Return 0 if the value is `undefined` and 1 if the value is defined. */
    static defined01(value) {
        return value === undefined ? 0 : 1;
    }
    /**
     * Return `numerator` divided by `denominator`.
     * @param numerator the numerator
     * @param denominator the denominator
     * @returns return `numerator/denominator` but if the ratio exceeds [[largeFractionResult]],
     * return `undefined`.
     */
    static conditionalDivideFraction(numerator, denominator) {
        if (0 === denominator)
            return undefined;
        if (Math.abs(denominator) * Geometry.largeFractionResult >= Math.abs(numerator))
            return numerator / denominator;
        return undefined;
    }
    /**
     * Return `numerator` divided by `denominator`.
     * @param numerator the numerator
     * @param denominator the denominator
     * @returns return `numerator/denominator` but if the ratio exceeds [[largeFractionResult]],
     * return `defaultResult`.
     */
    static safeDivideFraction(numerator, denominator, defaultResult) {
        const ratio = Geometry.conditionalDivideFraction(numerator, denominator);
        if (ratio !== undefined)
            return ratio;
        return defaultResult;
    }
    /**
     * Return `numerator` divided by `denominator` (with a given `largestResult`).
     * @param numerator the numerator
     * @param denominator the denominator
     * @param largestResult the ratio threshold, defaults to [[largeCoordinateResult]]
     * @returns return `numerator/denominator` but if the ratio exceeds `largestResult`, return `undefined`.
     */
    static conditionalDivideCoordinate(numerator, denominator, largestResult = Geometry.largeCoordinateResult) {
        if (0 === denominator)
            return undefined;
        if (Math.abs(denominator * largestResult) >= Math.abs(numerator))
            return numerator / denominator;
        return undefined;
    }
    /**
     * Return solution(s) of equation `constCoff + cosCoff*c + sinCoff*s = 0` for `c` and `s` with the
     * constraint `c*c + s*s = 1`.
     * * There could be 0, 1, or 2 solutions. Return `undefined` if there is no solution.
     */
    static solveTrigForm(constCoff, cosCoff, sinCoff) {
        /**
         * Solutions can be found by finding the intersection of line "ax + by + d = 0" and unit circle "x^2 + y^2 = 1".
         * From the line equation we have "y = (-ax - d) / b". By replacing this into the circle equation we get
         * "x^2 + (ax+d)^2/b^2 = 1". If we solve this by quadratic formula we get
         *      x = (-ad +- b*sqrt(a^2+b^2-d^2)) / (a^2+b^2)
         *      y = (-ad -+ a*sqrt(a^2+b^2-d^2)) / (a^2+b^2)
         *
         * If "a^2+b^2-d^2 > 0" then there are two solutions (above).
         * If "a^2+b^2-d^2 = 0" then there is one solution which is (-ad/(a^2+b^2), -bd/(a^2+b^2)).
         * If "a^2+b^2-d^2 < 0" then there is no solution.
         *
         * Below in the code we have "a = cosCoff", "b = sinCoff", and "d = constCoff". Also equivalent criterion
         * is used in the code. For example, "a^2+b^2-d^2 > 0" is equivalent of "1 - d^2/(a^2+b^2) > 0".
         */
        const a2b2 = cosCoff * cosCoff + sinCoff * sinCoff; // a^2+b^2
        const d2 = constCoff * constCoff; // d^2
        let result;
        if (a2b2 > 0.0) {
            const a2b2r = 1.0 / a2b2; // 1/(a^2+b^2)
            const d2a2b2 = d2 * a2b2r; // d^2/(a^2+b^2)
            const criterion = 1.0 - d2a2b2; // 1 - d^2/(a^2+b^2);
            if (criterion < -Geometry.smallMetricDistanceSquared) // nSolution = 0
                return result;
            const da2b2 = -constCoff * a2b2r; // -d/(a^2+b^2)
            // (c0,s0) is the closest approach of the line to the circle center (origin)
            const c0 = da2b2 * cosCoff; // -ad/(a^2+b^2)
            const s0 = da2b2 * sinCoff; // -bd/(a^2+b^2)
            if (criterion <= Geometry.smallMetricDistanceSquared) { // nSolution = 1
                // We observed criterion = -2.22e-16 in a rotated tangent system, and criterion = 4.44e-16 in a
                // transverse line-arc intersectXYZ near-tangency, therefore for criteria near zero (on either side),
                // return the (near) tangency; any larger criteria fall through to return both solutions.
                result = [Vector2d.create(c0, s0)];
            }
            else { // nSolution = 2
                const s = Math.sqrt(criterion * a2b2r); // sqrt(a^2+b^2-d^2)) / (a^2+b^2)
                result = [
                    Vector2d.create(c0 - s * sinCoff, s0 + s * cosCoff),
                    Vector2d.create(c0 + s * sinCoff, s0 - s * cosCoff),
                ];
            }
        }
        return result;
    }
    /**
     * For a line `f(x)` where `f(x0) = f0` and `f(x1) = f1`, return the `x` value at which `f(x) = fTarget`.
     * Return `defaultResult` if `(fTarget - f0) / (f1 - f0)` exceeds [[largeFractionResult]].
     */
    static inverseInterpolate(x0, f0, x1, f1, fTarget = 0, defaultResult) {
        /**
         * Line equation is "fTarget-f0 = (f1-f0)/(x1-x0) * (x-x0)" or "(fTarget-f0)/(f1-f0) = (x-x0)/(x1-x0)".
         * The left hand side is known so if we call it "fr" (short for "fraction") we get "fr = (x-x0)/(x1-x0)".
         * Therefore, "x = x0*(1-fr) + x1*fr". This is same as interpolation between "x0" and "x1" with fraction "fr".
         */
        const fr = Geometry.conditionalDivideFraction(fTarget - f0, f1 - f0); // (fTarget-f0)/(f1-f0)
        if (fr !== undefined)
            return Geometry.interpolate(x0, fr, x1); // x = x0*(1-fr) + x1*fr
        return defaultResult;
    }
    /**
     * For a line `f(x)` where `f(0) = f0` and `f(1) = f1`, return the `x` value at which `f(x) = fTarget`
     * Return `undefined` if `(fTarget - f0) / (f1 - f0)` exceeds [[largeFractionResult]].
     */
    static inverseInterpolate01(f0, f1, fTarget = 0) {
        // Line equation is fTarget-f0 = (f1-f0)*x so x = (fTarget-f0)/(f1-f0)
        return Geometry.conditionalDivideFraction(fTarget - f0, f1 - f0);
    }
    /**
     * Return `true` if `json` is an array with at least `minEntries` entries and all entries are numbers (including
     * those beyond minEntries).
     */
    static isNumberArray(json, minEntries = 0) {
        if (Array.isArray(json) && json.length >= minEntries) {
            let entry;
            for (entry of json) {
                if (!Number.isFinite(entry))
                    return false;
            }
            return true;
        }
        return false;
    }
    /**
     * Return `true` if `json` is an array of at least `minArrays` arrays with at least `minEntries` entries in
     * each array and all entries are numbers (including those beyond minEntries).
     */
    static isArrayOfNumberArray(json, minArrays, minEntries = 0) {
        if (Array.isArray(json) && json.length >= minArrays) {
            let entry;
            for (entry of json)
                if (!Geometry.isNumberArray(entry, minEntries))
                    return false;
            return true;
        }
        return false;
    }
    /**
     * Return the number of steps to take so that `numSteps * stepSize >= total`.
     * * `minCount` is returned in the following 3 cases:
     *   * (a) `stepSize <= 0`
     *   * (b) `stepSize >= total`
     *   * (b) `numSteps < minCount`
     * * `maxCount` is returned if `numSteps > maxCount`.
     */
    static stepCount(stepSize, total, minCount = 1, maxCount = 101) {
        if (stepSize <= 0)
            return minCount;
        total = Math.abs(total);
        if (stepSize >= total)
            return minCount;
        /**
         * 0.999999 is multiplied so we return the same "numSteps" if
         * stepSize*(numSteps-1) < total <= stepSize*numSteps.
         * For example, if "stepSize = 2" then we return "numSteps = 5" if 8 < total <= 10.
         */
        const numSteps = Math.floor((total + 0.999999 * stepSize) / stepSize);
        if (numSteps < minCount)
            return minCount;
        if (numSteps > maxCount)
            return maxCount;
        return numSteps;
    }
    /**
     * Test if `x` is in the interval [0,1] (but skip the test if `apply01 = false`).
     * * This odd behavior is very convenient for code that sometimes does not do the filtering.
     * @param x value to test.
     * @param apply01 if false, return `true` for all values of `x`. Default is true.
     */
    static isIn01(x, apply01 = true) {
        return apply01 ? x >= 0.0 && x <= 1.0 : true;
    }
    /**
     * Test if `x` is in the interval [0,1] for a given `tolerance`.
     * @param x value to test.
     * @param tolerance allowable distance outside the interval within which to classify `x` as inside.
     */
    static isIn01WithTolerance(x, tolerance) {
        tolerance = Math.abs(tolerance);
        return x + tolerance >= 0.0 && x - tolerance <= 1.0;
    }
    /**
     * Restrict x so it is in the interval `[a,b]` (allowing `a` and `b` to be in either order).
     * @param x value to restrict
     * @param a (usually the lower) interval limit
     * @param b (usually the upper) interval limit
     */
    static restrictToInterval(x, a, b) {
        if (a <= b) {
            if (x < a)
                return a;
            if (x > b)
                return b;
            return x;
        }
        // reversed interval
        if (x < b)
            return b;
        if (x > a)
            return a;
        return x;
    }
    /**
     * Case-insensitive string comparison.
     * * Return `true` if the `toUpperCase` values of `string1` and `string2` match.
     */
    static equalStringNoCase(string1, string2) {
        return string1.toUpperCase() === string2.toUpperCase();
    }
    /**
     * Test for exact match of two number arrays.
     * Returns `true` if both arrays have the same length and entries, or if both arrays are empty or `undefined`.
     */
    static exactEqualNumberArrays(a, b) {
        if (Array.isArray(a) && a.length === 0)
            a = undefined;
        if (Array.isArray(b) && b.length === 0)
            b = undefined;
        if (a === undefined && b === undefined)
            return true;
        if (Array.isArray(a) && Array.isArray(b)) {
            if (a.length !== b.length)
                return false;
            for (let i = 0; i < a.length; i++)
                if (a[i] !== b[i])
                    return false;
            return true;
        }
        return false;
    }
    /**
     * Test for match of two arrays of type `T`.
     * Returns `true` if both arrays have the same length and have the same entries (or both are empty arrays).
     */
    static almostEqualArrays(a, b, testFunction) {
        if (Array.isArray(a) && a.length === 0)
            a = undefined;
        if (Array.isArray(b) && b.length === 0)
            b = undefined;
        if (a === undefined && b === undefined)
            return true;
        if (Array.isArray(a) && Array.isArray(b)) {
            if (a.length !== b.length)
                return false;
            for (let i = 0; i < a.length; i++) {
                if (!testFunction(a[i], b[i]))
                    return false;
            }
            return true;
        }
        return false;
    }
    /**
     * Test for match of two arrays of type number or Float64Array.
     * Returns `true` if both arrays have the same length and have the same entries (or both are empty arrays).
     */
    static almostEqualNumberArrays(a, b, testFunction) {
        if (Array.isArray(a) && a.length === 0)
            a = undefined;
        if (Array.isArray(b) && b.length === 0)
            b = undefined;
        if (a === undefined && b === undefined)
            return true;
        if (Array.isArray(a) && Array.isArray(b)) {
            if (a.length !== b.length)
                return false;
            for (let i = 0; i < a.length; i++) {
                if (!testFunction(a[i], b[i]))
                    return false;
            }
            return true;
        }
        return false;
    }
    /**
     * Test for match of two values of type `T`.
     * @param a first value
     * @param b second value
     * @param resultIfBothUndefined returned value when both are `undefined`
     * @returns `true` if both values are defined and equal (with ===) and `false` if both values are defined
     * but not equal or if one is defined and the other undefined.
     */
    static areEqualAllowUndefined(a, b, resultIfBothUndefined = true) {
        if (a === undefined && b === undefined)
            return resultIfBothUndefined;
        if (a !== undefined && b !== undefined)
            return a === b;
        return false;
    }
    /**
     * Clone an array whose members have type `T`, which implements the clone method.
     * * If the clone method returns `undefined`, then `undefined` is forced into the cloned array.
     * @deprecated in 4.4.0 - will not be removed until after 2026-06-13. Use cloneArray.
     */
    // eslint-disable-next-line @typescript-eslint/no-deprecated
    static cloneMembers(array) {
        if (array === undefined)
            return undefined;
        const clonedArray = [];
        for (const element of array) {
            clonedArray.push(element.clone());
        }
        return clonedArray;
    }
    /**
     * Clone an array whose members have the cloneable type `T`.
     */
    static cloneArray(array) {
        if (array === undefined)
            return undefined;
        const clonedArray = [];
        for (const element of array) {
            clonedArray.push(element.clone());
        }
        return clonedArray;
    }
}
//# sourceMappingURL=Geometry.js.map