speedy-vision
Version:
GPU-accelerated Computer Vision for JavaScript
143 lines (125 loc) • 4.18 kB
JavaScript
/*
* speedy-vision.js
* GPU-accelerated Computer Vision for JavaScript
* Copyright 2020-2022 Alexandre Martins <alemartf(at)gmail.com>
*
* 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.
*
* sorting-networks.js
* Sorting Networks
*/
import { AbstractMethodError } from './errors';
/**
* @typedef {Array<[number,number]>} Sortnet
*/
/**
* An abstract Sorting Network
* @abstract
*/
class SortingNetwork
{
/**
* Generate a sequence of comparators for a
* sorting network supporting n data points
* @abstract
* @param {number} n number of data points
* @returns {Sortnet}
*/
static generate(n)
{
throw new AbstractMethodError();
}
/**
* Sort the given data points using this network
* @template T
* @param {T[]} data data points
* @param {function(T, T): number} [cmp] comparator function, as in Array.prototype.sort()
* @returns {T[]} sorted data
*/
static sort(data, cmp = ((a, b) => (+a) - (+b)))
{
const network = this.generate(data.length);
for(const [a, b] of network) {
if(cmp(data[a], data[b]) > 0)
[ data[a], data[b] ] = [ data[b], data[a] ];
}
return data;
}
}
/**
* An implementation of Batcher's Odd-Even Mergesort
*/
export class OddEvenMergesort extends SortingNetwork
{
/*
A reference for this algorithm can be found at:
https://www.inf.hs-flensburg.de/lang/algorithmen/sortieren/networks/oemen.htm
The algorithm will work if the size of the input array is a power of 2. In
order to extend the algorithm so that it works with arrays of any size - say
it's n - we use a very simple idea: extend the input array so that its size
becomes a power of 2. Set the new entries to infinity. Sort the extended
array and return its first n elements.
Any comparator [i,j] where j >= n is comparing some value with infinity,
meaning that no exchange will need to take place. Therefore, [i,j] can be
dropped from the network.
*/
/**
* Generate a sequence of comparators for a
* sorting network supporting n data points
* @param {number} n number of data points
* @returns {Sortnet}
*/
static generate(n)
{
const nextPot = 1 << Math.ceil(Math.log2(Math.max(n, 1)));
return this._mergesort(n, [], 0, nextPot);
}
/**
* Odd-Even Mergesort
* @param {number} count number of data points
* @param {Sortnet} net sorting network
* @param {number} lo starting index
* @param {number} n sequence length, a power of 2
* @returns {Sortnet} net
*/
static _mergesort(count, net, lo, n)
{
if(n > 1) {
const m = n / 2;
this._mergesort(count, net, lo, m);
this._mergesort(count, net, lo + m, m);
this._merge(count, net, lo, n, 1);
}
return net;
}
/**
* Odd-Even Merge
* @param {number} count number of data points
* @param {Sortnet} net sorting network
* @param {number} lo starting index
* @param {number} n a power of 2
* @param {number} jmp a power of 2
*/
static _merge(count, net, lo, n, jmp)
{
const dbljmp = jmp * 2;
if(dbljmp < n) {
this._merge(count, net, lo, n, dbljmp); // merge even subsequence
this._merge(count, net, lo + jmp, n, dbljmp); // merge odd subsequence
for(let i = lo + jmp; i + jmp < lo + n && i + jmp < count; i += dbljmp)
net.push([i, i + jmp]);
}
else if(lo + jmp < count)
net.push([lo, lo + jmp]);
}
}