# This file was automatically generated by SWIG (https://www.swig.org).
# Version 4.2.1
#
# Do not make changes to this file unless you know what you are doing - modify
# the SWIG interface file instead.

from sys import version_info as _swig_python_version_info
# Import the low-level C/C++ module
if __package__ or "." in __name__:
    from . import _fit2x
else:
    import _fit2x

try:
    import builtins as __builtin__
except ImportError:
    import __builtin__

def _swig_repr(self):
    try:
        strthis = "proxy of " + self.this.__repr__()
    except __builtin__.Exception:
        strthis = ""
    return "<%s.%s; %s >" % (self.__class__.__module__, self.__class__.__name__, strthis,)


def _swig_setattr_nondynamic_instance_variable(set):
    def set_instance_attr(self, name, value):
        if name == "this":
            set(self, name, value)
        elif name == "thisown":
            self.this.own(value)
        elif hasattr(self, name) and isinstance(getattr(type(self), name), property):
            set(self, name, value)
        else:
            raise AttributeError("You cannot add instance attributes to %s" % self)
    return set_instance_attr


def _swig_setattr_nondynamic_class_variable(set):
    def set_class_attr(cls, name, value):
        if hasattr(cls, name) and not isinstance(getattr(cls, name), property):
            set(cls, name, value)
        else:
            raise AttributeError("You cannot add class attributes to %s" % cls)
    return set_class_attr


def _swig_add_metaclass(metaclass):
    """Class decorator for adding a metaclass to a SWIG wrapped class - a slimmed down version of six.add_metaclass"""
    def wrapper(cls):
        return metaclass(cls.__name__, cls.__bases__, cls.__dict__.copy())
    return wrapper


class _SwigNonDynamicMeta(type):
    """Meta class to enforce nondynamic attributes (no new attributes) for a class"""
    __setattr__ = _swig_setattr_nondynamic_class_variable(type.__setattr__)


import weakref

SHARED_PTR_DISOWN = _fit2x.SHARED_PTR_DISOWN
r"""docstring"""
class SwigPyIterator(object):
    r"""docstring"""

    thisown = property(lambda x: x.this.own(), lambda x, v: x.this.own(v), doc="The membership flag")

    def __init__(self, *args, **kwargs):
        raise AttributeError("No constructor defined - class is abstract")
    __repr__ = _swig_repr
    __swig_destroy__ = _fit2x.delete_SwigPyIterator

    def value(self):
        r"""docstring"""
        return _fit2x.SwigPyIterator_value(self)

    def incr(self, n=1):
        r"""docstring"""
        return _fit2x.SwigPyIterator_incr(self, n)

    def decr(self, n=1):
        r"""docstring"""
        return _fit2x.SwigPyIterator_decr(self, n)

    def distance(self, x):
        r"""docstring"""
        return _fit2x.SwigPyIterator_distance(self, x)

    def equal(self, x):
        r"""docstring"""
        return _fit2x.SwigPyIterator_equal(self, x)

    def copy(self):
        r"""docstring"""
        return _fit2x.SwigPyIterator_copy(self)

    def next(self):
        r"""docstring"""
        return _fit2x.SwigPyIterator_next(self)

    def __next__(self):
        r"""docstring"""
        return _fit2x.SwigPyIterator___next__(self)

    def previous(self):
        r"""docstring"""
        return _fit2x.SwigPyIterator_previous(self)

    def advance(self, n):
        r"""docstring"""
        return _fit2x.SwigPyIterator_advance(self, n)

    def __eq__(self, x):
        r"""docstring"""
        return _fit2x.SwigPyIterator___eq__(self, x)

    def __ne__(self, x):
        r"""docstring"""
        return _fit2x.SwigPyIterator___ne__(self, x)

    def __iadd__(self, n):
        r"""docstring"""
        return _fit2x.SwigPyIterator___iadd__(self, n)

    def __isub__(self, n):
        r"""docstring"""
        return _fit2x.SwigPyIterator___isub__(self, n)

    def __add__(self, n):
        r"""docstring"""
        return _fit2x.SwigPyIterator___add__(self, n)

    def __sub__(self, *args):
        r"""docstringdocstring"""
        return _fit2x.SwigPyIterator___sub__(self, *args)
    def __iter__(self):
        return self

# Register SwigPyIterator in _fit2x:
_fit2x.SwigPyIterator_swigregister(SwigPyIterator)
class VectorDouble(object):
    r"""docstring"""

    thisown = property(lambda x: x.this.own(), lambda x, v: x.this.own(v), doc="The membership flag")
    __repr__ = _swig_repr

    def iterator(self):
        r"""docstring"""
        return _fit2x.VectorDouble_iterator(self)
    def __iter__(self):
        return self.iterator()

    def __nonzero__(self):
        r"""docstring"""
        return _fit2x.VectorDouble___nonzero__(self)

    def __bool__(self):
        r"""docstring"""
        return _fit2x.VectorDouble___bool__(self)

    def __len__(self):
        r"""docstring"""
        return _fit2x.VectorDouble___len__(self)

    def __getslice__(self, i, j):
        r"""docstring"""
        return _fit2x.VectorDouble___getslice__(self, i, j)

    def __setslice__(self, *args):
        r"""docstringdocstring"""
        return _fit2x.VectorDouble___setslice__(self, *args)

    def __delslice__(self, i, j):
        r"""docstring"""
        return _fit2x.VectorDouble___delslice__(self, i, j)

    def __delitem__(self, *args):
        r"""docstringdocstring"""
        return _fit2x.VectorDouble___delitem__(self, *args)

    def __getitem__(self, *args):
        r"""docstringdocstring"""
        return _fit2x.VectorDouble___getitem__(self, *args)

    def __setitem__(self, *args):
        r"""docstringdocstringdocstring"""
        return _fit2x.VectorDouble___setitem__(self, *args)

    def pop(self):
        r"""docstring"""
        return _fit2x.VectorDouble_pop(self)

    def append(self, x):
        r"""docstring"""
        return _fit2x.VectorDouble_append(self, x)

    def empty(self):
        r"""docstring"""
        return _fit2x.VectorDouble_empty(self)

    def size(self):
        r"""docstring"""
        return _fit2x.VectorDouble_size(self)

    def swap(self, v):
        r"""docstring"""
        return _fit2x.VectorDouble_swap(self, v)

    def begin(self):
        r"""docstring"""
        return _fit2x.VectorDouble_begin(self)

    def end(self):
        r"""docstring"""
        return _fit2x.VectorDouble_end(self)

    def rbegin(self):
        r"""docstring"""
        return _fit2x.VectorDouble_rbegin(self)

    def rend(self):
        r"""docstring"""
        return _fit2x.VectorDouble_rend(self)

    def clear(self):
        r"""docstring"""
        return _fit2x.VectorDouble_clear(self)

    def get_allocator(self):
        r"""docstring"""
        return _fit2x.VectorDouble_get_allocator(self)

    def pop_back(self):
        r"""docstring"""
        return _fit2x.VectorDouble_pop_back(self)

    def erase(self, *args):
        r"""docstringdocstring"""
        return _fit2x.VectorDouble_erase(self, *args)

    def __init__(self, *args):
        r"""docstringdocstringdocstringdocstring"""
        _fit2x.VectorDouble_swiginit(self, _fit2x.new_VectorDouble(*args))

    def push_back(self, x):
        r"""docstring"""
        return _fit2x.VectorDouble_push_back(self, x)

    def front(self):
        r"""docstring"""
        return _fit2x.VectorDouble_front(self)

    def back(self):
        r"""docstring"""
        return _fit2x.VectorDouble_back(self)

    def assign(self, n, x):
        r"""docstring"""
        return _fit2x.VectorDouble_assign(self, n, x)

    def resize(self, *args):
        r"""docstringdocstring"""
        return _fit2x.VectorDouble_resize(self, *args)

    def insert(self, *args):
        r"""docstringdocstring"""
        return _fit2x.VectorDouble_insert(self, *args)

    def reserve(self, n):
        r"""docstring"""
        return _fit2x.VectorDouble_reserve(self, n)

    def capacity(self):
        r"""docstring"""
        return _fit2x.VectorDouble_capacity(self)
    __swig_destroy__ = _fit2x.delete_VectorDouble

