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