Source: vector.js

const Pair = require('./pair');

/**
 * @typedef {object} Vector
 * @property {number} x
 * @property {number} y
 */

/**
 *
 * @param {number} x
 * @param {number} y
 *
 * @returns {Vector}
 */
function vector(x, y) {
  return { x, y };
}

/**
 * @param {Vector|number} value
 * @returns {Vector}
 */
function cast(value) {
  if (typeof value === 'number') {
    return vector(value, value);
  } else {
    return value;
  }
}

 /**
  * This module contains functions for dealing with objects with x and y
  * coordinates that represent or include point data
  *
  * @module Vector
  */

/**
 * Returns a new (0, 0) vector
 *
 * @returns {Vector}
 */
function zero() {
  return vector(0, 0);
}

/**
 * Compares two points
 *
 * @param {Vector} one
 * @param {Vector} other
 * @param {number} [delta] the tolance in comparison
 * @returns {boolean}
 */
function equal(one, other, delta = 0) {
  return Pair.equal(one.x, one.y, other.x, other.y, delta);
}

/**
 * Creates a copy of the given point
 *
 * @param {Vector} one
 * @returns {Vector}
 */
function copy({x, y}) {
  return {x, y}
}

/**
 * @param {Vector} vector
 * @param {any} x
 * @param {any} y
 */
function update(vector, x, y) {
  vector.x = x;
  vector.y = y;
}

/**
 * @param {Vector} one
 * @param {Vector} other
 * @returns {import('./pair').Pair};
 */
function diff(one, other) {
  return Pair.diff(one.x, one.y, other.x, other.y);
}

/**
 * @param {Vector|number} one
 * @param {Vector|number} other
 *
 * @returns {Vector}
 */
function multiply(one, other) {
  return apply(one, other, (v1, v2) => v1 * v2);
}

/**
 * @param {Vector|number} one
 * @param {Vector|number} other
 *
 * @returns {Vector}
 */
function divide(one, other) {
  return apply(one, other, (v1, v2) => v1 / v2);
}

/**
 * @param {Vector|number} one
 * @param {Vector|number} other
 *
 * @returns {Vector}
 */
function plus(one, other) {
  return apply(one, other, (v1, v2) => v1 + v2);
}

/**
 * @param {Vector|number} one
 * @param {Vector|number} other
 *
 * @returns {Vector}
 */
function minus(one, other) {
  return apply(one, other, (v1, v2) => v1 - v2);
}

/**
 * @param {Vector|number} one
 * @param {Vector|number} other
 *
 * @returns {Vector}
 */
function min(one, other) {
  return apply(one, other, Math.min);
}

/**
 * @param {Vector|number} one
 * @param {Vector|number} other
 *
 * @returns {Vector}
 */
function max(one, other) {
  return apply(one, other, Math.max);
}

/**
 * @param {Vector|number} one
 * @param {Vector|number} other
 * @param {(one: number, other: number) => number} f
 *
 * @returns {Vector}
 */
function apply(one, other, f) {
  const first = cast(one);
  const second = cast(other);
  return {x: f(first.x, second.x), y: f(first.y, second.y)};
}

const inner = {
  /**
   * @param {Vector} one
   *
   * @returns {number}
   */
  min(one) {
    return this.apply(one, Math.min);
  },

  /**
   * @param {Vector} one
   *
   * @returns {number}
   */
  max(one) {
    return this.apply(one, Math.max);
  },

  /**
   * @param {Vector} one
   * @param {(one: number, other: number) => number} f
   * @return {number}
   */
  apply(one, f) {
    return f(one.x, one.y)
  }
}

module.exports = {
  cast,
  vector,
  copy,
  equal,
  zero,
  update,
  diff,
  multiply,
  divide,
  plus,
  minus,
  apply,
  min,
  max,
  inner
};