# Register VectorDouble in _fit2x:
_fit2x.VectorDouble_swigregister(VectorDouble)
class VectorInt32(object):
    r"""docstring"""

    thisown = property(lambda x: x.this.own(), lambda x, v: x.this.own(v), doc="The membership flag")
    __repr__ = _swig_repr

    def iterator(self):
        r"""docstring"""
        return _fit2x.VectorInt32_iterator(self)
    def __iter__(self):
        return self.iterator()

    def __nonzero__(self):
        r"""docstring"""
        return _fit2x.VectorInt32___nonzero__(self)

    def __bool__(self):
        r"""docstring"""
        return _fit2x.VectorInt32___bool__(self)

    def __len__(self):
        r"""docstring"""
        return _fit2x.VectorInt32___len__(self)

    def __getslice__(self, i, j):
        r"""docstring"""
        return _fit2x.VectorInt32___getslice__(self, i, j)

    def __setslice__(self, *args):
        r"""docstringdocstring"""
        return _fit2x.VectorInt32___setslice__(self, *args)

    def __delslice__(self, i, j):
        r"""docstring"""
        return _fit2x.VectorInt32___delslice__(self, i, j)

    def __delitem__(self, *args):
        r"""docstringdocstring"""
        return _fit2x.VectorInt32___delitem__(self, *args)

    def __getitem__(self, *args):
        r"""docstringdocstring"""
        return _fit2x.VectorInt32___getitem__(self, *args)

    def __setitem__(self, *args):
        r"""docstringdocstringdocstring"""
        return _fit2x.VectorInt32___setitem__(self, *args)

    def pop(self):
        r"""docstring"""
        return _fit2x.VectorInt32_pop(self)

    def append(self, x):
        r"""docstring"""
        return _fit2x.VectorInt32_append(self, x)

    def empty(self):
        r"""docstring"""
        return _fit2x.VectorInt32_empty(self)

    def size(self):
        r"""docstring"""
        return _fit2x.VectorInt32_size(self)

    def swap(self, v):
        r"""docstring"""
        return _fit2x.VectorInt32_swap(self, v)

    def begin(self):
        r"""docstring"""
        return _fit2x.VectorInt32_begin(self)

    def end(self):
        r"""docstring"""
        return _fit2x.VectorInt32_end(self)

    def rbegin(self):
        r"""docstring"""
        return _fit2x.VectorInt32_rbegin(self)

    def rend(self):
        r"""docstring"""
        return _fit2x.VectorInt32_rend(self)

    def clear(self):
        r"""docstring"""
        return _fit2x.VectorInt32_clear(self)

    def get_allocator(self):
        r"""docstring"""
        return _fit2x.VectorInt32_get_allocator(self)

    def pop_back(self):
        r"""docstring"""
        return _fit2x.VectorInt32_pop_back(self)

    def erase(self, *args):
        r"""docstringdocstring"""
        return _fit2x.VectorInt32_erase(self, *args)

    def __init__(self, *args):
        r"""docstringdocstringdocstringdocstring"""
        _fit2x.VectorInt32_swiginit(self, _fit2x.new_VectorInt32(*args))

    def push_back(self, x):
        r"""docstring"""
        return _fit2x.VectorInt32_push_back(self, x)

    def front(self):
        r"""docstring"""
        return _fit2x.VectorInt32_front(self)

    def back(self):
        r"""docstring"""
        return _fit2x.VectorInt32_back(self)

    def assign(self, n, x):
        r"""docstring"""
        return _fit2x.VectorInt32_assign(self, n, x)

    def resize(self, *args):
        r"""docstringdocstring"""
        return _fit2x.VectorInt32_resize(self, *args)

    def insert(self, *args):
        r"""docstringdocstring"""
        return _fit2x.VectorInt32_insert(self, *args)

    def reserve(self, n):
        r"""docstring"""
        return _fit2x.VectorInt32_reserve(self, n)

    def capacity(self):
        r"""docstring"""
        return _fit2x.VectorInt32_capacity(self)
    __swig_destroy__ = _fit2x.delete_VectorInt32

# Register VectorInt32 in _fit2x:
_fit2x.VectorInt32_swigregister(VectorInt32)
import numpy as np
import fit2x


class Fit2x(object):

    def __init__(
            self,
            dt: float,
            irf: np.ndarray,
            background: np.ndarray,
            period: float,
            g_factor: float = 1.0,
            l1: float = 0.0,
            l2: float = 0.0,
            convolution_stop: int = -1,
            soft_bifl_scatter_flag: bool = True,
            verbose: bool = False,
            p2s_twoIstar_flag: bool = False
    ):
        """
        :param dt: time difference between microtime bins
        :param irf: counting histogram of the instrument response function in
        Jordi format
        :param background: background counting histogram in Jordi format
        :param period: excitation period of the light source
        :param g_factor: g-factor, only used for calculation of anisotropy by
        intensity in Ss and Sp
        :param l1: factor correcting mixing between parallel and perpendicular
        detection channel, only used for calculation of anisotropy by
        intensity in Ss and Sp
        :param l2: factor correcting mixing between parallel and perpendicular
        detection channel, only used for calculation of anisotropy by
        intensity in Ss and Sp
        :param convolution_stop: maximum micro time channel for comvolution. If
        no value is provided the length of the IRF is used
        :param soft_bifl_scatter_flag: if set to True the returned Istar value
        is reduced by the background photon contribution (background photons do
        no inform on the fluorescence lifetime)
        :param p2s_twoIstar_flag: If this is set to True the sum decay composed
        by P + 2S (P - Parallel, S - Perpendicular) is optimized. Otherwise
        (default) the decays P and S are optimized individually in a global fit.
        :param verbose: if set to True is more verbose
        """
        if len(irf) != len(background):
            raise ValueError("The IRF and the background differ in size")
        if len(irf) % 2 != 0:
            raise ValueError("The length of the input arrays is not divisible "
                             "by two. Inputs need to be in Jordi format.")
        if convolution_stop < 0:
            convolution_stop = len(irf) // 2 - 1
        self._bifl_scatter = -1 if soft_bifl_scatter_flag else 0
        self._p_2s_flag = p2s_twoIstar_flag
        self._corrections = np.array([period, g_factor, l1, l2, convolution_stop])
        self._irf = irf
        self._background = background
        self._dt = dt
        self._m_param = fit2x.CreateMParam(
            irf=self._irf,
            background=self._background,
            corrections=self._corrections,
            dt=self._dt
        )
        self._verbose = verbose

    @property
    def model(self):
        return np.array([x for x in self._m_param.get_model()])

    @property
    def data(self):
        return np.array([x for x in self._m_param.get_data()])

    @property
    def irf(self):
        return np.array([x for x in self._m_param.get_irf()])

    @property
    def background(self):
        return np.array([x for x in self._m_param.get_background()])

    def __call__(
            self,
            data: np.ndarray
    ) -> None:
        """Take care of data ana make sure that it is a numpy object
        with appropriate dtype

        :param data:
        :return:
        """
        self._m_param.get_data().set_data(
            np.array(data).astype(dtype=np.int32)
        )

class LVI32Array(object):
    r"""
    docstring


    Structures and functions used for LabView interface.  

    fit2x was originally developed as a C backend for LabView software. Therefore,
    the interface with fit2x uses structures that can be accessed by Labview. In
    order to make an interfacing with Python and other languages possible there is a
    this files defines a set of functions that facilitate the creation of the
    LabView structures.  

    C++ includes: lvarrays.h

    """

    thisown = property(lambda x: x.this.own(), lambda x, v: x.this.own(v), doc="The membership flag")
    __repr__ = _swig_repr
    length = property(_fit2x.LVI32Array_length_get, _fit2x.LVI32Array_length_set, doc=r"""docstring""")
    data = property(_fit2x.LVI32Array_data_get, _fit2x.LVI32Array_data_set, doc=r"""docstring""")
    __swig_destroy__ = _fit2x.delete_LVI32Array

    def __getitem__(self, i):
        r"""docstring"""
        return _fit2x.LVI32Array___getitem__(self, i)

    def __setitem__(self, i, value):
        r"""docstring"""
        return _fit2x.LVI32Array___setitem__(self, i, value)

    def __len__(self):
        r"""docstring"""
        return _fit2x.LVI32Array___len__(self)

    def insert(self, i, value):
        r"""docstring"""
        return _fit2x.LVI32Array_insert(self, i, value)

    def set_data(self, input):
        r"""docstring"""
        return _fit2x.LVI32Array_set_data(self, input)

    def __init__(self):
        r"""
        docstring


        Structures and functions used for LabView interface.  

        fit2x was originally developed as a C backend for LabView software. Therefore,
        the interface with fit2x uses structures that can be accessed by Labview. In
        order to make an interfacing with Python and other languages possible there is a
        this files defines a set of functions that facilitate the creation of the
        LabView structures.  

        C++ includes: lvarrays.h

        """
        _fit2x.LVI32Array_swiginit(self, _fit2x.new_LVI32Array())

# Register LVI32Array in _fit2x:
_fit2x.LVI32Array_swigregister(LVI32Array)
class LVDoubleArray(object):
    r"""
    docstring


    """

    thisown = property(lambda x: x.this.own(), lambda x, v: x.this.own(v), doc="The membership flag")
    __repr__ = _swig_repr
    length = property(_fit2x.LVDoubleArray_length_get, _fit2x.LVDoubleArray_length_set, doc=r"""docstring""")
    data = property(_fit2x.LVDoubleArray_data_get, _fit2x.LVDoubleArray_data_set, doc=r"""docstring""")
    __swig_destroy__ = _fit2x.delete_LVDoubleArray

    def __getitem__(self, i):
        r"""docstring"""
        return _fit2x.LVDoubleArray___getitem__(self, i)

    def __setitem__(self, i, value):
        r"""docstring"""
        return _fit2x.LVDoubleArray___setitem__(self, i, value)

    def __len__(self):
        r"""docstring"""
        return _fit2x.LVDoubleArray___len__(self)

    def insert(self, i, value):
        r"""docstring"""
        return _fit2x.LVDoubleArray_insert(self, i, value)

    def set_data(self, input):
        r"""docstring"""
        return _fit2x.LVDoubleArray_set_data(self, input)

    def __init__(self):
        r"""
        docstring


        """
        _fit2x.LVDoubleArray_swiginit(self, _fit2x.new_LVDoubleArray())

