# binarySearch.mjs

    import size from 'lodash-es/size.js'
    import filter from 'lodash-es/filter.js'
    import maxBy from 'lodash-es/maxBy.js'

    /**
     * 二元搜索法(Binary Search)，通過fGetValue(index)取值，求最靠近0但小於0的值，並回傳值與所在的index與所查得的數據items[index]
     *
     * @param {Array} items 輸入欲搜尋陣列
     * @param {Function} fGetValue 輸入取值函數，傳入index
     * @returns {Object} 回傳物件index，最靠近0但小於0的值、所在的index與所查得的數據items[index]
     */
    function binarySearch(items, fGetValue) {
        let n = size(items)
        let iStart = 0
        let vStart = fGetValue(iStart)
        let iEnd = n - 1
        let vEnd = fGetValue(iEnd)

        let run = true
        let index = null
        while (run) {
            let iMiddle = Math.floor((iEnd + iStart) / 2)
            let vMiddle = fGetValue(iMiddle)
            //console.log(`iStart=${iStart},  vStart=${vStart},  iMiddle=${iMiddle},  vMiddle=${vMiddle},  iEnd=${iEnd},  vEnd=${vEnd}`)

            //check
            if (vStart < 0 && vMiddle < 0 && vEnd < 0) {
                index = null
                run = false
                break
            }
            else if (vStart > 0 && vMiddle > 0 && vEnd > 0) {
                index = null
                run = false
                break
            }
            else if (vStart === 0) {
                index = iStart
                run = false
                break
            }
            else if (vMiddle === 0) {
                index = iMiddle
                run = false
                break
            }
            else if (vEnd === 0) {
                index = iEnd
                run = false
                break
            }
            else if (iMiddle === iStart || iMiddle === iEnd) {
                let rs = [{
                    i: iStart,
                    v: vStart,
                },
                {
                    i: iMiddle,
                    v: vMiddle,
                },
                {
                    i: iEnd,
                    v: vEnd,
                }]
                rs = filter(rs, (v) => {
                    return v.v <= 0
                })
                let r = maxBy(rs, 'v')
                index = r.i
                run = false
                break
            }

            //update
            if (vStart * vMiddle > 0) {
                iStart = iMiddle
                vStart = vMiddle
            }
            else {
                iEnd = iMiddle
                vEnd = vMiddle
            }

        }

        return index
    }

    export default binarySearch