function Range(start, end, step) {
    if (!(this instanceof Range)) {
      throw new SyntaxError('Constructor must be called with the new operator');
    }

    var hasStart = start !== null && start !== undefined;
    var hasEnd = end !== null && end !== undefined;
    var hasStep = step !== null && step !== undefined;

    if (hasStart) {
      if (type.isBigNumber(start)) {
        start = start.toNumber();
      } else if (typeof start !== 'number') {
        throw new TypeError('Parameter start must be a number');
      }
    }

    if (hasEnd) {
      if (type.isBigNumber(end)) {
        end = end.toNumber();
      } else if (typeof end !== 'number') {
        throw new TypeError('Parameter end must be a number');
      }
    }

    if (hasStep) {
      if (type.isBigNumber(step)) {
        step = step.toNumber();
      } else if (typeof step !== 'number') {
        throw new TypeError('Parameter step must be a number');
      }
    }

    this.start = hasStart ? parseFloat(start) : 0;
    this.end = hasEnd ? parseFloat(end) : 0;
    this.step = hasStep ? parseFloat(step) : 1;
  }
Create a range. A range has a start, step, and end, and contains functions
to iterate over the range.

A range can be constructed as:

    const range = new Range(start, end)
    const range = new Range(start, end, step)

To get the result of the range:
    range.forEach(function (x) {
        console.log(x)
    })
    range.map(function (x) {
        return math.sin(x)
    })
    range.toArray()

Example usage:

    const c = new Range(2, 6)       // 2:1:5
    c.toArray()                     // [2, 3, 4, 5]
    const d = new Range(2, -3, -1)  // 2:-1:-2
    d.toArray()                     // [2, 1, 0, -1, -2]

@class Range
@constructor Range
@param {number} start  included lower bound
@param {number} end    excluded upper bound
@param {number} [step] step size, default value is 1