Skip to content

Commit

Permalink
More clean up (#112)
Browse files Browse the repository at this point in the history
* More clean up

* Add test coverage
  • Loading branch information
jscheiny authored Feb 6, 2019
1 parent f319d07 commit 217e46f
Show file tree
Hide file tree
Showing 9 changed files with 75 additions and 47 deletions.
4 changes: 4 additions & 0 deletions src/measure/__test__/numberMeasureTests.ts
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,10 @@ describe("Number measures", () => {
);
});

it("pow", () => {
expect(Measure.pow(Measure.of(3, meters), 4)).toEqual(Measure.of(81, meters.toThe(4)));
});

it("round", () => {
expect(Measure.round(Measure.of(7.8, mps))).toEqual(Measure.of(8, mps));
});
Expand Down
2 changes: 1 addition & 1 deletion src/measure/genericMeasureClass.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import {
} from "./unitTypeArithmetic";
import { divideUnits, exponentiateUnit, multiplyUnits } from "./unitValueArithmetic";

export type GenericMeasureConstructor<N> = new <U extends Unit>(
type GenericMeasureConstructor<N> = new <U extends Unit>(
value: N,
unit: UnitWithSymbols<U>,
symbol?: string,
Expand Down
33 changes: 24 additions & 9 deletions src/measure/genericMeasureStatic.ts
Original file line number Diff line number Diff line change
@@ -1,22 +1,30 @@
import { IGenericMeasure } from "./genericMeasure";
import { BinaryMeasureFunction, PrefixFunction, SpreadMeasureFunction } from "./genericMeasureUtils";
import { DivideUnits, DivisorUnit, MultiplicandUnit, MultiplyUnits, Unit } from "./unitTypeArithmetic";
import { BinaryFn, PrefixFn, SpreadFn } from "./genericMeasureUtils";
import {
AllowedExponents,
DivideUnits,
DivisorUnit,
ExponentiateUnit,
MultiplicandUnit,
MultiplyUnits,
Unit,
} from "./unitTypeArithmetic";

export interface IGenericMeasureStatic<N> {
/** Sums a list of one or more measures, all of the same unit. */
sum: SpreadMeasureFunction<N>;
sum: SpreadFn<N>;

/** Returns the smallest of a list of one or more measures. */
min: SpreadMeasureFunction<N>;
min: SpreadFn<N>;

/** Returns the largest of a list of one or more measures. */
max: SpreadMeasureFunction<N>;
max: SpreadFn<N>;

/** Static version of `left.plus(right)` */
add<U extends Unit>(left: IGenericMeasure<N, U>, right: IGenericMeasure<N, U>): IGenericMeasure<N, U>;
add: BinaryFn<N>;

/** Static version of `left.minus(right)` */
subtract<U extends Unit>(left: IGenericMeasure<N, U>, right: IGenericMeasure<N, U>): IGenericMeasure<N, U>;
subtract: BinaryFn<N>;

/** Static version of `left.times(right)` */
multiply<L extends Unit, R extends MultiplicandUnit<L>>(
Expand All @@ -30,13 +38,19 @@ export interface IGenericMeasureStatic<N> {
right: IGenericMeasure<N, R>,
): IGenericMeasure<N, DivideUnits<L, R>>;

/** Static version of `value.toThe(exp)` */
pow<U extends Unit, E extends AllowedExponents<U>>(
value: IGenericMeasure<N, U>,
exp: E,
): IGenericMeasure<N, ExponentiateUnit<U, E>>;

/**
* Creates a function that takes a measure and applies a symbol to its prefix and scales it by a given multiplier.
* @param prefix the prefix to add to symbols of measures passed into the resulting function
* @param multiplier the scalar by which to multiply measures passed into the resulting function
* @returns a function that takes measures and adds a prefix to their symbols and multiplies them by a given value
*/
prefix(prefix: string, multiplier: N): PrefixFunction<N>;
prefix(prefix: string, multiplier: N): PrefixFn<N>;
}

export const getGenericMeasureStaticMethods = <N>(): IGenericMeasureStatic<N> => ({
Expand All @@ -47,6 +61,7 @@ export const getGenericMeasureStaticMethods = <N>(): IGenericMeasureStatic<N> =>
subtract: (left, right) => left.minus(right),
multiply: (left, right) => left.times(right),
divide: (left, right) => left.over(right),
pow: (value, exp) => value.toThe(exp),
prefix: (prefix, multiplier) => {
return measure => {
const { symbol } = measure;
Expand All @@ -55,6 +70,6 @@ export const getGenericMeasureStaticMethods = <N>(): IGenericMeasureStatic<N> =>
},
});

function reduce<N>(fn: BinaryMeasureFunction<N>): SpreadMeasureFunction<N> {
function reduce<N>(fn: BinaryFn<N>): SpreadFn<N> {
return (first, ...rest) => rest.reduce(fn, first);
}
37 changes: 28 additions & 9 deletions src/measure/genericMeasureUtils.ts
Original file line number Diff line number Diff line change
@@ -1,23 +1,30 @@
import { NonZeroExponent } from "../exponent/exponentArithmetic";
import { IGenericMeasure } from "./genericMeasure";
import { Unit } from "./unitTypeArithmetic";
import { NthRootUnit, RadicandUnit, Unit } from "./unitTypeArithmetic";
import { nthRootUnit } from "./unitValueArithmetic";

/** A function which applies a symbol prefix and multiplier to a given measure. */
export type PrefixFunction<N> = {
export type PrefixFn<N> = {
<U extends Unit>(measure: IGenericMeasure<N, U>): IGenericMeasure<N, U>;
};

/** A function which transforms a single measure into another measure with the same unit. */
export type UnaryMeasureFunction<N> = {
export type UnaryFn<N> = {
<U extends Unit>(x: IGenericMeasure<N, U>): IGenericMeasure<N, U>;
};

/** A function which takes the Rth root of a measure's value and unit. */
export type NthRootFn<N, R extends NonZeroExponent> = {
<U extends RadicandUnit<R>>(x: IGenericMeasure<N, U>): IGenericMeasure<N, NthRootUnit<U, R>>;
};

/** A function which transforms two measures with same unit into a single measure with the same unit. */
export type BinaryMeasureFunction<N> = {
export type BinaryFn<N> = {
<U extends Unit>(left: IGenericMeasure<N, U>, right: IGenericMeasure<N, U>): IGenericMeasure<N, U>;
};

/** A function which transforms one or more measure with the same unit into a single measure with the same unit. */
export type SpreadMeasureFunction<N> = {
export type SpreadFn<N> = {
<U extends Unit>(first: IGenericMeasure<N, U>, ...rest: Array<IGenericMeasure<N, U>>): IGenericMeasure<N, U>;
};

Expand All @@ -28,18 +35,30 @@ export type SpreadMeasureFunction<N> = {
* @param fn a unary function of numeric types
* @returns a unary function of measures
*/
export function wrapUnaryFn<N>(fn: (x: N) => N): UnaryMeasureFunction<N> {
export function wrapUnaryFn<N>(fn: (x: N) => N): UnaryFn<N> {
return x => x.unsafeMap(fn);
}

/**
* Converts a function that takes the nth root of a number type (for a specific n) into a function of measures. The `n`
* parameter must be a constant which matches the root that the function takes (e.g. 2 for square root, 3 for cube
* root).
* @param nthRoot a function that takes a specific root of a numeric type
* @param n a compile time constant specifying which nth root the first parameter performs
* @returns a function of measures which takes the nth root of the value and the unit.
*/
export function wrapRootFn<N, R extends NonZeroExponent>(nthRoot: (x: N) => N, n: R): NthRootFn<N, R> {
return x => x.unsafeMap(nthRoot, unit => nthRootUnit(unit, n));
}

/**
* Converts a binary function of unitless numbers into a function of measures. This assumes that the underlying
* operation makes no change to the unit of the measure. For example, this would be an incorrect usage:
* `mult = wrapBinaryFn((left, right) => left * right)` since multiplying two measures would result in a different unit.
* @param fn a binary function of numeric types
* @returns a binary function of measures
*/
export function wrapBinaryFn<N>(fn: (left: N, right: N) => N): BinaryMeasureFunction<N> {
export function wrapBinaryFn<N>(fn: (left: N, right: N) => N): BinaryFn<N> {
return (left, right) => left.unsafeMap(lValue => fn(lValue, right.value));
}

Expand All @@ -51,7 +70,7 @@ export function wrapBinaryFn<N>(fn: (left: N, right: N) => N): BinaryMeasureFunc
* @param fn a spread function of numeric types
* @returns a spread function of measures
*/
export function wrapSpreadFn<N>(fn: (...x: N[]) => N): SpreadMeasureFunction<N> {
export function wrapSpreadFn<N>(fn: (...x: N[]) => N): SpreadFn<N> {
return (first, ...rest) => {
const measureValues = [first, ...rest].map(m => m.value);
const newValue = fn(...measureValues);
Expand All @@ -68,7 +87,7 @@ export function wrapSpreadFn<N>(fn: (...x: N[]) => N): SpreadMeasureFunction<N>
* @param fn a binary function of numeric types
* @returns a spread function of measures that reduces its arguments using the binary function passed
*/
export function wrapReducerFn<N>(fn: (curr: N, prev: N, index: number) => N): SpreadMeasureFunction<N> {
export function wrapReducerFn<N>(fn: (curr: N, prev: N, index: number) => N): SpreadFn<N> {
return (first, ...rest) => {
const values = rest.map(m => m.value);
return first.unsafeMap(() => values.reduce(fn, first.value));
Expand Down
27 changes: 13 additions & 14 deletions src/measure/numberMeasure.ts
Original file line number Diff line number Diff line change
@@ -1,19 +1,18 @@
import { IGenericMeasure, INumericOperations } from "./genericMeasure";
import { createMeasureType, GenericMeasureType } from "./genericMeasureFactory";
import { SpreadMeasureFunction, UnaryMeasureFunction, wrapSpreadFn, wrapUnaryFn } from "./genericMeasureUtils";
import { NthRootUnit, RadicandUnit, Unit } from "./unitTypeArithmetic";
import { cbrtUnit, sqrtUnit } from "./unitValueArithmetic";
import { NthRootFn, SpreadFn, UnaryFn, wrapRootFn, wrapSpreadFn, wrapUnaryFn } from "./genericMeasureUtils";
import { Unit } from "./unitTypeArithmetic";

interface IMeasureStaticMethods {
abs: UnaryMeasureFunction<number>;
ceil: UnaryMeasureFunction<number>;
floor: UnaryMeasureFunction<number>;
fround: UnaryMeasureFunction<number>;
round: UnaryMeasureFunction<number>;
trunc: UnaryMeasureFunction<number>;
hypot: SpreadMeasureFunction<number>;
sqrt<U extends RadicandUnit<2>>(x: Measure<U>): Measure<NthRootUnit<U, 2>>;
cbrt<U extends RadicandUnit<3>>(x: Measure<U>): Measure<NthRootUnit<U, 3>>;
abs: UnaryFn<number>;
ceil: UnaryFn<number>;
floor: UnaryFn<number>;
fround: UnaryFn<number>;
round: UnaryFn<number>;
trunc: UnaryFn<number>;
hypot: SpreadFn<number>;
sqrt: NthRootFn<number, 2>;
cbrt: NthRootFn<number, 3>;
}

const staticMethods: IMeasureStaticMethods = {
Expand All @@ -24,8 +23,8 @@ const staticMethods: IMeasureStaticMethods = {
round: wrapUnaryFn(Math.round),
trunc: wrapUnaryFn(Math.trunc),
hypot: wrapSpreadFn(Math.hypot),
sqrt: x => x.unsafeMap(Math.sqrt, sqrtUnit),
cbrt: x => x.unsafeMap(Math.cbrt, cbrtUnit),
sqrt: wrapRootFn(Math.sqrt, 2),
cbrt: wrapRootFn(Math.cbrt, 3),
};

const numericOps: INumericOperations<number> = {
Expand Down
8 changes: 0 additions & 8 deletions src/measure/unitValueArithmetic.ts
Original file line number Diff line number Diff line change
Expand Up @@ -79,14 +79,6 @@ export function nthRootUnit<U extends RadicandUnit<N>, N extends NonZeroExponent
return expAndRootImpl(unit, exponent => exponent / root);
}

export function sqrtUnit<U extends RadicandUnit<2>>(unit: UnitWithSymbols<U>): UnitWithSymbols<NthRootUnit<U, 2>> {
return nthRootUnit(unit, 2);
}

export function cbrtUnit<U extends RadicandUnit<3>>(unit: UnitWithSymbols<U>): UnitWithSymbols<NthRootUnit<U, 3>> {
return nthRootUnit(unit, 3);
}

function expAndRootImpl(unit: UnitWithSymbols, updateExponent: (exp: Exponent) => number): any {
const result: UnitWithSymbols = {};
for (const dimension in unit) {
Expand Down
4 changes: 2 additions & 2 deletions src/unit/memory.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import { PrefixFunction } from "../measure/genericMeasureUtils";
import { PrefixFn } from "../measure/genericMeasureUtils";
import { Measure } from "../measure/numberMeasure";
import { bits } from "./base";
import { Memory } from "./quantities";

export const bytes: Memory = Measure.of(8, bits, "B");

// HACKHACK: Explicitly type this so we can import PrefixFunction and avoid absolute paths in the generated typings.
export const kibi: PrefixFunction<number> = Measure.prefix("Ki", 1 << 10);
export const kibi: PrefixFn<number> = Measure.prefix("Ki", 1 << 10);
export const mebi = Measure.prefix("Mi", 1 << 20);
export const gibi = Measure.prefix("Gi", 1 << 30);
export const tebi = Measure.prefix("Ti", 1 << 40);
Expand Down
4 changes: 2 additions & 2 deletions src/unit/metric.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { PrefixFunction } from "../measure/genericMeasureUtils";
import { PrefixFn } from "../measure/genericMeasureUtils";
import { Measure } from "../measure/numberMeasure";
import { amperes, candelas, kilograms, meters, moles, seconds, steradians } from "./base";
import * as Quantity from "./quantities";
Expand All @@ -22,7 +22,7 @@ export const lumens: Quantity.LuminousFlux = candelas.times(steradians).withSymb
export const luxes: Quantity.Illuminance = lumens.per(meters.squared()).withSymbol("lx");

// HACKHACK: Explicitly type this so we can import PrefixFunction and avoid absolute paths in the generated typings.
export const yotta: PrefixFunction<number> = Measure.prefix("Y", 1e24);
export const yotta: PrefixFn<number> = Measure.prefix("Y", 1e24);
export const zetta = Measure.prefix("Z", 1e21);
export const exa = Measure.prefix("E", 1e18);
export const peta = Measure.prefix("P", 1e15);
Expand Down
3 changes: 1 addition & 2 deletions test/types/exponents.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import { AddendOf, MultiplicandOf, Exponent, ProductOf } from "../../src/exponent";
import { AddendOf, MultiplicandOf, Exponent, ProductOf, SubtrahendOf } from "../../src/exponent";
import { IsSame } from "./utils";
import { SubtrahendOf } from "../../src/exponent/exponentArithmetic";

type AddendOf3 = IsSame<-5 | -4 | -3 | -2 | -1 | 0 | 1 | 2, AddendOf<3>>; // $ExpectType true
type AddendOf0 = AddendOf<0>; // $ExpectType Exponent
Expand Down

0 comments on commit 217e46f

Please sign in to comment.