Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

optional log base for ticks functions #233

Closed
wants to merge 10 commits into from
4 changes: 2 additions & 2 deletions src/nice.js
@@ -1,9 +1,9 @@
import {tickIncrement} from "./ticks.js";

export default function nice(start, stop, count) {
export default function nice(start, stop, count, log) {
let prestep;
while (true) {
const step = tickIncrement(start, stop, count);
const step = tickIncrement(start, stop, count, log);
if (step === prestep || step === 0 || !isFinite(step)) {
return [start, stop];
} else if (step > 0) {
Expand Down
38 changes: 24 additions & 14 deletions src/ticks.js
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Other implicit dependencies on base 10 (e.g., 2 and 5 are factors of 10, and 10 appears elsewhere in code)

Could you point me to the other parts of the code that would need to be addressed?

Expand Up @@ -2,35 +2,42 @@ const e10 = Math.sqrt(50),
e5 = Math.sqrt(10),
e2 = Math.sqrt(2);

function tickSpec(start, stop, count) {
const logParams = {
binary: [Math.log2, 2],
natural: [(n) => Math.log1p(n - 1), Math.E],
common: [Math.log10, 10],
Comment on lines +6 to +8
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In practice will probably only be used for base = 2 and base = e

Limits the log types to just common, natural and binary.

}
Comment on lines +5 to +9
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tiny loss of precision and performance using Math.log(base) etc. instead of Math.LN10

Does this approach work for you to use the respective precise Math methods for each allowed log type?


function tickSpec(start, stop, count, log = 'common') {
const [logFn, base] = logParams[log] || logParams.common;
const step = (stop - start) / Math.max(0, count),
power = Math.floor(Math.log10(step)),
error = step / Math.pow(10, power),
factor = error >= e10 ? 10 : error >= e5 ? 5 : error >= e2 ? 2 : 1;
power = Math.floor(logFn(step)),
error = step / Math.pow(base, power),
factor = error >= e10 ? 10 : error >= e5 ? 5 : error >= e2 ? 2 : 1;
let i1, i2, inc;
if (power < 0) {
inc = Math.pow(10, -power) / factor;
inc = Math.pow(base, -power) / factor;
i1 = Math.round(start * inc);
i2 = Math.round(stop * inc);
if (i1 / inc < start) ++i1;
if (i2 / inc > stop) --i2;
inc = -inc;
} else {
inc = Math.pow(10, power) * factor;
inc = Math.pow(base, power) * factor;
i1 = Math.round(start / inc);
i2 = Math.round(stop / inc);
if (i1 * inc < start) ++i1;
if (i2 * inc > stop) --i2;
}
if (i2 < i1 && 0.5 <= count && count < 2) return tickSpec(start, stop, count * 2);
if (i2 < i1 && 0.5 <= count && count < 2) return tickSpec(start, stop, count * 2, log);
return [i1, i2, inc];
}

export default function ticks(start, stop, count) {
export default function ticks(start, stop, count, log) {
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Too many positional arguments (start, stop, count, base). Overall, I think I would prefer to keep the existing methods as-is and have base-specific variants of d3.ticks and d3.tickIncrement for base = 2 (binary) and base = e (“natural”).

I'm happy to separate these added methods into separate methods. But I'd like to avoid duplication of code. Would you be ok with having the log option on a base function then exporting a wrapped function for each log type with just start, stop and count args?

So exporting ticks, ticksNatural and ticksBinary, along with their respective tickIncrement functions.

stop = +stop, start = +start, count = +count;
if (!(count > 0)) return [];
if (start === stop) return [start];
const reverse = stop < start, [i1, i2, inc] = reverse ? tickSpec(stop, start, count) : tickSpec(start, stop, count);
const reverse = stop < start, [i1, i2, inc] = reverse ? tickSpec(stop, start, count, log) : tickSpec(start, stop, count, log);
if (!(i2 >= i1)) return [];
const n = i2 - i1 + 1, ticks = new Array(n);
if (reverse) {
Expand All @@ -43,13 +50,16 @@ export default function ticks(start, stop, count) {
return ticks;
}

export function tickIncrement(start, stop, count) {
stop = +stop, start = +start, count = +count;
return tickSpec(start, stop, count)[2];
export function tickIncrement(start, stop, count, log) {
(stop = +stop), (start = +start), (count = +count);
return tickSpec(start, stop, count, log)[2];
}

export function tickStep(start, stop, count) {
export function tickStep(start, stop, count, log) {
stop = +stop, start = +start, count = +count;
const reverse = stop < start, inc = reverse ? tickIncrement(stop, start, count) : tickIncrement(start, stop, count);
const reverse = stop < start,
inc = reverse
? tickIncrement(stop, start, count, log)
: tickIncrement(start, stop, count, log);
return (reverse ? -1 : 1) * (inc < 0 ? 1 / -inc : inc);
}
21 changes: 21 additions & 0 deletions test/nice-test.js
Expand Up @@ -44,3 +44,24 @@ it("nice(start, stop, count) returns the expected values", () => {
assert.deepStrictEqual(nice(132, 876, 5), [0, 1000]);
assert.deepStrictEqual(nice(132, 876, 1), [0, 1000]);
});

it("nice(start, stop, count, base) returns the expected values for binary log", () => {
assert.deepStrictEqual(nice(0.126, 0.51, 32, 'binary'), [0.125, 0.515625]);
assert.deepStrictEqual(nice(0.126, 0.48, 10, 'binary'), [0.125, 0.5]);
assert.deepStrictEqual(nice(0.126, 0.48, 6, 'binary'), [0.125, 0.5]);
assert.deepStrictEqual(nice(0.126, 0.48, 5, 'binary'), [0.125, 0.5]);
assert.deepStrictEqual(nice(0.126, 0.48, 1, 'binary'), [0, 0.5]);
assert.deepStrictEqual(nice(129, 876, 1000, 'binary'), [129, 876]);
assert.deepStrictEqual(nice(129, 876, 100, 'binary'), [128, 880]);
assert.deepStrictEqual(nice(129, 876, 30, 'binary'), [128, 896]);
assert.deepStrictEqual(nice(129, 876, 10, 'binary'), [128, 896]);
assert.deepStrictEqual(nice(129, 876, 6, 'binary'), [128, 896]);
assert.deepStrictEqual(nice(129, 876, 5, 'binary'), [128, 896]);
assert.deepStrictEqual(nice(129, 876, 1, 'binary'), [0, 1024]);
});

it("nice(start, stop, count, base) returns the expected values for natural log", () => {
assert.deepStrictEqual(nice(Math.E + 0.1, Math.E * 3 + 0.1, 2, 'natural'), [0, Math.E * 4]);
assert.deepStrictEqual(nice(-Math.E - 0.1, Math.E + 0.1, 2, 'natural'), [-Math.E * 2, Math.E * 2]);
assert.deepStrictEqual(nice(-Math.E * 10 - 0.5, Math.E * 20 - 0.5, 30, 'natural'), [-Math.E * 11, Math.E * 20]);
});
95 changes: 95 additions & 0 deletions test/tickIncrement-test.js
Expand Up @@ -60,3 +60,98 @@ it("tickIncrement(start, stop, count) returns approximately count + 1 tickIncrem
assert.strictEqual(tickIncrement(-10, 10, 2), 10);
assert.strictEqual(tickIncrement(-10, 10, 1), 20);
});


describe("tickIncrement(start, stop, count, base) returns approximately count + 1 tickIncrement when start < stop", () => {
it("common", () => {
assert.strictEqual(tickIncrement( 0, 1, 10), -10);
assert.strictEqual(tickIncrement( 0, 1, 9), -10);
assert.strictEqual(tickIncrement( 0, 1, 8), -10);
assert.strictEqual(tickIncrement( 0, 1, 7), -5);
assert.strictEqual(tickIncrement( 0, 1, 6), -5);
assert.strictEqual(tickIncrement( 0, 1, 5), -5);
assert.strictEqual(tickIncrement( 0, 1, 4), -5);
assert.strictEqual(tickIncrement( 0, 1, 3), -2);
assert.strictEqual(tickIncrement( 0, 1, 2), -2);
assert.strictEqual(tickIncrement( 0, 1, 1), 1);
assert.strictEqual(tickIncrement( 0, 10, 10), 1);
assert.strictEqual(tickIncrement( 0, 10, 9), 1);
assert.strictEqual(tickIncrement( 0, 10, 8), 1);
assert.strictEqual(tickIncrement( 0, 10, 7), 2);
assert.strictEqual(tickIncrement( 0, 10, 6), 2);
assert.strictEqual(tickIncrement( 0, 10, 5), 2);
assert.strictEqual(tickIncrement( 0, 10, 4), 2);
assert.strictEqual(tickIncrement( 0, 10, 3), 5);
assert.strictEqual(tickIncrement( 0, 10, 2), 5);
assert.strictEqual(tickIncrement( 0, 10, 1), 10);
assert.strictEqual(tickIncrement(-10, 10, 10), 2);
assert.strictEqual(tickIncrement(-10, 10, 9), 2);
assert.strictEqual(tickIncrement(-10, 10, 8), 2);
assert.strictEqual(tickIncrement(-10, 10, 7), 2);
assert.strictEqual(tickIncrement(-10, 10, 6), 5);
assert.strictEqual(tickIncrement(-10, 10, 5), 5);
assert.strictEqual(tickIncrement(-10, 10, 4), 5);
assert.strictEqual(tickIncrement(-10, 10, 3), 5);
assert.strictEqual(tickIncrement(-10, 10, 2), 10);
assert.strictEqual(tickIncrement(-10, 10, 1), 20);
});

it("binary", () => {
assert.strictEqual(tickIncrement(0, 1, 10, "binary"), -8);
assert.strictEqual(tickIncrement(0, 1, 9, "binary"), -8);
assert.strictEqual(tickIncrement(0, 1, 8, "binary"), -8);
assert.strictEqual(tickIncrement(0, 1, 7, "binary"), -8);
assert.strictEqual(tickIncrement(0, 1, 6, "binary"), -8);
assert.strictEqual(tickIncrement(0, 1, 5, "binary"), -4);
assert.strictEqual(tickIncrement(0, 1, 4, "binary"), -4);
assert.strictEqual(tickIncrement(0, 1, 3, "binary"), -4);
assert.strictEqual(tickIncrement(0, 1, 2, "binary"), -2);
assert.strictEqual(tickIncrement(0, 1, 1, "binary"), 1);
assert.strictEqual(tickIncrement(0, 10, 10, "binary"), 1);
assert.strictEqual(tickIncrement(0, 10, 9, "binary"), 1);
assert.strictEqual(tickIncrement(0, 10, 8, "binary"), 1);
assert.strictEqual(tickIncrement(0, 10, 7, "binary"), 2);
assert.strictEqual(tickIncrement(0, 10, 6, "binary"), 2);
assert.strictEqual(tickIncrement(0, 10, 5, "binary"), 2);
assert.strictEqual(tickIncrement(0, 10, 4, "binary"), 2);
assert.strictEqual(tickIncrement(0, 10, 3, "binary"), 4);
assert.strictEqual(tickIncrement(0, 10, 2, "binary"), 4);
assert.strictEqual(tickIncrement(0, 10, 1, "binary"), 8);
assert.strictEqual(tickIncrement(-10, 10, 10, "binary"), 2);
assert.strictEqual(tickIncrement(-10, 10, 9, "binary"), 2);
assert.strictEqual(tickIncrement(-10, 10, 8, "binary"), 2);
assert.strictEqual(tickIncrement(-10, 10, 7, "binary"), 4);
assert.strictEqual(tickIncrement(-10, 10, 6, "binary"), 4);
assert.strictEqual(tickIncrement(-10, 10, 5, "binary"), 4);
assert.strictEqual(tickIncrement(-10, 10, 4, "binary"), 4);
assert.strictEqual(tickIncrement(-10, 10, 3, "binary"), 8);
assert.strictEqual(tickIncrement(-10, 10, 2, "binary"), 8);
assert.strictEqual(tickIncrement(-10, 10, 1, "binary"), 16);
});

it("natural", () => {
assert.strictEqual(normalizeLn(tickIncrement( 0, Math.E / 100, 10, 'natural')), -1);
assert.strictEqual(normalizeLn(tickIncrement( 0, Math.E / 10, 10, 'natural')), -0.5);
assert.strictEqual(normalizeLn(tickIncrement( 0, Math.E / 10, 1, 'natural')), -0.5);
assert.strictEqual(normalizeLn(tickIncrement( 0, Math.E, 3, 'natural')), -0.5);
assert.strictEqual(normalizeLn(tickIncrement( -Math.E, Math.E, 2, 'natural')), 1);
assert.strictEqual(normalizeLn(tickIncrement( 0, Math.E * 10, 10, 'natural')), 1);
assert.strictEqual(normalizeLn(tickIncrement( 1, Math.E * 10, 10, 'natural')), 2);
assert.strictEqual(normalizeLn(tickIncrement( -Math.E * 10, Math.E * 10, 10, 'natural')), 2);
assert.strictEqual(normalizeLn(tickIncrement( Math.E, Math.E * 100, 10, 'natural')), 1);
assert.strictEqual(normalizeLn(tickIncrement( -Math.E * 100, Math.E * 100, 10, 'natural')), 2);
assert.strictEqual(normalizeLn(tickIncrement( 0, Math.E * 100, 10, 'natural')), 1);
assert.strictEqual(tickIncrement( 0, 10, 10, 'natural'), 1);
assert.strictEqual(tickIncrement( -10, 10, 10, 'natural'), 2);
});
})

export function normalizeLn(value) {
let tries = 0, normValue = value;
while (tries++ < 10 && normValue % 0.5 !== 0) normValue *= Math.E;
if (tries > 10) {
tries = 0, normValue = value
while (tries++ < 10 && normValue % 0.5 !== 0) normValue /= Math.E;
}
return tries > 10 ? NaN : normValue;
}
116 changes: 116 additions & 0 deletions test/tickStep-test.js
@@ -1,4 +1,5 @@
import assert from "assert";
import {normalizeLn} from "./tickIncrement-test.js";
import {tickStep} from "../src/index.js";

it("tickStep(start, stop, count) returns NaN if any argument is NaN", () => {
Expand Down Expand Up @@ -93,3 +94,118 @@ it("tickStep(start, stop, count) returns -tickStep(stop, start, count)", () => {
assert.strictEqual(tickStep(-10, 10, 2), -tickStep(10, -10, 2));
assert.strictEqual(tickStep(-10, 10, 1), -tickStep(10, -10, 1));
});

it("tickStep(start, stop, count, base) with binary log returns approximately count + 1 tickStep when start < stop", () => {
assert.strictEqual(tickStep( 0, 1, 10, 'binary'), 0.125);
assert.strictEqual(tickStep( 0, 1, 9, 'binary'), 0.125);
assert.strictEqual(tickStep( 0, 1, 8, 'binary'), 0.125);
assert.strictEqual(tickStep( 0, 1, 7, 'binary'), 0.125);
assert.strictEqual(tickStep( 0, 1, 6, 'binary'), 0.125);
assert.strictEqual(tickStep( 0, 1, 5, 'binary'), 0.25);
assert.strictEqual(tickStep( 0, 1, 4, 'binary'), 0.25);
assert.strictEqual(tickStep( 0, 1, 3, 'binary'), 0.25);
assert.strictEqual(tickStep( 0, 1, 2, 'binary'), 0.5);
assert.strictEqual(tickStep( 0, 1, 1, 'binary'), 1);
assert.strictEqual(tickStep( 0, 10, 10, 'binary'), 1);
assert.strictEqual(tickStep( 0, 10, 9, 'binary'), 1);
assert.strictEqual(tickStep( 0, 10, 8, 'binary'), 1);
assert.strictEqual(tickStep( 0, 10, 7, 'binary'), 2);
assert.strictEqual(tickStep( 0, 10, 6, 'binary'), 2);
assert.strictEqual(tickStep( 0, 10, 5, 'binary'), 2);
assert.strictEqual(tickStep( 0, 10, 4, 'binary'), 2);
assert.strictEqual(tickStep( 0, 10, 3, 'binary'), 4);
assert.strictEqual(tickStep( 0, 10, 2, 'binary'), 4);
assert.strictEqual(tickStep( 0, 10, 1, 'binary'), 8);
assert.strictEqual(tickStep(-10, 10, 10, 'binary'), 2);
assert.strictEqual(tickStep(-10, 10, 9, 'binary'), 2);
assert.strictEqual(tickStep(-10, 10, 8, 'binary'), 2);
assert.strictEqual(tickStep(-10, 10, 7, 'binary'), 4);
assert.strictEqual(tickStep(-10, 10, 6, 'binary'), 4);
assert.strictEqual(tickStep(-10, 10, 5, 'binary'), 4);
assert.strictEqual(tickStep(-10, 10, 4, 'binary'), 4);
assert.strictEqual(tickStep(-10, 10, 3, 'binary'), 8);
assert.strictEqual(tickStep(-10, 10, 2, 'binary'), 8);
assert.strictEqual(tickStep(-10, 10, 1, 'binary'), 16);
});

it("tickStep(start, stop, count, base) with natural log returns approximately count + 1 tickStep when start < stop", () => {
assert.strictEqual(normalizeLn(tickStep( 0, Math.E / 100, 10, 'natural')), 1);
assert.strictEqual(normalizeLn(tickStep( 0, Math.E / 10, 10, 'natural')), 2);
assert.strictEqual(normalizeLn(tickStep( 0, Math.E / 10, 1, 'natural')), 2);
assert.strictEqual(normalizeLn(tickStep( 0, Math.E, 3, 'natural')), 2);
assert.strictEqual(normalizeLn(tickStep( -Math.E, Math.E, 2, 'natural')), 1);
assert.strictEqual(normalizeLn(tickStep( 0, Math.E * 10, 10, 'natural')), 1);
assert.strictEqual(normalizeLn(tickStep( 1, Math.E * 10, 10, 'natural')), 2);
assert.strictEqual(normalizeLn(tickStep( -Math.E * 10, Math.E * 10, 10, 'natural')), 2);
assert.strictEqual(normalizeLn(tickStep( Math.E, Math.E * 100, 10, 'natural')), 1);
assert.strictEqual(normalizeLn(tickStep( -Math.E * 100, Math.E * 100, 10, 'natural')), 2);
assert.strictEqual(normalizeLn(tickStep( 0, Math.E * 100, 10, 'natural')), 1);
assert.strictEqual(tickStep( 0, 10, 10, 'natural'), 1);
assert.strictEqual(tickStep( -10, 10, 10, 'natural'), 2);
});

it("tickStep(start, stop, count, base) with binary log returns -tickStep(stop, start, count)", () => {
assert.strictEqual(tickStep( 0, 1, 10, 'binary'), -tickStep( 1, 0, 10, 'binary'));
assert.strictEqual(tickStep( 0, 1, 9, 'binary'), -tickStep( 1, 0, 9, 'binary'));
assert.strictEqual(tickStep( 0, 1, 8, 'binary'), -tickStep( 1, 0, 8, 'binary'));
assert.strictEqual(tickStep( 0, 1, 7, 'binary'), -tickStep( 1, 0, 7, 'binary'));
assert.strictEqual(tickStep( 0, 1, 6, 'binary'), -tickStep( 1, 0, 6, 'binary'));
assert.strictEqual(tickStep( 0, 1, 5, 'binary'), -tickStep( 1, 0, 5, 'binary'));
assert.strictEqual(tickStep( 0, 1, 4, 'binary'), -tickStep( 1, 0, 4, 'binary'));
assert.strictEqual(tickStep( 0, 1, 3, 'binary'), -tickStep( 1, 0, 3, 'binary'));
assert.strictEqual(tickStep( 0, 1, 2, 'binary'), -tickStep( 1, 0, 2, 'binary'));
assert.strictEqual(tickStep( 0, 1, 1, 'binary'), -tickStep( 1, 0, 1, 'binary'));
assert.strictEqual(tickStep( 0, 10, 10, 'binary'), -tickStep(10, 0, 10, 'binary'));
assert.strictEqual(tickStep( 0, 10, 9, 'binary'), -tickStep(10, 0, 9, 'binary'));
assert.strictEqual(tickStep( 0, 10, 8, 'binary'), -tickStep(10, 0, 8, 'binary'));
assert.strictEqual(tickStep( 0, 10, 7, 'binary'), -tickStep(10, 0, 7, 'binary'));
assert.strictEqual(tickStep( 0, 10, 6, 'binary'), -tickStep(10, 0, 6, 'binary'));
assert.strictEqual(tickStep( 0, 10, 5, 'binary'), -tickStep(10, 0, 5, 'binary'));
assert.strictEqual(tickStep( 0, 10, 4, 'binary'), -tickStep(10, 0, 4, 'binary'));
assert.strictEqual(tickStep( 0, 10, 3, 'binary'), -tickStep(10, 0, 3, 'binary'));
assert.strictEqual(tickStep( 0, 10, 2, 'binary'), -tickStep(10, 0, 2, 'binary'));
assert.strictEqual(tickStep( 0, 10, 1, 'binary'), -tickStep(10, 0, 1, 'binary'));
assert.strictEqual(tickStep(-10, 10, 10, 'binary'), -tickStep(10, -10, 10, 'binary'));
assert.strictEqual(tickStep(-10, 10, 9, 'binary'), -tickStep(10, -10, 9, 'binary'));
assert.strictEqual(tickStep(-10, 10, 8, 'binary'), -tickStep(10, -10, 8, 'binary'));
assert.strictEqual(tickStep(-10, 10, 7, 'binary'), -tickStep(10, -10, 7, 'binary'));
assert.strictEqual(tickStep(-10, 10, 6, 'binary'), -tickStep(10, -10, 6, 'binary'));
assert.strictEqual(tickStep(-10, 10, 5, 'binary'), -tickStep(10, -10, 5, 'binary'));
assert.strictEqual(tickStep(-10, 10, 4, 'binary'), -tickStep(10, -10, 4, 'binary'));
assert.strictEqual(tickStep(-10, 10, 3, 'binary'), -tickStep(10, -10, 3, 'binary'));
assert.strictEqual(tickStep(-10, 10, 2, 'binary'), -tickStep(10, -10, 2, 'binary'));
assert.strictEqual(tickStep(-10, 10, 1, 'binary'), -tickStep(10, -10, 1, 'binary'));
});

it("tickStep(start, stop, count, base) with natural log returns -tickStep(stop, start, count)", () => {
assert.strictEqual(tickStep( 0, 1, 10, 'natural'), -tickStep( 1, 0, 10, 'natural'));
assert.strictEqual(tickStep( 0, 1, 9, 'natural'), -tickStep( 1, 0, 9, 'natural'));
assert.strictEqual(tickStep( 0, 1, 8, 'natural'), -tickStep( 1, 0, 8, 'natural'));
assert.strictEqual(tickStep( 0, 1, 7, 'natural'), -tickStep( 1, 0, 7, 'natural'));
assert.strictEqual(tickStep( 0, 1, 6, 'natural'), -tickStep( 1, 0, 6, 'natural'));
assert.strictEqual(tickStep( 0, 1, 5, 'natural'), -tickStep( 1, 0, 5, 'natural'));
assert.strictEqual(tickStep( 0, 1, 4, 'natural'), -tickStep( 1, 0, 4, 'natural'));
assert.strictEqual(tickStep( 0, 1, 3, 'natural'), -tickStep( 1, 0, 3, 'natural'));
assert.strictEqual(tickStep( 0, 1, 2, 'natural'), -tickStep( 1, 0, 2, 'natural'));
assert.strictEqual(tickStep( 0, 1, 1, 'natural'), -tickStep( 1, 0, 1, 'natural'));
assert.strictEqual(tickStep( 0, 10, 10, 'natural'), -tickStep(10, 0, 10, 'natural'));
assert.strictEqual(tickStep( 0, 10, 9, 'natural'), -tickStep(10, 0, 9, 'natural'));
assert.strictEqual(tickStep( 0, 10, 8, 'natural'), -tickStep(10, 0, 8, 'natural'));
assert.strictEqual(tickStep( 0, 10, 7, 'natural'), -tickStep(10, 0, 7, 'natural'));
assert.strictEqual(tickStep( 0, 10, 6, 'natural'), -tickStep(10, 0, 6, 'natural'));
assert.strictEqual(tickStep( 0, 10, 5, 'natural'), -tickStep(10, 0, 5, 'natural'));
assert.strictEqual(tickStep( 0, 10, 4, 'natural'), -tickStep(10, 0, 4, 'natural'));
assert.strictEqual(tickStep( 0, 10, 3, 'natural'), -tickStep(10, 0, 3, 'natural'));
assert.strictEqual(tickStep( 0, 10, 2, 'natural'), -tickStep(10, 0, 2, 'natural'));
assert.strictEqual(tickStep( 0, 10, 1, 'natural'), -tickStep(10, 0, 1, 'natural'));
assert.strictEqual(tickStep(-10, 10, 10, 'natural'), -tickStep(10, -10, 10, 'natural'));
assert.strictEqual(tickStep(-10, 10, 9, 'natural'), -tickStep(10, -10, 9, 'natural'));
assert.strictEqual(tickStep(-10, 10, 8, 'natural'), -tickStep(10, -10, 8, 'natural'));
assert.strictEqual(tickStep(-10, 10, 7, 'natural'), -tickStep(10, -10, 7, 'natural'));
assert.strictEqual(tickStep(-10, 10, 6, 'natural'), -tickStep(10, -10, 6, 'natural'));
assert.strictEqual(tickStep(-10, 10, 5, 'natural'), -tickStep(10, -10, 5, 'natural'));
assert.strictEqual(tickStep(-10, 10, 4, 'natural'), -tickStep(10, -10, 4, 'natural'));
assert.strictEqual(tickStep(-10, 10, 3, 'natural'), -tickStep(10, -10, 3, 'natural'));
assert.strictEqual(tickStep(-10, 10, 2, 'natural'), -tickStep(10, -10, 2, 'natural'));
assert.strictEqual(tickStep(-10, 10, 1, 'natural'), -tickStep(10, -10, 1, 'natural'));
});