/**
* Mergesort module
* @module Mergesort
*/
(function (global, factory) {
typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
typeof define === 'function' && define.amd ? define(factory) :
(global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.Mergesort = factory());
}(this, (function () { 'use strict';
const temp = [];
function sort(arr, l1, l2, f, o = 0){
let i = 0,
j = 0,
s1 = o,
e1 = o + l1 - 1,
s2 = e1 + 1,
e2 = s2 + l2 - 1,
state = "both not traversed";
if(f(arr[e1], arr[s2]) <= 0){
return;
} else if(f(arr[s1], arr[e2]) >= 0){
while(l2--){
temp[i++] = arr[s2++];
}
while(l1--){
temp[i++] = arr[s1++];
}
state = "skip";
}
outer:
while(state){
switch(state){
case "both not traversed":
if(f(arr[s1], arr[s2]) > 0) {
temp[i++] = arr[s2++];
} else {
temp[i++] = arr[s1++];
}
if (s1 > e1) {
state = "left traversed";
continue outer;
} else if (s2 > e2) {
state = "right traversed";
continue outer;
}
continue outer;
case "left traversed":
temp[i++] = arr[s2++];
if (s2 > e2){
break outer;
}
continue outer;
case "right traversed":
temp[i++] = arr[s1++];
if (s1 > e1){
break outer;
}
continue outer;
case "skip":
break outer;
}
}
while(i--){
arr[o++] = temp[j++];
}
}
function insertionSort(arr, l, f, o = 0){
let i = 0,
j,
u,
temp;
--l;
while(i < l){
j = i;
u = o + j;
temp = arr[u + 1];
while(~j && f(arr[u], temp) > 0){
arr[u + 1] = arr[u];
--j,--u;
}
arr[u + 1] = temp;
i++;
}
return arr;
}
function walk(arr, tree, f, hThreshold = 8, backtrack = false){
outer:
while(tree){
let leaf = tree.firstChild;
if(!leaf){
if(tree.p){
tree = tree.p;
continue;
}
tree = null;
continue;
}
while(leaf.firstChild){
leaf = leaf.firstChild;
}
if(leaf.nextSibling.firstChild){
tree = leaf.nextSibling;
continue;
}
if(leaf.l <= hThreshold){
insertionSort(arr, leaf.p.l, f, leaf.o);
} else {
sort(arr, leaf.l, leaf.nextSibling.l, f, leaf.o);
}
leaf = leaf.p;
backtrack && (leaf.lastChild = leaf.firstChild.nextSibling);
leaf.firstChild = null;
while(!leaf.nextSibling){
leaf = leaf.p || leaf;
if(!leaf.p){
tree = leaf;
continue outer;
}
}
tree = leaf.nextSibling;
}
}
function tree(l) {
let node = {
o: 0,
l: l,
p: null,
nextSibling: null,
prevSibling: null
},
state = "down";
outer:
while(state){
switch(state){
case "down":
if(node.l <= 1){
state = "right";
continue outer;
}
let nL = (node.l / 2) | 0;
node.firstChild = {
o: node.o,
l: nL,
p: node,
nextSibling: null,
prevSibling: null
};
node.firstChild.nextSibling
= node.lastChild
= {
o: node.o + nL,
l: node.l - nL,
p: node,
nextSibling: null,
prevSibling: node.firstChild
};
node = node.firstChild;
state = "down";
continue outer;
case "right":
if(!node.nextSibling){
state = "up";
continue outer;
}
node = node.nextSibling;
state = "down";
continue outer;
case "up":
if(!node.p){
return node;
}
node = node.p;
if(node.nextSibling){
state = "right";
continue outer;
}
state = "up";
continue outer;
}
}
}
function regen(tree){
outer:
while(tree){
let leaf = tree.lastChild;
if(!leaf){
if(tree.p){
tree = tree.p;
continue;
}
tree = null;
continue;
}
while(leaf.lastChild){
leaf = leaf.lastChild;
}
if(leaf.prevSibling.lastChild){
tree = leaf.prevSibling;
continue;
}
leaf = leaf.p;
leaf.firstChild = leaf.lastChild.prevSibling;
leaf.lastChild = null;
while(!leaf.prevSibling){
leaf = leaf.p || leaf;
if(!leaf.p){
tree = leaf;
continue outer;
}
}
tree = leaf.prevSibling;
}
}
/**
* An optinal configuration object that can be passed to the `Mergesort` factory function
* @typedef {Object} module:Mergesort~config
* @property {number} threshold The threshold below where the algorithm temporarily switches over to insertion sort
* @property {number} size Use this if you are going to consistently sort arrays of fixed size.
* Refers to size of the array to be sorted, where a binary tree is precalculated.
* The tree will be reused for each call of the returned `instance`.
* For each use, the tree is *walked* by setting `firstChild` properties of leaf nodes to null
* and then regenerated by setting them again from the `lastChild` property of their parent.
* This option results in a performance gain for large (> 1M) arrays, where cost of creating
* the tree is greater than the cost of walking + regenerating the tree. For small arrays,
* it has the reverse effect. It is set to off by default.
*/
const undef = void(0);
/**
* Returns a `Mergesort` `instance`
*
* ```javascript
*
* let instance = Mergesort(); //switches to insertion sort for array fragments < 16
*
* instance = Mergesort({threshold: 64}); //will switch to insertion sort for fragments < 64
*
* instance = Mergesort({size:999}); //will throw an error if you try to sort arrays with length other than 999
*
* ```
*
* @alias module:Mergesort
*
* @param {module:Mergesort~config} config a configuration object with optional parameters (default 16)
* @returns {module:Mergesort~instance}
*
*/
function index(
{
threshold = 16,
size = undef
}
= {
threshold: 16,
size: undef
}
){
if(size && typeof size !== "number"){
throw new Error("Size must be of type number");
}
const hThreshold = threshold / 2,
_tree = size ? tree(size) : undef,
/**
*
* ```javascript
* let instance = Mergesort(),
*
* inputArray = [{value:10},{value:1},{value:5}],
*
* compare = (a, b) => a.value - b.value;
*
* instance(inputArray, compare); //[{value:1}, ...]
*
* @namespace
* @param {Array} ArrayToBeSorted - Input array to be sorted
* @param {Function} Compare - compare function, same as in [here](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort)
* @returns {Array} input array
*/
instance = function(arr, f){
let _length = arr.length;
if(_tree){
if (_length !== _tree.l) {
throw new Error("Array length must be " + size);
}
temp.length = _length;
walk(arr, _tree, f, hThreshold ,true);
regen(_tree);
} else {
temp.length = _length;
walk(arr, tree(_length), f, hThreshold);
}
temp.length = 0;
return arr;
};
return instance;
}
index.version = "0.0.3";
return index;
})));