/* CopyrightTag: Copyright (c) 2004-2008 Satmetrix Systems, Inc. All Rights Reserved. */
/* $Id: arrayOps.js,v 1.5 2008/01/08 23:54:14 build Exp $ */
/**
 * Implements various array operations.
 * Where possible, these effect changes directly in the array.
 * Otherwise, a new array is created that reflects the desired manipulations.
 * In all cases the result array (the altered original or the new result array) is returned
 * as the value of the operation.
 * Clone the array first if you do not want to change the original.
 */

/**
 * Return a shallow copy of the input array.
 */
function arrayClone(array) {
    if (!array) {
        return null
    }
    var i = array.length
    var a2 = new Array(i)
    while(--i >= 0) {
        a2[i] = array[i]
    }
    return a2
}

/**
 * Return true iff the input arrays are "equal",
 * i.e., have equal elements, using "==".
 */
function arrayEquals(a1, a2) {
    if (a1 == a2) {
        return true
    } else if (a1 && a2 && a1.length == a2.length) {
        var i = a1.length
        while (--i >= 0 && a1[i] == a2[i]) {
        }
        return i < 0
    }
    return false
}

/**
 *  Return true iff the input arrays have the same elements -
 *  The elements must be the same, but can be in a different order.
 */
function arraySameElements(a1, a2) {
    if ( a1 == a2 ) {
        return true
    }
    else if ( a1 && a2 && a1.length == a2.length ) {
        for ( var i = 0; i < a1.length; i++ ) {
            if ( arrayIndex(a2, a1[i]) == -1 )
                return false
        }
        return true
    }
    else
        return false
}

/**
 * Return the position of the given element, or -1 if not present.
 */
function arrayIndex(array, element) {
    var i = array ? array.length : 0
    while (--i >= 0 && array[i] != element) {
    }
    return i
}

/**
 * Return a copy of the input array, without the element at 'index'. 
 * Does nothing if 'index' < 0 or >= array length
 */
function arrayRemove(array, index) {
    if (array && index >= 0 && index < array.length) {
        var len = array.length
        var a2 = new Array()
        for (var i = 0; i < len; ++i) {
            if (i != index) {
                a2[a2.length] = array[i]
            }
        }
        array = a2
    }
    return array
}

/**
 * Return a copy of the input array, but without the element with the given value. 
 * Does nothing if 'value' not an element of the array.
 */
function arrayRemoveElement(array, value) {
    return arrayRemove(array, arrayIndex(array, value))
}

/**
 * Insert 'value' to array at position 'index'.
 * Elements originally at 'index' and beyond are shifted.
 * Does nothing if 'index' < 0 or > array length
 * 'index' = 0 adds to array beginning.
 * 'index' = array.length adds to array end.
 * Returns the modified array.
 */
function arrayInsert(array, value, index) {
    if (array && index >= 0 && index <= array.length) {
        for (var i = array.length; --i >= index;) {
            array[i+1] = array[i]
        }
        array[index] = value
    }
    return array
}

/**
 * Copy elements from a source array to a destination array (they may be the same).
 * 'src'    Source array
 * 'srcx'   Starting index in 'src' for the copy region.
 * 'dst'    Destination array.
 * 'dstx'   Starting index in 'dst' for the copy region.
 * 'len'    Length of copy region.
 * Returns the modified destination array.
 */
function arrayCopy(src, srcx, dst, dstx, len) {
    if (src && dst && src.length >= 0 && dst.length >= 0 && !(src == dst && srcx == dstx)) {
        len = Math.min(len, src.length - srcx)
        if (len > 0) {
            // Order the copy in case 'src' == 'dst'
            if (dstx >= srcx) {
                while (--len >= 0) {
                    dst[dstx + len] = src[srcx + len]
                }
            } else {
                for (var i = 0; i < len; ++i) {
                    dst[dstx + i] = src[srcx + i]
                }
            }
        }
    }
    return dst
}

/**
 * Move a block of elements from one position to another, shifting the intervening elements.
 * 'array'      Array containing the block of elements.
 * 'from'       Initial index of block beginning.
 * 'to'         Destination index of block beginning.
 * 'len'        Length of block.
 * Returns the modified array.
 */
function arrayShiftBlock(ar, from, to, len) {
    if (len == 1) {
        return arrayShift(ar, from, to)
    }
    if (ar && from >= 0 && to >= 0 && len > 0 && from + len <= ar.length && to + len <= ar.length) {
        var temp = new Array()
        arrayCopy(ar, from, temp, 0, len)
        if (from < to) {
            arrayCopy(ar, from + len, ar, from, to - from)
        } else {
            arrayCopy(ar, to, ar, to + len, from - to)
        }
        arrayCopy(temp, 0, ar, to, len)
    }
    return ar
}

/**
 * Move an element from one position to another, shifting the intervening elements.
 * 'array'      Array whose element is to be moved.
 * 'from'       Initial index of element to be moved.
 * 'to'         Destination index of element to be moved.
 * Returns the modified array.
 */
function arrayShift(array, from, to) {
    if (array && from >= 0 && to >= 0 && from < array.length && to < array.length) {
        var item = array[from]
        if (from < to) {
            while (from < to) {
                array[from] = array[from + 1]
                ++from
            }
        } else {
            while (from > to) {
                array[from] = array[from -1]
                --from
            }
        }
        array[to] = item
    }
    return array
}

