import assert from "assert";
import {cumsum, fcumsum} from "../src/index.js";

it("fcumsum(array) returns a Float64Array of the expected length", () => {
  const A = [0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1];
  const R = cumsum(A);
  assert(R instanceof Float64Array);
  assert.strictEqual(R.length, A.length);
});

it("fcumsum(array) is an exact cumsum", () => {
  assert.strictEqual(lastc([0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1]), 1);
  assert.strictEqual(lastc([0.3, 0.3, 0.3, 0.3, 0.3, 0.3, 0.3, 0.3, 0.3, 0.3, -0.3, -0.3, -0.3, -0.3, -0.3, -0.3, -0.3, -0.3, -0.3, -0.3]), 0);
  assert.strictEqual(lastc(["20", "3"].map(box), unbox), 23);
});

it("fcumsum(array) returns the fsum of the specified numbers", () => {
  assert.strictEqual(lastc([1]), 1);
  assert.strictEqual(lastc([5, 1, 2, 3, 4]), 15);
  assert.strictEqual(lastc([20, 3]), 23);
  assert.strictEqual(lastc([3, 20]), 23);
});

it("fcumsum(array) observes values that can be coerced to numbers", () => {
  assert.strictEqual(lastc(["20", "3"]), 23);
  assert.strictEqual(lastc(["3", "20"]), 23);
  assert.strictEqual(lastc(["3", 20]), 23);
  assert.strictEqual(lastc([20, "3"]), 23);
  assert.strictEqual(lastc([3, "20"]), 23);
  assert.strictEqual(lastc(["20", 3]), 23);
});

it("fcumsum(array) ignores non-numeric values", () => {
  assert.strictEqual(lastc(["a", "b", "c"]), 0);
  assert.strictEqual(lastc(["a", 1, "2"]), 3);
});

it("fcumsum(array) ignores null, undefined and NaN", () => {
  assert.strictEqual(lastc([NaN, 1, 2, 3, 4, 5]), 15);
  assert.strictEqual(lastc([1, 2, 3, 4, 5, NaN]), 15);
  assert.strictEqual(lastc([10, null, 3, undefined, 5, NaN]), 18);
});

it("fcumsum(array) returns an array of zeros if there are no numbers", () => {
  assert.deepStrictEqual(Array.from(fcumsum([])), []);
  assert.deepStrictEqual(Array.from(fcumsum([NaN])), [0]);
  assert.deepStrictEqual(Array.from(fcumsum([undefined])), [0]);
  assert.deepStrictEqual(Array.from(fcumsum([undefined, NaN])), [0, 0]);
  assert.deepStrictEqual(Array.from(fcumsum([undefined, NaN, {}])), [0, 0, 0]);
});

it("fcumsum(array, f) returns the fsum of the specified numbers", () => {
  assert.strictEqual(lastc([1].map(box), unbox), 1);
  assert.strictEqual(lastc([5, 1, 2, 3, 4].map(box), unbox), 15);
  assert.strictEqual(lastc([20, 3].map(box), unbox), 23);
  assert.strictEqual(lastc([3, 20].map(box), unbox), 23);
});

it("fcumsum(array, f) observes values that can be coerced to numbers", () => {
  assert.strictEqual(lastc(["20", "3"].map(box), unbox), 23);
  assert.strictEqual(lastc(["3", "20"].map(box), unbox), 23);
  assert.strictEqual(lastc(["3", 20].map(box), unbox), 23);
  assert.strictEqual(lastc([20, "3"].map(box), unbox), 23);
  assert.strictEqual(lastc([3, "20"].map(box), unbox), 23);
  assert.strictEqual(lastc(["20", 3].map(box), unbox), 23);
});

it("fcumsum(array, f) ignores non-numeric values", () => {
  assert.strictEqual(lastc(["a", "b", "c"].map(box), unbox), 0);
  assert.strictEqual(lastc(["a", 1, "2"].map(box), unbox), 3);
});

it("fcumsum(array, f) ignores null, undefined and NaN", () => {
  assert.strictEqual(lastc([NaN, 1, 2, 3, 4, 5].map(box), unbox), 15);
  assert.strictEqual(lastc([1, 2, 3, 4, 5, NaN].map(box), unbox), 15);
  assert.strictEqual(lastc([10, null, 3, undefined, 5, NaN].map(box), unbox), 18);
});

it("fcumsum(array, f) returns zero if there are no numbers", () => {
  assert.deepStrictEqual(Array.from(fcumsum([].map(box), unbox)), []);
  assert.deepStrictEqual(Array.from(fcumsum([NaN].map(box), unbox)), [0]);
  assert.deepStrictEqual(Array.from(fcumsum([undefined].map(box), unbox)), [0]);
  assert.deepStrictEqual(Array.from(fcumsum([undefined, NaN].map(box), unbox)), [0, 0]);
  assert.deepStrictEqual(Array.from(fcumsum([undefined, NaN, {}].map(box), unbox)), [0, 0, 0]);
});

it("fcumsum(array, f) passes the accessor d, i, and array", () => {
  const results = [];
  const array = ["a", "b", "c"];
  lastc(array, (d, i, array) => results.push([d, i, array]));
  assert.deepStrictEqual(results, [["a", 0, array], ["b", 1, array], ["c", 2, array]]);
});

it("fcumsum(array, f) uses the undefined context", () => {
  const results = [];
  lastc([1, 2], function() { results.push(this); });
  assert.deepStrictEqual(results, [undefined, undefined]);
});

function box(value) {
  return {value: value};
}

function unbox(box) {
  return box.value;
}

function lastc(values, valueof) {
  const array = fcumsum(values, valueof);
  return array[array.length -1];
}
