all files / src/ frontiers.js

92.11% Statements 35/38
60% Branches 12/20
100% Functions 4/4
96.55% Lines 28/29
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91                  45×             27×   36×                                                       18×     18×   16×   18× 18×         18×                        
/** ******************************************************************************************************************
 * @file Calculates dominanace frontiers from a dominator tree.
 * @author Julian Jensen <jjdanois@gmail.com>
 * @since 1.0.0
 * @date 11-Dec-2017
 *********************************************************************************************************************/
"use strict";
 
const
    { isArray: array } = Array,
    consistent = list => list.map( l => array( l ) ? l : typeof l === 'number' ? [ l ] : [] );
 
/**
 * @param {Array<Array<number>>} succs
 */
function reverse_flow( succs )
{
    if ( !array( succs ) )
        throw new TypeError( `The list of successor lists must be an array` );
 
    const preds = succs.map( () => [] );
 
    consistent( succs ).forEach( ( _succs, i ) => _succs.forEach( s => preds[ s ].push( i ) ) );
 
    return preds;
}
 
/**
 * Find dominance frontiers
 *
 * @param {Array<Array<number>>} vertices
 * @param {Array<?number>} idoms
 */
function check( vertices, idoms )
{
    if ( !array( idoms ) || !array( vertices ) )
        throw new TypeError( `Both predecessors and immediate dominators must be arrays` );
 
    Iif ( vertices.length !== idoms.length )
        throw new Error( `Both predecessors and immediate dominators must be arrays of equal length` );
 
    Iif ( vertices.length < 2 ) return vertices.length ? [ [] ] : [];
}
 
/**
 * Find dominance frontiers
 *
 * @param {Array<Array<number>>} preds
 * @param {Array<?number>} idoms
 */
function frontiers_from_preds( preds, idoms )
{
    const
        frontiers = [],
        trivial = check( preds, idoms );
 
    Iif ( trivial ) return trivial;
 
    preds = consistent( preds );
 
    preds.forEach( ( p, i ) => frontiers.push( new Set() ) );
 
    preds.forEach( ( edges, b ) => {
 
        if ( edges.length < 2 ) return;
 
        edges.forEach( runner => {
            while ( runner !== idoms[ b ] )
            {
                frontiers[ runner ].add( b );
                runner = idoms[ runner ];
            }
        } );
    } );
 
    return frontiers.map( df => [ ...df ] );
}
 
/**
 * Find dominance frontiers
 *
 * @param {Array<Array<number>>} succs
 * @param {Array<?number>} idoms
 */
function frontiers_from_succs( succs, idoms )
{
    return frontiers_from_preds( reverse_flow( succs ), idoms );
}
 
module.exports = { frontiers_from_preds, frontiers_from_succs, reverse_flow };