text: /**
  * @license Apache-2.0
  *
  * Copyright (c) 2018 The Stdlib Authors.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
  * You may obtain a copy of the License at
  *
  *    http://www.apache.org/licenses/LICENSE-2.0
  *
  * Unless required by applicable law or agreed to in writing, software
  * distributed under the License is distributed on an "AS IS" BASIS,
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
  
  /* eslint-disable no-restricted-syntax, no-invalid-this */
  
  'use strict';
  
  // MODULES //
  
  var isCollection = require( '@stdlib/assert/is-collection' );
  var isPositiveInteger = require( '@stdlib/assert/is-positive-integer' ).isPrimitive;
  var isNonNegativeInteger = require( '@stdlib/assert/is-nonnegative-integer' ).isPrimitive;
  var setReadOnly = require( '@stdlib/utils/define-nonenumerable-read-only-property' );
  var setReadOnlyAccessor = require( '@stdlib/utils/define-nonenumerable-read-only-accessor' );
  var iteratorSymbol = require( '@stdlib/symbol/iterator' );
  var MAX_ITERATIONS = require( '@stdlib/constants/float64/max' );
  
  
  // MAIN //
  
  /**
  * Circular buffer constructor.
  *
  * @constructor
  * @param {(PositiveInteger|Collection)} buffer - buffer size or an array-like object to use as the underlying buffer
  * @throws {TypeError} must provide either a valid buffer size or an array-like object
  * @returns {CircularBuffer} circular buffer instance
  *
  * @example
  * var b = new CircularBuffer( 3 );
  *
  * // Fill the buffer...
  * var v = b.push( 'foo' );
  * // returns undefined
  *
  * v = b.push( 'bar' );
  * // returns undefined
  *
  * v = b.push( 'beep' );
  * // returns undefined
  *
  * // Add another value to the buffer and return the removed element:
  * v = b.push( 'boop' );
  * // returns 'foo'
  */
  function CircularBuffer( buffer ) {
  	var i;
  	if ( !(this instanceof CircularBuffer) ) {
  		return new CircularBuffer( buffer );
  	}
  	if ( isPositiveInteger( buffer ) ) {
  		this._buffer = [];
  		for ( i = 0; i < buffer; i++ ) {
  			this._buffer.push( 0.0 ); // initialize with zeros, but could be any value (we're just ensuring a contiguous block of memory)
  		}
  	} else if ( isCollection( buffer ) ) {
  		this._buffer = buffer;
  	} else {
  		throw new TypeError( 'invalid argument. Must provide either a valid buffer size (positive integer) or an array-like object which can serve as the underlying buffer. Value: `' + buffer + '`.' );
  	}
  	this._length = this._buffer.length;
  	this._count = 0;
  	this._i = -1;
  	return this;
  }
  
  /**
  * Clears the buffer.
  *
  * @name clear
  * @memberof CircularBuffer.prototype
  * @type {Function}
  * @returns {CircularBuffer} circular buffer instance
  *
  * @example
  * var b = new CircularBuffer( 2 );
  *
  * // Add values to the buffer:
  * b.push( 'foo' );
  * b.push( 'bar' );
  * b.push( 'beep' );
  * b.push( 'boop' );
  *
  * // Get the number of elements currently in the buffer:
  * var n = b.count;
  * // returns 2
  *
  * // Clear the buffer:
  * b.clear();
  *
  * // Get the number of buffer values:
  * n = b.count;
  * // returns 0
  */
  setReadOnly( CircularBuffer.prototype, 'clear', function clear() {
  	this._count = 0;
  	this._i = -1; // NOTE: this ensures that we always fill the buffer starting at index `0`.
  	return this;
  });
  
  /**
  * Number of elements currently in the buffer.
  *
  * @name count
  * @memberof CircularBuffer.prototype
  * @type {NonNegativeInteger}
  *
  * @example
  * var b = new CircularBuffer( 4 );
  *
  * // Get the value count:
  * var n = b.count;
  * // returns 0
  *
  * // Add values to the buffer:
  * b.push( 'foo' );
  * b.push( 'bar' );
  *
  * // Get the value count:
  * n = b.count;
  * // returns 2
  */
  setReadOnlyAccessor( CircularBuffer.prototype, 'count', function get() {
  	return this._count;
  });
  
  /**
  * Boolean indicating whether a circular buffer is full.
  *
  * @name full
  * @memberof CircularBuffer.prototype
  * @type {boolean}
  *
  * @example
  * var b = new CircularBuffer( 3 );
  *
  * // Determine if the buffer is full:
  * var bool = b.full;
  * // returns false
  *
  * // Add values to the buffer:
  * b.push( 'foo' );
  * b.push( 'bar' );
  * b.push( 'beep' );
  * b.push( 'boop' );
  *
  * // Determine if the buffer is full:
  * bool = b.full;
  * // returns true
  */
  setReadOnlyAccessor( CircularBuffer.prototype, 'full', function get() {
  	return this._count === this._length;
  });
  
  /**
  * Returns an iterator for iterating over a circular buffer.
  *
  * ## Notes
  *
  * -   An iterator does not iterate over partially full buffers.
  *
  * @name iterator
  * @memberof CircularBuffer.prototype
  * @type {Function}
  * @param {NonNegativeInteger} [niters] - number of iterations
  * @throws {TypeError} must provide a nonnegative integer
  * @returns {Iterator} iterator
  *
  * @example
  * var b = new CircularBuffer( 3 );
  *
  * // Add values to the buffer:
  * b.push( 'foo' );
  * b.push( 'bar' );
  * b.push( 'beep' );
  * b.push( 'boop' );
  *
  * // Create an iterator:
  * var it = b.iterator( b.length );
  *
  * // Iterate over the buffer...
  * var v = it.next().value;
  * // returns 'bar'
  *
  * v = it.next().value;
  * // returns 'beep'
  *
  * v = it.next().value;
  * // returns 'boop'
  *
  * var bool = it.next().done;
  * // returns true
  */
  setReadOnly( CircularBuffer.prototype, 'iterator', function iterator( niters ) {
  	var iter;
  	var self;
  	var FLG;
  	var N;
  	var n;
  	var i;
  
  	if ( arguments.length ) {
  		if ( !isNonNegativeInteger( niters ) ) {
  			throw new TypeError( 'invalid argument. Must provide a nonnegative integer. Value: `' + niters + '`.' );
  		}
  		N = niters;
  	} else {
  		N = MAX_ITERATIONS;
  	}
  	self = this;
  
  	// Initialize the iteration index and counter:
  	i = this._i;
  	n = 0;
  
  	// Create an iterator protocol-compliant object:
  	iter = {};
  	setReadOnly( iter, 'next', next );
  	setReadOnly( iter, 'return', end );
  	if ( iteratorSymbol ) {
  		setReadOnly( iter, iteratorSymbol, factory );
  	}
  	return iter;
  
  	/**
  	* Returns an iterator protocol-compliant object containing the next iterated value.
  	*
  	* @private
  	* @returns {Object} iterator protocol-compliant object
  	*/
  	function next() {
  		/* eslint-disable no-underscore-dangle */
  		n += 1;
  		if ( FLG || n > N ) {
  			return {
  				'done': true
  			};
  		}
  		// If the buffer is only partially full, don't allow iteration over "undefined" elements (this ensures similar behavior with `toArray()`)...
  		if ( self._count !== self._length ) {
  			FLG = true;
  			return {
  				'done': true
  			};
  		}
  		i = (i+1) % self._length;
  		return {
  			'value': self._buffer[ i ],
  			'done': false
  		};
  
  		/* eslint-enable no-underscore-dangle */
  	}
  
  	/**
  	* Finishes an iterator.
  	*
  	* @private
  	* @param {*} [value] - value to return
  	* @returns {Object} iterator protocol-compliant object
  	*/
  	function end( value ) {
  		FLG = true;
  		if ( arguments.length ) {
  			return {
  				'value': value,
  				'done': true
  			};
  		}
  		return {
  			'done': true
  		};
  	}
  
  	/**
  	* Returns a new iterator.
  	*
  	* @private
  	* @returns {Iterator} iterator
  	*/
  	function factory() {
  		return self.iterator( N );
  	}
  });
  
  /**
  * Circular buffer length (capacity).
  *
  * @name length
  * @memberof CircularBuffer.prototype
  * @type {NonNegativeInteger}
  *
  * @example
  * var b = new CircularBuffer( 4 );
  *
  * // Get the buffer capacity:
  * var len = b.length;
  * // returns 4
  */
  setReadOnlyAccessor( CircularBuffer.prototype, 'length', function get() {
  	return this._length;
  });
  
  /**
  * Adds a value to the circular buffer.
  *
  * @name push
  * @memberof CircularBuffer.prototype
  * @type {Function}
  * @returns {(*|void)} removed element or undefined
  *
  * @example
  * var b = new CircularBuffer( 3 );
  *
  * // Fill the buffer:
  * var v = b.push( 'foo' );
  * // returns undefined
  *
  * v = b.push( 'bar' );
  * // returns undefined
  *
  * v = b.push( 'beep' );
  * // returns undefined
  *
  * // Add another value to the buffer and return the removed element:
  * v = b.push( 'boop' );
  * // returns 'foo'
  */
  setReadOnly( CircularBuffer.prototype, 'push', function push( value ) {
  	var v;
  
  	// Compute the next buffer index:
  	this._i = (this._i+1) % this._length;
  
  	// Check if we are still filling the buffer...
  	if ( this._count < this._length ) {
  		this._buffer[ this._i ] = value;
  		this._count += 1;
  		return;
  	}
  	// Replace an existing buffer element...
  	v = this._buffer[ this._i ];
  	this._buffer[ this._i ] = value;
  	return v;
  });
  
  /**
  * Returns an array of circular buffer values.
  *
  * @name toArray
  * @memberof CircularBuffer.prototype
  * @type {Function}
  * @returns {Array} circular buffer values
  *
  * @example
  * var b = new CircularBuffer( 3 );
  *
  * // Add values to the buffer:
  * b.push( 'foo' );
  * b.push( 'bar' );
  * b.push( 'beep' );
  * b.push( 'boop' );
  *
  * // Get an array of buffer values:
  * var vals = b.toArray();
  * // returns [ 'bar', 'beep', 'boop' ]
  */
  setReadOnly( CircularBuffer.prototype, 'toArray', function toArray() {
  	var out;
  	var k;
  	var i;
  
  	out = [];
  	for ( i = 1; i <= this._count; i++ ) {
  		// Note: in a full buffer, `count == length`; in a partially full buffer, we need to ensure we always start at index `0`
  		k = (this._i+i) % this._count;
  		out.push( this._buffer[ k ] );
  	}
  	return out;
  });
  
  /**
  * Serializes a circular buffer as JSON.
  *
  * ## Notes
  *
  * -   `JSON.stringify()` implicitly calls this method when stringifying a `CircularBuffer` instance.
  *
  * @name toJSON
  * @memberof CircularBuffer.prototype
  * @type {Function}
  * @returns {Object} serialized circular buffer
  *
  * @example
  * var b = new CircularBuffer( 3 );
  *
  * // Add values to the buffer:
  * b.push( 'foo' );
  * b.push( 'bar' );
  * b.push( 'beep' );
  * b.push( 'boop' );
  *
  * // Serialize to JSON:
  * var o = b.toJSON();
  * // returns { 'type': 'circular-buffer', 'length': 3, 'data': [ 'bar', 'beep', 'boop' ] }
  */
  setReadOnly( CircularBuffer.prototype, 'toJSON', function toJSON() {
  	var out = {};
  	out.type = 'circular-buffer';
  	out.length = this._length;
  	out.data = this.toArray();
  	return out;
  });
  
  
  // EXPORTS //
  
  module.exports = CircularBuffer;
  