2015-07-06 11:14:42 +02:00
|
|
|
/**
|
|
|
|
* @namespace
|
|
|
|
* @memberof L3D
|
|
|
|
*/
|
2015-07-01 16:31:43 +02:00
|
|
|
L3D.utils = (function() {
|
2015-06-10 17:24:07 +02:00
|
|
|
|
|
|
|
var utils = {};
|
|
|
|
|
2015-07-06 11:14:42 +02:00
|
|
|
/**
|
|
|
|
* @constructor
|
|
|
|
* @description Linked list in javascript
|
|
|
|
* @memberof L3D.utils
|
|
|
|
*/
|
|
|
|
var List = function() {
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @private
|
|
|
|
* @type {Number}
|
|
|
|
* @description number of elements in the list
|
|
|
|
*/
|
2015-06-10 17:24:07 +02:00
|
|
|
this._size = 0;
|
2015-07-06 11:14:42 +02:00
|
|
|
|
|
|
|
/**
|
|
|
|
* @private
|
|
|
|
* @type {Object}
|
|
|
|
* @description first chain element of the list
|
|
|
|
*/
|
2015-06-10 17:24:07 +02:00
|
|
|
this._begin = null;
|
2015-07-06 11:14:42 +02:00
|
|
|
|
|
|
|
/**
|
|
|
|
* @private
|
|
|
|
* @type {Object}
|
|
|
|
* @description last chain element of the list
|
|
|
|
*/
|
2015-06-10 17:24:07 +02:00
|
|
|
this._end = null;
|
2015-07-01 10:14:15 +02:00
|
|
|
};
|
2015-06-10 17:24:07 +02:00
|
|
|
|
2015-07-06 11:14:42 +02:00
|
|
|
/**
|
|
|
|
* Size of the list
|
|
|
|
* Complexity O(1)
|
|
|
|
* @returns {Number} the number of elements in the list
|
|
|
|
*/
|
|
|
|
List.prototype.size = function() {
|
2015-06-10 17:24:07 +02:00
|
|
|
return this._size;
|
2015-07-01 10:14:15 +02:00
|
|
|
};
|
2015-06-10 17:24:07 +02:00
|
|
|
|
2015-07-06 11:14:42 +02:00
|
|
|
/**
|
|
|
|
* Push an element at the end of the list
|
|
|
|
* Complexity O(1)
|
|
|
|
* @param element {Object} object to push at the end of the list
|
|
|
|
*/
|
|
|
|
List.prototype.push = function(element) {
|
2015-06-10 17:24:07 +02:00
|
|
|
if (this._size === 0) {
|
|
|
|
this._begin = { data : element, next: null, prev: null };
|
|
|
|
this._end = this._begin;
|
|
|
|
this._size = 1;
|
|
|
|
} else {
|
|
|
|
var newObject = { data: element, next: null, prev: this._end };
|
|
|
|
this._end.next = newObject;
|
|
|
|
this._end = newObject;
|
|
|
|
this._size++;
|
|
|
|
}
|
2015-07-01 10:14:15 +02:00
|
|
|
};
|
2015-06-10 17:24:07 +02:00
|
|
|
|
2015-07-06 11:14:42 +02:00
|
|
|
/**
|
|
|
|
* Sorts the list by creating an array, sorting it, and recopying it to the list
|
2015-07-09 14:43:35 +02:00
|
|
|
* @param comparator {function} comparator between objects
|
2015-07-06 11:14:42 +02:00
|
|
|
* Complexity O(size() * log (size()))
|
|
|
|
*/
|
|
|
|
List.prototype.sort = function(comparator) {
|
2015-06-10 17:24:07 +02:00
|
|
|
|
|
|
|
if (comparator === undefined) {
|
|
|
|
comparator = priv.defaultComparator;
|
|
|
|
}
|
|
|
|
|
|
|
|
var array = [];
|
|
|
|
this.forEach(function(elt) {
|
|
|
|
array.push(elt);
|
|
|
|
});
|
|
|
|
|
|
|
|
array.sort(function(a, b) {
|
|
|
|
return comparator(a, b);
|
|
|
|
});
|
|
|
|
|
|
|
|
var size = this.size();
|
|
|
|
this.clear();
|
|
|
|
|
|
|
|
for (var i = 0; i < size; i++) {
|
|
|
|
this.push(array[i]);
|
|
|
|
}
|
2015-07-01 10:14:15 +02:00
|
|
|
};
|
2015-06-10 17:24:07 +02:00
|
|
|
|
2015-07-06 11:14:42 +02:00
|
|
|
/**
|
|
|
|
* Removes and returns the last element of the list
|
|
|
|
* Complexity O(1)
|
|
|
|
* @returns {Object} the last element of the list
|
|
|
|
*/
|
|
|
|
List.prototype.pop = function() {
|
2015-06-10 17:24:07 +02:00
|
|
|
var tmp = this._end;
|
|
|
|
this._end = null;
|
|
|
|
return tmp.data;
|
2015-07-01 10:14:15 +02:00
|
|
|
};
|
2015-06-10 17:24:07 +02:00
|
|
|
|
2015-07-06 11:14:42 +02:00
|
|
|
/**
|
|
|
|
* Apply a call back to each element of the list
|
|
|
|
* Complexity O(size())
|
|
|
|
* @param callback {function} callback to call on all elements of the list
|
|
|
|
*/
|
|
|
|
List.prototype.forEach = function(callback) {
|
2015-06-10 17:24:07 +02:00
|
|
|
var chain = this._begin;
|
|
|
|
|
|
|
|
while (chain !== null) {
|
|
|
|
callback(chain.data);
|
|
|
|
chain = chain.next;
|
|
|
|
}
|
2015-07-01 10:14:15 +02:00
|
|
|
};
|
2015-06-10 17:24:07 +02:00
|
|
|
|
2015-07-06 11:14:42 +02:00
|
|
|
/**
|
|
|
|
* Apply a call back to each element of the list in reverse order
|
|
|
|
* Complexity O(size())
|
|
|
|
* @param callback {function} callback to call on all elements of the list in reverse order
|
|
|
|
*/
|
|
|
|
List.prototype.forEachInverse = function(callback) {
|
2015-06-10 17:24:07 +02:00
|
|
|
var chain = this._end;
|
|
|
|
|
|
|
|
while (chain !== null) {
|
|
|
|
callback(chain.data);
|
|
|
|
chain = chain.prev;
|
|
|
|
}
|
2015-07-01 10:14:15 +02:00
|
|
|
};
|
2015-06-10 17:24:07 +02:00
|
|
|
|
2015-07-06 11:14:42 +02:00
|
|
|
/**
|
|
|
|
* Get the ith element of the list
|
|
|
|
* Complexity O(ith)
|
|
|
|
* @param ith {Number} index of the element to get
|
|
|
|
* @returns {Object} the ith element if it exists, null otherwise
|
|
|
|
*/
|
|
|
|
List.prototype.at = function(ith) {
|
2015-06-10 17:24:07 +02:00
|
|
|
if (ith < 0 || ith >= this.size()) {
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
|
|
|
var chain = this._begin;
|
|
|
|
for (var i = 0; i < ith; i++) {
|
|
|
|
chain = chain.next;
|
|
|
|
}
|
|
|
|
|
|
|
|
return chain.data;
|
2015-07-01 10:14:15 +02:00
|
|
|
};
|
2015-06-10 17:24:07 +02:00
|
|
|
|
2015-07-06 11:14:42 +02:00
|
|
|
/**
|
|
|
|
* Empty the list
|
|
|
|
*/
|
|
|
|
List.prototype.clear = function() {
|
2015-06-10 17:24:07 +02:00
|
|
|
this._begin = null;
|
|
|
|
this._end = null;
|
|
|
|
this._size = 0;
|
2015-07-01 10:14:15 +02:00
|
|
|
};
|
2015-06-10 17:24:07 +02:00
|
|
|
|
2015-07-06 11:14:42 +02:00
|
|
|
/**
|
|
|
|
* Insert an element at the right place in a sorted list
|
|
|
|
* Precondition : the list must be sorted
|
|
|
|
* Complexity : O(i) where i is the number of elements lower than elt
|
|
|
|
* @param elt {Object} element to add
|
|
|
|
* @param comparator {function} classic js comparator
|
|
|
|
*/
|
|
|
|
List.prototype.insertSorted = function(elt, comparator) {
|
2015-07-01 10:14:15 +02:00
|
|
|
var newElement;
|
|
|
|
|
2015-06-10 17:24:07 +02:00
|
|
|
if (comparator === undefined) {
|
|
|
|
comparator = priv.defaultComparator;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (this._begin === null) {
|
|
|
|
// Inserted in front (empty list)
|
|
|
|
this.push(elt);
|
|
|
|
} else if (comparator(this._begin.data, elt) > 0) {
|
|
|
|
// Inserted in front (smallest element)
|
2015-07-01 10:14:15 +02:00
|
|
|
newElement = {prev: null, next: this._begin, data: elt};
|
2015-06-10 17:24:07 +02:00
|
|
|
|
|
|
|
this._begin.prev = newElement;
|
|
|
|
|
|
|
|
this._begin = newElement;
|
|
|
|
this._size ++;
|
|
|
|
|
|
|
|
} else if (comparator(this._end.data, elt) < 0) {
|
|
|
|
// Inserted in end
|
|
|
|
this.push(elt);
|
|
|
|
} else {
|
|
|
|
// Inserted in the middle
|
|
|
|
var chain = this._begin;
|
|
|
|
|
|
|
|
while (chain.next !== null) {
|
|
|
|
// If chain < elt < chain.next
|
|
|
|
if (comparator(chain.next.data, elt) > 0) {
|
2015-07-01 10:14:15 +02:00
|
|
|
newElement = {data: elt, next: chain.next, prev: chain};
|
2015-06-10 17:24:07 +02:00
|
|
|
if (chain.next) {
|
|
|
|
chain.next.prev = newElement;
|
|
|
|
}
|
|
|
|
chain.next = newElement;
|
|
|
|
this._size ++;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Next step
|
|
|
|
chain = chain.next;
|
|
|
|
}
|
|
|
|
}
|
2015-07-01 10:14:15 +02:00
|
|
|
};
|
2015-06-10 17:24:07 +02:00
|
|
|
|
2015-07-06 11:14:42 +02:00
|
|
|
/**
|
|
|
|
* Checks if a list is sorted
|
|
|
|
* Complexity : O(size()) if the list is sorted, O(i) where i is the first non-sorted element in the list
|
|
|
|
* @param comparator {function} classic js comparator
|
|
|
|
* @returns {Boolean} true if the list is sorted, false otherwise
|
|
|
|
*/
|
|
|
|
List.prototype.isSorted = function(comparator) {
|
2015-06-10 17:24:07 +02:00
|
|
|
var chain = this._begin;
|
|
|
|
|
|
|
|
if (comparator === undefined) {
|
|
|
|
comparator = priv.defaultComparator;
|
|
|
|
}
|
|
|
|
|
|
|
|
while (chain.next !== null) {
|
|
|
|
if (comparator(chain.data, chain.next.data) > 0) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
chain = chain.next;
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
2015-07-01 10:14:15 +02:00
|
|
|
};
|
2015-06-10 17:24:07 +02:00
|
|
|
|
2015-07-06 11:14:42 +02:00
|
|
|
/**
|
|
|
|
* Returns an iterator to the begin of the list
|
|
|
|
* @returns {Iterator} an interator to the first element
|
|
|
|
*/
|
|
|
|
List.prototype.begin = function() {
|
|
|
|
return new Iterator(this._begin, 0);
|
2015-07-01 10:14:15 +02:00
|
|
|
};
|
2015-06-10 17:24:07 +02:00
|
|
|
|
2015-07-06 11:14:42 +02:00
|
|
|
/**
|
|
|
|
* Returns an iterator to the end of the list
|
|
|
|
* @returns {Iterator} an interator to the first element
|
|
|
|
*/
|
|
|
|
List.prototype.end = function() {
|
|
|
|
return new Iterator(this._end, this.size() - 1);
|
2015-07-01 10:14:15 +02:00
|
|
|
};
|
2015-06-10 17:24:07 +02:00
|
|
|
|
2015-07-06 11:14:42 +02:00
|
|
|
/**
|
|
|
|
* @constructor
|
|
|
|
* @description Reprensents an iterator to an element of a list
|
|
|
|
* @param chain {Object} chain element of a list
|
|
|
|
* @param counter {Number} index of the current element
|
|
|
|
* @memberof L3D.utils
|
|
|
|
*/
|
|
|
|
var Iterator = function(chain, counter) {
|
2015-06-10 17:24:07 +02:00
|
|
|
this._chain = chain;
|
|
|
|
this._counter = counter;
|
2015-07-01 10:14:15 +02:00
|
|
|
};
|
2015-06-10 17:24:07 +02:00
|
|
|
|
2015-07-06 11:14:42 +02:00
|
|
|
/**
|
|
|
|
* Go to the next element
|
|
|
|
* @method
|
|
|
|
*/
|
|
|
|
Iterator.prototype.next = function() {
|
2015-06-10 17:24:07 +02:00
|
|
|
this._chain = this._chain.next;
|
|
|
|
this._counter ++;
|
2015-07-01 10:14:15 +02:00
|
|
|
};
|
2015-06-10 17:24:07 +02:00
|
|
|
|
2015-07-06 11:14:42 +02:00
|
|
|
/**
|
|
|
|
* Go to the previous element
|
|
|
|
* @method
|
|
|
|
*/
|
|
|
|
Iterator.prototype.prev = function() {
|
2015-06-10 17:24:07 +02:00
|
|
|
this._chain = this._chain.prev;
|
|
|
|
this._counter --;
|
2015-07-01 10:14:15 +02:00
|
|
|
};
|
2015-06-10 17:24:07 +02:00
|
|
|
|
2015-07-06 11:14:42 +02:00
|
|
|
/**
|
|
|
|
* Returns the current element
|
|
|
|
* @method
|
|
|
|
* @returns {Object} current element
|
|
|
|
*/
|
|
|
|
Iterator.prototype.get = function() {
|
2015-06-10 17:24:07 +02:00
|
|
|
return this._chain.data;
|
2015-07-01 10:14:15 +02:00
|
|
|
};
|
2015-06-10 17:24:07 +02:00
|
|
|
|
2015-07-06 11:14:42 +02:00
|
|
|
/**
|
|
|
|
* Checks if there is a element after the current element
|
|
|
|
* @method
|
|
|
|
* @returns {Boolean} true if the element exists, false otherwise
|
|
|
|
*/
|
|
|
|
Iterator.prototype.hasNext = function() {
|
2015-06-10 17:24:07 +02:00
|
|
|
return this._chain.next !== null;
|
2015-07-01 10:14:15 +02:00
|
|
|
};
|
2015-06-10 17:24:07 +02:00
|
|
|
|
2015-07-06 11:14:42 +02:00
|
|
|
/**
|
|
|
|
* Checks if there is a element before the current element
|
|
|
|
* @method
|
|
|
|
* @returns {Boolean} true if the element exists, false otherwise
|
|
|
|
*/
|
|
|
|
Iterator.prototype.hasPrev = function() {
|
2015-06-10 17:24:07 +02:00
|
|
|
return this._chain.prev !== null;
|
2015-07-01 10:14:15 +02:00
|
|
|
};
|
2015-06-10 17:24:07 +02:00
|
|
|
|
2015-07-06 11:14:42 +02:00
|
|
|
/**
|
|
|
|
* Compares two iterators of the same list
|
|
|
|
* @param it2 {Iterator} second iterator of the comparison
|
|
|
|
* @returns {Boolean} result of this < it2
|
|
|
|
*/
|
|
|
|
Iterator.prototype.lowerThan = function(it2) {
|
|
|
|
return distance(this, it2) > 0;
|
2015-07-01 10:14:15 +02:00
|
|
|
};
|
2015-06-10 17:24:07 +02:00
|
|
|
|
2015-07-06 11:14:42 +02:00
|
|
|
/**
|
|
|
|
* Compares two iterators of the same list
|
|
|
|
* @method
|
|
|
|
* @param it2 {Iterator} second iterator of the comparison
|
|
|
|
* @returns {Boolean} result of this > it2
|
|
|
|
*/
|
|
|
|
Iterator.prototype.greaterThan = function(it2) {
|
|
|
|
return distance(this, it2) < 0;
|
2015-07-01 10:14:15 +02:00
|
|
|
};
|
2015-06-10 17:24:07 +02:00
|
|
|
|
2015-07-06 11:14:42 +02:00
|
|
|
/**
|
|
|
|
* Compute the distance between two iterators
|
|
|
|
* @method
|
|
|
|
* @private
|
|
|
|
* @param it1 {Iterator} first iterator of the computation
|
|
|
|
* @param it2 {Iterator} second iterator of the computation
|
|
|
|
* @returns {Number} distance between it1 and it2
|
|
|
|
*/
|
|
|
|
var distance = function(it1, it2) {
|
2015-06-10 17:24:07 +02:00
|
|
|
return it2._counter - it1._counter;
|
2015-07-01 10:14:15 +02:00
|
|
|
};
|
2015-06-10 17:24:07 +02:00
|
|
|
|
|
|
|
priv = {};
|
|
|
|
|
|
|
|
priv.defaultComparator = function(a,b) {
|
|
|
|
if (a < b)
|
|
|
|
return -1;
|
|
|
|
if (a > b)
|
|
|
|
return 1;
|
|
|
|
return 0;
|
2015-07-01 10:14:15 +02:00
|
|
|
};
|
2015-06-10 17:24:07 +02:00
|
|
|
|
2015-07-06 11:14:42 +02:00
|
|
|
utils.List = List;
|
|
|
|
utils.Iterator = Iterator;
|
|
|
|
|
|
|
|
return utils;
|
|
|
|
|
2015-06-10 17:24:07 +02:00
|
|
|
})();
|