# Path: osrefl/loaders/reduction/rebin.py
# def rebin2d(x,y,I,xo,yo,Io=None,dtype=None):
#     """
#     Rebin a matrix.
# 
#     x,y are the existing bin edges
#     xo,yo are the new bin edges
#     I is the existing counts (one fewer than edges in each direction)
# 
#     For example, with x representing the column edges in each row and
#     y representing the row edges in each column, the following
#     represents a uniform field::
# 
#         >>> from osrefl.loaders.rebin import rebin2d
#         >>> x,y = [0,2,4,5], [0,1,3]
#         >>> z = [[2,2,1],[4,4,2]]
# 
#     We can check this by rebinning with uniform size bins::
# 
#         >>> xo,yo = range(6), range(4)
#         >>> rebin2d(y,x,z,yo,xo)
#         array([[ 1.,  1.,  1.,  1.,  1.],
#                [ 1.,  1.,  1.,  1.,  1.],
#                [ 1.,  1.,  1.,  1.,  1.]])
# 
#     dtype is the type to use for the intensity vectors.  This can be
#     integer (uint8, uint16, uint32) or real (float32 or f, float64 or d).
#     The edge vectors are all coerced to doubles.
# 
#     Note that total intensity is not preserved for integer rebinning.
#     The algorithm uses truncation so total intensity will be down on
#     average by half the total number of bins.
# 
#     Io will be used if present, if it is contiguous and if it has the
#     correct shape and type for the input.  Otherwise it will raise a
#     TypeError.  This will allow you to rebin the slices of an appropriately
#     ordered matrix without making copies.
#     """
#     # Coerce axes to float arrays
#     x,y,xo,yo = [_input(v, dtype='d') for v in (x,y,xo,yo)]
#     shape_in = numpy.array([x.shape[0]-1,y.shape[0]-1])
#     shape_out = numpy.array([xo.shape[0]-1,yo.shape[0]-1])
# 
#     # Coerce counts to correct type and check shape
#     if dtype is None:
#         try: dtype = I.dtype
#         except AttributeError: dtype = numpy.float64
#     I = _input(I, dtype=dtype)
#     if (shape_in != I.shape).any():
#         raise TypeError("input array incorrect shape %s"%str(I.shape))
# 
#     # Create output vector
#     Io = _output(Io,shape_out,dtype=dtype)
# 
#     # Call rebin on type if it is available
#     try:
#         rebincore = getattr(_reduction,'rebin2d_'+I.dtype.name)
#     except AttributeError:
#         raise TypeError("rebin2d supports uint8 uint16 uint32 float32 float64, not "
#                         + I.dtype.name)
#     rebincore(x,y,I,xo,yo,Io)
#     return Io
# 
# def rebin(x,I,xo,Io=None,dtype=numpy.float64):
#     """
#     Rebin a vector.
# 
#     x are the existing bin edges
#     xo are the new bin edges
#     I are the existing counts (one fewer than edges)
# 
#     Io will be used if present, but be sure that it is a contiguous
#     array of the correct shape and size.
# 
#     dtype is the type to use for the intensity vectors.  This can be
#     integer (uint8, uint16, uint32) or real (float32 or f, float64 or d).
#     The edge vectors are all coerced to doubles.
# 
#     Note that total intensity is not preserved for integer rebinning.
#     The algorithm uses truncation so total intensity will be down on
#     average by half the total number of bins.
#     """
#     # Coerce axes to float arrays
#     x,xo = _input(x,dtype='d'),_input(xo,dtype='d')
#     shape_in = numpy.array([x.shape[0]-1])
#     shape_out = numpy.array([xo.shape[0]-1])
# 
#     # Coerce counts to correct type and check shape
#     if dtype is None:
#         try:
#             dtype = I.dtype
#         except AttributeError:
#             dtype = numpy.float64
#     I = _input(I, dtype=dtype)
#     if shape_in != I.shape:
#         raise TypeError("input array incorrect shape %s"%I.shape)
# 
#     # Create output vector
#     Io = _output(Io,shape_out,dtype=dtype)
# 
#     # Call rebin on type if it is available
#     try:
#         rebincore = getattr(_reduction,'rebin_'+I.dtype.name)
#     except AttributeError:
#         raise TypeError("rebin supports uint8 uint16 uint32 float32 float64, not "
#                         + I.dtype.name)
#     rebincore(x,I,xo,Io)
#     return Io


import numpy
from .rebin import rebin2d, rebin
     w_1   w_2        w_n

    L = sum(w) = length of the detector
    C_i = measured counts in cell i
    D = sum(C)/L = average flux on the detector

Given a uniform flux on the detector, what is the probability of seeing
the particular measured set of counts P(C|w).  We will assume a Poisson
distribution for the counts in the individual pixels.

    D = flux in counts/unit area
    lambda_i = D w_i = expected counts in cell i
    P_i(C_i|D w_i) = exp(-D w_i) (D w_i)**C_i / C_i!
    P(C|w) = prod P_i(C_i|D w_i)

Minimize the log probability by setting the derivative to zero:

    log P = sum log P_i
    log P_i = -D w_i + C_i log D w_i - log C_i!

    d/dw_i log P = d/dw_i log P_i
                 = d/dw_i (-D w_i) + C_i d/dw_i log D w_i
                 = -D + C_i D / (D w_i)
    d/dw_i log P = 0
        => D = C_i/w_i
        => w_i = C_i/D = L * C_i / C