# Register LVDoubleArray in _fit2x:
_fit2x.LVDoubleArray_swigregister(LVDoubleArray)
class MParam(object):
    r"""
    docstring


    """

    thisown = property(lambda x: x.this.own(), lambda x, v: x.this.own(v), doc="The membership flag")
    __repr__ = _swig_repr
    expdata = property(_fit2x.MParam_expdata_get, _fit2x.MParam_expdata_set, doc=r"""docstring""")
    irf = property(_fit2x.MParam_irf_get, _fit2x.MParam_irf_set, doc=r"""docstring""")
    bg = property(_fit2x.MParam_bg_get, _fit2x.MParam_bg_set, doc=r"""docstring""")
    dt = property(_fit2x.MParam_dt_get, _fit2x.MParam_dt_set, doc=r"""docstring""")
    corrections = property(_fit2x.MParam_corrections_get, _fit2x.MParam_corrections_set, doc=r"""docstring""")
    M = property(_fit2x.MParam_M_get, _fit2x.MParam_M_set, doc=r"""docstring""")
    __swig_destroy__ = _fit2x.delete_MParam

    def get_irf(self):
        r"""docstring"""
        return _fit2x.MParam_get_irf(self)

    def get_model(self):
        r"""docstring"""
        return _fit2x.MParam_get_model(self)

    def get_data(self):
        r"""docstring"""
        return _fit2x.MParam_get_data(self)

    def get_corrections(self):
        r"""docstring"""
        return _fit2x.MParam_get_corrections(self)

    def get_background(self):
        r"""docstring"""
        return _fit2x.MParam_get_background(self)

    def __init__(self):
        r"""
        docstring


        """
        _fit2x.MParam_swiginit(self, _fit2x.new_MParam())

# Register MParam in _fit2x:
_fit2x.MParam_swigregister(MParam)

def CreateLVI32Array(len):
    r"""
    docstring


    Parameters
    ----------
    * `len` :  

    Returns
    -------  

    """
    return _fit2x.CreateLVI32Array(len)

def CreateLVDoubleArray(len):
    r"""
    docstring


    Parameters
    ----------
    * `len` :  

    Returns
    -------  

    """
    return _fit2x.CreateLVDoubleArray(len)

def CreateMParam(*args, **kwargs):
    r"""
    docstring


    """
    return _fit2x.CreateMParam(*args, **kwargs)

def fconv_per_cs_time_axis(model, time_axis, instrument_response_function, lifetime_spectrum, convolution_start=0, convolution_stop=-1, period=100.0):
    r"""
    docstring


    Compute the fluorescence decay for a lifetime spectrum and a instrument response
    function considering periodic excitation.  

    Fills the pre-allocated output array `output_decay` with a fluorescence
    intensity decay defined by a set of fluorescence lifetimes defined by the
    parameter `lifetime_spectrum`. The fluorescence decay will be convolved (non-
    periodically) with an instrumental response function that is defined by
    `instrument_response_function`.  

    This function calculates a fluorescence intensity model_decay that is convolved
    with an instrument response function (IRF). The fluorescence intensity
    model_decay is specified by its fluorescence lifetime spectrum, i.e., an
    interleaved array containing fluorescence lifetimes with corresponding
    amplitudes.  

    This convolution only works with evenly linear spaced time axes.  

    Parameters
    ----------
    * `inplace_output[in`, `out]` :  
        Inplace output array that is filled with the values of the computed
        fluorescence intensity decay model  
    * `n_output[in]` :  
        Number of elements in the output array  
    * `time_axis[in]` :  
        the time-axis of the model_decay  
    * `n_time_axis[in]` :  
        length of the time axis  
    * `irf[in]` :  
        the instrument response function array  
    * `n_irf[in]` :  
        length of the instrument response function array  
    * `lifetime_spectrum[in]` :  
        Interleaved array of amplitudes and fluorescence lifetimes of the form
        (amplitude, lifetime, amplitude, lifetime, ...)  
    * `n_lifetime_spectrum[in]` :  
        number of elements in the lifetime spectrum  
    * `convolution_start[in]` :  
        Start channel of convolution (position in array of IRF)  
    * `convolution_stop[in]` :  
        convolution stop channel (the index on the time-axis)  
    * `period` :  
        Period of repetition in units of the lifetime (usually, nano-seconds)  

    """
    return _fit2x.fconv_per_cs_time_axis(model, time_axis, instrument_response_function, lifetime_spectrum, convolution_start, convolution_stop, period)

def add_pile_up_to_model(*args, **kwargs):
    r"""
    docstring


    Add a pile-up distortion to the model function.  

    This function adds a pile up distortion to a model fluorescence decay. The model
    used to compute the pile-up distortion follows the description of Coates (1968,
    eq. 2 and eq. 4)  

    Reference: Coates, P.: The correction for photonpile-up in the measurement of
    radiative lifetimes. J. Phys. E: Sci. Instrum. 1(8), 878–879 (1968)  

    Parameters
    ----------
    * `model[in`, `out]` :  
        The array containing the model function  
    * `n_model[in]` :  
        Number of elements in the model array  
    * `data[in]` :  
        The array containing the experimental decay  
    * `n_data[in]` :  
        number of elements in experimental decay  
    * `repetition_rate[in]` :  
        The repetition-rate (excitation rate) in MHz  
    * `instrument_dead_time[in]` :  
        The overall dead-time of the detection system in nanoseconds  
    * `measurement_time[in]` :  
        The measurement time in seconds  
    * `pile_up_model[in]` :  
        The model used to compute the pile up distortion. (default "coates")  

    """
    return _fit2x.add_pile_up_to_model(*args, **kwargs)

def rescale(fit, decay, start=0, stop=-1):
    r"""docstring"""
    return _fit2x.rescale(fit, decay, start, stop)

def rescale_w(fit, decay, w_sq, start=0, stop=-1):
    r"""docstring"""
    return _fit2x.rescale_w(fit, decay, w_sq, start, stop)

def rescale_w_bg(fit, decay, w_sq, bg=0.0, start=0, stop=-1):
    r"""docstring"""
    return _fit2x.rescale_w_bg(fit, decay, w_sq, bg, start, stop)

def fconv(fit, irf, x, start=0, stop=-1, dt=1.0):
    r"""docstring"""
    return _fit2x.fconv(fit, irf, x, start, stop, dt)

def fconv_avx(fit, irf, x, start=0, stop=-1, dt=1.0):
    r"""docstring"""
    return _fit2x.fconv_avx(fit, irf, x, start, stop, dt)

def fconv_per(fit, irf, x, period, start=0, stop=-1, dt=1.0):
    r"""docstring"""
    return _fit2x.fconv_per(fit, irf, x, period, start, stop, dt)

def fconv_per_avx(fit, irf, x, period, start=0, stop=-1, dt=1.0):
    r"""docstring"""
    return _fit2x.fconv_per_avx(fit, irf, x, period, start, stop, dt)

def fconv_per_cs(fit, irf, x, period, conv_stop=-1, stop=-1, dt=1.0):
    r"""docstring"""
    return _fit2x.fconv_per_cs(fit, irf, x, period, conv_stop, stop, dt)

def fconv_ref(fit, irf, x, tauref, start=0, stop=-1, dt=1.0):
    r"""docstring"""
    return _fit2x.fconv_ref(fit, irf, x, tauref, start, stop, dt)

def sconv(fit, irf, model, start=0, stop=-1):
    r"""docstring"""
    return _fit2x.sconv(fit, irf, model, start, stop)

def shift_lamp(lamp, lampsh, ts):
    r"""docstring"""
    return _fit2x.shift_lamp(lamp, lampsh, ts)

def modelf23(len1, len2, len3, dt, len4, len5):
    r"""docstring"""
    return _fit2x.modelf23(len1, len2, len3, dt, len4, len5)

def targetf23(len1, p):
    r"""docstring"""
    return _fit2x.targetf23(len1, p)

def fit23(len1, len2, p):
    r"""docstring"""
    return _fit2x.fit23(len1, len2, p)
import numpy as np
import fit2x


