import { AllowedExponents, DivideUnits, DivisorUnit, ExponentiateUnit, MultiplicandUnit, MultiplyUnits, Unit, UnitWithSymbols } from "./unitTypeArithmetic";
export interface MeasureFormatter<N> {
    formatValue?: (value: N) => string;
    formatUnit?: (unit: UnitWithSymbols) => string;
}
/** The set of numeric operations required to fully represent a `GenericMeasure` for a given numeric type */
export interface NumericOperations<N> {
    /** Returns the multiplicative identity for numbers of type N */
    one(): N;
    /** Returns the negative of a number of type N */
    neg(value: N): N;
    /** Returns the sum of two numbers of type N */
    add(left: N, right: N): N;
    /** Returns the difference of two numbers of type N */
    sub(left: N, right: N): N;
    /** Returns the product of two numbers of type N */
    mult(left: N, right: N): N;
    /** Returns the quotient of two numbers of type N */
    div(left: N, right: N): N;
    /** Returns the base raised to the exponent for numbers of type N */
    pow(base: N, exponent: number): N;
    /** Compares two numbers returning a negative, zero, or positive value. */
    compare(left: N, right: N): number;
    /** Formats a number for display */
    format(value: N): string;
}
/** A numeric value with a corresponding unit of measurement. */
export interface GenericMeasure<N, U extends Unit> {
    /** The numeric value of this measure */
    readonly value: N;
    /** The unit of this measure */
    readonly unit: UnitWithSymbols<U>;
    /** The symbol of the unit this measure represents (e.g. 0.3048 m = 1 ft) */
    readonly symbol: string | undefined;
    /**
     * If this measure can be squared, squares it. If this measure is not squarable (due to exponent limitations), then
     * this function will have type `never`.
     * @returns this measure multiplied by itself
     */
    squared: "2" extends AllowedExponents<U> ? () => GenericMeasure<N, ExponentiateUnit<U, "2">> : never;
    /**
     * If this measure can be cubed, cubes it. If this measure cannot be cubed (due to exponent limitations), then
     * this function will have type `never`.
     * @returns this cube of this measure
     */
    cubed: "3" extends AllowedExponents<U> ? () => GenericMeasure<N, ExponentiateUnit<U, "3">> : never;
    /**
     * Raises this measure to a given power. If the result would give exponents outside of the allowable bounds, this
     * will return `never`.
     * @param exponent the exponent to raise this measure to
     * @returns this exponent to the given power
     */
    toThe<E extends AllowedExponents<U>>(exponent: E): GenericMeasure<N, ExponentiateUnit<U, E>>;
    /**
     * Adds this measure to another measure with the same unit.
     * @param other the value to add
     * @returns the sum
     */
    plus(other: GenericMeasure<N, U>): GenericMeasure<N, U>;
    /**
     * Subtracts another measure with the same unit from this measure.
     * @param other the value to subtract
     * @returns the difference
     */
    minus(other: GenericMeasure<N, U>): GenericMeasure<N, U>;
    /**
     * Negates the value of this measure.
     * @returns A measure whose value is the negative of this measure
     */
    negate(): GenericMeasure<N, U>;
    /**
     * Multiplies this measure by a dimensionless value.
     * @param value a scalar dimensionless value by which to scale this measure
     * @returns A measure scaled by the value
     */
    scale(value: N): GenericMeasure<N, U>;
    /**
     * Multiplies this measure with another measure.
     * @param other the value to multiply
     * @returns the product measure with a unit thats the product of the units
     */
    times<V extends MultiplicandUnit<U>>(other: GenericMeasure<N, V>): GenericMeasure<N, MultiplyUnits<U, V>>;
    /**
     * Divides this measure by another measure.
     * @param other the divisor
     * @returns the quotient measure with a unit thats the quotient of the units
     */
    over<V extends DivisorUnit<U>>(other: GenericMeasure<N, V>): GenericMeasure<N, DivideUnits<U, V>>;
    /**
     * Divides this measure by another measure.
     * @param other the divisor
     * @returns the quotient measure with a unit thats the quotient of the units
     */
    per<V extends DivisorUnit<U>>(other: GenericMeasure<N, V>): GenericMeasure<N, DivideUnits<U, V>>;
    /**
     * Divides this measure by another measure.
     * @param other the divisor
     * @returns the quotient measure with a unit thats the quotient of the units
     */
    div<V extends DivisorUnit<U>>(other: GenericMeasure<N, V>): GenericMeasure<N, DivideUnits<U, V>>;
    /**
     * Returns the reciprocal of this measure.
     * @returns the reciprocal of this measure with a recriprocal unit
     */
    inverse(): GenericMeasure<N, ExponentiateUnit<U, "-1">>;
    /**
     * Returns the reciprocal of this measure.
     * @returns the reciprocal of this measure with a recriprocal unit
     */
    reciprocal(): GenericMeasure<N, ExponentiateUnit<U, "-1">>;
    /**
     * Maps the value and possibly unit of this measure.
     * @param valueMap a mapping on the value of the measure
     * @param unitMap an optional mapping on the unit of the measure
     * @returns a new measure whose value and unit have been mapped
     */
    unsafeMap(fn: (value: N) => N): GenericMeasure<N, U>;
    unsafeMap<V extends Unit>(valueMap: (value: N) => N, unitMap: (unit: UnitWithSymbols<U>) => UnitWithSymbols<V>): GenericMeasure<N, V>;
    /**
     * Compares two measures to each other. Returns a negative value if this < other, a postive value if this > other
     * and 0 if the two are equal.
     * @param another measure with the same unit
     * @returns a value indicating how the value of this measure compares to the value of the other measure
     */
    compare(other: GenericMeasure<N, U>): number;
    /**
     * @param another measure with the same unit
     * @returns true if the value of this measure is less than the value of the other measure
     */
    lt(other: GenericMeasure<N, U>): boolean;
    /**
     * @param another measure with the same unit
     * @returns true if the value of this measure is less than or equal to the value of the other measure
     */
    lte(other: GenericMeasure<N, U>): boolean;
    /**
     * @param another measure with the same unit
     * @returns true if the value of this measure is equal to the value of the other measure
     */
    eq(other: GenericMeasure<N, U>): boolean;
    /**
     * @param another measure with the same unit
     * @returns true if the value of this measure is not equal to the value of the other measure
     */
    neq(other: GenericMeasure<N, U>): boolean;
    /**
     * @param another measure with the same unit
     * @returns true if the value of this measure is greater than or equal to the value of the other measure
     */
    gte(other: GenericMeasure<N, U>): boolean;
    /**
     * @param another measure with the same unit
     * @returns true if the value of this measure is greater than the value of the other measure
     */
    gt(other: GenericMeasure<N, U>): boolean;
    /**
     * Formats the value and the unit.
     * @returns a string representation of measure
     */
    toString(formatter?: MeasureFormatter<N>): string;
    /**
     * Formats this measure as a product of another unit. If the given unit has a symbol, this will format as a number
     * followed by that symbol. If not, this is equivalent to calling `toString()`.
     * @param a unit to be used to represent this measure
     * @returns a string representation of measure
     */
    in(unit: GenericMeasure<N, U>, formatter?: MeasureFormatter<N>): string;
    /**
     * Converts this measure to another unit, e.g. for passing to an external API that requires pure numbers of particular unit.
     * @param unit the unit to convert to
     * @returns the numeric value of this measure in the given unit
     */
    valueIn(unit: GenericMeasure<N, U>): N;
    /**
     * Adds a symbol to this measure.
     * @param symbol the symbol of the unit represented by this measure
     */
    withSymbol(symbol: string | undefined): GenericMeasure<N, U>;
    /** Shallow copies this measure instance. */
    clone(): GenericMeasure<N, U>;
}
/**
 * Translates a measure type from one numeric type to another while preserving the unit.
 * @example
 * const metersPerSecond = meters.per(seconds);
 * type Velocity<N> = LiftMeasure<typeof metersPerSecond, N>;
 */
export type LiftMeasure<M extends GenericMeasure<any, any>, N> = M extends GenericMeasure<any, infer U> ? GenericMeasure<N, U> : never;
