func (bf *boundaries) findBoundary(missingSecond int64) (bd boundary) {
	idxOrig, vOrig := bf.tr.Closest(float64(missingSecond))
	valOrig := int64(vOrig)
	if valOrig == missingSecond {
		bd.lower = valOrig
		bd.lowerIdx = idxOrig
		bd.upper = valOrig
		bd.upperIdx = idxOrig
		return
	}
	idxx := bf.num2SortedIdx[valOrig]
	if missingSecond > valOrig {
		bd.lower = valOrig
		bd.lowerIdx = idxOrig
		for j := idxx + 1; j < len(bf.numsSorted); j++ {
			if bf.numsSorted[j] > missingSecond {
				bd.upper = bf.numsSorted[j]
				bd.upperIdx = bf.num2OrigIdx[bf.numsSorted[j]]
				return
			}
		}
		bd.upper = 0
		bd.upperIdx = -1
		return
	}
	bd.upper = valOrig
	bd.upperIdx = idxOrig
	for j := idxx - 1; j >= 0; j-- {
		if bf.numsSorted[j] < missingSecond {
			bd.lower = bf.numsSorted[j]
			bd.lowerIdx = bf.num2OrigIdx[bf.numsSorted[j]]
			return
		}
	}
	bd.lower = 0
	bd.lowerIdx = -1
	return
}
returns the boundary with closest upper, lower value.
returns the index of the value if found.
package inspect
import (
	"math"
	"sort"
)
func BinarySearchInt64(nums []int64, v int64) int {
	lo := 0
	hi := len(nums) - 1
	for lo <= hi {
		mid := lo + (hi-lo)/2
		if nums[mid] < v {
			lo = mid + 1  
			continue
		}
		if nums[mid] > v {
			hi = mid - 1  
			continue
		}
		return mid
	}
	return -1
}
type Tree interface {
	Closest(v float64) (index int, value float64)
}
func NewBinaryTree(nums []float64) Tree {
	if len(nums) == 0 {
		return nil
	}
	root := newFloat64Node(0, nums[0])
	for i := range nums {
		if i == 0 {
			continue
		}
		insert(root, i, nums[i])
	}
	return root
}
func NewBinaryTreeInt64(nums []int64) Tree {
	fs := make([]float64, len(nums))
	for i := range nums {
		fs[i] = float64(nums[i])
	}
	return NewBinaryTree(fs)
}
func (root *float64Node) Closest(v float64) (index int, value float64) {
	nd := searchClosest(root, v)
	return nd.Idx, nd.Value
}
type float64Node struct {
	Idx   int
	Value float64
	Left  *float64Node
	Right *float64Node
}
func newFloat64Node(idx int, v float64) *float64Node {
	return &float64Node{Idx: idx, Value: v}
}
func insert(root *float64Node, idx int, v float64) *float64Node {
	if root == nil {
		return newFloat64Node(idx, v)
	}
	if root.Value > v {
		root.Left = insert(root.Left, idx, v)
	} else {
		root.Right = insert(root.Right, idx, v)
	}
	return root
}
func search(root *float64Node, v float64) *float64Node {
	if root == nil {
		return nil
	}
	if root.Value == v {
		return root
	}
	if root.Value > v {
		return search(root.Left, v)
	}
	return search(root.Right, v)
}
func searchClosest(root *float64Node, v float64) *float64Node {
	if root == nil {
		return nil
	}
	var child *float64Node
	if root.Value > v {
		child = searchClosest(root.Left, v)
	} else {
		child = searchClosest(root.Right, v)
	}
	if child == nil {
		return root
	}
	rootDiff := math.Abs(float64(root.Value - v))
	childDiff := math.Abs(float64(child.Value - v))
	if rootDiff < childDiff {
		return root
	}
	return child
}
type boundary struct {
	lower    int64
	lowerIdx int
	upper    int64
	upperIdx int
}
type boundaries struct {
	numsOrig    []int64
	num2OrigIdx map[int64]int
	numsSorted    []int64
	num2SortedIdx map[int64]int
	tr Tree
}
func buildBoundaries(nums []int64) *boundaries {
	num2OrigIdx := make(map[int64]int)
	for i := range nums {
		num2OrigIdx[nums[i]] = i
	}
	numsOrig := make([]int64, len(nums))
	copy(numsOrig, nums)
	tr := NewBinaryTreeInt64(nums)
	sort.Sort(int64Slice(nums))
	num2SortedIdx := make(map[int64]int)
	for i := range nums {
		num2SortedIdx[nums[i]] = i
	}
	return &boundaries{
		numsOrig:      numsOrig,
		num2OrigIdx:   num2OrigIdx,
		numsSorted:    nums,
		num2SortedIdx: num2SortedIdx,
		tr:            tr,
	}
}
func (bf *boundaries) add(sec int64) {
	bf.numsOrig = append(bf.numsOrig, sec)
	bf.num2OrigIdx[sec] = len(bf.numsOrig)
	bf.numsSorted = append(bf.numsSorted, sec)
	bf.tr = NewBinaryTreeInt64(bf.numsSorted)
	sort.Sort(int64Slice(bf.numsSorted))
	num2SortedIdx := make(map[int64]int)
	for i := range bf.numsSorted {
		num2SortedIdx[bf.numsSorted[i]] = i
	}
	bf.num2SortedIdx = num2SortedIdx
}