class Fit23(fit2x.Fit2x):

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)

    def __call__(
            self,
            data: np.ndarray,
            initial_values: np.ndarray,
            fixed: np.ndarray = None,
            include_model: bool = False
    ) -> dict:
        """

        :param data: counting histogram containing experimental data
        :param initial_values: initial values of the model parameters that can
        be optimized. [tau, gamma, r0, rho]
        :param fixed: optional array of short (16bit) integers that specifies if
        a parameter is fixed. Parameters that are fixed are not optimized.
        :param include_model: if set to True (default is False) the realization
        of the model that corresponds to the optimized parameters is included in
        the returned dictionary.
        :return: dictionary containing a quality parameter (key: "Istar"), the
        corresponding optimized model parameter values (key: "x"), and an array
        which parameters were fixed (key: "fixed").
        """
        super().__call__(data=data)
        if len(initial_values) < 4:
            raise ValueError(
                "Provide initial values for all for all 4 fitting "
                "parameters."
            )
        if fixed is None:
# lifetime free
            fixed = np.array([0, 1, 1, 1], dtype=np.int16)
        elif isinstance(fixed, np.ndarray):
            if len(fixed) < 4:
                raise ValueError(
                    "The fixed array is too short. Specify for all 4 fitting "
                    "parameters if they are fixed."
                )
        else:
            raise ValueError(
                "The fixed array is of the wrong type. Use an numpy array of "
                "length 4 to specify the fixed state for all 4 model "
                "parameters."
            )
        r = dict()
        x = np.zeros(8, dtype=np.float64)
        x[:4] = initial_values
        x[4] = self._bifl_scatter
        x[5] = self._p_2s_flag
# the other x values are used as outputs
        fixed = fixed.astype(dtype=np.int16)
        twoIstar = fit2x.fit23(x, fixed, self._m_param)
        r['x'] = x
        r['fixed'] = fixed
        r['twoIstar'] = twoIstar
        if include_model:
            r['model'] = self.model
        return r



def modelf24(len1, len2, len3, dt, len4, len5):
    r"""docstring"""
    return _fit2x.modelf24(len1, len2, len3, dt, len4, len5)

def targetf24(len1, p):
    r"""docstring"""
    return _fit2x.targetf24(len1, p)

def fit24(len1, len2, p):
    r"""docstring"""
    return _fit2x.fit24(len1, len2, p)
import numpy as np
import fit2x


class Fit24(fit2x.Fit2x):

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self._parameter_names = [
            'tau1', 'gamma', 'tau2', 'a2',
            'offset / background'
        ]

    def __call__(
            self,
            data: np.ndarray,
            initial_values: np.ndarray,
            fixed: np.ndarray = None,
            include_model: bool = False
    ) -> dict:
        """
        :param data: counting histogram containing experimental data
        :param initial_values: initial values of the model parameters that can
        be optimized. [tau1, gamma, tau2, A2, offset]
        :param fixed: optional array of short (16bit) integers that specifies if
        a parameter is fixed. Parameters that are fixed are not optimized.
        :param include_model: if set to True (default is False) the realization
        of the model that corresponds to the optimized parameters is included in
        the returned dictionary.
        :return: dictionary containing a quality parameter (key: "twoIstar"), the
        corresponding optimized model parameter values (key: "x"), and an array
        which parameters were fixed (key: "fixed").
        """
        super().__call__(data=data)
        if len(initial_values) < 5:
            raise ValueError(
                "Provide initial values for all for all 4 fitting "
                "parameters."
            )
        if fixed is None:
# lifetime free
            fixed = np.array([0, 0, 0, 0, 0], dtype=np.int16)
        elif isinstance(fixed, np.ndarray):
            if len(fixed) < 5:
                raise ValueError(
                    "The fixed array is too short. Specify for all 5 fitting "
                    "parameters if they are fixed."
                )
        else:
            raise ValueError(
                "The fixed array is of the wrong type. Use an numpy array of "
                "length 5 to specify the fixed state for all 5 model "
                "parameters."
            )
        bifl_scatter = self._bifl_scatter
        x = np.zeros(8, dtype=np.float64)
        x[:5] = initial_values
        x[5] = bifl_scatter
        fixed = fixed.astype(dtype=np.int16)
        twoIstar = fit2x.fit24(x, fixed, self._m_param)
        if self._verbose:
            print("Fitting")
            print("Parameter names: ", self._parameter_names)
            print("initial_values: ", initial_values)
            print("fixed: ", fixed)
            print("include_model: ", include_model)
            print("x0: ", x)
        r = dict()
        r['x'] = x
        r['fixed'] = fixed
        r['twoIstar'] = twoIstar
        if include_model:
            r['model'] = self.model
        return r



def targetf25(len1, p):
    r"""docstring"""
    return _fit2x.targetf25(len1, p)

def fit25(len1, len2, p):
    r"""docstring"""
    return _fit2x.fit25(len1, len2, p)
import numpy as np
import fit2x


class Fit25(fit2x.Fit2x):

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self._parameter_names = [
            'tau1', 'tau2', 'tau3', 'tau4', 'gamma'
        ]

    def __call__(
            self,
            data: np.ndarray,
            initial_values: np.ndarray,
            fixed: np.ndarray = None,
            include_model: bool = False
    ) -> dict:
        """
        :param data: counting histogram containing experimental data
        :param initial_values: initial values of the model parameters that can
        be optimized. [tau1, tau2, tau3, tau4, gamma]. The lifetimes are always
        fixed and not optimized. The lifetime best describing the data is
        returned.
        :param fixed: optional array of short (16bit) integers that specifies if
        a parameter is fixed. Parameters that are fixed are not optimized.
        :param include_model: if set to True (default is False) the realization
        of the model that corresponds to the optimized parameters is included in
        the returned dictionary.
        :return: dictionary containing a quality parameter (key: "twoIstar"), the
        corresponding optimized model parameter values (key: "x"), and an array
        which parameters were fixed (key: "fixed").
        """
        super().__call__(data=data)
        if len(initial_values) < 5:
            raise ValueError(
                "Provide initial values for all for all 6 fitting "
                "parameters."
            )
        if fixed is None:
            fixed = np.array([0, 0, 0, 0, 0], dtype=np.int16)
        elif isinstance(fixed, np.ndarray):
            if len(fixed) < 5:
                raise ValueError(
                    "The fixed array is too short. Specify for all 6 fitting "
                    "parameters if they are fixed."
                )
        else:
            raise ValueError(
                "The fixed array is of the wrong type. Use an numpy array of "
                "length 5 to specify the fixed state for all 6 model "
                "parameters."
            )
        bifl_scatter = self._bifl_scatter
        x = np.zeros(9, dtype=np.float64)
        if self._verbose:
            print("Fitting")
            print("Parameter names: ", self._parameter_names)
            print("initial_values: ", initial_values)
            print("fixed: ", fixed)
            print("include_model: ", include_model)
        x[:6] = initial_values
        x[6] = bifl_scatter
        if self._verbose:
            print("x0: ", x)
        fixed = fixed.astype(dtype=np.int16)
        twoIstar = fit2x.fit25(x, fixed, self._m_param)
        r = dict()
        r['x'] = x
        r['fixed'] = fixed
        r['twoIstar'] = twoIstar
        if include_model:
            r['model'] = self.model
        return r



def targetf26(len1, p):
    r"""docstring"""
    return _fit2x.targetf26(len1, p)

def fit26(len1, len2, p):
    r"""docstring"""
    return _fit2x.fit26(len1, len2, p)
import numpy as np
import fit2x


class Fit26(fit2x.Fit2x):

    def __init__(
            self,
            pattern_1: np.ndarray,
            pattern_2: np.ndarray,
            convolution_stop: int = -1,
            verbose: bool = False
    ):
        if convolution_stop < 0:
            convolution_stop = min(len(pattern_1), len(pattern_2))
        self._corrections = np.array([1., 1.0, 0.0, 0.0, convolution_stop])
        self._irf = pattern_1
        self._background = pattern_2
        self._m_param = fit2x.CreateMParam(
            irf=self._irf,
            background=self._background,
            corrections=self._corrections,
            dt=1.0
        )
        self._verbose = verbose
        self._parameter_names = ['x1']

    def __call__(
            self,
            data: np.ndarray,
            initial_values: np.ndarray,
            fixed: np.ndarray = None,
            include_model: bool = False
    ) -> dict:
        """
        :param data: counting histogram containing experimental data
        :param initial_values: initial values of the model parameters that can
        be optimized. Only the fraction of the first pattern [x1] can be optimized.
        :param fixed: optional array of short (16bit) integers that specifies if
        the fraction x1 is fixed.
        :param include_model: if set to True (default is False) the realization
        of the model that corresponds to the optimized parameters is included in
        the returned dictionary.
        :return: dictionary containing a quality parameter (key: "twoIstar"), the
        corresponding optimized model parameter values (key: "x"), and an array
        which parameters were fixed (key: "fixed").
        """
        super().__call__(data=data)
        if len(initial_values) < 1:
            raise ValueError(
                "Provide initial values for all for all 6 fitting "
                "parameters."
            )
        if fixed is None:
            fixed = np.array([0], dtype=np.int16)
        elif isinstance(fixed, np.ndarray):
            if len(fixed) < 1:
                raise ValueError(
                    "The fixed array is too short. Specify for all 6 fitting "
                    "parameters if they are fixed."
                )
        else:
            raise ValueError(
                "The fixed array is of the wrong type. Use an numpy array of "
                "length 5 to specify the fixed state for all 6 model "
                "parameters."
            )
        x = np.zeros(1, dtype=np.float64)
        x[0] = initial_values[0]
        if self._verbose:
            print("Fitting")
            print("Parameter names: ", self._parameter_names)
            print("initial_values: ", initial_values)
            print("fixed: ", fixed)
            print("include_model: ", include_model)
        if self._verbose:
            print("x0: ", x)
        fixed = fixed.astype(dtype=np.int16)
        twoIstar = fit2x.fit26(x, fixed, self._m_param)
        r = dict()
        r['x'] = x
        r['fixed'] = fixed
        r['twoIstar'] = twoIstar
        if include_model:
            r['model'] = self.model
        return r