"""




# Path: osrefl/loaders/reduction/rebin.py
# def rebin2d(x,y,I,xo,yo,Io=None,dtype=None):
#     """
#     Rebin a matrix.
# 
#     x,y are the existing bin edges
#     xo,yo are the new bin edges
#     I is the existing counts (one fewer than edges in each direction)
# 
#     For example, with x representing the column edges in each row and
#     y representing the row edges in each column, the following
#     represents a uniform field::
# 
#         >>> from osrefl.loaders.rebin import rebin2d
#         >>> x,y = [0,2,4,5], [0,1,3]
#         >>> z = [[2,2,1],[4,4,2]]
# 
#     We can check this by rebinning with uniform size bins::
# 
#         >>> xo,yo = range(6), range(4)
#         >>> rebin2d(y,x,z,yo,xo)
#         array([[ 1.,  1.,  1.,  1.,  1.],
#                [ 1.,  1.,  1.,  1.,  1.],
#                [ 1.,  1.,  1.,  1.,  1.]])
# 
#     dtype is the type to use for the intensity vectors.  This can be
#     integer (uint8, uint16, uint32) or real (float32 or f, float64 or d).
#     The edge vectors are all coerced to doubles.
# 
#     Note that total intensity is not preserved for integer rebinning.
#     The algorithm uses truncation so total intensity will be down on
#     average by half the total number of bins.
# 
#     Io will be used if present, if it is contiguous and if it has the
#     correct shape and type for the input.  Otherwise it will raise a
#     TypeError.  This will allow you to rebin the slices of an appropriately
#     ordered matrix without making copies.
#     """
#     # Coerce axes to float arrays
#     x,y,xo,yo = [_input(v, dtype='d') for v in (x,y,xo,yo)]
#     shape_in = numpy.array([x.shape[0]-1,y.shape[0]-1])
#     shape_out = numpy.array([xo.shape[0]-1,yo.shape[0]-1])
# 
#     # Coerce counts to correct type and check shape
#     if dtype is None:
#         try: dtype = I.dtype
#         except AttributeError: dtype = numpy.float64
#     I = _input(I, dtype=dtype)
#     if (shape_in != I.shape).any():
#         raise TypeError("input array incorrect shape %s"%str(I.shape))
# 
#     # Create output vector
#     Io = _output(Io,shape_out,dtype=dtype)
# 
#     # Call rebin on type if it is available
#     try:
#         rebincore = getattr(_reduction,'rebin2d_'+I.dtype.name)
#     except AttributeError:
#         raise TypeError("rebin2d supports uint8 uint16 uint32 float32 float64, not "
#                         + I.dtype.name)
#     rebincore(x,y,I,xo,yo,Io)
#     return Io
# 
# def rebin(x,I,xo,Io=None,dtype=numpy.float64):
#     """
#     Rebin a vector.
# 
#     x are the existing bin edges
#     xo are the new bin edges
#     I are the existing counts (one fewer than edges)
# 
#     Io will be used if present, but be sure that it is a contiguous
#     array of the correct shape and size.
# 
#     dtype is the type to use for the intensity vectors.  This can be
#     integer (uint8, uint16, uint32) or real (float32 or f, float64 or d).
#     The edge vectors are all coerced to doubles.
# 
#     Note that total intensity is not preserved for integer rebinning.
#     The algorithm uses truncation so total intensity will be down on
#     average by half the total number of bins.
#     """
#     # Coerce axes to float arrays
#     x,xo = _input(x,dtype='d'),_input(xo,dtype='d')
#     shape_in = numpy.array([x.shape[0]-1])
#     shape_out = numpy.array([xo.shape[0]-1])
# 
#     # Coerce counts to correct type and check shape
#     if dtype is None:
#         try:
#             dtype = I.dtype
#         except AttributeError:
#             dtype = numpy.float64
#     I = _input(I, dtype=dtype)
#     if shape_in != I.shape:
#         raise TypeError("input array incorrect shape %s"%I.shape)
# 
#     # Create output vector
#     Io = _output(Io,shape_out,dtype=dtype)
# 
#     # Call rebin on type if it is available
#     try:
#         rebincore = getattr(_reduction,'rebin_'+I.dtype.name)
#     except AttributeError:
#         raise TypeError("rebin supports uint8 uint16 uint32 float32 float64, not "
#                         + I.dtype.name)
#     rebincore(x,I,xo,Io)
#     return Io


# Path: osrefl/loaders/reduction/areacor.py
import numpy
from .rebin import rebin2d, rebin

     w_1   w_2        w_n

    L = sum(w) = length of the detector
    C_i = measured counts in cell i
    D = sum(C)/L = average flux on the detector

Given a uniform flux on the detector, what is the probability of seeing
the particular measured set of counts P(C|w).  We will assume a Poisson
distribution for the counts in the individual pixels.

    D = flux in counts/unit area
    lambda_i = D w_i = expected counts in cell i
    P_i(C_i|D w_i) = exp(-D w_i) (D w_i)**C_i / C_i!
    P(C|w) = prod P_i(C_i|D w_i)

Minimize the log probability by setting the derivative to zero:

    log P = sum log P_i
    log P_i = -D w_i + C_i log D w_i - log C_i!

    d/dw_i log P = d/dw_i log P_i
                 = d/dw_i (-D w_i) + C_i d/dw_i log D w_i
                 = -D + C_i D / (D w_i)
    d/dw_i log P = 0
        => D = C_i/w_i
        => w_i = C_i/D = L * C_i / C
"""




def measured_area_correction(data, rebin=False):