From 8e0cf9cfa86b16f2d57d5ec7d9fa367a17c299d4 Mon Sep 17 00:00:00 2001 From: James Gill Date: Sat, 20 Apr 2013 09:39:45 -0700 Subject: [PATCH] Replacing the insert method in sorting. It has been replaced with a binary search, instead of an insertion-sort O(n^2) search. --- packages/minimongo/minimongo.js | 39 +++++++++++++---- packages/minimongo/minimongo_tests.js | 62 +++++++++++++++++++++++++++ 2 files changed, 92 insertions(+), 9 deletions(-) diff --git a/packages/minimongo/minimongo.js b/packages/minimongo/minimongo.js index b8c45951fec..b72370bd56d 100644 --- a/packages/minimongo/minimongo.js +++ b/packages/minimongo/minimongo.js @@ -694,21 +694,42 @@ LocalCollection._findInOrderedResults = function (query, doc) { throw Error("object missing from query"); }; +//This binary search puts a value between any equal values, and the first +//lesser value. This is to match the previous insertion behaviour of comparing +//each element and inserting it as soon as it compares less than the +//encountered value. +LocalCollection._binarySearch = function (cmp, array, value) { + if (array.length == 0) return 0; + lower = 0; + upper = array.length - 1; + + while (lower <= upper) { + idx = Math.floor( (lower + upper) / 2 ); + comparison = cmp(value, array[idx]); + //console.log("lower:"+lower + " upper:" + upper + " idx:"+idx +" c:"+comparison); + if (lower == upper) { + if (comparison >= 0) idx++; + return idx; + } + + if ( comparison < 0 ) + upper = idx; + else + lower = idx + 1; + } + + return idx; +} + LocalCollection._insertInSortedList = function (cmp, array, value) { if (array.length === 0) { array.push(value); return 0; } - for (var i = 0; i < array.length; i++) { - if (cmp(value, array[i]) < 0) { - array.splice(i, 0, value); - return i; - } - } - - array.push(value); - return array.length - 1; + idx = LocalCollection._binarySearch (cmp, array, value) + array.splice(idx, 0, value); + return idx; }; // To track what documents are affected by a piece of code, call saveOriginals() diff --git a/packages/minimongo/minimongo_tests.js b/packages/minimongo/minimongo_tests.js index debc0f8f730..3feaf0477f2 100644 --- a/packages/minimongo/minimongo_tests.js +++ b/packages/minimongo/minimongo_tests.js @@ -1048,6 +1048,68 @@ Tinytest.add("minimongo - array sort", function (test) { _.range(c.find().count())); }); +Tinytest.add("minimongo - binary search", function (test) { + var forward_cmp = function (a, b) { + if (a < b) + return -1; + else if (b < a) + return 1; + else + return 0; + }; + + var backward_cmp = function (a, b) { + return -1 * forward_cmp(a, b); + }; + + var check_search = function (cmp, array, value, expected, message) { + actual = LocalCollection._binarySearch(cmp, array, value); + if (expected != actual) { + test.fail({type: "minimongo-binary-search", + message: message + " : Expected index " + expected + + " but had " + actual + }); + } + }; + + var check_search_forward = function (array, value, expected, message) { + check_search(forward_cmp, array, value, expected, message); + } + var check_search_backward = function (array, value, expected, message) { + check_search(backward_cmp, array, value, expected, message); + } + + check_search_forward([1, 2, 5, 7], 4, 2, "Inner insert"); + check_search_forward([1, 2, 3, 4], 3, 3, "Inner insert, equal value"); + check_search_forward([1, 2, 5], 4, 2, "Inner insert, odd length"); + check_search_forward([1, 3, 5, 6], 9, 4, "End insert"); + check_search_forward([1, 3, 5, 6], 0, 0, "Beginning insert"); + check_search_forward([1], 0, 0, "Single array, less than."); + check_search_forward([1], 1, 1, "Single array, equal."); + check_search_forward([1], 2, 1, "Single array, greater than."); + check_search_forward([], 1, 0, "Empty array"); + check_search_forward([1, 1, 1, 2, 2, 2, 2], 1, 3, "Highly degenerate array, lower"); + check_search_forward([1, 1, 1, 2, 2, 2, 2], 2, 7, "Highly degenerate array, upper"); + check_search_forward([2, 2, 2, 2, 2, 2, 2], 1, 0, "Highly degenerate array, lower"); + check_search_forward([2, 2, 2, 2, 2, 2, 2], 2, 7, "Highly degenerate array, equal"); + check_search_forward([2, 2, 2, 2, 2, 2, 2], 3, 7, "Highly degenerate array, upper"); + + check_search_backward([7, 5, 2, 1], 4, 2, "Backward: Inner insert"); + check_search_backward([4, 3, 2, 1], 3, 2, "Backward: Inner insert, equal value"); + check_search_backward([5, 2, 1], 4, 1, "Backward: Inner insert, odd length"); + check_search_backward([6, 5, 3, 1], 9, 0, "Backward: Beginning insert"); + check_search_backward([6, 5, 3, 1], 0, 4, "Backward: End insert"); + check_search_backward([1], 0, 1, "Backward: Single array, less than."); + check_search_backward([1], 1, 1, "Backward: Single array, equal."); + check_search_backward([1], 2, 0, "Backward: Single array, greater than."); + check_search_backward([], 1, 0, "Backward: Empty array"); + check_search_backward([2, 2, 2, 2, 1, 1, 1], 1, 7, "Backward: Degenerate array, lower"); + check_search_backward([2, 2, 2, 2, 1, 1, 1], 2, 4, "Backward: Degenerate array, upper"); + check_search_backward([2, 2, 2, 2, 2, 2, 2], 1, 7, "Backward: Highly degenerate array, upper"); + check_search_backward([2, 2, 2, 2, 2, 2, 2], 2, 7, "Backward: Highly degenerate array, upper"); + check_search_backward([2, 2, 2, 2, 2, 2, 2], 3, 0, "Backward: Highly degenerate array, upper"); + +}); Tinytest.add("minimongo - modify", function (test) { var modify = function (doc, mod, result) {