class DecayCurve(object):
    r"""docstring"""

    thisown = property(lambda x: x.this.own(), lambda x, v: x.this.own(v), doc="The membership flag")
    __repr__ = _swig_repr

    @staticmethod
    def shift_array(input, shift, set_outside=True, outside_value=0.0):
        r"""docstring"""
        return _fit2x.DecayCurve_shift_array(input, shift, set_outside, outside_value)

    @staticmethod
    def add_arrays(curve1, curve2, areal_fraction_curve2, start=0, stop=-1):
        r"""docstring"""
        return _fit2x.DecayCurve_add_arrays(curve1, curve2, areal_fraction_curve2, start, stop)

    def size(self):
        r"""docstring"""
        return _fit2x.DecayCurve_size(self)

    def empty(self):
        r"""docstring"""
        return _fit2x.DecayCurve_empty(self)

    def get_dx(self):
        r"""docstring"""
        return _fit2x.DecayCurve_get_dx(self)

    def resize(self, n, v=0.0, dx=1.0):
        r"""docstring"""
        return _fit2x.DecayCurve_resize(self, n, v, dx)

    def get_average_dx(self):
        r"""docstring"""
        return _fit2x.DecayCurve_get_average_dx(self)

    def get_x(self):
        r"""docstring"""
        return _fit2x.DecayCurve_get_x(self)

    def set_x(self, *args):
        r"""docstringdocstring"""
        return _fit2x.DecayCurve_set_x(self, *args)

    def get_y(self):
        r"""docstring"""
        return _fit2x.DecayCurve_get_y(self)

    def set_y(self, *args):
        r"""docstringdocstring"""
        return _fit2x.DecayCurve_set_y(self, *args)

    def get_ey(self):
        r"""docstring"""
        return _fit2x.DecayCurve_get_ey(self)

    def set_ey(self, *args):
        r"""docstringdocstring"""
        return _fit2x.DecayCurve_set_ey(self, *args)

    def __init__(self, *args, **kwargs):
        r"""docstring"""
        _fit2x.DecayCurve_swiginit(self, _fit2x.new_DecayCurve(*args, **kwargs))

    def __add__(self, *args):
        r"""docstringdocstring"""
        return _fit2x.DecayCurve___add__(self, *args)

    def __mul__(self, *args):
        r"""docstringdocstring"""
        return _fit2x.DecayCurve___mul__(self, *args)

    def __iadd__(self, v):
        r"""docstring"""
        return _fit2x.DecayCurve___iadd__(self, v)

    def __imul__(self, v):
        r"""docstring"""
        return _fit2x.DecayCurve___imul__(self, v)
    acquisition_time = property(_fit2x.DecayCurve_acquisition_time_get, _fit2x.DecayCurve_acquisition_time_set, doc=r"""docstring""")
    shift = property(_fit2x.DecayCurve_shift_get, _fit2x.DecayCurve_shift_set, doc=r"""docstring""")
    @property
    def x(self):
        return np.array(self.get_x())

    @x.setter
    def x(self, v):
        self.set_x(v)

    @property
    def y(self):
        return np.array(self.get_y())

    @y.setter
    def y(self, v):
        self.set_y(v)

    @property
    def ey(self):
        return np.array(self.get_ey())


    __swig_destroy__ = _fit2x.delete_DecayCurve

# Register DecayCurve in _fit2x:
_fit2x.DecayCurve_swigregister(DecayCurve)
class DecayRange(object):
    r"""docstring"""

    thisown = property(lambda x: x.this.own(), lambda x, v: x.this.own(v), doc="The membership flag")
    __repr__ = _swig_repr

    def get_start(self, d=None):
        r"""docstring"""
        return _fit2x.DecayRange_get_start(self, d)

    def get_stop(self, d=None):
        r"""docstring"""
        return _fit2x.DecayRange_get_stop(self, d)

    def set_range(self, v):
        r"""docstring"""
        return _fit2x.DecayRange_set_range(self, v)

    def get_range(self, d=None):
        r"""docstring"""
        return _fit2x.DecayRange_get_range(self, d)

    def set(self, start=0, stop=-1):
        r"""docstring"""
        return _fit2x.DecayRange_set(self, start, stop)

    def __init__(self, start, stop):
        r"""docstring"""
        if self.__class__ == DecayRange:
            _self = None
        else:
            _self = self
        _fit2x.DecayRange_swiginit(self, _fit2x.new_DecayRange(_self, start, stop))
    __swig_destroy__ = _fit2x.delete_DecayRange
    start = property(_fit2x.DecayRange_start_get, _fit2x.DecayRange_start_set, doc=r"""docstring""")
    stop = property(_fit2x.DecayRange_stop_get, _fit2x.DecayRange_stop_set, doc=r"""docstring""")
    def __disown__(self):
        self.this.disown()
        _fit2x.disown_DecayRange(self)
        return weakref.proxy(self)

# Register DecayRange in _fit2x:
_fit2x.DecayRange_swigregister(DecayRange)
class DecayLifetimeHandler(object):
    r"""docstring"""

    thisown = property(lambda x: x.this.own(), lambda x, v: x.this.own(v), doc="The membership flag")
    __repr__ = _swig_repr

    def set_lifetime_spectrum(self, v):
        r"""docstring"""
        return _fit2x.DecayLifetimeHandler_set_lifetime_spectrum(self, v)

    def add_lifetime(self, amplitude, lifetime):
        r"""docstring"""
        return _fit2x.DecayLifetimeHandler_add_lifetime(self, amplitude, lifetime)

    def get_lifetime_spectrum_vec(self):
        r"""docstring"""
        return _fit2x.DecayLifetimeHandler_get_lifetime_spectrum_vec(self)

    def get_lifetime_spectrum(self):
        r"""docstring"""
        return _fit2x.DecayLifetimeHandler_get_lifetime_spectrum(self)

    def __init__(self, *args, **kwargs):
        r"""docstring"""
        _fit2x.DecayLifetimeHandler_swiginit(self, _fit2x.new_DecayLifetimeHandler(*args, **kwargs))
    abs_lifetime_spectrum = property(_fit2x.DecayLifetimeHandler_abs_lifetime_spectrum_get, _fit2x.DecayLifetimeHandler_abs_lifetime_spectrum_set, doc=r"""docstring""")
    use_amplitude_threshold = property(_fit2x.DecayLifetimeHandler_use_amplitude_threshold_get, _fit2x.DecayLifetimeHandler_use_amplitude_threshold_set, doc=r"""docstring""")
    amplitude_threshold = property(_fit2x.DecayLifetimeHandler_amplitude_threshold_get, _fit2x.DecayLifetimeHandler_amplitude_threshold_set, doc=r"""docstring""")
    @property
    def lifetime_spectrum(self):
        return self.get_lifetime_spectrum()

    @lifetime_spectrum.setter
    def lifetime_spectrum(self, v):
        self.set_lifetime_spectrum(v)


    __swig_destroy__ = _fit2x.delete_DecayLifetimeHandler

# Register DecayLifetimeHandler in _fit2x:
_fit2x.DecayLifetimeHandler_swigregister(DecayLifetimeHandler)
class DecayModifier(DecayRange):
    r"""docstring"""

    thisown = property(lambda x: x.this.own(), lambda x, v: x.this.own(v), doc="The membership flag")
    __repr__ = _swig_repr

    def set(self, data, start=0, stop=-1, active=True):
        r"""docstring"""
        return _fit2x.DecayModifier_set(self, data, start, stop, active)

    def resize(self, n, v=0.0):
        r"""docstring"""
        return _fit2x.DecayModifier_resize(self, n, v)

    def add(self, decay):
        r"""docstring"""
        return _fit2x.DecayModifier_add(self, decay)

    def __init__(self, data=None, start=0, stop=-1, active=True):
        r"""docstring"""
        if self.__class__ == DecayModifier:
            _self = None
        else:
            _self = self
        _fit2x.DecayModifier_swiginit(self, _fit2x.new_DecayModifier(_self, data, start, stop, active))
    __swig_destroy__ = _fit2x.delete_DecayModifier
    active = property(_fit2x.DecayModifier_active_get, _fit2x.DecayModifier_active_set, doc=r"""docstring""")
    data = property(_fit2x.DecayModifier_data_get, _fit2x.DecayModifier_data_set, doc=r"""docstring""")
    def __disown__(self):
        self.this.disown()
        _fit2x.disown_DecayModifier(self)
        return weakref.proxy(self)

