Skip to content

Commit

Permalink
Jsdocs + some cleanup (#70)
Browse files Browse the repository at this point in the history
  • Loading branch information
jscheiny authored Oct 4, 2018
1 parent 975a3f4 commit 250d62e
Show file tree
Hide file tree
Showing 10 changed files with 368 additions and 47 deletions.
9 changes: 4 additions & 5 deletions src/measure/__test__/measureTests.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { cubic, Measure, pow, square } from "../measure";
import { Measure } from "../measure";
import { addSymbols } from "./testUtils";

describe("Measures", () => {
Expand Down Expand Up @@ -77,11 +77,10 @@ describe("Measures", () => {

expect(value.inverse()).toEqual(Measure.of(0.1, meter.inverse()));
expect(value.reciprocal()).toEqual(Measure.of(0.1, meter.inverse()));
expect(pow(value, 0)).toEqual(Measure.dimensionless(1));
expect(pow(value, 1)).toEqual(Measure.of(10, meter));
expect(value.pow(0)).toEqual(Measure.dimensionless(1));
expect(value.pow(1)).toEqual(Measure.of(10, meter));
expect(value.squared()).toEqual(Measure.of(100, meter.squared()));
expect(square(value)).toEqual(Measure.of(100, meter.squared()));
expect(cubic(value)).toEqual(Measure.of(1000, meter.cubed()));
expect(value.cubed()).toEqual(Measure.of(1000, meter.cubed()));
});
});

Expand Down
169 changes: 168 additions & 1 deletion src/measure/genericMeasure.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import {
} from "./unitTypeArithmetic";
import { dimension, divideUnits, exponentiateUnit, multiplyUnits } from "./unitValueArithmetic";

/** The set of numeric operations required to fully represent a `GenericMeasure` for a given numeric type */
export interface Numeric<N> {
guard(value: any): value is N;
one(): N;
Expand All @@ -30,43 +31,209 @@ export interface Numeric<N> {
format(value: N): string;
}

/** The functions needed to construct a measure of a given numeric type */
export interface GenericMeasureFactory<N> {
/** The constructor for this generic measure type, useful for doing `instanceof` checks. */
class: new (...args: any[]) => GenericMeasure<any, N>;

/**
* Creates a new dimension base unit.
* @param dim a unique string literal which names this dimension (e.g. "length")
* @param symbol the symbol of the base unit of the dimension (e.g. "m")
* @returns A measure representing 1 base unit of the dimension (1 m)
*/
dimension<Dim extends string>(dim: Dim, symbol?: string): GenericMeasure<{ [D in Dim]: 1 }, N>;

/**
* Creates a dimensionless measure.
* @param value the value of the measure
* @returns a measure with no dimensions
*/
dimensionless(value: N): GenericMeasure<{}, N>;

/**
* Creates a measure as a multiple of another measure.
* @param value the number of measures
* @param quantity the measure to be multiplied
* @param symbol an optional unit symbol for this measure
* @returns a measure of value number of quantities.
*/
of<U extends Unit>(value: N, quantity: GenericMeasure<U, N>, symbol?: string): GenericMeasure<U, N>;

/**
* Creates a measure from a raw unit, should be avoided unless you know what you're doing.
* @param value the value of the measure
* @param unit the raw unit of the measure
* @param symbol an optional unit symbo for this measure
*/
unsafeConstruct<U extends Unit>(value: N, unit: UnitWithSymbols<U>, symbol?: string): GenericMeasure<U, N>;
}

/** A numeric value with a corresponding unit of measurement. */
export interface GenericMeasure<U extends Unit, N> {
/** 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;
// Methods

/**
* 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: U extends BaseUnit<2> ? () => GenericMeasure<ExponentiateUnit<U, 2>, N> : 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: U extends BaseUnit<3> ? () => GenericMeasure<ExponentiateUnit<U, 3>, N> : 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
*/
pow<E extends Exponent>(exponent: E): U extends BaseUnit<E> ? GenericMeasure<ExponentiateUnit<U, E>, N> : never;

/**
* Adds a symbol to this measure.
* @param symbol the symbol of the unit represented by this measure
*/
withSymbol(symbol: String): GenericMeasure<U, N>;

/**
* Adds this measure to another measure with the same unit.
* @param other the value to add
* @returns the sum
*/
plus(other: GenericMeasure<U, N>): GenericMeasure<U, N>;

/**
* Subtracts another measure with the same unit from this measure.
* @param other the value to subtract
* @returns the difference
*/
minus(other: GenericMeasure<U, N>): GenericMeasure<U, N>;

/**
* Negates the value of this measure.
* @returns A measure whose value is the negative of this measure
*/
negate(): GenericMeasure<U, N>;

/**
* 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>): GenericMeasure<U, N>;

/**
* 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<V, N>): GenericMeasure<MultiplyUnits<U, V>, N>;

/**
* 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<V, N>): GenericMeasure<DivideUnits<U, V>, N>;

/**
* 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<V, N>): GenericMeasure<DivideUnits<U, V>, N>;

/**
* Returns the reciprocal of this measure.
* @returns the reciprocal of this measure with a recriprocal unit
*/
inverse(): GenericMeasure<ExponentiateUnit<U, -1>, N>;

/**
* Returns the reciprocal of this measure.
* @returns the reciprocal of this measure with a recriprocal unit
*/
reciprocal(): GenericMeasure<ExponentiateUnit<U, -1>, N>;

/**
* Maps the value of this measure without affecting the unit. This should be used for writing unit safe functions
* that only alter the value of a measure (e.g. abs).
* @param fn a mapping on the value of the measure
* @returns a new measure with the same unit whose value has been mapped
*/
unsafeMap(fn: (value: N) => N): GenericMeasure<U, N>;

/**
* @param another measure with the same unit
* @returns true if the value of this measure is less than the value of the other measure
*/
isLessThan(other: GenericMeasure<U, N>): 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
*/
isLessThanOrEqualTo(other: GenericMeasure<U, N>): 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
*/
isEqualTo(other: GenericMeasure<U, N>): 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
*/
isNotEqualTo(other: GenericMeasure<U, N>): 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
*/
isGreaterThanOrEqualTo(other: GenericMeasure<U, N>): 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
*/
isGreaterThan(other: GenericMeasure<U, N>): boolean;

/**
* Formats the value and the unit.
* @returns a string representation of measure
*/
toString(): 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<U, N>): string;
}

/**
* Creates a new measure factory for a given numeric type. The numeric type of the measure is inferred from the
* parameter.
* @param num the set of numeric operations needed to implement a measure for an arbitrary numeric type
* @returns a factory for constructing measures of the given numeric type
* @example
* type MyMeasure<U extends Unit> = GenericMeasure<U, MyNumberType>;
* const MyMeasure = createMeasureType({ ... });
*/
export function createMeasureType<N>(num: Numeric<N>): GenericMeasureFactory<N> {
class InternalMeasure<U extends Unit> implements GenericMeasure<U, N> {
public readonly symbol: string | undefined;
Expand Down
94 changes: 85 additions & 9 deletions src/measure/math.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
import { Exponent } from "../exponent";
import { Dimensionless, Length, PlaneAngle } from "../quantity/quantities";
import { radians } from "../unit/base";
import { Measure, pow } from "./measure";
import { GenericMeasure } from "./genericMeasure";
import { Measure } from "./measure";
import {
BaseUnit,
DivideUnits,
DivisorUnit,
ExponentiateUnit,
MultiplicandUnit,
MultiplyUnits,
NthRootUnit,
Expand All @@ -12,58 +16,130 @@ import {
} from "./unitTypeArithmetic";
import { nthRootUnit } from "./unitValueArithmetic";

export { pow };

/** `Math.abs` for `number` measures. The measure's unit is preserved under this operation. */
export const abs = wrapUnaryFn(Math.abs);
export const acos = wrapInverseTrigFn(Math.acos);
export const asin = wrapInverseTrigFn(Math.asin);
export const atan = wrapInverseTrigFn(Math.atan);

/** `Math.ceil` for `number` measures. The measure's unit is preserved under this operation. */
export const ceil = wrapUnaryFn(Math.ceil);
export const cos = wrapTrigFn(Math.cos);

/** `Math.floor` for `number` measures. The measure's unit is preserved under this operation. */
export const floor = wrapUnaryFn(Math.floor);

/** `Math.fround` for `number` measures. The measure's unit is preserved under this operation. */
export const fround = wrapUnaryFn(Math.fround);

/** `Math.round` for `number` measures. The measure's unit is preserved under this operation. */
export const round = wrapUnaryFn(Math.round);

/** `Math.trunc` for `number` measures. The measure's unit is preserved under this operation. */
export const trunc = wrapUnaryFn(Math.trunc);

/** `Math.hypot` for `number` measures. At least 1 parameter must be provided and all must have the same unit. */
export const hypot = warpNary(Math.hypot);

/** `Math.max` for `number` measures. At least 1 parameter must be provided and all must have the same unit. */
export const max = warpNary(Math.max);

/** `Math.min` for `number` measures. At least 1 parameter must be provided and all must have the same unit. */
export const min = warpNary(Math.min);
export const round = wrapUnaryFn(Math.round);

/**
* `Math.acos` for `number` measures.
* @param x a dimensionless value
* @returns an angle
*/
export const acos = wrapInverseTrigFn(Math.acos);

/**
* `Math.asin` for `number` measures.
* @param x a dimensionless value
* @returns an angle
*/
export const asin = wrapInverseTrigFn(Math.asin);

/**
* `Math.atan` for `number` measures.
* @param x a dimensionless value
* @returns an angle
*/
export const atan = wrapInverseTrigFn(Math.atan);

/**
* `Math.cos` for `number` measures.
* @param x an angle
* @returns a dimensionless value
*/
export const cos = wrapTrigFn(Math.cos);

/**
* `Math.sin` for `number` measures.
* @param x an angle
* @returns a dimensionless value
*/
export const sin = wrapTrigFn(Math.sin);

/**
* `Math.tan` for `number` measures.
* @param x an angle
* @returns a dimensionless value
*/
export const tan = wrapTrigFn(Math.tan);
export const trunc = wrapUnaryFn(Math.trunc);

/**
* `Math.atan2` for `number` measures.
* @param x a length
* @param y a length
* @returns an angle
*/
export function atan2(y: Length, x: Length): PlaneAngle {
return Measure.of(Math.atan2(y.value, x.value), radians);
}

/** `Math.sqrt` for `number` measures. Can only be applied to units with even exponents. */
export function sqrt<U extends RadicandUnit<2>>(x: Measure<U>): Measure<NthRootUnit<U, 2>> {
return Measure.unsafeConstruct(Math.sqrt(x.value), nthRootUnit(x.unit, 2));
}

/** `Math.cbrt` for `number` measures. Can only be applied to units with exponents that are multiples of 3. */
export function cbrt<U extends RadicandUnit<3>>(x: Measure<U>): Measure<NthRootUnit<U, 3>> {
return Measure.unsafeConstruct(Math.cbrt(x.value), nthRootUnit(x.unit, 3));
}

/** `Math.pow` for measures of any kind. The power is restricted to integers between -5 and 5 */
export function pow<U extends BaseUnit<E>, E extends Exponent, N>(
measure: GenericMeasure<U, N>,
power: E,
): GenericMeasure<ExponentiateUnit<U, E>, N> {
return measure.pow(power);
}

/** Equivalent to `left.plus(right)` */
export function add<U extends Unit>(left: Measure<U>, right: Measure<U>): Measure<U> {
return left.plus(right);
}

/** Equivalent to `left.minus(right)` */
export function subtract<U extends Unit>(left: Measure<U>, right: Measure<U>): Measure<U> {
return left.minus(right);
}

/** Equivalent to `left.times(right)` */
export function multiply<L extends Unit, R extends MultiplicandUnit<L>>(
left: Measure<L>,
right: Measure<R>,
): Measure<MultiplyUnits<L, R>> {
return left.times(right);
}

/** Equivalent to `left.over(right)` */
export function divide<L extends Unit, R extends DivisorUnit<L>>(
left: Measure<L>,
right: Measure<R>,
): Measure<DivideUnits<L, R>> {
return left.over(right);
}

/** Sums a list of 1 or more measures, all of the same unit. */
export function sum<U extends Unit>(first: Measure<U>, ...rest: Array<Measure<U>>): Measure<U> {
let result = first;
for (const measure of rest) {
Expand Down
Loading

0 comments on commit 250d62e

Please sign in to comment.