range = {
const range_proxy_handler = {
get: function get(obj, prop) {
if (typeof prop == 'symbol') return Reflect.get(obj, prop);
const num = +prop;
if (!Number.isInteger(num)) return Reflect.get(obj, prop);
if ((0 <= num) && (num < obj.length))
return obj.start + obj.step * num;
if ((num < 0) && (num >= -obj.length))
return obj.start + obj.step * (obj.length + num);
return void 0; },
set: function set(obj, prop, value) {
if (typeof prop === 'symbol') return Reflect.set(obj, prop, value);
if (!Number.isInteger(+prop)) {
if (['start', 'stop', 'step', 'length'].indexOf(prop) >= 0)
throw new Error(`Cannot change ${prop} of a range.`);
return Reflect.set(obj, prop, value);
}
throw new Error('Cannot set integer properties on a range.'); },
has: function has(obj, prop) {
if (typeof prop === 'symbol') return Reflect.has(obj, prop);
const num = +prop;
if (!Number.isInteger(num)) return Reflect.has(obj, prop);
return (0 <= num) && (num < obj.length); }}
const range_proto = Object.assign(Object.create(Array.prototype), {
concat: function concat() {
return Array.prototype.concat.call(this, ...arguments); },
includes: function includes(value, from_index=0) {
if (+value !== value) return false;
const k = Math.round((value - this.start) / this.step);
return (from_index <= k) && (k < this.length) &&
(this.start + this.step * k === value); },
indexOf: function indexOf(value, from_index=0) {
if (+value !== value) return -1;
if (from_index < 0) from_index = length + from_index;
const k = Math.round((value - this.start) / this.step);
if ((from_index <= k) && (k < this.length) &&
(this.start + this.step * k === value)) return k;
return -1; },
lastIndexOf: function indexOf(value, from_index) {
if (+value !== value) return -1;
const {length} = this.length
if (from_index == null || from_index >= length) from_index = length - 1;
else if (from_index < 0) from_index = length + from_index;
const k = Math.round((value - this.start) / this.step);
if ((0 <= k) && (k <= from_index) &&
(this.start + this.step * k === value)) return k;
return -1; },
reverse: function reverse() {
this.start = this.start + (this.length - 1)*this.step;
this.step = -this.step;
this.stop = this.start + this.length*this.step;
return this; },
slice: function slice(begin, end) {
const {start, step, length} = this;
begin = (begin == null) ? 0
: (begin < 0) ? length + begin
: (begin > length) ? length : begin;
end = (end == null) ? length
: (end < 0) ? length + end
: (end > length) ? length : end;
if (end < begin) end = begin;
return new Range(start + begin*step, start + end*step, step); },
*[Symbol.iterator]() {
for (let i = 0; i < this.length; i++) yield this.start + i*this.step; },
[Symbol.species]() { return Array; },
[Symbol.toStringTag]: 'Range',
[Symbol.toPrimitive](hint) {
if (hint === 'number') return NaN;
return `Range(${this.start}, ${this.stop}, ${this.step})`; },
[Symbol.isConcatSpreadable]: true });
for (let unsupported_method of [
"copyWithin", "fill", "pop", "push",
"shift", "sort", "splice", "unshift"]) {
range_proto[unsupported_method] = void 0; }
function Range(start, stop, step=1) {
if (stop == null) stop = start, start = 0;
if (!(Number.isInteger(start) && Number.isInteger(stop) && Number.isInteger(step)))
throw new Error(`Range start, stop, and step must be integers.`);
if (step == 0) throw new Error(`Range step cannot be zero.`);
if (!(step > 0 ^ stop < start)) stop = start;
const length = Math.max(0, Math.ceil((stop - start) / step)) | 0;
this.start = start, this.stop = stop, this.step = step, this.length = length;
return new Proxy(this, range_proxy_handler); }
Range.prototype = range_proto;
return function range(start, stop, step=1) {
return new Range(start, stop, step); }; }