# Register DecayModifier in _fit2x:
_fit2x.DecayModifier_swigregister(DecayModifier)
ConvFastPeriodicTime = _fit2x.ConvFastPeriodicTime
r"""docstring"""
ConvFastTime = _fit2x.ConvFastTime
r"""docstring"""
ConvFastPeriodic = _fit2x.ConvFastPeriodic
r"""docstring"""
ConvFast = _fit2x.ConvFast
r"""docstring"""
ConvFastAVX = _fit2x.ConvFastAVX
r"""docstring"""
ConvFastPerAVX = _fit2x.ConvFastPerAVX
r"""docstring"""
class DecayConvolution(DecayModifier):
    r"""docstring"""

    thisown = property(lambda x: x.this.own(), lambda x, v: x.this.own(v), doc="The membership flag")
    __repr__ = _swig_repr

    @staticmethod
    def compute_corrected_irf(irf, corrected_irf, irf_shift_channels, irf_background_counts):
        r"""docstring"""
        return _fit2x.DecayConvolution_compute_corrected_irf(irf, corrected_irf, irf_shift_channels, irf_background_counts)

    @staticmethod
    def compute_mean_lifetime(irf_histogram, decay_histogram, micro_time_resolution):
        r"""docstring"""
        return _fit2x.DecayConvolution_compute_mean_lifetime(irf_histogram, decay_histogram, micro_time_resolution)

    def set_irf(self, v):
        r"""docstring"""
        return _fit2x.DecayConvolution_set_irf(self, v)

    def get_irf(self):
        r"""docstring"""
        return _fit2x.DecayConvolution_get_irf(self)

    def get_mean_lifetime(self, decay):
        r"""docstring"""
        return _fit2x.DecayConvolution_get_mean_lifetime(self, decay)

    def set(self, convolution_method=ConvFastPeriodicTime, excitation_period=100, irf_shift_channels=0.0, irf_background_counts=0):
        r"""docstring"""
        return _fit2x.DecayConvolution_set(self, convolution_method, excitation_period, irf_shift_channels, irf_background_counts)

    def __init__(self, lifetime_handler, instrument_response_function=None, convolution_method=ConvFast, excitation_period=100, irf_shift_channels=0.0, irf_background_counts=0, start=0, stop=-1, active=True):
        r"""docstring"""
        if self.__class__ == DecayConvolution:
            _self = None
        else:
            _self = self
        _fit2x.DecayConvolution_swiginit(self, _fit2x.new_DecayConvolution(_self, lifetime_handler, instrument_response_function, convolution_method, excitation_period, irf_shift_channels, irf_background_counts, start, stop, active))
    __swig_destroy__ = _fit2x.delete_DecayConvolution

    def add(self, out):
        r"""docstring"""
        return _fit2x.DecayConvolution_add(self, out)
    corrected_irf = property(_fit2x.DecayConvolution_corrected_irf_get, doc=r"""docstring""")
    convolution_method = property(_fit2x.DecayConvolution_convolution_method_get, _fit2x.DecayConvolution_convolution_method_set, doc=r"""docstring""")
    excitation_period = property(_fit2x.DecayConvolution_excitation_period_get, _fit2x.DecayConvolution_excitation_period_set, doc=r"""docstring""")
    irf_shift_channels = property(_fit2x.DecayConvolution_irf_shift_channels_get, _fit2x.DecayConvolution_irf_shift_channels_set, doc=r"""docstring""")
    irf_background_counts = property(_fit2x.DecayConvolution_irf_background_counts_get, _fit2x.DecayConvolution_irf_background_counts_set, doc=r"""docstring""")
    def __disown__(self):
        self.this.disown()
        _fit2x.disown_DecayConvolution(self)
        return weakref.proxy(self)

# Register DecayConvolution in _fit2x:
_fit2x.DecayConvolution_swigregister(DecayConvolution)
class DecayPattern(DecayModifier):
    r"""docstring"""

    thisown = property(lambda x: x.this.own(), lambda x, v: x.this.own(v), doc="The membership flag")
    __repr__ = _swig_repr

    def add(self, decay):
        r"""docstring"""
        return _fit2x.DecayPattern_add(self, decay)

    def __init__(self, constant_offset=0.0, pattern=None, pattern_fraction=0.0, start=0, stop=-1, active=True):
        r"""docstring"""
        if self.__class__ == DecayPattern:
            _self = None
        else:
            _self = self
        _fit2x.DecayPattern_swiginit(self, _fit2x.new_DecayPattern(_self, constant_offset, pattern, pattern_fraction, start, stop, active))
    pattern = property(_fit2x.DecayPattern_pattern_get, _fit2x.DecayPattern_pattern_set, doc=r"""docstring""")
    pattern_fraction = property(_fit2x.DecayPattern_pattern_fraction_get, _fit2x.DecayPattern_pattern_fraction_set, doc=r"""docstring""")
    constant_offset = property(_fit2x.DecayPattern_constant_offset_get, _fit2x.DecayPattern_constant_offset_set, doc=r"""docstring""")
    __swig_destroy__ = _fit2x.delete_DecayPattern
    def __disown__(self):
        self.this.disown()
        _fit2x.disown_DecayPattern(self)
        return weakref.proxy(self)

# Register DecayPattern in _fit2x:
_fit2x.DecayPattern_swigregister(DecayPattern)
class DecayPileup(DecayModifier):
    r"""docstring"""

    thisown = property(lambda x: x.this.own(), lambda x, v: x.this.own(v), doc="The membership flag")
    __repr__ = _swig_repr

    def add(self, model):
        r"""docstring"""
        return _fit2x.DecayPileup_add(self, model)

    def __init__(self, *args, **kwargs):
        r"""docstring"""
        if self.__class__ == DecayPileup:
            _self = None
        else:
            _self = self
        _fit2x.DecayPileup_swiginit(self, _fit2x.new_DecayPileup(_self, *args, **kwargs))
    instrument_dead_time = property(_fit2x.DecayPileup_instrument_dead_time_get, _fit2x.DecayPileup_instrument_dead_time_set, doc=r"""docstring""")
    repetition_rate = property(_fit2x.DecayPileup_repetition_rate_get, _fit2x.DecayPileup_repetition_rate_set, doc=r"""docstring""")
    data = property(_fit2x.DecayPileup_data_get, _fit2x.DecayPileup_data_set, doc=r"""docstring""")
    pile_up_model = property(_fit2x.DecayPileup_pile_up_model_get, _fit2x.DecayPileup_pile_up_model_set, doc=r"""docstring""")
    __swig_destroy__ = _fit2x.delete_DecayPileup
    def __disown__(self):
        self.this.disown()
        _fit2x.disown_DecayPileup(self)
        return weakref.proxy(self)

# Register DecayPileup in _fit2x:
_fit2x.DecayPileup_swigregister(DecayPileup)
class DecayLinearization(DecayModifier):
    r"""docstring"""

    thisown = property(lambda x: x.this.own(), lambda x, v: x.this.own(v), doc="The membership flag")
    __repr__ = _swig_repr

    def add(self, decay):
        r"""docstring"""
        return _fit2x.DecayLinearization_add(self, decay)

    def __init__(self, linearization_table, start, stop, active=False):
        r"""docstring"""
        if self.__class__ == DecayLinearization:
            _self = None
        else:
            _self = self
        _fit2x.DecayLinearization_swiginit(self, _fit2x.new_DecayLinearization(_self, linearization_table, start, stop, active))
    linearization = property(_fit2x.DecayLinearization_linearization_get, _fit2x.DecayLinearization_linearization_set, doc=r"""docstring""")
    __swig_destroy__ = _fit2x.delete_DecayLinearization
    def __disown__(self):
        self.this.disown()
        _fit2x.disown_DecayLinearization(self)
        return weakref.proxy(self)

# Register DecayLinearization in _fit2x:
_fit2x.DecayLinearization_swigregister(DecayLinearization)
class DecayScale(DecayModifier):
    r"""docstring"""

    thisown = property(lambda x: x.this.own(), lambda x, v: x.this.own(v), doc="The membership flag")
    __repr__ = _swig_repr

    @staticmethod
    def scale_model(start, stop, constant_background, model, data, squared_data_weights):
        r"""docstring"""
        return _fit2x.DecayScale_scale_model(start, stop, constant_background, model, data, squared_data_weights)

    def set(self, data=None, constant_background=0.0, start=0, stop=-1, active=True):
        r"""docstring"""
        return _fit2x.DecayScale_set(self, data, constant_background, start, stop, active)

    def __init__(self, data=None, constant_background=0.0, start=0, stop=-1, active=True):
        r"""docstring"""
        if self.__class__ == DecayScale:
            _self = None
        else:
            _self = self
        _fit2x.DecayScale_swiginit(self, _fit2x.new_DecayScale(_self, data, constant_background, start, stop, active))

    def add(self, decay):
        r"""docstring"""
        return _fit2x.DecayScale_add(self, decay)
    number_of_photons = property(_fit2x.DecayScale_number_of_photons_get, doc=r"""docstring""")
    constant_background = property(_fit2x.DecayScale_constant_background_get, _fit2x.DecayScale_constant_background_set, doc=r"""docstring""")
    __swig_destroy__ = _fit2x.delete_DecayScale
    def __disown__(self):
        self.this.disown()
        _fit2x.disown_DecayScale(self)
        return weakref.proxy(self)