// Return a string representation of an array,
// E.g. an array of the first 3 integers would produce: "[1, 2, 3]"
function arrayToString(array) {
    return "[" + arrayToBareString(array) + "]"
}

// Return a string representation of an array,
// E.g. an array of the first 3 integers would produce: "[1, 2, 3]"
function arrayToBareString(array) {
    var str = ""
    if (array) {
        var len = array.length
        for (var i = 0; i < len; ++i) {
            if (i > 0) {
                str += ", "
            }
            str += array[i]
        }
    }
    return str
}

/**
 * Alas!  IE5.0 does not support Array.push(), thus:
 */
function arrayPush(array, obj) {
    if (array) {
        array[array.length] = obj
    } else {
        array = new Array(obj)
    }
    return array
}

/**
 * Push 'obj' on 'array' if not already present.
 */
function arrayPushOnce(array, obj) {
    if (array) {
        if (arrayIndex(array, obj) < 0) {
            array[array.length] = obj
        }
    } else {
        array = new Array(obj)
    }
    return array
}

/**
 * Alas!  IE5.0 does not support Array.pop(), thus:
 */
function arrayPop(array) {
    if (array && array.length > 0) {
        array = arrayRemove(array, array.length-1)
    }
    return array
}

function arrayContains(array, elem) {
    return arrayIndex(array, elem) != -1;
}


/**
 * Replacement for new Array( 2 ) but we want [2], not [,].
 * Good for when have something like type, which is a number, but want
 * it say as additionalCreationArgs to INTERN_VARIABLE.
 * In other words, if the argument element is a number, that is NOT
 * the length of the new array - it is the first and only ELEMENT.
 */
function newArrayOfElt( element ){
    var valArray = new Array(1); // The 1 here IS the length! :)
    valArray[ 0 ] = element;
    return valArray;
}


/** testing */
/*
function assertTrue(test) {
    if (!eval(test)) {
        trace("Failed: " + test)
    }
}

function arrayTest() {
    var ar = new Array(0, 1, 2, 3, 4, 5, 6, 7, 8, 9)
    assertTrue("arrayEquals(arrayCopy(new Array(" + arrayToBareString(ar) + "), 2, new Array(" + arrayToBareString(ar) + "), 4, 3), new Array(0, 1, 2, 3, 2, 3, 4, 7, 8, 9))")
    assertTrue("arrayEquals(arrayCopy(new Array(" + arrayToBareString(ar) + "), 4, new Array(" + arrayToBareString(ar) + "), 2, 3), new Array(0, 1, 4, 5, 6, 5, 6, 7, 8, 9))")
    assertTrue("arrayEquals(arrayShiftBlock(new Array(" + arrayToBareString(ar) + "), 6, 2, 3), new Array(0, 1, 6, 7, 8, 2, 3, 4, 5, 9))")
    assertTrue("arrayEquals(arrayShiftBlock(new Array(" + arrayToBareString(ar) + "), 2, 6, 3), new Array(0, 1, 5, 6, 7, 8, 2, 3, 4, 9))")
    assertTrue("arrayEquals(arrayShiftBlock(new Array(" + arrayToBareString(ar) + "), 4, 2, 5), new Array(0, 1, 4, 5, 6, 7, 8, 2, 3, 9))")
    assertTrue("arrayEquals(arrayShiftBlock(new Array(" + arrayToBareString(ar) + "), 2, 4, 5), new Array(0, 1, 7, 8, 2, 3, 4, 5, 6, 9))")
}

function arrayTestTrace() {
    var ar = new Array(0, 1, 2, 3, 4, 5, 6, 7, 8, 9)
    var ar1 = arrayClone(ar)
    trace("ar1 = " + arrayToString(ar1) + ": arrayCopy(ar1, 2, ar1, 4, 3)")
    trace("  ->  " + arrayToString(arrayCopy(ar1, 2, ar1, 4, 3)))
    ar1 = arrayClone(ar)
    trace("ar1 = " + arrayToString(ar1) + ": arrayCopy(ar1, 4, ar1, 2, 3)")
    trace("  ->  " + arrayToString(arrayCopy(ar1, 4, ar1, 2, 3)))
    trace("")
    ar1 = arrayClone(ar)
    trace("ar1 = " + arrayToString(ar1) + ": arrayShiftBlock(ar1, 6, 2, 3)")
    trace("  ->  " + arrayToString(arrayShiftBlock(ar1, 6, 2, 3)))
    ar1 = arrayClone(ar)
    trace("ar1 = " + arrayToString(ar1) + ": arrayShiftBlock(ar1, 2, 6, 3)")
    trace("  ->  " + arrayToString(arrayShiftBlock(ar1, 2, 6, 3)))
    trace("")
    ar1 = arrayClone(ar)
    trace("ar1 = " + arrayToString(ar1) + ": arrayShiftBlock(ar1, 4, 2, 5)")
    trace("  ->  " + arrayToString(arrayShiftBlock(ar1, 4, 2, 5)))
    ar1 = arrayClone(ar)
    trace("ar1 = " + arrayToString(ar1) + ": arrayShiftBlock(ar1, 2, 4, 5)")
    trace("  ->  " + arrayToString(arrayShiftBlock(ar1, 2, 4, 5)))
    trace("")
    trace("ar == arrayClone(ar) -> " + (ar == arrayClone(ar)))
    trace("arrayEquals(ar, arrayClone(ar)) -> " + arrayEquals(ar, arrayClone(ar)))
}

arrayTest()
*/
