# Source code for refl1d.lib_numba.rebin

```import numba
import numba.experimental
import math

# Define a bin iterator to adapt to either forward or reversed inputs.
spec = [
('forward', numba.boolean),
('n', numba.int64),
('edges', numba.float64[:]),
('bin', numba.int64),
('lo', numba.float64),
('hi', numba.float64),
('atend', numba.boolean)
]

@numba.experimental.jitclass(spec)
class BinIter:
def __init__(self, n, edges):
self.n = n
self.edges = edges
self.forward = edges[0] < edges[n]

if (self.forward):
self.bin = 0
self.lo = edges[0]
self.hi = edges[1]
else:
self.bin = n - 1
self.lo = edges[n]
self.hi = edges[n-1]

self.atend = n < 1

def increment(self):
if self.atend:
raise IndexError("moving beyond final bin")
self.lo = self.hi
if self.forward:
self.bin += 1
self.atend = (self.bin >= self.n)
if (not self.atend):
self.hi = self.edges[self.bin+1]
else:
self.atend = (self.bin == 0)
if not self.atend:
self.bin -= 1
self.hi = self.edges[self.bin]
return self

@numba.njit(parallel=False, cache=True)
def rebin_counts_portion(Nold, vold, Iold, Nnew, vnew, Inew, ND_portion):

# Note: inspired by rebin from OpenGenie, but using counts per bin
# rather than rates.

# Does not work in place
if (Iold is Inew):
raise ValueError("does not work in place")

# Traverse both sets of bin edges; if there is an overlap, add the portion
# of the overlapping old bin to the new bin.
_from = BinIter(Nold, vold)
_to = BinIter(Nnew, vnew)
while (not _from.atend and not _to.atend):
# std::cout << "from " << from.bin << ": [" << from.lo << ", " << from.hi << "]\n";
# std::cout << "to " << to.bin << ": [" << to.lo << ", " << to.hi << "]\n";
if (_to.hi <= _from.lo):
_to.increment()  # new must catch up to old
elif (_from.hi <= _to.lo):
_from.increment()  # old must catch up to new
else:
overlap = min(_from.hi, _to.hi) - max(_from.lo, _to.lo)
portion = overlap/(_from.hi - _from.lo)
Inew[_to.bin] += Iold[_from.bin]*portion*ND_portion
if (_to.hi > _from.hi):
_from.increment()
else:
_to.increment()

@numba.njit(parallel=False, cache=True)
def rebin_counts_old(Nold, xold, Iold, Nnew, xnew, Inew):

# Note: inspired by rebin from OpenGenie, but using counts per bin
# rather than rates.

# Clear the new bins
for i in range(Nnew):
Inew[i] = 0

rebin_counts_portion(Nold, xold, Iold, Nnew, xnew, Inew, 1.)

[docs]
@numba.njit(parallel=False, cache=True)
def rebin_counts(xold, Iold, xnew, Inew):

# Note: inspired by rebin from OpenGenie, but using counts per bin
# rather than rates.
Nold = len(Iold)
Nnew = len(Inew)
# Clear the new bins
for i in range(Nnew):
Inew[i] = 0

rebin_counts_portion(Nold, xold, Iold, Nnew, xnew, Inew, 1.)

@numba.njit(parallel=False, cache=True)
def rebin_intensity(Nold, xold, Iold, dIold, Nnew, xnew, Inew, dInew):

# Note: inspired by rebin from OpenGenie, but using counts per bin rather than rates.

# Clear the new bins
for i in range(Nnew):
dInew[i] = Inew[i] = 0

# Traverse both sets of bin edges; if there is an overlap, add the portion
# of the overlapping old bin to the new bin.
_from = BinIter(Nold, xold)
_to = BinIter(Nnew, xnew)
while (not _from.atend and not _to.atend):
# std::cout << "from " << from.bin << ": [" << from.lo << ", " << from.hi << "]\n";
# std::cout << "to " << to.bin << ": [" << to.lo << ", " << to.hi << "]\n";
if (_to.hi <= _from.lo):
_to.increment()  # new must catch up to old
elif (_from.hi <= _to.lo):
_from.increment()  # old must catch up to new
else:
overlap = min(_from.hi, _to.hi) - max(_from.lo, _to.lo)
portion = overlap/(_from.hi - _from.lo)

Inew[_to.bin] += (Iold[_from.bin])*portion
dInew[_to.bin] += (dIold[_from.bin]*portion)**2
if (_to.hi > _from.hi):
_from.increment()
else:
_to.increment()

# Convert variance to standard deviation.
for i in range(Nnew):
dInew[i] = math.sqrt(dInew[i])

[docs]
@numba.njit(cache=True)
def rebin_counts_2D(xold, yold, Iold, xnew, ynew, Inew):
Nxold = len(xold) - 1
Nyold = len(yold) - 1
Nxnew = len(xnew) - 1
Nynew = len(ynew) - 1

# Clear the new bins
for i in range(Nxnew):
for j in range(Nynew):
Inew[i][j] = 0

# Traverse both sets of bin edges; if there is an overlap, add the portion
# of the overlapping old bin to the new bin.  Scale this by the portion
# of the overlap in y.
_from = BinIter(Nxold, xold)
_to = BinIter(Nxnew, xnew)

while (not _from.atend and not _to.atend):
if (_to.hi <= _from.lo):
_to.increment()  # new must catch up to old
elif (_from.hi <= _to.lo):
_from.increment()  # old must catch up to new
else:
overlap = min(_from.hi, _to.hi) - max(_from.lo, _to.lo)
portion = overlap / (_from.hi - _from.lo)
rebin_counts_portion(Nyold, yold, Iold[_from.bin],
Nynew, ynew, Inew[_to.bin],
portion)
if (_to.hi > _from.hi):
_from.increment()
else:
_to.increment()

```