# Register DecayScale in _fit2x:
_fit2x.DecayScale_swigregister(DecayScale)
class DecayScore(DecayRange):
    r"""docstring"""

    thisown = property(lambda x: x.this.own(), lambda x, v: x.this.own(v), doc="The membership flag")
    __repr__ = _swig_repr

    def get_score(self, start=0, stop=-1, score_type=None):
        r"""docstring"""
        return _fit2x.DecayScore_get_score(self, start, stop, score_type)

    def set(self, *args, **kwargs):
        r"""docstring"""
        return _fit2x.DecayScore_set(self, *args, **kwargs)

    def __init__(self, *args, **kwargs):
        r"""docstring"""
        if self.__class__ == DecayScore:
            _self = None
        else:
            _self = self
        _fit2x.DecayScore_swiginit(self, _fit2x.new_DecayScore(_self, *args, **kwargs))
    __swig_destroy__ = _fit2x.delete_DecayScore
    score_type = property(_fit2x.DecayScore_score_type_get, _fit2x.DecayScore_score_type_set, doc=r"""docstring""")
    score = property(_fit2x.DecayScore_score_get, doc=r"""docstring""")
    model = property(_fit2x.DecayScore_model_get, _fit2x.DecayScore_model_set, doc=r"""docstring""")
    data = property(_fit2x.DecayScore_data_get, _fit2x.DecayScore_data_set, doc=r"""docstring""")
    weighted_residuals = property(_fit2x.DecayScore_weighted_residuals_get, doc=r"""docstring""")
    def __disown__(self):
        self.this.disown()
        _fit2x.disown_DecayScore(self)
        return weakref.proxy(self)

# Register DecayScore in _fit2x:
_fit2x.DecayScore_swigregister(DecayScore)

def compute_signal_and_background(p):
    r"""
    docstring


    Computes the total number of photons in the parallel and perpendicular detection
    channel for the background and the measured signal. The computed number of
    photons are stored in the static variables Sp, Ss, Bp, Bs.  

    Parameters
    ----------
    * `p[in]` :  
        a pointer to a MParam object  

    """
    return _fit2x.compute_signal_and_background(p)

def normM(*args):
    r"""
    docstringdocstring


    Normalizes a model function (that is already normalized to a unit area) to the
    total number of photons in parallel and perpendicular,  

    Parameters
    ----------
    * `M[in`, `out]` :  
        array containing the model function in Jordi format  
    * `s[in]` :  
        a scaling factor by which the model function is divided.  
    * `Nchannels[in]` :  
        the number of channels in the model function (half length of M array)  

    """
    return _fit2x.normM(*args)

def normM_p2s(M, Nchannels):
    r"""
    docstring


    Normalizes the number of photons in the model function for Ss and Sp
    individually to the number of experimental photons in Ss and Sp.  

    Here, the number of experimental photons are global variables that can be
    computed by `compute_signal_and_background`.  

    Parameters
    ----------
    * `M` :  
        array[in,out] containing the model function in Jordi format  
    * `Nchannels[in]` :  
        number of channels in the experiment (half length of M array)  

    """
    return _fit2x.normM_p2s(M, Nchannels)
"""Use a table to redirect the access of attributes and methods to class members


Example
-------

    class ClassA():

        @property
        def y(self):
            return self._y

        @y.setter
        def y(self, v):
            self._y = v

        def print(self, value):
            print("ClassA:", value)

        def __init__(self):
            self._y = 1

    class ClassB():

        def get(self):
            print("Class A.get")
            return self._b

        def set(self, v):
            print("Class A.set:", v)
            self._b = v

        def __init__(self):
            self._b = "b"


    attributes = {
        "NewNameA": ("obj1", "y"), # works with properties
        "NewNameB": ("obj2", "get", "set") # or getter/setter methods
    }
    methods = {
        "method_renamed": ("obj1", "print"), # works with properties
    }
    pd = ParameterDirector(attributes, methods)
    pd.obj1 = ClassA()
    pd.obj2 = ClassB()

    pd.get("NewNameA") == 1
    pd.set("NewNameA", 2)
    pd.NewNameA = 33
    pd.obj1._y == 33
    pd.method_renamed(42)  # prints "42"


"""
import json
import pathlib
import collections.abc

from typing import Dict, Tuple


class ParameterDirector(object):

    _ignore_attributes = list()
    _attributes = dict()
    _methods = dict()

    def __init__(
            self,
            attributes: Dict[str, Tuple[str, str, str]] = None,
            methods: Dict[str, Tuple[str, str]] = None,
            state_file = None
    ):
        if isinstance(attributes, dict):
            self._attributes = attributes
        if isinstance(methods, dict):
            self._methods = methods
        if state_file:
            with open(state_file, 'r') as fp:
                s = fp.readlines()
                self.from_json(s)

    def resolve(self, origin):
        if len(origin) == 3:
            source, getter, setter = origin
        else:
            setter = None
            source, getter = origin
        source_obj = self
        for s in source.split("."):
            if len(s) > 0:
                source_obj = source_obj.__getattribute__(s)
        return source_obj, getter, setter

    def set(self, key, value):
        if key in self._attributes:
            origin = self._attributes[key]
            source_obj, getter_attr, setter = self.resolve(origin)
            if setter is None:
                try:
                    setattr(source_obj, getter_attr, value)
                except AttributeError:
                    pass
#print("Cannot set attribute", key)
            else:
                getattr(source_obj, setter)(value)
            return True
        return False

    def get(self, key):
        target = self._attributes.get(key, None)
        is_attribute = True
        if target is None:
            target = self._methods.get(key, None)
            is_attribute = False
        if target is None:
#print("Cannt get key", key)
            raise AttributeError
        target_obj, getter, _ = self.resolve(target)
        try:
            result = getattr(target_obj, getter)
            if callable(result) and is_attribute:
                return result()
            return result
        except TypeError:
            return None

    @property
    def parameter(self):
        keys, values = list(), list()
        for key in self._attributes.keys():
            value = self.get(key)
            if value is not None:
                values.append(value)
                keys.append(key)
        return dict(zip(keys, values))

    @parameter.setter
    def parameter(self, v):
        for key, value in v.items():
            try:
                self.set(key, value)
            except AttributeError:
                pass
#print("Cannot set key:", key)

    def __setattr__(self, key, value):
        if key in self._attributes:
            self.set(key, value)
        else:
            super().__setattr__(key, value)

    def __getattr__(self, item):
        return self.get(item)

    def to_json(self, *args, **kwargs) -> str:
        d = dict()
        for key, value in self.parameter.items():
            if key in self._ignore_attributes:
                continue
            if not isinstance(value, str) and isinstance(value, collections.abc.Iterable):
                d[key] = [v for v in value]
            else:
                d[key] = value
        s = json.dumps(d, *args, **kwargs)
        return s

    @classmethod
    def from_json(cls, s):
        if isinstance(s, pathlib.Path):
            json_str = open(s, 'r').read()
        else:
            json_str = s
        re = cls()
        re.__setstate__(json.loads(json_str))
        return re

    def save(self, filename, file_type='json', **kwargs):
        if file_type == 'json':
            s = self.to_json(**kwargs)
            with open(filename, 'w+') as fp:
                fp.write(s)
        else:
            raise NotImplementedError

    def load(self, filename, file_type='json'):
        if file_type == 'json':
            with open(filename, 'r') as fp:
                s = fp.read()
                d = json.loads(s)
                self.__setstate__(d)
        else:
            raise NotImplementedError

    def __getstate__(self):
        state = dict()
        state.update(self.parameter)
        return state

    def __setstate__(self, state):
# see https://stackoverflow.com/questions/9310053/how-to-make-my-swig-extension-module-work-with-pickle/
# Requires empty constructor: __init__()
        self.__init__()
        self.parameter = state

import typing
import numpy as np

import fit2x
import tttrlib

from typing import NamedTuple

MAX_FLOAT = np.finfo(np.float64).max
MIN_FLOAT = np.finfo(np.float64).min


class DataSettings(NamedTuple):
    data: np.ndarray = None
    data_noise: np.ndarray = None
    time_axis: np.array = None
    irf_histogram: np.array = None
# tttr_data: tttrlib.TTTR = None
# tttr_irf: tttrlib.TTTR = None
    tttr_micro_time_coarsening: int = 1
    acquisition_time: float = MAX_FLOAT


class LifetimeSettings(NamedTuple):
    lifetime_spectrum: np.array = np.array([], dtype=np.float64)
    use_amplitude_threshold: bool = False
    abs_lifetime_spectrum: bool = True
    amplitude_threshold: float = MIN_FLOAT


class ConvolutionSettings(NamedTuple):
    convolution_method: int = fit2x.ConvFast
    excitation_period: float = 100.0
    irf_shift_channels: float = 0.0
    irf_background_counts: float = 0.0
    start: int = 0
    stop: int = -1
    active: bool = True


class PatternSettings(NamedTuple):
    constant_offset: float = 0.0
    pattern: fit2x.DecayCurve = None
    pattern_fraction: float = 0.0
    start: int = 0
    stop: int = -1
    active: bool = True


class PileupSettings(NamedTuple):
    pile_up_model: str = "coates"
    repetition_rate: float = 10.0
    instrument_dead_time: float = 120.0
    start: int = 0
    stop: int = -1
    active: bool = True


class ScalingSettings(NamedTuple):
    constant_offset: float = 0.0
    start: int = 0
    stop: int = -1
    active: bool = True


class LinearizationSettings(NamedTuple):
    linearization_table: fit2x.DecayCurve = None
    start: int = 0
    stop: int = -1
    active: bool = True


class ScoreSettings(NamedTuple):
    score_type: str = "default"
    start: int = 0
    stop: int = -1


class Decay(ParameterDirector):

# These attributes are skipped when saving to JSON
    _ignore_attributes = [
        'irf_histogram',
        'tttr_data',
        'tttr_irf',
        'linearization_table',
        'corrected_irf',
        'x',
        'y',
        'data_weights',
        'number_of_photons'
    ]

    _attributes = {
#VERSION
#####################
# TODO
# 'version': ("", "get_version"),
# Data
############################
        'data': ("_data", "y"),
        'data_noise': ("_data", "ey"),
        'model': ("_model", "y"),
        'y': ("_model", "y"),
        'x': ("_model", "x"),
        'acquisition_time': ("_data", "acquisition_time"),
        'time_axis': ("_data", "x"),
        'irf_histogram': ("_irf", "y"),
        'irf': ("_irf", "y"),
        'tttr_data': ("_data", None, "set_tttr"),
        'tttr_irf': ("_irf", None, "set_tttr"),
# LIFETIME SPECTRUM
############################
        'lifetime_spectrum': ("lifetime_handler", "get_lifetime_spectrum", "set_lifetime_spectrum"),
        'use_amplitude_threshold': ("lifetime_handler", "use_amplitude_threshold"),
        'abs_lifetime_spectrum': ("lifetime_handler", "abs_lifetime_spectrum"),
        'amplitude_threshold': ("lifetime_handler", "amplitude_threshold"),
# CONVOLUTION
############################
        'corrected_irf': ("decay_convolution.corrected_irf", "y"),
        'convolution_range': ("decay_convolution", "get_range", "set_range"),
        'convolution_method': ("decay_convolution", "convolution_method"),
        'excitation_period': ("decay_convolution", "excitation_period"),
        'irf_shift_channels': ("decay_convolution", "irf_shift_channels"),
        'irf_background_counts': ("decay_convolution", "irf_background_counts"),
# Scatter
############################
        'scatter_fraction': ("decay_scatter", "pattern_fraction"),
# BACKGROUND
############################
        'constant_offset': ("", "constant_background"),
        'pattern_fraction': ("decay_background", "pattern_fraction"),
# PILE-UP
#############################
        'pile_up_model': ("decay_pileup", "pile_up_model"),
        'instrument_dead_time': ("decay_pileup", "instrument_dead_time"),
        'use_pile_up_correction': ("decay_pileup", "active"),
# SCALE
############################
        'scale_model_to_data': ("decay_scale", "active"),
        'number_of_photons': ("decay_scale", "number_of_photons"),
# LINEARIZATION
############################
        'linearization_table': ("decay_linearization.data", "y"),
        'linearization': ("decay_linearization.data", "y"),
        'use_linearization': ("decay_linearization", "active"),
# SCORE
###########################
        'score_type': ("decay_score", "score_type"),
        'score_range': ('', 'get_score_range', 'set_score_range'),
        'score': ("decay_score", "score"),
        'weighted_residuals': ("decay_score", "weighted_residuals")
    }

    _methods = {
        'get_mean_lifetime': ("decay_convolution", "get_mean_lifetime"),
        'get_score': ("decay_score", "get_score")
    }

# @staticmethod
# def make_decay_curves(
#         data: np.ndarray = None,
#         data_noise: np.ndarray = None,
#         time_axis: np.array = None,
#         irf_histogram: np.array = None,
#         # tttr_data: tttrlib.TTTR = None,
#         # tttr_irf: tttrlib.TTTR = None,
#         tttr_micro_time_coarsening: int = 1,
#         acquisition_time: float = MAX_FLOAT,
# ) -> typing.Tuple[fit2x.DecayCurve, fit2x.DecayCurve]:
#     # Data
#     if time_axis is None:
#         time_axis = np.array([], np.float64)
#     if data is None:
#         data = np.array([], np.float64)
#     # IRF
#     if irf_histogram is None:
#         irf_histogram = np.zeros_like(data)
#         if len(irf_histogram) > 0:
#             irf_histogram[0] = 1.0
#     decay = fit2x.DecayCurve(x=time_axis, y=data, acquisition_time=acquisition_time)
#     irf = fit2x.DecayCurve(x=time_axis, y=irf_histogram)
#     if data_noise is not None:
#         decay.set_ey(data_noise)
#     # Handle TTTR data
#     if isinstance(tttr_data, tttrlib.TTTR):
#         decay.set_tttr(tttr_data, tttr_micro_time_coarsening)
#     if isinstance(tttr_irf, tttrlib.TTTR):
#         irf.set_tttr(tttr_irf, tttr_micro_time_coarsening)
#     return decay, irf

    def __init__(self,
                 data_settings=None,  # type: DataSettings
                 lifetime_settings=None,  # type: LifetimeSettings
                 convolution_settings=None,  # type: ConvolutionSettings
                 scatter_settings=None,  # type: PatternSettings
                 background_settings=None,  # type: PatternSettings
                 pileup_settings=None,  # type: PileupSettings
                 scaling_settings=None,  # type: ScalingSettings
                 linearization_settings=None,  # type: LinearizationSettings
                 score_settings=None  # type: ScoreSettings
                 ):
        super().__init__()
        if data_settings is None:
            data_settings = DataSettings()
        if lifetime_settings is None:
            lifetime_settings = LifetimeSettings()
        if convolution_settings is None:
            convolution_settings = ConvolutionSettings()
        if background_settings is None:
            background_settings = PatternSettings()
        if scatter_settings is None:
            scatter_settings = PatternSettings()
        if pileup_settings is None:
            pileup_settings = PileupSettings()
        if linearization_settings is None:
            linearization_settings = LinearizationSettings()
        if scaling_settings is None:
            scaling_settings = ScalingSettings()
        if score_settings is None:
            score_settings = ScoreSettings()

# init decay curves
        data, irf = self.make_decay_curves(*data_settings)
        model = fit2x.DecayCurve(data.x)

        lifetime_handler = fit2x.DecayLifetimeHandler(*lifetime_settings)
        decay_convolution = fit2x.DecayConvolution(lifetime_handler, irf, *convolution_settings)
        decay_scatter = fit2x.DecayPattern(*scatter_settings)
        decay_scatter.data = irf
        decay_background = fit2x.DecayPattern(*background_settings)
        decay_pileup = fit2x.DecayPileup(data, *pileup_settings)
        decay_linearization = fit2x.DecayLinearization(*linearization_settings)
        decay_scale = fit2x.DecayScale(data, *scaling_settings)
        decay_score = fit2x.DecayScore(model, data, *score_settings)

        self._data, self._irf, self._model = data, irf, model
        self.lifetime_handler = lifetime_handler
        self.decay_convolution = decay_convolution
        self.decay_scatter = decay_scatter
        self.decay_pileup = decay_pileup
        self.decay_linearization = decay_linearization
        self.decay_scale = decay_scale
        self.decay_background = decay_background
        self.decay_score = decay_score

        self._decay_modifier = [
            self.decay_convolution,
            self.decay_scatter,
# self.decay_pileup, # TODO: seems broken
            self.decay_linearization,
            self.decay_scale,
            self.decay_background,
        ]

    def load(self, *args, **kwargs):
        super().load(*args, **kwargs)
        self._model.x = self._data.x
        self._irf.x = self._data.x

    def update(self):
        dc = self
        for dm in self._decay_modifier:
            dm.add(dc._model)

    @property
    def score(self, *args, **kwargs):
        self.update()
        return self.decay_score.get_score(*args, **kwargs)

    @property
    def mean_lifetime(self):
        return self.decay_convolution.get_mean_lifetime(self._data)

    @property
    def constant_background(self):
        return self.decay_scale.constant_background

    @constant_background.setter
    def constant_background(self, v):
        self.decay_scale.constant_background = v
        self.decay_background.constant_offset = v

    def get_score_range(self):
        return self.decay_score.get_range(self._data)

    def set_score_range(self, start_stop):
        for dm in self._decay_modifier:
            dm.set_range(start_stop)
        self.decay_score.set_range(start_stop)



